I am developing an outlook add-in which is supposed to sync an email to another server for our use case. I was able to get the mail stored on the remote DB but the problem is with fetching attachments. Since an add-in can't itself send an attachment, I have to send the remote server some details such as (ews url, access token, attachment id etc) so that it can use that information the contact the outlook server for getting respective attachments. No matter how I tried it appears to be failing. This basically leaves me thinking does outlook.com supports EWS? Proper references are hard to find but some of those references say 'No'. But my belief is since outlook.com looks like a part of 'Exchange Online' (although free) then it should support EWS too. This is evident by the fact that I am able to retrieve an ewsUrl and an attachmentToken by executing getCallbackTokenAsync() method of the Office.context.mailbox object via my outlook add-in. The add-in documentation says:
"The Office.context.mailbox object provides the getCallbackTokenAsync function to get a token that the remote server can use to authenticate with the Exchange server."
So there it is. But when I try to execute a SOAP request against the ewsUrl with an XML payload (from my remote server), the outlook server endpoint (https://outlook.office365.com/EWS/Exchange.asmx) keeps returning an error 500 - internal server error. There's not much conclusive details as well in the response so I can track down if there's anything wrong with the request I send to it. I am really puzzled at this. Appreciate some help from somebody who has done this already. Below is the code block (Laravel) I execute for your reference:
// check for attachments
if(is_object($incoming->attachments))
{
// we have attachments
$attachments = $incoming->attachments->attachments;
$ewsToken = $incoming->attachments->attachmentToken;
$ewsUrl = $incoming->attachments->ewsUrl;
foreach($attachments as $k => $v){
Log::info('Attachment ID: '.$v->id);
$attachmentSoapRequest = <<<EOD
#"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:xsi=""https://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""https://www.w3.org/2001/XMLSchema""
xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">
<soap:Header>
<t:RequestServerVersion Version=""Exchange2013"" />
</soap:Header>
<soap:Body>
<GetAttachment xmlns=""http://schemas.microsoft.com/exchange/services/2006/messages""
xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">
<AttachmentShape/>
<AttachmentIds>
<t:AttachmentId Id=""$v->id""/>
</AttachmentIds>
</GetAttachment>
</soap:Body>
</soap:Envelope>"
EOD;
try{
$response = Http::withBody($attachmentSoapRequest, 'text/xml')->withToken($ewsToken)->withOptions([
'debug' => true,
'allow_redirects' => false
])->post($ewsUrl);
// process response
if ($response->successful()) {
Log::info('received ews response!');
$responseEnvelope = $response->body();
Log::info($responseEnvelope);
} else {
$response->throw();
}
} catch (RequestException $e) {
Log::info('ews request error!');
Log::info($e->getMessage());
}
Log::info(print_r($response, true));
}
}
If somebody knows the answer, it doesn't has to be Laravel specific. Even a pure PHP solution is welcome.
Thanks.
Related
I want to subscribe the Spring framework WebSocket and receive the reply.
According to my target WebSocket server, the communication is done using STOMP publish protocol (Build based on Java Springframework API) https://stomp.github.io/stomp-specification-1.1.html
Now the client that I am working on, is build based on PHP and using https://github.com/Textalk/websocket-php/ for websocket client.
My idea to receive the server response is to follow the STOMP over Websocket technique based on this guy's answer Websocket Client not receiving any messages.
Using the current websocket client, I perform these steps
Send Connection (request?)
Send Subscription
Actively receive the reply
$client = new WebSocket\Client($ws_url);
//Step 1 Inintate connection;
$open_msg = "CONNECT\naccept-version:1.0,1.1,2.0\n\n\x00\n";
//Step 2 Subscribe Request;
$client->send($open_msg);
$subs = "SUBSCRIBE\nid:0\ndestination:/user/queue\nack:auto\n\n\x00\n";
$client->send($subs);
while (true) {
try {
$message = $client->receive();
echo $message;
// Act[enter image description here][4] on received message
// Later, Break while loop to stop listening
} catch (\WebSocket\ConnectionException $e) {
// Possibly log errors
}
}
$client->close();
The connection (Step 1) is done and tested.
current send and receive result image
Since it is running on the loop, the Received is always printed.
Does anyone know why the API did not send reply?
It turns out, I have to implement the other Websocket library instead
Instead of using https://github.com/Textalk/websocket-php/ , I moved on and use https://github.com/ratchetphp/Pawl
I don't know what just happened. But I think Textalk is synchronous websocket library and ratchet is asynchronous websocket library.
My current hypothesis is whenever you want to do Stomp over websocket, make sure
Send Connection message ("CONNECT\naccept-version:1.0,1.1,2.0\n\n\x00\n")
Send subscription ("SUBSCRIBE\nid:0\ndestination:/user/queue\nack:auto\n\n\x00\n")
Use the asynchronous Websocket instead of synchronous one
Have a nice day
I've setup a basic webhook php page as modeled on the stripe documentation and listed below. When I send a test event from the Stripe webhooks dashboard, stripe responds "Test webhook sent successfully" with a blankk reponse. However, the output log file is not written to, no email is sent and there is nothing logged to the http server error log or the php error log. My php version is 5.3.3. What am I doing wrong?
<?php
error_reporting(15);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
require_once('vendor/autoload.php');
\Stripe\Stripe::setApiKey("secret_test_key");
$handle = fopen("webhook.log","a");
// Retrieve the request's body and parse it as JSON
$input = file_get_contents("php://input");
$event_json = json_decode($input);
// Do something with $event_json
if (fwrite($handle, $event_json) === FALSE) {
mail("mike#example.com","Cannot write to webhook.log","");
echo "Cannot write to webhook.log";
exit;
}
mail('mike#example.com','Webhook Event',$event_json);
header(':', true, 200);
//http_response_code(200); // PHP 5.4 or greater
?>
You have a few potential problems. As a quick rule of thumb, the best way to debug this is to start by triggering the event yourself, which you can do simply by loading up your webhook url in a browser yourself. Then you can test it directly and make sure it is doing what you expect it to be doing. There are obviously two possibilities:
Stripe is not triggering your webhook handler for some reason
Your handler is not properly logging itself
The latter first: it could be that Stripe is triggering your handler but it isn't logging that fact successfully. This would mean that both your email logging and file logging are failing. That is actually quite possible. Email logging with the mail function is actually very unreliable, unless you know for a fact that it works. Mail sent with the mail function is dropped silently by most modern email systems (gmail, etc) unless you have your DNS records properly configured, which most people don't. So unless you know for sure that your mail attempt is working properly, it probably isn't. If you also happen to have a permission issue in your attempt to write to a log file (which is not uncommon for a newly setup server), your logs could simply be failing. The easiest way to check that is to load up the webhook URL in a browser yourself. That way you know it is being triggered, and can know for sure if the issue is improper logging or Stripe not calling your webhook.
If you determine for sure that stripe isn't calling your webhook, the most likely culprit would be an invalid HTTPS certificate. Is your webhook connected via HTTPS (it should be)? If so, is it a valid certificate? You can tell your browser to ignore an invalid certificate when you browse your own site, but stripe will simply refuse to send the request if it encounters an invalid certificate.
If none of the above fixes it then it will be time for more digging, but I would start with those: they are probably the most likely problems.
The solution is that $event_json is an object and the fwrite failed because it expects a string not an object. By converting to an array and then serializing I was able to both write to the log and send the email.
$event_json = (array)json_decode($input);
$event = serialize($event_json);
I am trying to connect to an API using PHP and its built-in SoapClient. I have checked against the url I was given through the ill-formatted documents the client gave and $client->__getFunctions() returns a list of three functions. HelloWorld($name), which responds with Hello ~name~, shows me that I am communicating with the server through the SoapClient call and the URL is correct.
However, when I try to access one of the other methods that __getFunctions() gives me, even after copy/pasting the XML from the docs and putting in my own credentials, I am still being given an Internal Server Error faultstring and 500 as faultcode from the SoapFault object.
I am sure that it is my own XML string that is causing the issue but I cannot for the life of me figure out how. Reaching out to the API provider directly hasn't proven helpful. This is my first time dealing with Soap/Web Services so I am unsure of where to go from here.
I did wget http//xxx.xxx.xxx?wsdl and it returned me what looks like a valid XML response, the same one I get when I go directly to the url in the browser. What should I be looking into in order to solve this issue? All of the past API's I've dealt with have been JSON/RESTful so I feel out of my element trying to debug PHP errors.
Edit
I have slowly deleted parts of my method call and parts of my XML string, trying to trigger a different error or something in order to find what I need to fix. What I have found is that by not passing in my XML string, I get a valid response from the $client->FunctionCall(...). It's an "this isn't right" message but it's a message! In fact, passing that function ANYTHING for the xml parameter causes the 500 http faultcode/faultstring. Does this mean that my XMl is poorly formatted or does it mean that there is an issue on their end handling requests?
Second Edit
If I make my $client decleration as follows, I get the faultstring Could not connect to host
$opts = array(
'ssl' => array('ciphers'=>'RC4-SHA')
);
$client = new SoapClient($CREDS['orderingWSDL'], array (
"encoding"=>"ISO-8859-1",
'stream_context' => stream_context_create($opts),
'exceptions'=>true,
));
I am getting more confused the longer I try to fix this.
Sometimes a 500 status coming from a SOAP service could be a SoapFault exception being thrown. To help your troubleshooting, you'll want to be able to inspect both your request XML, and the response XML.
Put your code in try/catch blocks, and use $client->__getLastRequest() and $client->__getLastResponse() to inspect the actual XML.
Example:
$client = new SoapClient('http//xxx.xxx.xxx?wsdl', array('soap_version'=>SOAP_1_1,'trace' => 1,'exceptions' => true));
try {
$response = $client->someFunction();
var_dump($response);
} catch (Exception $e) {
var_dump($e->getMessage());
var_dump($client->__getLastRequest());
var_dump($client->__getLastResponse());
}
I’m trying to invoke a WCF service (.NET) from PHP. It’s a little more complicated than just using a SoapClient since the service uses a WS2007FederationHttpBinding to authenticate.
Here’s the code I’m using at the moment. I haven’t even added credentials as I’m not sure how, but regardless, I’m not even at the point where I’m getting access denied errors.
$wsdl = "https://slc.centershift.com/sandbox40/StoreService.svc?wsdl";
$client = new SoapClient($wsdl,array(
//'soap_version'=>SOAP_1_2 // default 1.1, but this gives 'Uncaught SoapFault exception: [HTTP] Error Fetching http headers'
));
$params = array();
$params['SiteID'] = 123;
$params['GetPromoData'] = false;
$ret = $client->GetSiteUnitData(array('GetSiteUnitData_Request'=>$params));
print_r($ret);
Which WSDL should I be pointing to?
https://slc.centershift.com/Sandbox40/StoreService.svc?wsdl
Seems to be very short, but includes a reference to (note the wsdl0) https://slc.centershift.com/Sandbox40/StoreService.svc?wsdl=wsdl0
https://slc.centershift.com/Sandbox40/StoreService.svc?singleWsdl
Seems to have everything in it.
Do I need to specify SOAP 1.2? When I do, I get a connection timeout ([HTTP] Error Fetching http headers). When I don’t, the default of SOAP 1.1 is used and I get a [HTTP] Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'. Is this because I’m not authenticated yet, or because I’m using the wrong SOAP version?
How to authenticate in PHP? Here’s the corresponding .NET/C# code. Do I need to somehow put these as SOAP headers? Or am I thinking about it all wrong, and I need to do some kind of authentication before I even call the method (from what I read, I’m supposed to get a token back and then use it for all future method calls – I think I see an example of this in an answer here on Stack Overflow.
If I call $client->__getFunctions(), using either WSDL and either SOAP version, I’m getting a valid list of all functions, so I assume either of these is fine and my real issue is the authentication.
Other programmers I’ve talked to had spent time trying to get this to work, but gave up and instead implemented a proxy in .NET. They pass their parameters from PHP to their own unsecured .NET service, which in turn calls this secure service. It works, but seems crazily inefficient to me, and counter-productive, as the purpose of WCF is to support all types of clients (even non-HTTP ones!).
I’ve read How to: Create a WSFederationHttpBinding on MSDN, but it didn’t help.
You can use this URL for WSDL https://slc.centershift.com/Sandbox40/StoreService.svc?singleWsdl. This WSDL has all definitions.
You have to use 1.2 because this webservice works with SOAP 1.2 version. I tried it with 1.1 and 1.2 and both of them gived error. 1.1 is version error, 1.2 is timeout error. I think there is an error at this test server. I used it with svcutil to generate code but it gived error too. Normaly it should get information and generate the code example to call service.
Normally you can add authenticate parameters with SoapHeader or directly add to options in SoapClient consruct (if service authentication is basic authentication). I write below code according to your screenshot. But it gives timeout after long wait.
$wsdl = "https://slc.centershift.com/sandbox40/StoreService.svc?wsdl";
$client = new SoapClient($wsdl,array('trace' => 1,'soap_version' => SOAP_1_2));
$security = array(
'UserName' => array(
'UserName'=>'TestUser',
'Password'=>'TestPassword',
'SupportInteractive'=>false
)
);
$header = new SoapHeader('ChannelFactory','Credentials',$security, false);
$client->__setSoapHeaders($header);
$params = array();
$params['SiteID'] = 100000000;
$params['Channel'] = 999;
try {
$ret = $client->GetSiteUnitData($params);
print_r($ret);
}catch(Exception $e){
echo $e->getMessage();
}
__getFunctions works, because it prints functions defined in WSDL. There is no problem with getting WSDL information at first call. But real problem is communication. PHP gets WSDL, generates required SOAP request then sends to server, but server is not responding correctly. SOAP server always gives a response even if parameters or request body are not correct.
You should communicate with service provider, I think they can give clear answer to your questions.
Having worked with consuming .NET WS from PHP before I believe you would need to create objects from classes in PHP that matches the names that .NET is expecting. The WSDL should tell you the types it is expecting. I hope this assist with your path forward!
If the SOAP call works from a C# application, you could use Wireshark (with the filter ip.dst == 204.246.130.80) to view the actual request being made and then construct a similar request from php.
Check this answer to see how you can do a custom SOAP call.
There's also the option of doing raw curl requests, since it might be easier to build your xml body, but then you would have to parse the response yourself with simplexml.
I need to reply back to a response received.
How should I reply using the response?
I want to reply to a response received in my MDM System. The device contacts the server and delivers status as idle. Now I need to send a xml command directly to the device.
You can't push from PHP Webserver to HTTPClient, the HTTPClient must contact you first (Unless you write a sockets module, then php could be the wrong language) - Only said it as I'm not certain what your trying to achieve.
Anyway straight to the point - Once the device contacts the server, and gives the status 'idle', you can just immediately write out an echo response
if(isset($_POST['status']) && $_POST['status'] == 'idle') {
echo '<?xml version="1.0" encoding="ISO-8859-1"?><root>
<data>blablabla</data>
</root>';
}
etc. Depends on how the devices actually contacts the server, is it sending a post command? Is it actually connecting as a HTTPClient or just a direct TCP/IP? Is there anymore info you can provide?