Esempio n. 1
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);
                }
            }
        }