Exchange Web services example
=============================
Overview
--------
According to Microsoft *"Exchange Web Services (EWS) provides the functionality to enable client applications to communicate with the Exchange server. EWS provides access to much of the same data that is made available through Microsoft OfficeOutlook. EWS clients can integrate Outlook data into Line-of-Business (LOB) applications. SOAP provides the messaging framework for messages sent between the client application and the Exchange server. The SOAP messages are sent by HTTP."* [link to article.](https://msdn.microsoft.com/en-us/library/office/dd877045.aspx)
You will need an Outlook email account to test the EWS client application. Create an [Outlook email account](https://www.microsoft.com/en-us/outlook-com/) if you do not have one already.
It is recommended to read the CodeProject article written by Bethany Sanders and Ning Xie [How to Use Exchange Web Service in C++ With gSOAP.](https://www.codeproject.com/Articles/1119224/How-to-Use-Exchange-Web-Service-in-Cplusplus-With) Below you will find the project build instructions with links to the files you can download from here, and an example EWS client application explained.
[![To top](../../images/go-up.png) To top](#)
Build steps
-----------
Before running wsdl2h to generate the EWS services definitions for C++, we want to assign meaningful XML namespace prefixes to the EWS service. To do so, create a [`typemap.dat`](typemap.dat) file specifically for EWS with the following two lines:
[command]
ewsmsg = "http://schemas.microsoft.com/exchange/services/2006/messages"
ewstype = "http://schemas.microsoft.com/exchange/services/2006/types"
Or download this [`typemap.dat`](typemap.dat) file.
Assuming wsdl2h is HTTPS-enabled (by default) now execute in the directory with the [`typemap.dat`](typemap.dat) file:
[command]
wsdl2h -u -o service.h https://{your-exchange-server}/ews/Services.wsdl
where `{your-exchange-server}` your EWS server's domain.
If there is trouble executing wsdl2h on this URL, then download the following copies of the WSDL and XSD files instead:
- [Services.wsdl](Services.wsdl)
- [messages.xsd](messages.xsd)
- [types.xsd](types.xsd)
and execute wsdl2h as follows:
[command]
wsdl2h -u -o service.h Services.wsdl
The generated [`service.h`](service.h) interface header file contains C++ declarations of the EWS services, the service operations and the C++ data types exchanged by the operations marshalled in SOAP/XML messages.
The second step is to use the soapcpp2 tool on the interface header file [`service.h`](service.h) created by the wsdl2h tool:
[command]
soapcpp2 -C -r -j -I /path/to/gsoap/import service.h
where `/path/to/gsoap/import` points to the `import` directory in the gSOAP source distribution.
This command generates the following client-side code (`-C` option), a report (`-r` option) with C++ service proxy classes (`-j` option):
* [`soapStub.h`](soapStub.h) a copy of the specification in plain C/C++ header file syntax without annotations.
* [`soapH.h`](soapH.h) declares XML serializers.
* [`soapC.cpp`](soapC.cpp) implements XML serializers.
* [`soapExchangeServiceBindingProxy.h`](soapExchangeServiceBindingProxy.h) defines the client-side XML services API proxy class `ExchangeServiceBindingProxy`.
* [`soapExchangeServiceBindingProxy.cpp`](soapExchangeServiceBindingProxy.cpp) implements the client-side XML services API proxy class `ExchangeServiceBindingProxy`.
* [`ExchangeServiceBinding.nsmap`](ExchangeServiceBinding.nsmap) XML namespace binding table, you should #include this file.
* [`soapReadme.md`](soapReadme.html) service and data binding interface details.
These files together are needed to build a EWS client application and should be compiled with your main application code and the gSOAP library `stdsoap2.cpp` (declared in `stdsoap2.h`):
[command]
c++ -DWITH_OPENSSL -o yourapp yourapp.cpp soapExchangeServiceBindingProxy.cpp soapC.cpp stdsoap2.cpp -lssl -lcrypto
OpenSSL is required to use EWS with HTTPS, thus the executable is built with `-DWITH_OPENSSL` and the OpenSSL libraries are linked.
Using the ExchangeServiceBindingProxy
-------------------------------------
After generating the source code files that we need to access our EWS server, we can start building EWS client applications.
The first thing to do is to include the header files that we need and then instantiate a EWS proxy, e.g. in the application main:
#include "soapExchangeServiceBindingProxy.h"
#include "ExchangeServiceBinding.nsmap"
int main()
{
ExchangeServiceBindingProxy ews(
"https://outlook.office365.com/EWS/Exchange.asmx",
SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_XML_NOTYPE);
soap_ssl_init();
if (soap_ssl_client_context(
ews.soap,
SOAP_SSL_DEFAULT,
NULL,
NULL,
"cacerts.pem",
NULL,
NULL))
{
// could not initialize OpenSSL
ews.soap_stream_fault(std::cerr);
exit(1);
}
It is not recommeded to skip the host check, but if necessary use `SOAP_SSL_SKIP_HOST_CHECK` instead of `SOAP_SSL_DEFAULT`. The `cacerts.pem` file is included with the gSOAP source tree in gsoap/samples/ssl. Updated `cacerts.pem` certificates can be [downloaded here.](https://www.genivia.com/files/cacerts.zip)
EWS servers requires authentication with HTTP Basic auth or NTLM, so we set the credentials:
ews.soap->userid = "your-userid";
ews.soap->passwd = "your-passwd";
Next we need to set the SOAP Header to the EWS 2010 version:
_ewstype__RequestServerVersion *rsv = soap_new__ewstype__RequestServerVersion(ews.soap);
rsv.Version = ewstype__ExchangeVersionType__Exchange2010_USCORESP2;
ews.soap_header(NULL, NULL, NULL, rsv, NULL, NULL);
This will serialize the SOAP Header as:
[xml]
Exchange2010_SP2
...
Note that managed allocation of class instances and other types on the heap is done with `soap_new_Type(ews.soap)` where `Type` is a serializable type (name). All managed heap allocations are deleted with `ews.destroy()`. See [C++ memory management.](https://www.genivia.com/doc/databinding/html/index.html#memory2)
There are many types of operations that EWS 2010 services provide as explained by [Microsoft.](https://msdn.microsoft.com/en-us/library/office/dd877045(v=exchg.140).aspx) The `ExchangeServiceBindingProxy` methods to invoke EWS service operations are:
- `ResolveNames`
- `ExpandDL`
- `GetServerTimeZones`
- `FindFolder`
- `FindItem`
- `GetFolder`
- `ConvertId`
- `UploadItems`
- `ExportItems`
- `CreateFolder`
- `DeleteFolder`
- `EmptyFolder`
- `UpdateFolder`
- `MoveFolder`
- `CopyFolder`
- `Subscribe`
- `Unsubscribe`
- `GetEvents`
- `GetStreamingEvents`
- `SyncFolderHierarchy`
- `SyncFolderItems`
- `GetItem`
- `CreateItem`
- `DeleteItem`
- `UpdateItem`
- `SendItem`
- `MoveItem`
- `CopyItem`
- `CreateAttachment`
- `DeleteAttachment`
- `GetAttachment`
- `CreateManagedFolder`
- `GetDelegate`
- `AddDelegate`
- `RemoveDelegate`
- `UpdateDelegate`
- `CreateUserConfiguration`
- `DeleteUserConfiguration`
- `GetUserConfiguration`
- `UpdateUserConfiguration`
- `GetUserAvailability`
- `GetUserOofSettings`
- `SetUserOofSettings`
- `GetServiceConfiguration`
- `GetMailTips`
- `PlayOnPhone`
- `GetPhoneCallInformation`
- `DisconnectPhoneCall`
- `GetSharingMetadata`
- `RefreshSharingFolder`
- `GetSharingFolder`
- `GetRoomLists`
- `GetRooms`
- `FindMessageTrackingReport`
- `GetMessageTrackingReport`
- `FindConversation`
- `ApplyConversationAction`
- `GetInboxRules`
- `UpdateInboxRules`
- `GetPasswordExpirationDate`
The methods and parameters can be viewed in the auto-generated [`soapReadme.md`](soapReadme.html) (it may take a minute to generate this very long page).
To understand how EWS services are accessed in SOAP/XML, Microsoft provides [C# and XML code snippets.](https://msdn.microsoft.com/en-us/library/office/dn535506(v=exchg.150).aspx)
The EWS operations on items such as `SendItem` first require `FindItem`. As an example we invoke the `FindItem` EWS operation in C++ with gSOAP. There are different kinds of `FindItem` requests that can be made to the EWS server. This example shows one elaborate request with a paging scheme, grouping scheme, folder to search, sort order, traversal option (shallow), and item shape.
To create the `FindItem` request we instantiate `ewsmsg__FindItemType`, which is the first parameter of `ews.FindItem(&request, response)`. We also create the response:
ewsmsg__FindItemType request;
__ewsmsg__FindItemResponse response;
We will use stack-allocated instances for `request` and its pointer-based members, instead of managed heap-allocated instances, which is fine as long as the stack-allocated instances remain allocated until the `ews.FindItem(&request, response)` call is executed, which serializes the stack-allocated instances in XML.
To define the paging scheme for the result, we set the following values:
ewstype__IndexedPageViewType ipvt;
ipvt.Offset = 0;
ipvt.BasePoint = ewstype__IndexBasePointType__Beginning;
request.IndexedPageItemView = &ipvt;
To define the grouping scheme for the result, we set the following values:
ewstype__GroupByType group;
ewstype__PathToUnindexedFieldType recetime;
recetime.FieldURI = ewstype__UnindexedFieldURIType__item_x003aDateTimeReceived;
ewstype__AggregateOnType aggregate;
aggregate.FieldURI = &recetime;
aggregate.Aggregate = ewstype__AggregateType__Maximum;
group.AggregateOn = &aggregate;
ewstype__PathToUnindexedFieldType catagory;
catagory.FieldURI = ewstype__UnindexedFieldURIType__item_x003aCategories;
group.FieldURI = &catagory;
group.Order = ewstype__SortDirectionType__Descending;
request.GroupBy = &group;
Now we identify which folder to search:
ewstype__NonEmptyArrayOfBaseFolderIdsType searchedFolder;
__ewstype__union_NonEmptyArrayOfBaseFolderIdsType uneaobf;
ewstype__DistinguishedFolderIdType distFolder;
distFolder.Id = ewstype__DistinguishedFolderIdNameType__drafts; // or use ewstype__DistinguishedFolderIdNameType__inbox
uneaobf.DistinguishedFolderId = &distFolder;
searchedFolder.__size_NonEmptyArrayOfBaseFolderIdsType = 1;
searchedFolder.__union_NonEmptyArrayOfBaseFolderIdsType = &uneaobf;
request.ParentFolderIds = &searchedFolder;
Then define the sort order of items:
ewstype__NonEmptyArrayOfFieldOrdersType sortOrder;
ewstype__FieldOrderType fieldOrder;
ewstype__PathToUnindexedFieldType cataOrder;
cataOrder.FieldURI = ewstype__UnindexedFieldURIType__item_x003aDateTimeReceived;
fieldOrder.FieldURI = &cataOrder;
fieldOrder.Order = ewstype__SortDirectionType__Ascending;
sortOrder.FieldOrder.push_back(&fieldOrder);
request.SortOrder = &sortOrder;
And set the traversal option to only search the top level of the searched folder:
request.Traversal = ewstype__ItemQueryTraversalType__Shallow;
We also need to define three item properties that are returned in the response:
ewstype__ItemResponseShapeType itemShape;
itemShape.BaseShape = ewstype__DefaultShapeNamesType__IdOnly;
ewstype__NonEmptyArrayOfPathsToElementType addPropertyArray;
__ewstype__union_NonEmptyArrayOfPathsToElementType* properties = soap_new___ewstype__union_NonEmptyArrayOfPathsToElementType(ews.soap, 3);
// Add property Subject
ewstype__PathToUnindexedFieldType properetyURL1;
properetyURL1.FieldURI = ewstype__UnindexedFieldURIType__item_x003aSubject;
properties[0].FieldURI = &properetyURL1;
// Add property DateTimeReceived
ewstype__PathToUnindexedFieldType properetyURL2;
properetyURL2.FieldURI = ewstype__UnindexedFieldURIType__item_x003aDateTimeReceived;
properties[1].FieldURI = &properetyURL2;
// Add Property Sender
ewstype__PathToUnindexedFieldType properetyURL3;
properetyURL3.FieldURI = ewstype__UnindexedFieldURIType__message_x003aSender;
properties[2].FieldURI = &properetyURL3;
addPropertyArray.__union_NonEmptyArrayOfPathsToElementType = properties;
itemShape.AdditionalProperties = &addPropertyArray;
request.ItemShape = &itemShape;
And finally we send the request and print the response parameters to standard output:
if (ews.FindItem(&request, response) == SOAP_OK)
{
if (response.ewsmsg__FindItemResponse &&
response.ewsmsg__FindItemResponse->ResponseMessages &&
response.ewsmsg__FindItemResponse->ResponseMessages->__union_ArrayOfResponseMessagesType)
{
ewsmsg__FindItemResponseMessageType *firmt = response.ewsmsg__FindItemResponse->ResponseMessages->__union_ArrayOfResponseMessagesType->FindItemResponseMessage;
if (firmt->ResponseClass == ewstype__ResponseClassType__Error)
{
// an EWS error occurred
std::cout << *firmt->__ResponseMessageType_sequence->MessageText << std::endl;
}
else
{
if (firmt->RootFolder)
{
ewstype__FindItemParentType *fipt = firmt->RootFolder;
std::cout << "Number of items in view: "<< *fipt->TotalItemsInView << std::endl;
if (fipt->Items && fipt->Items->__size_ArrayOfRealItemsType > 0)
std::cout << "Number of items found: " << fipt->Items->__size_ArrayOfRealItemsType << std::endl;
if (fipt->Groups && fipt->Groups->GroupedItems.size() > 0)
{
for (int i = 0; i < fipt->Groups->GroupedItems.size(); i++)
{
ewstype__GroupedItemsType* gitem = fipt->Groups->GroupedItems[i];
if (gitem)
{
ewstype__ArrayOfRealItemsType* gritem = gitem->Items;
std::cout << "Group " << gitem->GroupIndex << ":" << std::endl;
for (int j = 0; j < gritem->__size_ArrayOfRealItemsType; j++)
{
if (gritem->__union_ArrayOfRealItemsType[j].Message)
{
std::cout <<
"Id: " <<
gritem->__union_ArrayOfRealItemsType[j].Message->ItemId->Id <<
"\nChangeKey: " <<
*gritem->__union_ArrayOfRealItemsType[j].Message->ItemId->ChangeKey <<
'\n';
}
}
}
std::cout << std::endl << std::endl;
}
}
}
}
}
}
else
{
ews.soap_stream_fault(std::cerr);
exit(1);
}
ews.destroy(); // done, delete all managed instances and deserialized data
The CodeProject article [How to Use Exchange Web Service in C++ With gSOAP](https://www.codeproject.com/Articles/1119224/How-to-Use-Exchange-Web-Service-in-Cplusplus-With) includes additional code examples to invoke common operations on EWS services.
Readme report
-------------
See the auto-generated [soapReadme](soapReadme.html) for this example (it may take a minute to generate this very long page).
[![To top](../../images/go-up.png) To top](#)