diff --git a/src/BeSimple/SoapClient/.gitignore b/src/BeSimple/SoapClient/.gitignore
new file mode 100644
index 0000000..f8d178e
--- /dev/null
+++ b/src/BeSimple/SoapClient/.gitignore
@@ -0,0 +1,5 @@
+vendor
+/phpunit.xml
+.buildpath
+.project
+.settings
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/CONTRIBUTORS.md b/src/BeSimple/SoapClient/CONTRIBUTORS.md
new file mode 100644
index 0000000..c79e9ef
--- /dev/null
+++ b/src/BeSimple/SoapClient/CONTRIBUTORS.md
@@ -0,0 +1,3 @@
+* [Francis Besset](https://github.com/francisbesset)
+* [aschamberger](https://github.com/aschamberger)
+* [Scheb](https://github.com/Scheb)
diff --git a/src/BeSimple/SoapClient/composer.json b/src/BeSimple/SoapClient/composer.json
new file mode 100644
index 0000000..0c5e927
--- /dev/null
+++ b/src/BeSimple/SoapClient/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "besimple/soap-client",
+ "type": "library",
+ "description": "Build and consume SOAP Client based web services",
+ "keywords": [ "soap", "soap-client" ],
+ "homepage": "https://github.com/BeSimple/BeSimpleSoapClient",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Francis Besset",
+ "email": "francis.besset@gmail.com"
+ },
+ {
+ "name": "Christian Kerl",
+ "email": "christian-kerl@web.de"
+ },
+ {
+ "name": "Andreas Schamberger",
+ "email": "mail@andreass.net"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "besimple/soap-common": "dev-master",
+ "ass/xmlsecurity": "dev-master"
+ },
+ "autoload": {
+ "psr-0": {
+ "BeSimple\\SoapClient": "BeSimple/SoapClient/src/"
+ }
+ },
+ "target-dir": "BeSimple/SoapClient"
+}
diff --git a/src/BeSimple/SoapClient/phpunit.xml.dist b/src/BeSimple/SoapClient/phpunit.xml.dist
new file mode 100644
index 0000000..4f8e547
--- /dev/null
+++ b/src/BeSimple/SoapClient/phpunit.xml.dist
@@ -0,0 +1,31 @@
+
+
+
+
+
+ ./tests/BeSimple/
+
+
+
+
+
+ benchmark
+
+
+
+
+
+ ./src/BeSimple/
+
+
+
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/Curl.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/Curl.php
new file mode 100644
index 0000000..54e40cf
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/Curl.php
@@ -0,0 +1,313 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+/**
+ * cURL wrapper class for doing HTTP requests that uses the soap class options.
+ *
+ * @author Andreas Schamberger
+ */
+class Curl
+{
+ /**
+ * HTTP User Agent.
+ *
+ * @var string
+ */
+ const USER_AGENT = 'PHP-SOAP/\BeSimple\SoapClient';
+
+ /**
+ * Curl resource.
+ *
+ * @var resource
+ */
+ private $ch;
+
+ /**
+ * Maximum number of location headers to follow.
+ *
+ * @var int
+ */
+ private $followLocationMaxRedirects;
+
+ /**
+ * Request response data.
+ *
+ * @var string
+ */
+ private $response;
+
+ /**
+ * Constructor.
+ *
+ * @param array $options Options array from SoapClient constructor
+ * @param int $followLocationMaxRedirects Redirection limit for Location header
+ */
+ public function __construct(array $options = array(), $followLocationMaxRedirects = 10)
+ {
+ // set the default HTTP user agent
+ if (!isset($options['user_agent'])) {
+ $options['user_agent'] = self::USER_AGENT;
+ }
+ $this->followLocationMaxRedirects = $followLocationMaxRedirects;
+
+ // make http request
+ $this->ch = curl_init();
+ $curlOptions = array(
+ CURLOPT_ENCODING => '',
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_FAILONERROR => false,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_HEADER => true,
+ CURLOPT_USERAGENT => $options['user_agent'],
+ CURLINFO_HEADER_OUT => true,
+ );
+ curl_setopt_array($this->ch, $curlOptions);
+ if (isset($options['compression']) && !($options['compression'] & SOAP_COMPRESSION_ACCEPT)) {
+ curl_setopt($this->ch, CURLOPT_ENCODING, 'identity');
+ }
+ if (isset($options['connection_timeout'])) {
+ curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, $options['connection_timeout']);
+ }
+ if (isset($options['proxy_host'])) {
+ $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
+ curl_setopt($this->ch, CURLOPT_PROXY, $options['proxy_host'] . ':' . $port);
+ }
+ if (isset($options['proxy_user'])) {
+ curl_setopt($this->ch, CURLOPT_PROXYUSERPWD, $options['proxy_user'] . ':' . $options['proxy_password']);
+ }
+ if (isset($options['login'])) {
+ curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+ curl_setopt($this->ch, CURLOPT_USERPWD, $options['login'].':'.$options['password']);
+ }
+ if (isset($options['local_cert'])) {
+ curl_setopt($this->ch, CURLOPT_SSLCERT, $options['local_cert']);
+ curl_setopt($this->ch, CURLOPT_SSLCERTPASSWD, $options['passphrase']);
+ }
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ curl_close($this->ch);
+ }
+
+ /**
+ * Execute HTTP request.
+ * Returns true if request was successfull.
+ *
+ * @param string $location HTTP location
+ * @param string $request Request body
+ * @param array $requestHeaders Request header strings
+ *
+ * @return bool
+ */
+ public function exec($location, $request = null, $requestHeaders = array())
+ {
+ curl_setopt($this->ch, CURLOPT_URL, $location);
+
+ if (!is_null($request)) {
+ curl_setopt($this->ch, CURLOPT_POST, true);
+ curl_setopt($this->ch, CURLOPT_POSTFIELDS, $request);
+ }
+
+ if (count($requestHeaders) > 0) {
+ curl_setopt($this->ch, CURLOPT_HTTPHEADER, $requestHeaders);
+ }
+
+ $this->response = $this->execManualRedirect($this->followLocationMaxRedirects);
+
+ return ($this->response === false) ? false : true;
+ }
+
+ /**
+ * Custom curl_exec wrapper that allows to follow redirects when specific
+ * http response code is set. SOAP only allows 307.
+ *
+ * @param int $redirects Current redirection count
+ *
+ * @return mixed
+ */
+ private function execManualRedirect($redirects = 0)
+ {
+ if ($redirects > $this->followLocationMaxRedirects) {
+
+ // TODO Redirection limit reached, aborting
+ return false;
+ }
+ curl_setopt($this->ch, CURLOPT_HEADER, true);
+ curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
+ $response = curl_exec($this->ch);
+ $httpResponseCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
+ if ($httpResponseCode == 307) {
+ $headerSize = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
+ $header = substr($response, 0, $headerSize);
+ $matches = array();
+ preg_match('/Location:(.*?)\n/', $header, $matches);
+ $url = trim(array_pop($matches));
+ // @parse_url to suppress E_WARNING for invalid urls
+ if (($url = @parse_url($url)) !== false) {
+ $lastUrl = parse_url(curl_getinfo($this->ch, CURLINFO_EFFECTIVE_URL));
+ if (!isset($url['scheme'])) {
+ $url['scheme'] = $lastUrl['scheme'];
+ }
+ if (!isset($url['host'])) {
+ $url['host'] = $lastUrl['host'];
+ }
+ if (!isset($url['path'])) {
+ $url['path'] = $lastUrl['path'];
+ }
+ $newUrl = $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query'] ? '?' . $url['query'] : '');
+ curl_setopt($this->ch, CURLOPT_URL, $newUrl);
+
+ return $this->execManualRedirect($redirects++);
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Error code mapping from cURL error codes to PHP ext/soap error messages
+ * (where applicable)
+ *
+ * http://curl.haxx.se/libcurl/c/libcurl-errors.html
+ *
+ * @return array(int=>string)
+ */
+ protected function getErrorCodeMapping()
+ {
+ return array(
+ 1 => 'Unknown protocol. Only http and https are allowed.', //CURLE_UNSUPPORTED_PROTOCOL
+ 3 => 'Unable to parse URL', //CURLE_URL_MALFORMAT
+ 5 => 'Could not connect to host', //CURLE_COULDNT_RESOLVE_PROXY
+ 6 => 'Could not connect to host', //CURLE_COULDNT_RESOLVE_HOST
+ 7 => 'Could not connect to host', //CURLE_COULDNT_CONNECT
+ 9 => 'Could not connect to host', //CURLE_REMOTE_ACCESS_DENIED
+ 28 => 'Failed Sending HTTP SOAP request', //CURLE_OPERATION_TIMEDOUT
+ 35 => 'Could not connect to host', //CURLE_SSL_CONNECT_ERROR
+ 41 => 'Can\'t uncompress compressed response', //CURLE_FUNCTION_NOT_FOUND
+ 51 => 'Could not connect to host', //CURLE_PEER_FAILED_VERIFICATION
+ 52 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_GOT_NOTHING
+ 53 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_NOTFOUND
+ 54 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_SETFAILED
+ 55 => 'Failed Sending HTTP SOAP request', //CURLE_SEND_ERROR
+ 56 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_RECV_ERROR
+ 58 => 'Could not connect to host', //CURLE_SSL_CERTPROBLEM
+ 59 => 'Could not connect to host', //CURLE_SSL_CIPHER
+ 60 => 'Could not connect to host', //CURLE_SSL_CACERT
+ 61 => 'Unknown Content-Encoding', //CURLE_BAD_CONTENT_ENCODING
+ 65 => 'Failed Sending HTTP SOAP request', //CURLE_SEND_FAIL_REWIND
+ 66 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_INITFAILED
+ 67 => 'Could not connect to host', //CURLE_LOGIN_DENIED
+ 77 => 'Could not connect to host', //CURLE_SSL_CACERT_BADFILE
+ 80 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_SSL_SHUTDOWN_FAILED
+ );
+ }
+
+ /**
+ * Gets the curl error message.
+ *
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ $errorCodeMapping = $this->getErrorCodeMapping();
+ $errorNumber = curl_errno($this->ch);
+ if (isset($errorCodeMapping[$errorNumber])) {
+
+ return $errorCodeMapping[$errorNumber];
+ }
+
+ return curl_error($this->ch);
+ }
+
+ /**
+ * Gets the request headers as a string.
+ *
+ * @return string
+ */
+ public function getRequestHeaders()
+ {
+ return curl_getinfo($this->ch, CURLINFO_HEADER_OUT);
+ }
+
+ /**
+ * Gets the whole response (including headers) as a string.
+ *
+ * @return string
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Gets the response body as a string.
+ *
+ * @return string
+ */
+ public function getResponseBody()
+ {
+ $headerSize = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
+
+ return substr($this->response, $headerSize);
+ }
+
+ /**
+ * Gets the response content type.
+ *
+ * @return string
+ */
+ public function getResponseContentType()
+ {
+ return curl_getinfo($this->ch, CURLINFO_CONTENT_TYPE);
+ }
+
+ /**
+ * Gets the response headers as a string.
+ *
+ * @return string
+ */
+ public function getResponseHeaders()
+ {
+ $headerSize = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
+
+ return substr($this->response, 0, $headerSize);
+ }
+
+ /**
+ * Gets the response http status code.
+ *
+ * @return string
+ */
+ public function getResponseStatusCode()
+ {
+ return curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
+ }
+
+ /**
+ * Gets the response http status message.
+ *
+ * @return string
+ */
+ public function getResponseStatusMessage()
+ {
+ preg_match('/HTTP\/(1\.[0-1]+) ([0-9]{3}) (.*)/', $this->response, $matches);
+
+ return trim(array_pop($matches));
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/MimeFilter.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/MimeFilter.php
new file mode 100644
index 0000000..beee156
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/MimeFilter.php
@@ -0,0 +1,138 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\Helper;
+use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart;
+use BeSimple\SoapCommon\Mime\Parser as MimeParser;
+use BeSimple\SoapCommon\Mime\Part as MimePart;
+use BeSimple\SoapCommon\SoapRequest;
+use BeSimple\SoapCommon\SoapRequestFilter;
+use BeSimple\SoapCommon\SoapResponse;
+use BeSimple\SoapCommon\SoapResponseFilter;
+
+/**
+ * MIME filter.
+ *
+ * @author Andreas Schamberger
+ */
+class MimeFilter implements SoapRequestFilter, SoapResponseFilter
+{
+ /**
+ * Attachment type.
+ *
+ * @var int Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM
+ */
+ protected $attachmentType = Helper::ATTACHMENTS_TYPE_SWA;
+
+ /**
+ * Constructor.
+ *
+ * @param int $attachmentType Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM
+ */
+ public function __construct($attachmentType)
+ {
+ $this->attachmentType = $attachmentType;
+ }
+
+ /**
+ * Reset all properties to default values.
+ */
+ public function resetFilter()
+ {
+ $this->attachmentType = Helper::ATTACHMENTS_TYPE_SWA;
+ }
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
+ *
+ * @return void
+ */
+ public function filterRequest(SoapRequest $request)
+ {
+ // get attachments from request object
+ $attachmentsToSend = $request->getAttachments();
+
+ // build mime message if we have attachments
+ if (count($attachmentsToSend) > 0) {
+ $multipart = new MimeMultiPart();
+ $soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT);
+ $soapVersion = $request->getVersion();
+ // change content type headers for MTOM with SOAP 1.1
+ if ($soapVersion == SOAP_1_1 && $this->attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
+ $multipart->setHeader('Content-Type', 'type', 'application/xop+xml');
+ $multipart->setHeader('Content-Type', 'start-info', 'text/xml');
+ $soapPart->setHeader('Content-Type', 'application/xop+xml');
+ $soapPart->setHeader('Content-Type', 'type', 'text/xml');
+ }
+ // change content type headers for SOAP 1.2
+ elseif ($soapVersion == SOAP_1_2) {
+ $multipart->setHeader('Content-Type', 'type', 'application/soap+xml');
+ $soapPart->setHeader('Content-Type', 'application/soap+xml');
+ }
+ $multipart->addPart($soapPart, true);
+ foreach ($attachmentsToSend as $cid => $attachment) {
+ $multipart->addPart($attachment, false);
+ }
+ $request->setContent($multipart->getMimeMessage());
+
+ // TODO
+ $headers = $multipart->getHeadersForHttp();
+ list($name, $contentType) = explode(': ', $headers[0]);
+
+ $request->setContentType($contentType);
+ }
+ }
+
+ /**
+ * Modify the given response XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
+ *
+ * @return void
+ */
+ public function filterResponse(SoapResponse $response)
+ {
+ // array to store attachments
+ $attachmentsRecieved = array();
+
+ // check content type if it is a multipart mime message
+ $responseContentType = $response->getContentType();
+ if (false !== stripos($responseContentType, 'multipart/related')) {
+ // parse mime message
+ $headers = array(
+ 'Content-Type' => trim($responseContentType),
+ );
+ $multipart = MimeParser::parseMimeMessage($response->getContent(), $headers);
+ // get soap payload and update SoapResponse object
+ $soapPart = $multipart->getPart();
+ // convert href -> myhref for external references as PHP throws exception in this case
+ // http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436
+ $content = preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent());
+ $response->setContent($content);
+ $response->setContentType($soapPart->getHeader('Content-Type'));
+ // store attachments
+ $attachments = $multipart->getParts(false);
+ foreach ($attachments as $cid => $attachment) {
+ $attachmentsRecieved[$cid] = $attachment;
+ }
+ }
+
+ // add attachments to response object
+ if (count($attachmentsRecieved) > 0) {
+ $response->setAttachments($attachmentsRecieved);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapClient.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapClient.php
new file mode 100644
index 0000000..d0d99e8
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapClient.php
@@ -0,0 +1,333 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\Helper;
+use BeSimple\SoapCommon\Converter\MtomTypeConverter;
+use BeSimple\SoapCommon\Converter\SwaTypeConverter;
+
+/**
+ * Extended SoapClient that uses a a cURL wrapper for all underlying HTTP
+ * requests in order to use proper authentication for all requests. This also
+ * adds NTLM support. A custom WSDL downloader resolves remote xsd:includes and
+ * allows caching of all remote referenced items.
+ *
+ * @author Andreas Schamberger
+ */
+class SoapClient extends \SoapClient
+{
+ /**
+ * Soap version.
+ *
+ * @var int
+ */
+ protected $soapVersion = SOAP_1_1;
+
+ /**
+ * Tracing enabled?
+ *
+ * @var boolean
+ */
+ protected $tracingEnabled = false;
+
+ /**
+ * cURL instance.
+ *
+ * @var \BeSimple\SoapClient\Curl
+ */
+ protected $curl = null;
+
+ /**
+ * Last request headers.
+ *
+ * @var string
+ */
+ private $lastRequestHeaders = '';
+
+ /**
+ * Last request.
+ *
+ * @var string
+ */
+ private $lastRequest = '';
+
+ /**
+ * Last response headers.
+ *
+ * @var string
+ */
+ private $lastResponseHeaders = '';
+
+ /**
+ * Last response.
+ *
+ * @var string
+ */
+ private $lastResponse = '';
+
+ /**
+ * Soap kernel.
+ *
+ * @var \BeSimple\SoapClient\SoapKernel
+ */
+ protected $soapKernel = null;
+
+ /**
+ * Constructor.
+ *
+ * @param string $wsdl WSDL file
+ * @param array(string=>mixed) $options Options array
+ */
+ public function __construct($wsdl, array $options = array())
+ {
+ // tracing enabled: store last request/response header and body
+ if (isset($options['trace']) && $options['trace'] === true) {
+ $this->tracingEnabled = true;
+ }
+ // store SOAP version
+ if (isset($options['soap_version'])) {
+ $this->soapVersion = $options['soap_version'];
+ }
+ $this->curl = new Curl($options);
+ $wsdlFile = $this->loadWsdl($wsdl, $options);
+ // TODO $wsdlHandler = new WsdlHandler($wsdlFile, $this->soapVersion);
+ $this->soapKernel = new SoapKernel();
+ // set up type converter and mime filter
+ $this->configureMime($options);
+ // we want the exceptions option to be set
+ $options['exceptions'] = true;
+ // disable obsolete trace option for native SoapClient as we need to do our own tracing anyways
+ $options['trace'] = false;
+ // disable WSDL caching as we handle WSDL caching for remote URLs ourself
+ $options['cache_wsdl'] = WSDL_CACHE_NONE;
+ parent::__construct($wsdlFile, $options);
+ }
+
+
+ /**
+ * Perform HTTP request with cURL.
+ *
+ * @param SoapRequest $soapRequest SoapRequest object
+ *
+ * @return SoapResponse
+ */
+ private function __doHttpRequest(SoapRequest $soapRequest)
+ {
+ // HTTP headers
+ $headers = array(
+ 'Content-Type:' . $soapRequest->getContentType(),
+ 'SOAPAction: "' . $soapRequest->getAction() . '"',
+ );
+ // execute HTTP request with cURL
+ $responseSuccessfull = $this->curl->exec(
+ $soapRequest->getLocation(),
+ $soapRequest->getContent(),
+ $headers
+ );
+ // tracing enabled: store last request header and body
+ if ($this->tracingEnabled === true) {
+ $this->lastRequestHeaders = $this->curl->getRequestHeaders();
+ $this->lastRequest = $soapRequest->getContent();
+ }
+ // in case of an error while making the http request throw a soapFault
+ if ($responseSuccessfull === false) {
+ // get error message from curl
+ $faultstring = $this->curl->getErrorMessage();
+ throw new \SoapFault('HTTP', $faultstring);
+ }
+ // tracing enabled: store last response header and body
+ if ($this->tracingEnabled === true) {
+ $this->lastResponseHeaders = $this->curl->getResponseHeaders();
+ $this->lastResponse = $this->curl->getResponseBody();
+ }
+ // wrap response data in SoapResponse object
+ $soapResponse = SoapResponse::create(
+ $this->curl->getResponseBody(),
+ $soapRequest->getLocation(),
+ $soapRequest->getAction(),
+ $soapRequest->getVersion(),
+ $this->curl->getResponseContentType()
+ );
+
+ return $soapResponse;
+ }
+
+ /**
+ * Custom request method to be able to modify the SOAP messages.
+ * $oneWay parameter is not used at the moment.
+ *
+ * @param string $request Request string
+ * @param string $location Location
+ * @param string $action SOAP action
+ * @param int $version SOAP version
+ * @param int $oneWay 0|1
+ *
+ * @return string
+ */
+ public function __doRequest($request, $location, $action, $version, $oneWay = 0)
+ {
+ // wrap request data in SoapRequest object
+ $soapRequest = SoapRequest::create($request, $location, $action, $version);
+
+ // do actual SOAP request
+ $soapResponse = $this->__doRequest2($soapRequest);
+
+ // return SOAP response to ext/soap
+ return $soapResponse->getContent();
+ }
+
+ /**
+ * Runs the currently registered request filters on the request, performs
+ * the HTTP request and runs the response filters.
+ *
+ * @param SoapRequest $soapRequest SOAP request object
+ *
+ * @return SoapResponse
+ */
+ protected function __doRequest2(SoapRequest $soapRequest)
+ {
+ // run SoapKernel on SoapRequest
+ $this->soapKernel->filterRequest($soapRequest);
+
+ // perform HTTP request with cURL
+ $soapResponse = $this->__doHttpRequest($soapRequest);
+
+ // run SoapKernel on SoapResponse
+ $this->soapKernel->filterResponse($soapResponse);
+
+ return $soapResponse;
+ }
+
+ /**
+ * Get last request HTTP headers.
+ *
+ * @return string
+ */
+ public function __getLastRequestHeaders()
+ {
+ return $this->lastRequestHeaders;
+ }
+
+ /**
+ * Get last request HTTP body.
+ *
+ * @return string
+ */
+ public function __getLastRequest()
+ {
+ return $this->lastRequest;
+ }
+
+ /**
+ * Get last response HTTP headers.
+ *
+ * @return string
+ */
+ public function __getLastResponseHeaders()
+ {
+ return $this->lastResponseHeaders;
+ }
+
+ /**
+ * Get last response HTTP body.
+ *
+ * @return string
+ */
+ public function __getLastResponse()
+ {
+ return $this->lastResponse;
+ }
+
+ /**
+ * Get SoapKernel instance.
+ *
+ * @return \BeSimple\SoapClient\SoapKernel
+ */
+ public function getSoapKernel()
+ {
+ return $this->soapKernel;
+ }
+
+ /**
+ * Configure filter and type converter for SwA/MTOM.
+ *
+ * @param array &$options SOAP constructor options array.
+ *
+ * @return void
+ */
+ private function configureMime(array &$options)
+ {
+ if (isset($options['attachment_type']) && Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) {
+ // register mime filter in SoapKernel
+ $mimeFilter = new MimeFilter($options['attachment_type']);
+ $this->soapKernel->registerFilter($mimeFilter);
+ // configure type converter
+ if (Helper::ATTACHMENTS_TYPE_SWA === $options['attachment_type']) {
+ $converter = new SwaTypeConverter();
+ $converter->setKernel($this->soapKernel);
+ } elseif (Helper::ATTACHMENTS_TYPE_MTOM === $options['attachment_type']) {
+ $xmlMimeFilter = new XmlMimeFilter($options['attachment_type']);
+ $this->soapKernel->registerFilter($xmlMimeFilter);
+ $converter = new MtomTypeConverter();
+ $converter->setKernel($this->soapKernel);
+ }
+ // configure typemap
+ if (!isset($options['typemap'])) {
+ $options['typemap'] = array();
+ }
+ $options['typemap'][] = array(
+ 'type_name' => $converter->getTypeName(),
+ 'type_ns' => $converter->getTypeNamespace(),
+ 'from_xml' => function($input) use ($converter) {
+ return $converter->convertXmlToPhp($input);
+ },
+ 'to_xml' => function($input) use ($converter) {
+ return $converter->convertPhpToXml($input);
+ },
+ );
+ }
+ }
+
+ /**
+ * Downloads WSDL files with cURL. Uses all SoapClient options for
+ * authentication. Uses the WSDL_CACHE_* constants and the 'soap.wsdl_*'
+ * ini settings. Does only file caching as SoapClient only supports a file
+ * name parameter.
+ *
+ * @param string $wsdl WSDL file
+ * @param array(string=>mixed) $options Options array
+ *
+ * @return string
+ */
+ private function loadWsdl($wsdl, array $options)
+ {
+ // option to resolve wsdl/xsd includes
+ $resolveRemoteIncludes = true;
+ if (isset($options['resolve_wsdl_remote_includes'])) {
+ $resolveRemoteIncludes = $options['resolve_wsdl_remote_includes'];
+ }
+ // option to enable cache
+ $wsdlCache = WSDL_CACHE_DISK;
+ if (isset($options['cache_wsdl'])) {
+ $wsdlCache = $options['cache_wsdl'];
+ }
+ $wsdlDownloader = new WsdlDownloader($this->curl, $resolveRemoteIncludes, $wsdlCache);
+ try {
+ $cacheFileName = $wsdlDownloader->download($wsdl);
+ } catch (\RuntimeException $e) {
+ throw new \SoapFault('WSDL', "SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl . "' : failed to load external entity \"" . $wsdl . "\"");
+ }
+
+ return $cacheFileName;
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapClientBuilder.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapClientBuilder.php
new file mode 100644
index 0000000..cce4a9a
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapClientBuilder.php
@@ -0,0 +1,239 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\AbstractSoapBuilder;
+use BeSimple\SoapCommon\Helper;
+
+/**
+ * Fluent interface builder for SoapClient instance.
+ *
+ * @author Francis Besset
+ * @author Christian Kerl
+ */
+class SoapClientBuilder extends AbstractSoapBuilder
+{
+ /**
+ * Authentication options.
+ *
+ * @var array(string=>mixed)
+ */
+ protected $soapOptionAuthentication = array();
+
+ /**
+ * Create new instance with default options.
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public static function createWithDefaults()
+ {
+ return parent::createWithDefaults()
+ ->withUserAgent('BeSimpleSoap');
+ }
+
+ /**
+ * Finally returns a SoapClient instance.
+ *
+ * @return \BeSimple\SoapClient\SoapClient
+ */
+ public function build()
+ {
+ $this->validateOptions();
+
+ return new SoapClient($this->wsdl, $this->getSoapOptions());
+ }
+
+ /**
+ * Get final array of SOAP options.
+ *
+ * @return array(string=>mixed)
+ */
+ public function getSoapOptions()
+ {
+ return parent::getSoapOptions() + $this->soapOptionAuthentication;
+ }
+
+ /**
+ * Configure option 'trace'.
+ *
+ * @param boolean $trace Enable/Disable
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withTrace($trace = true)
+ {
+ $this->soapOptions['trace'] = $trace;
+
+ return $this;
+ }
+
+ /**
+ * Configure option 'exceptions'.
+ *
+ * @param boolean $exceptions Enable/Disable
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withExceptions($exceptions = true)
+ {
+ $this->soapOptions['exceptions'] = $exceptions;
+
+ return $this;
+ }
+
+ /**
+ * Configure option 'user_agent'.
+ *
+ * @param string $userAgent User agent string
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withUserAgent($userAgent)
+ {
+ $this->soapOptions['user_agent'] = $userAgent;
+
+ return $this;
+ }
+
+ /**
+ * Enable gzip compression.
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withCompressionGzip()
+ {
+ $this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
+
+ return $this;
+ }
+
+ /**
+ * Enable deflate compression.
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withCompressionDeflate()
+ {
+ $this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE;
+
+ return $this;
+ }
+
+ /**
+ * Configure basic authentication
+ *
+ * @param string $username Username
+ * @param string $password Password
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withBasicAuthentication($username, $password)
+ {
+ $this->soapOptionAuthentication = array(
+ 'authentication' => SOAP_AUTHENTICATION_BASIC,
+ 'login' => $username,
+ 'password' => $password,
+ );
+
+ return $this;
+ }
+
+ /**
+ * Configure digest authentication.
+ *
+ * @param string $certificate Certificate
+ * @param string $passphrase Passphrase
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withDigestAuthentication($certificate, $passphrase = null)
+ {
+ $this->soapOptionAuthentication = array(
+ 'authentication' => SOAP_AUTHENTICATION_DIGEST,
+ 'local_cert' => $certificate,
+ );
+
+ if ($passphrase) {
+ $this->soapOptionAuthentication['passphrase'] = $passphrase;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Configure proxy.
+ *
+ * @param string $host Host
+ * @param int $port Port
+ * @param string $username Username
+ * @param string $password Password
+ *
+ * @return \BeSimple\SoapClient\SoapClientBuilder
+ */
+ public function withProxy($host, $port, $username = null, $password = null)
+ {
+ $this->soapOptions['proxy_host'] = $host;
+ $this->soapOptions['proxy_port'] = $port;
+
+ if ($username) {
+ $this->soapOptions['proxy_login'] = $username;
+ $this->soapOptions['proxy_password'] = $password;
+ }
+
+ return $this;
+ }
+
+ /**
+ * SOAP attachment type Base64.
+ *
+ * @return \BeSimple\SoapServer\SoapServerBuilder
+ */
+ public function withBase64Attachments()
+ {
+ $this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_BASE64;
+
+ return $this;
+ }
+
+ /**
+ * SOAP attachment type SwA.
+ *
+ * @return \BeSimple\SoapServer\SoapServerBuilder
+ */
+ public function withSwaAttachments()
+ {
+ $this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_SWA;
+
+ return $this;
+ }
+
+ /**
+ * SOAP attachment type MTOM.
+ *
+ * @return \BeSimple\SoapServer\SoapServerBuilder
+ */
+ public function withMtomAttachments()
+ {
+ $this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_MTOM;
+
+ return $this;
+ }
+
+ /**
+ * Validate options.
+ */
+ protected function validateOptions()
+ {
+ $this->validateWsdl();
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapKernel.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapKernel.php
new file mode 100644
index 0000000..949798e
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapKernel.php
@@ -0,0 +1,47 @@
+
+ * (c) Francis Besset
+ * (c) Andreas Schamberger
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\SoapKernel as CommonSoapKernel;
+use BeSimple\SoapCommon\SoapRequest;
+use BeSimple\SoapCommon\SoapResponse;
+
+/**
+ * SoapKernel for Client.
+ *
+ * @author Andreas Schamberger
+ */
+class SoapKernel extends CommonSoapKernel
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function filterRequest(SoapRequest $request)
+ {
+ $request->setAttachments($this->attachments);
+ $this->attachments = array();
+
+ parent::filterRequest($request);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function filterResponse(SoapResponse $response)
+ {
+ parent::filterResponse($response);
+
+ $this->attachments = $response->getAttachments();
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapRequest.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapRequest.php
new file mode 100644
index 0000000..00580cb
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapRequest.php
@@ -0,0 +1,48 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
+use BeSimple\SoapCommon\SoapMessage;
+
+/**
+ * SoapRequest class for SoapClient. Provides factory function for request object.
+ *
+ * @author Andreas Schamberger
+ */
+class SoapRequest extends CommonSoapRequest
+{
+ /**
+ * Factory function for SoapRequest.
+ *
+ * @param string $content Content
+ * @param string $location Location
+ * @param string $action SOAP action
+ * @param string $version SOAP version
+ *
+ * @return BeSimple\SoapClient\SoapRequest
+ */
+ public static function create($content, $location, $action, $version)
+ {
+ $request = new SoapRequest();
+ // $content is if unmodified from SoapClient not a php string type!
+ $request->setContent((string) $content);
+ $request->setLocation($location);
+ $request->setAction($action);
+ $request->setVersion($version);
+ $contentType = SoapMessage::getContentTypeForVersion($version);
+ $request->setContentType($contentType);
+
+ return $request;
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapResponse.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapResponse.php
new file mode 100644
index 0000000..24a12c3
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/SoapResponse.php
@@ -0,0 +1,47 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
+use BeSimple\SoapCommon\SoapMessage;
+
+/**
+ * SoapResponse class for SoapClient. Provides factory function for response object.
+ *
+ * @author Andreas Schamberger
+ */
+class SoapResponse extends CommonSoapResponse
+{
+ /**
+ * Factory function for SoapResponse.
+ *
+ * @param string $content Content
+ * @param string $location Location
+ * @param string $action SOAP action
+ * @param string $version SOAP version
+ * @param string $contentType Content type header
+ *
+ * @return BeSimple\SoapClient\SoapResponse
+ */
+ public static function create($content, $location, $action, $version, $contentType)
+ {
+ $response = new SoapResponse();
+ $response->setContent($content);
+ $response->setLocation($location);
+ $response->setAction($action);
+ $response->setVersion($version);
+ $response->setContentType($contentType);
+
+ return $response;
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsAddressingFilter.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsAddressingFilter.php
new file mode 100644
index 0000000..721fe87
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsAddressingFilter.php
@@ -0,0 +1,347 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\FilterHelper;
+use BeSimple\SoapCommon\Helper;
+use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
+use BeSimple\SoapCommon\SoapRequestFilter;
+use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
+use BeSimple\SoapCommon\SoapResponseFilter;
+
+/**
+ * This plugin implements a subset of the following standards:
+ * * Web Services Addressing 1.0 - Core
+ * http://www.w3.org/TR/2006/REC-ws-addr-core
+ * * Web Services Addressing 1.0 - SOAP Binding
+ * http://www.w3.org/TR/ws-addr-soap
+ *
+ * Per default this plugin uses the SoapClient's $action and $location values
+ * for wsa:Action and wsa:To. Therefore the only REQUIRED property 'wsa:Action'
+ * is always set automatically.
+ *
+ * Limitation: wsa:From, wsa:FaultTo and wsa:ReplyTo only support the
+ * wsa:Address element of the endpoint reference at the moment.
+ *
+ * @author Andreas Schamberger
+ */
+class WsAddressingFilter implements SoapRequestFilter, SoapResponseFilter
+{
+ /**
+ * (2.1) Endpoint reference (EPR) anonymous default address.
+ *
+ * Some endpoints cannot be located with a meaningful IRI; this URI is used
+ * to allow such endpoints to send and receive messages. The precise meaning
+ * of this URI is defined by the binding of Addressing to a specific
+ * protocol and/or the context in which the EPR is used.
+ *
+ * @see http://www.w3.org/TR/2006/REC-ws-addr-core-20060509/#predefaddr
+ */
+ const ENDPOINT_REFERENCE_ANONYMOUS = 'http://www.w3.org/2005/08/addressing/anonymous';
+
+ /**
+ * (2.1) Endpoint reference (EPR) address for discarting messages.
+ *
+ * Messages sent to EPRs whose [address] is this value MUST be discarded
+ * (i.e. not sent). This URI is typically used in EPRs that designate a
+ * reply or fault endpoint (see section 3.1 Abstract Property Definitions)
+ * to indicate that no reply or fault message should be sent.
+ *
+ * @see http://www.w3.org/TR/2006/REC-ws-addr-core-20060509/#predefaddr
+ */
+ const ENDPOINT_REFERENCE_NONE = 'http://www.w3.org/2005/08/addressing/none';
+
+ /**
+ * (3.1) Predefined value for reply.
+ *
+ * Indicates that this is a reply to the message identified by the [message id] IRI.
+ *
+ * see http://www.w3.org/TR/2006/REC-ws-addr-core-20060509/#predefrels
+ */
+ const RELATIONSHIP_TYPE_REPLY = 'http://www.w3.org/2005/08/addressing/reply';
+
+ /**
+ * FaultTo.
+ *
+ * @var string
+ */
+ protected $faultTo;
+
+ /**
+ * From.
+ *
+ * @var string
+ */
+ protected $from;
+
+ /**
+ * MessageId.
+ *
+ * @var string
+ */
+ protected $messageId;
+
+ /**
+ * List of reference parameters associated with this soap message.
+ *
+ * @var unknown_type
+ */
+ protected $referenceParametersSet = array();
+
+ /**
+ * List of reference parameters recieved with this soap message.
+ *
+ * @var unknown_type
+ */
+ protected $referenceParametersRecieved = array();
+
+ /**
+ * RelatesTo.
+ *
+ * @var string
+ */
+ protected $relatesTo;
+
+ /**
+ * RelatesTo@RelationshipType.
+ *
+ * @var string
+ */
+ protected $relatesToRelationshipType;
+
+ /**
+ * ReplyTo.
+ *
+ * @var string
+ */
+ protected $replyTo;
+
+ /**
+ * Add additional reference parameters
+ *
+ * @param string $ns Namespace URI
+ * @param string $pfx Namespace prefix
+ * @param string $parameter Parameter name
+ * @param string $value Parameter value
+ *
+ * @return void
+ */
+ public function addReferenceParameter($ns, $pfx, $parameter, $value)
+ {
+ $this->referenceParametersSet[] = array(
+ 'ns' => $ns,
+ 'pfx' => $pfx,
+ 'parameter' => $parameter,
+ 'value' => $value,
+ );
+ }
+
+ /**
+ * Get additional reference parameters.
+ *
+ * @param string $ns Namespace URI
+ * @param string $parameter Parameter name
+ *
+ * @return string|null
+ */
+ public function getReferenceParameter($ns, $parameter)
+ {
+ if (isset($this->referenceParametersRecieved[$ns][$parameter])) {
+
+ return $this->referenceParametersRecieved[$ns][$parameter];
+ }
+
+ return null;
+ }
+
+ /**
+ * Reset all properties to default values.
+ */
+ public function resetFilter()
+ {
+ $this->faultTo = null;
+ $this->from = null;
+ $this->messageId = null;
+ $this->referenceParametersRecieved = array();
+ $this->referenceParametersSet = array();
+ $this->relatesTo = null;
+ $this->relatesToRelationshipType = null;
+ $this->replyTo = null;
+ }
+
+ /**
+ * Set FaultTo address of type xs:anyURI.
+ *
+ * @param string $faultTo xs:anyURI
+ *
+ * @return void
+ */
+ public function setFaultTo($faultTo)
+ {
+ $this->faultTo = $faultTo;
+ }
+
+ /**
+ * Set From address of type xs:anyURI.
+ *
+ * @param string $from xs:anyURI
+ *
+ * @return void
+ */
+ public function setFrom($from)
+ {
+ $this->from = $from;
+ }
+
+ /**
+ * Set MessageId of type xs:anyURI.
+ * Default: UUID v4 e.g. 'uuid:550e8400-e29b-11d4-a716-446655440000'
+ *
+ * @param string $messageId xs:anyURI
+ *
+ * @return void
+ */
+ public function setMessageId($messageId = null)
+ {
+ if (null === $messageId) {
+ $messageId = 'uuid:' . Helper::generateUUID();
+ }
+ $this->messageId = $messageId;
+ }
+
+ /**
+ * Set RelatesTo of type xs:anyURI with the optional relationType
+ * (of type xs:anyURI).
+ *
+ * @param string $relatesTo xs:anyURI
+ * @param string $relationType xs:anyURI
+ *
+ * @return void
+ */
+ public function setRelatesTo($relatesTo, $relationType = null)
+ {
+ $this->relatesTo = $relatesTo;
+ if (null !== $relationType && $relationType != self::RELATIONSHIP_TYPE_REPLY) {
+ $this->relatesToRelationshipType = $relationType;
+ }
+ }
+
+ /**
+ * Set ReplyTo address of type xs:anyURI
+ * Default: self::ENDPOINT_REFERENCE_ANONYMOUS
+ *
+ * @param string $replyTo xs:anyURI
+ *
+ * @return void
+ */
+ public function setReplyTo($replyTo = null)
+ {
+ if (null === $replyTo) {
+ $replyTo = self::ENDPOINT_REFERENCE_ANONYMOUS;
+ }
+ $this->replyTo = $replyTo;
+ }
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
+ *
+ * @return void
+ */
+ public function filterRequest(CommonSoapRequest $request)
+ {
+ // get \DOMDocument from SOAP request
+ $dom = $request->getContentDocument();
+
+ // create FilterHelper
+ $filterHelper = new FilterHelper($dom);
+
+ // add the neccessary namespaces
+ $filterHelper->addNamespace(Helper::PFX_WSA, Helper::NS_WSA);
+
+ $action = $filterHelper->createElement(Helper::NS_WSA, 'Action', $request->getAction());
+ $filterHelper->addHeaderElement($action);
+
+ $to = $filterHelper->createElement(Helper::NS_WSA, 'To', $request->getLocation());
+ $filterHelper->addHeaderElement($to);
+
+ if (null !== $this->faultTo) {
+ $faultTo = $filterHelper->createElement(Helper::NS_WSA, 'FaultTo');
+ $filterHelper->addHeaderElement($faultTo);
+
+ $address = $filterHelper->createElement(Helper::NS_WSA, 'Address', $this->faultTo);
+ $faultTo->appendChild($address);
+ }
+
+ if (null !== $this->from) {
+ $from = $filterHelper->createElement(Helper::NS_WSA, 'From');
+ $filterHelper->addHeaderElement($from);
+
+ $address = $filterHelper->createElement(Helper::NS_WSA, 'Address', $this->from);
+ $from->appendChild($address);
+ }
+
+ if (null !== $this->messageId) {
+ $messageId = $filterHelper->createElement(Helper::NS_WSA, 'MessageID', $this->messageId);
+ $filterHelper->addHeaderElement($messageId);
+ }
+
+ if (null !== $this->relatesTo) {
+ $relatesTo = $filterHelper->createElement(Helper::NS_WSA, 'RelatesTo', $this->relatesTo);
+ if (null !== $this->relatesToRelationshipType) {
+ $filterHelper->setAttribute($relatesTo, Helper::NS_WSA, 'RelationshipType', $this->relatesToRelationshipType);
+ }
+ $filterHelper->addHeaderElement($relatesTo);
+ }
+
+ if (null !== $this->replyTo) {
+ $replyTo = $filterHelper->createElement(Helper::NS_WSA, 'ReplyTo');
+ $filterHelper->addHeaderElement($replyTo);
+
+ $address = $filterHelper->createElement(Helper::NS_WSA, 'Address', $this->replyTo);
+ $replyTo->appendChild($address);
+ }
+
+ foreach ($this->referenceParametersSet as $rp) {
+ $filterHelper->addNamespace($rp['pfx'], $rp['ns']);
+ $parameter = $filterHelper->createElement($rp['ns'], $rp['parameter'], $rp['value']);
+ $filterHelper->setAttribute($parameter, Helper::NS_WSA, 'IsReferenceParameter', 'true');
+ $filterHelper->addHeaderElement($parameter);
+ }
+ }
+
+ /**
+ * Modify the given response XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
+ *
+ * @return void
+ */
+ public function filterResponse(CommonSoapResponse $response)
+ {
+ // get \DOMDocument from SOAP response
+ $dom = $response->getContentDocument();
+
+ $this->referenceParametersRecieved = array();
+ $referenceParameters = $dom->getElementsByTagNameNS(Helper::NS_WSA, 'ReferenceParameters')->item(0);
+ if (null !== $referenceParameters) {
+ foreach ($referenceParameters->childNodes as $childNode) {
+ if (!isset($this->referenceParametersRecieved[$childNode->namespaceURI])) {
+ $this->referenceParametersRecieved[$childNode->namespaceURI] = array();
+ }
+ $this->referenceParametersRecieved[$childNode->namespaceURI][$childNode->localName] = $childNode->nodeValue;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsSecurityFilter.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsSecurityFilter.php
new file mode 100644
index 0000000..cb3fc7c
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsSecurityFilter.php
@@ -0,0 +1,581 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use ass\XmlSecurity\DSig as XmlSecurityDSig;
+use ass\XmlSecurity\Enc as XmlSecurityEnc;
+use ass\XmlSecurity\Key as XmlSecurityKey;
+
+use BeSimple\SoapCommon\FilterHelper;
+use BeSimple\SoapCommon\Helper;
+use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
+use BeSimple\SoapCommon\SoapRequestFilter;
+use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
+use BeSimple\SoapCommon\SoapResponseFilter;
+use BeSimple\SoapCommon\WsSecurityKey;
+
+/**
+ * This plugin implements a subset of the following standards:
+ * * Web Services Security: SOAP Message Security 1.0 (WS-Security 2004)
+ * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf
+ * * Web Services Security UsernameToken Profile 1.0
+ * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf
+ * * Web Services Security X.509 Certificate Token Profile
+ * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0.pdf
+ *
+ * @author Andreas Schamberger
+ */
+class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter
+{
+ /*
+ * The date format to be used with {@link \DateTime}
+ */
+ const DATETIME_FORMAT = 'Y-m-d\TH:i:s.000\Z';
+
+ /**
+ * (UT 3.1) Password type: plain text.
+ */
+ const PASSWORD_TYPE_TEXT = 0;
+
+ /**
+ * (UT 3.1) Password type: digest.
+ */
+ const PASSWORD_TYPE_DIGEST = 1;
+
+ /**
+ * (X509 3.2.1) Reference to a Subject Key Identifier
+ */
+ const TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER = 0;
+
+ /**
+ * (X509 3.2.1) Reference to a Security Token
+ */
+ const TOKEN_REFERENCE_SECURITY_TOKEN = 1;
+
+ /**
+ * (SMS_1.1 7.3) Key Identifiers
+ */
+ const TOKEN_REFERENCE_THUMBPRINT_SHA1 = 2;
+
+ /**
+ * Actor.
+ *
+ * @var string
+ */
+ protected $actor;
+
+ /**
+ * (SMS 10) Add security timestamp.
+ *
+ * @var boolean
+ */
+ protected $addTimestamp;
+
+ /**
+ * Encrypt the signature?
+ *
+ * @var boolean
+ */
+ protected $encryptSignature;
+
+ /**
+ * (SMS 10) Security timestamp expires time in seconds.
+ *
+ * @var int
+ */
+ protected $expires;
+
+ /**
+ * (UT 3.1) Password.
+ *
+ * @var string
+ */
+ protected $password;
+
+ /**
+ * (UT 3.1) Password type: text or digest.
+ *
+ * @var int
+ */
+ protected $passwordType;
+
+ /**
+ * Sign all headers.
+ *
+ * @var boolean
+ */
+ protected $signAllHeaders;
+
+ /**
+ * (X509 3.2) Token reference type for encryption.
+ *
+ * @var int
+ */
+ protected $tokenReferenceEncryption = null;
+
+ /**
+ * (X509 3.2) Token reference type for signature.
+ *
+ * @var int
+ */
+ protected $tokenReferenceSignature = null;
+
+ /**
+ * Service WsSecurityKey.
+ *
+ * @var \BeSimple\SoapCommon\WsSecurityKey
+ */
+ protected $serviceSecurityKey;
+
+ /**
+ * (UT 3.1) Username.
+ *
+ * @var string
+ */
+ protected $username;
+
+ /**
+ * User WsSecurityKey.
+ *
+ * @var \BeSimple\SoapCommon\WsSecurityKey
+ */
+ protected $userSecurityKey;
+
+ /**
+ * Constructor.
+ *
+ * @param boolean $addTimestamp (SMS 10) Add security timestamp.
+ * @param int $expires (SMS 10) Security timestamp expires time in seconds.
+ * @param string $actor SOAP actor
+ */
+ public function __construct($addTimestamp = true, $expires = 300, $actor = null)
+ {
+ $this->addTimestamp = $addTimestamp;
+ $this->expires = $expires;
+ $this->actor = $actor;
+ }
+
+ /**
+ * Add user data.
+ *
+ * @param string $username Username
+ * @param string $password Password
+ * @param int $passwordType self::PASSWORD_TYPE_DIGEST | self::PASSWORD_TYPE_TEXT
+ *
+ * @return void
+ */
+ public function addUserData($username, $password = null, $passwordType = self::PASSWORD_TYPE_DIGEST)
+ {
+ $this->username = $username;
+ $this->password = $password;
+ $this->passwordType = $passwordType;
+ }
+
+ /**
+ * Reset all properties to default values.
+ */
+ public function resetFilter()
+ {
+ $this->actor = null;
+ $this->addTimestamp = null;
+ $this->encryptSignature = null;
+ $this->expires = null;
+ $this->password = null;
+ $this->passwordType = null;
+ $this->serviceSecurityKey = null;
+ $this->signAllHeaders = null;
+ $this->tokenReferenceEncryption = null;
+ $this->tokenReferenceSignature = null;
+ $this->username = null;
+ $this->userSecurityKey = null;
+ }
+
+ /**
+ * Get service security key.
+ *
+ * @param \BeSimple\SoapCommon\WsSecurityKey $serviceSecurityKey Service security key
+ *
+ * @return void
+ */
+ public function setServiceSecurityKeyObject(WsSecurityKey $serviceSecurityKey)
+ {
+ $this->serviceSecurityKey = $serviceSecurityKey;
+ }
+
+ /**
+ * Get user security key.
+ *
+ * @param \BeSimple\SoapCommon\WsSecurityKey $userSecurityKey User security key
+ *
+ * @return void
+ */
+ public function setUserSecurityKeyObject(WsSecurityKey $userSecurityKey)
+ {
+ $this->userSecurityKey = $userSecurityKey;
+ }
+
+ /**
+ * Set security options.
+ *
+ * @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1
+ * @param boolean $encryptSignature Encrypt signature
+ *
+ * @return void
+ */
+ public function setSecurityOptionsEncryption($tokenReference, $encryptSignature = false)
+ {
+ $this->tokenReferenceEncryption = $tokenReference;
+ $this->encryptSignature = $encryptSignature;
+ }
+
+ /**
+ * Set security options.
+ *
+ * @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1
+ * @param boolean $signAllHeaders Sign all headers?
+ *
+ * @return void
+ */
+ public function setSecurityOptionsSignature($tokenReference, $signAllHeaders = false)
+ {
+ $this->tokenReferenceSignature = $tokenReference;
+ $this->signAllHeaders = $signAllHeaders;
+ }
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
+ *
+ * @return void
+ */
+ public function filterRequest(CommonSoapRequest $request)
+ {
+ // get \DOMDocument from SOAP request
+ $dom = $request->getContentDocument();
+
+ // create FilterHelper
+ $filterHelper = new FilterHelper($dom);
+
+ // add the neccessary namespaces
+ $filterHelper->addNamespace(Helper::PFX_WSS, Helper::NS_WSS);
+ $filterHelper->addNamespace(Helper::PFX_WSU, Helper::NS_WSU);
+ $filterHelper->registerNamespace(XmlSecurityDSig::PFX_XMLDSIG, XmlSecurityDSig::NS_XMLDSIG);
+
+ // init timestamp
+ $dt = new \DateTime('now', new \DateTimeZone('UTC'));
+ $createdTimestamp = $dt->format(self::DATETIME_FORMAT);
+
+ // create security header
+ $security = $filterHelper->createElement(Helper::NS_WSS, 'Security');
+ $filterHelper->addHeaderElement($security, true, $this->actor, $request->getVersion());
+
+ if (true === $this->addTimestamp || null !== $this->expires) {
+ $timestamp = $filterHelper->createElement(Helper::NS_WSU, 'Timestamp');
+ $created = $filterHelper->createElement(Helper::NS_WSU, 'Created', $createdTimestamp);
+ $timestamp->appendChild($created);
+ if (null !== $this->expires) {
+ $dt->modify('+' . $this->expires . ' seconds');
+ $expiresTimestamp = $dt->format(self::DATETIME_FORMAT);
+ $expires = $filterHelper->createElement(Helper::NS_WSU, 'Expires', $expiresTimestamp);
+ $timestamp->appendChild($expires);
+ }
+ $security->appendChild($timestamp);
+ }
+
+ if (null !== $this->username) {
+ $usernameToken = $filterHelper->createElement(Helper::NS_WSS, 'UsernameToken');
+ $security->appendChild($usernameToken);
+
+ $username = $filterHelper->createElement(Helper::NS_WSS, 'Username', $this->username);
+ $usernameToken->appendChild($username);
+
+ if (null !== $this->password
+ && (null === $this->userSecurityKey
+ || (null !== $this->userSecurityKey && !$this->userSecurityKey->hasPrivateKey()))) {
+
+ if (self::PASSWORD_TYPE_DIGEST === $this->passwordType) {
+ $nonce = mt_rand();
+ $password = base64_encode(sha1($nonce . $createdTimestamp . $this->password, true));
+ $passwordType = Helper::NAME_WSS_UTP . '#PasswordDigest';
+ } else {
+ $password = $this->password;
+ $passwordType = Helper::NAME_WSS_UTP . '#PasswordText';
+ }
+ $password = $filterHelper->createElement(Helper::NS_WSS, 'Password', $password);
+ $filterHelper->setAttribute($password, null, 'Type', $passwordType);
+ $usernameToken->appendChild($password);
+ if (self::PASSWORD_TYPE_DIGEST === $this->passwordType) {
+ $nonce = $filterHelper->createElement(Helper::NS_WSS, 'Nonce', base64_encode($nonce));
+ $usernameToken->appendChild($nonce);
+
+ $created = $filterHelper->createElement(Helper::NS_WSU, 'Created', $createdTimestamp);
+ $usernameToken->appendChild($created);
+ }
+ }
+ }
+
+ if (null !== $this->userSecurityKey && $this->userSecurityKey->hasKeys()) {
+ $guid = 'CertId-' . Helper::generateUUID();
+ // add token references
+ $keyInfo = null;
+ if (null !== $this->tokenReferenceSignature) {
+ $keyInfo = $this->createKeyInfo($filterHelper, $this->tokenReferenceSignature, $guid, $this->userSecurityKey->getPublicKey());
+ }
+ $nodes = $this->createNodeListForSigning($dom, $security);
+ $signature = XmlSecurityDSig::createSignature($this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N, $security, null, $keyInfo);
+ $options = array(
+ 'id_ns_prefix' => Helper::PFX_WSU,
+ 'id_prefix_ns' => Helper::NS_WSU,
+ );
+ foreach ($nodes as $node) {
+ XmlSecurityDSig::addNodeToSignature($signature, $node, XmlSecurityDSig::SHA1, XmlSecurityDSig::EXC_C14N, $options);
+ }
+ XmlSecurityDSig::signDocument($signature, $this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N);
+
+ $publicCertificate = $this->userSecurityKey->getPublicKey()->getX509Certificate(true);
+ $binarySecurityToken = $filterHelper->createElement(Helper::NS_WSS, 'BinarySecurityToken', $publicCertificate);
+ $filterHelper->setAttribute($binarySecurityToken, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary');
+ $filterHelper->setAttribute($binarySecurityToken, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3');
+ $filterHelper->setAttribute($binarySecurityToken, Helper::NS_WSU, 'Id', $guid);
+ $security->insertBefore($binarySecurityToken, $signature);
+
+ // encrypt soap document
+ if (null !== $this->serviceSecurityKey && $this->serviceSecurityKey->hasKeys()) {
+ $guid = 'EncKey-' . Helper::generateUUID();
+ // add token references
+ $keyInfo = null;
+ if (null !== $this->tokenReferenceEncryption) {
+ $keyInfo = $this->createKeyInfo($filterHelper, $this->tokenReferenceEncryption, $guid, $this->serviceSecurityKey->getPublicKey());
+ }
+ $encryptedKey = XmlSecurityEnc::createEncryptedKey($guid, $this->serviceSecurityKey->getPrivateKey(), $this->serviceSecurityKey->getPublicKey(), $security, $signature, $keyInfo);
+ $referenceList = XmlSecurityEnc::createReferenceList($encryptedKey);
+ // token reference to encrypted key
+ $keyInfo = $this->createKeyInfo($filterHelper, self::TOKEN_REFERENCE_SECURITY_TOKEN, $guid);
+ $nodes = $this->createNodeListForEncryption($dom, $security);
+ foreach ($nodes as $node) {
+ $type = XmlSecurityEnc::ELEMENT;
+ if ($node->localName == 'Body') {
+ $type = XmlSecurityEnc::CONTENT;
+ }
+ XmlSecurityEnc::encryptNode($node, $type, $this->serviceSecurityKey->getPrivateKey(), $referenceList, $keyInfo);
+ }
+ }
+ }
+ }
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
+ *
+ * @return void
+ */
+ public function filterResponse(CommonSoapResponse $response)
+ {
+ // get \DOMDocument from SOAP response
+ $dom = $response->getContentDocument();
+
+ // locate security header
+ $security = $dom->getElementsByTagNameNS(Helper::NS_WSS, 'Security')->item(0);
+ if (null !== $security) {
+ // add SecurityTokenReference resolver for KeyInfo
+ if (null !== $this->serviceSecurityKey) {
+ $keyResolver = array($this, 'keyInfoSecurityTokenReferenceResolver');
+ XmlSecurityDSig::addKeyInfoResolver(Helper::NS_WSS, 'SecurityTokenReference', $keyResolver);
+ }
+ // do we have a reference list in header
+ $referenceList = XmlSecurityEnc::locateReferenceList($security);
+ // get a list of encrypted nodes
+ $encryptedNodes = XmlSecurityEnc::locateEncryptedData($dom, $referenceList);
+ // decrypt them
+ if (null !== $encryptedNodes) {
+ foreach ($encryptedNodes as $encryptedNode) {
+ XmlSecurityEnc::decryptNode($encryptedNode);
+ }
+ }
+ // locate signature node
+ $signature = XmlSecurityDSig::locateSignature($security);
+ if (null !== $signature) {
+ // verify references
+ $options = array(
+ 'id_ns_prefix' => Helper::PFX_WSU,
+ 'id_prefix_ns' => Helper::NS_WSU,
+ );
+ if (XmlSecurityDSig::verifyReferences($signature, $options) !== true) {
+ throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid');
+ }
+ // verify signature
+ if (XmlSecurityDSig::verifyDocumentSignature($signature) !== true) {
+ throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid');
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the configured KeyInfo to the parentNode.
+ *
+ * @param FilterHelper $filterHelper Filter helper object
+ * @param int $tokenReference Token reference type
+ * @param string $guid Unique ID
+ * @param \ass\XmlSecurity\Key $xmlSecurityKey XML security key
+ *
+ * @return \DOMElement
+ */
+ protected function createKeyInfo(FilterHelper $filterHelper, $tokenReference, $guid, XmlSecurityKey $xmlSecurityKey = null)
+ {
+ $keyInfo = $filterHelper->createElement(XmlSecurityDSig::NS_XMLDSIG, 'KeyInfo');
+ $securityTokenReference = $filterHelper->createElement(Helper::NS_WSS, 'SecurityTokenReference');
+ $keyInfo->appendChild($securityTokenReference);
+ // security token
+ if (self::TOKEN_REFERENCE_SECURITY_TOKEN === $tokenReference) {
+ $reference = $filterHelper->createElement(Helper::NS_WSS, 'Reference');
+ $filterHelper->setAttribute($reference, null, 'URI', '#' . $guid);
+ if (null !== $xmlSecurityKey) {
+ $filterHelper->setAttribute($reference, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3');
+ }
+ $securityTokenReference->appendChild($reference);
+ // subject key identifier
+ } elseif (self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER === $tokenReference && null !== $xmlSecurityKey) {
+ $keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier');
+ $filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary');
+ $filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_X509 . '#509SubjectKeyIdentifier');
+ $securityTokenReference->appendChild($keyIdentifier);
+ $certificate = $xmlSecurityKey->getX509SubjectKeyIdentifier();
+ $dataNode = new \DOMText($certificate);
+ $keyIdentifier->appendChild($dataNode);
+ // thumbprint sha1
+ } elseif (self::TOKEN_REFERENCE_THUMBPRINT_SHA1 === $tokenReference && null !== $xmlSecurityKey) {
+ $keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier');
+ $filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary');
+ $filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_SMS_1_1 . '#ThumbprintSHA1');
+ $securityTokenReference->appendChild($keyIdentifier);
+ $thumbprintSha1 = base64_encode(sha1(base64_decode($xmlSecurityKey->getX509Certificate(true)), true));
+ $dataNode = new \DOMText($thumbprintSha1);
+ $keyIdentifier->appendChild($dataNode);
+ }
+
+ return $keyInfo;
+ }
+
+ /**
+ * Create a list of \DOMNodes that should be encrypted.
+ *
+ * @param \DOMDocument $dom DOMDocument to query
+ * @param \DOMElement $security Security element
+ *
+ * @return \DOMNodeList
+ */
+ protected function createNodeListForEncryption(\DOMDocument $dom, \DOMElement $security)
+ {
+ $xpath = new \DOMXPath($dom);
+ $xpath->registerNamespace('SOAP-ENV', $dom->documentElement->namespaceURI);
+ $xpath->registerNamespace('ds', XmlSecurityDSig::NS_XMLDSIG);
+ if ($this->encryptSignature === true) {
+ $query = '//ds:Signature | //SOAP-ENV:Body';
+ } else {
+ $query = '//SOAP-ENV:Body';
+ }
+
+ return $xpath->query($query);
+ }
+
+ /**
+ * Create a list of \DOMNodes that should be signed.
+ *
+ * @param \DOMDocument $dom DOMDocument to query
+ * @param \DOMElement $security Security element
+ *
+ * @return array(\DOMNode)
+ */
+ protected function createNodeListForSigning(\DOMDocument $dom, \DOMElement $security)
+ {
+ $nodes = array();
+ $body = $dom->getElementsByTagNameNS($dom->documentElement->namespaceURI, 'Body')->item(0);
+ if (null !== $body) {
+ $nodes[] = $body;
+ }
+ foreach ($security->childNodes as $node) {
+ if (XML_ELEMENT_NODE === $node->nodeType) {
+ $nodes[] = $node;
+ }
+ }
+ if ($this->signAllHeaders) {
+ foreach ($security->parentNode->childNodes as $node) {
+ if (XML_ELEMENT_NODE === $node->nodeType &&
+ Helper::NS_WSS !== $node->namespaceURI) {
+ $nodes[] = $node;
+ }
+ }
+ }
+
+ return $nodes;
+ }
+
+ /**
+ * Gets the referenced node for the given URI.
+ *
+ * @param \DOMElement $node Node
+ * @param string $uri URI
+ *
+ * @return \DOMElement
+ */
+ protected function getReferenceNodeForUri(\DOMElement $node, $uri)
+ {
+ $url = parse_url($uri);
+ $referenceId = $url['fragment'];
+ $query = '//*[@'.Helper::PFX_WSU.':Id="'.$referenceId.'" or @Id="'.$referenceId.'"]';
+ $xpath = new \DOMXPath($node->ownerDocument);
+ $xpath->registerNamespace(Helper::PFX_WSU, Helper::NS_WSU);
+
+ return $xpath->query($query)->item(0);
+ }
+
+ /**
+ * Tries to resolve a key from the given \DOMElement.
+ *
+ * @param \DOMElement $node Node where to resolve the key
+ * @param string $algorithm XML security key algorithm
+ *
+ * @return \ass\XmlSecurity\Key|null
+ */
+ public function keyInfoSecurityTokenReferenceResolver(\DOMElement $node, $algorithm)
+ {
+ foreach ($node->childNodes as $key) {
+ if (Helper::NS_WSS === $key->namespaceURI) {
+ switch ($key->localName) {
+ case 'KeyIdentifier':
+
+ return $this->serviceSecurityKey->getPublicKey();
+ case 'Reference':
+ $uri = $key->getAttribute('URI');
+ $referencedNode = $this->getReferenceNodeForUri($node, $uri);
+
+ if (XmlSecurityEnc::NS_XMLENC === $referencedNode->namespaceURI
+ && 'EncryptedKey' == $referencedNode->localName) {
+ $key = XmlSecurityEnc::decryptEncryptedKey($referencedNode, $this->userSecurityKey->getPrivateKey());
+
+ return XmlSecurityKey::factory($algorithm, $key, XmlSecurityKey::TYPE_PRIVATE);
+ } else {
+ //$valueType = $key->getAttribute('ValueType');
+
+ return $this->serviceSecurityKey->getPublicKey();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsdlDownloader.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsdlDownloader.php
new file mode 100644
index 0000000..9f62fe4
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/WsdlDownloader.php
@@ -0,0 +1,261 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\Helper;
+
+/**
+ * Downloads WSDL files with cURL. Uses the WSDL_CACHE_* constants and the
+ * 'soap.wsdl_*' ini settings. Does only file caching as SoapClient only
+ * supports a file name parameter. The class also resolves remote XML schema
+ * includes.
+ *
+ * @author Andreas Schamberger
+ */
+class WsdlDownloader
+{
+ /**
+ * Cache enabled.
+ *
+ * @var bool
+ */
+ protected $cacheEnabled;
+
+ /**
+ * Cache dir.
+ *
+ * @var string
+ */
+ protected $cacheDir;
+
+ /**
+ * Cache TTL.
+ *
+ * @var int
+ */
+ protected $cacheTtl;
+
+ /**
+ * cURL instance for downloads.
+ *
+ * @var unknown_type
+ */
+ protected $curl;
+
+ /**
+ * Resolve WSDl/XSD includes.
+ *
+ * @var boolean
+ */
+ protected $resolveRemoteIncludes = true;
+
+ /**
+ * Constructor.
+ *
+ * @param \BeSimple\SoapClient\Curl $curl Curl instance
+ * @param boolean $resolveRemoteIncludes WSDL/XSD include enabled?
+ * @param boolean $cacheWsdl Cache constant
+ */
+ public function __construct(Curl $curl, $resolveRemoteIncludes = true, $cacheWsdl = WSDL_CACHE_DISK)
+ {
+ $this->curl = $curl;
+ $this->resolveRemoteIncludes = $resolveRemoteIncludes;
+ // get current WSDL caching config
+ $this->cacheEnabled = (bool) ini_get('soap.wsdl_cache_enabled');
+ if ($this->cacheEnabled === true
+ && $cacheWsdl === WSDL_CACHE_NONE) {
+ $this->cacheEnabled = false;
+ }
+ $this->cacheDir = ini_get('soap.wsdl_cache_dir');
+ if (!is_dir($this->cacheDir)) {
+ $this->cacheDir = sys_get_temp_dir();
+ }
+ $this->cacheDir = rtrim($this->cacheDir, '/\\');
+ $this->cacheTtl = ini_get('soap.wsdl_cache_ttl');
+ }
+
+ /**
+ * Download given WSDL file and return name of cache file.
+ *
+ * @param string $wsdl WSDL file URL/path
+ *
+ * @return string
+ */
+ public function download($wsdl)
+ {
+ // download and cache remote WSDL files or local ones where we want to
+ // resolve remote XSD includes
+ $isRemoteFile = $this->isRemoteFile($wsdl);
+ if ($isRemoteFile === true || $this->resolveRemoteIncludes === true) {
+ $cacheFile = $this->cacheDir . DIRECTORY_SEPARATOR . 'wsdl_' . md5($wsdl) . '.cache';
+ if ($this->cacheEnabled === false
+ || !file_exists($cacheFile)
+ || (filemtime($cacheFile) + $this->cacheTtl) < time()) {
+ if ($isRemoteFile === true) {
+ // execute request
+ $responseSuccessfull = $this->curl->exec($wsdl);
+ // get content
+ if ($responseSuccessfull === true) {
+ $response = $this->curl->getResponseBody();
+ if ($this->resolveRemoteIncludes === true) {
+ $this->resolveRemoteIncludes($response, $cacheFile, $wsdl);
+ } else {
+ file_put_contents($cacheFile, $response);
+ }
+ } else {
+ throw new \ErrorException("SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl ."'");
+ }
+ } elseif (file_exists($wsdl)) {
+ $response = file_get_contents($wsdl);
+ $this->resolveRemoteIncludes($response, $cacheFile);
+ } else {
+ throw new \ErrorException("SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl ."'");
+ }
+ }
+
+ return $cacheFile;
+ } elseif (file_exists($wsdl)) {
+
+ return realpath($wsdl);
+ } else {
+ throw new \ErrorException("SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl ."'");
+ }
+ }
+
+ /**
+ * Do we have a remote file?
+ *
+ * @param string $file File URL/path
+ *
+ * @return boolean
+ */
+ private function isRemoteFile($file)
+ {
+ $isRemoteFile = false;
+ // @parse_url to suppress E_WARNING for invalid urls
+ if (($url = @parse_url($file)) !== false) {
+ if (isset($url['scheme']) && substr($url['scheme'], 0, 4) == 'http') {
+ $isRemoteFile = true;
+ }
+ }
+
+ return $isRemoteFile;
+ }
+
+ /**
+ * Resolves remote WSDL/XSD includes within the WSDL files.
+ *
+ * @param string $xml XML file
+ * @param string $cacheFile Cache file name
+ * @param boolean $parentFile Parent file name
+ *
+ * @return void
+ */
+ private function resolveRemoteIncludes($xml, $cacheFile, $parentFile = null)
+ {
+ $doc = new \DOMDocument();
+ $doc->loadXML($xml);
+ $xpath = new \DOMXPath($doc);
+ $xpath->registerNamespace(Helper::PFX_XML_SCHEMA, Helper::NS_XML_SCHEMA);
+ $xpath->registerNamespace(Helper::PFX_WSDL, Helper::NS_WSDL);
+ // WSDL include/import
+ $query = './/' . Helper::PFX_WSDL . ':include | .//' . Helper::PFX_WSDL . ':import';
+ $nodes = $xpath->query($query);
+ if ($nodes->length > 0) {
+ foreach ($nodes as $node) {
+ $location = $node->getAttribute('location');
+ if ($this->isRemoteFile($location)) {
+ $location = $this->download($location);
+ $node->setAttribute('location', $location);
+ } elseif (!is_null($parentFile)) {
+ $location = $this->resolveRelativePathInUrl($parentFile, $location);
+ $location = $this->download($location);
+ $node->setAttribute('location', $location);
+ }
+ }
+ }
+ // XML schema include/import
+ $query = './/' . Helper::PFX_XML_SCHEMA . ':include | .//' . Helper::PFX_XML_SCHEMA . ':import';
+ $nodes = $xpath->query($query);
+ if ($nodes->length > 0) {
+ foreach ($nodes as $node) {
+ if ($node->hasAttribute('schemaLocation')) {
+ $schemaLocation = $node->getAttribute('schemaLocation');
+ if ($this->isRemoteFile($schemaLocation)) {
+ $schemaLocation = $this->download($schemaLocation);
+ $node->setAttribute('schemaLocation', $schemaLocation);
+ } elseif (null !== $parentFile) {
+ $schemaLocation = $this->resolveRelativePathInUrl($parentFile, $schemaLocation);
+ $schemaLocation = $this->download($schemaLocation);
+ $node->setAttribute('schemaLocation', $schemaLocation);
+ }
+ }
+ }
+ }
+ $doc->save($cacheFile);
+ }
+
+ /**
+ * Resolves the relative path to base into an absolute.
+ *
+ * @param string $base Base path
+ * @param string $relative Relative path
+ *
+ * @return string
+ */
+ private function resolveRelativePathInUrl($base, $relative)
+ {
+ $urlParts = parse_url($base);
+ // combine base path with relative path
+ if (isset($urlParts['path']) && strpos($relative, '/') === 0) {
+ // $relative is absolute path from domain (starts with /)
+ $path = $relative;
+ } elseif (isset($urlParts['path']) && strrpos($urlParts['path'], '/') === (strlen($urlParts['path']) )) {
+ // base path is directory
+ $path = $urlParts['path'] . $relative;
+ } elseif (isset($urlParts['path'])) {
+ // strip filename from base path
+ $path = substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) . '/' . $relative;
+ } else {
+ // no base path
+ $path = '/' . $relative;
+ }
+ // foo/./bar ==> foo/bar
+ $path = preg_replace('~/\./~', '/', $path);
+ // remove double slashes
+ $path = preg_replace('~/+~', '/', $path);
+ // split path by '/'
+ $parts = explode('/', $path);
+ // resolve /../
+ foreach ($parts as $key => $part) {
+ if ($part == "..") {
+ $keyToDelete = $key-1;
+ while ($keyToDelete > 0) {
+ if (isset($parts[$keyToDelete])) {
+ unset($parts[$keyToDelete]);
+ break;
+ } else {
+ $keyToDelete--;
+ }
+ }
+ unset($parts[$key]);
+ }
+ }
+ $hostname = $urlParts['scheme'] . '://' . $urlParts['host'];
+ if (isset($urlParts['port'])) {
+ $hostname .= ':' . $urlParts['port'];
+ }
+
+ return $hostname . implode('/', $parts);
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/src/BeSimple/SoapClient/XmlMimeFilter.php b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/XmlMimeFilter.php
new file mode 100644
index 0000000..142a271
--- /dev/null
+++ b/src/BeSimple/SoapClient/src/BeSimple/SoapClient/XmlMimeFilter.php
@@ -0,0 +1,75 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapCommon\FilterHelper;
+use BeSimple\SoapCommon\Helper;
+use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart;
+use BeSimple\SoapCommon\Mime\Parser as MimeParser;
+use BeSimple\SoapCommon\Mime\Part as MimePart;
+use BeSimple\SoapCommon\SoapRequest;
+use BeSimple\SoapCommon\SoapRequestFilter;
+use BeSimple\SoapCommon\SoapResponse;
+use BeSimple\SoapCommon\SoapResponseFilter;
+
+/**
+ * XML MIME filter that fixes the namespace of xmime:contentType attribute.
+ *
+ * @author Andreas Schamberger
+ */
+class XmlMimeFilter implements SoapRequestFilter
+{
+ /**
+ * Reset all properties to default values.
+ */
+ public function resetFilter()
+ {
+ }
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
+ *
+ * @return void
+ */
+ public function filterRequest(SoapRequest $request)
+ {
+ // get \DOMDocument from SOAP request
+ $dom = $request->getContentDocument();
+
+ // create FilterHelper
+ $filterHelper = new FilterHelper($dom);
+
+ // add the neccessary namespaces
+ $filterHelper->addNamespace(Helper::PFX_XMLMIME, Helper::NS_XMLMIME);
+
+ // get xsd:base64binary elements
+ $xpath = new \DOMXPath($dom);
+ $xpath->registerNamespace('XOP', Helper::NS_XOP);
+ $query = '//XOP:Include/..';
+ $nodes = $xpath->query($query);
+
+ // exchange attributes
+ if ($nodes->length > 0) {
+ foreach ($nodes as $node) {
+ if ($node->hasAttribute('contentType')) {
+ $contentType = $node->getAttribute('contentType');
+ $node->removeAttribute('contentType');
+ $filterHelper->setAttribute($node, Helper::NS_XMLMIME, 'contentType', $contentType);
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/MTOM.php b/src/BeSimple/SoapClient/tests/AxisInterop/MTOM.php
new file mode 100644
index 0000000..38efbf0
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/MTOM.php
@@ -0,0 +1,100 @@
+ SOAP_1_1,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
+ 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM,
+ 'cache_wsdl' => WSDL_CACHE_NONE,
+ 'classmap' => array(
+ 'base64Binary' => 'base64Binary',
+ 'AttachmentRequest' => 'AttachmentRequest',
+ ),
+);
+
+/*
+ * Deploy "axis_services/sample-mtom.aar" to Apache Axis2 to get this
+ * example to work.
+ *
+ * Apache Axis2 MTOM example.
+ *
+ */
+$sc = new BeSimpleSoapClient('MTOM.wsdl', $options);
+
+//var_dump($sc->__getFunctions());
+//var_dump($sc->__getTypes());
+
+try {
+ $b64 = new base64Binary();
+ $b64->_ = 'This is a test. :)';
+ $b64->contentType = 'text/plain';
+
+ $attachment = new AttachmentRequest();
+ $attachment->fileName = 'test123.txt';
+ $attachment->binaryData = $b64;
+
+ var_dump($sc->attachment($attachment));
+
+} catch (Exception $e) {
+ var_dump($e);
+}
+
+// var_dump(
+// $sc->__getLastRequestHeaders(),
+// $sc->__getLastRequest(),
+// $sc->__getLastResponseHeaders(),
+// $sc->__getLastResponse()
+// );
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/MTOM.wsdl b/src/BeSimple/SoapClient/tests/AxisInterop/MTOM.wsdl
new file mode 100644
index 0000000..f0c9a6d
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/MTOM.wsdl
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/SwA.php b/src/BeSimple/SoapClient/tests/AxisInterop/SwA.php
new file mode 100644
index 0000000..eaf1bc6
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/SwA.php
@@ -0,0 +1,84 @@
+ SOAP_1_1,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
+ 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA,
+ 'cache_wsdl' => WSDL_CACHE_NONE,
+);
+
+/*
+ * Deploy "axis_services/besimple-swa.aar" to Apache Axis2 to get this
+ * example to work.
+ *
+ * Run ant to rebuild aar.
+ *
+ * Example based on:
+ * http://axis.apache.org/axis2/java/core/docs/mtom-guide.html#a3
+ * http://wso2.org/library/1675
+ *
+ * Doesn't work directly with ?wsdl served by Apache Axis!
+ *
+ */
+
+$sc = new BeSimpleSoapClient('SwA.wsdl', $options);
+
+//var_dump($sc->__getFunctions());
+//var_dump($sc->__getTypes());
+
+try {
+ $file = new stdClass();
+ $file->name = 'upload.txt';
+ $file->data = 'This is a test text!';
+ $result = $sc->uploadFile($file);
+
+ var_dump(
+ $result->return
+ );
+
+ $file = new stdClass();
+ $file->name = 'upload.txt';
+ $result = $sc->downloadFile($file);
+
+ var_dump(
+ $result->data
+ );
+
+ $file = new stdClass();
+ $file->name = 'image.jpg'; // source: http://www.freeimageslive.com/galleries/light/pics/swirl3768.jpg
+ $file->data = file_get_contents('image.jpg');
+ $result = $sc->uploadFile($file);
+
+ var_dump(
+ $result->return
+ );
+
+ $crc32 = crc32($file->data);
+
+ $file = new stdClass();
+ $file->name = 'image.jpg';
+ $result = $sc->downloadFile($file);
+
+ file_put_contents('image2.jpg', $result->data);
+
+
+ var_dump(
+ crc32($result->data) === $crc32
+ );
+
+} catch (Exception $e) {
+ var_dump($e);
+}
+
+// var_dump(
+// $sc->__getLastRequestHeaders(),
+// $sc->__getLastRequest(),
+// $sc->__getLastResponseHeaders(),
+// $sc->__getLastResponse()
+// );
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/SwA.wsdl b/src/BeSimple/SoapClient/tests/AxisInterop/SwA.wsdl
new file mode 100644
index 0000000..a4de7e0
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/SwA.wsdl
@@ -0,0 +1,162 @@
+
+
+ BeSimpleSwaService
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/SwA/build.xml b/src/BeSimple/SoapClient/tests/AxisInterop/SwA/build.xml
new file mode 100644
index 0000000..f5261ed
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/SwA/build.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/SwA/resources/META-INF/services.xml b/src/BeSimple/SoapClient/tests/AxisInterop/SwA/resources/META-INF/services.xml
new file mode 100644
index 0000000..8b49f87
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/SwA/resources/META-INF/services.xml
@@ -0,0 +1,15 @@
+
+
+ BeSimple test service for SwA.
+ true
+ besimple.service.BeSimpleSwaService
+
+ urn:uploadFile
+
+
+
+ urn:downloadFile
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/SwA/src/besimple/service/BeSimpleSwaService.java b/src/BeSimple/SoapClient/tests/AxisInterop/SwA/src/besimple/service/BeSimpleSwaService.java
new file mode 100644
index 0000000..b173e15
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/SwA/src/besimple/service/BeSimpleSwaService.java
@@ -0,0 +1,78 @@
+package besimple.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.xml.namespace.QName;
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+
+import org.apache.axiom.attachments.Attachments;
+import org.apache.axiom.om.OMAbstractFactory;
+import org.apache.axiom.om.OMAttribute;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMFactory;
+import org.apache.axiom.om.OMNamespace;
+
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.context.OperationContext;
+import org.apache.axis2.wsdl.WSDLConstants;
+
+public class BeSimpleSwaService {
+
+ String namespace = "http://service.besimple";
+
+ public OMElement uploadFile(OMElement element) throws Exception {
+ OMElement dataElement = (OMElement)element.getFirstChildWithName(new QName(namespace, "data"));
+ OMAttribute hrefAttribute = dataElement.getAttribute(new QName("href"));
+
+ String contentID = hrefAttribute.getAttributeValue();
+ contentID = contentID.trim();
+ if (contentID.substring(0, 3).equalsIgnoreCase("cid")) {
+ contentID = contentID.substring(4);
+ }
+ OMElement nameElement = (OMElement)element.getFirstChildWithName(new QName(namespace, "name"));
+ String name = nameElement.getText();
+
+ MessageContext msgCtx = MessageContext.getCurrentMessageContext();
+ Attachments attachment = msgCtx.getAttachmentMap();
+ DataHandler dataHandler = attachment.getDataHandler(contentID);
+
+ File file = new File(name);
+ FileOutputStream fileOutputStream = new FileOutputStream(file);
+ dataHandler.writeTo(fileOutputStream);
+ fileOutputStream.flush();
+ fileOutputStream.close();
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ OMNamespace omNs = factory.createOMNamespace(namespace, "swa");
+ OMElement wrapperElement = factory.createOMElement("uploadFileResponse", omNs);
+ OMElement returnElement = factory.createOMElement("return", omNs, wrapperElement);
+ returnElement.setText("File saved succesfully.");
+
+ return wrapperElement;
+ }
+
+ public OMElement downloadFile(OMElement element) throws Exception {
+ OMElement nameElement = (OMElement)element.getFirstChildWithName(new QName(namespace, "name"));
+ String name = nameElement.getText();
+
+ MessageContext msgCtxIn = MessageContext.getCurrentMessageContext();
+ OperationContext operationContext = msgCtxIn.getOperationContext();
+ MessageContext msgCtxOut = operationContext.getMessageContext(WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
+
+ FileDataSource fileDataSource = new FileDataSource(name);
+ DataHandler dataHandler = new DataHandler(fileDataSource);
+
+ String contentID = "cid:" + msgCtxOut.addAttachment(dataHandler);
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ OMNamespace omNs = factory.createOMNamespace(namespace, "swa");
+ OMElement wrapperElement = factory.createOMElement("downloadFileResponse", omNs);
+ OMElement dataElement = factory.createOMElement("data", omNs, wrapperElement);
+ dataElement.addAttribute("href", contentID, null);
+
+ return wrapperElement;
+ }
+}
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/WsAddressing.php b/src/BeSimple/SoapClient/tests/AxisInterop/WsAddressing.php
new file mode 100644
index 0000000..dacabcb
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/WsAddressing.php
@@ -0,0 +1,73 @@
+ SOAP_1_2,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
+);
+
+/*
+ * Deploy "axis_services/version2.aar" to Apache Axis2 to get this example to
+ * work.
+ *
+ * To rebuild the "axis_services/version2.aar" the following steps need to be
+ * done to build a working Apache Axis2 version service with SOAP session
+ * enabled.
+ *
+ * 1) Go to $AXIS_HOME/samples/version and edit the following files:
+ *
+ * resources/META-INF/services.xml:
+ *
+ * ...
+ *
+ *
+ * build.xml:
+ * replace version.aar with version2.aar
+ *
+ * 2) Run ant build.xml in "$AXIS_HOME/samples/version"
+ *
+ */
+
+$sc = new BeSimpleSoapClient('http://localhost:8080/axis2/services/Version2?wsdl', $options);
+$soapKernel = $sc->getSoapKernel();
+$wsaFilter = new BeSimpleWsAddressingFilter();
+$soapKernel->registerFilter($wsaFilter);
+
+//var_dump($sc->__getFunctions());
+//var_dump($sc->__getTypes());
+
+try {
+ $wsaFilter->setReplyTo(BeSimpleWsAddressingFilter::ENDPOINT_REFERENCE_ANONYMOUS);
+ $wsaFilter->setMessageId();
+
+ var_dump($sc->getVersion());
+
+ $soapSessionId1 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId');
+ echo 'ID1: ' .$soapSessionId1 . PHP_EOL;
+
+ $wsaFilter->addReferenceParameter('http://ws.apache.org/namespaces/axis2', 'axis2', 'ServiceGroupId', $soapSessionId1);
+
+ var_dump($sc->getVersion());
+
+ $soapSessionId2 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId');
+ echo 'ID2: ' . $soapSessionId2 . PHP_EOL;
+
+ if ($soapSessionId1 == $soapSessionId2) {
+ echo PHP_EOL;
+ echo 'SOAP session worked :)';
+ }
+} catch (Exception $e) {
+ var_dump($e);
+}
+
+// var_dump(
+// $sc->__getLastRequestHeaders(),
+// $sc->__getLastRequest(),
+// $sc->__getLastResponseHeaders(),
+// $sc->__getLastResponse()
+// );
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/WsSecuritySigEnc.php b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecuritySigEnc.php
new file mode 100644
index 0000000..d6a31fb
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecuritySigEnc.php
@@ -0,0 +1,116 @@
+';
+
+$options = array(
+ 'soap_version' => SOAP_1_2,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
+);
+
+/*
+ * Deploy "axis_services/library-signencr.aar" to Apache Axis2 to get this
+ * example to work.
+ *
+ * Links:
+ * http://www.dcc.uchile.cl/~pcamacho/tutorial/web/xmlsec/xmlsec.html
+ * http://www.aleksey.com/xmlsec/xmldsig-verifier.html
+ *
+ * Using code from axis example:
+ * http://www.ibm.com/developerworks/java/library/j-jws5/index.html
+ *
+ * Download key tool to export private key
+ * http://couchpotato.net/pkeytool/
+ *
+ * keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer
+ * openssl x509 -out servercert.pem -outform pem -in servercert.pem -inform der
+ *
+ * keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer
+ * openssl x509 -out clientcert.pem -outform pem -in clientcert.pem -inform der
+ * java -jar pkeytool.jar -exportkey -keystore client.keystore -storepass nosecret -keypass clientpass -rfc -alias clientkey -file clientkey.pem
+ *
+ * C:\Program Files\Java\jre6\bin\keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer
+ * C:\xampp\apache\bin\openssl x509 -out servercert.pem -outform pem -in servercert.cer -inform der
+ *
+ * C:\Program Files\Java\jre6\bin\keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer
+ * C:\xampp\apache\bin\openssl x509 -out clientcert.pem -outform pem -in clientcert.cer -inform der
+ * java -jar C:\axis2\pkeytool\pkeytool.jar -exportkey -keystore client.keystore -storepass nosecret -keypass clientpass -rfc -alias clientkey -file clientkey.pem
+ *
+ * build.properties:
+ * server-policy=hash-policy-server.xml
+ *
+ * allows both text and digest!
+ */
+
+class getBook {}
+class getBookResponse {}
+class getBooksByType {}
+class getBooksByTypeResponse {}
+class addBook {}
+class addBookResponse {}
+class BookInformation {}
+
+$options['classmap'] = array(
+ 'getBook' => 'getBook',
+ 'getBookResponse' => 'getBookResponse',
+ 'getBooksByType' => 'getBooksByType',
+ 'getBooksByTypeResponse' => 'getBooksByTypeResponse',
+ 'addBook' => 'addBook',
+ 'addBookResponse' => 'addBookResponse',
+ 'BookInformation' => 'BookInformation',
+);
+
+$sc = new BeSimpleSoapClient('WsSecuritySigEnc.wsdl', $options);
+
+$wssFilter = new BeSimpleWsSecurityFilter();
+// user key for signature and encryption
+$securityKeyUser = new BeSimpleWsSecurityKey();
+$securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, 'clientkey.pem', true);
+$securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, 'clientcert.pem', true);
+$wssFilter->setUserSecurityKeyObject($securityKeyUser);
+// service key for encryption
+$securityKeyService = new BeSimpleWsSecurityKey();
+$securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC);
+$securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, 'servercert.pem', true);
+$wssFilter->setServiceSecurityKeyObject($securityKeyService);
+// TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1
+$wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN);
+$wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1);
+
+$soapKernel = $sc->getSoapKernel();
+$soapKernel->registerFilter($wssFilter);
+
+//var_dump($sc->__getFunctions());
+//var_dump($sc->__getTypes());
+
+try {
+ $gb = new getBook();
+ $gb->isbn = '0061020052';
+ var_dump($sc->getBook($gb));
+
+ $ab = new addBook();
+ $ab->isbn = '0445203498';
+ $ab->title = 'The Dragon Never Sleeps';
+ $ab->author = 'Cook, Glen';
+ $ab->type = 'scifi';
+ var_dump($sc->addBook($ab));
+
+ // getBooksByType("scifi");
+} catch (Exception $e) {
+ var_dump($e);
+}
+
+//var_dump(
+// $sc->__getLastRequestHeaders(),
+// $sc->__getLastRequest(),
+// $sc->__getLastResponseHeaders(),
+// $sc->__getLastResponse()
+//);
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/WsSecuritySigEnc.wsdl b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecuritySigEnc.wsdl
new file mode 100644
index 0000000..620ea51
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecuritySigEnc.wsdl
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/WsSecurityUserPass.php b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecurityUserPass.php
new file mode 100644
index 0000000..231f1e5
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecurityUserPass.php
@@ -0,0 +1,79 @@
+ SOAP_1_2,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
+);
+
+/*
+ * Deploy "axis_services/library-username-digest.aar" to Apache Axis2 to get
+ * this example to work.
+ *
+ * Using code from axis example:
+ * http://www.ibm.com/developerworks/java/library/j-jws4/index.html
+ *
+ * build.properties:
+ * server-policy=hash-policy-server.xml
+ *
+ * allows both text and digest!
+ */
+
+class getBook {}
+class getBookResponse {}
+class getBooksByType {}
+class getBooksByTypeResponse {}
+class addBook {}
+class addBookResponse {}
+class BookInformation {}
+
+$options['classmap'] = array(
+ 'getBook' => 'getBook',
+ 'getBookResponse' => 'getBookResponse',
+ 'getBooksByType' => 'getBooksByType',
+ 'getBooksByTypeResponse' => 'getBooksByTypeResponse',
+ 'addBook' => 'addBook',
+ 'addBookResponse' => 'addBookResponse',
+ 'BookInformation' => 'BookInformation',
+);
+
+$sc = new BeSimpleSoapClient('WsSecurityUserPass.wsdl', $options);
+
+$wssFilter = new BeSimpleWsSecurityFilter(true, 600);
+$wssFilter->addUserData('libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_TEXT);
+//$wssFilter->addUserData( 'libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_DIGEST );
+
+$soapKernel = $sc->getSoapKernel();
+$soapKernel->registerFilter($wssFilter);
+
+//var_dump($sc->__getFunctions());
+//var_dump($sc->__getTypes());
+
+try {
+ $gb = new getBook();
+ $gb->isbn = '0061020052';
+ var_dump($sc->getBook($gb));
+
+ $ab = new addBook();
+ $ab->isbn = '0445203498';
+ $ab->title = 'The Dragon Never Sleeps';
+ $ab->author = 'Cook, Glen';
+ $ab->type = 'scifi';
+ var_dump($sc->addBook($ab));
+
+ // getBooksByType("scifi");
+} catch (Exception $e) {
+ var_dump($e);
+}
+
+//var_dump(
+// $sc->__getLastRequestHeaders(),
+// $sc->__getLastRequest(),
+// $sc->__getLastResponseHeaders(),
+// $sc->__getLastResponse()
+//);
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/WsSecurityUserPass.wsdl b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecurityUserPass.wsdl
new file mode 100644
index 0000000..6e72411
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/WsSecurityUserPass.wsdl
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/besimple-swa.aar b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/besimple-swa.aar
new file mode 100644
index 0000000..bb41750
Binary files /dev/null and b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/besimple-swa.aar differ
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/library-signencr.aar b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/library-signencr.aar
new file mode 100644
index 0000000..25ffc2d
Binary files /dev/null and b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/library-signencr.aar differ
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/library-username-digest.aar b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/library-username-digest.aar
new file mode 100644
index 0000000..f6f8595
Binary files /dev/null and b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/library-username-digest.aar differ
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/sample-mtom.aar b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/sample-mtom.aar
new file mode 100644
index 0000000..a215edc
Binary files /dev/null and b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/sample-mtom.aar differ
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/version2.aar b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/version2.aar
new file mode 100644
index 0000000..df76662
Binary files /dev/null and b/src/BeSimple/SoapClient/tests/AxisInterop/axis_services/version2.aar differ
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/clientcert.pem b/src/BeSimple/SoapClient/tests/AxisInterop/clientcert.pem
new file mode 100644
index 0000000..f433d48
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/clientcert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICoDCCAgkCBEnhw2IwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAk5aMRMw
+EQYDVQQIEwpXZWxsaW5ndG9uMRowGAYDVQQHExFQYXJhcGFyYXVtdSBCZWFjaDEq
+MCgGA1UEChMhU29zbm9za2kgU29mdHdhcmUgQXNzb2NpYXRlcyBMdGQuMRAwDgYD
+VQQLEwdVbmtub3duMRgwFgYDVQQDEw9EZW5uaXMgU29zbm9za2kwHhcNMDkwNDEy
+MTAzMzA2WhcNMzYwODI3MTAzMzA2WjCBljELMAkGA1UEBhMCTloxEzARBgNVBAgT
+CldlbGxpbmd0b24xGjAYBgNVBAcTEVBhcmFwYXJhdW11IEJlYWNoMSowKAYDVQQK
+EyFTb3Nub3NraSBTb2Z0d2FyZSBBc3NvY2lhdGVzIEx0ZC4xEDAOBgNVBAsTB1Vu
+a25vd24xGDAWBgNVBAMTD0Rlbm5pcyBTb3Nub3NraTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAhOVyNK8xyxtb4DnKtU6mF9KoiFqCk7eKoLE26+9h410CtTkx
+zWAfgnR+8i+LPbdsPY+yXAo6NYpCCKolXfDLe+AG2GwnMZGrIl6+BLF3hqTmIXBF
+TLGUmC7A7uBTivaWgdH1w3hb33rASoVU67BVtQ3QQi99juZX4vU9o9pScocCAwEA
+ATANBgkqhkiG9w0BAQUFAAOBgQBMNPo1KAGbz8Jl6HGbtAcetieSJ3bEAXmv1tcj
+ysBS67AXzdu1Ac+onHh2EpzBM7kuGbw+trU+AhulooPpewIQRApXP1F0KHRDcbqW
+jwvknS6HnomN9572giLGKn2601bHiRUj35hiA8aLmMUBppIRPFFAoQ0QUBCPx+m8
+/0n33w==
+-----END CERTIFICATE-----
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/clientkey.pem b/src/BeSimple/SoapClient/tests/AxisInterop/clientkey.pem
new file mode 100644
index 0000000..a47f923
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/clientkey.pem
@@ -0,0 +1,14 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAITlcjSvMcsbW+A5yrVOphfSqIha
+gpO3iqCxNuvvYeNdArU5Mc1gH4J0fvIviz23bD2PslwKOjWKQgiqJV3wy3vgBthsJzGRqyJevgSx
+d4ak5iFwRUyxlJguwO7gU4r2loHR9cN4W996wEqFVOuwVbUN0EIvfY7mV+L1PaPaUnKHAgMBAAEC
+gYAZ6UqtLwN8YGc3fs0hMKZ9upsViuAuwPiMgED/G3twgzAF+ZLWQkmie+hMfCyf6eV200+pVm0n
+Bz/8xH/oowxpX0Kk3szoB4vFghjU84GKUcrbhu/NRIm7l3drnfbzqhQkHDCx6n1CotI4Gs49cDWu
+4uEAuxJkEIVY553unZjZgQJBAOJVIallNKmD0iQlvtWRmRzpmYDjt9vhNY6WBTIOx6SDn9SRaoSA
+fkipQ2HXo04r78TQ674+zfZ1lRTkFG7px6ECQQCWUPHp3pSZOM1oGzJrNvNaw+MizZAZjq34npHm
+9GRquFLG7BlCaI9QNGE7pN2ryYsYCRUMaM2e4GR0tUXxVGknAkAgrxqFU9AfCqI2Bh1gyf3KZxF7
+w2axofwR8ygc6nV6FGfoUneHWubhp0/LuVAj4cRmL6Vbe8ZSaPh2Y9lviuMBAkEAicP8Q+1E4j1m
+PPEYP51oYprANOiUFmhnWEL00+jPk+QFsd03tV6hYs/vAbwzkjuwqMHCMdJoCiH8z95IEUvc5wJA
+MvLOuZdu4dmhOXg/YKsbMSPjFNEVskLQNSXqw6O2wIrpPg1NQvBBAOTbiuZj3vind4VPos1wc4vB
+QocvdUC6dA==
+-----END PRIVATE KEY-----
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/image.jpg b/src/BeSimple/SoapClient/tests/AxisInterop/image.jpg
new file mode 100644
index 0000000..4331bf5
Binary files /dev/null and b/src/BeSimple/SoapClient/tests/AxisInterop/image.jpg differ
diff --git a/src/BeSimple/SoapClient/tests/AxisInterop/servercert.pem b/src/BeSimple/SoapClient/tests/AxisInterop/servercert.pem
new file mode 100644
index 0000000..040b22c
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/AxisInterop/servercert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICoDCCAgkCBEnhwzMwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAk5aMRMw
+EQYDVQQIEwpXZWxsaW5ndG9uMRowGAYDVQQHExFQYXJhcGFyYXVtdSBCZWFjaDEq
+MCgGA1UEChMhU29zbm9za2kgU29mdHdhcmUgQXNzb2NpYXRlcyBMdGQuMRAwDgYD
+VQQLEwdVbmtub3duMRgwFgYDVQQDEw9EZW5uaXMgU29zbm9za2kwHhcNMDkwNDEy
+MTAzMjE5WhcNMzYwODI3MTAzMjE5WjCBljELMAkGA1UEBhMCTloxEzARBgNVBAgT
+CldlbGxpbmd0b24xGjAYBgNVBAcTEVBhcmFwYXJhdW11IEJlYWNoMSowKAYDVQQK
+EyFTb3Nub3NraSBTb2Z0d2FyZSBBc3NvY2lhdGVzIEx0ZC4xEDAOBgNVBAsTB1Vu
+a25vd24xGDAWBgNVBAMTD0Rlbm5pcyBTb3Nub3NraTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEA1H3mjQCF9uce2jmm/Yq9kE4ytfvkp4c8G90cDfJXJvOiGQds
+p2vDZXKuCkHQ7vsBBXPNTt8J/d8ZbEwyuB9Ccz5pJqi6Ig6Y2/mEsPthDyh5SrJV
+yQ/wxUGwmfSuwdrIMnplMTq+OR9BOfT3CvjSvuy9d6BQNo4wOMkDvmZTtI8CAwEA
+ATANBgkqhkiG9w0BAQUFAAOBgQCqv4475QaqlKcN2QCZJbLVKZEX+76XLQurGkgf
+2fCgesRHjfUfOHyTTlhWQdEKTcBB2XviUyyW6I//fmKfXUIiQqvgh4LHdXRPEXDf
+Y9nr89MjyQpDlnl6AlrvSej30a9iwVRUeVk4d6gxWHMRonKBFgh+TGexxUXHtPkf
+B1Pdtg==
+-----END CERTIFICATE-----
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/CurlTest.php b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/CurlTest.php
new file mode 100644
index 0000000..d669f4c
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/CurlTest.php
@@ -0,0 +1,181 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapClient\Curl;
+
+/**
+ * @author Andreas Schamberger
+ */
+class CurlTest extends \PHPUnit_Framework_TestCase
+{
+ protected $webserverProcessId;
+
+ protected function startPhpWebserver()
+ {
+ $dir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures';
+ if ('Windows' == substr(php_uname('s'), 0, 7)) {
+ $powershellCommand = "\$app = start-process php.exe -ArgumentList '-S localhost:8000 -t ".$dir."' -WindowStyle 'Hidden' -passthru; Echo \$app.Id;";
+ $shellCommand = 'powershell -command "& {'.$powershellCommand.'}"';
+ } else {
+ $shellCommand = "nohup php -S localhost:8000 -t ".$dir." &";
+ }
+ $output = array();
+ exec($shellCommand, $output);
+ $this->webserverProcessId = $output[0]; // pid is in first element
+ }
+
+ protected function stopPhpWebserver()
+ {
+ if (!is_null($this->webserverProcessId)) {
+ if ('Windows' == substr(php_uname('s'), 0, 7 )) {
+ exec('TASKKILL /F /PID ' . $this->webserverProcessId);
+ } else {
+ exec('kill ' . $this->webserverProcessId);
+ }
+ $this->webserverProcessId = null;
+ }
+ }
+
+ public function testExec()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $this->assertTrue($curl->exec('http://localhost:8000/curl.txt'));
+ $this->assertTrue($curl->exec('http://localhost:8000/404.txt'));
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetErrorMessage()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://unknown/curl.txt');
+ $this->assertEquals('Could not connect to host', $curl->getErrorMessage());
+
+ $curl->exec('xyz://localhost:8000/@404.txt');
+ $this->assertEquals('Unknown protocol. Only http and https are allowed.', $curl->getErrorMessage());
+
+ $curl->exec('');
+ $this->assertEquals('Unable to parse URL', $curl->getErrorMessage());
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetRequestHeaders()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals(136, strlen($curl->getRequestHeaders()));
+
+ $curl->exec('http://localhost:8000/404.txt');
+ $this->assertEquals(135, strlen($curl->getRequestHeaders()));
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetResponse()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals(150, strlen($curl->getResponse()));
+
+ $curl->exec('http://localhost:8000/404.txt');
+ $this->assertEquals(1282, strlen($curl->getResponse()));
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetResponseBody()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals('This is a testfile for cURL.', $curl->getResponseBody());
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetResponseContentType()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals('text/plain; charset=UTF-8', $curl->getResponseContentType());
+
+ $curl->exec('http://localhost:8000/404.txt');
+ $this->assertEquals('text/html; charset=UTF-8', $curl->getResponseContentType());
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetResponseHeaders()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals(122, strlen($curl->getResponseHeaders()));
+
+ $curl->exec('http://localhost:8000/404.txt');
+ $this->assertEquals(130, strlen($curl->getResponseHeaders()));
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetResponseStatusCode()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals(200, $curl->getResponseStatusCode());
+
+ $curl->exec('http://localhost:8000/404.txt');
+ $this->assertEquals(404, $curl->getResponseStatusCode());
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testGetResponseStatusMessage()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+
+ $curl->exec('http://localhost:8000/curl.txt');
+ $this->assertEquals('OK', $curl->getResponseStatusMessage());
+
+ $curl->exec('http://localhost:8000/404.txt');
+ $this->assertEquals('Not Found', $curl->getResponseStatusMessage());
+
+ $this->stopPhpWebserver();
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/curl.txt b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/curl.txt
new file mode 100644
index 0000000..070def3
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/curl.txt
@@ -0,0 +1 @@
+This is a testfile for cURL.
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/foobar.wsdl b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/foobar.wsdl
new file mode 100644
index 0000000..a890dd8
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/foobar.wsdl
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/type_include.xsd b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/type_include.xsd
new file mode 100644
index 0000000..a41dd9a
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/type_include.xsd
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdl_include.wsdl b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdl_include.wsdl
new file mode 100644
index 0000000..775240a
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdl_include.wsdl
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdlinclude/wsdlinctest_absolute.xml b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdlinclude/wsdlinctest_absolute.xml
new file mode 100644
index 0000000..dae033e
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdlinclude/wsdlinctest_absolute.xml
@@ -0,0 +1,5 @@
+
+
+ wsdlincludetest
+
+
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdlinclude/wsdlinctest_relative.xml b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdlinclude/wsdlinctest_relative.xml
new file mode 100644
index 0000000..8148e60
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/wsdlinclude/wsdlinctest_relative.xml
@@ -0,0 +1,5 @@
+
+
+ wsdlincludetest
+
+
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/xsdinclude/xsdinctest_absolute.xml b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/xsdinclude/xsdinctest_absolute.xml
new file mode 100644
index 0000000..dc1b373
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/xsdinclude/xsdinctest_absolute.xml
@@ -0,0 +1,9 @@
+
+
+ xsdinctest
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/xsdinclude/xsdinctest_relative.xml b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/xsdinclude/xsdinctest_relative.xml
new file mode 100644
index 0000000..58cea74
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/Fixtures/xsdinclude/xsdinctest_relative.xml
@@ -0,0 +1,9 @@
+
+
+ xsdinctest
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/SoapClientBuilderTest.php b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/SoapClientBuilderTest.php
new file mode 100644
index 0000000..9b33cce
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/SoapClientBuilderTest.php
@@ -0,0 +1,119 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\Tests\SoapCommon\Soap;
+
+use BeSimple\SoapClient\SoapClientBuilder;
+
+class SoapClientBuilderTest extends \PHPUnit_Framework_TestCase
+{
+ private $defaultOptions = array(
+ 'features' => 0,
+ 'classmap' => array(),
+ 'typemap' => array(),
+ );
+
+ public function testContruct()
+ {
+ $options = $this
+ ->getSoapBuilder()
+ ->getSoapOptions()
+ ;
+
+ $this->assertEquals($this->mergeOptions(array()), $options);
+ }
+
+ public function testWithTrace()
+ {
+ $builder = $this->getSoapBuilder();
+
+ $builder->withTrace();
+ $this->assertEquals($this->mergeOptions(array('trace' => true)), $builder->getSoapOptions());
+
+ $builder->withTrace(false);
+ $this->assertEquals($this->mergeOptions(array('trace' => false)), $builder->getSoapOptions());
+ }
+
+ public function testWithExceptions()
+ {
+ $builder = $this->getSoapBuilder();
+
+ $builder->withExceptions();
+ $this->assertEquals($this->mergeOptions(array('exceptions' => true)), $builder->getSoapOptions());
+
+ $builder->withExceptions(false);
+ $this->assertEquals($this->mergeOptions(array('exceptions' => false)), $builder->getSoapOptions());
+ }
+
+ public function testWithUserAgent()
+ {
+ $builder = $this->getSoapBuilder();
+
+ $builder->withUserAgent('BeSimpleSoap Test');
+ $this->assertEquals($this->mergeOptions(array('user_agent' => 'BeSimpleSoap Test')), $builder->getSoapOptions());
+ }
+
+ public function testWithCompression()
+ {
+ $builder = $this->getSoapBuilder();
+
+ $builder->withCompressionGzip();
+ $this->assertEquals($this->mergeOptions(array('compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP)), $builder->getSoapOptions());
+
+ $builder->withCompressionDeflate();
+ $this->assertEquals($this->mergeOptions(array('compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE)), $builder->getSoapOptions());
+ }
+
+ public function testWithAuthentication()
+ {
+ $builder = $this->getSoapBuilder();
+
+ $builder->withDigestAuthentication(__DIR__.'/Fixtures/cert.pem', 'foobar');
+ $this->assertEquals($this->mergeOptions(array('authentication' => SOAP_AUTHENTICATION_DIGEST, 'local_cert' => __DIR__.'/Fixtures/cert.pem', 'passphrase' => 'foobar')), $builder->getSoapOptions());
+
+ $builder->withDigestAuthentication(__DIR__.'/Fixtures/cert.pem');
+ $this->assertEquals($this->mergeOptions(array('authentication' => SOAP_AUTHENTICATION_DIGEST, 'local_cert' => __DIR__.'/Fixtures/cert.pem')), $builder->getSoapOptions());
+
+ $builder->withBasicAuthentication('foo', 'bar');
+ $this->assertEquals($this->mergeOptions(array('authentication' => SOAP_AUTHENTICATION_BASIC, 'login' => 'foo', 'password' => 'bar')), $builder->getSoapOptions());
+ }
+
+ public function testWithProxy()
+ {
+ $builder = $this->getSoapBuilder();
+
+ $builder->withProxy('localhost', 8080);
+ $this->assertEquals($this->mergeOptions(array('proxy_host' => 'localhost', 'proxy_port' => 8080)), $builder->getSoapOptions());
+
+ $builder->withProxy('127.0.0.1', 8585, 'foo', 'bar');
+ $this->assertEquals($this->mergeOptions(array('proxy_host' => '127.0.0.1', 'proxy_port' => 8585, 'proxy_login' => 'foo', 'proxy_password' => 'bar')), $builder->getSoapOptions());
+ }
+
+ public function testCreateWithDefaults()
+ {
+ $builder = SoapClientBuilder::createWithDefaults();
+
+ $this->assertInstanceOf('BeSimple\SoapClient\SoapClientBuilder', $builder);
+
+ $this->assertEquals($this->mergeOptions(array('soap_version' => SOAP_1_2, 'encoding' => 'UTF-8', 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, 'user_agent' => 'BeSimpleSoap')), $builder->getSoapOptions());
+ }
+
+ private function getSoapBuilder()
+ {
+ return new SoapClientBuilder();
+ }
+
+ private function mergeOptions(array $options)
+ {
+ return array_merge($this->defaultOptions, $options);
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/WsdlDownloaderTest.php b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/WsdlDownloaderTest.php
new file mode 100644
index 0000000..198341d
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/BeSimple/Tests/SoapClient/WsdlDownloaderTest.php
@@ -0,0 +1,266 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace BeSimple\SoapClient;
+
+use BeSimple\SoapClient\WsdlDownloader;
+use BeSimple\SoapClient\Curl;
+
+/**
+ * @author Andreas Schamberger
+ */
+class WsdlDownloaderTest extends \PHPUnit_Framework_TestCase
+{
+ protected $webserverProcessId;
+
+ protected function startPhpWebserver()
+ {
+ $dir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures';
+ if ('Windows' == substr(php_uname('s'), 0, 7)) {
+ $powershellCommand = "\$app = start-process php.exe -ArgumentList '-S localhost:8000 -t ".$dir."' -WindowStyle 'Hidden' -passthru; Echo \$app.Id;";
+ $shellCommand = 'powershell -command "& {'.$powershellCommand.'}"';
+ } else {
+ $shellCommand = "nohup php -S localhost:8000 -t ".$dir." &";
+ }
+ $output = array();
+ exec($shellCommand, $output);
+ $this->webserverProcessId = $output[0]; // pid is in first element
+ }
+
+ protected function stopPhpWebserver()
+ {
+ if (!is_null($this->webserverProcessId)) {
+ if ('Windows' == substr(php_uname('s'), 0, 7)) {
+ exec('TASKKILL /F /PID ' . $this->webserverProcessId);
+ } else {
+ exec('kill ' . $this->webserverProcessId);
+ }
+ $this->webserverProcessId = null;
+ }
+ }
+
+ public function testDownload()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+ $wd = new WsdlDownloader($curl);
+
+ $cacheDir = ini_get('soap.wsdl_cache_dir');
+ if (!is_dir($cacheDir)) {
+ $cacheDir = sys_get_temp_dir();
+ $cacheDirForRegExp = preg_quote($cacheDir);
+ }
+
+ $tests = array(
+ 'localWithAbsolutePath' => array(
+ 'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_absolute.xml',
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ 'localWithRelativePath' => array(
+ 'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_relative.xml',
+ 'assertRegExp' => '~.*\.\./type_include\.xsd.*~',
+ ),
+ 'remoteWithAbsolutePath' => array(
+ 'source' => 'http://localhost:8000/xsdinclude/xsdinctest_absolute.xml',
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ 'remoteWithAbsolutePath' => array(
+ 'source' => 'http://localhost:8000/xsdinclude/xsdinctest_relative.xml',
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ );
+
+ foreach ($tests as $name => $values) {
+ $cacheFileName = $wd->download($values['source']);
+ $result = file_get_contents($cacheFileName);
+ $this->assertRegExp($values['assertRegExp'], $result, $name);
+ unlink($cacheFileName);
+ }
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testIsRemoteFile()
+ {
+ $curl = new Curl();
+ $wd = new WsdlDownloader($curl);
+
+ $class = new \ReflectionClass($wd);
+ $method = $class->getMethod('isRemoteFile');
+ $method->setAccessible(true);
+
+ $this->assertTrue($method->invoke($wd, 'http://www.php.net/'));
+ $this->assertTrue($method->invoke($wd, 'http://localhost/'));
+ $this->assertTrue($method->invoke($wd, 'http://mylocaldomain/'));
+ $this->assertTrue($method->invoke($wd, 'http://www.php.net/dir/test.html'));
+ $this->assertTrue($method->invoke($wd, 'http://localhost/dir/test.html'));
+ $this->assertTrue($method->invoke($wd, 'http://mylocaldomain/dir/test.html'));
+ $this->assertTrue($method->invoke($wd, 'https://www.php.net/'));
+ $this->assertTrue($method->invoke($wd, 'https://localhost/'));
+ $this->assertTrue($method->invoke($wd, 'https://mylocaldomain/'));
+ $this->assertTrue($method->invoke($wd, 'https://www.php.net/dir/test.html'));
+ $this->assertTrue($method->invoke($wd, 'https://localhost/dir/test.html'));
+ $this->assertTrue($method->invoke($wd, 'https://mylocaldomain/dir/test.html'));
+ $this->assertFalse($method->invoke($wd, 'c:/dir/test.html'));
+ $this->assertFalse($method->invoke($wd, '/dir/test.html'));
+ $this->assertFalse($method->invoke($wd, '../dir/test.html'));
+ }
+
+ public function testResolveWsdlIncludes()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+ $wd = new WsdlDownloader($curl);
+
+ $class = new \ReflectionClass($wd);
+ $method = $class->getMethod('resolveRemoteIncludes');
+ $method->setAccessible(true);
+
+ $cacheDir = ini_get('soap.wsdl_cache_dir');
+ if (!is_dir($cacheDir)) {
+ $cacheDir = sys_get_temp_dir();
+ $cacheDirForRegExp = preg_quote($cacheDir);
+ }
+
+ $remoteUrlAbsolute = 'http://localhost:8000/wsdlinclude/wsdlinctest_absolute.xml';
+ $remoteUrlRelative = 'http://localhost:8000/wsdlinclude/wsdlinctest_relative.xml';
+ $tests = array(
+ 'localWithAbsolutePath' => array(
+ 'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/wsdlinclude/wsdlinctest_absolute.xml',
+ 'cacheFile' => $cacheDir.'/cache_local_absolute.xml',
+ 'remoteParentUrl' => null,
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ 'localWithRelativePath' => array(
+ 'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/wsdlinclude/wsdlinctest_relative.xml',
+ 'cacheFile' => $cacheDir.'/cache_local_relative.xml',
+ 'remoteParentUrl' => null,
+ 'assertRegExp' => '~.*\.\./wsdl_include\.wsdl.*~',
+ ),
+ 'remoteWithAbsolutePath' => array(
+ 'source' => $remoteUrlAbsolute,
+ 'cacheFile' => $cacheDir.'/cache_remote_absolute.xml',
+ 'remoteParentUrl' => $remoteUrlAbsolute,
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ 'remoteWithAbsolutePath' => array(
+ 'source' => $remoteUrlRelative,
+ 'cacheFile' => $cacheDir.'/cache_remote_relative.xml',
+ 'remoteParentUrl' => $remoteUrlRelative,
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ );
+
+ foreach ($tests as $name => $values) {
+ $wsdl = file_get_contents($values['source']);
+ $method->invoke($wd, $wsdl, $values['cacheFile'], $values['remoteParentUrl']);
+ $result = file_get_contents($values['cacheFile']);
+ $this->assertRegExp($values['assertRegExp'], $result, $name);
+ unlink($values['cacheFile']);
+ }
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testResolveXsdIncludes()
+ {
+ $this->startPhpWebserver();
+
+ $curl = new Curl();
+ $wd = new WsdlDownloader($curl);
+
+ $class = new \ReflectionClass($wd);
+ $method = $class->getMethod('resolveRemoteIncludes');
+ $method->setAccessible(true);
+
+ $cacheDir = ini_get('soap.wsdl_cache_dir');
+ if (!is_dir($cacheDir)) {
+ $cacheDir = sys_get_temp_dir();
+ $cacheDirForRegExp = preg_quote($cacheDir);
+ }
+
+ $remoteUrlAbsolute = 'http://localhost:8000/xsdinclude/xsdinctest_absolute.xml';
+ $remoteUrlRelative = 'http://localhost:8000/xsdinclude/xsdinctest_relative.xml';
+ $tests = array(
+ 'localWithAbsolutePath' => array(
+ 'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_absolute.xml',
+ 'cacheFile' => $cacheDir.'/cache_local_absolute.xml',
+ 'remoteParentUrl' => null,
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ 'localWithRelativePath' => array(
+ 'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_relative.xml',
+ 'cacheFile' => $cacheDir.'/cache_local_relative.xml',
+ 'remoteParentUrl' => null,
+ 'assertRegExp' => '~.*\.\./type_include\.xsd.*~',
+ ),
+ 'remoteWithAbsolutePath' => array(
+ 'source' => $remoteUrlAbsolute,
+ 'cacheFile' => $cacheDir.'/cache_remote_absolute.xml',
+ 'remoteParentUrl' => $remoteUrlAbsolute,
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ 'remoteWithAbsolutePath' => array(
+ 'source' => $remoteUrlRelative,
+ 'cacheFile' => $cacheDir.'/cache_remote_relative.xml',
+ 'remoteParentUrl' => $remoteUrlRelative,
+ 'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
+ ),
+ );
+
+ foreach ($tests as $name => $values) {
+ $wsdl = file_get_contents($values['source']);
+ $method->invoke($wd, $wsdl, $values['cacheFile'], $values['remoteParentUrl']);
+ $result = file_get_contents($values['cacheFile']);
+ $this->assertRegExp($values['assertRegExp'], $result, $name);
+ unlink($values['cacheFile']);
+ }
+
+ $this->stopPhpWebserver();
+ }
+
+ public function testResolveRelativePathInUrl()
+ {
+ $curl = new Curl();
+ $wd = new WsdlDownloader($curl);
+
+ $class = new \ReflectionClass($wd);
+ $method = $class->getMethod('resolveRelativePathInUrl');
+ $method->setAccessible(true);
+
+ $this->assertEquals('http://localhost:8080/test', $method->invoke($wd, 'http://localhost:8080/sub', '/test'));
+ $this->assertEquals('http://localhost:8080/test', $method->invoke($wd, 'http://localhost:8080/sub/', '/test'));
+
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub', '/test'));
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/', '/test'));
+
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost', './test'));
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/', './test'));
+
+ $this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub', './test'));
+ $this->assertEquals('http://localhost/sub/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/', './test'));
+
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub', '../test'));
+ $this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/', '../test'));
+
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub', '../../test'));
+ $this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/', '../../test'));
+
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/sub', '../../../test'));
+ $this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/sub/', '../../../test'));
+
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub', '../../../test'));
+ $this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/', '../../../test'));
+ }
+}
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/ServerInterop/MTOM.php b/src/BeSimple/SoapClient/tests/ServerInterop/MTOM.php
new file mode 100644
index 0000000..4f815b9
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/ServerInterop/MTOM.php
@@ -0,0 +1,61 @@
+ SOAP_1_1,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
+ 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM,
+ 'cache_wsdl' => WSDL_CACHE_NONE,
+ 'classmap' => array(
+ 'base64Binary' => 'base64Binary',
+ 'AttachmentRequest' => 'AttachmentRequest',
+ ),
+);
+
+$sc = new BeSimpleSoapClient('MTOM.wsdl', $options);
+
+//var_dump($sc->__getFunctions());
+//var_dump($sc->__getTypes());
+
+try {
+ $b64 = new base64Binary();
+ $b64->_ = 'This is a test. :)';
+ $b64->contentType = 'text/plain';
+
+ $attachment = new AttachmentRequest();
+ $attachment->fileName = 'test123.txt';
+ $attachment->binaryData = $b64;
+
+ var_dump($sc->attachment($attachment));
+
+} catch (Exception $e) {
+ var_dump($e);
+}
+
+// var_dump(
+// $sc->__getLastRequestHeaders(),
+// $sc->__getLastRequest(),
+// $sc->__getLastResponseHeaders(),
+// $sc->__getLastResponse()
+// );
\ No newline at end of file
diff --git a/src/BeSimple/SoapClient/tests/ServerInterop/MTOM.wsdl b/src/BeSimple/SoapClient/tests/ServerInterop/MTOM.wsdl
new file mode 100644
index 0000000..7772015
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/ServerInterop/MTOM.wsdl
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BeSimple/SoapClient/tests/ServerInterop/MTOMServer.php b/src/BeSimple/SoapClient/tests/ServerInterop/MTOMServer.php
new file mode 100644
index 0000000..5deee38
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/ServerInterop/MTOMServer.php
@@ -0,0 +1,53 @@
+ SOAP_1_1,
+ 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
+ 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM,
+ 'cache_wsdl' => WSDL_CACHE_NONE,
+ 'classmap' => array(
+ 'base64Binary' => 'base64Binary',
+ 'AttachmentRequest' => 'AttachmentRequest',
+ ),
+);
+
+class Mtom
+{
+ public function attachment(AttachmentRequest $attachment)
+ {
+ $b64 = $attachment->binaryData;
+
+ file_put_contents('test.txt', var_export(array(
+ $attachment->fileName,
+ $b64->_,
+ $b64->contentType
+ ), true));
+
+ return 'done';
+ }
+}
+
+$ss = new BeSimpleSoapServer('MTOM.wsdl', $options);
+$ss->setClass('Mtom');
+$ss->handle();
diff --git a/src/BeSimple/SoapClient/tests/bootstrap.php b/src/BeSimple/SoapClient/tests/bootstrap.php
new file mode 100644
index 0000000..d2872c6
--- /dev/null
+++ b/src/BeSimple/SoapClient/tests/bootstrap.php
@@ -0,0 +1,36 @@
+
+ * (c) Francis Besset
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+/*
+
+CAUTION: This file installs the dependencies needed to run the BeSimpleSoapClient test suite.
+
+https://github.com/BeSimple/BeSimpleSoapClient
+
+*/
+
+if (!is_dir($vendorDir = dirname(__FILE__).'/vendor')) {
+ mkdir($vendorDir, 0777, true);
+}
+
+$deps = array(
+ array('besimple-soapcommon', 'http://github.com/BeSimple/BeSimpleSoapCommon.git', 'origin/HEAD'),
+ array('XmlSecurity', 'https://github.com/aschamberger/XmlSecurity.git', 'origin/HEAD'),
+);
+
+foreach ($deps as $dep) {
+ list($name, $url, $rev) = $dep;
+
+ echo "> Installing/Updating $name\n";
+
+ $installDir = $vendorDir.'/'.$name;
+ if (!is_dir($installDir)) {
+ system(sprintf('git clone %s %s', escapeshellarg($url), escapeshellarg($installDir)));
+ }
+
+ system(sprintf('cd %s && git fetch origin && git reset --hard %s', escapeshellarg($installDir), escapeshellarg($rev)));
+}