Wednesday, July 27, 2016

EWS Basics : Sending Messages using EWS

One of the most basic things you might want to do with EWS is Send a Message, while its may sound easy sending messages programmatically can be at times confusing.

But first lets take one step back for a second from EWS and look at the different ways you could send Email programmatically on Exchange.

SMTP : (Simple Mail Transfer Protocol) SMTP is the backbone protocol for Email and is how most Email is transferred between servers on the Internet. Its also the protocol POP and IMAP clients use to send email. Because SMTP is a protocol (meaning its a set of rules defined in a RFC) rather then an API to use it you need to use a library that will give you some objects that you can code against that will then generated the necessary communication that follows the protocol rules. Some example of these are CDOSys, and System.NET.Mail. From an Exchange technical point of view when you submit a message via SMTP your submitting it directly into the Transport Pipeline so it doesn't going through Mailbox Store.

Pickup and Replay Directories : These are special directories on the Transport server that you can place Messages into https://technet.microsoft.com/en-us/library/bb124230(v=exchg.150).aspx typically used by specialized applications like foreign connectors https://technet.microsoft.com/en-us/library/aa996779(v=exchg.150).aspx

Mailbox API's : These are the specific API's that Microsoft has made available to access a Mailbox which are MAPI, EWS Managed API and for Office365 the new REST api. (ActiveSync could also be used but is a more specialized protocol) . When you send a Message via one of these Mailbox API's you first have to talk to the Mailbox role server (via the CAS ) and the message is submitted to Exchange Store which will then send it through into the transport pipeline. As part of that process you may choose to save a copy of the message your sending into the sentItems folder of the user who is sending the message. As you are sending via the Store you can also assert the SendOnBehalf rights if you send a message on behalf of a delegate.

EWS : To Send a Message using EWS you use the SendItem operation https://msdn.microsoft.com/en-us/library/office/aa580238(v=exchg.150).aspx you can also use the CreateItem Operation and set the SendDisposition to Send or SendAndSaveCopy. Depending on if your sending a message with Attachments you may need to make multiple requests to the server to create a draft message and then add attachments. In the EWS Managed API this complexity is implemented in the API so you don't have to worry about it. eg the following is a basic function for sending a message using EWS in PowerShell.

function Send-EWSMessage  {
     param( 
             [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
  [Parameter(Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials,
  [Parameter(Position=2, Mandatory=$false)] [switch]$useImpersonation,
  [Parameter(Position=3, Mandatory=$false)] [string]$url,
  [Parameter(Position=6, Mandatory=$true)] [String]$To,
  [Parameter(Position=7, Mandatory=$true)] [String]$Subject,
  [Parameter(Position=8, Mandatory=$true)] [String]$Body,
  [Parameter(Position=9, Mandatory=$false)] [String]$Attachment
    )  
  Begin
 {
  if($url){
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials -url $url 
  }
  else{
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials
  }
  if($useImpersonation.IsPresent){
   $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) 
  }
  $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$MailboxName)   
  $SentItems = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
  $EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service  
  $EmailMessage.Subject = $Subject
  #Add Recipients    
  $EmailMessage.ToRecipients.Add($To)  
  $EmailMessage.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody  
  $EmailMessage.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML  
  $EmailMessage.Body.Text = "Body"  
  $EmailMessage.From = $MailboxName
  if($Attachment)
  {   
   $EmailMessage.Attachments.AddFileAttachment($Attachment)
  }
  $EmailMessage.SendAndSaveCopy($SentItems.Id) 
  
 }
}

One other way of sending a Message in EWS is you can use the MIMEContent that maybe generated by another library or maybe a message that was exported or saved eg

function Send-MimeMessage {
     param( 
     [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
  [Parameter(Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials,
  [Parameter(Position=2, Mandatory=$false)] [switch]$useImpersonation,
  [Parameter(Position=3, Mandatory=$false)] [String]$MimeMessage
    )  
  Begin
 {
  if($url){
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials -url $url 
  }
  else{
   $service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials
  }
  if($useImpersonation.IsPresent){
   $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) 
  }
  $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$MailboxName)   
  $EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
  [byte[]]$bdBinaryData1 = [System.IO.File]::ReadAllBytes($MimeMessage)  
  $EmailMessage.MimeContent = new-object Microsoft.Exchange.WebServices.Data.MimeContent("UTF-8", $bdBinaryData1)
  $EmailMessage.SendAndSaveCopy($SentItems.Id) 
 }
 
}

Both of these functions come from the following github script https://github.com/gscales/Powershell-Scripts/blob/master/EWSSend.ps1

Sending Options : To Send a Message as another user (eg other then the current user being used to authenticate) you need to have been granted the SendAs permission on a Mailbox (which is a separate permission from the Mailbox rights granted via Add-MailboxPermission). The other option in EWS is that you can use EWS impersonation which would give the caller the same rights as the Mailbox owner,  the script I've posted on GitHub has an option to use EWS Impersonation.

Both of these example functions save a copy of the message being sent into the SentItemFolder of the Mailbox passed into the Function. This is controlled via the

$EmailMessage.SendAndSaveCopy($SentItems.Id)

line if you didn't want to save a copy to the SentItem folder you can use the Send Method instead eg

$EmailMessage.Send()

This will set the necessary MessageDispostion value in the SOAP request.

EWS only allows you to send using one body format eg you need to choose between HTML or Text if you do want to use the more advanced body types allowed in MIME look to use the MIME Content option to send a message. There is also an example of sending an encrypted message using the MIME content  https://blogs.msdn.microsoft.com/emeamsgdev/2015/08/10/ews-how-to-send-signed-email-using-the-ews-managed-api/

Using VBA or VB6 : If your still enjoying the retro Programing languages like VBA or VB6 its still possible to use EWS to send a message from your code by manually constructing the SOAP message involved. This maybe helpfully where your enviorment has been migrated to the cloud and your CDOsys code that was using  SMTP to send messages no longer works. As a work around you can use a simple function like this to do a Send using EWS in Office365 (or change the URL to you OnPrem EWS endpoint).

Sub SendMessage(Subject As String, Recipient As String, Body As String, User As String, Password As String)
   Dim sReq As String
   Dim xmlMethod As String
   Dim XMLreq As New MSXML2.XMLHTTP60
   Dim EWSEndPoint As String
   EWSEndPoint = "https://outlook.office365.com/EWS/Exchange.asmx"
   sReq = "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
   sReq = sReq & "<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">" & vbCrLf
   sReq = sReq & "<soap:Header>" & vbCrLf
   sReq = sReq & "<t:RequestServerVersion Version=""Exchange2010""/>" & vbCrLf
   sReq = sReq & "</soap:Header>" & vbCrLf
   sReq = sReq & "<soap:Body>" & vbCrLf
   sReq = sReq & "<CreateItem MessageDisposition=""SendAndSaveCopy"" xmlns=""http://schemas.microsoft.com/exchange/services/2006/messages"">" & vbCrLf
   sReq = sReq & "<SavedItemFolderId>" & vbCrLf
   sReq = sReq & "<t:DistinguishedFolderId Id=""sentitems"" />" & vbCrLf
   sReq = sReq & "</SavedItemFolderId>" & vbCrLf
   sReq = sReq & "<Items>" & vbCrLf
   sReq = sReq & "<t:Message>" & vbCrLf
   sReq = sReq & "<t:ItemClass>IPM.Note</t:ItemClass>" & vbCrLf
   sReq = sReq & "<t:Subject>" & Subject & "</t:Subject>" & vbCrLf
   sReq = sReq & "<t:Body BodyType=""Text"">" & Body & "</t:Body>" & vbCrLf
   sReq = sReq & "<t:ToRecipients>" & vbCrLf
   sReq = sReq & "  <t:Mailbox>" & vbCrLf
   sReq = sReq & "       <t:EmailAddress>" & Recipient & "</t:EmailAddress>" & vbCrLf
   sReq = sReq & "  </t:Mailbox>" & vbCrLf
   sReq = sReq & "</t:ToRecipients>" & vbCrLf
   sReq = sReq & "</t:Message>" & vbCrLf
   sReq = sReq & "</Items>" & vbCrLf
   sReq = sReq & "</CreateItem>" & vbCrLf
   sReq = sReq & "</soap:Body>" & vbCrLf
   sReq = sReq & "</soap:Envelope>" & vbCrLf
   xmlMethod = "POST"
   XMLreq.Open xmlMethod, EWSEndPoint, False, User, Password
   XMLreq.setRequestHeader "Content-Type", "text/xml; charset=""UTF-8"""
   XMLreq.setRequestHeader "Translate", "F"
   XMLreq.setRequestHeader "User-Agent", "VBAEWSSender"
   XMLreq.send sReq
   If XMLreq.Status = 200 Then
        ' Message Sent okay
    Else
        ' Something went Wrong
   End If
End Sub

4 comments:

Terry said...

Thank you so much for all of your postings. I am trying to learn EWS with PowerShell and your blogging helps a lot. Someone needs to write a book :-)

Paristocrate said...

THANK YOU SO MUCH. It worked beautifully.

Anonymous said...

Hello, is it possible to send attachement with VBA?

ASIC Antminer S9 For Sale Bitmain Chinese Store said...

ASIC Antminer S9 For Sale Bitmain Chinese Store
We are manufacturer, exporter and supplier bitcoin and altcoin ASIC mining products ASIC Antminer S9 For Sale it is ready to ship to your address any where in the world
ASIC Antminer S9 For Sale at Cheap Rate
with Free express shipping