private void processSpiraAccount(Pop3Client clientPOP3, AccountDetails account, ApplicationSystem appServer) { const string METHOD = CLASS + "processSpiraAccount()"; this._eventLog.EntryLog(METHOD); //Create the application client and connect to our project and get users.. SoapServiceClient clientAppl = (SoapServiceClient)this.CreateApplicationClient(appServer, account); //Get users in the project.. List <RemoteProjectUser> spiraUsers = clientAppl.Project_RetrieveUserMembership(); //Get the known message IDs.. List <string> seenUIDs = this.readMessageIDsForAccount(account.AccountID.Value); //Get new emails from the client. List <Message> newMsgs = this.popGetNewMessages(clientPOP3, account, seenUIDs); //Get all projects.. List <RemoteProject> spiraProjs = clientAppl.Project_Retrieve(); //Loop through each email. foreach (Message msg in newMsgs) { this._eventLog.WriteTrace(METHOD, "Starting on message " + msg.MessageLogID + "..."); //Make sure we have a from address, otherwise skip (Delivery Returned messages have no FROM address) //First see if the message should be skipped. (Keywords, Headers, or Email Addresses) if (msg.Headers != null && msg.Headers.From != null && !String.IsNullOrWhiteSpace(msg.Headers.From.Address) && msg.Headers.From.Address.ToLowerInvariant().Trim() != account.AccountEmail.ToLowerInvariant().Trim()) { string filterMsg; if (this.doesMessageClearFilters(msg, out filterMsg)) { //First see if there's a header we can get the artifact ID from.. ArtifactTypeEnum artType = ArtifactTypeEnum.None; int artId = -1; if (msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_SPIRA_ARTIFACT] != null && rgxArtifactToken.IsMatch(msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_SPIRA_ARTIFACT])) { //Get the art type and the id.. Match matches = rgxArtifactToken.Match(msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_SPIRA_ARTIFACT]); this.retrieveArtTypeAndId(matches.Groups["type"].Value, matches.Groups["id"].Value, out artType, out artId); } if (artId == -1 || artType == ArtifactTypeEnum.None) { if (rgxArtifactToken.IsMatch(msg.Headers.Subject)) { //Get the art type and the id.. Match matches = rgxArtifactToken.Match(msg.Headers.Subject); this.retrieveArtTypeAndId(matches.Groups["type"].Value, matches.Groups["id"].Value, out artType, out artId); } } //Change projects, if necessary, and if we're able to.. try { int projNum = clientAppl.System_GetProjectIdForArtifact((int)artType, artId); if (projNum != 0) { bool succNewProject = clientAppl.Connection_ConnectToProject(projNum); if (!succNewProject) { //Couldn't connect to the project this item belongs to. Throw an error. this._eventLog.WriteMessage("Message " + msg.MessageLogID + " contains information for a project [PR:" + projNum.ToString() + "] that the client could not connect to. Skipping.", System.Diagnostics.EventLogEntryType.Information); artType = ArtifactTypeEnum.Skip; artId = 0; } } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Message " + msg.MessageLogID + " - while trying to retrieve project number from artifact. Skipping."); } //If we have a match, find the user.. if (artId > 0 && artType != ArtifactTypeEnum.None) { //Detect if more than one user is found.. int userCnt = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Count(); if (userCnt == 1) { RemoteProjectUser selUser = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Single(); //See if the item exists in the server.. RemoteArtifact remArt = null; try { switch (artType) { case ArtifactTypeEnum.Requirement: remArt = clientAppl.Requirement_RetrieveById(artId); break; case ArtifactTypeEnum.Test_Case: remArt = clientAppl.TestCase_RetrieveById(artId); break; case ArtifactTypeEnum.Incident: remArt = clientAppl.Incident_RetrieveById(artId); break; case ArtifactTypeEnum.Release: remArt = clientAppl.Release_RetrieveById(artId); break; case ArtifactTypeEnum.Task: remArt = clientAppl.Task_RetrieveById(artId); break; case ArtifactTypeEnum.Test_Set: remArt = clientAppl.TestSet_RetrieveById(artId); break; } if (remArt == null) { throw new Exception("Artifact did not exist: " + artType.ToString() + " #" + artId.ToString()); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "For message " + msg.MessageLogID + ", referenced artifact did not exist."); continue; } try { //The artifact exists, let's add a comment.. RemoteComment comment = new RemoteComment(); comment.ArtifactId = artId; comment.CreationDate = DateTime.UtcNow; comment.UserId = selUser.UserId; comment.Text = this.getTextFromMessage(msg, true, true); switch (artType) { case ArtifactTypeEnum.Requirement: comment = clientAppl.Requirement_CreateComment(comment); break; case ArtifactTypeEnum.Test_Case: comment = clientAppl.TestCase_CreateComment(comment); break; case ArtifactTypeEnum.Incident: comment = clientAppl.Incident_AddComments(new List <RemoteComment>() { comment }).FirstOrDefault(); break; case ArtifactTypeEnum.Release: comment = clientAppl.Release_CreateComment(comment); break; case ArtifactTypeEnum.Task: comment = clientAppl.Task_CreateComment(comment); break; case ArtifactTypeEnum.Test_Set: comment = clientAppl.TestSet_CreateComment(comment); break; } if (comment != null && comment.CommentId.HasValue) { //Now check for attachments try { foreach (MessagePart attach in msg.FindAllAttachments().Where(aa => aa.IsMultiPart == false && aa.IsText == false)) { //Add the file.. RemoteLinkedArtifact artifactLink = new RemoteLinkedArtifact(); artifactLink.ArtifactId = comment.ArtifactId; artifactLink.ArtifactTypeId = (int)artType; RemoteDocument newDoc = new RemoteDocument(); newDoc.AttachedArtifacts = new List <RemoteLinkedArtifact>() { artifactLink }; newDoc.AttachmentTypeId = 1; newDoc.AuthorId = selUser.UserId; newDoc.FilenameOrUrl = attach.FileName; newDoc.UploadDate = DateTime.UtcNow; //Check for string overrun and add extension if necessary. if (newDoc.FilenameOrUrl.Length > 250) { newDoc.FilenameOrUrl = newDoc.FilenameOrUrl.Substring(0, 250); } if (string.IsNullOrWhiteSpace(Path.GetExtension(newDoc.FilenameOrUrl)) && attach.ContentType != null) { string tempFileExtension = Utils.GetExtensionFromMimeType(attach.ContentType.MediaType); if (!string.IsNullOrWhiteSpace(tempFileExtension)) { newDoc.FilenameOrUrl += "." + tempFileExtension; } } //Call the function to upload the file to Spira newDoc = clientAppl.Document_AddFile(newDoc, attach.Body); //Log it. this._eventLog.WriteMessage("Attachment #" + newDoc.AttachmentId.ToSafeString() + " created from file '" + attach.FileName + "' for Artifact " + artType + " #:" + comment.ArtifactId + " from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); } } catch (Exception ex) { //Log and continue this._eventLog.WriteMessage(METHOD, ex, "Saving attachment for message " + msg.MessageLogID); } } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "While trying to save message '" + msg.MessageLogID + "' as a new comment."); continue; } //If we get this far, mark the message as processed. try { //Add it to our list.. seenUIDs.Add(msg.MessageUID); if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } } else { string msgLog = ""; if (userCnt == 0) { msgLog = "Message " + msg.MessageLogID + " was sent from a user not a mamber of the specified project. Not importing unknown users."; } else if (userCnt > 1) { msgLog = "Message " + msg.MessageLogID + " was sent from a user that did not have a unique email address. Cannot import to avoid selecting the wrong user."; } this._eventLog.WriteMessage(msgLog, System.Diagnostics.EventLogEntryType.Information); } } else { if (artType != ArtifactTypeEnum.Skip) { int userCnt = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Count(); if (userCnt == 1) { RemoteProjectUser selUser = spiraUsers.Where(su => su.EmailAddress.ToLowerInvariant() == msg.Headers.From.MailAddress.Address.ToLowerInvariant()).Single(); //Create a new Incident.. RemoteIncident newIncident = new RemoteIncident(); newIncident.CreationDate = DateTime.Now; newIncident.Description = this.getTextFromMessage(msg, true, false); newIncident.Name = msg.Headers.Subject; newIncident.OpenerId = selUser.UserId; //Check regex other projects if (account.UseRegexToMatch) { Regex regProj1 = new Regex(@"\[PR[\:\-\s](\d*?)\]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (regProj1.IsMatch(newIncident.Description) || regProj1.IsMatch(newIncident.Name)) { //Someone used the PR tag in the email body, we'll use it, if possible. string matchNum = ""; if (regProj1.IsMatch(newIncident.Name)) { matchNum = regProj1.Matches(newIncident.Name)[0].Groups[1].Value; } else if (regProj1.IsMatch(newIncident.Description)) { matchNum = regProj1.Matches(newIncident.Description)[0].Groups[1].Value; } else { this._eventLog.WriteMessage("ERROR: At least one RegEx returned IsMatch, but none contained match.", System.Diagnostics.EventLogEntryType.Information); continue; } int projNum = 0; if (int.TryParse(matchNum, out projNum)) { //We had a number, let's see if it's a valid product. RemoteProject proj = spiraProjs.FirstOrDefault(prd => prd.ProjectId == projNum); if (proj != null) { //Connect to project.. if (clientAppl.Connection_ConnectToProject(proj.ProjectId.Value)) { newIncident.ProjectId = proj.ProjectId.Value; this._eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Project '" + proj.Name + "' due to having [PR:xx] tag.", System.Diagnostics.EventLogEntryType.Information); } else { this._eventLog.WriteMessage("Message " + msg.MessageLogID + " contained project token for project '" + proj.Name + "', but email import has no access to that project. Using default.", System.Diagnostics.EventLogEntryType.Information); } } else { this._eventLog.WriteMessage("Message '" + msg.MessageLogID + "' contained token for project " + projNum.ToString() + " but project was inaccessible. Using default.", System.Diagnostics.EventLogEntryType.Information); } } } else { foreach (RemoteProject prod in spiraProjs) { Regex regProd3 = new Regex(@"\b" + prod.Name + @"\b", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); if (regProd3.IsMatch(newIncident.Description) || regProd3.IsMatch(newIncident.Name)) { newIncident.ProjectId = prod.ProjectId.Value; this._eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Product '" + prod.Name + "' due to having Product name '" + prod.Name + "'", System.Diagnostics.EventLogEntryType.Information); break; } } } } //Now save the Incident.. try { newIncident = clientAppl.Incident_Create(newIncident); if (newIncident.IncidentId.HasValue) { //Now check for attachments try { foreach (MessagePart attach in msg.FindAllAttachments().Where(aa => aa.IsMultiPart == false && aa.IsText == false)) { //Add the file.. RemoteLinkedArtifact artifactLink = new RemoteLinkedArtifact(); artifactLink.ArtifactId = newIncident.IncidentId.Value; artifactLink.ArtifactTypeId = 3; /* Incident */ RemoteDocument newDoc = new RemoteDocument(); newDoc.AttachedArtifacts = new List <RemoteLinkedArtifact>() { artifactLink }; newDoc.AttachmentTypeId = 1; newDoc.AuthorId = selUser.UserId; newDoc.FilenameOrUrl = attach.FileName; newDoc.UploadDate = DateTime.UtcNow; //Check for string overrun and add extension if necessary. if (newDoc.FilenameOrUrl.Length > 250) { newDoc.FilenameOrUrl = newDoc.FilenameOrUrl.Substring(0, 250); } if (string.IsNullOrWhiteSpace(Path.GetExtension(newDoc.FilenameOrUrl)) && attach.ContentType != null) { string tempFileExtension = Utils.GetExtensionFromMimeType(attach.ContentType.MediaType); if (!string.IsNullOrWhiteSpace(tempFileExtension)) { newDoc.FilenameOrUrl += "." + tempFileExtension; } } //Call the function to upload the file to Spira newDoc = clientAppl.Document_AddFile(newDoc, attach.Body); //Log it. this._eventLog.WriteMessage("Attachment #" + newDoc.AttachmentId.ToSafeString() + " created from file '" + attach.FileName + "' for Incident IN:" + newIncident.IncidentId.Value + " from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); } } catch (Exception ex) { //Log and continue this._eventLog.WriteMessage(METHOD, ex, "Saving attachment for message " + msg.MessageLogID); } } //If we get this far, mark the message as processed. try { //Add it to our list.. seenUIDs.Add(msg.MessageUID); if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "While trying to save message '" + msg.MessageLogID + "' as an incident."); } } else { string msgLog = ""; if (userCnt == 0) { msgLog = "Message " + msg.MessageLogID + " was sent from a user not a mamber of the specified project. Not importing unknown users."; } else if (userCnt > 1) { msgLog = "Message " + msg.MessageLogID + " was sent from a user that did not have a unique email address. Cannot import to avoid selecting the wrong user."; } this._eventLog.WriteMessage(msgLog, System.Diagnostics.EventLogEntryType.Information); } } else { } } } else { //Log it.. this._eventLog.WriteMessage("Message " + msg.MessageLogID + " on the server did not pass filters:" + Environment.NewLine + "Subject: " + msg.Headers.Subject + Environment.NewLine + "Reasons: " + Environment.NewLine + filterMsg, System.Diagnostics.EventLogEntryType.Warning); } } else { //Log it.. this._eventLog.WriteMessage("Message " + msg.MessageLogID + " had no From address or was from the import account --" + Environment.NewLine + "Subject: " + msg.Headers.Subject + Environment.NewLine + "Msg UID: " + msg.Headers.MessageId + Environment.NewLine + "For reason: From Email address same as importing account, or was null.", System.Diagnostics.EventLogEntryType.Warning); } } //Save the seen message IDs.. this.saveMessageIDsForAccount(account.AccountID.Value, seenUIDs); //Disconnect client and POP3.. try { clientPOP3.Disconnect(); clientAppl.Connection_Disconnect(); } catch (Exception ex) { this._eventLog.WriteMessage(METHOD, ex, "Trying to close connections."); } }
private void processKronoAccount(Pop3Client clientPOP3, AccountDetails account, ApplicationSystem appServer) { const string METHOD = CLASS + "processKronoAccount()"; _eventLog.EntryLog(METHOD); //Create the application client and connect to our project and get users.. SoapServiceClient clientAppl = (SoapServiceClient)this.CreateApplicationClient(appServer, account); //Get the products and settings from Kronodesk. List <RemoteProduct> krnProds = clientAppl.Product_Retrieve(false); //Get list of blocked emails.. List <string> blockedEmails = clientAppl.System_GetBlockedEmails(); //Get list of seperator lines.. List <string> seperatorLines = clientAppl.System_GetEmailSeparator(); //Get the known message IDs.. List <string> seenUIDs = readMessageIDsForAccount(account.AccountID.Value); //Get new emails from the client. List <Message> newMsgs = popGetNewMessages(clientPOP3, account, seenUIDs); //Loop through each email. foreach (Message msg in newMsgs) { try { _eventLog.WriteTrace(METHOD, "Starting on message " + msg.MessageLogID + "..."); //First see if the message should be skipped. (Keywords, Headers, or Email Addresses) if (msg.Headers != null && msg.Headers.From != null && !string.IsNullOrWhiteSpace(msg.Headers.From.Address) && msg.Headers.From.Address.ToLowerInvariant().Trim() != account.AccountEmail.ToLowerInvariant().Trim()) { string filterMsg; //Run it though our own filters. if (doesMessageClearFilters(msg, out filterMsg, blockedEmails)) { long?tktId = null; //See if we can get a ticket ID.. if ((!tktId.HasValue) && msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_KRONO_TICKET] != null) { tktId = retrieveTicketId(msg.Headers.UnknownHeaders[Common.MESSAGEHEADER_KRONO_TICKET]); } if (!tktId.HasValue) { Regex rgx = new Regex(@"\[[a-zA-Z]{2}[\:-][0-9]+\]", RegexOptions.IgnoreCase | RegexOptions.Singleline); tktId = retrieveTicketId(rgx.Match(msg.Headers.Subject).Value); } //Get user that this email is from.. RemoteUser usrFrom = clientAppl.User_RetrieveByEmailAddress(msg.Headers.From.MailAddress.Address); //See if we have any CC users List <string> ccList = null; if (msg.Headers.Cc != null && msg.Headers.Cc.Count > 0) { ccList = msg.Headers.Cc.Select(c => c.MailAddress.Address).ToList(); } //Now process the email. RemoteTicket newTicket = null; RemoteTicketNote newTktNote = null; if (tktId.HasValue) { try { //The ticket exists, we're going to add the text as a new comment. //The flag on whether we need to make it a '3rd party' note. bool add3rd = (usrFrom == null); //If at least one user exists.. if (!add3rd) { //Create the note.. newTktNote = new RemoteTicketNote(); newTktNote.CreationDate = DateTime.UtcNow; newTktNote.AuthorId = usrFrom.UserId; newTktNote.TicketId = tktId.Value; newTktNote.Text = getTextFromMessage(msg, true, true, seperatorLines); try { newTktNote = clientAppl.Ticket_AddNote(newTktNote); } catch (Exception ex) { _eventLog.WriteMessage("Error adding note to message. Adding it as third-party."); add3rd = true; } if (!add3rd) { _eventLog.WriteMessage("Note #" + newTktNote.NoteId.ToSafeString() + " added to Ticket #" + newTktNote.TicketId.ToSafeString() + " created from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); //If we get this far, mark the message as processed. try { if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } //We processed the message, add it. seenUIDs.Add(msg.MessageUID); } } if (add3rd) { try { //An email was sent in from a third party. string strDescDetails = "Full Subject: " + msg.Headers.Subject + Environment.NewLine + "From Address: " + ((string.IsNullOrWhiteSpace(msg.Headers.From.Address)) ? "< empty >" : msg.Headers.From.Address) + Environment.NewLine + "From Name: " + ((string.IsNullOrWhiteSpace(msg.Headers.From.DisplayName)) ? "< empty >" : msg.Headers.From.DisplayName); RemoteDocument rawMsg = getMsgAsAttachment(msg, tktId.Value, null, "3rd Party Email:" + Environment.NewLine + strDescDetails); rawMsg = clientAppl.Document_AddFile(rawMsg, msg.RawMessage); //Add comment. newTktNote = new RemoteTicketNote(); newTktNote.CreationDate = DateTime.UtcNow; newTktNote.TicketId = tktId.Value; newTktNote.Text = "An email for this ticket was recieved from a third unknown party, and has been attached to this ticket as a file. Details:" + Environment.NewLine + "<pre>" + strDescDetails + Environment.NewLine + "Attachment: " + rawMsg.FilenameOrUrl + "</pre>"; newTktNote.Text = newTktNote.Text.Replace(Environment.NewLine, "<br />"); //Convert to HTML. newTktNote = clientAppl.Ticket_AddNote(newTktNote); _eventLog.WriteMessage("Message from 3rd party to ticket. Message " + msg.MessageLogID + " saved as Attachment #" + rawMsg.AttachmentId.ToSafeString() + " & Note #" + newTktNote.NoteId + " added.", System.Diagnostics.EventLogEntryType.Information); //If we get this far, mark the message as processed. try { if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } //We processed the message, add it. seenUIDs.Add(msg.MessageUID); } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Error adding 3rd party email to ticket from message " + msg.MessageLogID); } } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Error adding note to ticket from message " + msg.MessageLogID + "."); break; } } else { try { //Create the fake user first.. if (usrFrom == null) { RemoteUser newUser = new RemoteUser(); newUser.Active = true; newUser.Approved = false; newUser.EmailAddress = msg.Headers.From.MailAddress.Address; newUser.Login = msg.Headers.From.MailAddress.Address; Dictionary <string, string> names = getFullNameFromAddress(msg.Headers.From); newUser.FirstName = names["first"]; newUser.LastName = names["last"]; //Check for too-long strings.. if (newUser.FirstName.Length > 49) { newUser.FirstName = newUser.FirstName.Substring(0, 49); } if (newUser.LastName.Length > 49) { newUser.LastName = newUser.LastName.Substring(0, 49); } if (newUser.Login.Length > 49) { newUser.Login = newUser.Login.Substring(0, 49); } if (newUser.EmailAddress.Length > 49) { newUser.EmailAddress = newUser.EmailAddress.Substring(0, 49); } usrFrom = clientAppl.User_Create(newUser, "abcdefghijkLMNOPQ1", "Registration Code", Membership.GeneratePassword(10, 0), new List <string>()); _eventLog.WriteMessage("Temporary user '" + newUser.Login + "' created, user ID#" + usrFrom.UserId.ToSafeString(), System.Diagnostics.EventLogEntryType.Information); } //Need to create a new ticket. newTicket = new RemoteTicket(); newTicket.CreationDate = DateTime.UtcNow; newTicket.Name = msg.Headers.Subject; newTicket.Description = getTextFromMessage(msg, true); newTicket.OpenerId = usrFrom.UserId; newTicket.ProductId = account.ProductOrProjectId.Value; //Add any CC users newTicket.CCList = ccList; //Add the source newTicket.SourceId = -2; //Email //Check regex other products if (account.UseRegexToMatch) { Regex regProd1 = new Regex(@"\[PR[\:\-\s](\d*?)\]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); if (regProd1.IsMatch(newTicket.Description) || regProd1.IsMatch(newTicket.Name)) { //Someone used the PR tag in the email body, we'll use it, if possible. string matchNum = ""; if (regProd1.IsMatch(newTicket.Name)) { matchNum = regProd1.Matches(newTicket.Name)[0].Groups[1].Value; } else if (regProd1.IsMatch(newTicket.Description)) { matchNum = regProd1.Matches(newTicket.Description)[0].Groups[1].Value; } else { _eventLog.WriteMessage("ERROR: At least one RegEx returned IsMatch, but none contained match.", System.Diagnostics.EventLogEntryType.Information); continue; } int prodNum = 0; if (int.TryParse(matchNum, out prodNum)) { //We had a number, let's see if it's a valid product. RemoteProduct prod = krnProds.Where(prd => prd.ProductId == prodNum).SingleOrDefault(); if (prod != null) { newTicket.ProductId = (int)prod.ProductId.Value; _eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Product '" + prod.Name + "' due to having [PR:xx] tag.", System.Diagnostics.EventLogEntryType.Information); } } } else { bool foundToken = false; foreach (RemoteProduct prod in krnProds) { if (!string.IsNullOrWhiteSpace(prod.ProductToken)) { Regex regProd2 = new Regex(@"\b" + prod.ProductToken + @"\b", RegexOptions.CultureInvariant | RegexOptions.Compiled); if (regProd2.IsMatch(newTicket.Description) || regProd2.IsMatch(newTicket.Name)) { newTicket.ProductId = (int)prod.ProductId.Value; foundToken = true; _eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Product '" + prod.Name + "' due to having Product token '" + prod.ProductToken + "'", System.Diagnostics.EventLogEntryType.Information); break; } } } if (!foundToken) { foreach (RemoteProduct prod in krnProds) { if (!string.IsNullOrWhiteSpace(prod.Name)) { Regex regProd3 = new Regex(@"\b" + prod.Name + @"\b", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); if (regProd3.IsMatch(newTicket.Description) || regProd3.IsMatch(newTicket.Name)) { newTicket.ProductId = (int)prod.ProductId.Value; _eventLog.WriteMessage("Message " + msg.MessageLogID + " changed to Product '" + prod.Name + "' due to having Product name '" + prod.Name + "'", System.Diagnostics.EventLogEntryType.Information); break; } } } } } } //Check for string overrun.. if (newTicket.Name.Length > 254) { newTicket.Name = newTicket.Name.Substring(0, 254); } //Check for null (or empty) Name.. if (string.IsNullOrWhiteSpace(newTicket.Name)) { newTicket.Name = "(no subject)"; } newTicket = clientAppl.Ticket_Create(newTicket); tktId = newTicket.TicketId; _eventLog.WriteMessage("Ticket #" + newTicket.TicketId.ToSafeString() + " created from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); //If we get this far, mark the message as processed. try { //Add it to our list.. seenUIDs.Add(msg.MessageUID); if (account.RemoveFromServer) { clientPOP3.DeleteMessage(msg.MessageIndex); } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Trying to delete message " + msg.MessageLogID + " from server."); } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Creating new ticket/user for message " + msg.MessageLogID); continue; } } //Check for attachments now.. if (tktId.HasValue) { //The list of contentID's mapped to URLs.. Dictionary <string, string> mappedURLs = new Dictionary <string, string>(); try { //Get existing attachments and add them to the list. List <RemoteDocument> tktDocs = clientAppl.Document_GetForArtifact(6, tktId.Value, true); Dictionary <string, RemoteDocument> savedHashes = new Dictionary <string, RemoteDocument>(); foreach (RemoteDocument doc in tktDocs) { string hash = System.Text.ASCIIEncoding.ASCII.GetString(doc.Hash).Trim(); if (!savedHashes.ContainsKey(hash)) { savedHashes.Add(hash.Trim(), doc); } } foreach (MessagePart attach in msg.FindAllAttachments().Where(aa => aa.IsMultiPart == false && aa.IsText == false)) { //First get the MD5 of the attachment.. string attHash; using (var md5 = new MD5CryptoServiceProvider()) attHash = System.Text.ASCIIEncoding.ASCII.GetString(md5.ComputeHash(attach.Body)).Trim(); //Make sure the hash dosen't already exist.. bool doesExist = false; if (savedHashes.ContainsKey(attHash)) { //It exists, so se tthe flag and add the URL and Id to our dictionary.. doesExist = true; if (!mappedURLs.ContainsKey(attach.ContentId)) { mappedURLs.Add(attach.ContentId, savedHashes[attHash].DocumentURL); } } if (!doesExist) { //Add the file.. RemoteDocument newDoc = new RemoteDocument(); newDoc.ArtifactId = tktId.Value; newDoc.ArtifactTypeId = 6; newDoc.AttachmentTypeId = 1; newDoc.AuthorId = usrFrom.UserId; newDoc.FilenameOrUrl = attach.FileName; newDoc.UploadDate = DateTime.UtcNow; //Check for string overrun and add extension if necessary. if (newDoc.FilenameOrUrl.Length > 250) { newDoc.FilenameOrUrl = newDoc.FilenameOrUrl.Substring(0, 250); } if (string.IsNullOrWhiteSpace(Path.GetExtension(newDoc.FilenameOrUrl)) && attach.ContentType != null) { string tempFileExtension = clientAppl.System_GetExtensionFromMimeType(attach.ContentType.MediaType); if (!string.IsNullOrWhiteSpace(tempFileExtension)) { newDoc.FilenameOrUrl += "." + tempFileExtension; } } //Call the function and update mappings.. newDoc = clientAppl.Document_AddFile(newDoc, attach.Body); savedHashes.Add(attHash, newDoc); if (!string.IsNullOrWhiteSpace(attach.ContentId)) { mappedURLs.Add(attach.ContentId, newDoc.DocumentURL); } //Loggit. _eventLog.WriteMessage("Attachment #" + newDoc.AttachmentId.ToSafeString() + " created from file '" + attach.FileName + "' for Ticket #" + tktId.ToSafeString() + " from message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Information); } else { _eventLog.WriteMessage("Attachment file '" + attach.FileName + "' not uploaded from message " + msg.MessageLogID + ", hash already exists in Ticket #" + tktId.ToSafeString() + ".", System.Diagnostics.EventLogEntryType.Information); } } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Saving attachment for message " + msg.MessageLogID); break; } //If the flag's set, get the entire message.. if (account.SaveRawEmailAsAttachment) { try { RemoteDocument rawMsg = getMsgAsAttachment(msg, tktId.Value, usrFrom.UserId, null); rawMsg = clientAppl.Document_AddFile(rawMsg, msg.RawMessage); _eventLog.WriteMessage("Message " + msg.MessageLogID + " saved as Attachment #" + rawMsg.AttachmentId.ToSafeString() + ".", System.Diagnostics.EventLogEntryType.Information); } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Saving message " + msg.MessageLogID + " as attachment:"); break; } } //Now loop through each saved attachment, and update URLs.. bool needsTktUpdating = false; bool needsNoteUpdating = false; foreach (KeyValuePair <string, string> kvpURL in mappedURLs) { //See if we need to updat ethe note or the ticket.. if (newTicket == null && newTktNote != null) { if (newTktNote.Text.Contains(kvpURL.Key)) { newTktNote.Text = newTktNote.Text.Replace("cid:" + kvpURL.Key, kvpURL.Value); needsNoteUpdating = true; } } else if (newTicket != null && newTktNote == null) { if (newTicket.Description.Contains(kvpURL.Key)) { newTicket.Description = newTicket.Description.Replace("cid:" + kvpURL.Key, kvpURL.Value); needsTktUpdating = true; } } else { //Log warning message here. _eventLog.WriteMessage("Both newTicket and newTktNote were null or not null while processing message " + msg.MessageLogID + ".", System.Diagnostics.EventLogEntryType.Warning); } } if (needsTktUpdating) { try { //The date is set to 4/26/1976 so that the KronoDesk code knows not to record history changes and send out another notification. //All we're doing is converting the description URLs for the newly-uploaded attachments. _eventLog.WriteTrace(METHOD, "Updating ticket description with attachment URLs for message " + msg.MessageLogID + "."); newTicket.LastUpdateDate = new DateTime(1976, 4, 26); clientAppl.Ticket_Update(newTicket); } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Trying to update ticket description for message " + msg.MessageLogID + "."); } } else if (needsNoteUpdating) { try { _eventLog.WriteTrace(METHOD, "Updating note text with attachment URLs for message " + msg.MessageLogID + "."); clientAppl.Ticket_UpdateNote(newTktNote); } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "Trying to update note text for message " + msg.MessageLogID + "."); } } } } else { //Log it.. _eventLog.WriteMessage("Message " + msg.MessageLogID + " on the server did not pass filters:" + Environment.NewLine + "Subject: " + msg.Headers.Subject + Environment.NewLine + "Reasons: " + Environment.NewLine + filterMsg, System.Diagnostics.EventLogEntryType.Warning); } } else { //Log it.. _eventLog.WriteMessage("Message " + msg.MessageLogID + " had no From address or was from the import account --" + Environment.NewLine + "Subject: " + msg.Headers.Subject + Environment.NewLine + "Msg UID: " + msg.Headers.MessageId + Environment.NewLine + "For reason: From Email address same as importing account, or was null.", System.Diagnostics.EventLogEntryType.Warning); } } catch (Exception ex) { _eventLog.WriteMessage(METHOD, ex, "While importing message " + msg.MessageLogID); } } //Save the seen message IDs first, in case there's a problem disconnecting. saveMessageIDsForAccount(account.AccountID.Value, seenUIDs); //Disconnect client and POP3.. try { clientPOP3.Disconnect(); clientAppl.Connection_Disconnect(); } catch (Exception ex) { _eventLog.WriteMessage("Trying to disconnect from server.", ex); } }