Sunday, October 26, 2008

Vb.NET sample for getting the number of Unread messages from an inbox using Impersonation with Exchange Web Services

Although im not a great user of VB any more there does seem to be a bit of a dirth of samples for those people who haven't made the leap to using C#. So here's something that might fill the void for a few people I've put a donwload of this code here the code itself looks like

Imports ewsvbsamp.ews
Imports System.Net
Imports System.Net.Security
Imports System.Security.Cryptography.X509Certificates
Module Module1

Sub Main()
Console.WriteLine(GetUnreadEmailCount("user@domain.com"))
End Sub
Public Function GetUnreadEmailCount(ByVal emailaddress As String) As Integer
Dim UnreadCount As Integer = 0
ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateCertificate)
Dim esb As New ExchangeServiceBinding
esb.RequestServerVersionValue = New RequestServerVersion
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1
esb.Credentials = New NetworkCredential("user", "password", "domain")
esb.Url = "https://servername/EWS/Exchange.asmx"

Dim csConSid As New ConnectingSIDType
csConSid.PrimarySmtpAddress = emailaddress
Dim exImpersonate As New ExchangeImpersonationType
exImpersonate.ConnectingSID = csConSid
esb.ExchangeImpersonation = exImpersonate

Dim biArray(1) As BaseFolderIdType
Dim dfFolder As New DistinguishedFolderIdType
dfFolder.Id = DistinguishedFolderIdNameType.inbox
biArray(0) = dfFolder
Dim geGetFolder As New GetFolderType
geGetFolder.FolderIds = biArray
geGetFolder.FolderShape = New FolderResponseShapeType
geGetFolder.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties
Dim gfResponse As GetFolderResponseType = esb.GetFolder(geGetFolder)
Dim rmta As ResponseMessageType() = gfResponse.ResponseMessages.Items
Dim rmt As FolderInfoResponseMessageType = DirectCast(rmta(0), FolderInfoResponseMessageType)
If rmt.ResponseClass = ResponseClassType.Success Then
Dim folder As FolderType = DirectCast(rmt.Folders(0), FolderType)
UnreadCount = folder.UnreadCount
End If
Return UnreadCount
End Function

Private Function ValidateCertificate(ByVal sender As Object, ByVal certificate As X509Certificate, ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors) As Boolean
'Return True to force the certificate to be accepted.
Return True
End Function
End Module

Tuesday, October 14, 2008

WebService to release Quarantined messages in Exchange 2007

This post follows on from the quarantine digest message post last month and looks at the challeges around resubmitting a Quarantined message. Outlook 2007 and OWA 2007 have the ability to resend a message from the Quarantine mailbox using the Resend facilities that are documented in http://msdn.microsoft.com/en-us/library/ms527630(EXCHG.10).aspx. Unfortunately you can't do this using Exchange Web Services because EWS doesn't give you full access to to the recipient collection so this means its not possible to set the PR_RECIPIENT_TYPE for each recipient you want the message delivered to and to also mark those message recipients you dont want the message delivered to. One interesting piece of functionality the quarantine mailbox has is the ability to sendas any email address be it Internal or External eg all you need to do is set the following Mapi property on the message your sending and this will become the sender of the message when you send it. Note this will only work for the quarantine mailbox

ExtendedPropertyType osOrginalSender = new ExtendedPropertyType();
PathToExtendedFieldType epExPath = new PathToExtendedFieldType();
epExPath.DistinguishedPropertySetIdSpecified = true;
epExPath.DistinguishedPropertySetId = DistinguishedPropertySetType.PublicStrings;
epExPath.PropertyName = "quarantine-original-sender";
epExPath.PropertyType = MapiPropertyTypeType.String;
osOrginalSender.ExtendedFieldURI = epExPath;
osOrginalSender.Item = "";
Message.ExtendedProperty = new ExtendedPropertyType[1];
Message.ExtendedProperty[0] = osOrginalSender;

Back on track if you want to release a quarantined message you have the option of using Mapi or using the Replay directory on a Hub Role Server. There is a good description of the Replay directory here what makes the replay directory usefull for the task of resubmitting a quarantined message is that it allows specifying X-Sender and X-Reciever to allow you to specify who this message is delievered to ignoring the existing To and From Mail headers. With Quarantined messages the actual message that is quarantined is an attachment of the NDR message that is delivered to the quarantined mailbox. To to be able to submit the message to the replay directory I've come up with a WebService that uses EWS and takes one parameter which is the ItemID of the quarantine report message. It then Grabs this message using EWS looks at the attachments on the message and grabs the original message and then writes X-headers to represent the Sender and Recipients the message is to be delivered to. The messsage is then saved to the replay directory on the Hub Server.

This is still pretty rough and needs more work but this is the skeleton I've put a download here the code looks like.

using System;
using System.Collections.Generic;
using System.Web;
using System.Net;
using System.Web.Services;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using WebService1.ews;
using System.IO;

namespace WebService1
{
///
/// Summary description for Service1
///

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{

[WebMethod]
public string ResubmitMessage(String MessageID)
{
ServicePointManager.ServerCertificateValidationCallback =
delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
// Replace this line with code to validate server certificate.
return true;
};
string res = "";
ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.RequestServerVersionValue = new RequestServerVersion();
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1;
esb.Credentials = new NetworkCredential("user", "password", "domain");
esb.Url = @"https://casserver/EWS/Exchange.asmx";
MessageType[] oMessages = GetQuantined(esb, MessageID);
if (oMessages[0] != null & oMessages[1] != null)
{
byte[] MimeContent = Convert.FromBase64String(oMessages[1].MimeContent.Value);
System.Text.ASCIIEncoding asEncoding = new System.Text.ASCIIEncoding();
String xsXSender = "X-Sender: <" + oMessages[0].ExtendedProperty[0].Item.ToString() + ">";
String xrXreciever = "";
foreach (EmailAddressType recp in oMessages[0].ToRecipients)
{
xrXreciever = xrXreciever + "X-Receiver: <" + recp.EmailAddress + ">" + "\r\n";
}
String emlMessage = xsXSender + "\r\n" + xrXreciever + asEncoding.GetString(MimeContent);
String MessageGuid = Guid.NewGuid().ToString();
MessageGuid = MessageGuid + DateTime.Now.Ticks.ToString();
StreamWriter emlwriter = new StreamWriter((@"C:\Program Files\Microsoft\Exchange Server\TransportRoles\replay\" + MessageGuid + "eml"), true);
emlwriter.WriteLine(emlMessage);
emlwriter.Close();
res = "Email Written to Replay Directory";

}
else {
res = "Error geting message";
}
return res;
}
private MessageType[] GetQuantined(ExchangeServiceBinding esb,String MessageID) {
MessageType[] msMessages = new MessageType[2];
ItemIdType iiItemId = new ItemIdType();
iiItemId.Id = MessageID;
ItemIdType[] giGetItemArray = new ItemIdType[1];
giGetItemArray[0] = iiItemId;
BasePathToElementType[] beAdditionproperteis = new BasePathToElementType[1];
PathToExtendedFieldType osOriginalSenderEmail = new PathToExtendedFieldType();
osOriginalSenderEmail.PropertyTag = "0x0067";
osOriginalSenderEmail.PropertyType = MapiPropertyTypeType.String;
beAdditionproperteis[0] = osOriginalSenderEmail;
ItemResponseShapeType isItemResponseShape = new ItemResponseShapeType();
isItemResponseShape.BaseShape = DefaultShapeNamesType.AllProperties;
GetItemType giGetItem = new GetItemType();
isItemResponseShape.AdditionalProperties = beAdditionproperteis;
giGetItem.ItemShape = isItemResponseShape;
giGetItem.ItemIds = giGetItemArray;
GetItemResponseType giGetItemResponse = esb.GetItem(giGetItem);
if (giGetItemResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success)
{
ItemType miNDRItem = ((ItemInfoResponseMessageType)giGetItemResponse.ResponseMessages.Items[0]).Items.Items[0];
msMessages[0] = (MessageType)miNDRItem;
foreach (AttachmentType atAttachment in miNDRItem.Attachments)
{
if (atAttachment.ContentType == "message/rfc822")
{
msMessages[1] = GetOrigMsg(esb, atAttachment.AttachmentId);
}

}

}
else {
// Handle Error
}
return msMessages;
}
private MessageType GetOrigMsg(ExchangeServiceBinding esb, AttachmentIdType atAttachID)
{
MessageType omOrigMessage = null;
GetAttachmentType gaGetAttachment = new GetAttachmentType();
AttachmentIdType[] atArray = new AttachmentIdType[1];
atArray[0] = atAttachID;
gaGetAttachment.AttachmentShape = new AttachmentResponseShapeType();
gaGetAttachment.AttachmentShape.IncludeMimeContent = true;
gaGetAttachment.AttachmentShape.IncludeMimeContentSpecified = true;
gaGetAttachment.AttachmentIds = atArray;
GetAttachmentResponseType gaGetAttachmentResponse = esb.GetAttachment(gaGetAttachment);
foreach (AttachmentInfoResponseMessageType atAttachmentResponse in gaGetAttachmentResponse.ResponseMessages.Items)
{
if (atAttachmentResponse.ResponseClass == ResponseClassType.Success)
{
ItemAttachmentType itIemAttachmentType = atAttachmentResponse.Attachments[0] as ItemAttachmentType;
omOrigMessage = (MessageType)itIemAttachmentType.Item;
}
else
{
//Deal with error
}
}
return omOrigMessage;
}
}
}

Thursday, October 02, 2008

Exchange Reverse Permission audit Powershell Gui version 2 Exchange 2007

This script builds on a script I posted earlier in the year that produced a GUI that showed permissions and reverse permissions for Exchange Mailboxes (eg what other mailboxes a particular mailbox has access to). The previous version just looked at the Mailbox Rights and Send AS/Receive AS rights. This new version using Exchange Web Services looks at firstly the rights on the shared folders in a mailbox (eg Root,calendar,inbox,notes,task,journal) and also the Outlook Delegates and the rights assigned to the delegates such as Private Items access and Meeting recipients as well as all the other permissions it did previously. Sorry i haven't got the snapshot functionality done yet which i think i promised before but...

The cool thing is this script now lets us answer a bunch of new questions such as

Which users have given acess to paricular folders in their Mailbox to other people(including the default user which may mean the have unwillingly delegated access to everyone). And we can look at the reverse of this which is what mailboxes folders a particular mailbox has been given access to.

We can see all the Outlook delegations that have been configured and who will receive who's meeting request etc. And the reverse which is what delegate meeting request a particular mailbox will receive.

The big thing is that this script pulls together all the disparate permissions you have to consider when determining mailbox access and displays them in one location so any of the above type of questions become exceedingly easier to answer. Well that's the theory anyway.

This script uses the EWS Powershell library I've been building for a while to provide some eaiser to use functions when accessing Exchange Web Services from Powershell . The ability to access/set folder permissions was added in SP1 with Exchange Web Services. Folder permissions are a little tricky to deal with as I've talked about in the past, basically you have two different types of permission sets, calendarpermissions and normal folder permissions. So its important to detect and differentiate between them and there is also a problem with calendar permissions in that the Outlook Roles don't map to the CalendarPermissionLevelType in EWS. Fortunately i solved this one in the past with a few helper routines that will map the custom levels back to their correct Outlook roles so they make a bit more sense. The delegate code is pretty simple and just returns the outlook delegates as a genericlist.

To report on folder permissions and Delegates using Exchange Web Services you need to have rights on the mailboxes you are going to report on. There are a few ways of granting these rights firstly you can do this by granting those rights to a specific user using add-mailboxpermission as documented here. Or you can grant the rights via EWS impersonation by allowing impersonation on the server and mailStores your going to access see this. To allow for these varying degrees of rights allocations when you run the script its will first prompt you for the EWS authentication and Autodiscover settings. If you have autodiscover configured correctly the script should be able to query AD to find the correct EWS URL to use otherwise you can manually input the URL to your CAS which would look something like "https://casservername.domain.com/ews/exchange.asmx". Once you have made the EWS authentication choices the script will start querying active directory and the mailboxes for every users permissions and depending on the number of mailboxes and users you have hopefully eventually get back to you with a result.

To use this script you need to download the latest copy of the EWSUtil from here and put it in the c:\temp directory as this is where the script expect the dll eg

[void][Reflection.Assembly]::LoadFile("c:\temp\EWSUtil.dll")

I've put a download of the script here the new bits of the code look like.

if ($seAuthCheck.Checked -eq $false) {
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$useImp, $unUserNameTextBox.Text, $unPasswordTextBox.Text, $unDomainTextBox.Text,$casUrl)
}
else{
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$useImp, "", "", "",$casUrl)
}
$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 6
for ($fcint=0;$fcint -lt 6;$fcint++){
$dTypeFld = new-object EWSUtil.EWS.DistinguishedFolderIdType

switch ($fcint){
0 {$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::inbox}
1 {$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::calendar}
2 {$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::contacts}
3 {$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::tasks}
4 {$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::journal}
5 {$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::msgfolderroot}
}
$mbMailbox = new-object EWSUtil.EWS.EmailAddressType
$mbMailbox.EmailAddress = $mbMailboxEmail
$dTypeFld.Mailbox = $mbMailbox
$fldarry[$fcint] = $dTypeFld
}
$Folders = $ewc.GetFolder($fldarry)
If ($Folders.Count -ne 0) {
ForEach ($Folder in $Folders) {
if ($Folder.GetType() -eq [EWSUtil.EWS.CalendarFolderType]){
ForEach ($Permissions in $Folder.PermissionSet.CalendarPermissions){
if ($Permissions.UserId.DistinguishedUserSpecified -eq $false){
$sidbind = "LDAP://sid="
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),("FolderRight-" + $Folder.DisplayName),$aceuser.samaccountname.ToString(),$ewc.enumOutlookRole($Permissions),"Allow")
$fpCount++
if ($rvFolderPerms.Containskey($aceuser.samaccountname.ToString())){
$rvFolderPerms[$aceuser.samaccountname.ToString()] = [int]$rvFolderPerms[$aceuser.samaccountname.ToString()] +1
}
else {
$rvFolderPerms.add($aceuser.samaccountname.ToString(),1)
}
}
else{
if ($Permissions.UserId.DistinguishedUser -eq [EWSUtil.EWS.DistinguishedUserType]::Default){
if ($Permissions.CalendarPermissionLevel -ne [EWSUtil.EWS.CalendarPermissionLevelType]::None){
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),("FolderRight-" + $Folder.DisplayName),"Default",$ewc.enumOutlookRole($Permissions),"Allow")
$dfCount++
}
}
}
}


}
else {
ForEach ($Permissions in $Folder.PermissionSet.Permissions){
if ($Permissions.UserId.DistinguishedUserSpecified -eq $false){
$sidbind = "LDAP://sid="
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),("FolderRight-" + $Folder.DisplayName),$aceuser.samaccountname.ToString(),$Permissions.PermissionLevel.ToString(),"Allow")
$fpCount++
if ($rvFolderPerms.Containskey($aceuser.samaccountname.ToString())){
$rvFolderPerms[$aceuser.samaccountname.ToString()] = [int]$rvFolderPerms[$aceuser.samaccountname.ToString()] +1
}
else {
$rvFolderPerms.add($aceuser.samaccountname.ToString(),1)
}
}
else{
if ($Permissions.UserId.DistinguishedUser -eq [EWSUtil.EWS.DistinguishedUserType]::Default){
if ($Permissions.PermissionLevel -ne [EWSUtil.EWS.PermissionLevelType]::None){
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),("FolderRight-" + $Folder.DisplayName),"Default",$Permissions.PermissionLevel.ToString(),"Allow")
$dfCount++
}
}
}
}
}
}
}
$delegates = $ewc.getdeletgates($mbMailboxEmail)
foreach ($delegate in $delegates){
$sidbind = "LDAP://sid="
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
$dgCount++
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"DelegateRight",$aceuser.samaccountname.ToString(),"Mailbox Delegated","Allow")
if ($delegate.DelegateUser.ReceiveCopiesOfMeetingMessagesSpecified -eq $true){
if ($delegate.DelegateUser.ReceiveCopiesOfMeetingMessages-eq $true){
$dgCount++
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"DelegateRight",$aceuser.samaccountname.ToString(),"Recieve Meetings","Allow")
if ($rvDelegatePerms.Containskey($aceuser.samaccountname.ToString())){
$rvDelegatePerms[$aceuser.samaccountname.ToString()] = [int]$rvDelegatePerms[$aceuser.samaccountname.ToString()] +1
}
else {
$rvDelegatePerms.add($aceuser.samaccountname.ToString(),1)
}
}
}

if ($delegate.DelegateUser.ViewPrivateItemsSpecified -eq $true){
if ($delegate.DelegateUser.ViewPrivateItems-eq $true){
$dgCount++
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"DelegateRight",$aceuser.samaccountname.ToString(),"View Private Items","Allow")
if ($rvDelegatePerms.Containskey($aceuser.samaccountname.ToString())){
$rvDelegatePerms[$aceuser.samaccountname.ToString()] = [int]$rvDelegatePerms[$aceuser.samaccountname.ToString()] +1
}
else {
$rvDelegatePerms.add($aceuser.samaccountname.ToString(),1)
}
}
}
if ($rvDelegatePerms.Containskey($aceuser.samaccountname.ToString())){
$rvDelegatePerms[$aceuser.samaccountname.ToString()] = [int]$rvDelegatePerms[$aceuser.samaccountname.ToString()] +1
}
else {
$rvDelegatePerms.add($aceuser.samaccountname.ToString(),1)
}

}