1

I'm currently attempting to update an internal tool to handle an upgrade of our exchange servers to office 365.

I'm using the most recent version of James Armas's PHP-EWS tool. jamesiarmes/php-ews

Here is the code snippet that is inside of a function that we use to get events for a certain date range.

$email = '*email@domain*';
$password = '*password*';
$server = 'outlook.office365.com';

// Define EWS
//$ews = EWSAutodiscover::getEWS($email, $password);

$ews = new Client($server, $email, $password);

// Set init class
$request = new FindItemType();
// Use this to search only the items in the parent directory in question or use ::SOFT_DELETED
// to identify "soft deleted" items, i.e. not visible and not in the trash can.
$request->Traversal = ItemQueryTraversalType::SHALLOW;
// This identifies the set of properties to return in an item or folder response
$request->ItemShape = new ItemResponseShapeType();
$request->ItemShape->BaseShape = DefaultShapeNamesType::ALL_PROPERTIES;

// Define the timeframe to load calendar items
$request->CalendarView = new CalendarViewType();
$request->CalendarView->StartDate = $start_date;// an ISO8601 date e.g. 2012-06-12T15:18:34+03:00
$request->CalendarView->EndDate = $end_date;// an ISO8601 date later than the above


// Only look in the "calendars folder"
$request->ParentFolderIds = new NonEmptyArrayOfBaseFolderIdsType();
$request->ParentFolderIds->DistinguishedFolderId = new DistinguishedFolderIdType();
$request->ParentFolderIds->DistinguishedFolderId->Id = DistinguishedFolderIdNameType::CALENDAR;
$request->ParentFolderIds->DistinguishedFolderId->Mailbox = new StdClass;
$request->ParentFolderIds->DistinguishedFolderId->Mailbox->EmailAddress = $email_address;

// Send request
$response = $ews->FindItem($request);

When this code is run, we get a 404 from the SOAP client:

Fatal error: Uncaught exception 'Exception' with message 'SOAP client returned status of 404.' in /*dirs*/Client.php:1650 Stack trace: #0 /*dirs*/Client.php(1633): jamesiarmes\PhpEws\Client->processResponse(NULL) #1 /*dirs*/Client.php(670): jamesiarmes\PhpEws\Client->makeRequest('FindItem', Object(jamesiarmes\PhpEws\Request\FindItemType)) #2 /*dirs*/index_dev.php(64): jamesiarmes\PhpEws\Client->FindItem(Object(jamesiarmes\PhpEws\Request\FindItemType)) #3 /*dirs*/index_dev.php(269): getEventHTML('email@domain...', '2017-07-18T02:0...', '2017-07-18T21:5...') #4 {main} thrown in /*dirs*/Client.php on line 1650

I believe that I do have the connection set up correctly, because when I alter the credentials, I do get a 401.

I have looked into this page: PHP-EWS “Soap client returned status of 404” And I've tried the outlook.office365.com/EWS/Exchange.asmx endpoint as well, but I still get the SOAP 404.

Because of this, I thought this was enough of a separate question. (Although the more I research, the more that the REST client may be the next step)

I might be on the completely wrong track as well, so any help would be greatly appreciated!

Xgongiveittoya
  • 753
  • 1
  • 14
  • 33

3 Answers3

3

I had a similar issue when we moved from an on-premises Exchange server to Office 365 and managed to trace the issue back to SoapClient.php under php-ntlm.

Going from the error that was thrown in your request:

Fatal error: Uncaught exception 'Exception' with message 'SOAP client returned status of 404.' .... thrown in /*dirs*/Client.php on line 1650

If we look at that line in Client.php the exception appears to come from the function that calls the aforementioned SoapClient.php script.

protected function processResponse($response)
{
    // If the soap call failed then we need to throw an exception.
    $code = $this->soap->getResponseCode();
    if ($code != 200) {
        throw new \Exception(
            "SOAP client returned status of $code.",
            $code
        );
    }

    return $response;
}

I was able to resolve the issue by modifying the CURL request options in SoapClient.php (located around line 180).

Original Code:

protected function curlOptions($action, $request)
{
    $options = $this->options['curlopts'] + array(
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => $this->buildHeaders($action),
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_HTTPAUTH => CURLAUTH_BASIC | CURLAUTH_NTLM,
        CURLOPT_USERPWD => $this->options['user'] . ':'
                           . $this->options['password'],
    );
    // We shouldn't allow these options to be overridden.
    $options[CURLOPT_HEADER] = true;
    $options[CURLOPT_POST] = true;
    $options[CURLOPT_POSTFIELDS] = $request;
    return $options;
}

Modified Code:

protected function curlOptions($action, $request)
{
    $cOpts = array(
        CURLOPT_PROXY => "my.proxy.com:8080",
        CURLOPT_PROXYUSERPWD => $this->options['user'] . ':'
                           . $this->options['password'],
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => $this->buildHeaders($action),
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_HTTPAUTH => CURLAUTH_BASIC | CURLAUTH_NTLM,
        CURLOPT_USERPWD => $this->options['user'] . ':'
                           . $this->options['password'],
    );
    $options = $this->options['curlopts'] + $cOpts;
    // We shouldn't allow these options to be overridden.
    $options[CURLOPT_HEADER] = true;
    $options[CURLOPT_POST] = true;
    $options[CURLOPT_POSTFIELDS] = $request;

    return $options;
}

I set CURLOPT_SSL_VERIFYPEER to false and also added proxy options to the request as the connection is made from inside a corporate network which requires proxy authentication to access any external sites.

In my mailing PHP script I create the client using the following code:

$server = 'outlook.office365.com';
$username = 'user@domain.com';
$password = 'myPassword';
$version = Client::VERSION_2016;
$client = new Client($server, $username, $password, $version);
Tim B
  • 41
  • 4
2

Have you tried using the solution from https://github.com/jamesiarmes/php-ews/issues/196 eg change

$version = Client::VERSION_2016; 
$ews = new Client($server, $email, $password,$version);
Glen Scales
  • 20,495
  • 1
  • 20
  • 23
  • Unfortunately I have. I still get the same error regardless of the presence of the $version variable. Thanks for the assistance though! – Xgongiveittoya Jul 19 '17 at 13:04
  • 1
    On Office365 the Auth you should be using is basic, if you have a different UPN to Email address then you should be using the UPN as the username rather then the email. You could try doing some testing the with EWSEditor https://ewseditor.codeplex.com/ to validate the correct details to use – Glen Scales Jul 19 '17 at 22:25
2

I can't make out what is wrong with your code but maybe the following helps. I'm using this script successfully to export my calendar regulary from o365.

Host and User are like this:

host = "outlook.office365.com"
username = "user@domain.com"

Script:

$start_date = new Datetime('today -1 months');
$end_date = new Datetime('today +1 months');

$timezone = 'W. Europe Standard Time';

$ini_array = parse_ini_file($credentials_ini);
$host = $ini_array['host'];
$username = $ini_array['username'];
$password = $ini_array['password'];
$version = Client::VERSION_2016;

$client = new Client($host, $username, $password, $version);

$client->setTimezone($timezone);

$request = new FindItemType();
$request->ParentFolderIds = new NonEmptyArrayOfBaseFolderIdsType();

$request->ItemShape = new ItemResponseShapeType();
$request->ItemShape->BaseShape = DefaultShapeNamesType::ALL_PROPERTIES;
$folder_id = new DistinguishedFolderIdType();
$folder_id->Id = DistinguishedFolderIdNameType::CALENDAR;
$request->ParentFolderIds->DistinguishedFolderId[] = $folder_id;
$request->Traversal = ItemQueryTraversalType::SHALLOW;
$request->CalendarView = new CalendarViewType();
$request->CalendarView->StartDate = $start_date->format('c');
$request->CalendarView->EndDate = $end_date->format('c');
$request->ConnectionTimeout = 60;

$response = $client->FindItem($request);

$response_messages = $response->ResponseMessages->FindItemResponseMessage;
foreach ($response_messages as $response_message) {
  $items = $response_message->RootFolder->Items->CalendarItem;

  foreach ($items as $event){
    $id = $event->ItemId->Id;
    $subject = $event->Subject;
    $location = $event->Location;
    // ...

    // do something with it
  }
}
lumen
  • 219
  • 1
  • 2
  • 10