Author: karstene
Date: Wed Dec 5 01:02:54 2012
New Revision: 1417256
URL: http://svn.apache.org/viewvc?rev=1417256&view=rev
Log:
Adding PHP SOAP Client
Added:
chemistry/phpclient/trunk/soap/
chemistry/phpclient/trunk/soap/PccHTML.php
chemistry/phpclient/trunk/soap/README.txt
chemistry/phpclient/trunk/soap/include/
chemistry/phpclient/trunk/soap/include/CMISAlfrescoSoapClient.php
chemistry/phpclient/trunk/soap/include/CMISSoapClient.php
chemistry/phpclient/trunk/soap/include/CMISWebService.php
chemistry/phpclient/trunk/soap/include/cmisTypeDefinitions.php
chemistry/phpclient/trunk/soap/include/dialog.js
chemistry/phpclient/trunk/soap/include/pcc.css
chemistry/phpclient/trunk/soap/index.php
Added: chemistry/phpclient/trunk/soap/PccHTML.php
URL:
http://svn.apache.org/viewvc/chemistry/phpclient/trunk/soap/PccHTML.php?rev=1417256&view=auto
==============================================================================
--- chemistry/phpclient/trunk/soap/PccHTML.php (added)
+++ chemistry/phpclient/trunk/soap/PccHTML.php Wed Dec 5 01:02:54 2012
@@ -0,0 +1,277 @@
+<?php
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+/**
+ * PccHTML - HTML definitions for PHP CMIS Client
+ *
+ * @author Karsten Eberding
+ */
+
+class PccHTML {
+
+// Simple template mechanism for HTML code generation, {} will be replaced:
+// {$x} will be replaced by parameter x (1 - 9)
+// {$template} will be replaced by content of template "template"
+// {content} will be replaced by the eval result, e.g.
{urlencode('{@cmis:objectId}')}
+// recursive brackets are allowed
+
+ private static $templates = array(
+ // HTML Code sniplets
+ 'folderUrl' => '<a href="index.php?folderid={$1}">{$2}</a>',
+ 'folderListHead' => '<tr><th>Folder Name</th><th></th>',
+ 'folderListEntry' => "<tr><td>{\$folderUrl}</td><td></td></tr>\n",
+ 'docListHead' => '<tr><th>Document Name</th><th>Last Modification
Date</th><th>Actions</th>',
+ 'docListEntry' => '<tr><td><a
href="index.php?action=download&objectid={$1}">{$2}</a></td><td>{$3}</td><td
align="right">{$4}</td></tr>',
+ 'docListEmpty' => "<tr><td>No documents in this folder</td></tr>\n",
+ 'pageLink' => ' <a
href="index.php?folderid={$1}&page={$2}">{$2}</a> ',
+ 'pageCurrent' => ' <b>{$1}</b> ',
+ 'pageList' => '<div class="paging">{$1}</div>',
+ 'downloadFrame' => '<iframe width="1" height="1" frameborder="0"
src="index.php?action=download&objectid={$1}"></iframe>',
+ 'uploadAction' => '<a href="#" onclick="showModal(\'uploadFile\',
\'{$1}\', \'{$2}\')">Upload</a> ',
+ 'cancelCOAction' => '<a href="#" onclick="showModal(\'cancelCO\',
\'{$1}\', \'{$2}\')">CancelCO</a> ',
+ 'deleteAction' => '<a href="#" onclick="showModal(\'delete\',
\'{$1}\', \'{$2}\')">Delete</a> ',
+ 'checkinAction' => '<a href="#" onclick="showModal(\'checkin\',
\'{$1}\', \'{$2}\')">Check-In</a> ',
+ 'checkoutAction' => '<a href="#" onclick="showModal(\'checkout\',
\'{$1}\', \'{$2}\')">Check-Out</a> ',
+ 'detailsAction' => '<a
href="index.php?action=details&objectid={$1}">More</a> ',
+ 'createFolderBtn' => '<input type="button" value="Create Folder"
onclick="showModal(\'createFolder\', \'{$1}\')">',
+ 'createDocumentBtn' => '<input type="button" value="Create Document"
onclick="showModal(\'createDocument\', \'{$1}\')">',
+
+ // Dialogs
+ 'loginForm' => <<<EOF
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
+<html lang='en_us'>
+ <head>
+ <title>Login to PHP CMIS Client</title>
+ </head>
+ <body>
+ <form action="index.php" method="post">
+ Username: <input type="text" name="username" /><br />
+ Passwort: <input type="password" name="password" /><br />
+ <input type="hidden" name="action" value="login">
+ <input type="submit" value="Login">
+ </form>
+ </body>
+</html>
+EOF
+, 'modalDialog' => <<<EOF
+ <div id="{\$1}" class="modalContainer">
+ <div class="modalBackground"></div>
+ <div class="modalWindow">
+ <div class="modalDialog">
+ <div class="dialoghead">{\$2}</div>
+ <div class="dialogbody">
+ {\$3}
+ </div>
+ </div>
+ </div>
+ </div>
+EOF
+, 'uploadFileDialog' => <<<EOF
+ Select file to upload for document<div class="dialognotice"></div>
+ <form id="{\$1}Form" method="POST"
ENCTYPE="multipart/form-data" action="index.php">
+ <table><tbody><tr>
+ <td>File Name:</td><td>
+ <input type="file" name="filename" size="30" value="">
+ </td></tr><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action" value="upload">
+ <input type="submit" value="Upload">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+, 'cancelCODialog' => <<<EOF
+ Confirm Cancel Check-out for document<div
class="dialognotice"></div>Unsaved changes will be lost!
+ <form id="{\$1}Form" method="GET" action="index.php">
+ <table><tbody><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action" value="cancelco">
+ <input type="submit" value="Cancel
Check-Out">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+, 'deleteDialog' => <<<EOF
+ Confirm Deletion of All versions of document<div
class="dialognotice"></div>
+ <form id="{\$1}Form" method="GET" action="index.php">
+ <table><tbody><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action" value="delete">
+ <input type="submit" value="Delete">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+, 'checkoutDialog' => <<<EOF
+ Check-out document<div class="dialognotice"></div>Download will start
automatically.
+ <form id="{\$1}Form" method="GET" action="index.php">
+ <table><tbody><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action" value="checkout">
+ <input type="submit" value="Check-out and
Download">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+, 'checkinDialog' => <<<EOF
+ Select file to upload for check-in of<div
class="dialognotice"></div>(leave file name empty to check-in without uploading
a file)
+ <form id="{\$1}Form" method="POST"
ENCTYPE="multipart/form-data" action="index.php">
+ <table><tbody><tr>
+ <td>File Name:</td><td>
+ <input type="file" name="filename" size="30"
value=""></td>
+ </tr><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action" value="checkin">
+ <input type="submit"
value="Check-In">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+, 'createFolderDialog' => <<<EOF
+ Create new folder in current folder<div class="dialognotice"></div>
+ <form id="{\$1}Form" method="GET" action="index.php">
+ <table><tbody><tr>
+ <td>Folder Name:</td><td>
+ <input type="text" name="createfoldername" size="30"
value="">
+ </td></tr><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action"
value="createfolder">
+ <input type="submit" value="Create">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+, 'createDocumentDialog' => <<<EOF
+Create new document in current folder<div class="dialognotice"></div>(leave
file name empty to create a document without content)
+ <form id="{\$1}Form" method="POST"
ENCTYPE="multipart/form-data" action="index.php">
+ <table><tbody><tr>
+ <td>File Name:</td><td>
+ <input type="file" name="filename" size="30" value=""
+
onchange="document.getElementById('createfilename').value=this.value.replace(/^.*(\\\\|\/)/,'')"></td>
+ </tr><tr>
+ <td>Document Name:</td><td>
+ <input type="text" name="createfilename"
id="createfilename" size="30" value=""></td>
+ </tr><tr><td></td><td style="padding-top: 20px;">
+ <input type="hidden" name="action"
value="createdocument">
+ <input type="submit" value="Create">
+ <input type="reset" value="Cancel"
onclick="hideModal('{\$1}');">
+ </td></tr></tbody></table>
+ </form>
+EOF
+,
+ // Page elements
+ 'htmlHeader' => <<<EOF
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
+<html lang='en_us'>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta http-equiv="CACHE-CONTROL" CONTENT="NO-CACHE">
+ <script src="include/dialog.js" type="text/javascript"></script>
+ <title>PHP CMIS Client</title>
+ <link href="include/pcc.css" rel="stylesheet" type="text/css">
+ </head>
+ <body>
+EOF
+, 'htmlFooter' => <<<EOF
+ </body>
+</html>
+EOF
+, 'pageHeader' => <<<EOF
+
+ <div class="header">
+ <div class="titleContainer">
+ <div class="moduleTitle">
+ PHP CMIS Client
+ </div>
+ <div class="moduleSubTitle">
+ (c) 2012 Karsten Eberding
+ </div>
+ <div class="logoutTitle">
+ <a href="index.php?action=logout">Logout</a>
+ </div>
+ </div>
+ {\$1}
+ <div class="headerText">
+ Content in Folder {\$2}
+ </div>
+ <form id="headButtons">
+ {\$3}
+ </form>
+ </div>
+EOF
+, 'folderList' => <<<EOF
+ <div class="folderList">
+ <table>
+ <thead>
+ {\$folderListHead}
+ </thead>
+ <tbody>
+ {\$1}
+ </tbody>
+ </table>
+ </div>
+EOF
+, 'documentList' => <<<EOF
+ <div class="documentList">
+ <table>
+ <thead>
+ {\$1}
+ </thead>
+ <tbody>
+ {\$2}
+ </tbody>
+ </table>
+ {\$3}
+ </div>
+EOF
+ );
+
+ function PccLayout() {
+ // nothing to do
+ }
+
+ /* Apply Template function, variable parameter list */
+
+ public function applyTemplate($templateName) {
+ $param = func_get_args();
+ $template = self::$templates[$templateName];
+ return $this->applyTemplateString($template, $param);
+ }
+
+ public function applyTemplateString($template, $param) {
+ // small trick to make class variables available in callback function
+ $templates = self::$templates;
+ $self = $this;
+ $callback = // Start of anonymous callback function
+ function ($match) use ($param, $templates, $self) {
+ if ($match[1] == '$') {
+ if (is_numeric($match[2])) { // Numeric, use
parameter $x
+ $return = (string) $param[$match[2]];
+ } else { // non-numeric, apply
template again
+ $return = $templates[$match[2]];
+ if (strpos($return, '{') !== false) {
+ $return = $self->applyTemplateString($return,
$param);
+ }
+ }
+ } else { // does not start with $, eval result
+ $return = $self->applyTemplateString($match[2]);
+ $return = eval("return $return;");
+ }
+ return $return;
+ }; // End of anonymous callback function
+
+ $return = preg_replace_callback('/\{([$@]?)(((?>[^\{\}]+)|(?R))*)\}/',
$callback, $template);
+ return $return;
+ }
+}
+
+?>
Added: chemistry/phpclient/trunk/soap/README.txt
URL:
http://svn.apache.org/viewvc/chemistry/phpclient/trunk/soap/README.txt?rev=1417256&view=auto
==============================================================================
--- chemistry/phpclient/trunk/soap/README.txt (added)
+++ chemistry/phpclient/trunk/soap/README.txt Wed Dec 5 01:02:54 2012
@@ -0,0 +1,35 @@
+README for PHP CMIS Client
+
+The PHP CMIS Soap Client consists of the following files:
+
+1) CMIS Web Service Library
+
+CMISWebService.php - the main class to be included into an application
+cmisTypeDefinitions.php - the CMIS object model, used by CMISWebService.php
+CMISSoapClient.php - extends the PHP SoapClient class, used by
CMISWebService.php
+CMISAlfrescoSoapClient.php - an Alfresco specific version, includes ticket
support
+
+2) PHP CMIS Client
+
+index.php - the main file, includes all CMIS calls and program logic.
Configuration
+ parameters, including URL for service endpoints, is configured here.
+PccHTML.php - includes all HTML presentation code, no calls to CMIS
+pcc.css - CSS file, included from PccHTML.php
+dialog.js - Java Script for dialog handling, included from PccHTML.php
+
+The PHP CMIS Client is a sample application to show the use of the CMIS
library.
+Main features include folder and document list, checkin, checkout, upload,
+download, cancel checkout and delete.
+
+All CMIS functions and the program logic are included into the main file
index.php.
+Changes to the HTML presentation can be done independently in PccHTML.php.
+Jacascipt is used for dialog handling, to avoid page reloads for a dialog.
+A simple templating mechanism is included, to generate the HTML output.
+
+This software is presented to the Apache Chemistry project. Licensing will
+be changed to ASF if accepted by the Chemistry project.
+
+Contact information:
+
+Karsten Eberding
[email protected]
\ No newline at end of file
Added: chemistry/phpclient/trunk/soap/include/CMISAlfrescoSoapClient.php
URL:
http://svn.apache.org/viewvc/chemistry/phpclient/trunk/soap/include/CMISAlfrescoSoapClient.php?rev=1417256&view=auto
==============================================================================
--- chemistry/phpclient/trunk/soap/include/CMISAlfrescoSoapClient.php (added)
+++ chemistry/phpclient/trunk/soap/include/CMISAlfrescoSoapClient.php Wed Dec
5 01:02:54 2012
@@ -0,0 +1,217 @@
+<?php
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+/**
+ * CMISAlfrescoSoapClient extends the standard SoapClient class to include
message handling for CMIS for Alfresco:
+ * 1) include WebService Security (WSS) headers as required by CMIS (this part
is taken from Alfresco)
+ * 2) handle multipart response messages from the repository
+ * 3) handle Alfresco tickets
+ *
+ * @author Karsten Eberding, based on sample code from Alfresco
+ */
+class CMISAlfrescoSoapClient extends SoapClient {
+
+ private static $securityExtNS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
+ private static $wsUtilityNS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
+ private static $passwordType =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
+
+ private static $myUsername;
+ private static $myPassword;
+ private static $ticket;
+
+ public function __construct($wsdl, $options) {
+
+ $wsdlcache = $this->get_wsdl($wsdl);
+
+ // Call the base class
+ parent::__construct($wsdlcache, $options);
+ }
+
+ public function setCredentials($url, $username, $password) {
+
+ // Store the current username, password, ticket
+ self::$myUsername = $username;
+ self::$myPassword = $password;
+
+ if (isset($_SESSION['AlfrescoTicket'])) {
+ $ticket = $_SESSION['AlfrescoTicket'];
+ return true;
+ }
+
+ // get url for ticket service
+ $u = urlencode($username);
+ $pw = urlencode($password);
+ $url = parse_url($url);
+ $path = preg_replace('#/[^/]+/?$#', '/', $url['path']); // remove last
path element
+ $url = (isset($url['scheme']) ? $url['scheme'] : 'http') . '://' .
$url['host'] .
+ (isset($url['port']) ? ':'.$url['port'] : '') .
"{$path}service/api/login?u=$u&pw=$pw";
+
+ // get ticket
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+
+ $ticketxml = curl_exec($ch);
+ curl_close($ch);
+ $dom = new DOMDocument("1.0");
+ $dom->loadXML($ticketxml);
+ $ticket = $dom->getElementsByTagName('ticket')->item(0)->nodeValue;
+
+ if (isset($ticket)) {
+ self::$ticket = $ticket; // save ticket for
subsequent calls
+ $_SESSION['AlfrescoTicket'] = $ticket; // save ticket for
subsequent page requests
+ return true; // login success
+ }
+ return false; // login failure
+ }
+
+ public function clearCredentials() {
+ // Clear the current username, password, ticket
+ self::$myUsername = '';
+ self::$myPassword = '';
+ self::$ticket = '';
+ $_SESSION['AlfrescoTicket'] = '';
+ }
+
+ public function __call($function_name, $arguments = array()) {
+ return $this->__soapCall($function_name, $arguments);
+ }
+
+ public function __soapCall($function_name, $arguments = array(), $options
= array(), $input_headers = array(), $output_headers = array()) {
+
+ // Automatically add a security header
+ $input_headers[] = new SoapHeader(self::$securityExtNS, "Security",
null, 1);
+
+ $response = parent::__soapCall($function_name, $arguments, $options,
$input_headers, $output_headers);
+
+ return $response;
+ }
+
+ public function __doRequest($request, $location, $action, $version) {
+ // If this request requires authentication we have to manually
construct the
+ // security headers.
+ $dom = new DOMDocument("1.0");
+ $dom->loadXML($request);
+
+ $securityHeader = $dom->getElementsByTagName("Security");
+
+ if ($securityHeader->length != 1) {
+ throw new Exception("Expected length: 1, Received: " .
$securityHeader->length . ". No Security Header, or more than one element
called Security!");
+ }
+
+ $securityHeader = $securityHeader->item(0);
+
+ // Construct Timestamp Header
+ $timeStamp = $dom->createElementNS(self::$wsUtilityNS, "Timestamp");
+ $createdDate = date("Y-m-d\TH:i:s\Z", mktime(date("H"), date("i"),
date("s"), date("m"), date("d"), date("Y")));
+ $expiresDate = date("Y-m-d\TH:i:s\Z", mktime(date("H") + 24,
date("i"), date("s"), date("m"), date("d"), date("Y")));
+ $created = new DOMElement("Created", $createdDate, self::$wsUtilityNS);
+ $expires = new DOMElement("Expires", $expiresDate, self::$wsUtilityNS);
+ $timeStamp->appendChild($created);
+ $timeStamp->appendChild($expires);
+
+ // Construct UsernameToken Header
+ if (isset(self::$ticket) && self::$ticket != '') {
+ $username = '';
+ $password = self::$ticket;
+ } else {
+ $username = self::$myUsername;
+ $password = self::$myPassword;
+ }
+ $userNameToken = $dom->createElementNS(self::$securityExtNS,
"UsernameToken");
+ $userName = new DOMElement("Username", $username,
self::$securityExtNS);
+
+ $passWord = $dom->createElementNS(self::$securityExtNS, "Password");
+ $typeAttr = new DOMAttr("Type", self::$passwordType);
+ $passWord->appendChild($typeAttr);
+
+ $passWord->appendChild($dom->createTextNode($password));
+ $userNameToken->appendChild($userName);
+ $userNameToken->appendChild($passWord);
+
+ // Construct Security Header
+ $securityHeader->appendChild($timeStamp);
+ $securityHeader->appendChild($userNameToken);
+
+ // Save the XML Request
+ $request = $dom->saveXML();
+
+ $response = parent::__doRequest($request, $location, $action,
$version);
+ $responseHeader = $this->__getLastResponseHeaders();
+
+ $boundary = array();
+ $start = array();
+ $contentId = array();
+ $attachment = array();
+ $match = array();
+ if (preg_match('/content-type: multipart\/related;/i',
$responseHeader) === 1 &&
+ preg_match('/boundary="(.*?)";/i', $responseHeader, $boundary)
=== 1 &&
+ preg_match('/start="<(.*?)>";/i', $responseHeader, $start)) {
+ $parts = explode("\r\n--" . $boundary[1], $response);
+
+ foreach ($parts as $part) {
+ //if ($part == '' || $part == '--') continue;
+ $part = explode("\r\n\r\n", $part, 2); // array of part header
and content
+ if (count($part) < 2)
+ continue;
+ if (preg_match('/content-id:\s*<(.*?)>/i', $part[0],
$contentId) === 1) {
+ if ($contentId[1] == $start[1]) {
+ $response = $part[1];
+ } else {
+ $attachment[$contentId[1]] = $part[1];
+ //$attachment[] = $part[1];
+ }
+ }
+ }
+ if (count($attachment) > 0) {
+ if (preg_match('/<xop:include .*?href="cid:(.*?)".*?>/i',
$response, $match)) {
+ $response = str_replace($match[0],
base64_encode($attachment[$match[1]]), $response);
+ }
+ }
+ }
+
+ return $response;
+ }
+
+ private function get_wsdl($url) {
+ $cache_file = "/tmp/soap.wsdl." . md5($url);
+
+ //only fetch a new wsdl every hour
+ if (!file_exists($cache_file) || filectime($cache_file) < time() -
3600) {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+
+ $wsdldata = curl_exec($ch);
+ curl_close($ch);
+
+ $wsdldata = str_replace('anySimpleType', 'string', $wsdldata);
+
+ file_put_contents($cache_file, $wsdldata);
+ if (!file_exists($cache_file)) {
+ throw new Exception("Couldn't load WSDL at {$url}");
+ }
+ }
+ return $cache_file;
+ }
+
+}
+
+?>
Added: chemistry/phpclient/trunk/soap/include/CMISSoapClient.php
URL:
http://svn.apache.org/viewvc/chemistry/phpclient/trunk/soap/include/CMISSoapClient.php?rev=1417256&view=auto
==============================================================================
--- chemistry/phpclient/trunk/soap/include/CMISSoapClient.php (added)
+++ chemistry/phpclient/trunk/soap/include/CMISSoapClient.php Wed Dec 5
01:02:54 2012
@@ -0,0 +1,170 @@
+<?php
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+/**
+ * CMISSoapClient extends the standard SoapClient class to include message
handling for CMIS:
+ * 1) include WebService Security (WSS) headers as required by CMIS (this part
is taken from Alfresco)
+ * 2) handle multipart response messages from the repository
+ *
+ * @author Karsten Eberding, based on sample code from Alfresco
+ */
+class CMISSoapClient extends SoapClient {
+
+ private static $securityExtNS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
+ private static $wsUtilityNS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
+ private static $passwordType =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
+
+ private static $myUsername;
+ private static $myPassword;
+
+ public function __construct($wsdl, $options) {
+
+ $wsdlcache = $this->get_wsdl($wsdl);
+
+ // Call the base class
+ parent::__construct($wsdlcache, $options);
+ }
+
+ public function setCredentials($url, $username, $password) {
+
+ // Store the current username, password, ticket
+ self::$myUsername = $username;
+ self::$myPassword = $password;
+
+ return true; // login success
+ }
+
+ public function clearCredentials() {
+ // Clear the current username, password, ticket
+ self::$myUsername = '';
+ self::$myPassword = '';
+ }
+
+ public function __call($function_name, $arguments = array()) {
+ return $this->__soapCall($function_name, $arguments);
+ }
+
+ public function __soapCall($function_name, $arguments = array(), $options
= array(), $input_headers = array(), $output_headers = array()) {
+ // Automatically add a security header
+ $input_headers[] = new SoapHeader(self::$securityExtNS, "Security",
null, 1);
+
+ $response = parent::__soapCall($function_name, $arguments, $options,
$input_headers, $output_headers);
+
+ return $response;
+ }
+
+ public function __doRequest($request, $location, $action, $version) {
+ // If this request requires authentication we have to manually
construct the
+ // security headers.
+ $dom = new DOMDocument("1.0");
+ $dom->loadXML($request);
+
+ $securityHeader = $dom->getElementsByTagName("Security");
+
+ if ($securityHeader->length != 1) {
+ throw new Exception("Expected length: 1, Received: " .
$securityHeader->length . ". No Security Header, or more than one element
called Security!");
+ }
+
+ $securityHeader = $securityHeader->item(0);
+
+ // Construct Timestamp Header
+ $timeStamp = $dom->createElementNS(self::$wsUtilityNS, "Timestamp");
+ $createdDate = date("Y-m-d\TH:i:s\Z", mktime(date("H"), date("i"),
date("s"), date("m"), date("d"), date("Y")));
+ $expiresDate = date("Y-m-d\TH:i:s\Z", mktime(date("H") + 24,
date("i"), date("s"), date("m"), date("d"), date("Y")));
+ $created = new DOMElement("Created", $createdDate, self::$wsUtilityNS);
+ $expires = new DOMElement("Expires", $expiresDate, self::$wsUtilityNS);
+ $timeStamp->appendChild($created);
+ $timeStamp->appendChild($expires);
+
+ // Construct UsernameToken Header
+ $userNameToken = $dom->createElementNS(self::$securityExtNS,
"UsernameToken");
+ $userName = new DOMElement("Username", self::$myUsername,
self::$securityExtNS);
+ $passWord = $dom->createElementNS(self::$securityExtNS, "Password");
+ $typeAttr = new DOMAttr("Type", self::$passwordType);
+ $passWord->appendChild($typeAttr);
+ $passWord->appendChild($dom->createTextNode(self::$myPassword));
+ $userNameToken->appendChild($userName);
+ $userNameToken->appendChild($passWord);
+
+ // Construct Security Header
+ $securityHeader->appendChild($timeStamp);
+ $securityHeader->appendChild($userNameToken);
+
+ // Save the XML Request
+ $request = $dom->saveXML();
+
+ $response = parent::__doRequest($request, $location, $action,
$version);
+
+ $responseHeader = $this->__getLastResponseHeaders();
+
+ $boundary = array();
+ $start = array();
+ $contentId = array();
+ $attachment = array();
+ $match = array();
+ if (preg_match('/content-type: multipart\/related;/i',
$responseHeader) === 1 &&
+ preg_match('/boundary="(.*?)";/i', $responseHeader, $boundary)
=== 1 &&
+ preg_match('/start="<(.*?)>";/i', $responseHeader, $start)) {
+ $parts = explode("\r\n--" . $boundary[1], $response);
+
+ foreach ($parts as $part) {
+ $part = explode("\r\n\r\n", $part, 2); // array of part header
and content
+ if (count($part) < 2)
+ continue;
+ if (preg_match('/content-id:\s*<(.*?)>/i', $part[0],
$contentId) === 1) {
+ if ($contentId[1] == $start[1]) {
+ $response = $part[1];
+ } else {
+ $attachment[$contentId[1]] = $part[1];
+ }
+ }
+ }
+ if (count($attachment) > 0) {
+ if (preg_match('/<xop:include .*?href="cid:(.*?)".*?>/i',
$response, $match)) {
+ $response = str_replace($match[0],
base64_encode($attachment[$match[1]]), $response);
+ }
+ }
+ }
+
+ return $response;
+ }
+
+ private function get_wsdl($url) {
+ $cache_file = "/tmp/soap.wsdl." . md5($url);
+
+ //only fetch a new wsdl every hour
+ if (!file_exists($cache_file) || filectime($cache_file) < time() -
3600) {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+
+ $wsdldata = curl_exec($ch);
+ curl_close($ch);
+
+ $wsdldata = str_replace('anySimpleType', 'string', $wsdldata);
+
+ file_put_contents($cache_file, $wsdldata);
+ if (!file_exists($cache_file)) {
+ throw new Exception("Couldn't load WSDL at {$url}");
+ }
+ }
+ return $cache_file;
+ }
+}
+?>