/// <summary> /// Reads the mail. /// </summary> public void ReadMail() { Pop3MimeClient mailClient = new Pop3MimeClient(_Server, _Port, _UseSSL, _Username, _Password); try { mailClient.Connect(); List<int> messageIds; mailClient.GetEmailIdList(out messageIds); for (int i = 0; i < messageIds.Count; i++) { bool messageWasProcessed = false; RxMailMessage message; if (mailClient.GetEmail(messageIds[i], out message)) { string messageFrom = string.Empty; if (message.From.Address.Length > 0) { messageFrom = message.From.Address; } List<string> recipients = new List<string>(); foreach (MailAddress address in message.To) recipients.Add(address.Address); foreach (MailAddress address in message.CC) recipients.Add(address.Address); foreach (MailAddress address in message.Bcc) recipients.Add(address.Address); foreach (string mailbox in recipients) { ProjectMailbox pmbox = ProjectMailboxManager.GetByMailbox(mailbox); if (pmbox != null) { MailboxEntry entry = new MailboxEntry(); Project project = ProjectManager.GetById(pmbox.ProjectId); //TODO: Enhancements could include regex / string matching or not matching //for particular strings values in the subject or body. entry.Title = message.Subject.Trim(); entry.From = messageFrom; entry.ProjectMailbox = pmbox; entry.Date = message.DeliveryDate; if (message.Entities.Count > 0) { //find if there is an html version. if (message.Entities.FindAll(m => m.IsBodyHtml).Count > 0) { List<RxMailMessage> htmlMessages = message.Entities.FindAll(m => m.IsBodyHtml); if (htmlMessages.Count > 0) message = htmlMessages[0]; } } if (!message.IsBodyHtml) entry.Content.Append(message.Body.Replace("\n", "<br />")); else { // Strip the <body> out of the message (using code from below) Regex bodyExtractor = new Regex("<body.*?>(?<content>.*)</body>", RegexOptions.IgnoreCase | RegexOptions.Singleline); Match match = bodyExtractor.Match(message.Body); if (match != null && match.Success && match.Groups["content"] != null) { entry.Content.Append(match.Groups["content"].Value); } else { entry.Content.Append(message.Body); } } if (_ProcessAttachments) { foreach (Attachment attachment in message.Attachments) { if (attachment.ContentStream != null && attachment.ContentDisposition.FileName != null && attachment.ContentDisposition.FileName.Length > 0) { entry.MailAttachments.Add(attachment); } } } //save this message the mark the message as processed SaveMailboxEntry(entry); messageWasProcessed = true; //if delete all messages AND the message was processed delete the message from the server. if (_DeleteAllMessages && messageWasProcessed) mailClient.DeleteEmail(i); } else { if (Log.IsWarnEnabled) Log.WarnFormat("Project Mailbox Not Found: {0}", message.To.ToString()); } } } } } catch (Exception ex) { if (Log.IsErrorEnabled) Log.Error("Mailbox Reader Error", ex); } finally { try { mailClient.Disconnect(); } catch { } } }
/// <summary> /// Saves the mailbox entry. /// </summary> /// <param name="entry">The entry to be saved</param> Issue SaveMailboxEntry(MailboxEntry entry) { try { //load template var body = string.Format("<div >Sent by:{1} on: {2}<br/>{0}</div>", entry.Content.Trim(), entry.From, entry.Date); if (Config.BodyTemplate.Trim().Length > 0) { var data = new Dictionary<string, object> { { "MailboxEntry", entry } }; body = NotificationManager.GenerateNotificationContent(Config.BodyTemplate, data); } var projectId = entry.ProjectMailbox.ProjectId; // try to find if the creator is valid user in the project, otherwise take // the user defined in the mailbox config var creator = Config.ReportingUserName; var users = UserManager.GetUsersByProjectId(projectId); var emails = entry.From.Split(';').Select(e => e.Trim().ToLower()); var user = users.Find(x => emails.Contains(x.Email.ToLower())); if (user != null) creator = user.UserName; var mailIssue = IssueManager.GetDefaultIssueByProjectId( projectId, entry.Title.Trim(), body.Trim(), entry.ProjectMailbox.IssueTypeId, entry.ProjectMailbox.AssignToUserName, creator); if (entry.ProjectMailbox.CategoryId != 0) { // overwrite default category with mailbox category mailIssue.CategoryId = entry.ProjectMailbox.CategoryId; } if (!IssueManager.SaveOrUpdate(mailIssue)) return null; entry.IssueId = mailIssue.Id; entry.WasProcessed = true; var project = ProjectManager.GetById(projectId); var projectFolderPath = Path.Combine(Config.UploadsFolderPath, project.UploadPath); var doc = new HtmlDocument(); doc.LoadHtml(mailIssue.Description); // load the issue body to we can process it for inline images (if exist) //If there is an attached file present then add it to the database //and copy it to the directory specified in the web.config file foreach (MIME_Entity mimeEntity in entry.MailAttachments) { string fileName; var isInline = false; var contentType = mimeEntity.ContentType.Type.ToLower(); var attachment = new IssueAttachment { Id = 0, Description = "File attached by mailbox reader", DateCreated = DateTime.Now, ContentType = mimeEntity.ContentType.TypeWithSubtype, CreatorDisplayName = Config.ReportingUserName, CreatorUserName = Config.ReportingUserName, IssueId = mailIssue.Id, ProjectFolderPath = projectFolderPath }; switch (contentType) { case "application": attachment.Attachment = ((MIME_b_SinglepartBase)mimeEntity.Body).Data; break; case "attachment": case "image": case "video": case "audio": attachment.Attachment = ((MIME_b_SinglepartBase)mimeEntity.Body).Data; break; case "message": // we need to pull the actual email message out of the entity, and strip the "content type" out so that // email programs will read the file properly var messageBody = mimeEntity.ToString().Replace(mimeEntity.Header.ToString(), ""); if (messageBody.StartsWith("\r\n")) { messageBody = messageBody.Substring(2); } attachment.Attachment = Encoding.UTF8.GetBytes(messageBody); break; default: LogWarning(string.Format("MailboxReader: Attachment type could not be processed {0}", mimeEntity.ContentType.Type)); break; } if (contentType.Equals("attachment")) // this is an attached email { fileName = mimeEntity.ContentDisposition.Param_FileName; } else if (contentType.Equals("message")) // message has no filename so we create one { fileName = string.Format("Attached_Message_{0}.eml", entry.AttachmentsSavedCount); } else { isInline = true; fileName = string.IsNullOrWhiteSpace(mimeEntity.ContentType.Param_Name) ? string.Format("untitled.{0}", mimeEntity.ContentType.SubType) : mimeEntity.ContentType.Param_Name; } attachment.FileName = fileName; var saveFile = IsAllowedFileExtension(fileName); var fileSaved = false; // can we save the file? if (saveFile) { fileSaved = IssueAttachmentManager.SaveOrUpdate(attachment); if (fileSaved) { entry.AttachmentsSavedCount++; } else { LogWarning("MailboxReader: Attachment could not be saved, please see previous logs"); } } if (!entry.IsHtml || !isInline) continue; if (string.IsNullOrWhiteSpace(mimeEntity.ContentID)) continue; var contentId = mimeEntity.ContentID.Replace("<", "").Replace(">", "").Replace("[", "").Replace("]", ""); // this is pretty greedy but since people might be sending screenshots I doubt they will send in dozens of images // embedded in the email. one would hope foreach (var node in doc.DocumentNode.SelectNodes(XpathElementCaseInsensitive("img")).ToList()) { var attr = node.Attributes.FirstOrDefault(p => p.Name.ToLowerInvariant() == "src");// get the src attribute if (attr == null) continue; // image has no src attribute if (!attr.Value.Contains(contentId)) continue; // is the attribute value the content id? // swap out the content of the parent node html will our link to the image var anchor = string.Format("<span class='inline-mail-attachment'>Inline Attachment: <a href='DownloadAttachment.axd?id={0}' target='_blank'>{1}</a></span>", attachment.Id, fileName); // for each image in the body if the file was saved swap out the inline link for a link to the saved attachment // otherwise blank out the content link so we don't get a missing image link node.ParentNode.InnerHtml = fileSaved ? anchor : ""; } mailIssue.Description = doc.DocumentNode.InnerHtml; mailIssue.LastUpdateUserName = mailIssue.OwnerUserName; mailIssue.LastUpdate = DateTime.Now; IssueManager.SaveOrUpdate(mailIssue); } return mailIssue; } catch (Exception ex) { LogException(ex); throw; } }
/// <summary> /// Saves the mailbox entry. /// </summary> /// <param name="entry">The entry.</param> public void SaveMailboxEntry(MailboxEntry entry) { try { // TODO Should use XSLT templates BGN-1591 string body = string.Format(this._BodyTemplate, entry.Content.ToString().Trim(), entry.From, entry.Date.ToString()); int projectId = entry.ProjectMailbox.ProjectId; var mailIssue = IssueManager.GetDefaultIssueByProjectId(projectId, entry.Title.Trim(), body.Trim(), entry.ProjectMailbox.AssignToUserName, this._ReportingUserName); if (IssueManager.SaveOrUpdate(mailIssue)) { //If there is an attached file present then add it to the database //and copy it to the directory specified in the web.config file foreach (Attachment attMail in entry.MailAttachments) { var attachmentBytes = new byte[attMail.ContentStream.Length]; ReadWholeArray(attMail.ContentStream, attachmentBytes); var attachment = new IssueAttachment() { Id = 0, Attachment = attachmentBytes, Description = "Attached via email", DateCreated = DateTime.Now, ContentType = attMail.ContentType.ToString(), CreatorDisplayName = _ReportingUserName, CreatorUserName = _ReportingUserName, FileName = attMail.ContentDisposition.FileName, IssueId = mailIssue.Id, Size = attachmentBytes.Length }; if (!IssueAttachmentManager.SaveOrUpdate(attachment)) if (Log.IsWarnEnabled) Log.Warn("Attachment was not added via mailbox reader"); } } } catch (Exception ex) { throw ProcessException(ex); } }
private bool ProcessNewIssue(List<string> recipients, POP3_ClientMessage message, Mail_Message mailHeader, IList<Project> projects, MailboxReaderResult result) { var messageFrom = string.Empty; if (mailHeader.From.Count > 0) { messageFrom = string.Join("; ", mailHeader.From.ToList().Select(p => p.Address).ToArray()).Trim(); } bool processed = false; // loop through the mailboxes foreach (var address in recipients) { var pmbox = ProjectMailboxManager.GetByMailbox(address); // cannot find the mailbox skip the rest if (pmbox == null) { LogWarning(string.Format("MailboxReader: could not find project mailbox: {0} skipping.", address)); continue; } var project = projects.FirstOrDefault(p => p.Id == pmbox.ProjectId); if (project == null) { project = ProjectManager.GetById(pmbox.ProjectId); // project is disabled skip if (project.Disabled) { LogWarning(string.Format("MailboxReader: Project {0} - {1} is flagged as disabled skipping.", project.Id, project.Code)); continue; } projects.Add(project); } var entry = new MailboxEntry { Title = mailHeader.Subject.Trim(), From = messageFrom, ProjectMailbox = pmbox, Date = mailHeader.Date, Project = project, Content = "Email Body could not be parsed." }; var mailbody = Mail_Message.ParseFromByte(message.MessageToByte()); bool isHtml; List<MIME_Entity> attachments = null; string content = GetMessageContent(mailbody, project, out isHtml, ref attachments); entry.Content = content; entry.IsHtml = isHtml; foreach (var attachment in attachments) { entry.MailAttachments.Add(attachment); } //save this message Issue issue = SaveMailboxEntry(entry); //send notifications for the new issue SendNotifications(issue); // add the entry if the save did not throw any exceptions result.MailboxEntries.Add(entry); processed = true; } return processed; }