/// <summary> Esegue gli invii per la PECMailBox passata per parametro. </summary> /// <param name="box">PECMailBox in cui cercare i messaggi da spedire.</param> public void Process(PECMailBox box, string password) { FileLogger.Info(_loggerName, string.Format("Avvio ciclo di invio per la casella {0} - {1}", box.Id, box.MailBoxName)); // Gestisco la duplicazione delle PEC marchiate tali GenerateMultiplePecs(box); // Recupero da DB l'elenco delle PEC in uscita non ancora spedite (MailDate NULL). Carcio anche le PEC in stato Processing. bool useStatusProcessing = true; IList <Int32> mail_ids = _factory.PECMailFacade.GetOutgoingMails(box.Id, box.Configuration.MaxSendForSession, useStatusProcessing); FileLogger.Info(_loggerName, string.Format("Trovate {0} PEC da spedire.", mail_ids.Count)); // Eseguo un ciclo sulle PEC da spedire fino al numero massimo di PEC gestibili per sessione di lavoro int successSendMail = 0; int errorSendMail = 0; int counter = 0; for (int index = 0; index < mail_ids.Count && counter < box.Configuration.MaxSendForSession; index++) { if (_cancelRequest()) { FileLogger.Info(_loggerName, "Blocco invio PEC per chiusura modulo invocata dall'utente."); return; } // Avvio watch per verificare quanto ci impiega per singola mail //TODO : da mettere in debug mode Stopwatch watch = Stopwatch.StartNew(); PECMail mail = MailStoreFacade.Factory.PECMailFacade.GetById(mail_ids[index]); if (mail.IsActive != ActiveType.Cast(ActiveType.PECMailActiveType.Active) && mail.IsActive != ActiveType.Cast(ActiveType.PECMailActiveType.Processing)) { FileLogger.Info(_loggerName, string.Format("Errore in lettura pecMail [{0}]. Lo status doveva essere 1. Possibile aggiornamento dati non previsto.", mail.Id)); return; } MailStoreFacade.Factory.TaskHeaderFacade.ActivatePECTaskProcess(mail); mail.IsActive = ActiveType.Cast(ActiveType.PECMailActiveType.Processing); _factory.PECMailFacade.Update(ref mail); FileLogger.Info(_loggerName, string.Format("Inizio processamento PEC [{0}] per invio. - Item #{1}", mail.Id, counter)); // Preparo i dati per l'invio Guid guid = Guid.NewGuid(); String fullName = string.Empty; IMail iMailPec = null; int i = 0; Exception lastException = null; String errorLastSendMail = string.Empty; while (iMailPec == null && i < 5) { // Salvo solamente gli errori dell'ultimo step esecuitivo StringBuilder errorMessage = new StringBuilder(); try { iMailPec = PrepareEmlData(mail, guid, ref fullName, ref errorMessage); if (iMailPec == null) { FileLogger.Error(_loggerName, string.Format("Tentativo di creazione eml {0}/5 saltato per massimo numero di elementi in Temp raggiunto per la PEC [{1}]", i + 1, mail.Id)); } } catch (Exception ex) { string errMsg = string.Format("Errore in fase di inserimento in Biblos per invio della PEC [{0}] - Tentativo {1}/5", mail.Id, i + 1); errorMessage.Append(errMsg); FileLogger.Error(_loggerName, errMsg, ex); lastException = ex; if (_cancelRequest()) { String message = "Blocco invio PEC per chiusura modulo invocata dall'utente a causa di errore in fase di preparazione dati EML."; errorMessage.Append(message); _factory.PECMailLogFacade.Warning(ref mail, message); FileLogger.Info(_loggerName, message); return; } Thread.Sleep(1000 * 30); } finally { errorLastSendMail = errorMessage.ToString(); i++; } } // Ho eseguito operazioni importanti per cui libero la memoria GC.Collect(); // Se a questo punto non sono riuscito ad ottenere un oggetto iMail significa che ci sono problemi su Biblos o su FileSystem, // quindi notifico, blocco e procedo oltre if (iMailPec == null) { String errorResult = string.Format("La PEC {0} della casella {1} - {2} non è stata correttamente inserita in Biblos per 5 volte. La PEC non è stata inviata ed è stata disattivata (isActive = 255) e dovrà essere riattivata previa verifica dell'errore.", mail.Id, box.Id, box.MailBoxName); mail.IsActive = ActiveType.Cast(ActiveType.PECMailActiveType.Error); _factory.PECMailFacade.UpdateOnly(ref mail); _factory.PECMailLogFacade.Error(ref mail, string.Concat(errorResult, errorLastSendMail)); MailStoreFacade.SetPECMailTaskError(mail); throw new Exception(errorResult, lastException); } // Se sono qui significa che tutti i dati su Biblos sono stati memorizzati // Spedisco la PEC via SMTP try { if (SendMail(box, iMailPec, mail, guid, password)) { // Se sono riuscito ad inviare allora salvo su Server via IMAP successSendMail++; try { //Aggiorno l'attributo MailDate per l'archivio di conservazione BiblosDocumentInfo mainInfo = BiblosDocumentInfo.GetDocuments(mail.Location.ConservationServer, mail.IDMailContent).SingleOrDefault(); FileLogger.Info(_loggerName, "Tentativo di aggiornare attributo MailDate"); mainInfo.AddAttribute("MailDate", mail.MailDate.Value.ToString(new CultureInfo("it-IT") { DateTimeFormat = new DateTimeFormatInfo { ShortDatePattern = "dd/MM/yyyy" } }), true); mainInfo.Update("BibloDS"); FileLogger.Info(_loggerName, string.Format("Attributi della PEC con ID {0} aggiornati.", mail.Id)); } catch (Exception ex) { _factory.PECMailLogFacade.Error(ref mail, string.Format("Errore in fase di aggiornamento attributi conservazione: {0}", ex.Message)); _sendMessage(string.Format("Errore in fase di aggiornamento attributi conservazione: {0} - Stacktrace: {1}", ex.Message, _fullStacktrace(ex))); } try { mail.IsActive = ActiveType.Cast(ActiveType.PECMailActiveType.Active); _factory.PECMailFacade.UpdateOnly(ref mail); SaveMail(box, iMailPec, mail, password); } catch (Exception ex) { _factory.PECMailLogFacade.Error(ref mail, string.Format("Errore in fase di salvataggio mail su IMAP: {0}", ex.Message)); _sendMessage(string.Format("Errore in fase di salvataggio mail su IMAP: {0} - Stacktrace: {1}", ex.Message, _fullStacktrace(ex))); } MailStoreFacade.CompletePECMailTask(mail); // Pulisco eventuali dati sporchi rimanenti CleanEmlData(mail.Id, _parameters.TempFolder); } else { errorSendMail++; } } catch (Exception ex) { _factory.PECMailLogFacade.Error(ref mail, string.Format("Errore in fase di invio mail: {0}", ex.Message)); _sendMessage(string.Format("Errore in fase di invio mail: {0} - Stacktrace: {1}", ex.Message, _fullStacktrace(ex))); MailStoreFacade.SetPECMailTaskError(mail); } // Fermo il Watch e registro il LOG watch.Stop(); FileLogger.Info(_loggerName, string.Format("Fine processamento PEC [{0}] per invio. Gestione avvenuta in {1} millisecondi. Item #{2}", mail.Id, watch.ElapsedMilliseconds, counter)); // Procedo con la PEC successiva counter++; // Fermo il thread per non sovraccaricare troppo i sistemi if (_parameters.SendSleep > 0) { Thread.Sleep(1000 * _parameters.SendSleep); } } if (successSendMail > 0) { _factory.PECMailboxLogFacade.InsertLog(ref box, string.Format("PEC Inviate {0} con successo", successSendMail), PECMailBoxLogFacade.PecMailBoxLogType.Sent); } if (errorSendMail > 0) { _factory.PECMailboxLogFacade.InsertLog(ref box, string.Format("Ci sono {0} PecMail da controllare", errorSendMail), PECMailBoxLogFacade.PecMailBoxLogType.SentError); } FileLogger.Info(_loggerName, string.Format("Fine ciclo di invio per la casella {0} - {1}", box.Id, box.MailBoxName)); }
/// <summary> Spedisce l'email. </summary> /// <param name="mailbox"> MailBox da dove spedire l'email. </param> /// <param name="email"> Email da spedire. </param> /// <param name="pecMail"> PEC da inviare </param> /// <param name="guid"> Guid della mail</param> public bool SendMail(PECMailBox mailbox, IMail email, PECMail pecMail, Guid guid, string password) { // In caso di modalità DEBUG modifico i destinatari con quello di default: if (_parameters.DebugModeEnabled) { // Creo una nuova mail alla quale aggiungo come allegato la mail originale MailBuilder debugBuilder = new MailBuilder(); debugBuilder.From.Add(email.From.First()); debugBuilder.Subject = string.Format("Inoltro messaggio per DEBUG -> {0}", email.Subject); // Crea il corpo del messaggio di default (non è richiesto dall'Interoperabilità) o lo legge da base dati, se indicato debugBuilder.Text = "In allegato la mail che sarebbe stata spedita."; // Aggiungo il destinatario di debug debugBuilder.To.Add(new MailBox(_parameters.PecOutAddressDebugMode, "DEBUG ADDRESS")); // Aggiungo la mail come allegatos debugBuilder.AddAttachment(email); // Sostituisco item con il debugMail email = debugBuilder.Create(); FileLogger.Info(_loggerName, string.Format("Modificato l'indirizzo di invio della PEC con l'indirizzo {0}.", _parameters.PecOutAddressDebugMode)); } // Eseguo in modo atomico il blocco di invio int i = 0; bool sent = false; ISendMessageResult sendResult = null; Exception lastException = null; while (!sent && i < 5) { try { using (Smtp smtp = new Smtp()) { smtp.ServerCertificateValidate += ServerCertificateValidateHandler; // Utilizzo connessione SSL if (mailbox.OutgoingServerUseSsl.HasValue && mailbox.OutgoingServerUseSsl.Value) { smtp.SSLConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12; smtp.ConnectSSL(mailbox.OutgoingServerName, mailbox.OutgoingServerPort.HasValue ? mailbox.OutgoingServerPort.Value : 465); } else { smtp.Connect(mailbox.OutgoingServerName, mailbox.OutgoingServerPort.HasValue ? mailbox.OutgoingServerPort.Value : 25); } // Utilizzo autenticazione if (!string.IsNullOrEmpty(mailbox.Username) && !string.IsNullOrEmpty(password)) { smtp.UseBestLogin(mailbox.Username, password); } sendResult = smtp.SendMessage(email); sent = (sendResult.Status == SendMessageStatus.Success); if (!sent) { string errors = string.Empty; if (sendResult.GeneralErrors != null && sendResult.GeneralErrors.Any()) { errors = string.Join(", ", sendResult.GeneralErrors .Select(f => string.Concat("Code: ", f.Code, " - EnhancedStatusCode: ", f.EnhancedStatusCode, " - Message: ", f.Message, " - Status : ", f.Status))); } FileLogger.Error(_loggerName, string.Format("Errore in spedizione PEC {0} / casella {1} - {2}. Stato spedizione: {3} - Errori:{4}.", pecMail.Id, mailbox.Id, mailbox.MailBoxName, sendResult.Status, errors)); } smtp.Close(false); } if (!sent) { continue; } // Aggiorno immediatamente la PEC come spedita in modo da non avere dubbi che la PEC sia stata davvero spedita pecMail.MailDate = DateTime.Now; pecMail.XRiferimentoMessageID = string.Format("<{0}>", guid); _factory.PECMailFacade.UpdateOnly(ref pecMail); Protocol currentProtocol = _factory.PECMailFacade.GetProtocol(pecMail); if (currentProtocol != null && (short)ProtocolKind.FatturePA == currentProtocol.IdProtocolKind) { currentProtocol.IdStatus = (int)ProtocolStatusId.PAInvoiceSent; _factory.ProtocolFacade.UpdateOnly(ref currentProtocol); } } catch (Exception ex) { lastException = ex; // Se mi trovo in questo status è avvenuto un errore in fase di spedizione, per cui posso riprocedere ad inviare FileLogger.Error(_loggerName, string.Format("Non è stato possibile inviare la PEC {0} / casella {1} - {2} per un probabile errore di rete o di server PEC. La procedura verrà ritentata. - Tentativo {3}/5", pecMail.Id, mailbox.Id, mailbox.MailBoxName, i + 1), ex); // Attendo 1 minuto prima di riprovare #if !DEBUG Thread.Sleep(1000 * 30); #endif } finally { // Procedo i++; } } // Se dopo 5 tentativi ancora non ha ricevuto conferma di spedizione allora invio una mail e blocco l'invio if (sent) { _factory.PECMailboxLogFacade.SentMail(ref pecMail); return(true); } // Errori String errorMessages = string.Format("Errori di invio:{0}", Environment.NewLine); errorMessages = sendResult.GeneralErrors.Aggregate(errorMessages, (current, generalError) => current + string.Format("Code:{1} - Message:{2} - Status:{3}{0}", Environment.NewLine, generalError.Code, generalError.Message, generalError.Status)); _factory.PECMailboxLogFacade.SentErrorMail(ref pecMail, new Exception(errorMessages)); pecMail.IsActive = ActiveType.Cast(ActiveType.PECMailActiveType.Error); _factory.PECMailFacade.UpdateOnly(ref pecMail); String errorResult = string.Format("La PEC {0} / casella {1} - {2} non è stata spedita dopo 5 tentativi falliti. \nE' stata pertanto disattivata (isActive = 255) per evitare ulteriori tentativi.", pecMail.Id, mailbox.Id, mailbox.MailBoxName); _factory.PECMailLogFacade.Error(ref pecMail, errorResult); _factory.PECMailLogFacade.Error(ref pecMail, string.Concat(errorResult, errorMessages)); MailStoreFacade.SetPECMailTaskError(pecMail); throw new Exception(errorResult, lastException); }