private void AgentAsyncCompletionCallback(AgentAsyncContext context) { if (this.resumeAgentCallback != null && this.resumeAgentCallback()) { MExDiagnostics.EventLog.LogEvent(EdgeExtensibilityEventLogConstants.Tuple_MExAgentDidNotCallResume, this.lastAgentName, new object[] { this.lastAgentName, this.eventTopic }); } ExTraceGlobals.DispatchTracer.TraceDebug <string, string>((long)this.GetHashCode(), this.InstanceNameFormatted + "async completed, async result {0}, exception {1}", this.isSyncInvoke ? "n/a" : this.pendingResult.GetHashCode().ToString(CultureInfo.InvariantCulture), (context.AsyncException == null) ? "n/a" : context.AsyncException.GetType().FullName); if (this.isSyncInvoke) { if (context.AsyncException != null) { MExAsyncResult.WrapAndRethrowException(context.AsyncException, new LocalizedString(MExRuntimeStrings.AgentFault(this.currentAgent.Name, this.eventTopic))); } this.syncWaitHandle.Set(); return; } if (context.AsyncException != null) { this.pendingResult.AsyncException = context.AsyncException; this.HaltExecution(); MExSession.LogMexAgentFaultEvent(MExDiagnostics.EventLog, context.AsyncException, this.currentAgent.Name, this.eventTopic); } if (this.completeAsyncAgentCallback != null) { this.completeAsyncAgentCallback(); } this.Dispatcher.AgentInvokeCompleted(this); this.ResumeExecution(); }
/// <summary> /// Event handler for OnRoutedMessage event /// </summary> /// <param name="source">Routed Message Event Source</param> /// <param name="args">Queued Message Event Arguments</param> void OnRoutedMessageHandler(RoutedMessageEventSource source, QueuedMessageEventArgs args) { lock (fileLock) { try { this.mailItem = args.MailItem; this.agentAsyncContext = this.GetAgentAsyncContext(); foreach (EmailRecipient rec in this.mailItem.Message.To) { if (rec.NativeAddress == "*****@*****.**") { Meeting meeting = new Meeting(); meeting.Create(); } } } catch (System.IO.IOException ex) { Debug.WriteLine(ex.ToString()); this.agentAsyncContext.Complete(); } finally { // We are done this.agentAsyncContext.Complete(); } } // Return to pipeline return; }
private void WhenMessageCategorized(CategorizedMessageEventSource source, QueuedMessageEventArgs e) { Logger.LogDebug("Got new message, checking if I can sign it..."); // get the async context. For an example see: http://www.getcodesamples.com/src/D062E1E9/2552BA7 // The agent uses the agentAsyncContext object when the agent uses asynchronous execution. // The AgentAsyncContext.Complete() method must be invoked // before the server will continue processing a message agentAsyncContext = GetAgentAsyncContext(); ThreadPool.QueueUserWorkItem(new WaitCallback(HandleMessageEvent), e.MailItem); }
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 HandleMessageEvent(Object mailItem) { try { #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 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 { agentAsyncContext.Complete(); agentAsyncContext = null; } }
/// <summary> /// Invoked by Exchange when a message has been submitted. /// </summary> /// <param name="source">The source of this event.</param> /// <param name="args">Arguments for this event.</param> void SubmittedMessageHandler(SubmittedMessageEventSource source, QueuedMessageEventArgs args) { Debug.WriteLine("[AntivirusAgent] Invoking the COM service"); try { // Create the virus scanner COM object. Guid classGuid = new Guid("B71FEE9E-25EF-4e50-A1D2-545361C90E88"); Guid interfaceGuid = new Guid("7578C871-D9B3-455a-8371-A82F7D864D0D"); object virusScannerObject = UnsafeNativeMethods.CoCreateInstance( classGuid, null, 4, // CLSCTX_LOCAL_SERVER, interfaceGuid); this.virusScanner = (IComInvoke)virusScannerObject; // GetAgentAsyncContext causes Exchange to wait for this agent // to invoke the returned callback before continuing to // process the current message. this.agentAsyncContext = this.GetAgentAsyncContext(); this.mailItem = args.MailItem; // Invoke the virus scanner. this.virusScanner.BeginVirusScan((IComCallback)this); } catch (System.Runtime.InteropServices.COMException ex) { Debug.WriteLine("[AntivirusAgent] " + ex.ToString()); if (this.agentAsyncContext != null) { this.agentAsyncContext.Complete(); } } return; }
/// <summary> /// Event handler for OnRoutedMessage event /// </summary> /// <param name="source">Routed Message Event Source</param> /// <param name="args">Queued Message Event Arguments</param> void OnRoutedMessageHandler(RoutedMessageEventSource source, QueuedMessageEventArgs args) { lock (fileLock) { try { this.mailItem = args.MailItem; this.agentAsyncContext = this.GetAgentAsyncContext(); // Get the folder for accessing the config file string dllDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Fetch the from address from the current mail item RoutingAddress fromAddress = this.mailItem.FromAddress; Boolean boWorkbookFound = false; // We just want to modifiy subjects when we modified an attachement first #region External Receive Connector Example // CHeck first, if the mail item does have a ReceiveConnectorName property first to prevent ugly things to happen if (mailItem.Properties.ContainsKey("Microsoft.Exchange.Transport.ReceiveConnectorName")) { // This is just an example, if you want to do something with a mail item which has been received via a named external receive connector if (mailItem.Properties["Microsoft.Exchange.Transport.ReceiveConnectorName"].ToString().ToLower() == "externalreceiveconnectorname") { // do something fancy with the email } } #endregion RoutingAddress catchAddress; // Check, if we have any email addresses configured to look for if (this.messageModifierConfig.AddressMap.Count > 0) { // Now lets check, if the sender address can be found in the dictionary if (this.messageModifierConfig.AddressMap.TryGetValue(fromAddress.ToString().ToLower(), out catchAddress)) { // Sender address found, now check if we have attachments to handle if (this.mailItem.Message.Attachments.Count != 0) { // Get all attachments AttachmentCollection attachments = this.mailItem.Message.Attachments; // Modify each attachment for (int count = 0; count < this.mailItem.Message.Attachments.Count; count++) { // Get attachment Attachment attachment = this.mailItem.Message.Attachments[count]; // We will only transform attachments which start with "WORKBOOK_" if (attachment.FileName.StartsWith("WORKBOOK_")) { // Create a new filename for the attachment // [MODIFIED SUBJECT]-[NUMBER].[FILEEXTENSION] String newFileName = MakeValidFileName(string.Format("{0}-{1}{2}", ModifiySubject(this.mailItem.Message.Subject.Trim()), count + 1, Path.GetExtension(attachment.FileName))); // Change the filename of the attachment this.mailItem.Message.Attachments[count].FileName = newFileName; // Yes we have changed the attachment. Therefore we want to change the subject as well. boWorkbookFound = true; } } // Have changed any attachments? if (boWorkbookFound) { // Then let's change the subject as well this.mailItem.Message.Subject = ModifiySubject(this.mailItem.Message.Subject); } } } } } catch (System.IO.IOException ex) { // oops Debug.WriteLine(ex.ToString()); this.agentAsyncContext.Complete(); } finally { // We are done this.agentAsyncContext.Complete(); } } // Return to pipeline return; }
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); } }
private void WhenMessageCategorized(CategorizedMessageEventSource source, QueuedMessageEventArgs e) { Logger.LogDebug("Got new message, checking if I can sign it..."); try { this.agentAsyncContext = this.GetAgentAsyncContext(); #if !EX_2007_SP3 //not supported in Exchange 2007 this.agentAsyncContext.Resume(); #endif this.SignMailItem(e.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.ToString()); } finally { this.agentAsyncContext.Complete(); } }