Start and Stop script for exchange 2010

Stop exchange – 

net stop msexchangeadtopology /y
net stop msexchangefba /y
net stop msftesql-exchange /y
net stop msexchangeis /y
net stop msexchangesa /y
net stop iisadmin /y
net stop w3svc /y

Start Exchange –

net start msexchangeadtopology /y
net start msexchangefba /y
net start msftesql-exchange /y
net start msexchangeis /y
net start msexchangesa /y
net start iisadmin /y
net start w3svc /y

Exchange 2010: Said: 452 4.3.1 Insufficient system resources (in reply to MAIL FROM command)

Exchange 2010: Said: 452 4.3.1 Insufficient system resources (in reply to MAIL FROM command)

When the mail is not received by the mail server and you receive the error message from your mail gateway or a telnet client:
said: 452 4.3.1 Insufficient system resources (in reply to MAIL FROM command)

Or there is a event id 15006:

Microsoft Exchange Transport is rejecting message submissions because the available disk space has dropped below the configured threshold.

The following resources are under pressure:
Queue database logging disk space (“D:ProgramsMicrosoft Exchange 2010TransportRolesdataQueue”) = 60% [Medium] [Normal=58% Medium=60% High=62%]

Then the problem is that there are to less system recourses. In this case the disk space where the queue is, is to small. You can resize the volume or you can change the queue location in the file:
Exchange installation DirectoryBinEdgeTransport.exe.config

You must change the following to keys:

<add key=”QueueDatabasePath” value = “E:Queue” />
< add key=”QueueDatabaseLoggingPath” value = “E:QueueLog” />

After this you must restart the Exchange Transport
In the case of SAN EMC copy, I had to clear the contents of th queue folder and queue logging

EXCHANGE SERVER PRO

Microsoft Exchange Server News – Tips – Tutorials

EXCHANGE SERVER 2013 OFFICE 365 POWERSHELL SSL CERTIFICATES HIGH AVAILABILITY MIGRATION EBOOKS

YOU ARE HERE: HOME / TUTORIALS / A GUIDE TO BACK PRESSURE IN MICROSOFT EXCHANGE SERVER

A Guide to Back Pressure in Microsoft Exchange Server

AUGUST 27, 2012 PAUL CUNNINGHAM 27 COMMENTS 

 

inShare27

For most Exchange administrators the first time they encounter the concept of “back pressure” is when they see this error:

452 4.3.1 Insufficient system resources

They might see it for the first time in a non-delivery report, an SMTP error log from an application, a telnet session, or the queue viewer on another Exchange server.

In this article:

An overview of Transport service resource monitoringCustomizing back pressure thresholdsDetecting back pressureMonitoring Transport queuesMonitoring protocol logsMICROSOFT EXCHANGE TRANSPORT SERVICE RESOURCE MONITORING

Back pressure is the name for a condition that an Edge Transport or Hub Transport server is in when it is in an overloaded state and is actively refusing some or all further connection attempts from other systems.

The overloaded state is based on a series of resource utilization metrics:

Free disk space on the drive(s) that store the message queue database and logsUncommitted queue database transactions in memoryMemory utilization by the EdgeTransport.exe process (the Microsoft Exchange Transport service)

Each of those metrics is measured individually, and as such each is individually capable of causing the server to go into a back pressure state. There are two different levels of back pressure. as well as the condition where no over-utilization is occurring, so in total there are three resource utilization conditions that your Edge or Hub Transport servers can be in:

Normal – all is well and the server is performing its role as intended (assuming you haven’t modified the back pressure settings to mask a genuine problem – more on that later)Medium – a resource is moderately over-utilized and the server begins limiting some connection types. Typically internal email flow remains functional while email from external or non-Exchange sources will be rejected. – a resource is severely over-utilized. The server ceases to accept any new connections.

For disk space metrics the back pressure condition causes messages to be rejected. However for memory utilization metrics, before rejecting connections the server will first take actions to attempt to relieve the conditions.

For example, the server will perform garbage collection (reclaiming memory from unused objects) or flush the server’s DNS cache.

If after a certain number of polling intervals (which vary depending on the metric involved) the utilization is still above threshold, then the server will begin rejecting new connections as it does with disk space utilization.

Ultimately the problems you will actually notice are delays or a total lack of message delivery.

CUSTOMIZING BACK PRESSURE THRESHOLDS

The metrics used by Transport server resource monitoring to trigger back pressure are based on a combination of configurable settings as well as fixed algorithms.

The configurable settings are stored in theEdgeTransport.exe.config file, located in the following directories by default:

Exchange 2007: C:Program FilesMicrosoftExchange ServerBinExchange 2010: C:Program FilesMicrosoftExchange ServerV14BinExchange 2013: C:Program FilesMicrosoftExchange ServerV15Bin

If you installed Exchange Server to a different location then you will find the config file in the bin folder of your installation directory. In Exchange 2010 and 2013 this is referenced by the environment variable $env:exchangeinstallpath.

EdgeTransport.exe.config

The first two values control whether resource monitoring is enabled, and at what interval resource monitoring is performed.

Though the option exists it is not recommended to actually disable resource monitoring. Instead the recommended approach is to identify the cause of resource over-utilization and correct it.

The same goes for basically all of the configurable settings for resource monitoring. Rather than spend time tuning the settings you should resolve the underlying issue, for example by adding disk or memory capacity to the server, or by adding additional servers to assist with overall email traffic load.

If you do happen to want to dive into the specific metrics and thresholds you can read more about them here:

Understanding Back Pressure (Exchange 2007)Understanding Back Pressure (Exchange 2010)

Note: there is no Exchange 2013 documentation for this yet but my understanding at this stage is that it is largely the same as Exchange 2010 when it comes to resource monitoring.

DETECTING BACK PRESSURE

Back pressure can go undetected in your Exchange environment for quite some time if you are not monitoring for it. A Transport server can slip in and out of back pressure hundreds of times in a day and you could be completely unaware that it is happening if other servers manage to handle the email traffic load well enough that your customers don’t complain about delayed message delivery.

If you already have an Exchange-aware or event-based monitoring system running in your environment then then you are probably already monitoring for the signals I am about to describe, or can do so relatively easily with the monitoring system you have. For everyone else I will make a few suggestions for ways to detect these conditions.

Note that not all of these are practical to use for real time, proactive monitoring and alerting. But used as part of your regular server capacity monitoring they can be used to detect emerging capacity problems before they begin to seriously impact your environment.

MONITORING TRANSPORT QUEUES

Back pressure on a Transport server in one site can cause email to queue on Transport servers in other sites. Therefore by monitoring queue lengths you can detect the signs of back pressure.

You can use the Get-Queue cmdlet to manually check Transport queues on the local Transport server or on a remote server.

[PS] C:>Get-Queue Identity DeliveryType Status MessageCount NextHopDomain ——– ———— —— ———— ————- HO-EX2010-MB1866 SmtpRelayWithinAdSite Retry 3 hub version 8 HO-EX2010-MB1Submission Undefined Ready 0 Submission

One way to automate the checking of queues is with myTest-ExchangeServerHealth.ps1 script. However it is more suited to running as a scheduled report at a specific time of day, not as a continuous monitoring and alerting script (though you could customize it be one).

Get-Queue doesn’t always tell you the full story though. This is the Get-Queue output on an Exchange 2007 server that is in back pressure.

[PS] C:>Get-Queue Identity DeliveryType Status MessageCount NextHopDomain ——– ———— —— ———— ————- HO-EX2007-MB1Submis… Undefined Ready 0 Submission

Nothing appears wrong based on that. However in the event log:

Log Name: Application
Source: MSExchangeMailSubmission
Date: 25/08/2012 10:35:25 PM
Event ID: 1009
Task Category: MSExchangeMailSubmission
Level: Warning
Keywords: Classic
User: N/A
Computer: HO-EX2007-MB1.exchangeserverpro.net
Description:
The Microsoft Exchange Mail Submission Service is currently unable to contact any Hub Transport servers in the local Active Directory site. The servers may be too busy to accept new connections at this time.

The above event log entry is the Mailbox server’s mail submission to the local Hub Transport server in the site (which happens to be the same server in this particular case) being rejected due to back pressure on the Transport server.

Another option is to use Perfmon to monitor the aggregate delivery queue on each of your Transport servers in real time.

Aggregate Delivery Queues on Transport Servers (Nothing to see here)

Even if you don’t have an event-based monitoring system you can still monitor event logs using PowerShell and theGet-EventLog cmdlet.

Back pressure conditions are logged to the Application event log under a series of event IDs:

Event ID 15004: Increase in the utilization level for any resource (eg from Normal to Medium)Event ID 15005: Decrease in the utilization level for any resource (eg from High to Medium)Event ID 15006: High utilization for disk space (ie critically low free disk space)Event ID 15007: High utilization for memory (ie critically low available memory)

For example to search for instances of event ID 15004 in the past 24 hours you can run the following PowerShell command:

PS C:> Get-EventLog -ComputerName ho-ex2007-mb1 -LogName Application -After (Get-Date).AddDays(-1) | where {$_.EventID -eq “15004”} Index Time EntryType Source InstanceID Message —– —- ——— —— ———- ——- 93560 Aug 25 22:24 Warning MSExchangeTransport 2147760796 Resource pressure increased from Normal to High… 93535 Aug 25 22:09 Warning MSExchangeTransport 2147760796 Resource pressure increased from Normal to Medi…

A more detailed report can be generated with some scripting.

PowerShell Script: Check Hub Transport Servers for Back Pressure EventsMONITORING PROTOCOL LOGS

When an Edge or Hub Transport server rejects a connection due to back pressure it uses a 4.x.x SMTP status code.

Using a similar Log Parser query to the one I shared in my previous article on reporting SMTP error codes, you can scan your protocol logs for evidence of back pressure events.

This command when run from the same directory that has the protocol logs in it will produce a report of any 4.x.x SMTP error codes.

“C:Program Files (x86)Log Parser 2.2logparser.exe” “SELECT data as [Status Code],Count(*) as Hits FROM *.log WHERE data LIKE ‘4%’ GROUP BY data ORDER BY Hits DESC” -i:CSV -nSkipLines:4 -rtp:-1

The results will appear similar to this (if any such errors are present).

Status Code Hits ————————————————————————– —– 452 4.3.1 Insufficient system resources 19791 421 4.7.0 Too many errors on this connection, closing transmission channel 1298 421 4.4.1 Connection timed out 526 454 4.7.0 Temporary authentication failure 289 451 4.7.0 Timeout waiting for client input 32

You may be more interested in the per-day stats, in which case this query can be used:

“C:Program Files (x86)Log Parser 2.2logparser.exe” “SELECT TO_LOCALTIME(TO_TIMESTAMP(EXTRACT_PREFIX(TO_STRING([#Fields: date-time]),0,’T’), ‘yyyy-MM-dd’)) AS Date, COUNT(*) AS Hits from *.log where (data LIKE ‘4%’) GROUP BY Date ORDER BY Date ASC” -i:CSV -nSkipLines:4 -rtp:-1

The results will look similar to this:

Date Hits ———- —– 2012-08-08 12 2012-08-09 93 2012-08-10 72 2012-08-11 7 2012-08-12 48 2012-08-13 146 2012-08-14 460 2012-08-15 36 2012-08-16 389 2012-08-17 10345 2012-08-18 105 2012-08-19 252 2012-08-20 9631 2012-08-21 43 2012-08-22 133 2012-08-23 80 2012-08-24 57 2012-08-25 21 2012-08-26 6 Statistics: ———– Elements processed: 13181205 Elements output: 19 Execution time: 123.13 seconds (00:02:3.13)

This highlights the importance of taking samples of your server stats on a regular basis. While the above example makes it pretty easy to spot the significant increase in errors on two particular days, if your logging data did not cover a long enough time span then you may need to rely on previous benchmarks to spot problems.

It also highlights the importance of having diagnostic logging such as protocol logging turned on in advance of a problem occurring, so that you can begin to troubleshoot with all data immediately available to you.

SUMMARY

Back pressure can cause serious problems in an Exchange Server environment due to the interruptions it causes to message delivery. Be sure to check your Transport servers for signs of back pressure, and take steps to resolve the underlying issues.

FILED UNDER: TUTORIALS
TAGGED: EDGE TRANSPORT, EXCHANGE 2007, EXCHANGE 2010,EXCHANGE 2013, HUB TRANSPORT

 

inShare28

ABOUT PAUL CUNNINGHAM

Paul is a Microsoft Exchange Server MVP and publisher of Exchange Server Pro. He also holds several Microsoft certifications including for Exchange Server 2007, 2010 and 2013. Find Paul on Twitter, LinkedIn, Facebook, orGoogle+. Paul is also available for consulting/support engagements.

RELATED ARTICLES:

PowerShell Script: Check Hub Transport Servers for Back Pressure EventsExchange 2007/2010 Transport Rule LoggingA Look at Exchange Server 2013 Resource MailboxesTroubleshooting Email Delivery with Exchange Server Protocol LoggingAvoiding Infinite Loops with Internal Relay Domains in Exchange 2007/2010

COMMENTS

Ifiok says

August 28, 2012 at 11:15 pm

Great post Paul, Thanks.

Reply

TM says

August 28, 2012 at 11:31 pm

When I run the monitor protocol logs I get the following error.

I am running the command in my “TransportRolesLogsProtocolLogSmtpReceive” folder.

Unexpected token ‘SELECT data as [Status Code],Count(*) as Hits FROM *.log WHERE data LIKE ‘4%’ GROUP BY data ORDER BY
Hits DESC’ in expression or statement.
At line:1 char:167
+ “C:Program Files (x86)Log Parser 2.2logparser.exe” “SELECT data as [Status Code],Count(*) as Hits FROM *.log WHERE
data LIKE ‘4%’ GROUP BY data ORDER BY Hits DESC” <<<< -i:CSV -nSkipLines:4 -rtp:-1
+ CategoryInfo : ParserError: (SELECT data as …ER BY Hits DESC:String) [], ParentContainsErrorRecordExc
eption
+ FullyQualifiedErrorId : UnexpectedToken

Reply

DH says

August 30, 2012 at 12:48 am

Thanks for the script. However, I received the same error message

Reply

Paul Cunningham says

August 30, 2012 at 3:41 pm

Just re-tested with a straight copy/paste and its working ok for me on a 2007 server. Which version of Exchange are you both running?

Reply

jay c says

November 6, 2013 at 5:40 am

Make sure you’re running this in CMD and not in PowerShell.

Reply

Saran says

September 8, 2012 at 12:47 am

Hi, I’ve had this since day one, usually it’s the private bytes that overflow the ram. Haven’t been able to pinpoint what is filling up the RAM. Setup a script to restart the Service when the event is triggered

Reply

Navishkar Sadheo says

October 2, 2013 at 8:13 am

Hi Paul

I am getting this error here:

The Microsoft Exchange Mail Submission Service is currently unable to contact any Hub Transport servers in the local Active Directory site. The servers may be too busy to accept new connections at this time.

But no other events relating to back pressure in the event log .

I also ran the script you uploaded that checks for back pressure activity. Came up clean.

What else do you think it could be.

I am having trouble transferring email from one ht to another ht in a different ad site

I am getting 421 SMTP errors

Reply

Paul Cunningham says

October 2, 2013 at 8:23 am

You should check for back pressure or other transport problems on the servers in the destination site.

Reply

Navishkar Sadheo says

October 2, 2013 at 6:02 pm

odd thing is when we reboot the source server the emails start flowing fine for a while and then we start getting the 421 errors in the queue viewer after a while which leads me to believe that the problem is with the source server and not the destination one.

any other ideas?

Reply

Navishkar Sadheo says

October 3, 2013 at 5:30 pm

Hi Paul

found the problem, it was network issues at the destination site.

thank you for response and your article.

it gave me some good insight into back pressure

Reply

pravin says

November 6, 2013 at 6:25 pm

Hi Paul,

I am facing problem on Exchange server 2013 that Edge transport service using high memory and cause slowness. please suggest any solution on that.

Reply

TeeC says

March 5, 2014 at 8:43 pm

Reasonable guide, except you don’t provide any specific advice on tuning the back pressure options to resolve problems.

Reply

Paul Cunningham says

March 5, 2014 at 9:52 pm

That’s because you should not try to tune the back pressure settings.

From the article:

“Instead the recommended approach is to identify the cause of resource over-utilization and correct it.
The same goes for basically all of the configurable settings for resource monitoring. Rather than spend time tuning the settings you should resolve the underlying issue, for example by adding disk or memory capacity to the server, or by adding additional servers to assist with overall email traffic load.”

Reply

Joel says

March 14, 2014 at 9:30 am

I’m having an odd one I can put my finger on. Getting disk space event logs about back pressure, but have plenty of free space on all drives. So it looks like I need to tweak the thresholds. I have 63.1/100 free on C, but Exchange and logs are on E where I have 290/400 free space so it should be well below the warning levels. Yet I constantly see this popping up:

15004: 3/12/2014 11:00:47 PM  MSExchangeTransport-The resource pressure increased from Normal to High.

The following resources are under pressure:
Queue database logging disk space (“E:Program FilesMicrosoftExchange ServerV15TransportRolesdataQueue”) = 99% [High] [Normal=95% Medium=97% High=99%]
Temporary Storage disk space (“E:Program FilesMicrosoftExchange ServerV15TransportRolesdataTemp”) = 99% [High] [Normal=95% Medium=97% High=99%]

Reply

james says

September 17, 2014 at 12:42 am

Hi Paul,

I believe we are experiencing some back pressure issues in exchange 2010 mainly due to disk space. Although we don’t see any Event ID 15004, 15005, 15006 or 15007.

We do receive the following Event ID 1009 The Microsoft Exchange Mail Submission Service is currently unable to contact any Hub Transport servers in the local Active Directory site. We have a single Hub transport server which is hosted on the same box.

When I run the Get-EventLogLevel command I see MSExchangeTransportResourceManager is set to Lowest. Should I increase this log level in order to receive the events, if so to what level, High? Are the any other diagnostic levels i should increase in order to help identify whats going wrong?

Thanks

Reply

james says

September 17, 2014 at 9:43 pm

Hi Paul,

I believe we are experiencing some back pressure issues in exchange 2010 mainly due to disk space. Although we don’t see any Event ID 15004, 15005, 15006 or 15007.

We do receive the following Event ID 1009 The Microsoft Exchange Mail Submission Service is currently unable to contact any Hub Transport servers in the local Active Directory site. We have a single Hub transport server which is hosted on the same box.

When I run the Get-EventLogLevel command I see MSExchangeTransportResourceManager is set to Lowest. Should I increase this log level in order to receive the events, if so to what level, High? Are the any other diagnostic levels i should increase in order to help identify whats going wrong?

Thanks

Reply

Paul Cunningham says

September 18, 2014 at 4:39 pm

Higher diagnostic log level may show you something. Another thing to try is simply restarting the Transport service. This will log new back pressure events to the event log if a back pressure condition exists, usually within a few minutes.

Reply

Tomek says

September 24, 2014 at 6:12 pm

For all seeking remedy for emails stuck in submission queue on Exchange 2007 – not all emails, just few selected ones, mainly with attachments (unfortunately these are most important). Check event logs for ID 1050. If you see “The execution time of agent ‘Transport Rule Agent’ exceeded 300000 (milliseconds) while handling event ‘OnRoutedMessage’….” – disable all Transport Rules you can under Organization Config. – Hub Transport. I was trigger-happy at fighting spam this way, but my server couldn’t take the load of rules and exceptions. Figuring it out made me sick of stress. Once I disabled 90% of my rules, mail flow returned. Next night I’m finally going to sleep well.

Reply

LEAVE A REPLY

Your email address will not be published. Required fields are marked *

Name *

Email *

Comment

Notify me of followup comments via e-mail

POPULAR RESOURCES

LATEST ARTICLESTraining Course: High Availability for Exchange Server 2013Azure Active

TRAINING

Exchange 2013 Boot CampExchange 2010 Boot CampDeploying and Managing Exchange Server 2013 High AvailabilityMastering Message Tracking

RECOMMENDED

Exchange 2003 to 2010 MigrationExchange 2007 to 2010 MigrationExchange PowerShell ScriptsExchange Server 2013 BooksDigicert SSL Certificates

ABOUT PAUL CUNNINGHAM

Paul is a Microsoft Exchange Server MVP and publisher of Exchange Server Pro

Understanding Sharing Invitation Requests – EWS Managed API 1.2

Understanding Sharing Invitation Requests – EWS Managed API 1.2 : Part 1

Before I go into details on exactly how to accomplish programmatically creating a sharing invitation in C# for Exchange 2010 like it’s no big deal, I want to put a disclaimer down right up front:

Please consider the following while reading:

1) I in no way endorse or support any of the code posted here to be used in a production environment. I’m the last guy who needs to be blogging about optimized/secure coding practices and methodologies.
2) I am not responsible if you use this code and subsequently melt down your datacenter…
3) This is a proof of concept approach, so there are probably MANY areas that could be optimized to be less expensive when calling EWS. Again, I just wanted to see if it was possible…and I don’t code for a living…so don’t laugh at my code 😛
4) Finally, the following information on what I did will assume that you have an account setup for impersonation and have the security rights to perform all the relevant operations against EWS in order for this process to work.

Tools and Environment:

Environment Used:

Exchange Version: Exchange 2010 SP1 RU5
EWS Managed API Version: 1.2
My Operating System: Windows 7 x64
Application .NET version: Version 4.0 (NOT the Client Profile)

Development Tools Utilized:
– C# 2010 Express
– OutlookSpy
– EWSEditor
– MFCMAPI
– Outlook 2010

Other Items of note:

Using this method has only been tested for creating sharing folder invitations within the same Exchange Organization. Federated Sharing is a whole other topic that needs to be understood before trying to use this method to send sharing invites to external recipients…

Now that that’s out of the way, down to business!! So I had been playing with the Exchange Web Services Managed API 1.2 and recently built a relatively simple but effective utility that mimics Outlook’s Permissions GUI, except I added the ability to let myself impersonate anyone and used a ListTree of the users’ folders to allow me to manage the permissions on them. Yes, we do a lot of coddling to our users, but also realize we just got done with a huge GroupWise 7.0 -> Exchange 2010 SP1 RU5 migration and so I wanted an easy and effective way of handling the flood of requests coming in without having to perform too many steps to switch between users. The other reason for this app was that there were MANY outdated/irrelevant permissions that came across during the migrations, and some users had upwards of 100 folders and subfolders with tons of outdated permissions on them. Using EWS I was able to recursively loop through all the folders, leaving only the default permissions left for the user to set up once again how they wished. It’s pretty slick!!

In any case, that only whetted my appetite to see what else that the EWS Managed API could do. I decided as a final piece to this little utility, I wanted to be able to call a function that would create a sharing invite on behalf of the impersonated user and send it to the person I had just given reviewer rights on the default calendar folder for. Sounds simple enough…right???

WRONG!!!!! I tell you what, I was not prepared for this adventure….

First things first: Conversion from/to Binary/Hex & other *cough* fun stuff..

Before we even get into the structuring of the objects, let’s run through a couple functions I will be referencing in other code throughout this series.

In order to get the data formatted properly for some of the properties we will be setting later on the message and attachment objects, we need to be able to do two separate types of conversions: 1) Take a string and convert it to its hexadecimal equivalent and 2) Take that hex string and convert it to binary.

This function will take a string (i.e. “Mary a little lamb”) and return the Hexadecimal string value (“4D6172792061206C6974746C65206C616D62”):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public string ConvertStringToHex(string input)
{
    // Take our input and break it into an array
    char[] arrInput = input.ToCharArray();
    String result = String.Empty;
    // For each set of characters
    foreach (char element in arrInput)
    {
        // My lazy way of dealing with whether or not anything had been added yet
        if (String.IsNullOrEmpty(result))
        {
           result = String.Format("{0:X2}", Convert.ToUInt16(element)).ToString();
        }
        else {
            result += String.Format("{0:X2}", Convert.ToUInt16(element)).ToString();
        }
    }
    return result.ToString();
}

The following function will take the Hex value of your data (“4D6172792061206C6974746C65206C616D62”) and convert it to binary (Type byte[]):

Reference: Glen Scales on MSDN TechNet Forums
(His blog is here – If you have ANY curiosity about in-depth EWS development – his blog is the de-facto standard)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static byte[] HexStringToByteArray(string input) {
           byte[] Bytes;
           int ByteLength;
           string HexValue = "x0x1x2x3x4x5x6x7x8x9|||||||xAxBxCxDxExF";
           ByteLength = input.Length / 2;
           Bytes = new byte[ByteLength];
           for (int x = 0, i = 0; i < input.Length; i += 2, x += 1)
           {
                      Bytes[x] = (byte)(HexValue[Char.ToUpper(input[i + 0]) - '0'] << 4);
                      Bytes[x] |= (byte)(HexValue[Char.ToUpper(input[i + 1]) - '0']);
           }
           return Bytes;
}

The Mystery that is the Sharing Invitation

My initial research into the subject led me to this post on StackOverflow, which, while not entirely filling in the whole picture, started me on the right track…

Ok, according to the post the “hard part” was building the invitation itself? Well, after a bit of fussing around with code I randomly found I decided to bite the bullet and spend the next three days reading the documentation for the Exchange Messaging Protocol Specifications.

BTW, We are going to be focusing on the default Calendar folder for the remainder of this series. Making this functionality work more dynamically is certainly possible, but way too involved to be worth the effort for me.

Turns out that there are two major components to the Sharing Invitation for a user’s folder.


Figure 1: Sharing Message Object (High Level) – For details, read [MS-OXSHARE] Specification on MSDN

Figure 1: Sharing Message Object (High Level)

The first component, illustrated in Figure 1, is a normal email message that has a bunch of information (in the form of Extended Properties) that tells Exchange that this is a sharing invitation and who it’s for along with a couple other pieces of information. Some of the properties are constants, or rather constant depending on the situation, but we’ll get to that later…


Figure 2: Sharing Message Attachment (High Level)

Figure 2: Sharing Message Attachment (High Level)

As you can see from Figure 2, the second component is a magic “sharing_metadata.xml” file that is attached to the message before sending. It contains information relevant to the sharing of the folder as well. There are only a few XML elements to talk about regarding the sharing_metadata.xml file, so I will conclude this post with an overview of the file, how to obtain the information necessary for the document, and how to properly encode the data so you do not get a corrupted invitation message.

XML Attachment: Contents of the file

Here’s the information inside a sharing_metadata.xml document (the Id’s have been changed to protect the innocent):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0"?>
<SharingMessage xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/sharing/2008">
  <DataType>calendar</DataType>
  <Initiator>
    <Name>SharedByUserDisplayName</Name>
    <SmtpAddress>SharedByUser@contoso.com</SmtpAddress>
<EntryId>00000000DCA740C8C042101AB4B908002B2FE18201000656D2D4B65697A657220507
5626C6963205363686F6F6C732F6F753D6D696E6973747261746976652047726F757020284654
9424F48463233535092F636E3D52656369704757436F6E6E00</EntryId>
  </Initiator>
  <Invitation>
    <Providers>
      <Provider Type="ms-exchange-internal" TargetRecipients="sharedToUser@contoso.com">
        <FolderId xmlns="http://schemas.microsoft.com/exchange/sharing/2008">
00000000EA272C6387A99442B4B371A18905E4DB01007F8000</FolderId>
        <MailboxId xmlns="http://schemas.microsoft.com/exchange/sharing/2008">
00000000EA272C6387A993A547A9A1B0F249B6491B00000000000038A1BB1005E5101AA1BB08002B
2A56C20000454D534D44422E444C4C00000000000000001B55FA20AA6611CD9BC800AA002FC45A0C
000000445443454D585075626C6963205363686F6F6C732F6F753D4578636867652041646D6520
</MailboxId>
      </Provider>
    </Providers>
  </Invitation>
</SharingMessage>

XML Attachment: Elements and Values

That’s it huh? No big deal right? Well, unless you’re a Cylon you’re probably looking at the values in that XML document going WTF is that??? Don’t feel bad…I did too…until I found some interesting documentation in Microsoft Exchange Protocol Specification Library

For starters, it looks like we need 6 values (all strings) to load into the xml at the appropriate elements. Let’s take a look at them: sharing_metadata.xml Elements and relevant information about them

Great, so we have three HEX values we need to figure out, and we want to constrain to the standards as strictly as possible here, because even though I just wanna do it to do it, I also want to see if I can apply this to any user. That’s when I found the MS docs that specify how each of those HEX entries are determined:

XML Attachment: Getting the Initiator EntryId Value

Initiator > EntryId
Also known as: PidTagEntryId of the AddressBook object of the user sharing the folder)

The EntryId is broken down into the following pieces (as documented on MSDN):

Canonical name: PidLidSharingInitiatorEntryId
Description: Contains the value of the PidTagEntryId property (section 2.761) for the Address Book object of the currently logged-on user.
Property set: PSETID_Sharing {00062040-0000-0000-C000-000000000046}
Property long ID (LID): 0x00008A09
Data type:PtypBinary, 0x0102
Area: Sharing
Defining reference: [MS-OXSHARE] section 2.2.2.7
Alternate names: dispidSharingInitiatorEid
Flags (4 bytes): This value MUST be set to 0x00000000. Bits in this field indicate under what circumstances a short-term EntryID is valid. However, in any EntryID stored in a property value, these 4 bytes MUST be zero, indicating a long-term EntryID.
ProviderUID (16 bytes): The identifier for the provider that created the EntryID. This value is used to route EntryIDs to the correct provider and MUST be set to %xDC.A7.40.C8.C0.42.10.1A.B4.B9.08.00.2B.2F.E1.82.
Version (4 bytes): This value MUST be set to %x01.00.00.00.
Type (4 bytes): An integer representing the type of the object. It MUST be one of the values from the following table.

Value (hex bytes) Address book EntryID type
0x00000000%x00.00.00.00 Local mail user (Note there are other values for external recipients, etc but that’s outside the scope of this article)

X500DN (variable): The X500 DN of the Address Book object. The X500DN field is a null-terminated string of 8-bit characters.

So what this told me ultimately was there is a binary property called “PidLidSharingInitiatorEntryId” value I will be needing to assign, and I can use the same value for the EntryId on the sharing_metadata.xml file, but I will need to use the hex representation of that property instead of it’s binary native datatype. (I will need the binary value as well later, but that’s for next post)…

Luckily we can “construct” this value using the following code snippets:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        public String GetMailboxDN()
        {
            string result = "error";
            AutodiscoverService autodiscoverService = new AutodiscoverService();
            // The following RedirectionUrlValidationCallback required for httpsredirection.
            autodiscoverService.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
            autodiscoverService.Credentials = new WebCredentials(webCredentialObject);
            // Get the user settings.
            // Submit a request and get the settings. The response contains only the
            // settings that are requested, if they exist.
            GetUserSettingsResponse userresponse = autodiscoverService.GetUserSettings(
                txtImpersonatedUser.Text.ToString(),
                UserSettingName.UserDisplayName,
                UserSettingName.UserDN,
                );
                foreach (KeyValuePair usersetting in userresponse.Settings)
                {
                    if (usersetting.Key.ToString() == "UserDN")
                    {
                        result = usersetting.Value.ToString();
                    }
                }
                return result;
        }
        public String GetIntiatorEntryID()
        {
            String result = String.Empty;
            // Bind to EWS
            ExchangeService service = GetExchangeService();
service.ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, txtImpersonatedUser.Text.ToString());
              // Get LegacyDN Using the function above this one
              string sharedByLegacyDN = GetMailboxDN();
              // A conversion function from earlier
              string legacyDNinHex = ConvertStringToHex(sharedByLegacyDN);
            StringBuilder addBookEntryId= new StringBuilder();
         // Note while I was debugging I logged this to a text file as well
            using (StreamWriter w = new StreamWriter("BuildAddressBookEntryIdResult.txt"))
            {
                addBookEntryId.Append("00000000"); /* Flags */
                addBookEntryId.Append("DCA740C8C042101AB4B908002B2FE182"); /* ProviderUID */
                addBookEntryId.Append("01000000"); /* Version */
                addBookEntryId.Append("00000000"); /* Type - 00 00 00 00  = Local Mail User */
                addBookEntryId.Append(legacyDNinHex); /* Returns the userDN of the impersonated user */
                addBookEntryId.Append("00"); /* terminator bit */
                //Log(addBookEntryId.ToString(), w, "GetAddBookEntryId");
                w.Close();
            }
            result = addBookEntryId.ToString();
            service.ImpersonatedUserId = null;
            return result;
        }

So if you do this you should get a string returned that looks something like the following (one long string):

00000000DCA740C8C042101AB4B908002B2FE18201000000000000002F6F
3D53SDSDSDSDFGA6572205075626C6963205SDDFSD63686F6F63D4578636
8616E67652041646D696E6973747261746976652047726F7570202846594
FDFDFE4FDFFF435350444C54292F636E3D52

Congratulations! One value down…two more to go!!!

XML Attachment: Getting the FolderId Value

Invitation > FolderId
Also known as: EWS FolderId (converted to Hex of course) of the folder being shared
The FolderId that we want to use for the sharing_metadata.xml file can be obtained with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        public String GetConvertedEWSIDinHex(ExchangeService esb, String sID, String strSMTPAdd)
        {
            // Create a request to convert identifiers.
            AlternateId objAltID = new AlternateId();
            objAltID.Format = IdFormat.EwsId;
            objAltID.Mailbox = strSMTPAdd;
            objAltID.UniqueId = sID;
            //Convert  PR_ENTRYID identifier format to an EWS identifier.
            AlternateIdBase objAltIDBase = esb.ConvertId(objAltID, IdFormat.HexEntryId);
            AlternateId objAltIDResp = (AlternateId)objAltIDBase;
            return objAltIDResp.UniqueId.ToString();
        }
// Bind to the folder
Folder folderStoreInfo;
folderStoreInfo = Folder.Bind(service, WellKnownFolderName.Calendar);
string EwsID = folderStoreInfo.Id.UniqueId;
// The value of folderidHex will be what we need to use for the FolderId in the xml file
string folderidHex = GetConvertedEWSIDinHex(service,folderid,txtImpersonatedUser.Text);

Now on to the last mystery value:

XML Attachment: Getting the MailboxId Value

Invitation > MailboxId
Also known as: Structured Value of Sharing Folder’s parent Mailbox

Flags (4 bytes): This value MUST be set to 0x00000000. Bits in this field indicate under what circumstances a short-term EntryID is valid. However, in any EntryID stored in a property value, these 4 bytes MUST be zero, indicating a long-term EntryID.
ProviderUID (16 bytes): The identifier for the provider that created the EntryID. This value is used to route EntryIDs to the correct provider and MUST be set to %x38.A1.BB.10.05.E5.10.1A.A1.BB.08.00.2B.2A.56.C2.
Version (1 byte): This value MUST be set to zero.
Flag (1 byte): This value MUST be set to zero.
DLLFileName (14 bytes): This field MUST be set to the following value, which represents “emsmdb.dll”: %x45.4D.53.4D.44.42.2E.44.4C.4C.00.00.00.00.
WrappedFlags (4 bytes): This value MUST be set to 0x00000000.
WrappedProvider UID (16 bytes): This field MUST be set to one of the values in the following table.

Store object type ProviderUID value
Mailbox Store object %x1B.55.FA.20.AA.66.11.CD.9B.C8.00.AA.00.2F.C4.5A
Public folder Store object %x1C.83.02.10.AA.66.11.CD.9B.C8.00.AA.00.2F.C4.5A

WrappedType (4 bytes): The value of this field is determined by where the folder is located. For a mailbox store this value MUST be set to %x0C.00.00.00. For a public store, this value MUST be set to %x06.00.00.00.
ServerShortname (variable): A string of single-byte characters terminated by a single zero byte, indicating the short name or NetBIOS name of the server.
MailboxDN (variable): A string of single-byte characters terminated by a single zero byte and representing the X500 DN of the mailbox, as specified in [MS-OXOAB]. This field is present only for mailbox database

Using this information we can construct the proper value by using the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public String GetInvitationMailboxId()
{
    ExchangeService service = GetExchangeService();
    service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, txtImpersonatedUser.Text);
    // Generate The Store Entry Id for the impersonated user
    StringBuilder MailboxIDPointer= new StringBuilder();
    // Notice again the logging for debugging the results
    using (StreamWriter w = new StreamWriter("BuildInvitationMailboxIdResult.txt"))
    {
        MailboxIDPointer.Append("00000000"); /* Flags */
        MailboxIDPointer.Append("38A1BB1005E5101AA1BB08002B2A56C2"); /* ProviderUID */
        MailboxIDPointer.Append("00"); /* Version */
        MailboxIDPointer.Append("00"); /* Flag */
        MailboxIDPointer.Append("454D534D44422E444C4C00000000"); /* DLLFileName */
        MailboxIDPointer.Append("00000000"); /* Wrapped Flags */
        MailboxIDPointer.Append("1B55FA20AA6611CD9BC800AA002FC45A"); /* WrappedProvider UID (Mailbox Store Object) */
        MailboxIDPointer.Append("0C000000"); /* Wrapped Type (Mailbox Store) */
        MailboxIDPointer.Append(ConvertStringToHex(GetMailboxServer()).ToString()); /* ServerShortname (FQDN) */
        MailboxIDPointer.Append("00"); /* termination bit */
        MailboxIDPointer.Append(ConvertStringToHex(GetMailboxDN()).ToString()); /* Returns the userDN of the impersonated user */
        MailboxIDPointer.Append("00"); /* terminator bit */
        Log(MailboxIDPointer.ToString(), w, "GetInvitiationEntryID");
        w.Close();
    }
    service.ImpersonatedUserId = null;
    return MailboxIDPointer.ToString();
}

Conclusion

So THAT’s all there is to it….well…to the very first step that is…which was getting the values necessary to build our own
sharing_metadata.xml file.

Next post I will explain how I constructed the attachment, and start explaining how Extended Properties fits in to the puzzle.

Till next time…

Post navigation

10 comments on “Understanding Sharing Invitation Requests – EWS Managed API 1.2 : Part 1”

  1. SteveR says:

    I’ve been figuring out how to send a sharing invitation using EWS and have independently come to a somewhat similar solution. I saw your question on Stack Overflow but only thought to look to see if any answers had been posted the day after I worked out how to do it myself. 🙂

    A few observations:

    1) I construct the various Ids (PidLidSharingInitiatorEntryId etc) as byte arrays and convert them to hex strings later as needed using BitConverter.ToString(id).Replace(“-“, “”)

    private byte[] CombineHeaderAndData(byte[] header, params string[] data)
    {
    var enc = new ASCIIEncoding();
    byte[] ret;

    using (var memStream = new MemoryStream())
    {
    memStream.Write(header, 0, header.Length);

    foreach (string item in data)
    {
    byte[] dnBytes = enc.GetBytes(item);
    memStream.Write(dnBytes, 0, dnBytes.Length);
    memStream.WriteByte(new byte());
    }

    ret = memStream.ToArray();
    }

    return ret;
    }

    private byte[] UserEntryIdFromX500DN(string x500dn)
    {
    var header = new byte[] { 0x00, 0x00, 0x00, 0x00, 0xDC, 0xA7, 0x40, 0xC8, 0xC0, 0x42, 0x10, 0x1A, 0xB4, 0xB9, 0x08, 0x00, 0x2B, 0x2F, 0xE1, 0x82, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

    return CombineHeaderAndData(header, x500dn);
    }

    This seems simpler and perhaps more explicit.The spec says that the various pieces of data such as MailboxDN are “strings of single-byte characters” which seems to imply ASCII.

    2) The GetUserSettingsResponse.Settings is an IDictionary so you can just grab the value(s) you need straight out of it rather than loop through them looking for the one you want i.e. userresponse.Settings[UserSettingName.UserDN] as string

    3) You don’t show getting the ServerShortname needed for the value of PidLidSharingRemoteStoreUid but I’ve found that this can be derived from the UserSettingName.InternalMailboxServer value retrieved in the same way as the other settings. Since the entire process needs to retrieve several of these settings it’s more efficient to retrieve them all together rather than seperately since this results in multiple requests to the server.

    So far I haven’t had to construct and attach the sharing_metadata.xml manually for the sharing invitation to work correctly, although I’ve only tried it in OWA. It was my assumption that the message gets properly constructed on the server using the extended properties that are set on the message, and that includes the necessary metadata being created and attached. I’m not 100% certain about it but it’s working so far.

    Anyway, your S.O. question helped us on the way to figuring this all out, so thanks for that!

    Understanding Sharing Invitation Requests – EWS Managed API 1.2 : Part 2

    In my Last Post, I talked about how we get the three mystery hex values necessary to create the “sharing_metadata.xml” file, which is a crucial piece that get’s attached to a sharing invitiation message before it’s sent out to the user we want to share a folder with. Now that we have those values, let’s move on to actually building the XML document for the attachement…

    UPDATE 4/22: (Thanks for pointing this out SteveR!)

    I forgot to include in this post the method I use to actual GET the mailboxserver value for using in some of the code examples. Its in the user settings configuration. The code I used to obtain it is as follows:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public Dictionary<string, string> GetUserSettings(AutodiscoverService autodiscoverService)
    {
        // Get the user settings.
        // Submit a request and get the settings. The response contains only the
        // settings that are requested, if they exist.
        GetUserSettingsResponse userresponse = autodiscoverService.GetUserSettings(    
            txtImpersonatedUser.Text,
            UserSettingName.UserDisplayName,
            UserSettingName.InternalMailboxServer,
            UserSettingName.UserDN
        );
        Dictionary<string, string> myUserSettings = new Dictionary<string, string>();
        // Obviously this should be cleaned up with a switch statement
        // or something, but I was working through the problem hence the
        // extra effort on the code
        foreach (KeyValuePair<UserSettingName, Object> usersetting in userresponse.Settings)
        {
            if (usersetting.Key.ToString() == "InternalMailboxServer")
            {
                string[] arrResult = usersetting.Value.ToString().Split('.');
            
                myUserSettings.Add("InternalMailboxServer", arrResult[0].ToString());
            }
            if (usersetting.Key.ToString() == "UserDisplayName")
            {
                string[] arrResult = usersetting.Value.ToString().Split('.');
                myUserSettings.Add("UserDisplayName", arrResult[0].ToString());
            }
            if (usersetting.Key.ToString() == "UserDN")
            {
                string[] arrResult = usersetting.Value.ToString().Split('.');
                myUserSettings.Add("UserDN", arrResult[0].ToString());
            }
        }
        return myUserSettings;
    }

    Creating the sharing_metadata.xml File

    This part is fairly straightforward. Now that we have the 3 magic hex values, let’s look at the elements in the XML document one more time and see what we need in order for this to work:

    Elements:
    – Hex Id of the folder we want to share (check)
    – SmtpAddress of user sharing the folder (check)
    – The Hex Id of the Address book object for user sharing the folder (check)
    – The Hex Id of the mailbox where the folder resides (check)
    – SmtpAddress of the user being offered the invititation (check)
    – The datatype of the invitation (in this case “calendar” – check)

    Word of Caution

    When working with XML documents, case sensitivity is VERY IMPORTANT, so you must be sure you have all your values typed up correctly. I spent a good 3 – 4 hours wasting my time troubleshooting random things when ultimately the reason my invitation was corrupted was because I had the element in the XML file labeled as “EntryID” instead of “EntryId”. So pay close attention when(if) you build XML files statically like I did…

    By using the function below, we can generate a file that we will use later to attach to the sharing message object:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    public void CreateSharingMessageAttachment(string folderid, string userSharing, string userSharingEntryID, string invitationMailboxID, string userSharedTo, string dataType)
    {
        XmlDocument sharedMetadataXML = new XmlDocument();
        try
        {
            // just logging stuff as well during my debugging
            using (StreamWriter w = new StreamWriter("SharingMessageMetaData.txt",false,Encoding.ASCII))
            {
                // Create a String that contains our new sharing_metadata.xml file
                StringBuilder metadataString = new StringBuilder("<?xml version="1.0"?>");
                metadataString.Append("<SharingMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ");
                metadataString.Append("xmlns:xsd="http://www.w3.org/2001/XMLSchema" ");
                metadataString.Append("xmlns="http://schemas.microsoft.com/sharing/2008">");
                metadataString.Append("<DataType>" + dataType + "</DataType>");
                metadataString.Append("<Initiator>");
                metadataString.Append("<Name>" + GetDisplayName(userSharing) + "</Name>");
                metadataString.Append("<SmtpAddress>" + userSharing + "</SmtpAddress><EntryId>" + userSharingEntryID.Trim());
                metadataString.Append("</EntryId>");
                metadataString.Append("</Initiator>");
                metadataString.Append("<Invitation>");
                metadataString.Append("<Providers>");
                metadataString.Append("<Provider Type="ms-exchange-internal" TargetRecipients="" + userSharedTo + "">");
                metadataString.Append("<FolderId xmlns="http://schemas.microsoft.com/exchange/sharing/2008">");
                metadataString.Append(folderid);
                metadataString.Append("</FolderId>");
                metadataString.Append("<MailboxId xmlns="http://schemas.microsoft.com/exchange/sharing/2008">");
                metadataString.Append(invitationMailboxID);
                metadataString.Append("</MailboxId>");
                metadataString.Append("</Provider>");
                metadataString.Append("</Providers>");
                metadataString.Append("</Invitation>");
                metadataString.Append("</SharingMessage>");
               // MessageBox.Show(metadataString.ToString(), "metadataString before loading into soapEnvelope");
                sharedMetadataXML.LoadXml(metadataString.ToString());
                ExchangeFolderPermissionsManager.form1.Log(metadataString.ToString(), w, "Generate XML");
                // MessageBox.Show("returning SOAP envelope now");
                w.Close();
            }
            
            string tmpPath = Application.StartupPath + "\temp\";
            sharedMetadataXML.Save(tmpPath + "sharing_metadata.xml");
        }
        catch (Exception eg)
        {
            MessageBox.Show("Exception:" + eg.Message.ToString(), "Error Try CreateSharedMessageInvitation()");
           
        }
    }

    That’s pretty much all there is to the attachment file, so let’s move on to the actual invitation message itself…

    Background on my approach

    When you send an email message by way of the Exchange Web Services, API, it is fairly simple. The following code is from the example included in the Microsoft document Getting Started with EWS Managed API:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Create an email message and identify the Exchange service.
    EmailMessage message = new EmailMessage(service);
    // Add properties to the email message.
    message.Subject = "Interesting";
    message.Body = "The merger is finalized.";
    message.ToRecipients.Add("user1@contoso.com");
    // Send the email message and save a copy.
    message.SendAndSaveCopy();

    Not much to it right? Well if we look at the sharing invitation as an email message with just alot of extra properties tagged on, we come to the conclusion that the hard part about this is not actually creating the invitation message, but knowing what properties to set and how to set them properly. The majority of my time during this adventure I found myself pouring through three or four different variants and examples of how to set the properties, so I was getting confused and spending more time validating the information on Microsoft’s web site versus actually having problems with the code itself.

    My first issue was because I wasn’t familiar with EWS’s extended properties capabilities, I couldn’t validate my code was running right. Finally, I bit the bullet and posted a question regarding my tactics in the MSDN Forums. Glen Scales, whose posts and blog I ended up returning to frequently for information, was able to run my code successfully so at that point I knew I was working in the right direction, I just needed to set all the properties properly. At this point was when I returned to viewing my code line by line and discovering the “EntryId” attribute on my sharing xml file was actually entered as “EntryID”. Once I changed it, the message sent and I could open it up to share my calendar in OWA or Outlook 2010.

    Where I’m getting at is while I don’t believe you actually HAVE to set every one of these extended properties for a message to function properly, I was able to get these values to line up with their counterparts on my “control” invitation message sent through the normal means (ie sent through the client). So tweak/omit these at your own peril, but I got the process working so I wasn’t about to go back and try and figure out which ones were absolutely necessary. According to the Microsoft Protocol documents, these are the extended properties used in a sharing message so by God I was gonna set them!

    One interesting item I want to point out was something I was able to confirm with Glen Scales during this conversation regarding the question I had on whether or not me not properly reading some of these properties on my control message was that I generated it in OWA. My question and Glen’s response are below:

    (My post)

    Thanks for the tips Glen,

    Ok so the control reference (PR_Subject) totally came back and when I’m looking at the properties through outlook spy the ones I’m looking for (like x-sharing-capabilities) return a MAPI error so I’m thinking it’s one of two things:

    1) Could it be that since I generated the Invitation through OWA instead of the outlook client there are certain properties that don’t get populated when I generate the invite?

    or

    2) The weird bug thing you mentioned due to my Exchange Version (as you said, SP1 RU5)

    (Glen’s response)

    1) Yep I don’t why but if you do it via OWA none of those extended properties are created and only the sharing_metadata.xml attachment is availble.

    Ok, so from this I’m able to assume 1) my code works and 2) OWA was the right choice for a control because it sends a more “generic” form of sharing message that should be able to be used as a template, but since all my extra extended properties are working, again, this is why I didn’t try to ferret out the “unnecessary ones”…

    So Which Extended Properties Do I Use?

    I highly recommend you read the MS Protocol Specifications documents to familiarize yourself with all the properties and how they relate to the workflow of creating a sharing invitation. With that being said, I’m going to assume you either 1) don’t care or 2) are familiar with the protocols, so I am going to move on to the code I used to actually define and then set the extended properties on my sharing invitation.

    Let’s start off with the first part, actually defining which properties we are going to be adding to this message:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Bind to the web services and currently selected folder
               // Bind to Exchange and impersonate the guy I want to be the "sharer"
               ExchangeService service = GetExchangeService();
               service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, txtImpersonatedUser.Text);
               
               // Let's get our values regarding the sharing objects
               // Notice that this is where the previous functions I
               // talked about come into play....
               string tmpPath = Application.StartupPath + "\temp\";
               string folderid = GetSharedFolderId("Calendar");
               string folderidHex = GetConvertedEWSIDinHex(service,folderid,txtImpersonatedUser.Text);
               string initiatorEntryID = GetIntiatorEntryID();
               string invitationMailboxID = GetInvitationMailboxId(txtImpersonatedUser.Text);
               string ownerSMTPAddress = txtImpersonatedUser.Text;
               string ownerDisplayName = GetDisplayName(txtImpersonatedUser.Text));
               // This is where I need the binary value of that initiator ID we talked about
               byte[] binInitiatorEntryId = HexStringToByteArray(initiatorEntryID);
            

    Now that I have all our values ready to be used to set the appropriate properties, let’s define all the extended properties we will be using to attach to this message.

    1
    2
    3
    4
    5
    6
    7
    // This is the Guid of the Sharing Provider in Exchange, and it's value does not change
    Guid binSharingProviderGuid = new Guid("{AEF00600-0000-0000-C000-000000000046}");
    // Even though I don't think setting this property is mandatory,
    // it just seemed like the right thing to do and it works so I
    // ain't messin with it!                                       
    byte[] byteSharingProviderGuid = binSharingProviderGuid.ToByteArray();

    What we did there is first assign the GUID of the SharingProvider to a new Guid variable, because when we define these properties (remember, first we need to define them so our app knows what it means when we say ‘set extended property x to y’, since the API doesn’t have these properties available through all the usual means) we need to not only tell it the Id or Name, and what MapiProperty.Type the property is, we also need to tell it which providor is in charge. When we define extended properties we use the following format:

    ExtendedPropertyDefinition SomeProperty = new ExtendedPropertyDefinition(Provider,ID or Name,PropertyType);

    So without further adieu, let’s see how we define the Extended Properties for a Sharing Invitation message:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    // Sharing Properties (in order of reference according to protocol examples in: [MS-OXSHARE])
    // Common Message Object Properties
    // [MS-OXSHARE] 2.2.1
    ExtendedPropertyDefinition PidTagNormalizedSubject = new ExtendedPropertyDefinition(0x0E1D, MapiPropertyType.String);
    // The PidTagSubjectPrefix is a zero-length string, so I do not set it
    // ExtendedPropertyDefinition PidTagSubjectPrefix = new ExtendedPropertyDefinition(0x003D, MapiPropertyType.String);
    // Sharing Object Message Properties
    // [MS-OXSHARE] 2.2.2.1
    ExtendedPropertyDefinition PidLidSharingCapabilities = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A17, MapiPropertyType.Integer);
    // [MS-OXSHARE] 2.2.2.2
    ExtendedPropertyDefinition PidNameXSharingCapabilities = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Capabilities", MapiPropertyType.String);
    // Sections 2.3 and 2.4 are also zero-length strings, so I won't set those either
    // [MS-OXSHARE] 2.2.2.3
    // ExtendedPropertyDefinition PidLidSharingConfigurationUrl = new   //ExtendedPropertyDefinition(PropertySetSharing, 0x8A24, MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.2.4
    //ExtendedPropertyDefinition PidNameXSharingConfigUrl = new //ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Config-Url", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.2.5
    ExtendedPropertyDefinition PidLidSharingFlavor = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A18, MapiPropertyType.Integer);
    // [MS-OXSHARE] 2.2.2.6
    ExtendedPropertyDefinition PidNameXSharingFlavor = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Flavor", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.2.7
    ExtendedPropertyDefinition PidLidSharingInitiatorEntryId = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A09, MapiPropertyType.Binary);          
    // [MS-OXSHARE] 2.2.2.8
    ExtendedPropertyDefinition PidLidSharingInitiatorName = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A07, MapiPropertyType.String);          
    // [MS-OXSHARE] 2.2.2.9
    ExtendedPropertyDefinition PidLidSharingInitiatorSMTP = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A08, MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.2.10
    ExtendedPropertyDefinition PidLidSharingLocalType = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A14, MapiPropertyType.String);     
    // [MS-OXSHARE] 2.2.2.11
    ExtendedPropertyDefinition PidNameXSharingLocalType = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Local-Type", MapiPropertyType.String);      
    // [MS-OXSHARE] 2.2.2.12
    ExtendedPropertyDefinition PidLidSharingProviderGuid = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A01, MapiPropertyType.Binary);          
    // [MS-OXSHARE] 2.2.2.13
    ExtendedPropertyDefinition PidNameXSharingProviderGuid = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Provider-Guid", MapiPropertyType.String);          
    // [MS-OXSHARE] 2.2.2.14
    ExtendedPropertyDefinition PidLidSharingProviderName = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A02, MapiPropertyType.String);     
    // [MS-OXSHARE] 2.2.2.15          
    ExtendedPropertyDefinition PidNameXSharingProviderName = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Provider-Name", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.2.16
    ExtendedPropertyDefinition PidLidSharingProviderUrl = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A03, MapiPropertyType.String);        
    // [MS-OXSHARE] 2.2.2.17
    ExtendedPropertyDefinition PidNameXSharingProviderUrl = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Provider-URL", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.1
    ExtendedPropertyDefinition PidLidSharingRemoteName = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A05, MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.2
    ExtendedPropertyDefinition PidNameXSharingRemoteName = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Remote-Name", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.3
    ExtendedPropertyDefinition PidLidSharingRemoteStoreUid = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A48, MapiPropertyType.String);     
    // [MS-OXSHARE] 2.2.3.4
    ExtendedPropertyDefinition PidNameXSharingRemoteStoreUid = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Remote-Store-Uid", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.5
    ExtendedPropertyDefinition PidLidSharingRemoteType = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A1D, MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.6
    ExtendedPropertyDefinition PidNameXSharingRemoteType = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Remote-Type", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.7
    ExtendedPropertyDefinition PidLidSharingRemoteUid = new ExtendedPropertyDefinition(PropertySetSharing, 0x8A06, MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.3.8
    ExtendedPropertyDefinition PidNameXSharingRemoteUid = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "X-Sharing-Remote-Uid", MapiPropertyType.String);    
    // Additional Property Constraints
    // [MS-OXSHARE] 2.2.5.1
    ExtendedPropertyDefinition PidNameContentClass = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "Content-Class", MapiPropertyType.String);
    // [MS-OXSHARE] 2.2.5.2
    ExtendedPropertyDefinition PidTagMessageClass = new ExtendedPropertyDefinition(0x001A, MapiPropertyType.String);
    // From troubleshooting I noticed I was missing
    ExtendedPropertyDefinition PidTagPriority = new ExtendedPropertyDefinition(0x0026, MapiPropertyType.Integer);
    ExtendedPropertyDefinition PidTagSensitivity = new ExtendedPropertyDefinition(0x0036, MapiPropertyType.Integer);
    ExtendedPropertyDefinition PR_BODY_HTML = new ExtendedPropertyDefinition(0x1013, MapiPropertyType.Binary); //PR_BOD
    ExtendedPropertyDefinition PR_BODY = new ExtendedPropertyDefinition(0x1000, MapiPropertyType.String);
    ExtendedPropertyDefinition RecipientReassignmentProhibited = new ExtendedPropertyDefinition(0x002b, MapiPropertyType.Boolean);

    One more thing…during my troubleshooting I was thinking that part of the problem was that the message body was not available in regular and html format, and somehow that was causing an issue.

    Even though that didn’t turn out to be the case, I still went ahead and defined all instances I found for a message body and html counterpart, and built strings as well as the binary equivelants to set on the message…below is an example of what I did:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    string strPRBODYHTML = "<html dir="ltr">rn<head>rn<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">rn<meta name="GENERATOR" content="MSHTML 8.00.7601.17514">rn<style id="owaParaStyle">P {rn   MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px rn}rn</style>rn</head>rn<body fPStyle="1" ocsi="0">rn<tt>rn<pre>SharedByUserDisplayName (SharedByUserSmtpAddress) has invited you to view his or her Microsoft Exchange Calendar.rnrnFor instructions on how to view shared folders on Exchange, see the following article:rnrnhttp://go.microsoft.com/fwlink/?LinkId=57561rnrn*~*~*~*~*~*~*~*~*~*rnrn</pre>rn</tt>rn<div>rn<div style="direction: ltr;font-family: Tahoma;color: #000000;font-size: 10pt;">this is a test message</div>rn</div>rn</body>rn</html>rn";
               
    string strBODY = @"
    SharedByUserDisplayName (SharedByUserSmtpAddress) has invited you to view his or
    her Microsoft Exchange Calendar.
    For instructions on how to view shared folders on Exchange, see the
    following article:
    *~*~*~*~*~*~*~*~*~*
    test body
    ";
    // Convert these to hex and binary equivelants to assign to their relevant
    // extended properties
    string hexPRBODYHTML = ConvertStringToHex(strPRBODYHTML);
    byte[] binPRBODYHTML = HexStringToByteArray(hexPRBODYHTML);

    Finally! Creating the Message Itself

    Now, lets start building our invitation. First I created the sharing attachment using our CreateSharingMessageAttachment() method from earlier, so we have that in waiting. From there I started creating the invitation:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Create a new message
    EmailMessage invitationRequest = new EmailMessage(service);
    invitationRequest.Subject = "I'd like to share my calendar with you";
    invitationRequest.Body = "Sent by Exchange Administrator on behalf of user";
    invitationRequest.From = ownerSMTPAddress;
    invitationRequest.Culture = "en-US";
    invitationRequest.Sensitivity = Sensitivity.Normal;
    invitationRequest.Sender = txtImpersonatedUser.Text;
    // Set a sharing specific property on the message
    invitationRequest.ItemClass = "IPM.Sharing"; /* Constant Required Value [MS-ProtocolSpec] */

    Ok now let’s start assigning all of these extended properties to the message:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // Section 2.2.1
    invitationRequest.SetExtendedProperty(PidTagNormalizedSubject, "I'd like to share my calendar with you"); /* Constant Required Value [MS-OXSHARE] 2.2.1 */
    //invitationRequest.SetExtendedProperty(PidTagSubjectPrefix, String.Empty); /* Constant Required Value [MS-OXSHARE] 2.2.1 */
    // Section 2.2.2
    invitationRequest.SetExtendedProperty(PidLidSharingCapabilities, 0x40290); /* value for Special Folders */
    invitationRequest.SetExtendedProperty(PidNameXSharingCapabilities, "40290"); /* Test representation of SharingCapabilities value */
    //invitationRequest.SetExtendedProperty(PidLidSharingConfigurationUrl, String.Empty); /* Zero-Length String [MS-OXSHARE] 2.2.2.3 */
    //invitationRequest.SetExtendedProperty(PidNameXSharingConfigUrl, String.Empty); /* Zero-Length String [MS-OXSHARE] 2.2.2.4 */
    invitationRequest.SetExtendedProperty(PidLidSharingFlavor, 20310); /* Indicates Invitation for a special folder [MS-OXSHARE] 2.2.2.5 */
    invitationRequest.SetExtendedProperty(PidNameXSharingFlavor, "20310"); /* Text representation of SharingFlavor value [MS-OXSHARE] 2.2.2.6 */
    invitationRequest.SetExtendedProperty(PidLidSharingInitiatorEntryId, binInitiatorEntryId); /* Value from the Initiator/EntryId value in the Sharing Message attachment .xml document */
    invitationRequest.SetExtendedProperty(PidLidSharingInitiatorSMTP, txtImpersonatedUser.Text); /* Value from Initiator/Smtp Address in the Sharing message attachment .xml document */
    invitationRequest.SetExtendedProperty(PidLidSharingInitiatorName, ownerDisplayName); /* Value from Initiator/Name Address in the Sharing message attachment .xml document */
    invitationRequest.SetExtendedProperty(PidLidSharingLocalType, "IPF.Appointment"); /* MUST be set to PidTagContainerClass of folder to be shared */
    invitationRequest.SetExtendedProperty(PidNameXSharingLocalType, "IPF.Appointment"); /* MUST be set to same value as PidLidSharingLocalType */
    invitationRequest.SetExtendedProperty(PidLidSharingProviderGuid, byteSharingProviderGuid); /* Constant Required Value [MS-OXSHARE] 2.2.2.12 */
    invitationRequest.SetExtendedProperty(PidNameXSharingProviderGuid, "AEF0060000000000C000000000000046"); /* Constant Required Value [MS-OXSHARE] 2.2.2.13 */
    invitationRequest.SetExtendedProperty(PidLidSharingProviderName, "Microsoft Exchange"); /* Constant Required Value [MS-OXSHARE] 2.2.2.14 */
    invitationRequest.SetExtendedProperty(PidNameXSharingProviderName, "Microsoft Exchange"); /* Constant Required Value [MS-OXSHARE] 2.2.2.15] */
    invitationRequest.SetExtendedProperty(PidLidSharingProviderUrl, "HTTP://www.microsoft.com/exchange"); /* Constant Required Value [MS-OXSHARE] 2.2.2.16 */
    invitationRequest.SetExtendedProperty(PidNameXSharingProviderUrl, "HTTP://www.microsoft.com/exchange"); /* Constant Required Value [MS-OXSHARE] 2.2.2.17 */
    // Section 2.2.3
    invitationRequest.SetExtendedProperty(PidLidSharingRemoteName, "Calendar"); /* MUST be set to PidTagDisplayName of the folder being shared */
    invitationRequest.SetExtendedProperty(PidNameXSharingRemoteName, "Calendar"); /* MUST be set to same value as PidLidSharingRemoteName */
    invitationRequest.SetExtendedProperty(PidLidSharingRemoteStoreUid, invitationMailboxID); /* Must be set to PidTagStoreEntryId of the folder being shared */
    invitationRequest.SetExtendedProperty(PidNameXSharingRemoteStoreUid, invitationMailboxID); /* MUST be set to same value as PidLidSharingRemoteStoreUid */
    invitationRequest.SetExtendedProperty(PidLidSharingRemoteType, "IPF.Appointment"); /* Constant Required Value [MS-OXSHARE] 2.2.3.5 */
    invitationRequest.SetExtendedProperty(PidNameXSharingRemoteType, "IPF.Appointment"); /* Constant Required Value [MS-OXSHARE] 2.2.3.6 */
    invitationRequest.SetExtendedProperty(PidLidSharingRemoteUid, folderidHex); /* MUST be set to PidTagEntryId of folder being shared */
    invitationRequest.SetExtendedProperty(PidNameXSharingRemoteUid, folderidHex); /* Must be set to same value as PidLidSharingRemoteUid */
    // Section 2.2.5
    invitationRequest.SetExtendedProperty(PidNameContentClass, "Sharing"); /* Constant Required Value [MS-ProtocolSpec] */
    invitationRequest.SetExtendedProperty(PidTagMessageClass, "IPM.Sharing"); /* Constant Required Value [MS-ProtocolSpec] */
     // ********* ADDITIONAL MAPPED PROPERTIES IM FINDING AS I TROUBLESHOOT ********************** //
    invitationRequest.SetExtendedProperty(PidTagPriority, 0); /* From troubleshooting I'm just trying to match up values that were missing */
    invitationRequest.SetExtendedProperty(PidTagSensitivity, 0); /* From troubleshooting as well */
    invitationRequest.SetExtendedProperty(PR_BODY_HTML, binPRBODYHTML); /* From troubleshooting OWA error pointing to serializing HTML failing */
    invitationRequest.SetExtendedProperty(PR_BODY, strBODY);
    invitationRequest.SetExtendedProperty(RecipientReassignmentProhibited, true); /* Because it seemed like a good idea */

    So there you have it, the message has now been formatted correctly to be interpreted by Exchange to be an internal sharing invitation between two mailboxes. But we’re not done yet!

    Attaching the XML file and Sending the Message

    The last piece focuses on properly attaching the xml document and sending it off to the user. Note I said PROPERLY. The only way I was able to get a byte for byte match on the xml file once it had been attached was by using the code below. I’m not saying there’s most likely a better way, but everything else I tried always appended 3 extra bytes to the beginning of the file…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Add a file attachment by using a stream
    // We need to do the following in order to prevent 3 extra bytes from being prepended to the attachment
    string sharMetadata = File.ReadAllText(tmpPath + "sharing_metadata.xml",Encoding.ASCII);
    byte[] fileContents;
    UTF8Encoding encoding = new System.Text.UTF8Encoding();
    fileContents = encoding.GetBytes(sharMetadata);
    // fileContents is a Stream object that represents the content of the file to attach.
    invitationRequest.Attachments.AddFileAttachment("sharing_metadata.xml",fileContents);
             
    // This is where we set those "special" headers and other pertinent
    // information I noted in Part 1 of this series...
    Attachment thisAttachment = invitationRequest.Attachments[0];
    thisAttachment.ContentType = "application/x-sharing-metadata-xml";
    thisAttachment.Name = "sharing_metadata.xml";
    thisAttachment.IsInline = false;

    Now that everythings set to go it’s as simple as addressing the messaging and sending it off:

    1
    2
    3
    4
    5
    6
    7
    8
    // Add recipient info and send message
    invitationRequest.ToRecipients.Add(sharedToUser);
    invitationRequest.SendAndSaveCopy();
    // I always end my methods by returning the EWS
    // impersonated user to null to clean up
    service.ImpersonatedUserId = null;

    Conclusion

    While not necessarily the easiest or most intuitive way to create and send a sharing message invitation through EWS, I definitely feel like this is only the tip of the iceberg and can’t wait till another project comes along that sparks my curiosity and requires me to get my hands dirty with some more Exchange Web Services…

    This shows me that Microsoft is opening more and more up to people like me who are not necessarily “in the know” in the world of development, but are willing to do the required research necessary to understand and work through their messaging service, not against it!

    In any case this was a great opportunity for me to learn more about EWS and Exchange 2010 administration possibilities. I do most of my work in powershell, but this was a good opportunity as well to jump back into c# for a little bit…

    This post was geared towards someone trying to understand what it takes for sharing invitations with EWS (and has a little bit of development experience) out there, because I always found the questions regarding the subject having fairly vague or partial answers. That led me to believe it was possible but required alot of detail and attention. Now all these pieces are put together in one place, so hope it helps someone else!!!

    Till next time…

Exchange 2010 Certificate errors

Exchange 2010 Certificate cant complete pending request

Open certificate store (certmgr.msc) – if you have tried to complete the pending certificate request then you should find the certificate from you CA is installed but doesn’t have an association with the private key generated by the request (hence the request is still considered to be pending). Open the the certificate from the CA and on the details tab find the thumbprint field and copy it to your clipboard (CTRL-C).

Now run the following command from a command prompt:
certutil -repairstore My “<thumbprint>”

Refresh your view of the certificate store and hopefully your cert is now associated with its private key! Likewise Exchange will now list the certificate and allow you to assign services to it.

Error message when Outlook tries to connect to a server by using an RPC connection or an HTTPS connection: “There is a problem with the proxy server’s security certificate”

Launching Outlook returns one of the following messages.

  • There is a problem with the proxy server’s security certificate, %s. Outlook is unable to connect to this server. (%s)
  • There is a problem with the proxy server’s security certificate, %s. The name on the security certificate is invalid or does not match the name of the site. Outlook is unable to connect to this server. (%s)
  • There is a problem with the proxy server’s security certificate, %s. The security certificate is not from a trusted certifying authority. Outlook is unabletoconnect tothis server.(%s)”
  • There is a problem with the proxy server’s security certificate. The name on the security certificate is invalid or does not match the name of the target site outlook.office365.com. Outlook is unable to connect to the proxy server (Error Code 0)

Refer to the following Microsoft KB article

KB923575

The articles resolution is directly from the KB article from Microsoft.

Method 1: Examine the certificate

Use this method if you receive either error message 1 or error message 2. Examine the certificate. Then, contact your system administrator to resolve this issue.

To examine the certificate, follow these steps:

  1. In Microsoft Internet Explorer, connect to the RPC server or to the secure server. For example, type https://www.server_name.com/rpc in the Address bar of the Web browser, and then press ENTER.

    Note The server_name placeholder references the RPC server name or the secure server name.

  2. Double-click the padlock icon that is located in the lower-right corner of the Web browser.
  3. Click the Details tab.
  4. Note the information in the following fields:
    • Valid to
      The Valid to field indicates the date until which the certificate is valid.
    • Subject
      The data in the Subject field should match the site name.

Method 2: Install the trusted root certificate

Use this method if you receive error message 3. To install the trusted root certificate, follow these steps:

  1. Click Install Certificate when you are prompted with the Certificate dialog box.
  2. Click Next.
  3. Click to select the Place all certificate in the following store check box.
  4. Click Browse.
  5. Click Trusted Root Certification Authorities, and then click OK.
  6. Click Next.
  7. Click Finish.
  8. Click OK.

Method 3: Disable the third-party add-in or the third-party browser add-in

Use this method to disable the third-party add-in or third-party browser add-in if you receive error message 4.

Disable third-party add-ins

  1. Start Outlook in safe mode to help isolate the issue. To do this, click Start, click Run, type outlook.exe /safe, and then click OK.

    If Outlook successfully starts in safe mode, the issue that you’re experiencing may be caused by a third-party add-in.

  2. Check for third-party COM add-ins and disable them. To do this, follow these steps:
  1. On the File menu, click Options, and then click Add-Ins.
  2. In the Manage box, click COM Add-ins, and then click Go.
  3. Click to clear the check box next to the third-party add-ins that you want to disable.
  4. Restart Outlook.

For more info, see the “Step 6: Start Outlook in safe mode” section of the following Microsoft Knowledge Base article:

2632425

(http://support.microsoft.com/kb/2632425)

How to troubleshoot crashes in Outlook 2010 and Outlook 2013

Disable third-party browser add-ins

Outlook uses Internet Explorer settings for HTTP requests. If a third-party browser add-in is causing this issue, disable it in Internet Explorer. For steps on how to do this, see the “Disable add-ons in Internet Explorer” section of the following Microsoft Knowledge Base article:

956196

(https://support.microsoft.com/kb/956196/ )

“Internet Explorer cannot display the webpage” error

Item Recovery in Exchange 2010

Author: Ross Smith IV

A while back, I blogged about the mechanics of the Single Item Recovery features included in Exchange 2010. In this post, I discuss how you can utilize this functionality to recover accidentally or maliciously deleted items.

Essentially there are two steps:

  1. Search – Determining the location of the missing items.
  2. Recovery – Retrieving the missing items.

Remember, in order to discover and recover the data, each mailbox needs to have Single Item Recovery enabled prior to the accidental purge event. Therefore, we recommend enabling Single Item Recovery for mailboxes as part of the Exchange 2010 upgrade process.

The Scenario

Ross sent his administrative assistant, Julie, a message regarding his upcoming trip to Seattle, specifically requesting Julie to book his itinerary. Unfortunately, before she could work on Ross’ request, Julie shift-deleted the message while cleaning out her mailbox. Like most users, Julie has done this before and is familiar with the Recover Deleted Items capability within Outlook. However, this time, Julie made the mistake of clicking the delete button for the message in question instead of clicking the recover button. Panicking, Julie calls Help Desk to request recovery of the item.

Step 1: Search

The help desk ticket results in a workflow process that is performed by an IT administrator who has necessary rights to perform searches (in this scenario, the Help Desk technician’s user account has been delegated the Discovery Management role).

Note: By default, no accounts have the ability to perform mailbox searches. You can either create a custom role group to allow an administrator to search only a subset of mailboxes, or add the administrator to the Discovery Management built-in role group (which allows them to search all mailboxes in the Exchange organization) by using the following command:
Add-RoleGroupMember “Discovery Management” -Member <user account>

The Help Desk technician has two choices for performing discovery, and the choice will depend on the target user’s client access license (CAL):

  1. If the users included in the search have Standard CALs, the Help Desk technician can only use the Search-Mailbox cmdlet.
  2. If the users included in the search have Enterprise CALs, the Help Desk technician can also use the New-MailboxSearch cmdlet, or the Multi-Mailbox Searchfeature in the Exchange Control Panel (ECP).

In Julie’s case, she provided the Help Desk technician with the following information:

  • The message was sent from her boss.
  • The message contains the word “Seattle”.

Searching messages by using Search-Mailbox

When a mailbox with a Standard CAL will be searched, the Search-Mailbox cmdlet will be used. The Search-Mailbox cmdlet requires the following information:

  • The mailbox to be searched
  • The search query criteria
  • The mailbox and folder where the results will be placed
  1. Knowing this information, the Help Desk technician executes the following command from the Shell:

    Search-Mailbox sec -SearchQuery “from:’boss’ AND seattle” -TargetMailbox “Discovery Search Mailbox” -TargetFolder “Secretary Recovery” -LogLevel Full

    Note: Search-Mailbox does not allow the target mailbox to be the same as the source mailbox. Search-Mailbox does allow you to be very specific in your search criteria. Besides scoping the search with the SearchQuery parameter using Advanced Query Syntax (AQS), in Exchange 2010 SP1 you can also use theSearchDumpsterOnly switch to search only items in the dumpster.

    The Help Desk technician receives the following output:

    RunspaceId : fb25cadf-a63f-4e88-8567-cb4ae1b30ade
    Identity : corp.contoso.com/Users/Secretary
    TargetMailbox : corp.contoso.com/Users/DiscoverySearchMailbox {D919BA05-46A6-415f-80AD-7E09334BB852}
    TargetPSTFile :
    Success : True
    TargetFolder : Secretary RecoverySecretary-4/14/2010 6:28:33 AM
    ResultItemsCount : 1
    ResultItemsSize : 1.577 KB (1,615 bytes)

  2. The Help Desk technician then logs into OWA and opens the Discovery Search Mailbox via the Open Other Mailbox option:

    Note: The OWA and ECP screenshots are from Exchange 2010 SP1. These are preliminary screen shots from pre-Beta software that are subject to change before the final release of SP1.

  3. The Help Desk technician navigates the folder structure within the Discovery Search Mailbox and verifies that he has recovered the item in question:

Searching messages by using Multi-Mailbox Search

When a mailbox with an Enterprise CAL will be searched, the administrator can use the Multi-Mailbox Search feature in the Exchange Control Panel. The Help Desk technician takes the following steps:

  1. He launches the Exchange Control Panel via https://mail.contoso.com/ecp and logs on using his credentials.
  2. From the Options drop-down, he selects Manage My Organization.
  3. He clicks on Service Level and selects the Mailbox Searches applet.
  4. He clicks New to create a new search request which requires at least the following information:
    1. The search query criteria
    2. The mailbox to be searched
    3. The mailbox and folder where the results will be placed
  5. When the results are obtained, he can either click on the [Open] link in the Mailbox Searches Results pane, or open the Discovery Search Mailbox via the Open Other Mailbox option from within OWA.
  6. He navigates the folder structure within the Discovery Search Mailbox and verifies that he has recovered the item in question:

Step 2: Recovery

At this point the Search phase is complete and the Recovery phase begins. There are two options for how to recover and return the item back to the user and it depends on the version of Exchange 2010 you have deployed:

  1. If you are running Exchange 2010 RTM or later, you can utilize the Search-Mailbox cmdlet to restore the item back to the user.
  2. If you are running Exchange 2010 SP1, you can utilize the PST import and export cmdlets to restore the item back to the user.

Search-Mailbox Recovery Process

  1. The Help Desk technician executes the following command from the Shell:

    Search-Mailbox “Discovery Search Mailbox” -SearchQuery “from:’boss’ AND seattle” -TargetMailbox sec -TargetFolder “Recovered by HelpDesk” -LogLevel Full -DeleteContent

    He receives the following output:

    RunspaceId : fb25cadf-a63f-4e88-8567-cb4ae1b30ade
    Identity : corp.contoso.com/Users/DiscoverySearchMailbox {D919BA05-46A6-415f-80AD-7E09334BB852}
    TargetMailbox : corp.contoso.com/Users/Secretary
    TargetPSTFile :
    Success : True
    TargetFolder : Recovered by HelpDeskDiscovery Search Mailbox-4/14/2010 6:32:49 AM
    ResultItemsCount : 1
    ResultItemsSize : 1.577 KB (1,615 bytes)

  2. He notifies Julie that the item is recovered. Julie logs into her mailbox and verifies she has the correct item:

It’s important to note that due to the two-step process involved with Search-Mailbox (copying the results to the Discovery Mailbox and then copying the results back to the user’s mailbox) the hierarchy is same for the end user – the root of the Discovery Search Mailbox, as well as the folder target that was used to place the item in the Discovery Search Mailbox, are both visible.

PST Export/Import Recovery Process

Exchange 2010 SP1 includes infrastructure that allows administrators to perform bulk import and export of PST files without requiring the installation of the Outlook client. This infrastructure, supported by the cmdlets *-MailboxImportRequest and *-MailboxExportRequest, leverages the Mailbox Replication Service and the framework that exists for moving mailboxes between databases (see Understanding Move Requests for more information).

To use this functionality, two prerequisites must be met:

  1. The person performing the import or export must have the appropriate permissions within Exchange. By default, no RBAC role group provides this functionality. To grant the ability for a help desk administrator, compliance officer, or Exchange administrator to perform bulk import/export capabilities against all mailboxes, the following commands must be executed:

    New-RoleGroup “Mailbox Import-Export Management” -Roles “Mailbox Import Export”
    Add-RoleGroupMember “Mailbox Import-Export Management” -Member <user account>

    The first command creates a new role group that grants access to the *-MailboxImportRequest and *-MailboxExportRequest cmdlets. The second command adds a user to the role group.

  2. The Exchange Trusted Subsystem security group must have Full Control/Owner permissions on the file share that will be used to temporarily store the PST files.

In this scenario, the Help Desk technician is a member of the Mailbox Import-Export Management role group and thus can utilize the Import and Export cmdlets. The Help Desk technician:

  1. Runs the following command from the Shell to export the recovered data from the Discovery Search Mailbox to a PST file:

    New-MailboxExportRequest -Mailbox “Discovery Search Mailbox” -FilePath “\exchsvrHelpDeskPstSecretaryRecovery.pst” -ContentFilter {Subject -eq “april travel plans”} -SourceRootFolder “Secretary Recovery”

  2. Runs the following command from the Shell to import the recovered data into Julie’s mailbox:

    New-MailboxImportRequest -Mailbox sec -FilePath “\exchsvrHelpDeskPstSecretaryRecovery.pst” -TargetRootFolder “Recovered By HelpDesk”

  3. Notifies Julie that the item is recovered.

At this point, Julie logs into her mailbox and verifies she has the correct item:

Conclusion

Exchange 2010 provides you the means to ensure data is not deleted from the system prior to the expiration of its deleted item retention. In the event that a message is accidentally or maliciously purged from the user’s dumpster, it can be easily recovered and restored using built-in tools.

Ross Smith IV

Exchange 2010 Search and Restore deleted email

 Source: Did You Restart Blog?

Story goes something like this…  (we’ll use a fictitious name of Mary Lost to protect the innocent here)
Mary: “I never got super important email”.
Me: “Are you sure they sent it?”
Mary: “Definitely”
Me: Type the send or subject in the search and click “all outlook items”
Mary: “Can’t, I never got it at all”
Me: “Okay, let me see what I can find, who sent it”

And so my quest began to find the “missing” email that was never received but definitely had been sent.

To start I do the simple search the users mailbox.  Attach mailbox, search, nothing…

Now we can look to see if the system ever received it.  In my case we use MXLogic (now McAfee) so to start with I could run a message audit at that level.  Yep, mxlogic shows it being delivered to the Exchange server.  Don’t use MXLogic?  No problem, just go straight to the Exchange server to look.
Check Exchange for receipt:

  1. Open the EMC
  2. Toolbox
  3. Tracking Log Explorer
  4. Enter recipient, Subject, dates and look for the email.
  5. Note: I found it best to do this from the Exchange server itself.  Otherwise you have to properly populate the “server” field, and even then I got mixed results.

Once you’ve found the message you can see the EventID which will likely be “RECEIVE”.  If you can’t find it here then likely the message was never sent.

Great, now I KNOW it was delivered, perhaps it was deleted?  After some research I found that it may not be easy to tell if it was deleted, etc unless audit logging is turned on before hand.  Bleh, we don’t need to know that bad.  But can we restore it…?  That’s what is really important here.

So we know:

  1. It was delivered to the mailbox
  2. It’s no longer there
  3. Thus it was likely deleted
  4. Since it’s not in the deleted items folder, it was either SHIFT Deleted or also deleted from deleted items or the computer monster ate it.
  5. All that really matters is it’s gone and we want it back.

Fire up the EMS (powershell)!
In this case I’m going to search Mary Lost’s mailbox for the SearchQuery string (subject, from, etc) and then give it a TargetMailbox that is my mailbox so that when it’s restored it goes into my email instead under a TargetFolder named “recovery”.  (Targetfolder will be autocreated if it doesn’t already exist)

Search-Mailbox mlost -TargetMailbox Me -TargetFolder “Recovery” -SearchQuery “from:important@dude.com” -LogLevel Full

Now Mary’s missing email is in my mailbox under a folder called “Recovery”.  Copy it back to her inbox and all done 🙂

Exchange 2010 – The certificate is invalid for Exchange Server usage

Source: Did You Restart Blog repost

After attempting to open OWA I received a lovely message about the certificate being invalid today.  Huh?  That can’t be right.  Unfortunately we don’t utilize OWA very often, so the error had gone unnoticed for a long period of time.

First things first, look at the cert.

  • Certificate path is fine
  • Still within the valid date timeframe
  • SAN cert and all the DNS names look fine
  • As far as the certificates MMC all is swell.

But Exchange still shows “The certificate is invalid for Exchange Server usage”
After some browsing on the old google I find lots about this when the cert path is wrong.  So I play around with the intermediate / roots, but feel pretty confident that it’s correct (and the cert is showing the path valid).

Finally, I assign the Exchange roles to the self signed cert, delete the third party cert, and reimport it.  Same error, but now I of course can’t assign the roles back to it because it’s invalid.  So, of course after a few minutes people get a popup about the self signed cert.  Doh.  No problem though.  We can force that with the shell.

  • Get-ExchangeCertificate | fl
  • Find the cert wanted and get it’s Thumbprint
  • Enable-ExchangeCertificate -Thumbprint [thumbprintfromabove] -Services “SMTP,IIS”  (we don’t use POP or IMAP)

Okay, at least now we’re back where we had been, but what’s going on.

Opening up the shell I do a Get-ExchangeCertificate -Thumbprint thumbprint## | fl.  It shows a RootCAType of unknown.  Eh?  That’s definitely not right.

I pull up https://www.digicert.com/help/ and do a cert check.  Uhm, pretty sure it shouldn’t say “SSL Certificate is revoked”.  Yikes!

After some more head scratching I recall that with the latest project that I’m working with in my off hours (Exchange 2013) I had rekeyed the cert.  Of course when I rekeyed the cert I did import the new cert onto the old Exchange 2010 box, so that shouldn’t be the issue.

So, I look at the new Exchange 2013 box cert and compare it’s Serial Number to the one on the Exchange 2010.  They should be the same, but what the heck they are not!  Somewhere in the process I messed up the import into the 2010 box. (and I know I did the import, I logged it in our tickets with the steps)

Export the cert again from Exchange 2013, quick import into 2010, reassign the roles and all is happy!

So:

  1. Exchange doesn’t specifically complain that the cert is revoked.  It just states it’s invalid.
  2. If I had paid more attention to the OWA error I would have seen that it specifically said “The organizations certificate has been revoked” and it was correct.
  3. The certificates snap-in mmc doesn’t, as far as I can tell, show when a cert has been revoked.
  4. Certificates can be dang confusing, double check that you’ve got the right one (serial number seems to be a good way).