PHP library for Office365 EWS with GetRooms - wsdl

Whole last week i struggled with Php libraries for EWS. I tried Package365Ews and Php-ews but both of them are missing core feature for me, or it's not documented - GetRooms. Do anyone know how to handle it, or know another library implementing this?

I personally would suggest my own library, garethp/php-ews.
It's got simple usage, but not everything is covered under simpler API's. EWS is a large thing, and documenting everything would be intense. That being said, I can certainly help you translate existing documentation by Microsoft to using this code. And, if you find yourself with more issues after this post, I check my Github daily, so logging an issue against my repository will get more help in a better place for a back and forth.
But first, let me outlay how to perform functions that aren't directly documented. Like GetRooms. My API wraps around EWS, it doesn't block your access to it. So even though I've made no obvious way to do a GetRooms, it's still there. Like this
<?php
use garethp\ews\API;
use garethp\ews\API\Type;
$api = API::fromUsernameAndPassword($server, $username, $password);
//Build Request
$result = $api->getClient()->GetRooms($request);
var_dump($result);
So, the question becomes, how do we build the request? Well, thankfully EWS is very well documented in XML. First, find the article that describes what you're trying to do, then look for the XML. I'm not 100% what you want to do, but I'll use this article as a base. The XML that we're going to try to replicate is
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010" />
</soap:Header>
<soap:Body>
<m:GetRooms>
<m:RoomList>
<t:EmailAddress>bldg3rooms#contoso.com</t:EmailAddress>
</m:RoomList>
</m:GetRooms>
</soap:Body>
</soap:Envelope>
You can skip the header, and the <m:GetRooms> part, those are built for you. What we're focused on is the payload you want to send, which is
<m:RoomList>
<t:EmailAddress>bldg3rooms#contoso.com</t:EmailAddress>
</m:RoomList>
We want to make our request look like that. So, in our code, our request will look like:
$request = array (
'RoomsList' => array (
'EmailAddress' => 'bldg3rooms#contoso.com'
)
);
$request = Type::buildFromArray($request);
And this will be translated to XML for you for the SOAP call. Using this method, for any functions that aren't documented or outright supported, you can easily still use them and just refer to the official Microsoft documentation for any request you need to make

Related

How can I generate the "AWS Authentication String" to send a POST to CloudFront from iOS?

I want to use the CloudFront API to POST an invalidation request (http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/CreateInvalidation.html).
The request should look like this:
POST /2012-07-01/distribution/distribution ID/invalidation HTTP/1.0
Host: cloudfront.amazonaws.com
Authorization: AWS authentication string
Content-Type: text/xml
Other required headers
<?xml version="1.0" encoding="UTF-8"?>
<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2012-07-01/">
<Paths>
<Quantity>number of objects to invalidate</Quantity>
<Items>
<Path>/path to object to invalidate</Path>
</Items>
</Paths>
<CallerReference>unique identifier for this invalidation batch</CallerReference>
</InvalidationBatch>
What's the best way to generate the "AWS authentication string" from my iPhone app?
This link describes how to create the string, but it seems overly complicated: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RESTAuthentication.html
I have access to my PEM file if that helps at all.
I didn't see it it in the AWS iOS SDK . Sounds like it might not be available just yet. Which is odd since other libraries, like Ruby's fog and aws-cloudfront, seem to have ways.

Mapping extension content types in Apache CXF JAX-RS

JAX-RS offers a wonderful way to specify content types in #Produces, and the framework will automatically determine the best content type from the client's HTTP Accept header and, wonder of wonders, even convert your object to that type (e.g. XML using JAXB or JSON using Jackson) when returning information to the caller.
My (work) client, as clients often do, made a simple job more difficult by requesting I specify the content type by the extension in the URL, e.g. api/widgets.json. This would force me to have various getWidgetsXXX() methods, one with #Produces("application/json"), another with #Produces("application/xml"), etc.
But I'm using Apache CXF and I was delighted to find that I could configure CXF to map various extensions to content types using the jaxrs.extensions init parameter!
<!-- registers extension mappings -->
<init-param>
<param-name>jaxrs.extensions</param-name>
<param-value>
xml=application/xml
json=application/json
</param-value>
</init-param>
But I can find absolutely no documentation on how this works in the real world. I naively thought I could just annotate a method with a path with an extension and it would mimic the Accepts header:
#Path("/widgets.{extension}")
#GET
#Produces({ "application/json", "application/xml" })
public List<Widget> getWidgets();
So I call it using api/widgets.json, and it returns XML! Which is particularly odd, because JAX-RS specifies that the default content type is the first one listed.
Where can I find out how to use CXF extension content type mapping?
P.S. I am not using Spring.
Adding the following in your <jaxrs:server> works:
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
Source: http://cxf.apache.org/docs/jax-rs.html#JAX-RS-Debugging
Don't know whether that help you or not but I was also facing the same issue to introduce something like that in my JAX-RS services. I achieved this functionality using JAX-RS_Content_Negotiation Following location has details about it.
https://docs.jboss.org/resteasy/docs/3.0.6.Final/userguide/html/JAX-RS_Content_Negotiation.html
You just have to map your media types with the values which you want
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
<param-value>
html : text/html, json : application/json, xml :
application/xml
</param-value>
</context-param>
#GET
#Path("/second/{param}")
#Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response printStudent(#PathParam("param") String msg) {
}
now i can access my services like that and response is according the extension which i put at the end
http://localhost:8080/RESTfulExample/rest/message/second/bill.json
you can put .xml OR .json at the end of the url and service will generate response accordingly.
In your situation, I'd declare that the method #Produces the content type */* (i.e., a full wildcard) and then do the content negotiation myself. You'd probably be looking at a method signature like this:
#javax.ws.rs.GET
#javax.ws.rs.Path("{filename}")
#javax.ws.rs.Produces("*/*")
javax.ws.rs.core.Response getDirectoryOrFileContents(
#javax.ws.rs.PathParam("filename") String filename,
#javax.ws.rs.core.Context javax.ws.rs.core.HttpHeaders headers);
That gives you access to both the desired filename — one way to guess the media type to deliver — and the full set of HTTP headers (hint: use headers.getAcceptableMediaTypes()), which give the other way. How to balance the two is likely to be “interesting”. (The code I've got to do it is very specific to my app's internal model, so isn't likely to be useful to you.) You then return the result by constructing a Response, which gives you quite close control over what the client gets back.
Yes, this is more work than letting CXF handle all this for you (it normally generates a lot of boilerplate to do all of this stuff) but in a complex case you'll be glad of the control.
The extension mimics the Accept header as you guessed. However you must not declare the extenstion in the #Path annotation:
#Path("/widgets")
#GET
#Produces({ "application/json", "application/xml" })
public List<Widget> getWidgets();
You can then call widgets.xml or widgets.json

Fault calling SAP web service with generated SUDZC proxy: CX_ST_MATCH_ELEMENT

Trying to call a SAP SOAP Web Service from a generated sudzc app shows errors I don't know:
SudzCExamples[5192:f803] <?xml version="1.0" encoding="utf-8"?><soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:sap-
com:document:sap:soap:functions:mc-style"><soap:Body><ZComUrlGetrecords>
<IYear>2012</IYear></ZComUrlGetrecords></soap:Body></soap:Envelope>
SudzCExamples[5192:f803] <soap-env:Envelope xmlns:soap-
env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Header></soap-env:Header><soap-
env:Body><soap-env:Fault><faultcode>soap-env:Server</faultcode><faultstring
xml:lang="en">CX_ST_MATCH_ELEMENT:XSLT exception.System expected element
'IYear'</faultstring><detail><ns:SystemFault
xmlns:ns="http://www.sap.com/webas/710/soap/runtime/abap/fault/system/">
<Host>undefined</Host><Component>APPL</Component><ChainedException>
<Exception_Name>CX_SOAP_CORE</Exception_Name><Exception_Text>CX_ST_MATCH_ELEMENT:XSLT
exception.System expected element 'IYear'</Exception_Text></ChainedException>
<ChainedException><Exception_Name>CX_SXMLP</Exception_Name><Exception_Text>XSLT
exception</Exception_Text></ChainedException><ChainedException>
<Exception_Name>CX_ST_MATCH_ELEMENT</Exception_Name><Exception_Text>System expected
element 'IYear': Main Program:/1BCDWB/WSS825E06E4DEC40F9171D|
Program:/1BCDWB/WSS825E06E4DEC40F9171D| Line: 18| Valid:X</Exception_Text>
</ChainedException></ns:SystemFault></detail></soap-env:Fault></soap-env:Body></soap-
env:Envelope>
2012-03-11 20:09:30.631 SudzCExamples[5192:f803] soap-env:Server CX_ST_MATCH_ELEMENT:XSLT
exception.System expected element 'IYear'
(null)
The strange thing is that it seems as if the request has the IYear element. Can someone tell me where to search the problem?
I ran into this same problem yesterday and discovered the solution after some experimentation. First thing I did was use my SoapUI client to make the request successfully. SoapUI comes with a free trial and even if you do not use the free trial you can still use it to make accesses to the web service without registering it. I used the xml from the successful request I made to compare against the request that SudzC was making. They differ in several ways, and the way that SudzC forms the request is not sufficient.
My suggestion to you is to compare the two requests and change SudzC's request to match the SoapUI request. You can do this by editing the Soap source code that SudzC gives to you, this source code is found particularly in the Soap.m file in the createEnvelope function.
Also, if your requests have an empty header SudzC does not include the header part of the request. Hard code in an empty header after the namespace portion of the envelope. Doing all this fixed this exact issue for me.

Bad Request in SOAPUI

I am attempting to consume a web service using Delphi 2010 and Indy. To establish a usable SOAP stream to compare to the one created by my program, I am testing in SOAPUI. I am using a SOAP stream provided by the web service provider which also matches the SOAP stream specified in the WSDL file. I am getting an HTTP 400 (bad request) error from the service.
From what I can find online, it appears that receiving an HTTP 400 error indicates that your SOAP request is malformed and can not be read by the web service. I have tested my SOAP stream using XMLPad and the XML seems to be well formed. I suppose this may mean that something does not match its schema requirement. I will first check the schema description for the password in case that is expected to not be sent as plain text. What else should I be checking to eliminate an HTTP 400 error?
Here is my request (less username and password) in case it helps:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://wwww3.org/2001/XMLSchema-instance">
<soap:Header>
<wsa:Action>http://edd.ca.gov/SendTransmission</wsa:Action>
<wsa:MessageID>urn:uuid:5aa788dc-86e1-448b-b085-2d2743cf9f26</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://fsettestversion.edd.ca.gov/fsetproxy/fsetservice.asmx</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken">
<wsse:Username>#USERNAME#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">#PASSWORD#/wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">O5QWht1bslLCX6KnlEypAA==</wsse:Nonce>
<wsu:Created>2012-02-29T22:32:38.250Z</wsu:Created>
</wsse:UsernameToken>
<wsu:Timestamp wsu:Id="Timestamp-805a7373-335c-43b6-ba21-6596c4848dbf">
<wsu:Created>2012-02-22T15:41:42Z</wsu:Created>
<wsu:Expires>2012-02-22T15:46:42Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<SendTransmission xmlns="http://edd.ca.gov/">
<SendTransmissionRequest xmlns="http://www.irs.gov/a2a/mef/MeFTransmitterServiceWse.xsd">
<TransmissionDataList>
<Count>1</Count>
<TransmissionData>
<TransmissionId>123456789</TransmissionId>
<ElectronicPostmark>2012-02-22T07:41:42.2502206-08:00</ElectronicPostmark>
</TransmissionData>
</TransmissionDataList>
</SendTransmissionRequest>
<fileBytes>
<xop:Include href="cid:1.634654933022658454#example.org"/>
</fileBytes>
</SendTransmission>
</soap:Body>
</soap:Envelope>
There may be something else, but at the moment, I am suspicious of the wsse:UsernameToken. I downloaded the document at http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf and read it last night. It's written in fairly plain language and I feel like I understand what it is saying but it leaves me with a smaller question than the one I asked originally. This document proposes that you can use a plain text password in this format:
<S11:Envelope xmlns:S11="..." xmlns:wsse="...">
<S11:Header>
...
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>Zoe</wsse:Username>
<wsse:Password>IloveDogs</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
...
</S11:Header>
...
</S11:Envelope>
Or you can use a password digest. It defines a password digest like this:
Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
According to the reference, the format for a password digest would look like this:
<S11:Envelope xmlns:S11="..." xmlns:wsse="..." xmlns:wsu= "...">
<S11:Header>
...
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>NNK</wsse:Username>
<wsse:Password Type="...#PasswordDigest">
weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==
</wsse:Password>
<wsse:Nonce>WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>
<wsu:Created>2003-07-16T01:24:32Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
...
</S11:Header>
...
</S11:Envelope>
This is not the format used in the example provided by the web service publisher. The plain text version in the reference does not use a nonce. The example message uses a nonce but calls for a plain text password. It appears to me that the use of a nonce without a password digest does not add any security to the message. It could be any random string of characters if there is no agreement for how it is to be created. Am I missing the point?
I know this must seem like a tedious undertaking, but I am hoping that by providing this here, maybe we can provide a little help to the next person coming along.
I too have come across this issue. The web service publisher (edd.ca.gov) responded by stating that the " value is required by the SOAP 1.2 standards" yet I find no valid support for that. It looks like we both are heading down the same path (FSET) and maybe we should team up and work together, two heads are better than one. I have found many mistakes within the example code and I too have yet get it to work.

Microsoft Translator API answers 500 internal server error

I'm trying to use Microsoft's Translator API in my Rails app. Unfortunately and mostly unexpected, the server answers always with an internal server error. I also tried it manually with Poster[1] and I get the same results.
In more detail, what am I doing? I'm creating an XML string which goes into the body of the request. I used the C# Example of the API documentation. Well, and then I'm just invoking the RESTservice.
My code looks like this:
xmlns1 = "http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"
xmlns2 = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"
xml_builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.TranslateArrayRequest("xmlns:ms" => xmlns1, "xmlns:arr" => xmlns2) {
xml.AppId token #using temporary token instead of appId
xml.From source
xml.To target
xml.Options {
xml["ms"].ContentType {
xml.text "text/html"
}
}
xml.Texts {
translate.each do |key,val|
xml["arr"].string {
xml.text CGI::unescape(val)
}
end
}
}
end
headers = {
'Content-Type' => 'text/xml'
}
uri = URI.parse(##msTranslatorBase + "/TranslateArray" + "?appId=" + token)
req = Net::HTTP::Post.new(uri.path, headers)
req.body = xml_builder.to_xml
response = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
# [...]
The xml_builder produces something like the following XML. Differently to the example from the API page, I'm defining two namespaces instead of referencing them on the certain tags (mainly because I wanted to reduces the overhead) -- but this doesn't seem to be a problem, when I do it like the docu-example I also get an internal server error.
<?xml version="1.0" encoding="UTF-8"?>
<TranslateArrayRequest xmlns:ms="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<AppId>TX83NVx0MmIxxCzHjPwo2_HgYN7lmWIBqyjruYm7YzCpwnkZL5wtS5oucxqlEFKw9</AppId>
<From>de</From>
<To>en</To>
<Options>
<ms:ContentType>text/html</ms:ContentType>
</Options>
<Texts>
<arr:string>Bitte übersetze diesen Text.</arr:string>
<arr:string>Das hier muss auch noch übersetzt werden.</arr:string>
</Texts>
</TranslateArrayRequest>
Every time I request the service it answers with
#<Net::HTTPInternalServerError 500 The server encountered an error processing the request. Please see the server logs for more details.>
... except I do some unspecified things, like using GET instead of POST, then it answers with something like "method not allowed".
I thought it might be something wrong with the XML stuff, because I can request an AppIdToken and invoke the Translate method without problems. But to me, the XML looks just fine. The documentation states that there is a schema for the expected XML:
The request body is a xml string generated according to the schema specified at http:// api.microsofttranslator.com/v2/Http.svc/help
Unfortunately, I cannot find anything on that.
So now my question(s): Am I doing something wrong? Maybe someone experienced similar situations and can report on solutions or work-arounds?
[1] Poster FF plugin > addons.mozilla.org/en-US/firefox/addon/poster/
Well, after lot's of trial-and-error I think I made it. So in case someone has similar problems, here is how I fixed this:
Apparently, the API is kind of fussy with the incoming XML. But since there is no schema (or at least I couldn't find the one specified in the documentation) it's kind of hard to do it the right way: the ordering of the tags is crucial!
<TranslateArrayRequest>
<AppId/>
<From/>
<Options />
<Texts/>
<To/>
</TranslateArrayRequest>
When the XML has this ordering it works. Otherwise you'll only see the useless internal server error response. Furthermore, I read a couple of times that the API also breaks if the XML contains improper UTF-8. One can force untrusted UTF-8 (e.g. coming from a user form) this way:
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
valid_string = ic.iconv(untrusted_string + ' ')[0..-2]

Resources