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

DarkDevine
  • 1,047
  • 1
  • 9
  • 12
  • 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. – hakre Oct 05 '11 at 16:18
  • 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 response – DarkDevine Oct 05 '11 at 16:26
  • Enable verbose output, I think it's `-v`, check against `--help`. There are some other "debugging" options as well for certs etc. IIRC. – hakre Oct 05 '11 at 16:29
  • Seems to be something with the NTLM library on your client.... – Henning Krause Oct 05 '11 at 16:30
  • 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 response – DarkDevine Oct 05 '11 at 16:38
  • Hi @DarkDevine has you solved it? – Ignacio Ocampo Dec 11 '14 at 06:22

3 Answers3

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] => 
)
Maiiku
  • 305
  • 1
  • 9
  • 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 way – DarkDevine Oct 06 '11 at 15:26
  • 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. – Maiiku Oct 06 '11 at 17:06
  • 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 problem – DarkDevine Oct 06 '11 at 18:25
  • 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 way – DarkDevine Oct 11 '11 at 08:39
  • 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/curl – Maiiku Oct 11 '11 at 12:10
  • 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's – Laurence Feb 07 '15 at 11:05
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/

Jeffery Bennett
  • 490
  • 1
  • 5
  • 9
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!

Jpsy
  • 20,077
  • 7
  • 118
  • 115
  • 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 active – DarkDevine Oct 13 '11 at 11:58
  • 3
    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. – Jpsy Oct 13 '11 at 14:12