/// <summary> /// Publish requested mail content to the stream. /// </summary> /// <param name="cancel"></param> /// <param name="uid"></param> /// <returns></returns> private async Task PublishRequestedByUidMailContent(CancellationToken cancel, string uid) { try { using (IMailStorage <MailContentEntity> storage = _mailContentStorageFactory()) { // 1. Check if content already exists in the database. if (storage.Exists(x => x.Uid == uid)) { var mailContentEntity = storage.FindOne(x => x.Uid == uid); if (mailContentEntity == null || !mailContentEntity.IsComplete) { // 2. If content is not yet downloaded of any reason, download it and publish afterwards. await RunDownloadContentAsyncLoop(cancel, new List <string>() { uid }); } else { _mailContentStream.OnNext(new MailContent(mailContentEntity)); } } } } catch (Exception e) { Logger.Error(e, "PublishRequestedByUidMailContent"); } }
/// <summary> /// Reset the connection state. /// </summary> private void Rset(Pop3Context context) { if (context.LastCommand != -1) { if (context != null && !String.IsNullOrEmpty(context.Username)) { IMailStorage mail = (_storage != null ? _storage as IMailStorage : null); if (mail != null) { mail.Logout(context.Username); } } context.Reset(); context.Username = null; context.Password = null; context.LastCommand = -1; context.WriteLine(MESSAGE_OK); } else { context.WriteLine(MESSAGE_INVALID_COMMAND_ORDER); } }
/// <summary> /// Handels the PASS command. /// </summary> private void Pass(Pop3Context context, String[] inputs) { if (context.LastCommand == COMMAND_USER) { if (inputs.Length == 2) { IMailStorage mail = (_storage != null ? _storage as IMailStorage : null); if (mail == null || mail.Login(context.Username, inputs[1]) == true) { context.Password = inputs[1]; context.LastCommand = COMMAND_PASS; context.WriteLine(MESSAGE_PASSWORD_OK); } else { context.Username = null; context.Password = null; context.LastCommand = -1; context.WriteLine(MESSAGE_PASSWORD_INVALID); } } else { context.WriteLine(MESSAGE_INVALID_ARGUMENT_COUNT); } } else { context.WriteLine(MESSAGE_INVALID_COMMAND_ORDER); } }
public MessageProcessor(IMailDeliverer deliverer, IMailStorage storage, IDistributedCache cache, ILogger <MessageProcessor> logger) { this.deliverer = deliverer; this.storage = storage; this.logger = logger; this.cache = cache; }
public ApplicationWorker(AccountsSettingsModel accountsSettings, EventBus eventBus, OutgoingMailServiceResolver serviceResolver, AsyncObservableCollection <EmailGroupModel> emailGroupList, ApplicationSettingsModel appSettings, IMailStorage mailStorage) { _accountsSettings = accountsSettings; _eventBus = eventBus; _serviceResolver = serviceResolver; _emailGroupList = emailGroupList; _appSettings = appSettings; _mailStorage = mailStorage; }
public ApplicationWorker(AccountsSettingsModel accountsSettings, EventBus eventBus, OutgoingMailServiceResolver serviceResolver, AsyncObservableCollection<EmailGroupModel> emailGroupList, ApplicationSettingsModel appSettings, IMailStorage mailStorage) { _accountsSettings = accountsSettings; _eventBus = eventBus; _serviceResolver = serviceResolver; _emailGroupList = emailGroupList; _appSettings = appSettings; _mailStorage = mailStorage; }
/// <summary> /// Loads all existing mail headers in local database and publish them to the stream. /// </summary> public async void LoadAllExistingMailHeaders() { try { await Task.Run(() => { // Loads all existing mail headers in local database and publish them to subscribers. using (IMailStorage <MailHeaderEntity> storage = _mailHeaderStorageFactory()) { var mailHeaderEntities = storage.FindAll().ToList(); if (mailHeaderEntities.Count > 0) { // Publish mail headers. mailHeaderEntities.ForEach(mailHeaderEntity => { _mailHeaderStream.OnNext(new MailHeader(mailHeaderEntity)); }); } } }); } catch (Exception e) { Logger.Error(e, "LoadAllExistingMailHeaders"); } }
public MailClient(IMailDelivery delivery, IMailStorage storage, IMailLogger logger) { this.delivery = delivery; this.storage = storage; this.logger = logger; }
/// <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(); } }
/// <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); } } }
public MailService RegisterStorage(IMailStorage storage) { this.storage = storage; return this; }