Click here to Skip to main content
13,148,510 members (67,057 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

7.4K views
102 downloads
12 bookmarked
Posted 18 Aug 2016

How to Use Exchange Web Service in C++ With gSOAP

, 24 Sep 2017
Rate this:
Please Sign up or sign in to vote.
How to connect EWS with gSOAP to send, delete, and retrieve emails.

 

Introduction

This article will describe the process of using gSOAP to connect to the Exchange Web Service. All functionalities in EWS are not defined in this article, but reading it should give you a solid basis of using C++ and gSOAP to access EWS operations.

gSOAP is a toolkit that simplifies development with SOAP/XML web services by creating SOAP/XML data bindings for C and C++. With gSOAP, calling SOAP web services is extremely easy because the toolkit auto generates C/C++ code to handle the SOAP messages that are sent and received. This eleminates the developer's need to write complicated code to parse large XML messages. 

EWS is a web service that is used to access emails in a pre existing email account. It is a SOAP API, so it is the perfect example to show just how useful gSOAP can be. 

The code attached to this article was written in equal parts by myself and Ning Xie. 

Installation

gSOAP

Check this page to see how to download and install gSOAP on your system.

OpenSSL

You will need OpenSSL in order to connect gSOAP with EWS. You can see how to install it on their website.

Outlook email account

You will need an Outlook email account in order to test your EWS client. You can create an account on this page if you don't already have one. 

 

Generating C++ Bindings from the EWS WSDL with gSOAP 

Obtaining the .wsdl file

In order for the wsdl2h tool(described in the next step) to run, you need the wsdl file of the web service you wish to create a header file for. You will need to download three files: Services.wsdl, messages.xsd, and types.xsd. In order to download these files, you need to to know your exchange server. 

The format of your exchange server should look something like these examples: exchange.your_company.com, exchange.your_shcool.edu.

To download Services.wsdl, messages.xsd, and types.xsd, enter these links into your web browser:

https://{your-exchange-server}/ews/Services.wsdl

https://{your-exchange-server}/ews/messages.xsd

https://{your-exchange-server}/ews/types.xsd

When you visit these links, you will be prompted to enter a username and password. Use your Exchange email credentials, and you can view and download the files.

Make sure to save the files in the same directory. Also, you might need to modify Services.wsdl to make sure that the locations for messages.xsd and types.xsd are not attached to absolute paths (see below).

<wsdl:types>

        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

                <xs:import namespace="http://schemas.microsoft.com/exchange/services/2006/messages" schemaLocation="messages.xsd"/>

        <xs:schema/>

<wsdl:types/>

and that schemaLocation="types.xsd" is used in message.xsd without path:

<xs:import namespace="http://schemas.microsoft.com/exchange/services/2006/types" schemaLocation="types.xsd"/>


Step 1

In order to generate the EWS header file, run the wsdl2h tool with the EWS WSDL file as well as the desired title of you header file (we'll use service.h). You will need the file typemap.dat in your working directory in order for the tool to work. 

Note: Your typemap.dat file only needs the following two lines to work correctly with this example:
ewsmsg  = "http://schemas.microsoft.com/exchange/services/2006/messages"
ewstype = "http://schemas.microsoft.com/exchange/services/2006/types"

wsdl2h -o service.h -u Services.wsdl

Step 2

Now we will use soapcpp2 to generate the data bindings that will connect our code to the service operations. Make sure that stdsoap2.h and stdsoap2.cpp are either in either your working directory or the path to the gsoap folder is included in the following command.

soapcpp2 -j -CL -I /path/to/gSOAP/import service.h

Option -j produces C++ proxy classes while -CL indicates that we will only create the client side. You will need the path to the gSOAP import file for the #import statements in service.h. Several files will be generated. 

 

Connecting gSOAP with EWS

The following examples will show you how to find an item in your email account and delete that specific item programatically. We will be using the generated proxy classes to do so.

Example 1: SendItem service operation(Sending an email from the Drafts folder)

Before we begin, open up your Outlook email account. Create an email and list yourself as the recipient. You may set the subject and message if you want to, but it is not necessary. Now, abandon this email so that it is saved within the Drafts folder. This is the email that we will send in the next step. In order to send the email, we will have to know its Id and Change Key. In order to get this information, we will run a service operation defined in the tar file attached to this article: testClient_FindItem.cpp. This code will look in your Drafts folder and return all of the item Ids. To get your item's Id and Change Key, run the FindItem operation from the command line and pass in your username and password like this(I am using clang):

clang++ -Wno-undefined-bool-conversion -o fi -DWITH_OPENSSL testClient_FindItem.cpp soapC.cpp soapExchangeServiceBindingProxy.cpp /Path/to/stdsoap2.cpp -I. /path/to/gSOAP/gsoap/custom/duration.c -lssl -lcrypto

./fi emailusername@outlook.com email_password

Note: there is a more detailed explanation of compilation at the end of this step. 

The Id and Change Key of your draft will be printed to your screen.

Note: When testing the EWS client I prefer to handle emails found in the Drafts folder because it generally holds much less content than the other folders. You can continue using whatever email location you prefer,but the code you will use in order to find the email's Id is set to look in the Drafts folder.   

Now that we have a pre existing email to send, we can begin coding. The following code can be found in testClient_SendItem.cpp in the tar file attached to this article.

We must include the namespace mapping file as well as the generated proxy header file so that gSOAP's generated code can help us access the service operations. 

#include "ExchangeServiceBinding.nsmap"
#include "soapExchangeServiceBindingProxy.h"
#include "soapH.h"
#include <iostream>
#include <string>

Next, we will write our code so that we can pass in username and password parameters from the command line and handle the security measures of logging into your email accout. Notice this is also where the ewsBinding proxy variable is declared. This is what will allow us to call the SendItem function so that our draft is sent to the inbox!

static const char *userid;
static const char *passwd;
int main(int argc, char **argv)
{
    if(argc>=3)
    {
        userid = argv[1];
        passwd = argv[2];
    }

    ExchangeServiceBindingProxy ewsBinding("https://outlook.office365.com/EWS/Exchange.asmx");

    soap_init1(ewsBinding.soap, SOAP_IO_DEFAULT | SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_XML_NOTYPE);
    ewsBinding.soap->userid = userid;
    ewsBinding.soap->passwd = passwd;

    soap_ssl_init();
    if(soap_ssl_client_context(ewsBinding.soap,SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK,NULL,NULL,"cacerts.pem",NULL,NULL))
    {
        soap_print_fault(ewsBinding.soap, stderr);
        exit(1);
    }

    // set request server version
    _ewstype__RequestServerVersion serVer;
    enum ewstype__ExchangeVersionType exchangeVer = ewstype__ExchangeVersionType__Exchange2010_USCORESP2;
    serVer.Version = exchangeVer;
    ewsBinding.soap_header(NULL, NULL, NULL, &serVer,NULL,NULL);
Now we will set all of the information needed for the request we send to the web service. Remeber to set itemtosend.Id equal to the Id returned from FindItem and change equal to the Change Key returned from FindItem.
  ewstype__NonEmptyArrayOfBaseItemIdsType itemArray;
  itemArray.__size_NonEmptyArrayOfBaseItemIdsType = 1;

  __ewstype__union_NonEmptyArrayOfBaseItemIdsType member;

  ewstype__ItemIdType itemtosend;
  itemtosend.Id = "";
  std::string change = "";
  itemtosend.ChangeKey = &change;

  member.ItemId = &itemtosend;
  itemArray.__union_NonEmptyArrayOfBaseItemIdsType = &member;

  ewsmsg__SendItemType request;
  __ewsmsg__SendItemResponse response;
    
  request.ItemIds = &itemArray;
  request.SaveItemToFolder = true;

Now it is time to call the service and handle our results. 

if(ewsBinding.SendItem(&request, response)==SOAP_OK)
  {
        ewsmsg__ResponseMessageType* iirmt
        = response.ewsmsg__SendItemResponse->ResponseMessages->__union_ArrayOfResponseMessagesType->SendItemResponseMessage;
        if(iirmt != NULL)
        {
            if(iirmt->ResponseClass == ewstype__ResponseClassType__Error)
                    std::cout << *iirmt->__ResponseMessageType_sequence->MessageText <<std::endl;
            else
                    std::cout << "Message Sent!";
        }
    }
    else
        ewsBinding.soap_stream_fault(std::cerr);

    ewsBinding.destroy();
}

This will send the SOAP request in the variable request and send the SOAP response message back in response. Calling ewsBinding.destroy()deletes all of the managed data. 

Here is testClient_SendItem.cpp in its entirety:

#include "ExchangeServiceBinding.nsmap"
#include "soapExchangeServiceBindingProxy.h"
#include "soapH.h"
#include <iostream>
#include <string>

static const char *userid;
static const char *passwd;
int main(int argc, char **argv)
{
    if(argc>=3)
    {
        userid = argv[1];
        passwd = argv[2];
    }

    ExchangeServiceBindingProxy ewsBinding("https://outlook.office365.com/EWS/Exchange.asmx");

    soap_init1(ewsBinding.soap, SOAP_IO_DEFAULT | SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_XML_NOTYPE);
    ewsBinding.soap->userid = userid;
    ewsBinding.soap->passwd = passwd;

    soap_ssl_init();
    if(soap_ssl_client_context(ewsBinding.soap,SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK,NULL,NULL,"cacerts.pem",NULL,NULL))
    {
        soap_print_fault(ewsBinding.soap, stderr);
        exit(1);
    }

    // set request server version
    _ewstype__RequestServerVersion serVer;
    enum ewstype__ExchangeVersionType exchangeVer = ewstype__ExchangeVersionType__Exchange2010_USCORESP2;
    serVer.Version = exchangeVer;
    ewsBinding.soap_header(NULL, NULL, NULL, &serVer,NULL,NULL);

    ewstype__NonEmptyArrayOfBaseItemIdsType itemArray;
    itemArray.__size_NonEmptyArrayOfBaseItemIdsType = 1;

    __ewstype__union_NonEmptyArrayOfBaseItemIdsType member;

    ewstype__ItemIdType itemtosend;
    itemtosend.Id = "AAMkAGQ0MDg4MjMwLTYyZjEtNDc1Zi1hNWQwLWY4ZGFiODIyY2ZiOABGAAAAAADQJ5K0osuKQatv50kfuGI4BwALbeOJ+btaT4I3Qf8SQ+rtAAAAAAEQAAALbeOJ+btaT4I3Qf8SQ+rtAAKA2ysiAAA=";
    std::string change = "CQAAABYAAAALbeOJ+btaT4I3Qf8SQ+rtAAKBBRvI";
    itemtosend.ChangeKey = &change;

    member.ItemId = &itemtosend;
    itemArray.__union_NonEmptyArrayOfBaseItemIdsType = &member;

    ewsmsg__SendItemType request;
    __ewsmsg__SendItemResponse response;
    
    request.ItemIds = &itemArray;
    request.SaveItemToFolder = true;

    if(ewsBinding.SendItem(&request, response)==SOAP_OK)
    {
        ewsmsg__ResponseMessageType* iirmt
        = response.ewsmsg__SendItemResponse->ResponseMessages->__union_ArrayOfResponseMessagesType->SendItemResponseMessage;
        if(iirmt != NULL)
        {
            if(iirmt->ResponseClass == ewstype__ResponseClassType__Error)
                    std::cout << *iirmt->__ResponseMessageType_sequence->MessageText <<std::endl;
            else
                    std::cout << "Message Sent!";
        }
    }
    else
        ewsBinding.soap_stream_fault(std::cerr);

    ewsBinding.destroy();
}

Compiling the SendItem Example

We must compile with OPENSSL to ensure that our credentials are transmitted securely. This requires the OpenSSL and OpenSSL crypto libraries. 

You will need to include the paths to the stdsoap2.cpp and duration.c files in the gSOAP library.

Similarly to the FindItem compilation, you will need to pass username and password arguments to the executable.

clang++ -Wno-undefined-bool-conversion -o si -DWITH_OPENSSL testClient_SendItem.cpp soapC.cpp soapExchangeServiceBindingProxy.cpp /Path/to/stdsoap2.cpp -I. /path/to/gSOAP/gsoap/custom/duration.c -lssl -lcrypto

./si emailusername@outlook.com email_password

When yout run the SendItem operation, you should recieve a new email in your inbox!

Example 2: DeleteItem service operation

As I mentioned before, I prefer to work from the Drafts folder for simplicity, so we will be deleting an email in the Drafts folder. If you wish to delete from the inbox or other locations, you can easily modify the code in testClient_FindItem.cpp to search in other folders (set the variable dfit to ewstype__DistinguishedFolderIdNameType__inbox on line 80 to search in the inbox).

Like we did before, create a draft in your email account, and then run the FindItem service operation to get the item's Id. The Change Key is not needed for the DeleteItem service operation. 

clang++ -Wno-undefined-bool-conversion -o fi -DWITH_OPENSSL testClient_FindItem.cpp soapC.cpp soapExchangeServiceBindingProxy.cpp /Path/to/stdsoap2.cpp -I. /path/to/gSOAP/gsoap/custom/duration.c -lssl -lcrypto 

./fi emailusername@outlook.com email_password

Now that we have a pre existing email to send, we can begin coding. The following code can be found in testClient_DeleteItem.cpp in the tar file attached to this article.

We must include the namespace mapping file as well as the generated proxy header file so that gSOAP's generated code can help us access the service operations. 

#include "ExchangeServiceBinding.nsmap"
#include "soapExchangeServiceBindingProxy.h"
#include "soapH.h"
#include <iostream>
#include <string>

Now we set up the credentials so we can log into the email account as well as create the proxy we will use to call the web service and receive the results.

static const char *userid;
static const char *passwd;

int main(int argc, char **argv)
{
    if(argc>=3)
    {
        userid = argv[1];
        passwd = argv[2];
    }

    ExchangeServiceBindingProxy ewsBinding("https://outlook.office365.com/EWS/Exchange.asmx");
    ewsmsg__DeleteItemType request;
    __ewsmsg__DeleteItemResponse response;

    soap_init1(ewsBinding.soap, SOAP_IO_DEFAULT | SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_XML_NOTYPE);
    ewsBinding.soap->userid = userid;
    ewsBinding.soap->passwd = passwd;

    soap_ssl_init();
    if(soap_ssl_client_context(ewsBinding.soap,SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK,NULL,NULL,"cacerts.pem",NULL,NULL))
    {
        soap_print_fault(ewsBinding.soap, stderr);
        exit(1);
    }

    // set request server version
    _ewstype__RequestServerVersion serVer;
    enum ewstype__ExchangeVersionType exchangeVer = ewstype__ExchangeVersionType__Exchange2010_USCORESP2;
    serVer.Version = exchangeVer;
    ewsBinding.soap_header(NULL, NULL, NULL, &serVer,NULL,NULL);

It is now time to set all of the attributes the DeleteItem service needs to do its job. Don't forget to set item.Id to the item Id that the FindItem service returned. 

    ewstype__ItemIdType item;
    item.Id = "";

    __ewstype__union_NonEmptyArrayOfBaseItemIdsType member;
    member.ItemId = &item;

    ewstype__NonEmptyArrayOfBaseItemIdsType itemArray;
    itemArray.__size_NonEmptyArrayOfBaseItemIdsType = 1;
    itemArray.__union_NonEmptyArrayOfBaseItemIdsType = &member;

    request.ItemIds = &itemArray;

    request.DeleteType = ewstype__DisposalType__SoftDelete;

    ewstype__AffectedTaskOccurrencesType ATOType = ewstype__AffectedTaskOccurrencesType__SpecifiedOccurrenceOnly;
    request.AffectedTaskOccurrences = &ATOType;

    // Identify how meeting cancellations are handled.
    ewstype__CalendarItemCreateOrDeleteOperationType CalType = ewstype__CalendarItemCreateOrDeleteOperationType__SendOnlyToAll;
    request.SendMeetingCancellations = &CalType;

Now all that's left to do is call the web service!

if(ewsBinding.DeleteItem(&request, response)==SOAP_OK)
{
        ewsmsg__ResponseMessageType *iirmt
        = response.ewsmsg__DeleteItemResponse->ResponseMessages->__union_ArrayOfResponseMessagesType->DeleteItemResponseMessage;
        if(iirmt != NULL)
        {
            if (iirmt->ResponseClass == ewstype__ResponseClassType__Error)
                    std::cout << *iirmt->__ResponseMessageType_sequence->MessageText <<std::endl;
            else
                    std::cout << "Message deleted!";
        }
    }
    else
        ewsBinding.soap_stream_fault(std::cerr);

    ewsBinding.destroy();
}

Again, don't forget to call ewsBinding.destroy() to clean up all managed data!

Here is testClient_DeleteItem.cpp in its entirety:

#include "ExchangeServiceBinding.nsmap"
#include "soapExchangeServiceBindingProxy.h"
#include "soapH.h"
#include <iostream>
#include <string>

static const char *userid;
static const char *passwd;

int main(int argc, char **argv)
{
    if(argc>=3)
    {
        userid = argv[1];
        passwd = argv[2];
    }

    ExchangeServiceBindingProxy ewsBinding("https://outlook.office365.com/EWS/Exchange.asmx");
    ewsmsg__DeleteItemType request;
    __ewsmsg__DeleteItemResponse response;

    soap_init1(ewsBinding.soap, SOAP_IO_DEFAULT | SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_XML_NOTYPE);
    ewsBinding.soap->userid = userid;
    ewsBinding.soap->passwd = passwd;

    soap_ssl_init();
    if(soap_ssl_client_context(ewsBinding.soap,SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK,NULL,NULL,"cacerts.pem",NULL,NULL))
    {
        soap_print_fault(ewsBinding.soap, stderr);
        exit(1);
    }

    // set request server version
    _ewstype__RequestServerVersion serVer;
    enum ewstype__ExchangeVersionType exchangeVer = ewstype__ExchangeVersionType__Exchange2010_USCORESP2;
    serVer.Version = exchangeVer;
    ewsBinding.soap_header(NULL, NULL, NULL, &serVer,NULL,NULL);

    ewstype__ItemIdType item;
    item.Id = "AAMkAGQ0MDg4MjMwLTYyZjEtNDc1Zi1hNWQwLWY4ZGFiODIyY2ZiOABGAAAAAADQJ5K0osuKQatv50kfuGI4BwALbeOJ+btaT4I3Qf8SQ+rtAAAAAAEQAAALbeOJ+btaT4I3Qf8SQ+rtAAKA2yshAAA=";

    __ewstype__union_NonEmptyArrayOfBaseItemIdsType member;
    member.ItemId = &item;

    ewstype__NonEmptyArrayOfBaseItemIdsType itemArray;
    itemArray.__size_NonEmptyArrayOfBaseItemIdsType = 1;
    itemArray.__union_NonEmptyArrayOfBaseItemIdsType = &member;

    request.ItemIds = &itemArray;

    request.DeleteType = ewstype__DisposalType__SoftDelete;

    ewstype__AffectedTaskOccurrencesType ATOType = ewstype__AffectedTaskOccurrencesType__SpecifiedOccurrenceOnly;
    request.AffectedTaskOccurrences = &ATOType;

    // Identify how meeting cancellations are handled.
    ewstype__CalendarItemCreateOrDeleteOperationType CalType = ewstype__CalendarItemCreateOrDeleteOperationType__SendOnlyToAll;
    request.SendMeetingCancellations = &CalType;

    if(ewsBinding.DeleteItem(&request, response)==SOAP_OK)
    {
        ewsmsg__ResponseMessageType *iirmt
        = response.ewsmsg__DeleteItemResponse->ResponseMessages->__union_ArrayOfResponseMessagesType->DeleteItemResponseMessage;
        if(iirmt != NULL)
        {
            if (iirmt->ResponseClass == ewstype__ResponseClassType__Error)
                    std::cout << *iirmt->__ResponseMessageType_sequence->MessageText <<std::endl;
            else
                    std::cout << "Message deleted!";
        }
    }
    else
        ewsBinding.soap_stream_fault(std::cerr);

    ewsBinding.destroy();
}

Compiling the DeleteItem example

We must compile with OPENSSL to ensure that our credentials are transmitted securely. This requires the OpenSSL and OpenSSL crypto libraries. 

You will need to include the paths to the stdsoap2.cpp and duration.c files in the gSOAP library.

Similarly to the FindItem compilation, you will need to pass username and password arguments to the executable.

clang++ -Wno-undefined-bool-conversion -o di -DWITH_OPENSSL testClient_DeleteItem.cpp soapC.cpp soapExchangeServiceBindingProxy.cpp /Path/to/stdsoap2.cpp -I. /path/to/gSOAP/gsoap/custom/duration.c -lssl -lcrypto

./di emailusername@outlook.com email_password

With the soft delete option, the email will not be sent to your delete folder. If you would only like to move the item to the delete folder, use testClient_MoveItem.cpp(included in the tar attached to this article). When you compile and run this example, your email should dissapear from the drafts folder. 

More Examples

Now that you have familiarized yourself with connecting gSOAP and EWS to make a client, you can work through the other examples in the tar file on your own! The files included in the tar are as follows:

testClient_CreateAttachment.cpp

testClient_DeleteAttachment.cpp

testClient_CreateFolder.cpp

testClient_DeleteFolder.cpp

testClient_FindFolder.cpp

testClient_MoveFolder.cpp

testClient_CreateItem.cpp

testClient_DeleteItem.cpp(described in this article)

testClient_FindItem.cpp

testClient_SendItem.cpp(described in this article)

testClient_MoveItem.cpp

testClient_PwdExpirationDate.cpp

If you want to learn about other service operations, this page will be very useful. If you wanted to learn about CreateItem, you can search CreateItemtype Class in the search bar and a resut of CreateItemtype Class (ExchangeWebServices) will show. If you click on that result, exampe code is provided which shows which attributes need to be set in order to call the service operation (code is in C#). This search works when you enter and function in the format of Function_Nametype Class. 

If you have any questions feel free to leave a comment!

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Bethany_Sanders
United States United States
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170924.2 | Last Updated 24 Sep 2017
Article Copyright 2016 by Bethany_Sanders
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid