Access Exchange Web Services with PHP and cURL
Asked Answered
D

3

12

Hello,

I am currently writing a client to access a Microsoft Exchange server and read contacts, appointments etc. from it.

Through days of searching I've been able to connect to the EWS via PHP's Soap client and a custom HTTPS Stream wrapper. This website helped me greatly at this point.

Everything worked fine on my Windows 7 machine using XAMPP

Now I uploaded my project to a Debian 6.0 Squeeze development machine that has exactly the same configuration as my Windows machine regarding the web-server, php settings, mysql settings etc. but it just wont work anymore

The debian machine can resolve and ping the exchange server without problems

I nailed the actual problem down to a point, where cURL isn't able to retrieve the WSDL file of the EWS

It always receives an empty response and a 401 (Unauthorized) status code

The credentials I use are correct, the same credentials work on my windows machine

I extracted the faulty piece of code and tried running it stand-alone, it looks like this:

    echo "Trying to get https://".$cfg[ 'Exchange.Server' ]."/EWS/Services.wsdl<br>";
    $curl = curl_init( 'https://'.$cfg[ 'Exchange.Server' ].'/EWS/Services.wsdl' );
    curl_setopt( $curl, CURLOPT_RETURNTRANSFER,     true );
    curl_setopt( $curl, CURLOPT_HTTP_VERSION,       CURL_HTTP_VERSION_1_1 );
    curl_setopt( $curl, CURLOPT_HTTPAUTH,           CURLAUTH_NTLM );
    curl_setopt( $curl, CURLOPT_USERPWD,            $cfg[ 'Exchange.User' ].':'.$cfg[ 'Exchange.Password' ] );
    curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER,     false );
    curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST,     false );

    echo '<pre>';
    $response = curl_exec( $curl );
    $info = curl_getinfo( $curl );

    var_dump( $info );
    var_dump( $response );

    curl_close( $curl );

The result I receive here is the mentioned 401 status code and an empty response When I call the same url in my browser or with the same code on my windows machine, I get the WSDL file I want

Actually I can't even tell if this is a linux-based problem or if I do something wrong at some point, I'm struggling with this for 2 days now.

Is there someone that may be able to find my mistake or tell me the reason why it doesn't work?

I may provide any further needed information on demand

Dufour answered 5/10, 2011 at 16:0 Comment(6)
Have you tried to get the file on the commandline as well, e.g. with the curl commandline tool? It can reveal quite a lot of information.Jesu
Yes, I tried the same thing using the command-line tool "curl" using the parameters -k (ignore invalid certs), --ntlm (NTLM auth) and -u for my user credentials I received an empty responseDufour
Enable verbose output, I think it's -v, check against --help. There are some other "debugging" options as well for certs etc. IIRC.Jesu
Seems to be something with the NTLM library on your client....Alienist
I don't use any NTLM library in this case, cURL has inbuilt NTLM authentification -v pointed me to a mistake I made, which was using \\ instead of \ as my domain seperator for the username, though, I still get the 401 responseDufour
Hi @Dufour has you solved it?Choosey
S
8

If you initialize your soap client properly, you should be able to preform any requests requests this way:

$curl = curl_init($location); //'https://'.$server_address.'/EWS/Exchange.asmx'
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //valid soap headers with keep-alive
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_USERPWD, $user.':'.$password);
$response = curl_exec($curl);

As to your code, try commenting out following line:

curl_setopt( $curl, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );

Also take a look wherever this wrapper works on your setup: http://ewswrapper.lafiel.net/ If it does, take a look at SOAP classes used there - it uses php built-in as base.

Why can't you store wsdl file locally anyways?


UPDATE:

Ok, I played around with this in my Debian box and this works for me flawlessly:

$domain = 'xxxxxx';
$user = 'xxxxxx';
$password = 'xxxxxx';
$ch = curl_init('https://'.$domain.'/EWS/Services.wsdl'); 
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_USERPWD, $user.':'.$password);
$response = curl_exec($ch);
$info = curl_getinfo( $ch );
$error =  curl_error ($ch);
print_r(array($response,$info,$error));

returns

Array
(
    [0] => <?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions 
(...)
</wsdl:definitions>
    [1] => Array
        (
            [url] => xxxxx/EWS/Services.wsdl
            [content_type] => text/xml
            [http_code] => 200
            [header_size] => 250
            [request_size] => 147
            [filetime] => -1
            [ssl_verify_result] => 0
            [redirect_count] => 0
            [total_time] => 0.60574
            [namelookup_time] => 0.165249
            [connect_time] => 0.268173
            [pretransfer_time] => 0.474009
            [size_upload] => 0
            [size_download] => 55607
            [speed_download] => 91800
            [speed_upload] => 0
            [download_content_length] => 55607
            [upload_content_length] => 0
            [starttransfer_time] => 0.580931
            [redirect_time] => 0
            [certinfo] => Array
                (
                )

            [redirect_url] => 
        )

    [2] => 
)
Suffruticose answered 6/10, 2011 at 10:53 Comment(6)
Thanks for your answer. Sorry, I think I didn't point that out well enough, but this is not exactly about SOAP not being able to retrieve the WSDL, cURL isn't. I can't store it locally since we're not sure if it may or may not be changed, the project will run in more than one environments. An alternative for me was downloading it with cURL and then give the local version to SOAP, but that's already the problem. I can't simply download the WSDL with cURL on Linux. The NTLM authentification is required by Microsoft Exchange by the wayDufour
Have you tried commenting out the line with HTTPAUTH? Although technically NTLM auth is required by MS Ex, sometimes skipping that in curl execution does the trick. To download wsdl from Ex server I actually loop through all auth types and see which one returns wsdl file and use that one to connect to the server. You'll be surprised to see that various MS Ex servers use different auth types.Suffruticose
I already tried commenting it out, it didn't work either. I also tried all the authentication methods cURL provides yet, no luck either. It still works with the same code on my windows machine, so it's probably not a server-side, but a client-side problemDufour
I tried your code and I tried your code with a few tweaks here and there, I keep getting the 401. When I put in the NTLM authentification, I get a proper result on my windows machine, though, still nothing on my Debian. Thanks for your help by the wayDufour
That's odd, the code I posted works with my exchange on my Debian box. Perhaps try it from different linux box, since on your win box it works. As to NTLM auth - exchange I'm using requires it under windows, but doesn't like it on my Debian. Alternatively, if you're into python, you might also want to try downloading wsdl using urllib2 + ntml and loop through all auth methods checking which ones return 401 - prerhaps the problem lies in your version of php/curlSuffruticose
For anyone having trouble - I was able to use this answer - but I had to include curl_setopt( $curl, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );. Not having that line gave me 401'sTycoon
S
1

Your first check should not use any complex scripting. Instead try opening the WDSL from a simple browser window on the Debian machine like so: https://your.exchange-server.com/EWS/Services.wsdl

If that does not work there are probably access restrictions in place that depend on the client IP or client network (e.g. your development machine being in a trusted network, your Debian not). The fact that you get a 401 ("Unauthorized" - request requires user authentication) suggests that there is no problem with contacting the server but with authentication.

Another check I suggest is that you have a look into your phpinfo() to make sure your PHP installation on Debian is capable of handling HTTP*S* requests. Make sure, OpenSSL is installed!

Scholium answered 13/10, 2011 at 8:50 Comment(2)
HTTPS wrapper exists, OpenSSL installed, no browser on Debian, but tryed wget and curl from command-line, wget comes up with 403 (Obviously), curl gets 401, I tried all kinds of username and password combinations, with and without domain, 1 to 4 backslashes as the domain separator, no IP or client restrictions activeDufour
From what I find on the web it could be a problem with NTML v2 (obviously exclusively accepted by Exchange servers) and NTML v1 (offered by Linux clients). There seem to be common negotiation problems between both worlds, even if they both support v2. This does not answer your question, but maybe hints you in the right direction for further testing.Scholium
U
1

This article helped point me in the right direction. One thing to keep in mind is that your PHP installation may not be sharing your system's cURL / libcurl setup.

http://blog.ianty.com/ubuntu/exchange-web-services-ews-ntlmv2-and-linux/

Unfinished answered 1/7, 2012 at 23:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.