Before beginning

  • English is not my native language, so feel free to correct me.
  • I will use the acronym EWS a lot for the article's clarity. (for Exchange Web Service) 

Why to code in native language for EWS

  •  Code is a lot more portable between Operating System; some examples below
    • Allow to make a simple cgi by example that would run on a IIS or apache web server to talk with your Exchange Server. (To make by example a cgi that read the free/busy of a helpdesk mailbox, and to send request there by your users)
    • Allow the code to talk to your Exchange Server from like an android handheld
  • Allow to learn all the basic behind basic HTTP/HTTPS I/O
  • The output is usually faster and smaller

Why to NOT use native language for EWS

  • The learning step is harder
  • Coding in like .NET is really faster and easier
  • .NET IDE usually have some wizards to help make the same task easily


The setup used

For the example below, I used Visual Studio Express 2010 SP1 under Windows.


What you will need

 A lot of other options exist for the NTLM library and to handle SSL (like libcurl or WinHTTP)

Exchange answer in XML, so you can use xmllite, minixml or just decode the XML manually, it's your choice.


What to do

  • Uncompress all library and make a simple empty project.
  • From the libntlm library, export the .def from the libntlm-0.dll and make a .lib.
  • In your project link the .lib for libntlm, OpenSSL and Winsock.

  • Include the OpenSSL & libntlm's library

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include "ntlm.h" 

  •  Use the Winsock API to connect to your Exchange Server, a bit like that; (no error control!)

struct hostent *hp;
struct sockaddr_in addr;
WORD wVersionRequested = MAKEWORD(2,2);
int sock;
WSADATA wsaData;

           
WSAStartup(wVersionRequested, &wsaData);

if
(!(hp=gethostbyname("server_host")))
    return 0;
memset(&addr,0,
sizeof(addr));
addr.sin_addr=*(
struct in_addr*)
hp->h_addr_list[0];
addr.sin_family=AF_INET;
addr.sin_port=htons(443);
if((sock=socket(AF_INET,SOCK_STREAM, IPPROTO_TCP))<0)
   return 0;
if(connect(sock,(struct sockaddr *)&addr, sizeof(addr))<0)
   return 0;
return 1;

  • Use the OpenSSL API to connect in SSL with the "sock" received from Winsock, a bit like that; (no error control! and I don't check the remote certificate at all!)

      const SSL_METHOD *meth;
      SSL *ssl;
      BIO *sbio;
      BIO *rbio;
      BIO *wbio;
      SSL_CTX *ctx;

      SSL_library_init();
      SSL_load_error_strings();
      meth=SSLv23_method();
      ctx=SSL_CTX_new(meth);

      ssl=SSL_new(ctx);
      sbio=BIO_new_socket(sock,BIO_NOCLOSE);

    SSL_set_bio(ssl,sbio,sbio);
    SSL_connect(ssl);

    •  Send a dummy request to your Exchange Server to generate a 401 error. The 401 error mean you need to use the NTLM authentification against your Exchange Server

    SSL_write(ssl,"GET /EWS/Services.wsdl HTTP/1.1\r\nUser-Agent: test\r\nHost: serverhost:443\r\n\r\n",strlen("GET /EWS/Services.wsdl HTTP/1.1\r\nUser-Agent: test\r\nHost: serverhost:443\r\n\r\n"));

    Check the answer for "WWW-Authenticate: NTLM". If it's not there, be sure it's enabled on your IIS of your Exchange Server with the CAS role.

     

    • Use the libntlm library to authentificate (I use two base64 function, enc.Base64enc & enc.Base64dec. I didn't paste them to keep the code clean, but it's easy to find function (like there or there)

    string encoded, encodedpass, decoded, sBuffer;

    char cRequest[1024];
    char cBuffer[1024];
    tSmbNtlmAuthRequest request;
    tSmbNtlmAuthResponse response;

    ZeroMemory(cRequest, 1024);
    buildSmbNtlmAuthRequest (&request, "username", "domain");
    if (SmbLength(&request) > 1024)
        
    return 0;
    encoded = enc.Base64enc((
    unsigned char *) &request, SmbLength(&request));
    _snprintf(cRequest, 1024,
    "GET /EWS/Services.wsdl HTTP/1.1\r\nHost: serverhost\r\nConnection: Keep-Alive\r\nAuthorization: NTLM %s\r\n\r\n", encoded.c_str());
    SSL_write(ssl, cRequest);

    // read the answer, sBuffer = NTLM answer

    decoded = enc.Base64dec(sBuffer);
    buildSmbNtlmAuthResponse((tSmbNtlmAuthChallenge *)decoded.c_str(), &response, "username", "password");
    encodedpass = enc.Base64enc((unsigned char *) &response, SmbLength(&response));
    _snprintf(cRequest, 1024,
    "GET /EWS/Services.wsdl HTTP/1.1\r\nHost: servername\r\nConnection: Keep-Alive\r\nAuthorization: NTLM %s\r\n\r\n", encodedpass.c_str());
    SSL_write(ssl, cRequest);

    // If IIS answer with 200 then your password was ok !

    • Now you are ready to send command to your Exchange Server !

    Like to receive a distribution group member's list:

    char cRequest[1024];
    char request[1024] =
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
    "<soap:Envelope xmlns:soap=\http://schemas.xmlsoap.org/soap/envelope//
    " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
    " <soap:Body>"
    " <ExpandDL xmlns=\http://schemas.microsoft.com/exchange/services/2006/messages/
    " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
    " <Mailbox xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
    " <EmailAddress xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">test@constoco.com</EmailAddress>"
    " </Mailbox>"
    " </ExpandDL>"
    " </soap:Body>"
    "</soap:Envelope>";

    ZeroMemory(cRequest, 1024);
    _snprintf(cRequest, 1024, "POST /ews/Exchange.asmx HTTP/1.1\r\n"
    "Host: servername\r\n"
    "Content-Length: %d\r\n"
    "Content-Type: text/xml\r\n\r\n%s", strlen(request), request);

     // send it and decode the answer...


    •  Now from this point you can make anything with EWS

     


    Reference

    Quick and Dirty UNIX Shell Scripting with EWS - The idea that started it all for me was there.
    EWSEditor - To generate some SOAP message easily.
    Exchange Web Services (EWS) - MSDN referance with a lot of examples.
    theForger's Win32 API Programming Tutorial - A really good tutorial for direct API programming.

    Other Languages

    This article is also available the following languages: