private void HandleMessageEvent(Object mailItem) { try { SignMailItem((MailItem)mailItem); } catch (Exception ex) { Logger.LogError("Signing a mail item according to DKIM failed with an exception. Check the logged exception for details.\n" + ex); } finally { #if !EX_2007_SP3 //not supported in Exchange 2007 // This allows Transport poison detection to correclty handle this message // if there is a crash on this thread. agentAsyncContext.Resume(); #endif agentAsyncContext.Complete(); agentAsyncContext = null; } }
private void OnOnSubmittedMessage(SubmittedMessageEventSource source, QueuedMessageEventArgs queuedMessageEventArgs) { lock (_fileLock) { AgentAsyncContext agentAsyncContext = null; try { var mailItem = queuedMessageEventArgs.MailItem; agentAsyncContext = GetAgentAsyncContext(); // check the sender whitelist if (_exchangeAttachmentFilterConfig.SendersWhitelist.Any( f => Regex.IsMatch(mailItem.FromAddress.ToString(), WildcardToRegex(f)))) { return; } // maybe we will need list of recipients on single line... var recipientList = new StringBuilder(); for (var i = 0; i < mailItem.Recipients.Count; i++) { recipientList.Append(i == 0 ? mailItem.Recipients[i].Address.ToString() : "; " + mailItem.Recipients[i].Address); } var removedAttachments = new List <Attachment>(); var strippedAttachments = new List <Attachment>(); var messageRejected = false; var messageLogStringBuilder = new SysLogBuilder(); var mailItemStatusText = $"[from: {mailItem.FromAddress}] [to: {recipientList}] [method: {mailItem.InboundDeliveryMethod}] [subject: {mailItem.Message.Subject}] [size: {mailItem.MimeStreamLength.ToString("N0")}]"; messageLogStringBuilder.Log(mailItemStatusText); if (mailItem.Message.Attachments.Count == 0 && _exchangeAttachmentFilterConfig.LogAccepted) { messageLogStringBuilder.LogPadded( "ACCEPTED: [reason: no attachments]"); } else { foreach (var attachment in mailItem.Message.Attachments) { // It would be great idea to process only attachments with size greater // than some threshold, 'cos infected files are always quite small (only few kB) // But I am not sure how to get the attachment size here, ... // if (any previous) attachment rejected the message then break the processing now if (messageRejected) { break; } AttachmentFilterStatus attachmentStatus = null; if (_exchangeAttachmentFilterConfig.DsnStripOriginalMessage) { // DSN has InboundDeliveryMethod equal to DeliveryMethod.File and FromAddress is equal to <> // and DSN's original message is included as message/rfc822 attachment if (mailItem.InboundDeliveryMethod == DeliveryMethod.File && mailItem.FromAddress.ToString() == "<>" && attachment.ContentType.ToLower().Equals("message/rfc822")) { attachmentStatus = new AttachmentFilterStatus(AttachmentFilterStatusEnum.StripAttachment, "DSN original message"); } } if (attachmentStatus == null) { // default file status (by extension) attachmentStatus = FilenameFilterStatus(attachment.FileName); // is it archive? if (_exchangeAttachmentFilterConfig.ScanArchives && IsArchive(attachment.FileName)) { var archiveStatus = ProcessArchiveStream(attachment.GetContentReadStream()); if (archiveStatus.Status > attachmentStatus.Status) { attachmentStatus = archiveStatus; } } // is it OpenXml document? if (_exchangeAttachmentFilterConfig.ScanOpenXmlDocuments && IsOpenXmlDocument(attachment.FileName)) { var openXmlDocumentStatus = ProcessOpenXmlDocumentStream(attachment.GetContentReadStream()); if (openXmlDocumentStatus.Status > attachmentStatus.Status) { attachmentStatus = openXmlDocumentStatus; } } } var attachmentStatusText = $"[file: {attachment.FileName}] [type: {attachment.AttachmentType}] [content type:{attachment.ContentType}] [reason: {attachmentStatus.Reason}]"; switch (attachmentStatus.Status) { case AttachmentFilterStatusEnum.Accept: if (_exchangeAttachmentFilterConfig.LogAccepted) { messageLogStringBuilder.LogPadded($"ACCEPTED: {attachmentStatusText}"); } break; case AttachmentFilterStatusEnum.RemoveAttachment: // just mark this attachment for removement, but do not touch attachments collection now // (we are in foreach loop and need to process them all) removedAttachments.Add(attachment); if (_exchangeAttachmentFilterConfig.LogRejectedOrRemoved) { messageLogStringBuilder.LogPadded($"REMOVED: {attachmentStatusText}"); } break; case AttachmentFilterStatusEnum.StripAttachment: // just mark this attachment for removement, but do not touch attachments collection now // (we are in foreach loop and need to process them all) strippedAttachments.Add(attachment); if (_exchangeAttachmentFilterConfig.LogRejectedOrRemoved) { messageLogStringBuilder.LogPadded($"STRIPPED: {attachmentStatusText}"); } break; case AttachmentFilterStatusEnum.RejectMessage: // reject whole message if (_exchangeAttachmentFilterConfig.LogRejectedOrRemoved) { messageLogStringBuilder.LogPadded($"REJECTED: {attachmentStatusText}"); } messageRejected = true; break; } } } if (messageLogStringBuilder.MessageCount > 1) { SysLog.Log(messageLogStringBuilder); } // reject the message? if (messageRejected) { // delete the source message and do nothing more (we do not send DSN)... source.Delete(); return; } // for every attachment we marked as being removed create new txt attachment with some info why it happened... foreach (var removedAttachment in removedAttachments) { // new attachment filename var newFileName = $"{_exchangeAttachmentFilterConfig.RemovedAttachmentPrefix}{removedAttachment.FileName}.txt"; // add new attachment to the message... var newAttachment = mailItem.Message.Attachments.Add(newFileName); // ...and write content into it (info message) var newAttachmentWriter = new StreamWriter(newAttachment.GetContentWriteStream()); newAttachmentWriter.WriteLine(removedAttachment.FileName); newAttachmentWriter.WriteLine(); newAttachmentWriter.WriteLine(_exchangeAttachmentFilterConfig.RemovedAttachmentNewContent); newAttachmentWriter.Flush(); newAttachmentWriter.Close(); } // finally remove all attachments marked for removal foreach (var removedAttachment in removedAttachments) { mailItem.Message.Attachments.Remove(removedAttachment); } // ...and stripped attachments, too foreach (var strippedAttachment in strippedAttachments) { mailItem.Message.Attachments.Remove(strippedAttachment); } } catch (IOException ex) { SysLog.Log("IOException: " + ex.Message); } catch (Exception ex) { SysLog.Log("Exception: " + ex.Message); } finally { agentAsyncContext?.Complete(); } } }
void RvScopiaMeeting_OnResolvedMessage(ResolvedMessageEventSource source, QueuedMessageEventArgs args){ this.emailMessage = args.MailItem.Message; if (this.emailMessage == null || this.emailMessage.TnefPart == null) { return; } long now = DateTime.UtcNow.Ticks; SchedulingInfo schedulingInfo = new SchedulingInfo(); schedulingInfo.subject = args.MailItem.Message.Subject; schedulingInfo.delegatorEmailAddr = args.MailItem.Message.From.NativeAddress; RvLogger.DebugWrite("Enter transport agent, from: " + schedulingInfo.delegatorEmailAddr + ", subject: " + schedulingInfo.subject); try { this.agentAsyncContext = this.GetAgentAsyncContext(); schedulingInfo.requestType = this.getRequestType(this.emailMessage.MapiMessageClass); //Reject all meeting type except request and cancel. if (RequestType.Other == schedulingInfo.requestType) { RvLogger.DebugWrite("Reject other request type: " + this.emailMessage.MapiMessageClass); return; } RvMailParser parser = new RvMailParser(this); try { parser.parseTnefSimple(args.MailItem, schedulingInfo); } catch (Exception exceptionParseMail) { RvLogger.DebugWrite("Fail to parse mail."); RvLogger.DebugWrite(exceptionParseMail.Message); RvLogger.DebugWrite(exceptionParseMail.StackTrace); return; } //Reject forwarded appointment if (!string.IsNullOrEmpty(schedulingInfo.subjectPrefix) && "FW:".Equals(schedulingInfo.subjectPrefix)) { RvLogger.DebugWrite("Reject forward request type"); return; } if (schedulingInfo.recurrencePattern != null){ schedulingInfo.recurrenceHashInfo = RvScopiaMeeting.getHashString4Str(schedulingInfo.recurrencePattern.getStringForHash()); schedulingInfo.recurrencePattern.startDate = schedulingInfo.startDate; schedulingInfo.recurrencePattern.endDate = schedulingInfo.endDate; } if (null == schedulingInfo.emailMessage) { RvLogger.DebugWrite("null == schedulingInfo.emailMessage================================================"); return; } if (!isScopia(schedulingInfo)) { RvLogger.DebugWrite("This is not a SCOPIA meeting"); return; } parseRecipentsChanged(schedulingInfo); if (schedulingInfo.isRecipentsChanged) if (schedulingInfo.requestType == RequestType.CancelMeeting) { schedulingInfo.requestType = RequestType.CreateMeeting; schedulingInfo.isAddRecipents = false; schedulingInfo.subject = schedulingInfo.subject.Substring(schedulingInfo.subjectPrefix.Length + 1); Thread.Sleep(HOW_MANY_SECONDS_WAIT_FOR_FOLLOWING_REQUEST * 1000); }else schedulingInfo.isAddRecipents = true; if (RvScopiaMeeting.SERVER_ACCOUNT.Equals(schedulingInfo.senderEmailAddr)) { RvLogger.DebugWrite("Send a email back to notify the sender this mail is failed to send out."); return; } //when modify a recurrence, to make sure the modified ocurrence request is later than the recurrence request. if (schedulingInfo.meetingType == MeetingType.Ocurrence) Thread.Sleep(HOW_MANY_SECONDS_WAIT_FOR_FOLLOWING_REQUEST * 1000); icm.XmlApi.scheduleReportType result = changeMail(source, args, schedulingInfo, now); if (null != result && result.Success && isCreateMeetingRequest(schedulingInfo)) { Dictionary<string, byte[]> attachmentsdata = null; if (this.emailMessage.Attachments.Count > 0) { attachmentsdata = new Dictionary<string, byte[]>(this.emailMessage.Attachments.Count); for (int i = 0; i < this.emailMessage.Attachments.Count; i++) { Attachment attachment = this.emailMessage.Attachments[i]; Stream readStream = attachment.GetContentReadStream(); byte[] bytes = null; if (readStream.Length > 0) { bytes = new byte[readStream.Length]; readStream.Read(bytes, 0, bytes.Length); } else bytes = Encoding.ASCII.GetBytes(" "); attachmentsdata.Add(attachment.FileName, bytes); } } parser.changeBodyOfTnef(args.MailItem.Message.TnefPart, schedulingInfo); if (attachmentsdata != null) { foreach (KeyValuePair<string, byte[]> attachmentdata in attachmentsdata) { Attachment attachment = this.emailMessage.Attachments.Add(attachmentdata.Key); Stream attachmentStream = attachment.GetContentWriteStream(); attachmentStream.Write(attachmentdata.Value, 0, attachmentdata.Value.Length); attachmentStream.Flush(); attachmentStream.Close(); } } } } catch (Exception ex) { RvLogger.DebugWrite(ex.Message); RvLogger.DebugWrite(ex.StackTrace); string baseFailCode = ex.Message; sendBackAMail(source, schedulingInfo, baseFailCode); } finally { RvLogger.DebugWrite("Start to agentAsyncContext.Complete()================================================"); agentAsyncContext.Complete(); RvLogger.DebugWrite("Complete agentAsyncContext.Complete()================================================"); RvLogger.DebugWrite("Leave transport agent, from: " + schedulingInfo.delegatorEmailAddr + ", subject: " + schedulingInfo.subject); } }