Exemple #1
0
        /// <summary>
        /// Download MailContent for given uids.
        /// </summary>
        /// <param name="cancel"></param>
        /// <param name="uids"></param>
        /// <returns></returns>
        private async Task RunDownloadContentAsyncLoop(CancellationToken cancel, List <string> uids)
        {
            if (uids == null || uids.Count == 0)
            {
                return;
            }

            // Create mail client.
            IMailClient client = (new TrivialMailDllFactory()).Build(_serverType);

            try
            {
                await Task.Factory.StartNew(() =>
                {
                    client.Connect(_serverEncryption, _host);
                    client.Login(_user, _password);

                    // If is not connected or encrypted (if req) then will break and try to reconnect.
                    if (client.IsConnected && (_serverEncryption == MailServerEncryption.Unencrypted || client.IsEncrypted))
                    {
                        using (IMailStorage <MailContentEntity> storage = _mailContentStorageFactory())
                        {
                            foreach (var uid in uids)
                            {
                                if (string.IsNullOrWhiteSpace(uid))
                                {
                                    continue;
                                }

                                if (cancel.IsCancellationRequested)
                                {
                                    break;
                                }

                                var downloadRequired = true;
                                MailContentEntity mailContentEntity = null;
                                if (storage.Exists(x => x.Uid == uid))
                                {
                                    mailContentEntity = storage.FindOne(x => x.Uid == uid);

                                    if (mailContentEntity != null && mailContentEntity.IsComplete)
                                    {
                                        downloadRequired = false;
                                    }
                                }

                                if (downloadRequired)
                                {
                                    // 1. Insert empty MailContent with only uid set to prevent other concurrent method to download same content.
                                    mailContentEntity = new MailContentEntity()
                                    {
                                        Uid        = uid,
                                        IsComplete = false
                                    };
                                    storage.Insert(new List <MailContentEntity>()
                                    {
                                        mailContentEntity
                                    });

                                    // 2. Download complete email.
                                    var message = client.GetMessageByUid(uid);

                                    // Note: MailDll documentation states that message can be null.
                                    if (message != null)
                                    {
                                        IMail email = new MailBuilder().CreateFromEml(message);
                                        if (email != null)
                                        {
                                            // 3. Update database with downloaded email content.
                                            mailContentEntity = new MailContentEntity()
                                            {
                                                Uid          = uid,
                                                Date         = email?.Date == null ? DateTime.MinValue : email.Date.Value,
                                                Html         = email.Html,
                                                MessageId    = email.MessageID,
                                                Text         = email.Text,
                                                CustomHeader = email?.Document.Root.Headers["x-spam"],
                                                IsComplete   = true
                                            };

                                            storage.Update(new List <MailContentEntity>()
                                            {
                                                mailContentEntity
                                            });

                                            // Publish MailContent.
                                            _mailContentStream.OnNext(new MailContent(mailContentEntity));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }, cancel, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
            }
            catch (Exception e)
            {
                Logger.Error(e, $"RunDownloadContentAsync");
            }
            finally
            {
                client?.Close();
            }
        }
Exemple #2
0
        /// <summary>
        /// Every 30s queries mail server for new email.
        /// When there are new emails available it first download all mail headers and publishes them to the stream.
        /// Afterwards start downloading all mail content for just downloaded mail headers.
        /// </summary>
        /// <param name="cancel"></param>
        /// <returns></returns>
        private async Task RunCheckForNewMailAsyncLoop(CancellationToken cancel)
        {
            // Create mail client.
            IMailClient client = (new TrivialMailDllFactory()).Build(_serverType);

            try
            {
                // Publish Connecting state.
                _controllerStateStream.OnNext(ControllerState.Connecting);
                client.Connect(_serverEncryption, _host);

                // Publish LoggingIn state.
                _controllerStateStream.OnNext(ControllerState.LoggingIn);
                client.Login(_user, _password);

                // Publish Connected state.
                _controllerStateStream.OnNext(ControllerState.Connected);

                // Main loop
                while (!cancel.IsCancellationRequested)
                {
                    // If disconnect or not encrypted (when should be) then reconnect.
                    if (client.IsConnected && (_serverEncryption == MailServerEncryption.Unencrypted || client.IsEncrypted))
                    {
                        // MailHeaderList contains new headers which will be published to subscribers.
                        List <MailHeaderEntity> mailHeaderEntities = new List <MailHeaderEntity>();

                        using (IMailStorage <MailHeaderEntity> storage = _mailHeaderStorageFactory())
                        {
                            // 1. Get from mail server all uids (emails).
                            // ToDo: for Imap this could be improved.
                            List <string> newUids = client.GetAllUids().ToList();

                            // 2. Distinct list of uids which are not yet stored in the database.
                            // Let's reverse and start checking with the most recent email (the latest uid).
                            newUids.Reverse();
                            List <string> uidList = new List <string>();
                            foreach (var uid in newUids)
                            {
                                if (!storage.Exists(x => x.Uid == uid))
                                {
                                    uidList.Add(uid);
                                }
                                else
                                {
                                    break;
                                }
                                // Note: if any first exists, break the loop other emails are probably downloaded.
                            }

                            if (uidList.Count > 0)
                            {
                                // 3. Download mail headers.
                                foreach (var uid in uidList)
                                {
                                    // Download message header.
                                    var header = client.GetHeadersByUid(uid);

                                    // Note: MailDll documentation states that header can be null.
                                    if (header == null)
                                    {
                                        throw new ArgumentNullException(nameof(header), $"Downloaded an empty email header ({uid}).");
                                    }

                                    var email            = new MailBuilder().CreateFromEml(header);
                                    var emailFrom        = email?.From.FirstOrDefault();
                                    var mailHeaderEntity = new MailHeaderEntity()
                                    {
                                        Uid            = uid,
                                        Date           = email?.Date ?? DateTime.MinValue,
                                        Subject        = email?.Subject,
                                        MailFromEntity = new MailFromEntity()
                                        {
                                            Address    = emailFrom?.Address,
                                            Name       = emailFrom?.Name,
                                            LocalPart  = emailFrom?.LocalPart,
                                            DomainPart = emailFrom?.DomainPart
                                        }
                                    };

                                    mailHeaderEntities.Add(mailHeaderEntity);
                                }

                                // 4. Insert all new mail headers into the storage.
                                storage.Insert(mailHeaderEntities);
                            }
                        }

                        // For all new email headers publish them to the subscribers and download the content.
                        // Note: This whole block is taken out from above using() to release storage handle asap.
                        if (mailHeaderEntities.Count > 0)
                        {
                            // 5. Publish all new mail headers to the stream.
                            mailHeaderEntities.ForEach(mailHeaderEntity => { _mailHeaderStream.OnNext(new MailHeader(mailHeaderEntity)); });

                            // 6. Start downloading content loop
                            // It's not done in above foreach loop to not to keep storage open for too long
                            // when running over slow internet connection.
                            RunDownloadContentAsyncLoop(cancel, mailHeaderEntities.Select(x => x.Uid).ToList());
                        }
                    }
                    else
                    {
                        break;
                    }

                    // Check for new email again in 30s
                    await Observable.Return(0).Delay(TimeSpan.FromSeconds(30), Scheduler.CurrentThread).ToTask(cancel);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, $"RunCheckForNewMailAsyncLoop");
            }
            finally
            {
                client?.Close();

                if (!cancel.IsCancellationRequested)
                {
                    // Publish Disconnected state.
                    _controllerStateStream.OnNext(ControllerState.Disconnected);
                }
            }
        }