Comments on Talking SOAP With Exchange

Talking SOAP With Exchange Previously, talking to Exchange without using Microsoft products was pretty much out of the question. The binary MAPI protocol is proprietary and poorly documented. Exchange supports IMAP and POP, but these protocols only give acesss to emails, not the calendar, address book, todo lists etc. But beginning with version 2007, Exchange now ships with a SOAP interface called Exchange Web Services, or EWS. This interface gives us access to the functions necessary to write clients in any programming language on any platform. This article describes a PHP program to look up, delete and insert items in an Exchange calendar.

24 Comment(s)

Add comment

Please register in our forum first to comment.

Comments

By: Strawp

Hi, This is great stuff - I used Thomas Rabaix's code to create a data abstraction class for SharePoint but now I'm going to pick through this and create a sync script for Exchange and Google Calendar. Anyway, it seems that some of the formatting is a bit broken on this post (e.g. newlines). A zip of all the code would be nice so I don't have to copy-paste and then pick through fixing bits. Cheers, Strawp

By: Brian

Yes, I'm replying to myself...

Turns out that 'inbox' (or 'Inbox') isn't one of the blessed folders that can be called by name, you have to dig for the Id and ChangeKey for 'Top of Information Store' and take a look in there to find the folder with a display name of 'Inbox'.

 <code>
$FindFolder->Traversal = 'Shallow';
$FindFolder->FolderShape->BaseShape = 'AllProperties';
$FindFolder->ParentFolderIds->DistinguishedFolderId->Id = 'root';
$result = $client->FindFolder($FindFolder);
$folders = $result->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
foreach ($folders as $folder)
{
    if ('Top of Information Store' == $folder->DisplayName)
    {
        $tois_folder = $folder;
    }
}
$FindFolder = null;
$FindFolder->Traversal = 'Shallow';
$FindFolder->FolderShape->BaseShape = 'AllProperties';
$FindFolder->ParentFolderIds->FolderId->Id = $tois_folder->FolderId->Id;
$FindFolder->ParentFolderIds->FolderId->ChangeKey = $tois_folder->FolderId->ChangeKey;
$result = $client->FindFolder($FindFolder);
$folders = $result->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;
foreach ($folders as $folder)
{
     if ('Inbox' == $folder->DisplayName)
     {
          // $folder is now the Inbox
     }
}
</code>

By: Brian

Any clues on how to get to the message count of the Inbox, as well as the count of unread messages in there?  I'm having a hard time getting my mind around the MSN docs and not making much progress.

 Changing the $FindFolder->ParentFolderIds->DistinguishedFolderId->Id to 'Inbox' throws an error, and changing it to 'inbox' returns a ResponseClass : Success, as if it found the Inbox, but TotalItemsInView => 0 (which I know isn't true for my Inbox).

By:

Thanks for the kind words. When I wrote the howto, the default CSS mangled my attempts at making readable code excerpts. I found a way around this now, as you can see.

By: PMC

I agree that this HowTo is extremely useful for the most part, unfortunately, calls to the Exchange server that contain the updateItem function do not work and instead return a path error - (see http://bugs.php.net/bug.php?id=47924&thanks=6)

Any ideas how to get around this?

 

Thanks

By: Heratech

Hello,

 I had the UpdateItem bug to and apparently using the types.xsd from 

http://code.google.com/p/php-ews/source/browse/trunk/wsdl/types.xsd

 

fixes it- Just testing it now, so far so good.

Regards,

Heratech

By: Erik

Hi,

This solution is really the best I've ever seen!!! I'm already searching for 14 hours or so for a solution, saw this page several times but thought I didn't need it, because NTLM Authentication was changed to Basic Authentication...

My Exchange Webservices still didn't work and the __getFunctions didn't return result and now it does!

Thanks for this great article!

Erik.

p.s. a tip, make the pre boxes wider for better readability

By: Ryan

Great article.  An example or two on accessing Public Folders with this method would be fabulous. I would recommend an example on enumerating the contents of a public folder or creating new contacts in a public folder.  I have a need to take email/address information from a 3rd party app and dump it into a public folder on a nightly basis so its available to employees through Outlook.

By: jyoti

Hi

I  implemented this  fc8 and php5.2.6 its giving error. I know its login issue i tried all the possibles .it didn't work . please help me in this 

<output>:

PHP Fatal error:  Uncaught SoapFault exception: [Client] SoapSlient::__doRequest() returned non string value in /home/jyoti/contacts/php/calender.php:179
Stack trace:
#0 [internal function]: SoapClient->__call('FindFolder', Array)
#1 /home/jyoti/contacts/php/calender.php(179): ExchangeNTLMSoapClient->FindFolder(Object(stdClass))
#2 {main}
  thrown in /home/jyoti/contacts/php/calender.php on line 179
 </output>

 


 

<code>

class NTLMSoapClient extends SoapClient {
    function __doRequest($request, $location, $action, $version) {
        $headers = array( 'Method: POST', 'Connection: Keep-Alive', 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "'.$action.'"', );
        print $request;
        $this->__last_request_headers = $headers;
        $ch = curl_init($location);
        curl_setopt($ch, CURLOPT_USERAGENT, "PHP-SOAP-CURL");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POST, true );
        curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_SSLKEY, "/tmp/test.txt");
        curl_setopt($ch, CURLOPT_USERPWD, $this->user.':'.$this->password);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $response = curl_exec($ch);
        print_r($response);
        return $response;
    }
    function __getLastRequestHeaders() {
       print implode("n", $this->__last_request_headers)."n";
        return implode("n", $this->__last_request_headers)."n";
    }
}

 <code>

 thanks

jyoti

By: ludo42

I have the same problem...

Do you have a solution ?

By: Anonymous

 Make sure you use your actual wdsl address location, not the example 

<wsdl:service name="ExchangeServices"> <wsdl:port name="ExchangeServicePort" binding="tns:ExchangeServiceBinding"> <soap:address location="https://exchange.example.com/EWS/Exchange.asmx"/> </wsdl:port> </wsdl:service> </wsdl:definitions>

By: Dave Driesen

This is because curl_exec is returning boolean "true" (to indicate success) instead of the desired string data.

To actually return the data into the $response variable, set CURLOPT_RETURNTRANSFER to "true" as follows:

          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

I don't know why the author sets it to 0, probably this was perfectly valid at the time.

Perhaps it is explained in the article; I did not check.

 

Anyway with the value set to "true" the error disappears and normal execution continues.

 

Thanks

Dave

 

By: R. Veenbrink

Hello all,

for the ones where the script is not working. Check if you're running PHP version 5.2.6.
I was running on PHP 5.1.x and this was the problem why the script was not working.

What i found it has to do something with the cUrl module en a bug in PHP.

Hope you enjoy my comment.

By: Baz

Is there anyway to change distribution lists also known as mailing lists through soap?

By: Taras Kozlov

Erik,

I can't find a words to show my gratitude to you. It's the best article for the subject in the whole net. It's simply amazing tutorial.

Thank you so much!

By: zehhaf

Hi

when you delete un item from outlook, it goes to deleteditems folder but it doesn't keep the same Id before deleting.

 I have a real problem with my synchronization

 ex.

before (in contact folder)

AAAmAHRlc3RAY2FyYS1tZWwuZW1lYS5taWNyb3NvZnRvbmxpbmUuY29tAEYAAAAAAEo5vPb1H6pPkUGStDYeaCYHADRCVMaUGCxKlgdjf/vdoLwAHo47I/sAADRCVMaUGCxKlgdjf/vdoLwAHo5qWbwAAA==

after (the same item in deleteditems folder)

AAAmAHRlc3RAY2FyYS1tZWwuZW1lYS5taWNyb3NvZnRvbmxpbmUuY29tAEYAAAAAAEo5vPb1H6pPkUGStDYeaCYHADRCVMaUGCxKlgdjf/vdoLwAHo472hIAADRCVMaUGCxKlgdjf/vdoLwAHo5qY+UAAA==

 please help

By: Andi

...for the information on NTLM. I use NuSOAP and simply pass the ntlm information on:

$client = new nusoap_client(ENDPOINT, false);
$client->setCurlOption(CURLOPT_PROXY, "");
$client->setCurlOption(CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
$client->setCurlOption(CURLOPT_USERPWD, 'user:pass');
etcetc.

Btw. I had to unset the proxy because we had some local environment variables set (http_proxy and https_proxy).

Thank you again :-)

Andi

 

By: Jeroen Aarts

Great Article! Thanks for sharing the code.

 It works great on Windows machines (Win XP/Server, tested with different versions of PHP 5.2.8+).

However, on Mac machines (Mac OS 10.6/10.6 Server), using different versions of PHP (5.2.11/5.3.1), curl_exec() in the overridden __doRequest() method only returns an empty string. Has anyone any clue, or can anyone share experience on non-Windows machines, as I am executing exactly the same code on all machines?

 - Jeroen

By: Dave Driesen

Hey Jeroen,

 

This may be related to Jyoti's post.. Try setting CURLOPT_RETURNTRANSFER to "true" instead of 0.. If it is not set to true, curl_exec does not return its output into your variable (it then only reports success or failure).

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

This behavior may differ among platforms or PHP versions. Either way, setting to "true" fixes many issues.

 

Grtz,

Dave

By: Anonymous

From what I can see, this article seems to suggest that the SoapClient calls the __doRequest() method when requesting the wsdl.

 This is not what I'm experiencing with php 5.3.5.

Can anyone confirm / deny? Should this be in the forum instead?

 eg.

class TestSoap extends SoapClient
{

  function __doRequest($request, $location, $action, $version, $oneWay)
  {
// DOES NOT GO THROUGH HERE ON OBJECT INSTANTIATION
    die('HERE');
    return parent::__doRequest($request, $location, $action, $version, $oneWay);
  }

}
$wsdl = 'http://host/service?wsdl';
$options = aray( /* */ );
$client = new TestSoap($wsdl, $options);

By: John

Hi,

This is very good scipt for me to work with. I can read my own agenda items and show them in a webpage. 

Agenda image

The problem is that i can't access the agenda's shown in the image. I'm the owner. I also have the id's from these agenda's, but i realy don't see them. What am i doeing wrong? The script i use is exactly as above. The agenda´s are created by someone else but assigned to me. the agenda named "opdehei" just below "Agenda" is created by me, but i also can't read this one.

Thnxx

By: Ulf Boehme

On my side the code works fine.

It's possible to connect and to get the list of functions and types from the Exchange 2013 Server.

On the command:

$FindFolder->Traversal = "Shallow";

I get this problem:

Warning: Creating default object from empty value in D:\intranet\ews\start.php on line 31

In the types-list the variable Findfolder is only a subtype in some structs. It seems that the server doesnt know it. I also tried the

command:

$FindFolder = null;

but with the same result.Is there a possibility to initialize $FindFolder?Best Regards,

Ulf

 

By: Dave Driesen

I initialize them as "new StdClass" to make the warnings go away... There are probably better options, but this works.

 

$FindItem = new stdClass;

$FindItem->Traversal = "Shallow";

$FindItem->ItemShape = new stdClass;

$FindItem->ItemShape->BaseShape = "AllProperties";

$FindItem->ParentFolderIds = new stdClass;

$FindItem->ParentFolderIds->DistinguishedFolderId = new stdClass;

$FindItem->ParentFolderIds->DistinguishedFolderId->Id = "contacts";

 

Best wishes,

Dave

By: Mads

I'm getting the following error:  Notice: Trying to get property of non-object in

on line: $folders = $result->ResponseMessages->FindFolderResponseMessage->RootFolder->Folders->Folder;

Anyone who know what happens?