Index: branches/feature-module-update/data/module/SOAP/package.xml
===================================================================
--- branches/feature-module-update/data/module/SOAP/package.xml	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/package.xml	(revision 16957)
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.6.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+ <name>SOAP</name>
+ <channel>pear.php.net</channel>
+ <summary>SOAP Client/Server for PHP</summary>
+ <description>Implementation of SOAP protocol and services</description>
+ <lead>
+  <name>Jan Schneider</name>
+  <user>yunosh</user>
+  <email>jan@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <lead>
+  <name>Chuck Hagenbuch</name>
+  <user>chagenbu</user>
+  <email>chuck@horde.org</email>
+  <active>no</active>
+ </lead>
+ <lead>
+  <name>Shane Caraveo</name>
+  <user>shane</user>
+  <email>shane@php.net</email>
+  <active>no</active>
+ </lead>
+ <lead>
+  <name>Al Baker</name>
+  <user>abaker</user>
+  <email>abaker@php.net</email>
+  <active>no</active>
+ </lead>
+ <lead>
+  <name>Arnaud Limbourg</name>
+  <user>arnaud</user>
+  <email>arnaud@php.net</email>
+  <active>no</active>
+ </lead>
+ <date>2007-06-29</date>
+ <time>17:11:23</time>
+ <version>
+  <release>0.11.0</release>
+  <api>0.11.0</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.php.net/license">PHP License</license>
+ <notes>* Remove SOAP_RAW_CONVERT and SOAP_OBJECT_STRUCT settings.
+* Don&apos;t decode base64 encoded data automatically to be consistent with other encodings like hexBinary that aren&apos;t decoded either (Bug #9840).
+* Always close HTTP connections on requests to avoid keep-alive request (Maik Greubel, Bug #11123).
+* Fix serializing of arrays (Bugs #10283, #10635).
+* Fix MIME type quoting when using MIME attachments (Bug #10023).
+* Fix values of multiple arrays in result object being dismissed on SOAP to PHP-object conversion (Christian Weiske, Bug #2627).
+* Fix wrong Content-Type header on HTTPS requests (Christian Weiske, Bug #6213).
+* Fix empty arrays in SOAP responses being converted to empty string instead of empty array (Christian Weiske, Bug #10131).
+* Fix timezones in Type/dateTime.php not being converted correctly (Christian Weiske, Bug #10206).</notes>
+ <contents>
+  <dir baseinstalldir="SOAP" name="/">
+   <file baseinstalldir="SOAP" md5sum="9559e379c3becea8aeee54b914423854" name="example/attachment.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="b020797e29e8e64376c0c07ef75e0213" name="example/client.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="fc8a7de5818fc0c90793fde12ca0ca63" name="example/com_client.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="2b413f1e68aab19fbb75fdc5f8b9c0e8" name="example/disco_server.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="a9c530db7dd292c74c4e52d74b703e4e" name="example/email_client.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="9c5f82116808ff9bf712dd1e8b1926ad" name="example/email_gateway.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="bd8bdced809cfce44a6a2b3c2fd0e7ee" name="example/email_pop_gateway.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="feb3535b72a820095e71794784bef51a" name="example/email_pop_server.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="3c3b91a5833b265803efc1f13be8bb25" name="example/email_server.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="5e0a9c93d55bab8ca49a3618b7745e99" name="example/example_server.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="6b209f62024135e5939a34d6c08f3c14" name="example/example_types.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="0c5fe107da15351c51d458b4d11704cc" name="example/server.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="f43e9c93d8f3dcc4c8db335add85052c" name="example/server2.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="dad27403ba186d6f28588b690b695d34" name="example/smtp.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="58d8c6715bc962a7cb2f6485a38a6294" name="example/stockquote.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="9170adaf7ac611d5592636d1cb3ba020" name="example/tcp_client.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="ccc9acb5b161dd84ccec6d19a5579b59" name="example/tcp_daemon.pl" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="e5d8dc13139bcb5951aad270b37dd515" name="example/tcp_server.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="33c0068eaa6f21f209b8c93525824642" name="example/wsdl_client.php" role="doc" />
+   <file baseinstalldir="SOAP" md5sum="43b5245ae178f28c3dc78f3cff584299" name="Server/Email.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="506481286f6a94aaa6d592249bb6fe4e" name="Server/Email_Gateway.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="b8424ce597eb3eaee849a1ae951a36c0" name="Server/TCP.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="1c525226171b9b99966b4ed3eb7373f8" name="tools/genproxy.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="bacf397ba0e1569f542b72cb7c152cc3" name="Transport/HTTP.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="5a20d622c55bbf2e6304abb93bed34c7" name="Transport/SMTP.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="18b2224fe5c4ca3bc679d0d5d2601fe7" name="Transport/TCP.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="75bf753d4cceb4a16a9d0c40ee3f6dcf" name="Type/dateTime.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="57f91357f69e236ada838a2c7de35c70" name="Type/duration.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="16796001ac468d58c537dde94113bf12" name="Type/hexBinary.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="de2892f3c51856a37372127e9fd14617" name="Base.php" role="php">
+    <tasks:replace from="@version@" to="version" type="package-info" />
+   </file>
+   <file baseinstalldir="SOAP" md5sum="75299ff975d014ff037cac43d97d0796" name="Client.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="bca057623598956b278b4077571f7805" name="Disco.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="37309521cf2ec329d8ccfe6644ae8d8e" name="Fault.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="19bdf70d5f921b8cc85c8063a5541c43" name="Parser.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="27e6e319d92d9fa0aa1d1432f7f16c5d" name="Server.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="fa7376f1c015da685a6534f9cd483d6c" name="Transport.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="d3cfdea9ee3d97736fe3c881bc498f2d" name="Value.php" role="php" />
+   <file baseinstalldir="SOAP" md5sum="62a9ddff55d489dc2943d35a47421e6d" name="WSDL.php" role="php" />
+  </dir>
+ </contents>
+ <compatible>
+  <name>Services_Weather</name>
+  <channel>pear.php.net</channel>
+  <min>1.4.0</min>
+  <max>1.4.0</max>
+ </compatible>
+ <dependencies>
+  <required>
+   <php>
+    <min>4.3</min>
+   </php>
+   <pearinstaller>
+    <min>1.4.0b1</min>
+   </pearinstaller>
+   <package>
+    <name>PEAR</name>
+    <channel>pear.php.net</channel>
+   </package>
+   <package>
+    <name>HTTP_Request</name>
+    <channel>pear.php.net</channel>
+   </package>
+  </required>
+  <optional>
+   <package>
+    <name>Mail</name>
+    <channel>pear.php.net</channel>
+   </package>
+   <package>
+    <name>Mail_Mime</name>
+    <channel>pear.php.net</channel>
+   </package>
+   <package>
+    <name>Net_DIME</name>
+    <channel>pear.php.net</channel>
+   </package>
+  </optional>
+ </dependencies>
+ <phprelease />
+</package>
Index: branches/feature-module-update/data/module/SOAP/example/tcp_daemon.pl
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/tcp_daemon.pl	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/tcp_daemon.pl	(revision 16957)
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+use strict;
+
+=head1 Simple SOAP TCP Server
+
+    Simple SOAP TCP Server with test class (just to check things are working)
+
+    Before you run this test server you will need some perl classes, namely:
+        SOAP::Lite
+        Hook::LexWrap (if you want to see some debugging)
+
+        Flee off to the CPAN if you need them :)
+
+    To run type 'perl <filename>' and if you dont get any errors, it's time
+    to go write some code to connect.
+=cut
+
+use SOAP::Transport::TCP qw(trace);
+use Data::Dumper;
+
+
+#############
+## if you want to see incoming/outgoing raw xml
+## uncomment the following.
+
+#use Hook::LexWrap;
+#wrap *IO::SessionData::read, post => \&show_read;
+#wrap *IO::SessionData::write, post => \&show_write;
+
+##
+#############
+
+
+my $daemon = SOAP::Transport::TCP::Server->new(
+    LocalAddr => '127.0.0.1',
+    LocalPort => '82',
+    Listen    => 5,
+    Reuse     => 1
+);
+
+# dispatch
+$daemon->dispatch_to('SOAP_Example_Server');
+$daemon->handle;
+
+#############
+## callback functions for Hook::LexWrap;
+##
+
+# show incoming xml
+sub show_read {
+    print $/,'## read ##',$/;
+    print Dumper($_[0]);
+}
+
+# show outgoing xml
+sub show_write {
+    print $/,'## write ##',$/;
+    print Dumper($_[0]);
+}
+################################################################################
+
+
+
+################################################################################
+# SOAP_Example_Server
+# Simple test class, method test returns double what you send to it, thats all!
+################################################################################
+package SOAP_Example_Server;
+
+sub echoString {
+    return $_[1] x 2;
+}
+
+1;
Index: branches/feature-module-update/data/module/SOAP/example/stockquote.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/stockquote.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/stockquote.php	(revision 16957)
@@ -0,0 +1,55 @@
+<html><body>
+<?
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
+// | Authors: Dietrich Ayala <dietrich@ganx4.com> Original Author         |
+// +----------------------------------------------------------------------+
+//
+// $Id: stockquote.php,v 1.7 2005/03/10 23:16:40 yunosh Exp $
+//
+// include soap client class
+include("SOAP/Client.php");
+
+print "<br>\n<strong>wsdl:</strong>";
+$wsdl = new SOAP_WSDL("http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl");
+$soapclient = $wsdl->getProxy();
+print_r($soapclient->getQuote("ibm"));
+print "\n\n";
+
+if (extension_loaded('overload')) {
+	print "\n<br><strong>overloaded:</strong>";
+	$ret = $soapclient->getQuote("ibm");
+	print_r($ret);
+	print "\n\n";
+}
+unset($soapclient);
+
+print "\n<br><strong>non wsdl:</strong>";
+$soapclient = new SOAP_Client("http://services.xmethods.net:80/soap");
+$namespace = "urn:xmethods-delayed-quotes";
+/**
+ * some soap servers require a Soapaction http header.  PEAR::SOAP does
+ * not use them in any way, other to send them if you supply them.
+ * soapaction is deprecated in later SOAP versions.
+ */
+$soapaction = "urn:xmethods-delayed-quotes#getQuote";
+$ret = $soapclient->call("getQuote",array("symbol"=>"ibm"),$namespace,$soapaction);
+print_r($ret);
+print "\n\n";
+unset($soapclient);
+
+?>
+</html></body>
Index: branches/feature-module-update/data/module/SOAP/example/server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/server.php	(revision 16957)
@@ -0,0 +1,49 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: server.php,v 1.10 2005/03/10 23:16:40 yunosh Exp $
+//
+
+// first, include the SOAP/Server class
+require_once 'SOAP/Server.php';
+
+$server = new SOAP_Server;
+/* tell server to translate to classes we provide if possible */
+$server->_auto_translation = true;
+
+require_once 'example_server.php';
+
+$soapclass = new SOAP_Example_Server();
+$server->addObjectMap($soapclass,'urn:SOAP_Example_Server');
+
+if (isset($_SERVER['REQUEST_METHOD']) &&
+    $_SERVER['REQUEST_METHOD']=='POST') {
+    $server->service($HTTP_RAW_POST_DATA);
+} else {
+    require_once 'SOAP/Disco.php';
+    $disco = new SOAP_DISCO_Server($server,'ServerExample');
+    header("Content-type: text/xml");
+    if (isset($_SERVER['QUERY_STRING']) &&
+       strcasecmp($_SERVER['QUERY_STRING'],'wsdl')==0) {
+        echo $disco->getWSDL();
+    } else {
+        echo $disco->getDISCO();
+    }
+    exit;
+}
+?>
Index: branches/feature-module-update/data/module/SOAP/example/wsdl_client.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/wsdl_client.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/wsdl_client.php	(revision 16957)
@@ -0,0 +1,83 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: wsdl_client.php,v 1.5 2007/01/22 11:33:27 yunosh Exp $
+//
+include("SOAP/Client.php");
+
+/**
+ * this client runs against the example server in SOAP/example/server.php
+ * it does not use WSDL to run these requests, but that can be changed easily by simply
+ * adding '?wsdl' to the end of the url.
+ */
+$wsdl = new SOAP_WSDL("http://localhost/SOAP/example/server.php?wsdl");
+$soapclient = $wsdl->getProxy();
+
+$ret = $soapclient->echoStringSimple("this is a test string");
+//print $soapclient->getWire();
+print_r($ret);echo "<br>\n";
+
+$ret = $soapclient->echoString("this is a test string");
+print_r($ret);echo "<br>\n";
+
+$ret = $soapclient->divide(22,7);
+// print $soapclient->getWire();
+if (PEAR::isError($ret))
+    print("Error: " . $ret->getMessage() . "<br>\n");
+else
+    print("Quotient is " . $ret . "<br>\n");
+
+$ret = $soapclient->divide(22,0);
+if (PEAR::isError($ret))
+    print("Error: " . $ret->getMessage() . "<br>\n");
+else
+    print("Quotient is " . $ret . "<br>\n");
+
+
+// SOAPStruct is defined in the following file
+require_once 'example_types.php';
+
+$struct = new SOAPStruct('test string',123,123.123);
+
+/* Send an object, get an object back.
+ * Tell the client to translate to classes we provide if possible. */
+$soapclient->_auto_translation = true;
+/* Or you can explicitly set the translation for a specific class.
+ * auto_translation works for all cases, but opens ANY class in the script to
+ * be used as a data type, and may not be desireable.  both can be used on
+ * client or server. */
+$soapclient->setTypeTranslation('{http://soapinterop.org/xsd}SOAPStruct',
+                                'SOAPStruct');
+$ret = $soapclient->echoStruct($struct->__to_soap());
+//print $soapclient->getWire();
+print_r($ret);
+
+/**
+ * PHP doesn't support multiple OUT parameters in function calls, so we must
+ * do a little work to make it happen here.  This requires knowledge on the
+ * developers part to figure out how they want to deal with it.
+ */
+$ret = $soapclient->echoStructAsSimpleTypes($struct->__to_soap());
+if (PEAR::isError($ret)) {
+    print("Error: " . $ret->getMessage() . "<br>\n");
+} else {
+    list($string, $int, $float) = array_values($ret);
+}
+echo "varString: $string<br>\nvarInt: $int<br>\nvarFloat: $float<br>\n";
+
+?>
Index: branches/feature-module-update/data/module/SOAP/example/email_gateway.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/email_gateway.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/email_gateway.php	(revision 16957)
@@ -0,0 +1,48 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
+// +----------------------------------------------------------------------+
+//
+// $Id: email_gateway.php,v 1.5 2005/03/10 23:16:40 yunosh Exp $
+//
+
+/*
+This reads a message from stdin, and passes the request to
+a soap server residing on a web server, sends the response
+out as an email
+
+You can use this from qmail by creating a .qmail-soapgateway file with:
+    | /usr/bin/php /path/to/email_server.php
+*/
+
+# include the email server class
+require_once 'SOAP/Server/Email_Gateway.php';
+
+# read stdin
+$fin = fopen('php://stdin','rb');
+if (!$fin) exit(0);
+
+$email = '';
+while (!feof($fin) && $data = fread($fin, 8096)) {
+  $email .= $data;
+}
+
+fclose($fin);
+
+# doit!
+$server = new SOAP_Server_Email_Gateway();
+$server->service($data, 'http://localhost/soap/example/server.php');
+?>
Index: branches/feature-module-update/data/module/SOAP/example/com_client.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/com_client.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/com_client.php	(revision 16957)
@@ -0,0 +1,57 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: com_client.php,v 1.3 2005/03/10 23:16:40 yunosh Exp $
+//
+// SOAPStruct is defined in the following file
+require_once 'example_types.php';
+
+/* just a simple example of using MS SOAP on win32 as a client
+   to the php server.  */
+
+//load COM SOAP client object
+$soapclient = new COM("MSSOAP.SoapClient30");
+
+//connect to web service
+$soapclient->mssoapinit("http://localhost/SOAP/example/server.php?wsdl");
+
+//obtain result from web service method
+$ret = $soapclient->echoString("This is a test!");
+print("$ret\n");
+
+$ret = $soapclient->echoStringSimple("This is another test!");
+print("$ret\n");
+
+# the following causes an exception in the COM extension
+
+#$ret = $soapclient->divide(22,7);
+#print $soapclient->faultcode;
+#print $soapclient->faultstring;
+#print("22/7=$ret\n");
+#print_r($ret);
+#$ret = $soapclient->divide(22,0);
+#print("22/0=$ret\n");
+
+#$struct = new SOAPStruct('test string',123,123.123);
+#$ret = $soapclient->echoStruct($struct);
+#print_r($ret);
+
+#$ret = $soapclient->echoStructAsSimpleTypes($struct);
+#print_r($ret);
+
+?>
Index: branches/feature-module-update/data/module/SOAP/example/tcp_server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/tcp_server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/tcp_server.php	(revision 16957)
@@ -0,0 +1,35 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: tcp_server.php,v 1.4 2005/03/10 23:16:40 yunosh Exp $
+//
+
+// first, include the SOAP/Server class
+require_once 'SOAP/Server/TCP.php';
+set_time_limit(0);
+
+$server = new SOAP_Server_TCP("127.0.0.1",82);
+/* tell server to translate to classes we provide if possible */
+$server->_auto_translation = true;
+
+require_once 'example_server.php';
+
+$soapclass = new SOAP_Example_Server();
+$server->addObjectMap($soapclass,'urn:SOAP_Example_Server');
+$server->run();
+?>
Index: branches/feature-module-update/data/module/SOAP/example/email_server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/email_server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/email_server.php	(revision 16957)
@@ -0,0 +1,56 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
+// +----------------------------------------------------------------------+
+//
+// $Id: email_server.php,v 1.5 2005/03/10 23:16:40 yunosh Exp $
+//
+
+/*
+This reads a message from stdin, and calls the soap server defined
+
+You can use this from qmail by creating a .qmail-soaptest file with:
+    | /usr/bin/php /path/to/email_server.php
+*/
+
+# include the email server class
+require_once 'SOAP/Server/Email.php';
+
+$server = new SOAP_Server_Email;
+
+/* tell server to translate to classes we provide if possible */
+$server->_auto_translation = true;
+
+require_once 'example_server.php';
+
+$soapclass = new SOAP_Example_Server();
+$server->addObjectMap($soapclass,'urn:SOAP_Example_Server');
+
+# read stdin
+$fin = fopen('php://stdin','rb');
+if (!$fin) exit(0);
+
+$email = '';
+while (!feof($fin) && $data = fread($fin, 8096)) {
+  $email .= $data;
+}
+
+fclose($fin);
+
+# doit!
+$server->service($email);
+
+?>
Index: branches/feature-module-update/data/module/SOAP/example/client.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/client.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/client.php	(revision 16957)
@@ -0,0 +1,101 @@
+<?php
+/**
+ * This file contains an example SOAP client that calls methods on the example
+ * SOAP server in this same directory.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require 'SOAP/Client.php';
+
+/**
+ * This client runs against the example server in SOAP/example/server.php.  It
+ * does not use WSDL to run these requests, but that can be changed easily by
+ * simply adding '?wsdl' to the end of the url.
+ */
+$soapclient = new SOAP_Client('http://localhost/SOAP/example/server.php');
+// This namespace is the same as declared in server.php.
+$options = array('namespace' => 'urn:SOAP_Example_Server',
+                 'trace' => true);
+
+$ret = $soapclient->call('echoStringSimple',
+                         $params = array('inputStringSimple' => 'this is a test string'),
+                         $options);
+// print $soapclient->getWire();
+print_r($ret);
+echo "<br>\n";
+
+$ret = $soapclient->call('echoString',
+                         $params = array('inputString' => 'this is a test string'),
+                         $options);
+print_r($ret);
+echo "<br>\n";
+
+$ret = $soapclient->call('divide',
+                         $params = array('dividend' => 22, 'divisor' => 7),
+                         $options);
+// print $soapclient->getWire();
+if (PEAR::isError($ret)) {
+    echo 'Error: ' . $ret->getMessage() . "<br>\n";
+} else {
+    echo 'Quotient is ' . $ret . "<br>\n";
+}
+
+$ret = $soapclient->call('divide',
+                         $params = array('dividend' => 22, 'divisor' => 0),
+                         $options);
+if (PEAR::isError($ret)) {
+    echo 'Error: ' . $ret->getMessage() . "<br>\n";
+} else {
+    echo 'Quotient is ' . $ret . "<br>\n";
+}
+
+
+// SOAPStruct is defined in the following file.
+require_once 'example_types.php';
+
+$struct = new SOAPStruct('test string', 123, 123.123);
+
+/* Send an object, get an object back. Tell the client to translate to classes
+ * we provide if possible. */
+$soapclient->_auto_translation = true;
+/* You can explicitly set the translation for a specific
+ * class. auto_translation works for all cases, but opens ANY class in the
+ * script to be used as a data type, and may not be desireable. Both can be
+ * used on client or server. */
+$soapclient->setTypeTranslation('{http://soapinterop.org/xsd}SOAPStruct',
+                                'SOAPStruct');
+$ret = $soapclient->call('echoStruct',
+                         $p = array('inputStruct' => $struct->__to_soap()),
+                         $options);
+// print $soapclient->getWire();
+print_r($ret);
+
+/**
+ * PHP doesn't support multiple OUT parameters in function calls, so we must
+ * do a little work to make it happen here.  This requires knowledge on the
+ * developers part to figure out how they want to deal with it.
+ */
+$ret = $soapclient->call('echoStructAsSimpleTypes',
+                         $p = array('inputStruct' => $struct->__to_soap()),
+                         $options);
+if (PEAR::isError($ret)) {
+    echo 'Error: ' . $ret->getMessage() . "<br>\n";
+} else {
+    list($string, $int, $float) = array_values($ret);
+    echo "varString: $string<br>\nvarInt: $int<br>\nvarFloat: $float<br>\n";
+}
Index: branches/feature-module-update/data/module/SOAP/example/example_types.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/example_types.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/example_types.php	(revision 16957)
@@ -0,0 +1,44 @@
+<?php
+/**
+ * This is a data type that is used in SOAP Interop testing, but is here as an
+ * example of using complex types.  When the class is deserialized from a SOAP
+ * message, it's constructor IS NOT CALLED!  So your type classes need to
+ * behave in a way that will work with that.
+ *
+ * Some types may need more explicit serialization for SOAP.  The __to_soap
+ * function allows you to be very explicit in building the SOAP_Value
+ * structures.  The soap library does not call this directly, you would call
+ * it from your soap server class, echoStruct in the server class is an
+ * example of doing this.
+ */
+class SOAPStruct
+{
+
+    var $varString;
+    var $varInt;
+    var $varFloat;
+
+    function SOAPStruct($s = null, $i = null, $f = null)
+    {
+        $this->varString = $s;
+        $this->varInt = $i;
+        $this->varFloat = $f;
+    }
+    
+    function &__to_soap($name = 'inputStruct', $header = false,
+                        $mustUnderstand = 0,
+                        $actor = 'http://schemas.xmlsoap.org/soap/actor/next')
+    {
+        $inner[] = new SOAP_Value('varString', 'string', $this->varString);
+        $inner[] = new SOAP_Value('varInt', 'int', $this->varInt);
+        $inner[] = new SOAP_Value('varFloat', 'float', $this->varFloat);
+
+        if ($header) {
+            $value = new SOAP_Header($name,'{http://soapinterop.org/xsd}SOAPStruct',$inner,$mustUnderstand,$actor);
+        } else {
+            $value = new SOAP_Value($name,'{http://soapinterop.org/xsd}SOAPStruct',$inner);
+        }
+
+        return $value;
+    }
+}
Index: branches/feature-module-update/data/module/SOAP/example/example_server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/example_server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/example_server.php	(revision 16957)
@@ -0,0 +1,161 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: example_server.php,v 1.6 2007/01/22 11:51:45 yunosh Exp $
+//
+
+// first, include the SOAP/Server class
+require_once 'SOAP/Value.php';
+require_once 'SOAP/Fault.php';
+
+// SOAPStruct is defined in the following file
+require_once 'example_types.php';
+
+// create a class for your soap functions
+class SOAP_Example_Server {
+    /**
+     * The dispactch map does not need to be used, but aids
+     * the server class in knowing what parameters are used
+     * with the functions.  This is the ONLY way to have
+     * multiple OUT parameters.  If you use a dispatch map, you
+     * MUST add ALL functions you wish to allow be called.  If
+     * you do not use a dispatch map, then any public function
+     * can be called from soap (in php4, we consider this to be
+     * any function in the class unless it starts with underscore,
+     * php5 support is not complete yet in this regard).  
+     * if you do not define in/out parameters, the function can be
+     * called with parameters, but no validation on parameters will
+     * occure.
+     */
+    var $__dispatch_map = array();
+
+    function SOAP_Example_Server() {
+        /**
+        * when generating wsdl for a server, you have to define
+        * any special complex types that you use (ie classes).
+        * using a namespace id before the type will create an
+        * xml schema with the targetNamespace for the type
+        * multiple types with the same namespace will appear
+        * in the same schema section.  types with different
+        * namespaces will be in seperate schema sections.
+        * the following SOAPStruct typedef cooresponds to the
+        * SOAPStruct class above.
+        */
+        $this->__typedef['{http://soapinterop.org/xsd}SOAPStruct'] = 
+                    array(
+                        'varString' => 'string',
+                        'varInt' => 'int', 
+                        'varFloat' => 'float'
+                         );
+
+        // an aliased function with multiple out parameters
+	$this->__dispatch_map['echoStructAsSimpleTypes'] =
+		array('in' => array('inputStruct' => '{http://soapinterop.org/xsd}SOAPStruct'),
+		      'out' => array('outputString' => 'string', 'outputInteger' => 'int', 'outputFloat' => 'float'),
+		      'alias' => 'myEchoStructAsSimpleTypes'
+		      );
+	$this->__dispatch_map['echoStringSimple'] =
+		array('in' => array('inputStringSimple' => 'string'),
+		      'out' => array('outputStringSimple' => 'string'),
+		      );
+	$this->__dispatch_map['echoString'] =
+		array('in' => array('inputString' => 'string'),
+		      'out' => array('outputString' => 'string'),
+		      );
+	$this->__dispatch_map['divide'] =
+		array('in' => array('dividend' => 'int', 'divisor' => 'int'),
+		      'out' => array('outputFloat' => 'float'),
+		      );
+	$this->__dispatch_map['echoStruct'] =
+		array('in' => array('inputStruct' => '{http://soapinterop.org/xsd}SOAPStruct'),
+		      'out' => array('outputStruct' => '{http://soapinterop.org/xsd}SOAPStruct'),
+		      );
+	
+	$this->__dispatch_map['echoMimeAttachment'] = array();
+    }
+
+    /* this private function is called on by SOAP_Server to determine any
+        special dispatch information that might be necessary.  This, for example,
+        can be used to set up a dispatch map for functions that return multiple
+        OUT parameters */
+    function __dispatch($methodname) {
+        if (isset($this->__dispatch_map[$methodname]))
+            return $this->__dispatch_map[$methodname];
+        return NULL;
+    }
+
+    // a simple echoString function
+    function echoStringSimple($inputString)
+    {
+	return $inputString;
+    }
+    
+    // an explicit echostring function
+    function echoString($inputString)
+    {
+	return new SOAP_Value('outputString','string',$inputString);
+    }
+
+    function divide($dividend, $divisor)
+    {
+        // the soap server would normally catch errors like this
+        // and return a fault, but this is how you do it yourself.
+        if ($divisor == 0)
+            return new SOAP_Fault('You cannot divide by zero', 'Client');
+        else
+            return $dividend / $divisor;
+    }
+
+    function echoStruct($inputStruct)
+    {
+        return $inputStruct->__to_soap('outputStruct');
+    }
+    
+    /**
+     * echoStructAsSimpleTypes
+     * takes a SOAPStruct as input, and returns each of its elements
+     * as OUT parameters
+     *
+     * this function is also aliased so you have to call it with
+     * echoStructAsSimpleTypes
+     *
+     * SOAPStruct is defined as:
+     *
+     * struct SOAPStruct:
+     *    string varString
+     *    integer varInt
+     *    float varFloat
+     *
+     */
+    function myEchoStructAsSimpleTypes($struct)
+    {
+	# convert a SOAPStruct to an array
+	return array(
+	    new SOAP_Value('outputString','string',$struct->varString),
+	    new SOAP_Value('outputInteger','int',$struct->varInt),
+	    new SOAP_Value('outputFloat','float',$struct->varFloat)
+	    );
+    }
+    
+    function echoMimeAttachment($stuff)
+    {
+        return new SOAP_Attachment('return','application/octet-stream',NULL,$stuff);
+    }    
+}
+
+?>
Index: branches/feature-module-update/data/module/SOAP/example/smtp.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/smtp.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/smtp.php	(revision 16957)
@@ -0,0 +1,32 @@
+<?
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: smtp.php,v 1.5 2005/03/10 23:16:40 yunosh Exp $
+//
+// include soap client class
+include("SOAP/Client.php");
+
+$soapclient = new SOAP_Client("mailto:user@domain.com");
+$options = array('namespace'=>'http://soapinterop.org/','from'=>'user@domain.com','host'=>'localhost');
+$return = $soapclient->call("echoString",array("inputString"=>"this is a test"), $options);
+$return = $soapclient->call("echoStringArray",array('inputStringArray' => array('good','bad','ugly')), $options);
+// don't expect much of a result!
+print_r($return);
+print "<br>\n".$soapclient->wire;
+?>
+
Index: branches/feature-module-update/data/module/SOAP/example/tcp_client.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/tcp_client.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/tcp_client.php	(revision 16957)
@@ -0,0 +1,42 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Hanna <iordy_at_iordy_dot_com>                        |
+// +----------------------------------------------------------------------+
+//
+// $Id: tcp_client.php,v 1.5 2007/01/22 11:33:27 yunosh Exp $
+//
+
+require_once('SOAP/Client.php');
+
+// client
+$soapclient = new SOAP_Client("tcp://127.0.0.1:82");
+
+// namespace
+$options = array('namespace' => 'urn:SOAP_Example_Server', 'trace' => true);
+
+// one
+$params = array("string" => "this is string 1");
+$ret1 = $soapclient->call("echoString", $params, $options);
+// echo "WIRE: \n".$soapclient->getWire();
+print_r($ret1);
+echo "<br />\n";
+
+// two
+$params = array("string" => "this is string 2");
+$ret2 = $soapclient->call("echoString", $params, $options);
+// echo "WIRE: \n".$soapclient->getWire();
+print_r($ret2);
+echo "<br />\n";
Index: branches/feature-module-update/data/module/SOAP/example/email_client.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/email_client.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/email_client.php	(revision 16957)
@@ -0,0 +1,50 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
+// +----------------------------------------------------------------------+
+//
+// $Id: email_client.php,v 1.4 2005/03/10 23:16:40 yunosh Exp $
+//
+
+/*
+This reads a message from stdin, and calls the soap server defined
+
+You can use this from qmail by creating a .qmail-soaptest file with:
+    | /usr/bin/php /path/to/email_server.php
+*/
+
+# include the email server class
+require_once 'SOAP/Server/Email.php';
+
+$server = new SOAP_Server_Email;
+
+# read stdin
+$fin = fopen('php://stdin','rb');
+if (!$fin) exit(0);
+
+$email = '';
+while (!feof($fin) && $data = fread($fin, 8096)) {
+  $email .= $data;
+}
+
+fclose($fin);
+
+
+$response = $server->client($email);
+
+print_r($response);
+
+?>
Index: branches/feature-module-update/data/module/SOAP/example/email_pop_gateway.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/email_pop_gateway.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/email_pop_gateway.php	(revision 16957)
@@ -0,0 +1,70 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
+// +----------------------------------------------------------------------+
+//
+// $Id: email_pop_gateway.php,v 1.6 2005/03/10 23:16:40 yunosh Exp $
+//
+
+/* include the Email server class, which knows how to
+   parse a standard email as a soap message */
+
+require_once 'SOAP/Server/Email_Gateway.php';
+
+/* include a class to access POP3 */
+
+require_once 'Net/POP3.php';
+
+/* create the SOAP Server object */
+
+$server = new SOAP_Server_Email_Gateway('http://localhost/soap_interop/server_round2.php');
+
+/* tell server to translate to classes we provide if possible */
+$server->_auto_translation = true;
+
+require_once 'example_server.php';
+
+$soapclass = new SOAP_Example_Server();
+$server->addObjectMap($soapclass,'urn:SOAP_Example_Server');
+
+
+/* Connect to a POP3 server and read the messages */
+
+$pop3 =& new Net_POP3();
+if ($pop3->connect('localhost', 110)) {
+    if ($pop3->login('username', 'password')) {
+        $listing = $pop3->getListing();
+
+	/* now loop through each message, and call the 
+           SOAP server with that message */
+
+        foreach ($listing as $msg) {
+
+            $email = $pop3->getMsg($msg['msg_id']);
+
+            /* this is where we actually handle the SOAP
+               response.  The SOAP::Server::Email class we
+               are using will send the SOAP response to 
+               the sender via email. */
+
+            if ($email) $server->service($email);
+
+            #$pop3->deleteMsg($msg['msg_id']);
+        }
+    }
+    $pop3->disconnect();
+}
+?>
Index: branches/feature-module-update/data/module/SOAP/example/disco_server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/disco_server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/disco_server.php	(revision 16957)
@@ -0,0 +1,238 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Dmitri Vinogradov <dimitri@vinogradov.de>                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: disco_server.php,v 1.6 2006/12/27 10:50:39 arnaud Exp $
+
+/*
+  This example shows how to mark up a server object so that
+  wsdl files may be dynamicaly generated by the soap
+  server.  This also provides access to a generated DISCO document.
+  The fact that this example has an MP3 class is in no way
+  related to DISCO. ;)
+  
+  DISCO: http://msdn.microsoft.com/msdnmag/issues/02/02/xml/default.aspx
+  
+  A url accessing this server would look like:
+  
+  http://localhost/disco_server.php?wsdl (generate WSDL file)
+  http://localhost/disco_server.php (GET request generates DISCO)
+  http://localhost/disco_server.php (POST for normal soap requests)
+*/
+
+error_reporting(E_ALL);
+require_once 'SOAP/Server.php';
+
+class MP3DB_Class {
+    var $__dispatch_map = array();
+    var $__typedef     = array();
+    
+    function MP3DB_Class () {
+        /**
+        * the only way to describe all methods in WSDL (messages,
+        * PortType-operations and bindings) is to use __dispatch_map
+        * to describe every method (even methods using simple data 
+        * types in 'in' and 'out' parameters...)
+        */
+        
+        $this->__dispatch_map['SayHallo'] =
+                    array(
+                        'in' => array('input' => 'string'),
+                        'out' => array('return' => 'string')
+                    );
+                    
+        $this->__dispatch_map['SayThisNTimes'] =
+                    array(
+                        'in' => array('SayThis'=>'string','NTimes' => 'int'),
+                        'out' => array('return' => '{urn:MP3DB}ArrayOfStrings')
+                    );
+                    
+        $this->__dispatch_map['GetMP3Tracks'] =
+                    array(
+                        'in' => array('query' => 'string'),
+                        'out' => array('return' => '{urn:MP3DB}GetMP3TracksResult')
+                    );
+                    
+        $this->__dispatch_map['AddMP3Track'] =
+                    array(
+                        'in' => array('MP3Track' => '{urn:MP3DB}MP3Track'),
+                        'out' => array('return' => '{urn:MP3DB}AddMP3TrackResult')
+                    );
+
+        /**
+        * I use __typedef to describe userdefined Types in WSDL.
+        * Structs and one-dimensional arrays are supported:
+        * Struct example: $this->__typedef['TypeName'] = array('VarName' => 'xsdType', ... );
+        *    or $this->__typedef['TypeName'] = array('VarName' => '{namespace}SomeOtherType');
+        * Array example: $this->__typedef['TypeName'] = array(array('item' => 'xsdType'));
+        *    or $this->__typedef['TypeName'] = array(array('item' => '{namespace}SomeOtherType'));
+        */
+    
+        /**
+        * Struct 'MP3Track'
+        */
+        $this->__typedef['MP3Track'] = 
+                    array(
+                        'Title' => 'string',
+                        'Artist' => 'string', 
+                        'Album' => 'string', 
+                        'Year' => 'int', 
+                        'Genre' => 'int',
+                        'Comment' => 'string',
+                        'Composer' => 'string',
+                        'Orig_Artist' => 'string',
+                        'URL' => 'string',
+                        'Encoded_by' => 'string'
+                    );
+                    
+                    
+                    
+        /**
+        * MP3TracksArray - array of 'MP3Track' structs
+        */
+        $this->__typedef['MP3TracksArray'] = 
+                    array(
+                        array(
+                            'item' => '{urn:MP3DB}MP3Track'
+                        )
+                    );
+                    
+        /**
+        * Struct 'MethodDebug'
+        */
+        $this->__typedef['MethodDebug'] = 
+                    array(
+                        'rc' => 'boolean',
+                        'ErrNo' => 'int', 
+                        'Error' => 'string'
+                    );
+                    
+        /**
+        * return Struct of method GetMP3Tracks
+        */
+        $this->__typedef['GetMP3TracksResult'] = 
+                    array(
+                        'MethodDebug' => '{urn:MP3DB}MethodDebug',
+                        'MP3Tracks' => '{urn:MP3DB}MP3TracksArray'
+                    );
+                    
+        /**
+        * return Struct of method AddMP3Track
+        */
+        $this->__typedef['AddMP3TrackResult'] = 
+                    array(
+                        'MethodDebug' => '{urn:MP3DB}MethodDebug'
+                    );
+                    
+        /**
+        * Array of strings
+        */
+        $this->__typedef['ArrayOfStrings'] = 
+                    array(
+                        array('item'=>'string')
+                    );
+    
+    }
+    
+    
+    function SayHallo($name) {
+        return "Hallo, " . $name;
+    }
+    
+    
+    function SayThisNTimes($SayThis,$NTimes) {
+        for ($i = 0; $i < $NTimes; $i++) {
+            $return[$i] = $SayThis . " $i";
+        }
+        return new SOAP_Value('return','{urn:MP3DB}ArrayOfStrings',$return);
+    }
+    
+    
+    function GetMP3Tracks($query = "") {
+        for($i = 0; $i < 5; $i++) {
+            $this->MP3Tracks[$i] = new SOAP_Value(
+                    'item',
+                    '{urn:MP3DB}MP3Track',
+                    array(
+                        "Title"         => new SOAP_Value("Title","string","some track $i"),
+                        "Artist"        => new SOAP_Value("Artist","string","some artist $i"),
+                        "Album"         => new SOAP_Value("Album","string","some album $i"),
+                        "Year"          => new SOAP_Value("Year","int",(integer)1999),
+                        "Genre"         => new SOAP_Value("Genre","int",(integer)100),
+                        "Comment"       => new SOAP_Value("Comment","string","blabla $i"),
+                        "Composer"      => new SOAP_Value("Composer","string",""),
+                        "Orig_Artist"   => new SOAP_Value("Orig_Artist","string",""),
+                        "URL"           => new SOAP_Value("URL","string",""),
+                        "Encoded_by"    => new SOAP_Value("Encoded_by","string","")
+                        )
+                );
+        }
+        
+        $MethodDebug["rc"]    = new SOAP_Value("rc","boolean",true);
+        $MethodDebug["ErrNo"] = new SOAP_Value("ErrNo","int",(integer)0);
+        $MethodDebug["Error"] = new SOAP_Value("Error","string","");
+            
+        return new SOAP_Value('return','{urn:MP3DB}GetMP3TracksResult',array(
+                    "MethodDebug" => new SOAP_Value('MethodDebug','{urn:MP3DB}MethodDebug',$MethodDebug),
+                    "MP3Tracks"   => new SOAP_Value('MP3Tracks','{urn:MP3DB}MP3TracksArray',$this->MP3Tracks)
+                    )
+                );
+    }
+    
+    
+    function AddMP3Track($MP3Track) {
+        # well, lets imagine here some code for adding given mp3track to db or whatever...
+        $MethodDebug["rc"]    = new SOAP_Value("rc","boolean",true);
+        $MethodDebug["ErrNo"] = new SOAP_Value("ErrNo","int",(integer)0);
+        $MethodDebug["Error"] = new SOAP_Value("Error","string","");
+        
+        return new SOAP_Value('return','{urn:MP3DB}AddMP3TrackResult',array(
+                    "MethodDebug" => new SOAP_Value('MethodDebug','{urn:MP3DB}MethodDebug',$MethodDebug)
+                    )
+                );
+    }
+    
+    
+    function __dispatch($methodname) {
+        if (isset($this->__dispatch_map[$methodname]))
+            return $this->__dispatch_map[$methodname];
+        return NULL;
+    }
+}
+
+$server = new SOAP_Server;
+$server->_auto_translation = true;
+$MP3DB_Class = new MP3DB_Class();
+$server->addObjectMap($MP3DB_Class,'urn:MP3DB');
+
+
+if (isset($_SERVER['REQUEST_METHOD']) &&
+    $_SERVER['REQUEST_METHOD']=='POST') {
+    $server->service($HTTP_RAW_POST_DATA);
+} else {
+    require_once 'SOAP/Disco.php';
+    $disco = new SOAP_DISCO_Server($server,"MP3DB");
+    header("Content-type: text/xml");
+    if (isset($_SERVER['QUERY_STRING']) &&
+       strcasecmp($_SERVER['QUERY_STRING'],'wsdl')==0) {
+        echo $disco->getWSDL();
+    } else {
+        echo $disco->getDISCO();
+    }
+    exit;
+}
+?>
Index: branches/feature-module-update/data/module/SOAP/example/attachment.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/attachment.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/attachment.php	(revision 16957)
@@ -0,0 +1,26 @@
+<?php
+require_once("SOAP/Client.php");
+require_once("SOAP/test/test.utility.php");
+require_once("SOAP/Value.php");
+
+$filename = 'attachment.php';
+$v =  new SOAP_Attachment('test','text/plain',$filename);
+$methodValue = new SOAP_Value('testattach', 'Struct', array($v));
+
+$client = new SOAP_Client('mailto:user@domain.com');
+# calling with mime
+$resp = $client->call('echoMimeAttachment',array($v),
+                array('attachments'=>'Mime',
+                'namespace'=>'http://soapinterop.org/',
+                'from'=>'user@domain.com',
+                'host'=>'smtp.domain.com'));
+print $client->wire."\n\n\n";
+print_r($resp);
+
+# calling with DIME
+$resp = $client->call('echoMimeAttachment',array($v));
+# DIME has null spaces, change them so we can see the wire
+$wire = str_replace("\0",'*',$client->wire);
+print $wire."\n\n\n";
+print_r($resp);
+?>
Index: branches/feature-module-update/data/module/SOAP/example/server2.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/server2.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/server2.php	(revision 16957)
@@ -0,0 +1,64 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: server2.php,v 1.3 2005/03/10 23:16:40 yunosh Exp $
+//
+
+// first, include the SOAP/Server class
+require_once 'SOAP/Server.php';
+
+$server = new SOAP_Server;
+/* tell server to translate to classes we provide if possible */
+$server->_auto_translation = true;
+
+/* This is a simple example of implementing a custom
+   call handler.  If you do this, the soap server will ignore
+   objects or functions added to it, and will call your handler
+   for **ALL** soap calls the server receives, wether the call
+   is defined in your WSDL or not.  The handler receives two
+   arguments, the method name being called, and the arguments
+   sent for that call.
+*/
+function myCallHandler($methodname, $args)
+{
+    global $soapclass;
+    return @call_user_func_array(array($soapclass, $methodname),$args);
+}
+$server->setCallHandler('myCallHandler',false);
+
+require_once 'example_server.php';
+
+$soapclass = new SOAP_Example_Server();
+$server->addObjectMap($soapclass,'urn:SOAP_Example_Server');
+
+if (isset($_SERVER['REQUEST_METHOD']) &&
+    $_SERVER['REQUEST_METHOD']=='POST') {
+    $server->service($HTTP_RAW_POST_DATA);
+} else {
+    require_once 'SOAP/Disco.php';
+    $disco = new SOAP_DISCO_Server($server,'ServerExample');
+    header("Content-type: text/xml");
+    if (isset($_SERVER['QUERY_STRING']) &&
+       strcasecmp($_SERVER['QUERY_STRING'],'wsdl')==0) {
+        echo $disco->getWSDL();
+    } else {
+        echo $disco->getDISCO();
+    }
+    exit;
+}
+?>
Index: branches/feature-module-update/data/module/SOAP/example/email_pop_server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/example/email_pop_server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/example/email_pop_server.php	(revision 16957)
@@ -0,0 +1,72 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more   |
+// +----------------------------------------------------------------------+
+//
+// $Id: email_pop_server.php,v 1.6 2005/03/10 23:16:40 yunosh Exp $
+//
+
+/* include the Email server class, which knows how to
+   parse a standard email as a soap message */
+
+require_once 'SOAP/Server/Email.php';
+
+/* include a class to access POP3 */
+
+require_once 'Net/POP3.php';
+
+/* create the SOAP Server object */
+
+$server = new SOAP_Server_Email;
+
+/* tell server to translate to classes we provide if possible */
+$server->_auto_translation = true;
+
+require_once 'example_server.php';
+
+$soapclass = new SOAP_Example_Server();
+$server->addObjectMap($soapclass,'urn:SOAP_Example_Server');
+
+
+/* Connect to a POP3 server and read the messages */
+
+$pop3 =& new Net_POP3();
+if ($pop3->connect('localhost', 110)) {
+    if ($pop3->login('username', 'password')) {
+        $listing = $pop3->getListing();
+
+	/* now loop through each message, and call the 
+           SOAP server with that message */
+
+        foreach ($listing as $msg) {
+
+            $email = $pop3->getMsg($msg['msg_id']);
+
+            /* this is where we actually handle the SOAP
+               response.  The SOAP::Server::Email class we
+               are using will send the SOAP response to 
+               the sender via email. */
+
+            if ($email) {
+                $server->client($email);
+
+                $pop3->deleteMsg($msg['msg_id']);
+            }
+        }
+    }
+    $pop3->disconnect();
+}
+?>
Index: branches/feature-module-update/data/module/SOAP/Server.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Server.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Server.php	(revision 16957)
@@ -0,0 +1,816 @@
+<?php
+/**
+ * This file contains the code for the SOAP server.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Base.php';
+require_once 'SOAP/Fault.php';
+require_once 'SOAP/Parser.php';
+require_once 'SOAP/Value.php';
+require_once 'SOAP/WSDL.php';
+
+/**
+ * SOAP Server Class
+ *
+ * Originaly based on SOAPx4 by Dietrich Ayala
+ * http://dietrich.ganx4.com/soapx4
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ * @author   Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_Server extends SOAP_Base
+{
+    /**
+     *
+     * @var array
+     */
+    var $dispatch_map = array(); // create empty dispatch map
+    var $dispatch_objects = array();
+    var $soapobject = null;
+    var $call_methodname = null;
+    var $callHandler = null;
+    var $callValidation = true;
+
+    /**
+     * A list of headers that are going to be sent back to the client.
+     *
+     * @var array
+     */
+    var $headers = array();
+
+    /**
+     *
+     * @var string
+     */
+    var $request = '';
+
+    /**
+     *
+     * @var string  XML-Encoding
+     */
+    var $xml_encoding = SOAP_DEFAULT_ENCODING;
+    var $response_encoding = 'UTF-8';
+
+    var $result = 'successful'; // for logging interop results to db
+
+    var $endpoint = ''; // the uri to ME!
+
+    var $service = ''; //soapaction header
+    var $method_namespace = null;
+
+    /**
+     * Options.
+     *
+     * @var array
+     */
+    var $_options = array('use' => 'encoded',
+                          'style' => 'rpc',
+                          'parameters' => 0,
+                          'http_status_success' => '200 OK',
+                          'http_status_fault' => '500 SOAP Fault');
+
+    function SOAP_Server($options = null)
+    {
+        ini_set('track_errors', 1);
+        parent::SOAP_Base('Server');
+
+        if (is_array($options)) {
+            if (isset($options['use'])) {
+                $this->_options['use'] = $options['use'];
+            }
+            if (isset($options['style'])) {
+                $this->_options['style'] = $options['style'];
+            }
+            if (isset($options['parameters'])) {
+                $this->_options['parameters'] = $options['parameters'];
+            }
+        }
+        // assume we encode with section 5
+        $this->_section5 = true;
+        if ($this->_options['use'] == 'literal') {
+            $this->_section5 = false;
+        }
+    }
+
+    /**
+     * Error handler for errors that happen in proxied methods.
+     *
+     * To always return a valid SOAP response even on errors that don't happen
+     * in this code, the errors are catched, transformed to a SOAP fault and
+     * immediately sent to the client.
+     *
+     * @see http://www.php.net/set_error_handler
+     */
+    function _errorHandler($errno, $errmsg, $filename, $linenum)
+    {
+        /* The error handler should ignore '0' errors, eg. hidden by @ - see
+         * the set_error_handler manual page. (thanks to Alan Knowles). */
+        if (!$errno || !error_reporting() || $errno == E_NOTICE ||
+            (defined('E_STRICT') && $errno == constant('E_STRICT'))) {
+            return false;
+        }
+
+        $this->fault = new SOAP_Fault($errmsg, 'Server', 'PHP', "Errno: $errno\nFilename: $filename\nLineno: $linenum\n");
+
+        $this->_sendResponse();
+        exit;
+    }
+
+    function _getContentEncoding($content_type)
+    {
+        /* Get the character encoding of the incoming request treat incoming
+         * data as UTF-8 if no encoding set. */
+        $this->xml_encoding = 'UTF-8';
+        if (strpos($content_type, '=')) {
+            $enc = strtoupper(str_replace('"', '', substr(strstr($content_type, '='), 1)));
+            if (!in_array($enc, $this->_encodings)) {
+                return false;
+            }
+            $this->xml_encoding = $enc;
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Parses the request and posts or returns the response.
+     *
+     * @param string $data      The SOAP request data.
+     * @param string $endpoint  The service endpoint. Determined automatically
+     *                          if left empty.
+     * @param boolean $test
+     * @param boolean $return   Whether to return the SOAP response data
+     *                          instead of sending it to the client.
+     */
+    function service($data, $endpoint = '', $test = false, $return = false)
+    {
+        $response = null;
+        $attachments = array();
+        $useEncoding = 'DIME';
+
+        /* Figure out our endpoint. */
+        $this->endpoint = $endpoint;
+        if (!$test && !$this->endpoint) {
+            /* We'll try to build our endpoint. */
+            $this->endpoint = 'http://' . $_SERVER['SERVER_NAME'];
+            if ($_SERVER['SERVER_PORT']) {
+                $this->endpoint .= ':' . $_SERVER['SERVER_PORT'];
+            }
+            $this->endpoint .= $_SERVER['SCRIPT_NAME'];
+        }
+
+        /* Get the character encoding of the incoming request treat incoming
+         * data as UTF-8 if no encoding set. */
+        if (isset($_SERVER['CONTENT_TYPE'])) {
+            if (strcasecmp($_SERVER['CONTENT_TYPE'], 'application/dime') == 0) {
+                $this->_decodeDIMEMessage($data, $headers, $attachments);
+                $useEncoding = 'DIME';
+            } elseif (stristr($_SERVER['CONTENT_TYPE'], 'multipart/related')) {
+                /* This is a mime message, let's decode it. */
+                $data = 'Content-Type: ' .
+                    stripslashes($_SERVER['CONTENT_TYPE']) .
+                    "\r\n\r\n" . $data;
+                $this->_decodeMimeMessage($data, $headers, $attachments);
+                $useEncoding = 'Mime';
+            }
+            if (!isset($headers['content-type'])) {
+                $headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']);
+            }
+            if (!$this->fault &&
+                !$this->_getContentEncoding($headers['content-type'])) {
+                $this->xml_encoding = SOAP_DEFAULT_ENCODING;
+                /* Found encoding we don't understand; return a fault. */
+                $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server');
+            }
+        }
+
+        /* If this is not a POST with Content-Type text/xml, try to return a
+         * WSDL file. */
+        if (!$this->fault && !$test &&
+            ($_SERVER['REQUEST_METHOD'] != 'POST' ||
+             strncmp($headers['content-type'], 'text/xml', 8) != 0)) {
+            /* This is not possibly a valid SOAP request, try to return a WSDL
+             * file. */
+            $this->_raiseSoapFault('Invalid SOAP request, must be POST with content-type: text/xml, got: ' . (isset($headers['content-type']) ? $headers['content-type'] : 'Nothing!'), '', '', 'Server');
+        }
+
+        if (!$this->fault) {
+            /* $response is a SOAP_Msg object. */
+            $soap_msg = $this->parseRequest($data, $attachments);
+
+            /* Handle Mime or DIME encoding. */
+            /* TODO: DIME decoding should move to the transport, do it here
+             * for now and for ease of getting it done. */
+            if (count($this->_attachments)) {
+                if ($useEncoding == 'Mime') {
+                    $soap_msg = $this->_makeMimeMessage($soap_msg);
+                } else {
+                    // default is dime
+                    $soap_msg = $this->_makeDIMEMessage($soap_msg);
+                    $this->headers['Content-Type'] = 'application/dime';
+                }
+                if (PEAR::isError($soap_msg)) {
+                    return $this->_raiseSoapFault($soap_msg);
+                }
+            }
+
+            if (is_array($soap_msg)) {
+                $response = $soap_msg['body'];
+                if (count($soap_msg['headers'])) {
+                    $this->headers = $soap_msg['headers'];
+                }
+            } else {
+                $response = $soap_msg;
+            }
+        }
+
+        if ($return) {
+            if ($this->fault) {
+                $response = $this->fault->message();
+            }
+            return $response;
+        }
+
+        $this->_sendResponse($response);
+    }
+
+    /**
+     * Sends the final HTTP response to the client, including the HTTP header
+     * and the HTTP body.
+     *
+     * If an error happened, it returns a SOAP fault instead of the response
+     * body.
+     *
+     * @param string $response  The response body.
+     */
+    function _sendResponse($response = '')
+    {
+        /* Make distinction between the different SAPIs, running PHP as CGI or
+         * as a module. */
+        if (stristr(php_sapi_name(), 'cgi') === 0) {
+            $hdrs_type = 'Status:';
+        } else {
+            $hdrs_type = 'HTTP/1.1';
+        }
+
+        if ($this->fault) {
+            $hdrs = $hdrs_type . ' ' . $this->_options['http_status_fault'] . "\r\n";
+            $response = $this->fault->message($this->response_encoding);
+        } else {
+            $hdrs = $hdrs_type . ' ' . $this->_options['http_status_success'] . "\r\n";
+        }
+        header($hdrs);
+
+        $this->headers['Server'] = SOAP_LIBRARY_NAME;
+        if (!isset($this->headers['Content-Type'])) {
+            $this->headers['Content-Type'] = 'text/xml; charset=' .
+                $this->response_encoding;
+        }
+        $this->headers['Content-Length'] = strlen($response);
+
+        foreach ($this->headers as $k => $v) {
+            header("$k: $v");
+            $hdrs .= "$k: $v\r\n";
+        }
+
+        $this->response = $hdrs . "\r\n" . $response;
+        print $response;
+    }
+
+    function &callMethod($methodname, &$args)
+    {
+        if ($this->callHandler) {
+            $ret = @call_user_func_array($this->callHandler, array($methodname, $args));
+            return $ret;
+        }
+
+        set_error_handler(array($this, '_errorHandler'));
+
+        if ($args) {
+            /* Call method with parameters. */
+            if (isset($this->soapobject) && is_object($this->soapobject)) {
+                $ret = call_user_func_array(array(&$this->soapobject, $methodname), $args);
+            } else {
+                $ret = call_user_func_array($methodname, $args);
+            }
+        } else {
+            /* Call method withour parameters. */
+            if (is_object($this->soapobject)) {
+                $ret = call_user_func(array(&$this->soapobject, $methodname));
+            } else {
+                $ret = call_user_func($methodname);
+            }
+        }
+
+        restore_error_handler();
+
+        return $ret;
+    }
+
+    /**
+     * Creates SOAP_Value objects with return values from method.
+     * Uses method signature to determine type.
+     *
+     * @param mixed $method_response  The result(s).
+     * @param array|string $type      The type(s) of the return value(s).
+     * @param string $return_name     The name of the return value.
+     * @param string $namespace       The namespace of the return value.
+     *
+     * @return array  List of SOAP_Value objects.
+     */
+    function buildResult(&$method_response, &$return_type,
+                         $return_name = 'return', $namespace = '')
+    {
+        if (is_a($method_response, 'SOAP_Value')) {
+            $return_val = array($method_response);
+        } else {
+            if (is_array($return_type) && is_array($method_response)) {
+                $i = 0;
+
+                foreach ($return_type as $key => $type) {
+                    if (is_numeric($key)) {
+                        $key = 'item';
+                    }
+                    if (is_a($method_response[$i], 'SOAP_Value')) {
+                        $return_val[] =& $method_response[$i++];
+                    } else {
+                        $qn = new QName($key, $namespace);
+                        $return_val[] = new SOAP_Value($qn->fqn(), $type, $method_response[$i++]);
+                    }
+                }
+            } else {
+                if (is_array($return_type)) {
+                    $keys = array_keys($return_type);
+                    if (!is_numeric($keys[0])) {
+                        $return_name = $keys[0];
+                    }
+                    $values = array_values($return_type);
+                    $return_type = $values[0];
+                }
+                $qn = new QName($return_name, $namespace);
+                $return_val = array(new SOAP_Value($qn->fqn(), $return_type, $method_response));
+            }
+        }
+        return $return_val;
+    }
+
+    function parseRequest($data = '', $attachments = null)
+    {
+        /* Parse response, get SOAP_Parser object. */
+        $parser =& new SOAP_Parser($data, $this->xml_encoding, $attachments);
+        /* If fault occurred during message parsing. */
+        if ($parser->fault) {
+            $this->fault = $parser->fault;
+            return null;
+        }
+
+        /* Handle message headers. */
+        $request_headers = $parser->getHeaders();
+        $header_results = array();
+
+        if ($request_headers) {
+            if (!is_a($request_headers, 'SOAP_Value')) {
+                $this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_headers, '', '', 'Server');
+                return null;
+            }
+            if ($request_headers->value) {
+                /* Handle headers now. */
+                foreach ($request_headers->value as $header_val) {
+                    $f_exists = $this->validateMethod($header_val->name, $header_val->namespace);
+
+                    /* TODO: this does not take into account message routing
+                     * yet. */
+                    $myactor = !$header_val->actor ||
+                        $header_val->actor == 'http://schemas.xmlsoap.org/soap/actor/next' ||
+                        $header_val->actor == $this->endpoint;
+
+                    if (!$f_exists && $header_val->mustunderstand && $myactor) {
+                        $this->_raiseSoapFault('I don\'t understand header ' . $header_val->name, '', '', 'MustUnderstand');
+                        return null;
+                    }
+
+                    /* We only handle the header if it's for us. */
+                    $isok = $f_exists && $myactor;
+
+                    if ($isok) {
+                        /* Call our header now! */
+                        $header_method = $header_val->name;
+                        $header_data = array($this->_decode($header_val));
+                        /* If there are parameters to pass. */
+                        $hr =& $this->callMethod($header_method, $header_data);
+                        if (PEAR::isError($hr)) {
+                            $this->_raiseSoapFault($hr);
+                            return null;
+                        }
+                        $results = $this->buildResult($hr, $this->return_type, $header_method, $header_val->namespace);
+                        $header_results[] = $results[0];
+                    }
+                }
+            }
+        }
+
+        /* Handle the method call. */
+        /* Evaluate message, getting back a SOAP_Value object. */
+        $this->call_methodname = $this->methodname = $parser->root_struct_name[0];
+
+        /* Figure out the method namespace. */
+        $this->method_namespace = $parser->message[$parser->root_struct[0]]['namespace'];
+
+        if ($this->_wsdl) {
+            $this->_setSchemaVersion($this->_wsdl->xsd);
+            $dataHandler = $this->_wsdl->getDataHandler($this->methodname, $this->method_namespace);
+            if ($dataHandler)
+                $this->call_methodname = $this->methodname = $dataHandler;
+
+            $this->_portName = $this->_wsdl->getPortName($this->methodname);
+            if (PEAR::isError($this->_portName)) {
+                $this->_raiseSoapFault($this->_portName);
+                return null;
+            }
+            $opData = $this->_wsdl->getOperationData($this->_portName, $this->methodname);
+            if (PEAR::isError($opData)) {
+                $this->_raiseSoapFault($opData);
+                return null;
+            }
+            $this->_options['style'] = $opData['style'];
+            $this->_options['use'] = $opData['output']['use'];
+            $this->_options['parameters'] = $opData['parameters'];
+        }
+
+        /* Does method exist? */
+        if (!$this->methodname ||
+            !$this->validateMethod($this->methodname, $this->method_namespace)) {
+            $this->_raiseSoapFault('method "' . $this->method_namespace . $this->methodname . '" not defined in service', '', '', 'Server');
+            return null;
+        }
+
+        if (!$request_val = $parser->getResponse()) {
+            return null;
+        }
+        if (!is_a($request_val, 'SOAP_Value')) {
+            $this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_val, '', '', 'Server');
+            return null;
+        }
+
+        /* Verify that SOAP_Value objects in request match the methods
+         * signature. */
+        if (!$this->verifyMethod($request_val)) {
+            /* verifyMethod() creates the fault. */
+            return null;
+        }
+
+        /* Need to set special error detection inside the value class to
+         * differentiate between no params passed, and an error decoding. */
+        $request_data = $this->__decodeRequest($request_val);
+        if (PEAR::isError($request_data)) {
+            $this->_raiseSoapFault($request_data);
+            return null;
+        }
+        $method_response =& $this->callMethod($this->call_methodname, $request_data);
+        if (PEAR::isError($method_response)) {
+            $this->_raiseSoapFault($method_response);
+            return null;
+        }
+
+        if ($this->_options['parameters'] ||
+            !$method_response ||
+            $this->_options['style'] == 'rpc') {
+            /* Get the method result. */
+            if (is_null($method_response)) {
+                $return_val = null;
+            } else {
+                $return_val = $this->buildResult($method_response, $this->return_type);
+            }
+
+            $qn = new QName($this->methodname . 'Response', $this->method_namespace);
+            $methodValue = new SOAP_Value($qn->fqn(), 'Struct', $return_val);
+        } else {
+            $methodValue =& $method_response;
+        }
+        return $this->makeEnvelope($methodValue, $header_results, $this->response_encoding);
+    }
+
+    function &__decodeRequest($request, $shift = false)
+    {
+        if (!$request) {
+            $decoded = null;
+            return $decoded;
+        }
+
+        /* Check for valid response. */
+        if (PEAR::isError($request)) {
+            $fault = &$this->_raiseSoapFault($request);
+            return $fault;
+        } else if (!is_a($request, 'SOAP_Value')) {
+            $fault = &$this->_raiseSoapFault('Invalid data in server::__decodeRequest');
+            return $fault;
+        }
+
+        /* Decode to native php datatype. */
+        $requestArray = $this->_decode($request);
+        /* Fault? */
+        if (PEAR::isError($requestArray)) {
+            $fault = &$this->_raiseSoapFault($requestArray);
+            return $fault;
+        }
+        if (is_object($requestArray) &&
+            get_class($requestArray) == 'stdClass') {
+            $requestArray = get_object_vars($requestArray);
+        } elseif ($this->_options['style'] == 'document') {
+            $requestArray = array($requestArray);
+        }
+        if (is_array($requestArray)) {
+            if (isset($requestArray['faultcode']) ||
+                isset($requestArray['SOAP-ENV:faultcode'])) {
+                $faultcode = $faultstring = $faultdetail = $faultactor = '';
+                foreach ($requestArray as $k => $v) {
+                    if (stristr($k, 'faultcode')) {
+                        $faultcode = $v;
+                    }
+                    if (stristr($k, 'faultstring')) {
+                        $faultstring = $v;
+                    }
+                    if (stristr($k, 'detail')) {
+                        $faultdetail = $v;
+                    }
+                    if (stristr($k, 'faultactor')) {
+                        $faultactor = $v;
+                    }
+                }
+                $fault = &$this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
+                return $fault;
+            }
+            /* Return array of return values. */
+            if ($shift && count($requestArray) == 1) {
+                $decoded = array_shift($requestArray);
+                return $decoded;
+            }
+            return $requestArray;
+        }
+        return $requestArray;
+    }
+
+    function verifyMethod($request)
+    {
+        if (!$this->callValidation) {
+            return true;
+        }
+
+        $params = $request->value;
+
+        /* Get the dispatch map if one exists. */
+        $map = null;
+        if (array_key_exists($this->methodname, $this->dispatch_map)) {
+            $map = $this->dispatch_map[$this->methodname];
+        } elseif (isset($this->soapobject)) {
+            if (method_exists($this->soapobject, '__dispatch')) {
+                $map = $this->soapobject->__dispatch($this->methodname);
+            } elseif (method_exists($this->soapobject, $this->methodname)) {
+                /* No map, all public functions are SOAP functions. */
+                return true;
+            }
+        }
+        if (!$map) {
+            $this->_raiseSoapFault('SOAP request specified an unhandled method "' . $this->methodname . '"', '', '', 'Client');
+            return false;
+        }
+
+        /* If we aliased the SOAP method name to a PHP function, change
+         * call_methodname so we do the right thing. */
+        if (array_key_exists('alias', $map) && !empty($map['alias'])) {
+            $this->call_methodname = $map['alias'];
+        }
+
+        /* If there are input parameters required. */
+        if ($map['in']) {
+            $this->input_value = count($map['in']);
+            $this->return_type = false;
+            if (is_array($map['out'])) {
+                $this->return_type = count($map['out']) > 1
+                    ? $map['out']
+                    : array_shift($map['out']);
+            }
+            if (is_array($params)) {
+                /* Validate the number of parameters. */
+                if (count($params) == count($map['in'])) {
+                    /* Make array of param types. */
+                    foreach ($params as $param) {
+                        $p[] = strtolower($param->type);
+                    }
+                    $sig_t = array_values($map['in']);
+                    /* Validate each param's type. */
+                    for ($i = 0; $i < count($p); $i++) {
+                        /* If SOAP types do not match, it's still fine if the
+                         * mapped php types match this allows using plain PHP
+                         * variables to work (i.e. stuff like Decimal would
+                         * fail otherwise). We consider this only error if the
+                         * types exist in our type maps, and they differ. */
+                        if (strcasecmp($sig_t[$i], $p[$i]) != 0 &&
+                            isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]]) &&
+                            strcasecmp($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]], $this->_typemap[SOAP_XML_SCHEMA_VERSION][$p[$i]]) != 0) {
+
+                            $param = $params[$i];
+                            $this->_raiseSoapFault("SOAP request contained mismatching parameters of name $param->name had type [{$p[$i]}], which did not match signature's type: [{$sig_t[$i]}], matched? " . (strcasecmp($sig_t[$i], $p[$i])), '', '', 'Client');
+                            return false;
+                        }
+                    }
+                    return true;
+                } else {
+                    /* Wrong number of params. */
+                    $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" required ' . count($map['in']) . ' and request provided ' . count($params), '', '', 'Client');
+                    return false;
+                }
+            } else {
+                /* No params. */
+                $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" requires ' . count($map['in']) . ' parameters, and request provided none.', '', '', 'Client');
+                return false;
+            }
+        }
+
+        /* We'll try it anyway. */
+        return true;
+    }
+
+    function validateMethod($methodname, $namespace = null)
+    {
+        unset($this->soapobject);
+
+        if (!$this->callValidation) {
+            return true;
+        }
+
+        /* No SOAP access to private functions. */
+        if ($methodname[0] == '_') {
+            return false;
+        }
+
+        /* if it's in our function list, ok */
+        if (array_key_exists($methodname, $this->dispatch_map) &&
+            (!$namespace ||
+             !array_key_exists('namespace', $this->dispatch_map[$methodname]) ||
+             $namespace == $this->dispatch_map[$methodname]['namespace'])) {
+            if (array_key_exists('namespace', $this->dispatch_map[$methodname]))
+                $this->method_namespace = $this->dispatch_map[$methodname]['namespace'];
+            return true;
+        }
+
+        /* if it's in an object, it's ok */
+        if (isset($this->dispatch_objects[$namespace])) {
+            $c = count($this->dispatch_objects[$namespace]);
+            for ($i = 0; $i < $c; $i++) {
+                $obj =& $this->dispatch_objects[$namespace][$i];
+                /* If we have a dispatch map, and the function is not in the
+                 * dispatch map, then it is not callable! */
+                if (method_exists($obj, '__dispatch')) {
+                    if ($obj->__dispatch($methodname)) {
+                        $this->method_namespace = $namespace;
+                        $this->soapobject =& $obj;
+                        return true;
+                    }
+                } elseif (method_exists($obj, $methodname)) {
+                    $this->method_namespace = $namespace;
+                    $this->soapobject =& $obj;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    function addObjectMap(&$obj, $namespace = null, $service_name = 'Default',
+                          $service_desc = '')
+    {
+        if (!$namespace) {
+            if (isset($obj->namespace)) {
+                // XXX a bit of backwards compatibility
+                $namespace = $obj->namespace;
+            } else {
+                $this->_raiseSoapFault('No namespace provided for class!', '', '', 'Server');
+                return false;
+            }
+        }
+        if (!isset($this->dispatch_objects[$namespace])) {
+            $this->dispatch_objects[$namespace] = array();
+        }
+        $this->dispatch_objects[$namespace][] =& $obj;
+
+        // Create internal WSDL structures for object
+
+        // XXX Because some internal workings of PEAR::SOAP decide whether to
+        // do certain things by the presence or absence of _wsdl, we should
+        // only create a _wsdl structure if we know we can fill it; if
+        // __dispatch_map or __typedef for the object is missing, we should
+        // avoid creating it. Later, when we are using PHP 5 introspection, we
+        // will be able to make the data for all objects without any extra
+        // information from the developers, and this condition should be
+        // dropped.
+
+        // XXX Known issue: if imported WSDL (bindWSDL) or another WSDL source
+        // is used to add _wsdl structure information, then addObjectWSDL is
+        // used, there is a high possibility of _wsdl data corruption;
+        // therefore you should avoid using __dispatch_map/__typedef
+        // definitions AND other WSDL data sources in the same service. We
+        // exclude classes that don't have __typedefs to allow external WSDL
+        // files to be used with classes with no internal type definitions
+        // (the types are defined in the WSDL file). When addObjectWSDL is
+        // refactored to not cause corruption, this restriction can be
+        // relaxed.
+
+        // In summary, if you add an object with both a dispatch map and type
+        // definitions, then previous WSDL file operation and type definitions
+        // will be overwritten.
+        if (isset($obj->__dispatch_map) && isset($obj->__typedef)) {
+            $this->addObjectWSDL($obj, $namespace, $service_name, $service_desc);
+        }
+
+        return true;
+    }
+
+    /**
+     * Adds a method to the dispatch map.
+     */
+    function addToMap($methodname, $in, $out, $namespace = null, $alias = null)
+    {
+        if (!$this->callHandler && !function_exists($methodname)) {
+            $this->_raiseSoapFault('Error mapping function', '', '', 'Server');
+            return false;
+        }
+
+        $this->dispatch_map[$methodname]['in'] = $in;
+        $this->dispatch_map[$methodname]['out'] = $out;
+        $this->dispatch_map[$methodname]['alias'] = $alias;
+        if ($namespace) {
+            $this->dispatch_map[$methodname]['namespace'] = $namespace;
+        }
+
+        return true;
+    }
+
+    function setCallHandler($callHandler, $validation = true)
+    {
+        $this->callHandler = $callHandler;
+        $this->callValidation = $validation;
+    }
+
+    /**
+     * @deprecated use bindWSDL from now on
+     */
+    function bind($wsdl_url)
+    {
+        $this->bindWSDL($wsdl_url);
+    }
+
+    /**
+     * @param  string a url to a WSDL resource
+     * @return void
+     */
+    function bindWSDL($wsdl_url)
+    {
+        /* Instantiate WSDL class. */
+        $this->_wsdl = new SOAP_WSDL($wsdl_url);
+        if ($this->_wsdl->fault) {
+            $this->_raiseSoapFault($this->_wsdl->fault);
+        }
+    }
+
+    /**
+     * @return void
+     */
+    function addObjectWSDL(&$wsdl_obj, $targetNamespace, $service_name,
+                           $service_desc = '')
+    {
+        if (!isset($this->_wsdl)) {
+            $this->_wsdl = new SOAP_WSDL;
+        }
+
+        $this->_wsdl->parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc);
+
+        if ($this->_wsdl->fault) {
+            $this->_raiseSoapFault($this->_wsdl->fault);
+        }
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Transport/TCP.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Transport/TCP.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Transport/TCP.php	(revision 16957)
@@ -0,0 +1,153 @@
+<?php
+/**
+ * This file contains the code for a TCP transport layer.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Hanna <iordy_at_iordy_dot_com>
+ * @author     Jan Schneider <jan@horde.org>
+ * @copyright  2003-2006 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Transport.php';
+
+/**
+ * TCP transport for SOAP.
+ *
+ * @todo    use Net_Socket; implement some security scheme; implement support
+ *          for attachments
+ * @access  public
+ * @package SOAP
+ * @author  Shane Hanna <iordy_at_iordy_dot_com>
+ * @author  Jan Schneider <jan@horde.org>
+ */
+class SOAP_Transport_TCP extends SOAP_Transport
+{
+    /**
+     * Socket.
+     */
+    var $socket = null;
+
+    /**
+     * Constructor.
+     *
+     * @param string $url  HTTP url to SOAP endpoint.
+     *
+     * @access public
+     */
+    function SOAP_Transport_TCP($url, $encoding = SOAP_DEFAULT_ENCODING)
+    {
+        parent::SOAP_Base_Object('TCP');
+        $this->urlparts = @parse_url($url);
+        $this->url = $url;
+        $this->encoding = $encoding;
+    }
+
+    function _socket_ping()
+    {
+        // XXX how do we restart after socket_shutdown?
+        //if (!$this->socket) {
+            // Create socket resource.
+            $this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+            if ($this->socket < 0) {
+                return 0;
+            }
+
+            // Connect.
+            $result = socket_connect($this->socket, $this->urlparts['host'],
+                                     $this->urlparts['port']);
+            if ($result < 0) {
+                return 0;
+            }
+        //}
+        return 1;
+    }
+
+    /**
+     * Sends and receives SOAP data.
+     *
+     * @access public
+     *
+     * @param string  Outgoing SOAP data.
+     * @param array   Options.
+     *
+     * @return string|SOAP_Fault
+     */
+    function send($msg, $options = array())
+    {
+        $this->fault = null;
+        $this->incoming_payload = '';
+        $this->outgoing_payload = $msg;
+        if (!$this->_validateUrl()) {
+            return $this->fault;
+        }
+
+        // Check for TCP scheme.
+        if (strcasecmp($this->urlparts['scheme'], 'TCP') == 0) {
+            // Check connection.
+            if (!$this->_socket_ping()) {
+                return $this->_raiseSoapFault('Error connecting to ' . $this->url . '; reason: ' . socket_strerror(socket_last_error($this->socket)));
+            }
+
+            // Write to the socket.
+            if (!@socket_write($this->socket, $this->outgoing_payload,
+                               strlen($this->outgoing_payload))) {
+                return $this->_raiseSoapFault('Error sending data to ' . $this->url . '; reason: ' . socket_strerror(socket_last_error($this->socket)));
+            }
+
+            // Shutdown writing.
+            if(!socket_shutdown($this->socket, 1)) {
+                return $this->_raiseSoapFault('Cannot change socket mode to read.');
+            }
+
+            // Read everything we can.
+            while ($buf = @socket_read($this->socket, 1024, PHP_BINARY_READ)) {
+                $this->incoming_payload .= $buf;
+            }
+
+            // Return payload or die.
+            if ($this->incoming_payload) {
+                return $this->incoming_payload;
+            }
+
+            return $this->_raiseSoapFault('Error reveiving data from ' . $this->url);
+        }
+
+        return $this->_raiseSoapFault('Invalid url scheme ' . $this->url);
+    }
+
+    /**
+     * Validates the url data passed to the constructor.
+     *
+     * @return boolean
+     * @access private
+     */
+    function _validateUrl()
+    {
+        if (!is_array($this->urlparts) ) {
+            $this->_raiseSoapFault("Unable to parse URL $this->url");
+            return false;
+        }
+        if (!isset($this->urlparts['host'])) {
+            $this->_raiseSoapFault("No host in URL $this->url");
+            return false;
+        }
+        if (!isset($this->urlparts['path']) || !$this->urlparts['path']) {
+            $this->urlparts['path'] = '/';
+        }
+
+        return true;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Transport/HTTP.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Transport/HTTP.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Transport/HTTP.php	(revision 16957)
@@ -0,0 +1,620 @@
+<?php
+/**
+ * This file contains the code for a HTTP transport layer.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Caraveo <Shane@Caraveo.com>
+ * @author     Jan Schneider <jan@horde.org>
+ * @copyright  2003-2006 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+/**
+ * HTTP Transport class
+ *
+ * @package  SOAP
+ * @category Web_Services
+ */
+
+/**
+ * Needed Classes
+ */
+require_once 'SOAP/Transport.php';
+
+/**
+ *  HTTP Transport for SOAP
+ *
+ * @access public
+ * @package SOAP
+ * @author Shane Caraveo <shane@php.net>
+ * @author Jan Schneider <jan@horde.org>
+ */
+class SOAP_Transport_HTTP extends SOAP_Transport
+{
+    /**
+     * Basic Auth string.
+     *
+     * @var array
+     */
+    var $headers = array();
+
+    /**
+     * Cookies.
+     *
+     * @var array
+     */
+    var $cookies;
+
+    /**
+     * Connection timeout in seconds. 0 = none.
+     *
+     * @var integer
+     */
+    var $timeout = 4;
+
+    /**
+     * HTTP-Response Content-Type.
+     */
+    var $result_content_type;
+
+    var $result_headers = array();
+
+    var $result_cookies = array();
+
+    /**
+     * SOAP_Transport_HTTP Constructor
+     *
+     * @access public
+     *
+     * @param string $url       HTTP url to SOAP endpoint.
+     * @param string $encoding  Encoding to use.
+     */
+    function SOAP_Transport_HTTP($url, $encoding = SOAP_DEFAULT_ENCODING)
+    {
+        parent::SOAP_Base('HTTP');
+        $this->urlparts = @parse_url($url);
+        $this->url = $url;
+        $this->encoding = $encoding;
+    }
+
+    /**
+     * Sends and receives SOAP data.
+     *
+     * @access public
+     *
+     * @param string  Outgoing SOAP data.
+     * @param array   Options.
+     *
+     * @return string|SOAP_Fault
+     */
+    function send($msg, $options = array())
+    {
+        $this->fault = null;
+
+        if (!$this->_validateUrl()) {
+            return $this->fault;
+        }
+
+        if (isset($options['timeout'])) {
+            $this->timeout = (int)$options['timeout'];
+        }
+
+        if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
+            return $this->_sendHTTP($msg, $options);
+        } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
+            return $this->_sendHTTPS($msg, $options);
+        }
+
+        return $this->_raiseSoapFault('Invalid url scheme ' . $this->url);
+    }
+
+    /**
+     * Sets data for HTTP authentication, creates authorization header.
+     *
+     * @param string $username   Username.
+     * @param string $password   Response data, minus HTTP headers.
+     *
+     * @access public
+     */
+    function setCredentials($username, $password)
+    {
+        $this->headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password);
+    }
+
+    /**
+     * Adds a cookie.
+     *
+     * @access public
+     * @param string $name  Cookie name.
+     * @param mixed $value  Cookie value.
+     */
+    function addCookie($name, $value)
+    {
+        $this->cookies[$name] = $value;
+    }
+
+    /**
+     * Generates the correct headers for the cookies.
+     *
+     * @access private
+     *
+     * @param array $options  Cookie options. If 'nocookies' is set and true
+     *                        the cookies from the last response are added
+     *                        automatically. 'cookies' is name-value-hash with
+     *                        a list of cookies to add.
+     *
+     * @return string  The cookie header value.
+     */
+    function _generateCookieHeader($options)
+    {
+        $this->cookies = array();
+
+        if (empty($options['nocookies']) &&
+            isset($this->result_cookies)) {
+            // Add the cookies we got from the last request.
+            foreach ($this->result_cookies as $cookie) {
+                if ($cookie['domain'] == $this->urlparts['host']) {
+                    $this->cookies[$cookie['name']] = $cookie['value'];
+                }
+            }
+        }
+
+        // Add cookies the user wants to set.
+        if (isset($options['cookies'])) {
+            foreach ($options['cookies'] as $cookie) {
+                if ($cookie['domain'] == $this->urlparts['host']) {
+                    $this->cookies[$cookie['name']] = $cookie['value'];
+                }
+            }
+        }
+
+        $cookies = '';
+        foreach ($this->cookies as $name => $value) {
+            if (!empty($cookies)) {
+                $cookies .= '; ';
+            }
+            $cookies .= urlencode($name) . '=' . urlencode($value);
+        }
+
+        return $cookies;
+    }
+
+    /**
+     * Validate url data passed to constructor.
+     *
+     * @access private
+     * @return boolean
+     */
+    function _validateUrl()
+    {
+        if (!is_array($this->urlparts) ) {
+            $this->_raiseSoapFault('Unable to parse URL ' . $this->url);
+            return false;
+        }
+        if (!isset($this->urlparts['host'])) {
+            $this->_raiseSoapFault('No host in URL ' . $this->url);
+            return false;
+        }
+        if (!isset($this->urlparts['port'])) {
+            if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
+                $this->urlparts['port'] = 80;
+            } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
+                $this->urlparts['port'] = 443;
+            }
+
+        }
+        if (isset($this->urlparts['user'])) {
+            $this->setCredentials(urldecode($this->urlparts['user']),
+                                  urldecode($this->urlparts['pass']));
+        }
+        if (!isset($this->urlparts['path']) || !$this->urlparts['path']) {
+            $this->urlparts['path'] = '/';
+        }
+
+        return true;
+    }
+
+    /**
+     * Finds out what the encoding is.
+     * Sets the object property accordingly.
+     *
+     * @access private
+     * @param array $headers  Headers.
+     */
+    function _parseEncoding($headers)
+    {
+        $h = stristr($headers, 'Content-Type');
+        preg_match_all('/^Content-Type:\s*(.*)$/im', $h, $ct, PREG_SET_ORDER);
+        $n = count($ct);
+        $ct = $ct[$n - 1];
+
+        // Strip the string of \r.
+        $this->result_content_type = str_replace("\r", '', $ct[1]);
+
+        if (preg_match('/(.*?)(?:;\s?charset=)(.*)/i',
+                       $this->result_content_type,
+                       $m)) {
+            $this->result_content_type = $m[1];
+            if (count($m) > 2) {
+                $enc = strtoupper(str_replace('"', '', $m[2]));
+                if (in_array($enc, $this->_encodings)) {
+                    $this->result_encoding = $enc;
+                }
+            }
+        }
+
+        // Deal with broken servers that don't set content type on faults.
+        if (!$this->result_content_type) {
+            $this->result_content_type = 'text/xml';
+        }
+    }
+
+    /**
+     * Parses the headers.
+     *
+     * @param array $headers  The headers.
+     */
+    function _parseHeaders($headers)
+    {
+        /* Largely borrowed from HTTP_Request. */
+        $this->result_headers = array();
+        $headers = split("\r?\n", $headers);
+        foreach ($headers as $value) {
+            if (strpos($value,':') === false) {
+                $this->result_headers[0] = $value;
+                continue;
+            }
+            list($name, $value) = split(':', $value);
+            $headername = strtolower($name);
+            $headervalue = trim($value);
+            $this->result_headers[$headername] = $headervalue;
+
+            if ($headername == 'set-cookie') {
+                // Parse a SetCookie header to fill _cookies array.
+                $cookie = array('expires' => null,
+                                'domain'  => $this->urlparts['host'],
+                                'path'    => null,
+                                'secure'  => false);
+
+                if (!strpos($headervalue, ';')) {
+                    // Only a name=value pair.
+                    list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $headervalue));
+                    $cookie['name']  = urldecode($cookie['name']);
+                    $cookie['value'] = urldecode($cookie['value']);
+
+                } else {
+                    // Some optional parameters are supplied.
+                    $elements = explode(';', $headervalue);
+                    list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $elements[0]));
+                    $cookie['name']  = urldecode($cookie['name']);
+                    $cookie['value'] = urldecode($cookie['value']);
+
+                    for ($i = 1; $i < count($elements);$i++) {
+                        list($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+                        if ('secure' == $elName) {
+                            $cookie['secure'] = true;
+                        } elseif ('expires' == $elName) {
+                            $cookie['expires'] = str_replace('"', '', $elValue);
+                        } elseif ('path' == $elName OR 'domain' == $elName) {
+                            $cookie[$elName] = urldecode($elValue);
+                        } else {
+                            $cookie[$elName] = $elValue;
+                        }
+                    }
+                }
+                $this->result_cookies[] = $cookie;
+            }
+        }
+    }
+
+    /**
+     * Removes HTTP headers from response.
+     *
+     * @return boolean
+     * @access private
+     */
+    function _parseResponse()
+    {
+        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s",
+                       $this->incoming_payload,
+                       $match)) {
+            $this->response = $match[2];
+            // Find the response error, some servers response with 500 for
+            // SOAP faults.
+            $this->_parseHeaders($match[1]);
+
+            list(, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s');
+            unset($this->result_headers[0]);
+
+            switch($code) {
+                case 100: // Continue
+                    $this->incoming_payload = $match[2];
+                    return $this->_parseResponse();
+                case 400:
+                    $this->_raiseSoapFault("HTTP Response $code Bad Request");
+                    return false;
+                    break;
+                case 401:
+                    $this->_raiseSoapFault("HTTP Response $code Authentication Failed");
+                    return false;
+                    break;
+                case 403:
+                    $this->_raiseSoapFault("HTTP Response $code Forbidden");
+                    return false;
+                    break;
+                case 404:
+                    $this->_raiseSoapFault("HTTP Response $code Not Found");
+                    return false;
+                    break;
+                case 407:
+                    $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required");
+                    return false;
+                    break;
+                case 408:
+                    $this->_raiseSoapFault("HTTP Response $code Request Timeout");
+                    return false;
+                    break;
+                case 410:
+                    $this->_raiseSoapFault("HTTP Response $code Gone");
+                    return false;
+                    break;
+                default:
+                    if ($code >= 400 && $code < 500) {
+                        $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg");
+                        return false;
+                    }
+            }
+
+            $this->_parseEncoding($match[1]);
+
+            if ($this->result_content_type == 'application/dime') {
+                // XXX quick hack insertion of DIME
+                if (PEAR::isError($this->_decodeDIMEMessage($this->response, $this->headers, $this->attachments))) {
+                    // _decodeDIMEMessage already raised $this->fault
+                    return false;
+                }
+                $this->result_content_type = $this->headers['content-type'];
+            } elseif (stristr($this->result_content_type, 'multipart/related')) {
+                $this->response = $this->incoming_payload;
+                if (PEAR::isError($this->_decodeMimeMessage($this->response, $this->headers, $this->attachments))) {
+                    // _decodeMimeMessage already raised $this->fault
+                    return false;
+                }
+            } elseif ($this->result_content_type != 'text/xml') {
+                $this->_raiseSoapFault($this->response);
+                return false;
+            }
+            // if no content, return false
+            return strlen($this->response) > 0;
+        }
+        $this->_raiseSoapFault('Invalid HTTP Response');
+        return false;
+    }
+
+    /**
+     * Creates an HTTP request, including headers, for the outgoing request.
+     *
+     * @access private
+     *
+     * @param string $msg     Outgoing SOAP package.
+     * @param array $options  Options.
+     *
+     * @return string  Outgoing payload.
+     */
+    function _getRequest($msg, $options)
+    {
+        $this->headers = array();
+
+        $action = isset($options['soapaction']) ? $options['soapaction'] : '';
+        $fullpath = $this->urlparts['path'];
+        if (isset($this->urlparts['query'])) {
+            $fullpath .= '?' . $this->urlparts['query'];
+        }
+        if (isset($this->urlparts['fragment'])) {
+            $fullpath .= '#' . $this->urlparts['fragment'];
+        }
+
+        if (isset($options['proxy_host'])) {
+            $fullpath = 'http://' . $this->urlparts['host'] . ':' .
+                $this->urlparts['port'] . $fullpath;
+        }
+
+        if (isset($options['proxy_user'])) {
+            $this->headers['Proxy-Authorization'] = 'Basic ' .
+                base64_encode($options['proxy_user'] . ':' .
+                              $options['proxy_pass']);
+        }
+
+        if (isset($options['user'])) {
+            $this->setCredentials($options['user'], $options['pass']);
+        }
+
+        $this->headers['User-Agent'] = $this->_userAgent;
+        $this->headers['Host'] = $this->urlparts['host'];
+        $this->headers['Content-Type'] = "text/xml; charset=$this->encoding";
+        $this->headers['Content-Length'] = strlen($msg);
+        $this->headers['SOAPAction'] = '"' . $action . '"';
+        $this->headers['Connection'] = 'close';
+
+        if (isset($options['headers'])) {
+            $this->headers = array_merge($this->headers, $options['headers']);
+        }
+
+        $cookies = $this->_generateCookieHeader($options);
+        if ($cookies) {
+            $this->headers['Cookie'] = $cookies;
+        }
+
+        $headers = '';
+        foreach ($this->headers as $k => $v) {
+            $headers .= "$k: $v\r\n";
+        }
+        $this->outgoing_payload = "POST $fullpath HTTP/1.0\r\n" . $headers .
+            "\r\n" . $msg;
+
+        return $this->outgoing_payload;
+    }
+
+    /**
+     * Sends the outgoing HTTP request and reads and parses the response.
+     *
+     * @access private
+     *
+     * @param string $msg     Outgoing SOAP package.
+     * @param array $options  Options.
+     *
+     * @return string  Response data without HTTP headers.
+     */
+    function _sendHTTP($msg, $options)
+    {
+        $this->incoming_payload = '';
+        $this->_getRequest($msg, $options);
+        $host = $this->urlparts['host'];
+        $port = $this->urlparts['port'];
+        if (isset($options['proxy_host'])) {
+            $host = $options['proxy_host'];
+            $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
+        }
+        // Send.
+        if ($this->timeout > 0) {
+            $fp = @fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout);
+        } else {
+            $fp = @fsockopen($host, $port, $this->errno, $this->errmsg);
+        }
+        if (!$fp) {
+            return $this->_raiseSoapFault("Connect Error to $host:$port");
+        }
+        if ($this->timeout > 0) {
+            // some builds of PHP do not support this, silence the warning
+            @socket_set_timeout($fp, $this->timeout);
+        }
+        if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
+            return $this->_raiseSoapFault("Error POSTing Data to $host");
+        }
+
+        // get reponse
+        // XXX time consumer
+        do {
+            $data = fread($fp, 4096);
+            $_tmp_status = socket_get_status($fp);
+            if ($_tmp_status['timed_out']) {
+                return $this->_raiseSoapFault("Timed out read from $host");
+            } else {
+                $this->incoming_payload .= $data;
+            }
+        } while (!$_tmp_status['eof']);
+
+        fclose($fp);
+
+        if (!$this->_parseResponse()) {
+            return $this->fault;
+        }
+        return $this->response;
+    }
+
+    /**
+     * Sends the outgoing HTTPS request and reads and parses the response.
+     *
+     * @access private
+     *
+     * @param string $msg     Outgoing SOAP package.
+     * @param array $options  Options.
+     *
+     * @return string  Response data without HTTP headers.
+     */
+    function _sendHTTPS($msg, $options)
+    {
+        /* Check if the required curl extension is installed. */
+        if (!extension_loaded('curl')) {
+            return $this->_raiseSoapFault('CURL Extension is required for HTTPS');
+        }
+
+        $ch = curl_init();
+
+        if (isset($options['proxy_host'])) {
+            $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
+            curl_setopt($ch, CURLOPT_PROXY,
+                        $options['proxy_host'] . ':' . $port);
+        }
+        if (isset($options['proxy_user'])) {
+            curl_setopt($ch, CURLOPT_PROXYUSERPWD,
+                        $options['proxy_user'] . ':' . $options['proxy_pass']);
+        }
+
+        if (isset($options['user'])) {
+            curl_setopt($ch, CURLOPT_USERPWD,
+                        $options['user'] . ':' . $options['pass']);
+        }
+
+        if (!isset($options['soapaction'])) {
+            $options['soapaction'] = '';
+        }
+        if (!isset($options['headers']['Content-Type'])) {
+           $options['headers']['Content-Type'] = 'text/xml';
+        }
+        curl_setopt($ch, CURLOPT_HTTPHEADER,
+                    array('Content-Type: ' . $options['headers']['Content-Type']
+                         . ';charset=' . $this->encoding,
+                          'SOAPAction: "' . $options['soapaction'] . '"'));
+        curl_setopt($ch, CURLOPT_USERAGENT,
+                    $this->_userAgent);
+
+        if ($this->timeout) {
+            curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
+        }
+
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $msg);
+        curl_setopt($ch, CURLOPT_URL, $this->url);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_FAILONERROR, 0);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_HEADER, 1);
+        if (defined('CURLOPT_HTTP_VERSION')) {
+            curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
+        }
+        if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
+            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+        }
+        $cookies = $this->_generateCookieHeader($options);
+        if ($cookies) {
+            curl_setopt($ch, CURLOPT_COOKIE, $cookies);
+        }
+
+        if (isset($options['curl'])) {
+            foreach ($options['curl'] as $key => $val) {
+                curl_setopt($ch, $key, $val);
+            }
+        }
+
+        // Save the outgoing XML. This doesn't quite match _sendHTTP as CURL
+        // generates the headers, but having the XML is usually the most
+        // important part for tracing/debugging.
+        $this->outgoing_payload = $msg;
+
+        $this->incoming_payload = curl_exec($ch);
+        if (!$this->incoming_payload) {
+            $m = 'curl_exec error ' . curl_errno($ch) . ' ' . curl_error($ch);
+            curl_close($ch);
+            return $this->_raiseSoapFault($m);
+        }
+        curl_close($ch);
+
+        if (!$this->_parseResponse()) {
+            return $this->fault;
+        }
+
+        return $this->response;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Transport/SMTP.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Transport/SMTP.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Transport/SMTP.php	(revision 16957)
@@ -0,0 +1,206 @@
+<?php
+/**
+ * This file contains the code for an SMTP transport layer.
+ *
+ * This code is still a rough and untested draft.
+ * TODO:
+ *  switch to pear mail stuff
+ *  smtp authentication
+ *  smtp ssl support
+ *  ability to define smtp options (encoding, from, etc.)
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Caraveo <Shane@Caraveo.com>
+ * @author     Jan Schneider <jan@horde.org>
+ * @copyright  2003-2006 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Transport.php';
+require_once 'Mail/smtp.php';
+
+/**
+ * SMTP Transport for SOAP
+ *
+ * Implements SOAP-SMTP as defined at
+ * http://www.pocketsoap.com/specs/smtpbinding/
+ *
+ * @todo use PEAR smtp and Mime classes
+ *
+ * @access public
+ * @package SOAP
+ * @author Shane Caraveo <shane@php.net>
+ * @author Jan Schneider <jan@horde.org>
+ */
+class SOAP_Transport_SMTP extends SOAP_Transport
+{
+    var $credentials = '';
+    var $timeout = 4; // connect timeout
+    var $host = '127.0.0.1';
+    var $port = 25;
+    var $auth = null;
+
+    /**
+     * SOAP_Transport_SMTP Constructor
+     *
+     * @param string $url  mailto: address.
+     *
+     * @access public
+     */
+    function SOAP_Transport_SMTP($url, $encoding = 'US-ASCII')
+    {
+        parent::SOAP_Base('SMTP');
+        $this->encoding = $encoding;
+        $this->urlparts = @parse_url($url);
+        $this->url = $url;
+    }
+
+    /**
+     * Sends and receives SOAP data.
+     *
+     * @access public
+     *
+     * @param string  Outgoing SOAP data.
+     * @param array   Options.
+     *
+     * @return string|SOAP_Fault
+     */
+    function send($msg, $options = array())
+    {
+        $this->fault = null;
+        $this->incoming_payload = '';
+        $this->outgoing_payload = $msg;
+        if (!$this->_validateUrl()) {
+            return $this->fault;
+        }
+        if (!$options || !isset($options['from'])) {
+            return $this->_raiseSoapFault('No From: address to send message with');
+        }
+
+        if (isset($options['host'])) $this->host = $options['host'];
+        if (isset($options['port'])) $this->port = $options['port'];
+        if (isset($options['auth'])) $this->auth = $options['auth'];
+        if (isset($options['username'])) $this->username = $options['username'];
+        if (isset($options['password'])) $this->password = $options['password'];
+
+        $headers = array();
+        $headers['From'] = $options['from'];
+        $headers['X-Mailer'] = $this->_userAgent;
+        $headers['MIME-Version'] = '1.0';
+        $headers['Message-ID'] = md5(time()) . '.soap@' . $this->host;
+        $headers['To'] = $this->urlparts['path'];
+        if (isset($options['soapaction'])) {
+            $headers['Soapaction'] = "\"{$options['soapaction']}\"";
+        }
+
+        if (isset($options['headers']))
+            $headers = array_merge($headers, $options['headers']);
+
+        // If the content type is already set, we assume that MIME encoding is
+        // already done.
+        if (isset($headers['Content-Type'])) {
+            $out = $msg;
+        } else {
+            // Do a simple inline MIME encoding.
+            $headers['Content-Disposition'] = 'inline';
+            $headers['Content-Type'] = "text/xml; charset=\"$this->encoding\"";
+            if (isset($options['transfer-encoding'])) {
+                if (strcasecmp($options['transfer-encoding'], 'quoted-printable') == 0) {
+                    $headers['Content-Transfer-Encoding'] = $options['transfer-encoding'];
+                    $out = $msg;
+                } elseif (strcasecmp($options['transfer-encoding'],'base64') == 0) {
+                    $headers['Content-Transfer-Encoding'] = 'base64';
+                    $out = chunk_split(base64_encode($msg), 76, "\n");
+                } else {
+                    return $this->_raiseSoapFault("Invalid Transfer Encoding: {$options['transfer-encoding']}");
+                }
+            } else {
+                // Default to base64.
+                $headers['Content-Transfer-Encoding'] = 'base64';
+                $out = chunk_split(base64_encode($msg));
+            }
+        }
+
+        $headers['Subject'] = isset($options['subject']) ? $options['subject'] : 'SOAP Message';
+
+        foreach ($headers as $key => $value) {
+            $header_text .= "$key: $value\n";
+        }
+        $this->outgoing_payload = $header_text . "\r\n" . $this->outgoing_payload;
+
+        $mailer_params = array(
+            'host' => $this->host,
+            'port' => $this->port,
+            'username' => $this->username,
+            'password' => $this->password,
+            'auth' => $this->auth
+        );
+        $mailer = new Mail_smtp($mailer_params);
+        $result = $mailer->send($this->urlparts['path'], $headers, $out);
+        if (!PEAR::isError($result)) {
+            $val = new SOAP_Value('Message-ID', 'string', $headers['Message-ID']);
+        } else {
+            $sval[] = new SOAP_Value('faultcode', 'QName', 'SOAP-ENV:Client');
+            $sval[] = new SOAP_Value('faultstring', 'string', "couldn't send SMTP message to {$this->urlparts['path']}");
+            $val = new SOAP_Value('Fault', 'Struct', $sval);
+        }
+
+        $methodValue = new SOAP_Value('Response', 'Struct', array($val));
+
+        $this->incoming_payload = $this->makeEnvelope($methodValue,
+                                                      $this->headers,
+                                                      $this->encoding);
+
+        return $this->incoming_payload;
+    }
+
+    /**
+     * Sets data for HTTP authentication, creates Authorization header.
+     *
+     * @param string $username  Username.
+     * @param string $password  Response data, minus HTTP headers.
+     *
+     * @access public
+     */
+    function setCredentials($username, $password)
+    {
+        $this->username = $username;
+        $this->password = $password;
+    }
+
+    /**
+     * Validates url data passed to constructor.
+     *
+     * @return boolean
+     * @access private
+     */
+    function _validateUrl()
+    {
+        if (!is_array($this->urlparts)) {
+            $this->_raiseSoapFault("Unable to parse URL $this->url");
+            return false;
+        }
+        if (!isset($this->urlparts['scheme']) ||
+            strcasecmp($this->urlparts['scheme'], 'mailto') != 0) {
+                $this->_raiseSoapFault("Unable to parse URL $this->url");
+                return false;
+        }
+        if (!isset($this->urlparts['path'])) {
+            $this->_raiseSoapFault("Unable to parse URL $this->url");
+            return false;
+        }
+        return true;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/WSDL.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/WSDL.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/WSDL.php	(revision 16957)
@@ -0,0 +1,2280 @@
+<?php
+/**
+ * This file contains the code for dealing with WSDL access and services.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Base.php';
+require_once 'SOAP/Fault.php';
+require_once 'Request.php';
+
+define('WSDL_CACHE_MAX_AGE', 43200);
+
+/**
+ * This class parses WSDL files, and can be used by SOAP::Client to properly
+ * register soap values for services.
+ *
+ * Originally based on SOAPx4 by Dietrich Ayala
+ * http://dietrich.ganx4.com/soapx4
+ *
+ * @todo
+ * - add wsdl caching
+ * - refactor namespace handling ($namespace/$ns)
+ * - implement IDL type syntax declaration so we can generate WSDL
+ *
+ * @access public
+ * @package SOAP
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_WSDL extends SOAP_Base
+{
+    var $tns = null;
+    var $definition = array();
+    var $namespaces = array();
+    var $ns = array();
+    var $xsd = SOAP_XML_SCHEMA_VERSION;
+    var $complexTypes = array();
+    var $elements = array();
+    var $messages = array();
+    var $portTypes = array();
+    var $bindings = array();
+    var $imports = array();
+    var $services = array();
+    var $service = '';
+
+    /**
+     * URL to WSDL file.
+     *
+     * @var string
+     */
+    var $uri;
+
+    /**
+     * Parse documentation in the WSDL?
+     *
+     * @var boolean
+     */
+    var $docs;
+
+    /**
+     * Proxy parameters.
+     *
+     * @var array
+     */
+    var $proxy;
+
+    /**
+     * Enable tracing in the generated proxy class?
+     *
+     * @var boolean
+     */
+    var $trace = false;
+
+    /**
+     * Use WSDL cache?
+     *
+     * @var boolean
+     */
+    var $cacheUse;
+
+    /**
+     * WSDL cache directory.
+     *
+     * @var string
+     */
+    var $cacheDir;
+
+    /**
+     * Cache maximum lifetime (in seconds).
+     *
+     * @var integer
+     */
+    var $cacheMaxAge;
+
+    /**
+     * Class to use for WSDL parsing. Can be overridden for special cases,
+     * subclasses, etc.
+     *
+     * @var string
+     */
+    var $wsdlParserClass = 'SOAP_WSDL_Parser';
+
+    /**
+     * Reserved PHP keywords.
+     *
+     * @link http://www.php.net/manual/en/reserved.php
+     *
+     * @var array
+     */
+    var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case',
+                           'catch', 'cfunction', 'class', 'clone', 'const',
+                           'continue', 'declare', 'default', 'die', 'do',
+                           'echo', 'else', 'elseif', 'empty', 'enddeclare',
+                           'endfor', 'endforeach', 'endif', 'endswitch',
+                           'endwhile', 'eval', 'exception', 'exit', 'extends',
+                           'final', 'for', 'foreach', 'function', 'global',
+                           'if', 'implements', 'include', 'include_once',
+                           'interface', 'isset', 'list', 'new', 'old_function',
+                           'or', 'php_user_filter', 'print', 'private',
+                           'protected', 'public', 'require', 'require_once',
+                           'return', 'static', 'switch', 'this', 'throw',
+                           'try', 'unset', 'use', 'var', 'while', 'xor');
+
+    /**
+     * Regular expressions for invalid PHP labels.
+     *
+     * @link http://www.php.net/manual/en/language.variables.php.
+     *
+     * @var string
+     */
+    var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/');
+
+    /**
+     * SOAP_WSDL constructor.
+     *
+     * @param string $wsdl_uri          URL to WSDL file.
+     * @param array $proxy              Options for HTTP_Request class
+     *                                  @see HTTP_Request.
+     * @param boolean|string $cacheUse  Use WSDL caching? The cache directory
+     *                                  if a string.
+     * @param integer $cacheMaxAge      Cache maximum lifetime (in seconds).
+     * @param boolean $docs             Parse documentation in the WSDL?
+     *
+     * @access public
+     */
+    function SOAP_WSDL($wsdl_uri    = false,
+                       $proxy       = array(),
+                       $cacheUse    = false,
+                       $cacheMaxAge = WSDL_CACHE_MAX_AGE,
+                       $docs        = false)
+    {
+        parent::SOAP_Base('WSDL');
+        $this->uri         = $wsdl_uri;
+        $this->proxy       = $proxy;
+        $this->cacheUse    = !empty($cacheUse);
+        $this->cacheMaxAge = $cacheMaxAge;
+        $this->docs        = $docs;
+        if (is_string($cacheUse)) {
+            $this->cacheDir = $cacheUse;
+        }
+
+        if ($wsdl_uri) {
+            if (!PEAR::isError($this->parseURL($wsdl_uri))) {
+                reset($this->services);
+                $this->service = key($this->services);
+            }
+        }
+    }
+
+    /**
+     * @deprecated  Use setService().
+     */
+    function set_service($service)
+    {
+        $this->setService($service);
+    }
+
+    /**
+     * Sets the service currently to be used.
+     *
+     * @param string $service  An (existing) service name.
+     */
+    function setService($service)
+    {
+        if (array_key_exists($service, $this->services)) {
+            $this->service = $service;
+        }
+    }
+
+    /**
+     * Fills the WSDL array tree with data from a WSDL file.
+     *
+     * @param string $wsdl_uri  URL to WSDL file.
+     */
+    function parseURL($wsdl_uri)
+    {
+        $parser =& new $this->wsdlParserClass($wsdl_uri, $this, $this->docs);
+
+        if ($parser->fault) {
+            $this->_raiseSoapFault($parser->fault);
+        }
+    }
+
+    /**
+     * Fills the WSDL array tree with data from one or more PHP class objects.
+     *
+     * @param mixed $wsdl_obj          An object or array of objects to add to
+     *                                 the internal WSDL tree.
+     * @param string $targetNamespace  The target namespace of schema types
+     *                                 etc.
+     * @param string $service_name     Name of the WSDL service.
+     * @param string $service_desc     Optional description of the WSDL
+     *                                 service.
+     */
+    function parseObject(&$wsdl_obj, $targetNamespace, $service_name,
+                         $service_desc = '')
+    {
+        $parser =& new SOAP_WSDL_ObjectParser($wsdl_obj, $this,
+                                              $targetNamespace, $service_name,
+                                              $service_desc);
+
+         if ($parser->fault) {
+             $this->_raiseSoapFault($parser->fault);
+         }
+    }
+
+    function getEndpoint($portName)
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
+                ? $this->services[$this->service]['ports'][$portName]['address']['location']
+                : $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri);
+    }
+
+    function _getPortName($operation, $service)
+    {
+        if (isset($this->services[$service]['ports'])) {
+            $ports = $this->services[$service]['ports'];
+            foreach ($ports as $port => $portAttrs) {
+                $type = $ports[$port]['type'];
+                if ($type == 'soap' &&
+                    isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
+                    return $port;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds the name of the first port that contains an operation of name
+     * $operation. Always returns a SOAP portName.
+     */
+    function getPortName($operation, $service = null)
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        if (!$service) {
+            $service = $this->service;
+        }
+        if (isset($this->services[$service]['ports'])) {
+            if ($portName = $this->_getPortName($operation, $service)) {
+                return $portName;
+            }
+        }
+        // Try any service in the WSDL.
+        foreach ($this->services as $serviceName => $service) {
+            if (isset($this->services[$serviceName]['ports'])) {
+                if ($portName = $this->_getPortName($operation, $serviceName)) {
+                    $this->service = $serviceName;
+                    return $portName;
+                }
+            }
+        }
+        return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri);
+    }
+
+    function getOperationData($portName, $operation)
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        if (!isset($this->services[$this->service]['ports'][$portName]['binding']) ||
+            !($binding = $this->services[$this->service]['ports'][$portName]['binding'])) {
+            return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri);
+        }
+
+        // Get operation data from binding.
+        if (is_array($this->bindings[$binding]['operations'][$operation])) {
+            $opData = $this->bindings[$binding]['operations'][$operation];
+        }
+        // get operation data from porttype
+        $portType = $this->bindings[$binding]['type'];
+        if (!$portType) {
+            return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri);
+        }
+        if (is_array($type = $this->portTypes[$portType][$operation])) {
+            if (isset($type['parameterOrder'])) {
+                $opData['parameterOrder'] = $type['parameterOrder'];
+            }
+            $opData['input'] = array_merge($opData['input'], $type['input']);
+            $opData['output'] = array_merge($opData['output'], $type['output']);
+        }
+        if (!$opData)
+            return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri);
+        $opData['parameters'] = false;
+        if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
+            $opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace'];
+        // Message data from messages.
+        $inputMsg = $opData['input']['message'];
+        if (is_array($this->messages[$inputMsg])) {
+            foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
+                if ($opData['style'] == 'document' &&
+                    $opData['input']['use'] == 'literal' &&
+                    $pname == 'parameters') {
+                    $opData['parameters'] = true;
+                    $opData['namespace'] = $this->namespaces[$pattrs['namespace']];
+                    $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
+                    if (isset($el['elements'])) {
+                        foreach ($el['elements'] as $elname => $elattrs) {
+                            $opData['input']['parts'][$elname] = $elattrs;
+                        }
+                    }
+                } else {
+                    $opData['input']['parts'][$pname] = $pattrs;
+                }
+            }
+        }
+        $outputMsg = $opData['output']['message'];
+        if (is_array($this->messages[$outputMsg])) {
+            foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
+                if ($opData['style'] == 'document' &&
+                    $opData['output']['use'] == 'literal' &&
+                    $pname == 'parameters') {
+
+                    $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
+                    if (isset($el['elements'])) {
+                        foreach ($el['elements'] as $elname => $elattrs) {
+                            $opData['output']['parts'][$elname] = $elattrs;
+                        }
+                    }
+                } else {
+                    $opData['output']['parts'][$pname] = $pattrs;
+                }
+            }
+        }
+        return $opData;
+    }
+
+    function matchMethod(&$operation)
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        // Overloading lowercases function names :(
+        foreach ($this->services[$this->service]['ports'] as $portAttrs) {
+            foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) {
+                if (strcasecmp($op, $operation) == 0) {
+                    $operation = $op;
+                }
+            }
+        }
+    }
+
+    /**
+     * Given a datatype, what function handles the processing?
+     *
+     * This is used for doc/literal requests where we receive a datatype, and
+     * we need to pass it to a method in out server class.
+     *
+     * @param string $datatype
+     * @param string $namespace
+     * @return string
+     * @access public
+     */
+    function getDataHandler($datatype, $namespace)
+    {
+        // See if we have an element by this name.
+        if (isset($this->namespaces[$namespace])) {
+            $namespace = $this->namespaces[$namespace];
+        }
+
+        if (isset($this->ns[$namespace])) {
+            $nsp = $this->ns[$namespace];
+            //if (!isset($this->elements[$nsp]))
+            //    $nsp = $this->namespaces[$nsp];
+            if (isset($this->elements[$nsp][$datatype])) {
+                $checkmessages = array();
+                // Find what messages use this datatype.
+                foreach ($this->messages as $messagename => $message) {
+                    foreach ($message as $part) {
+                        if ($part['type'] == $datatype) {
+                            $checkmessages[] = $messagename;
+                            break;
+                        }
+                    }
+                }
+                // Find the operation that uses this message.
+                foreach($this->portTypes as $porttype) {
+                    foreach ($porttype as $opname => $opinfo) {
+                        foreach ($checkmessages as $messagename) {
+                            if ($opinfo['input']['message'] == $messagename) {
+                                return $opname;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    function getSoapAction($portName, $operation)
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
+            return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
+        }
+
+        return false;
+    }
+
+    function getNamespace($portName, $operation)
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
+            return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
+        }
+
+        return false;
+    }
+
+    function getNamespaceAttributeName($namespace)
+    {
+        /* If it doesn't exist at first, flip the array and check again. */
+        if (empty($this->ns[$namespace])) {
+            $this->ns = array_flip($this->namespaces);
+        }
+
+        /* If it doesn't exist now, add it. */
+        if (empty($this->ns[$namespace])) {
+            return $this->addNamespace($namespace);
+        }
+
+        return $this->ns[$namespace];
+    }
+
+    function addNamespace($namespace)
+    {
+        if (!empty($this->ns[$namespace])) {
+            return $this->ns[$namespace];
+        }
+
+        $n = count($this->ns);
+        $attr = 'ns' . $n;
+        $this->namespaces['ns' . $n] = $namespace;
+        $this->ns[$namespace] = $attr;
+
+        return $attr;
+    }
+
+    function _validateString($string)
+    {
+        return preg_match('/^[\w_:#\/]+$/', $string);
+    }
+
+    function _addArg(&$args, &$argarray, $argname)
+    {
+        if ($args) {
+            $args .= ', ';
+        }
+        $args .= '$' . $argname;
+        if (!$this->_validateString($argname)) {
+            return;
+        }
+        if ($argarray) {
+            $argarray .= ', ';
+        }
+        $argarray .= "'$argname' => $" . $argname;
+    }
+
+    function _elementArg(&$args, &$argarray, &$_argtype, $_argname)
+    {
+        $comments = '';
+        $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
+        $tns = isset($this->ns[$el['namespace']])
+            ? $this->ns[$el['namespace']]
+            : $_argtype['namespace'];
+
+        if (!empty($el['complex']) ||
+            (isset($el['type']) &&
+             isset($this->complexTypes[$tns][$el['type']]))) {
+            // The element is a complex type.
+            $comments .= "        // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
+            $attrname = "{$_argtype['type']}_attr";
+            if (isset($el['type']) &&
+                isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
+                $comments .= "        // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
+            }
+            $comments .= "        \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
+            $comments .= "        \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
+            $this->_addArg($args, $argarray, $_argtype['type']);
+            if (isset($el['type']) &&
+                isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
+                if ($args) {
+                    $args .= ', ';
+                }
+                $args .= '$' . $attrname;
+            }
+        } elseif (isset($el['elements'])) {
+            foreach ($el['elements'] as $ename => $element) {
+                $comments .= "        \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" .
+                    (isset($element['type']) ? $element['type'] : false) .
+                    "', \$$ename);\n";
+                $this->_addArg($args, $argarray, $ename);
+            }
+        } else {
+            $comments .= "        \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n";
+            $this->_addArg($args, $argarray, $_argname);
+        }
+
+        return $comments;
+    }
+
+    function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname)
+    {
+        $comments = '';
+        if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
+            $comments  = "        // $_argname is a ComplexType {$_argtype['type']},\n" .
+                "        // refer to wsdl for more info\n";
+            if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
+                $comments .= "        // $_argname may require attributes, refer to wsdl for more info\n";
+            }
+            $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type'];
+            $comments .= "        \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
+        }
+
+        $this->_addArg($args, $argarray, $_argname);
+
+        return $comments;
+    }
+
+    /**
+     * Generates stub code from the WSDL that can be saved to a file or eval'd
+     * into existence.
+     */
+    function generateProxyCode($port = '', $classname = '')
+    {
+        if ($this->_isfault()) {
+            return $this->_getfault();
+        }
+
+        $multiport = count($this->services[$this->service]['ports']) > 1;
+        if (!$port) {
+            reset($this->services[$this->service]['ports']);
+            $port = current($this->services[$this->service]['ports']);
+        }
+        // XXX currently do not support HTTP ports
+        if ($port['type'] != 'soap') {
+            return null;
+        }
+
+        // XXX currentPort is BAD
+        $clienturl = $port['address']['location'];
+        if (!$classname) {
+            if ($multiport || $port) {
+                $classname = 'WebService_' . $this->service . '_' . $port['name'];
+            } else {
+                $classname = 'WebService_' . $this->service;
+            }
+            $classname = $this->_sanitize($classname);
+        }
+
+        if (!$this->_validateString($classname)) {
+            return null;
+        }
+
+        if (is_array($this->proxy) && count($this->proxy)) {
+            $class = "class $classname extends SOAP_Client\n{\n" .
+            "    function $classname(\$path = '$clienturl')\n    {\n" .
+            "        \$this->SOAP_Client(\$path, 0, 0,\n" .
+            '                           array(';
+
+            foreach ($this->proxy as $key => $val) {
+                if (is_array($val)) {
+                    $class .= "'$key' => array(";
+                    foreach ($val as $key2 => $val2) {
+                        $class .= "'$key2' => '$val2', ";
+                    }
+                    $class .= ')';
+                } else {
+                    $class .= "'$key' => '$val', ";
+                }
+            }
+            $class .= "));\n    }\n";
+            $class = str_replace(', ))', '))', $class);
+        } else {
+            $class = "class $classname extends SOAP_Client\n{\n" .
+            "    function $classname(\$path = '$clienturl')\n    {\n" .
+            "        \$this->SOAP_Client(\$path, 0);\n" .
+            "    }\n";
+        }
+
+        // Get the binding, from that get the port type.
+        $primaryBinding = $port['binding'];
+        $primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding);
+        $portType = $this->bindings[$primaryBinding]['type'];
+        $portType = preg_replace("/^(.*:)/", '', $portType);
+        $style = $this->bindings[$primaryBinding]['style'];
+
+        // XXX currentPortType is BAD
+        foreach ($this->portTypes[$portType] as $opname => $operation) {
+            $binding = $this->bindings[$primaryBinding]['operations'][$opname];
+            if (isset($binding['soapAction'])) {
+                $soapaction = $binding['soapAction'];
+            } else {
+                $soapaction = null;
+            }
+            if (isset($binding['style'])) {
+                $opstyle = $binding['style'];
+            } else {
+                $opstyle = $style;
+            }
+            $use = $binding['input']['use'];
+            if ($use == 'encoded') {
+                $namespace = $binding['input']['namespace'];
+            } else {
+                $bindingType = $this->bindings[$primaryBinding]['type'];
+                $ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
+                $namespace = $this->namespaces[$ns];
+            }
+
+            $args = '';
+            $argarray = '';
+            $comments = '';
+            $wrappers = '';
+            foreach ($operation['input'] as $argname => $argtype) {
+                if ($argname == 'message') {
+                    foreach ($this->messages[$argtype] as $_argname => $_argtype) {
+                        $_argname = $this->_sanitize($_argname);
+                        if ($opstyle == 'document' && $use == 'literal' &&
+                            $_argtype['name'] == 'parameters') {
+                            // The type or element refered to is used for
+                            // parameters.
+                            $elattrs = null;
+                            $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
+
+                            if ($el['complex']) {
+                                $namespace = $this->namespaces[$_argtype['namespace']];
+                                // XXX need to wrap the parameters in a
+                                // SOAP_Value.
+                            }
+                            if (isset($el['elements'])) {
+                                foreach ($el['elements'] as $elname => $elattrs) {
+                                    $elname = $this->_sanitize($elname);
+                                    // Is the element a complex type?
+                                    if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
+                                        $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
+                                    } else {
+                                        $this->_addArg($args, $argarray, $elname);
+                                    }
+                                }
+                            }
+                            if ($el['complex'] && $argarray) {
+                                $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name'];
+                                $comments .= "        \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
+                                $argarray = "'{$el['name']}' => \${$el['name']}";
+                            }
+                        } else {
+                            if (isset($_argtype['element'])) {
+                                // Element argument.
+                                $comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']);
+                            } else {
+                                // Complex type argument.
+                                $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Validate entries.
+
+            // Operation names are function names, so try to make sure it's
+            // legal. This could potentially cause collisions, but let's try
+            // to make everything callable and see how many problems that
+            // causes.
+            $opname_php = $this->_sanitize($opname);
+            if (!$this->_validateString($opname_php)) {
+                return null;
+            }
+
+            if ($argarray) {
+                $argarray = "array($argarray)";
+            } else {
+                $argarray = 'null';
+            }
+
+            $class .= "    function &$opname_php($args)\n    {\n$comments$wrappers" .
+                "        \$result = \$this->call('$opname',\n" .
+                "                              \$v = $argarray,\n" .
+                "                              array('namespace' => '$namespace',\n" .
+                "                                    'soapaction' => '$soapaction',\n" .
+                "                                    'style' => '$opstyle',\n" .
+                "                                    'use' => '$use'" .
+                ($this->trace ? ",\n                                    'trace' => true" : '') . "));\n" .
+                "        return \$result;\n" .
+                "    }\n";
+        }
+
+        $class .= "}\n";
+
+        return $class;
+    }
+
+    function generateAllProxies()
+    {
+        $proxycode = '';
+        foreach (array_keys($this->services[$this->service]['ports']) as $key) {
+            $port =& $this->services[$this->service]['ports'][$key];
+            $proxycode .= $this->generateProxyCode($port);
+        }
+        return $proxycode;
+    }
+
+    function &getProxy($port = '', $name = '')
+    {
+        if ($this->_isfault()) {
+            $fault =& $this->_getfault();
+            return $fault;
+        }
+
+        $multiport = count($this->services[$this->service]['ports']) > 1;
+
+        if (!$port) {
+            reset($this->services[$this->service]['ports']);
+            $port = current($this->services[$this->service]['ports']);
+        }
+
+        if ($multiport || $port) {
+            $classname = 'WebService_' . $this->service . '_' . $port['name'];
+        } else {
+            $classname = 'WebService_' . $this->service;
+        }
+
+        if ($name) {
+            $classname = $name . '_' . $classname;
+        }
+
+        $classname = $this->_sanitize($classname);
+        if (!class_exists($classname)) {
+            $proxy = $this->generateProxyCode($port, $classname);
+            require_once 'SOAP/Client.php';
+            eval($proxy);
+        }
+        $proxy =& new $classname;
+
+        return $proxy;
+    }
+
+    /**
+     * Sanitizes a SOAP value, method or class name so that it can be used as
+     * a valid PHP identifier. Invalid characters are converted into
+     * underscores and reserved words are prefixed with an underscore.
+     *
+     * @param string $name  The identifier to sanitize.
+     *
+     * @return string  The sanitized identifier.
+     */
+    function _sanitize($name)
+    {
+        $name = preg_replace($this->_invalid, '_', $name);
+        if (in_array($name, $this->_reserved)) {
+            $name = '_' . $name;
+        }
+        return $name;
+    }
+
+    function &_getComplexTypeForElement($name, $namespace)
+    {
+        $t = null;
+        if (isset($this->ns[$namespace]) &&
+            isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
+
+            $type = $this->elements[$this->ns[$namespace]][$name]['type'];
+            $ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
+
+            if (isset($this->complexTypes[$ns][$type])) {
+                $t = $this->complexTypes[$ns][$type];
+            }
+        }
+        return $t;
+    }
+
+    function getComplexTypeNameForElement($name, $namespace)
+    {
+        $t = $this->_getComplexTypeForElement($name, $namespace);
+        if ($t) {
+            return $t['name'];
+        }
+        return null;
+    }
+
+    function getComplexTypeChildType($ns, $name, $child_ns, $child_name)
+    {
+        // Is the type an element?
+        $t = $this->_getComplexTypeForElement($name, $ns);
+        if ($t) {
+            // No, get it from complex types directly.
+            if (isset($t['elements'][$child_name]['type']))
+                return $t['elements'][$child_name]['type'];
+        } elseif (isset($this->ns[$ns]) &&
+                  isset($this->elements[$this->ns[$ns]][$name]['complex']) &&
+                  $this->elements[$this->ns[$ns]][$name]['complex']) {
+            // Type is not an element but complex.
+            return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
+        }
+        return null;
+    }
+
+    function getSchemaType($type, $name, $type_namespace)
+    {
+        // see if it's a complex type so we can deal properly with
+        // SOAPENC:arrayType.
+        if ($name && $type) {
+            // XXX TODO:
+            // look up the name in the wsdl and validate the type.
+            foreach ($this->complexTypes as $types) {
+                if (isset($types[$type])) {
+                    if (isset($types[$type]['type'])) {
+                        list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type]['arrayType'])
+                            ? $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType'])
+                            : array($this->namespaces[$types[$type]['namespace']], null, 0);
+                        return array($types[$type]['type'], $arraytype, $arraytype_ns, $array_depth);
+                    }
+                    if (isset($types[$type]['arrayType'])) {
+                        list($arraytype_ns, $arraytype, $array_depth) =
+                            $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']);
+                        return array('Array', $arraytype, $arraytype_ns, $array_depth);
+                    }
+                    if (!empty($types[$type]['elements'][$name])) {
+                        $type = $types[$type]['elements']['type'];
+                        return array($type, null, $this->namespaces[$types[$type]['namespace']], null);
+                    }
+                    break;
+                }
+            }
+        }
+        if ($type && $type_namespace) {
+            $arrayType = null;
+            // XXX TODO:
+            // this code currently handles only one way of encoding array
+            // types in wsdl need to do a generalized function to figure out
+            // complex types
+            $p = $this->ns[$type_namespace];
+            if ($p && !empty($this->complexTypes[$p][$type])) {
+                if ($arrayType = $this->complexTypes[$p][$type]['arrayType']) {
+                    $type = 'Array';
+                } elseif ($this->complexTypes[$p][$type]['order'] == 'sequence' &&
+                          array_key_exists('elements', $this->complexTypes[$p][$type])) {
+                    reset($this->complexTypes[$p][$type]['elements']);
+                    // assume an array
+                    if (count($this->complexTypes[$p][$type]['elements']) == 1) {
+                        $arg = current($this->complexTypes[$p][$type]['elements']);
+                        $arrayType = $arg['type'];
+                        $type = 'Array';
+                    } else {
+                        foreach ($this->complexTypes[$p][$type]['elements'] as $element) {
+                            if ($element['name'] == $type) {
+                                $arrayType = $element['type'];
+                                $type = $element['type'];
+                            }
+                        }
+                    }
+                } else {
+                    $type = 'Struct';
+                }
+                return array($type, $arrayType, $type_namespace, null);
+            }
+        }
+        return array(null, null, null, null);
+    }
+
+    /**
+     * Recurse through the WSDL structure looking for the innermost array type
+     * of multi-dimensional arrays.
+     *
+     * Takes a namespace prefix and a type, which can be in the form 'type' or
+     * 'type[]', and returns the full namespace URI, the type of the most
+     * deeply nested array type found, and the number of levels of nesting.
+     *
+     * @access private
+     * @return mixed array or nothing
+     */
+    function _getDeepestArrayType($nsPrefix, $arrayType)
+    {
+        static $trail = array();
+
+        $arrayType = ereg_replace('\[\]$', '', $arrayType);
+
+        // Protect against circular references XXX We really need to remove
+        // trail from this altogether (it's very inefficient and in the wrong
+        // place!) and put circular reference checking in when the WSDL info
+        // is generated in the first place
+        if (array_search($nsPrefix . ':' . $arrayType, $trail)) {
+            return array(null, null, -count($trail));
+        }
+
+        if (array_key_exists($nsPrefix, $this->complexTypes) &&
+            array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) &&
+            array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) {
+            $trail[] = $nsPrefix . ':' . $arrayType;
+            $result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
+                                                  $this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
+            return array($result[0], $result[1], $result[2] + 1);
+        }
+        return array($this->namespaces[$nsPrefix], $arrayType, 0);
+    }
+
+}
+
+class SOAP_WSDL_Cache extends SOAP_Base
+{
+    /**
+     * Use WSDL cache?
+     *
+     * @var boolean
+     */
+    var $_cacheUse;
+
+    /**
+     * WSDL cache directory.
+     *
+     * @var string
+     */
+    var $_cacheDir;
+
+    /**
+     * Cache maximum lifetime (in seconds)
+     *
+     * @var integer
+     */
+    var $_cacheMaxAge;
+
+    /**
+     * Constructor.
+     *
+     * @param boolean $cashUse      Use caching?
+     * @param integer $cacheMaxAge  Cache maximum lifetime (in seconds)
+     */
+    function SOAP_WSDL_Cache($cacheUse = false,
+                             $cacheMaxAge = WSDL_CACHE_MAX_AGE,
+                             $cacheDir = null)
+    {
+        parent::SOAP_Base('WSDLCACHE');
+        $this->_cacheUse = $cacheUse;
+        $this->_cacheDir = $cacheDir;
+        $this->_cacheMaxAge = $cacheMaxAge;
+    }
+
+    /**
+     * Returns the path to the cache and creates it, if it doesn't exist.
+     *
+     * @private
+     *
+     * @return string  The directory to use for the cache.
+     */
+    function _cacheDir()
+    {
+        if (!empty($this->_cacheDir)) {
+            $dir = $this->_cacheDir;
+        } else {
+            $dir = getenv('WSDLCACHE');
+            if (empty($dir)) {
+                $dir = './wsdlcache';
+            }
+        }
+        @mkdir($dir, 0700);
+        return $dir;
+    }
+
+    /**
+     * Retrieves a file from cache if it exists, otherwise retreive from net,
+     * add to cache, and return from cache.
+     *
+     * @param  string   URL to WSDL
+     * @param  array    proxy parameters
+     * @param  int      expected MD5 of WSDL URL
+     * @access public
+     * @return string  data
+     */
+    function get($wsdl_fname, $proxy_params = array(), $cache = 0)
+    {
+        $cachename = $md5_wsdl = $file_data = '';
+        if ($this->_cacheUse) {
+            // Try to retrieve WSDL from cache
+            $cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl';
+            if (file_exists($cachename)) {
+                $wf = fopen($cachename, 'rb');
+                if ($wf) {
+                    // Reading cached file
+                    $file_data = fread($wf, filesize($cachename));
+                    $md5_wsdl = md5($file_data);
+                    fclose($wf);
+                }
+                if ($cache) {
+                    if ($cache != $md5_wsdl) {
+                        return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
+                    }
+                } else {
+                    $fi = stat($cachename);
+                    $cache_mtime = $fi[8];
+                    //print cache_mtime, time()
+                    if ($cache_mtime + $this->_cacheMaxAge < time()) {
+                        // expired
+                        $md5_wsdl = ''; // refetch
+                    }
+                }
+            }
+        }
+
+        if (!$md5_wsdl) {
+            // Not cached or not using cache. Retrieve WSDL from URL
+
+            // is it a local file?
+            // this section should be replace by curl at some point
+            if (!preg_match('/^(https?|file):\/\//', $wsdl_fname)) {
+                if (!file_exists($wsdl_fname)) {
+                    return $this->_raiseSoapFault("Unable to read local WSDL $wsdl_fname", $wsdl_fname);
+                }
+                $file_data = file_get_contents($wsdl_fname);
+            } else {
+                $uri = explode('?', $wsdl_fname);
+                $rq =& new HTTP_Request($uri[0], $proxy_params);
+                // the user agent HTTP_Request uses fouls things up
+                if (isset($uri[1])) {
+                    $rq->addRawQueryString($uri[1]);
+                }
+
+                if (isset($proxy_params['proxy_host']) &&
+                    isset($proxy_params['proxy_port']) &&
+                    isset($proxy_params['proxy_user']) &&
+                    isset($proxy_params['proxy_pass'])) {
+                    $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port'],
+                                  $proxy_params['proxy_user'], $proxy_params['proxy_pass']);
+                } elseif (isset($proxy_params['proxy_host']) &&
+                          isset($proxy_params['proxy_port'])) {
+                    $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port']);
+                }
+
+                $result = $rq->sendRequest();
+                if (PEAR::isError($result)) {
+                    return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname);
+                }
+                $file_data = $rq->getResponseBody();
+                if (!$file_data) {
+                    return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname);
+                }
+            }
+
+            $md5_wsdl = md5($file_data);
+
+            if ($this->_cacheUse) {
+                $fp = fopen($cachename, "wb");
+                fwrite($fp, $file_data);
+                fclose($fp);
+            }
+        }
+        if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
+            return $this->_raiseSoapFault("WSDL Checksum error!", $wsdl_fname);
+        }
+        return $file_data;
+    }
+
+}
+
+class SOAP_WSDL_Parser extends SOAP_Base
+{
+
+    /**
+     * Define internal arrays of bindings, ports, operations,
+     * messages, etc.
+     */
+    var $currentMessage;
+    var $currentOperation;
+    var $currentPortType;
+    var $currentBinding;
+    var $currentPort;
+
+    /**
+     * Parser vars.
+     */
+    var $cache;
+
+    var $tns = null;
+    var $soapns = array('soap');
+    var $uri = '';
+    var $wsdl = null;
+
+    var $status = '';
+    var $element_stack = array();
+    var $parentElement = '';
+
+    var $schema = '';
+    var $schemaStatus = '';
+    var $schema_stack = array();
+    var $currentComplexType;
+    var $schema_element_stack = array();
+    var $currentElement;
+
+    /**
+     * Constructor.
+     */
+    function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false)
+    {
+        parent::SOAP_Base('WSDLPARSER');
+        $this->cache =& new SOAP_WSDL_Cache($wsdl->cacheUse,
+                                            $wsdl->cacheMaxAge,
+                                            $wsdl->cacheDir);
+        $this->uri = $uri;
+        $this->wsdl = &$wsdl;
+        $this->docs = $docs;
+        $this->parse($uri);
+    }
+
+    function parse($uri)
+    {
+        // Check whether content has been read.
+        $fd = $this->cache->get($uri, $this->wsdl->proxy);
+        if (PEAR::isError($fd)) {
+            return $this->_raiseSoapFault($fd);
+        }
+
+        // Create an XML parser.
+        $parser = xml_parser_create();
+        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
+        xml_set_object($parser, $this);
+        xml_set_element_handler($parser, 'startElement', 'endElement');
+        if ($this->docs) {
+            xml_set_character_data_handler($parser, 'characterData');
+        }
+
+        if (!xml_parse($parser, $fd, true)) {
+            $detail = sprintf('XML error on line %d: %s',
+                              xml_get_current_line_number($parser),
+                              xml_error_string(xml_get_error_code($parser)));
+            return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
+        }
+        xml_parser_free($parser);
+        return true;
+    }
+
+    /**
+     * start-element handler
+     */
+    function startElement($parser, $name, $attrs)
+    {
+        // Get element prefix.
+        $qname = new QName($name);
+        if ($qname->ns) {
+            $ns = $qname->ns;
+            if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) {
+                $name = $qname->name;
+            }
+        }
+        $this->currentTag = $qname->name;
+        $this->parentElement = '';
+        $stack_size = count($this->element_stack);
+        if ($stack_size) {
+            $this->parentElement = $this->element_stack[$stack_size - 1];
+        }
+        $this->element_stack[] = $this->currentTag;
+
+        // Find status, register data.
+        switch ($this->status) {
+        case 'types':
+            // sect 2.2 wsdl:types
+            // children: xsd:schema
+            $parent_tag = '';
+            $stack_size = count($this->schema_stack);
+            if ($stack_size) {
+                $parent_tag = $this->schema_stack[$stack_size - 1];
+            }
+
+            switch ($qname->name) {
+            case 'schema':
+                // No parent should be in the stack.
+                if (!$parent_tag || $parent_tag == 'types') {
+                    if (array_key_exists('targetNamespace', $attrs)) {
+                        $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
+                    } else {
+                        $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
+                    }
+                    $this->wsdl->complexTypes[$this->schema] = array();
+                    $this->wsdl->elements[$this->schema] = array();
+                }
+                break;
+
+            case 'complexType':
+                if ($parent_tag == 'schema') {
+                    $this->currentComplexType = $attrs['name'];
+                    if (!isset($attrs['namespace'])) {
+                        $attrs['namespace'] = $this->schema;
+                    }
+                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs;
+                    if (array_key_exists('base', $attrs)) {
+                        $qn = new QName($attrs['base']);
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns;
+                    } else {
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
+                    }
+                    $this->schemaStatus = 'complexType';
+                } else {
+                    $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true;
+                }
+                break;
+
+            case 'element':
+                if (isset($attrs['type'])) {
+                    $qn = new QName($attrs['type']);
+                    $attrs['type'] = $qn->name;
+                    if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) {
+                        $attrs['namespace'] = $qn->ns;
+                    }
+                }
+
+                $parentElement = '';
+                $stack_size = count($this->schema_element_stack);
+                if ($stack_size > 0) {
+                    $parentElement = $this->schema_element_stack[$stack_size - 1];
+                }
+
+                if (isset($attrs['ref'])) {
+                    $qn = new QName($attrs['ref']);
+                    $this->currentElement = $qn->name;
+                } else {
+                    $this->currentElement = $attrs['name'];
+                }
+                $this->schema_element_stack[] = $this->currentElement;
+                if (!isset($attrs['namespace'])) {
+                    $attrs['namespace'] = $this->schema;
+                }
+
+                if ($parent_tag == 'schema') {
+                    $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs;
+                    $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false;
+                    $this->schemaStatus = 'element';
+                } elseif ($this->currentComplexType) {
+                    // we're inside a complexType
+                    if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) &&
+                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence')
+                        && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') {
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null;
+                    }
+                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs;
+                } else {
+                    $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs;
+                }
+                break;
+
+            case 'complexContent':
+            case 'simpleContent':
+                break;
+
+            case 'extension':
+            case 'restriction':
+                if ($this->schemaStatus == 'complexType') {
+                    if (!empty($attrs['base'])) {
+                        $qn = new QName($attrs['base']);
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
+
+                        // Types that extend from other types aren't
+                        // *of* those types. Reflect this by denoting
+                        // which type they extend. I'm leaving the
+                        // 'type' setting here since I'm not sure what
+                        // removing it might break at the moment.
+                        if ($qname->name == 'extension') {
+                            $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name;
+                        }
+                    } else {
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
+                    }
+                }
+                break;
+
+            case 'sequence':
+                if ($this->schemaStatus == 'complexType') {
+                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
+                    if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
+                    }
+                }
+                break;
+
+            case 'all':
+                $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
+                if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
+                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
+                }
+                break;
+
+            case 'choice':
+                $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
+                if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
+                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
+                }
+
+            case 'attribute':
+                if ($this->schemaStatus == 'complexType') {
+                    if (isset($attrs['name'])) {
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs;
+                    } else {
+                        if (isset($attrs['ref'])) {
+                            $q = new QName($attrs['ref']);
+                            foreach ($attrs as $k => $v) {
+                                if ($k != 'ref' && strstr($k, $q->name)) {
+                                    $vq = new QName($v);
+                                    if ($q->name == 'arrayType') {
+                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo;
+                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
+                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->ns;
+                                    } else {
+                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+
+            $this->schema_stack[] = $qname->name;
+            break;
+
+        case 'message':
+            // sect 2.3 wsdl:message child wsdl:part
+            switch ($qname->name) {
+            case 'part':
+                $qn = null;
+                if (isset($attrs['type'])) {
+                    $qn = new QName($attrs['type']);
+                } elseif (isset($attrs['element'])) {
+                    $qn = new QName($attrs['element']);
+                }
+                if ($qn) {
+                    $attrs['type'] = $qn->name;
+                    $attrs['namespace'] = $qn->ns;
+                }
+                $this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs;
+                // error in wsdl
+
+            case 'documentation':
+                break;
+
+            default:
+                break;
+            }
+            break;
+
+        case 'portType':
+            // sect 2.4
+            switch ($qname->name) {
+            case 'operation':
+                // attributes: name
+                // children: wsdl:input wsdl:output wsdl:fault
+                $this->currentOperation = $attrs['name'];
+                $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs;
+                break;
+
+            case 'input':
+            case 'output':
+            case 'fault':
+                // wsdl:input wsdl:output wsdl:fault
+                // attributes: name message parameterOrder(optional)
+                if ($this->currentOperation) {
+                    if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) {
+                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name], $attrs);
+                    } else {
+                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs;
+                    }
+                    if (array_key_exists('message', $attrs)) {
+                        $qn = new QName($attrs['message']);
+                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name;
+                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns;
+                    }
+                }
+                break;
+
+            case 'documentation':
+                break;
+
+            default:
+                break;
+            }
+            break;
+
+        case 'binding':
+            $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
+            switch ($ns) {
+            case SCHEMA_SOAP:
+            case SCHEMA_SOAP12:
+                // this deals with wsdl section 3 soap binding
+                switch ($qname->name) {
+                case 'binding':
+                    // sect 3.3
+                    // soap:binding, attributes: transport(required), style(optional, default = document)
+                    // if style is missing, it is assumed to be 'document'
+                    if (!isset($attrs['style'])) {
+                        $attrs['style'] = 'document';
+                    }
+                    $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
+                    break;
+
+                case 'operation':
+                    // sect 3.4
+                    // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
+                    if (!isset($attrs['style'])) {
+                        $attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style'];
+                    }
+                    if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) {
+                        $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = array_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation], $attrs);
+                    } else {
+                        $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs;
+                    }
+                    break;
+
+                case 'body':
+                    // sect 3.5
+                    // soap:body attributes:
+                    // part - optional.  listed parts must appear in body, missing means all parts appear in body
+                    // use - required. encoded|literal
+                    // encodingStyle - optional.  space seperated list of encodings (uri's)
+                    $this->wsdl->bindings[$this->currentBinding]
+                                    ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
+                    break;
+
+                case 'fault':
+                    // sect 3.6
+                    // soap:fault attributes: name use  encodingStyle namespace
+                    $this->wsdl->bindings[$this->currentBinding]
+                                    ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
+                    break;
+
+                case 'header':
+                    // sect 3.7
+                    // soap:header attributes: message part use encodingStyle namespace
+                    $this->wsdl->bindings[$this->currentBinding]
+                                    ['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
+                    break;
+
+                case 'headerfault':
+                    // sect 3.7
+                    // soap:header attributes: message part use encodingStyle namespace
+                    $header = count($this->wsdl->bindings[$this->currentBinding]
+                                    ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1;
+                    $this->wsdl->bindings[$this->currentBinding]
+                                    ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs;
+                    break;
+
+                case 'documentation':
+                    break;
+
+                default:
+                    // error!  not a valid element inside binding
+                    break;
+                }
+                break;
+
+            case SCHEMA_WSDL:
+                // XXX verify correct namespace
+                // for now, default is the 'wsdl' namespace
+                // other possible namespaces include smtp, http, etc. for alternate bindings
+                switch ($qname->name) {
+                case 'operation':
+                    // sect 2.5
+                    // wsdl:operation attributes: name
+                    $this->currentOperation = $attrs['name'];
+                    break;
+
+                case 'output':
+                case 'input':
+                case 'fault':
+                    // sect 2.5
+                    // wsdl:input attributes: name
+                    $this->opStatus = $qname->name;
+                    break;
+
+                case 'documentation':
+                    break;
+
+                default:
+                    break;
+                }
+                break;
+
+            case SCHEMA_WSDL_HTTP:
+                switch ($qname->name) {
+                case 'binding':
+                    // sect 4.4
+                    // http:binding attributes: verb
+                    // parent: wsdl:binding
+                    $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
+                    break;
+
+                case 'operation':
+                    // sect 4.5
+                    // http:operation attributes: location
+                    // parent: wsdl:operation
+                    $this->wsdl->bindings[$this->currentBinding]['operations']
+                                                        [$this->currentOperation] = $attrs;
+                    break;
+
+                case 'urlEncoded':
+                    // sect 4.6
+                    // http:urlEncoded attributes: location
+                    // parent: wsdl:input wsdl:output etc.
+                    $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
+                                                        [$this->currentOperation]['uri'] = 'urlEncoded';
+                    break;
+
+                case 'urlReplacement':
+                    // sect 4.7
+                    // http:urlReplacement attributes: location
+                    // parent: wsdl:input wsdl:output etc.
+                    $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
+                                                        [$this->currentOperation]['uri'] = 'urlReplacement';
+                    break;
+
+                case 'documentation':
+                    break;
+
+                default:
+                    // error
+                    break;
+                }
+
+            case SCHEMA_MIME:
+                // sect 5
+                // all mime parts are children of wsdl:input, wsdl:output, etc.
+                // unsuported as of yet
+                switch ($qname->name) {
+                case 'content':
+                    // sect 5.3 mime:content
+                    // <mime:content part="nmtoken"? type="string"?/>
+                    // part attribute only required if content is child of multipart related,
+                    //        it contains the name of the part
+                    // type attribute contains the mime type
+                case 'multipartRelated':
+                    // sect 5.4 mime:multipartRelated
+                case 'part':
+                case 'mimeXml':
+                    // sect 5.6 mime:mimeXml
+                    // <mime:mimeXml part="nmtoken"?/>
+                    //
+                case 'documentation':
+                    break;
+
+                default:
+                    // error
+                    break;
+                }
+
+            case SCHEMA_DIME:
+                // DIME is defined in:
+                // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
+                // all DIME parts are children of wsdl:input, wsdl:output, etc.
+                // unsuported as of yet
+                switch ($qname->name) {
+                case 'message':
+                    // sect 4.1 dime:message
+                    // appears in binding section
+                    $this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs;
+                    break;
+
+                default:
+                    break;
+                }
+
+            default:
+                break;
+            }
+            break;
+
+        case 'service':
+            $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
+
+            switch ($qname->name) {
+            case 'port':
+                // sect 2.6 wsdl:port attributes: name binding
+                $this->currentPort = $attrs['name'];
+                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs;
+                // XXX hack to deal with binding namespaces
+                $qn = new QName($attrs['binding']);
+                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name;
+                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns;
+                break;
+
+            case 'address':
+                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs;
+                // what TYPE of port is it?  SOAP or HTTP?
+                $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
+                switch ($ns) {
+                case SCHEMA_WSDL_HTTP:
+                    $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http';
+                    break;
+
+                case SCHEMA_SOAP:
+                    $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
+                    break;
+
+                default:
+                    // Shouldn't happen, we'll assume SOAP.
+                    $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
+                }
+
+                break;
+
+            case 'documentation':
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        // Top level elements found under wsdl:definitions.
+        switch ($qname->name) {
+        case 'import':
+            // sect 2.1.1 wsdl:import attributes: namespace location
+            if ((isset($attrs['location']) || isset($attrs['schemaLocation'])) &&
+                !isset($this->wsdl->imports[$attrs['namespace']])) {
+                $uri = isset($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation'];
+                $location = @parse_url($uri);
+                if (!isset($location['scheme'])) {
+                    $base = @parse_url($this->uri);
+                    $uri = $this->mergeUrl($base, $uri);
+                }
+
+                $this->wsdl->imports[$attrs['namespace']] = $attrs;
+                $import_parser_class = get_class($this);
+                $import_parser =& new $import_parser_class($uri, $this->wsdl, $this->docs);
+                if ($import_parser->fault) {
+                    unset($this->wsdl->imports[$attrs['namespace']]);
+                    return false;
+                }
+                $this->currentImport = $attrs['namespace'];
+            }
+            // Continue on to the 'types' case - lack of break; is
+            // intentional.
+
+        case 'types':
+            // sect 2.2 wsdl:types
+            $this->status = 'types';
+            break;
+
+        case 'schema':
+            // We can hit this at the top level if we've been asked to
+            // import an XSD file.
+            if (!empty($attrs['targetNamespace'])) {
+                $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
+            } else {
+                $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
+            }
+            $this->wsdl->complexTypes[$this->schema] = array();
+            $this->wsdl->elements[$this->schema] = array();
+            $this->schema_stack[] = $qname->name;
+            $this->status = 'types';
+            break;
+
+        case 'message':
+            // sect 2.3 wsdl:message attributes: name children:wsdl:part
+            $this->status = 'message';
+            if (isset($attrs['name'])) {
+                $this->currentMessage = $attrs['name'];
+                $this->wsdl->messages[$this->currentMessage] = array();
+            }
+            break;
+
+        case 'portType':
+            // sect 2.4 wsdl:portType
+            // attributes: name
+            // children: wsdl:operation
+            $this->status = 'portType';
+            $this->currentPortType = $attrs['name'];
+            $this->wsdl->portTypes[$this->currentPortType] = array();
+            break;
+
+        case 'binding':
+            // sect 2.5 wsdl:binding attributes: name type
+            // children: wsdl:operation soap:binding http:binding
+            if ($qname->ns && $qname->ns != $this->tns) {
+                break;
+            }
+            $this->status = 'binding';
+            $this->currentBinding = $attrs['name'];
+            $qn = new QName($attrs['type']);
+            $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name;
+            $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns;
+            break;
+
+        case 'service':
+            // sect 2.7 wsdl:service attributes: name children: ports
+            $this->currentService = $attrs['name'];
+            $this->wsdl->services[$this->currentService]['ports'] = array();
+            $this->status = 'service';
+            break;
+
+        case 'definitions':
+            // sec 2.1 wsdl:definitions
+            // attributes: name targetNamespace xmlns:*
+            // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
+            $this->wsdl->definition = $attrs;
+            foreach ($attrs as $key => $value) {
+                if (strstr($key, 'xmlns:') !== false) {
+                    $qn = new QName($key);
+                    // XXX need to refactor ns handling.
+                    $this->wsdl->namespaces[$qn->name] = $value;
+                    $this->wsdl->ns[$value] = $qn->name;
+                    if ($key == 'targetNamespace' ||
+                        strcasecmp($value,SOAP_SCHEMA) == 0) {
+                        $this->soapns[] = $qn->name;
+                    } else {
+                        if (in_array($value, $this->_XMLSchema)) {
+                            $this->wsdl->xsd = $value;
+                        }
+                    }
+                }
+            }
+            if (isset($ns) && $ns) {
+                $namespace = 'xmlns:' . $ns;
+                if (!$this->wsdl->definition[$namespace]) {
+                    return $this->_raiseSoapFault("parse error, no namespace for $namespace", $this->uri);
+                }
+                $this->tns = $ns;
+            }
+            break;
+        }
+    }
+
+    /**
+     * end-element handler.
+     */
+    function endElement($parser, $name)
+    {
+        $stacksize = count($this->element_stack);
+        if ($stacksize) {
+            if ($this->element_stack[$stacksize - 1] == 'definitions') {
+                $this->status = '';
+            }
+            array_pop($this->element_stack);
+        }
+
+        if (stristr($name, 'schema')) {
+            array_pop($this->schema_stack);
+            $this->schema = '';
+        }
+
+        if ($this->schema) {
+            array_pop($this->schema_stack);
+            if (count($this->schema_stack) <= 1) {
+                /* Correct the type for sequences with multiple
+                 * elements. */
+                if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])
+                    && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array'
+                    && array_key_exists('elements', $this->wsdl->complexTypes[$this->schema][$this->currentComplexType])
+                    && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) {
+                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
+                }
+            }
+            if (stristr($name, 'complexType')) {
+                $this->currentComplexType = '';
+                if (count($this->schema_element_stack)) {
+                    $this->currentElement = array_pop($this->schema_element_stack);
+                } else {
+                    $this->currentElement = '';
+                }
+            } elseif (stristr($name, 'element')) {
+                if (count($this->schema_element_stack)) {
+                    $this->currentElement = array_pop($this->schema_element_stack);
+                } else {
+                    $this->currentElement = '';
+                }
+            }
+        }
+    }
+
+    /**
+     * Element content handler.
+     */
+    function characterData($parser, $data)
+    {
+        // Store the documentation in the WSDL file.
+        if ($this->currentTag == 'documentation') {
+            $data = trim(preg_replace('/\s+/', ' ', $data));
+            if (!strlen($data)) {
+                return;
+            }
+
+            switch ($this->status) {
+            case 'service':
+                $ptr =& $this->wsdl->services[$this->currentService];
+                break;
+
+            case 'portType':
+                $ptr =& $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation];
+                break;
+
+            case 'binding':
+                $ptr =& $this->wsdl->bindings[$this->currentBinding];
+                break;
+
+            case 'message':
+                $ptr =& $this->wsdl->messages[$this->currentMessage];
+                break;
+
+            case 'operation':
+                break;
+
+            case 'types':
+                if (isset($this->currentComplexType) &&
+                    isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
+                    if ($this->currentElement) {
+                        $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement];
+                    } else {
+                        $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType];
+                    }
+                }
+                break;
+            }
+
+            if (isset($ptr)) {
+                if (!isset($ptr['documentation'])) {
+                    $ptr['documentation'] = '';
+                } else {
+                    $ptr['documentation'] .= ' ';
+                }
+                $ptr['documentation'] .= $data;
+            }
+        }
+    }
+
+    /**
+     * $parsed is an array returned by parse_url().
+     *
+     * @access private
+     */
+    function mergeUrl($parsed, $path)
+    {
+        if (!is_array($parsed)) {
+            return false;
+        }
+
+        $uri = '';
+        if (!empty($parsed['scheme'])) {
+            $sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
+            $uri = $parsed['scheme'] . $sep;
+        }
+
+        if (isset($parsed['pass'])) {
+            $uri .= "$parsed[user]:$parsed[pass]@";
+        } elseif (isset($parsed['user'])) {
+            $uri .= "$parsed[user]@";
+        }
+
+        if (isset($parsed['host'])) {
+            $uri .= $parsed['host'];
+        }
+        if (isset($parsed['port'])) {
+            $uri .= ":$parsed[port]";
+        }
+        if ($path[0] != '/' && isset($parsed['path'])) {
+            if ($parsed['path'][strlen($parsed['path']) - 1] != '/') {
+                $path = dirname($parsed['path']) . '/' . $path;
+            } else {
+                $path = $parsed['path'] . $path;
+            }
+            $path = $this->_normalize($path);
+        }
+        $sep = $path[0] == '/' ? '' : '/';
+        $uri .= $sep . $path;
+
+        return $uri;
+    }
+
+    function _normalize($path_str)
+    {
+        $pwd = '';
+        $strArr = preg_split('/(\/)/', $path_str, -1, PREG_SPLIT_NO_EMPTY);
+        $pwdArr = '';
+        $j = 0;
+        for ($i = 0; $i < count($strArr); $i++) {
+            if ($strArr[$i] != ' ..') {
+                if ($strArr[$i] != ' .') {
+                    $pwdArr[$j] = $strArr[$i];
+                    $j++;
+                }
+            } else {
+                array_pop($pwdArr);
+                $j--;
+            }
+        }
+        $pStr = implode('/', $pwdArr);
+        $pwd = (strlen($pStr) > 0) ? ('/' . $pStr) : '/';
+        return $pwd;
+    }
+
+}
+
+/**
+ * Parses the types and methods used in web service objects into the internal
+ * data structures used by SOAP_WSDL.
+ *
+ * Assumes the SOAP_WSDL class is unpopulated to start with.
+ *
+ * @author Chris Coe <info@intelligentstreaming.com>
+ */
+class SOAP_WSDL_ObjectParser extends SOAP_Base
+{
+    /**
+     * Target namespace for the WSDL document will have the following
+     * prefix.
+     */
+    var $tnsPrefix = 'tns';
+
+    /**
+     * Reference to the SOAP_WSDL object to populate.
+     */
+    var $wsdl = null;
+
+    /**
+     * Constructor.
+     *
+     * @param object|array $objects    Reference to the object or array of
+     *                                 objects to parse.
+     * @param SOAP_WSDL $wsdl          Reference to the SOAP_WSDL object to
+     *                                 populate.
+     * @param string $targetNamespace  The target namespace of schema types
+     *                                 etc.
+     * @param string $service_name     Name of the WSDL <service>.
+     * @param string $service_desc     Optional description of the WSDL
+     *                                 <service>.
+     */
+    function SOAP_WSDL_ObjectParser(&$objects, &$wsdl, $targetNamespace,
+                                    $service_name, $service_desc = '')
+    {
+        parent::SOAP_Base('WSDLOBJECTPARSER');
+
+        $this->wsdl = &$wsdl;
+
+        // Set up the SOAP_WSDL object
+        $this->_initialise($service_name);
+
+        // Parse each web service object
+        $wsdl_ref = (is_array($objects)? $objects : array(&$objects));
+
+        foreach ($wsdl_ref as $ref_item) {
+            if (!is_object($ref_item)) {
+                $this->_raiseSoapFault('Invalid web service object passed to object parser');
+                continue;
+            }
+
+            if (!$this->_parse($ref_item, $targetNamespace, $service_name)) {
+                break;
+            }
+        }
+
+        // Build bindings from abstract data.
+        if ($this->fault == null) {
+            $this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc);
+        }
+    }
+
+    /**
+     * Initialise the SOAP_WSDL tree (destructive).
+     *
+     * If the object has already been initialised, the only effect
+     * will be to change the tns namespace to the new service name.
+     *
+     * @param  $service_name Name of the WSDL <service>
+     * @access private
+     */
+    function _initialise($service_name)
+    {
+        // Set up the basic namespaces that all WSDL definitions use.
+        $this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL;                                      // WSDL language
+        $this->wsdl->namespaces['soap'] = SCHEMA_SOAP;                                      // WSDL SOAP bindings
+        $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name;                 // Target namespace
+        $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces);           // XML Schema
+        $this->wsdl->namespaces['SOAP-ENC'] = array_search('SOAP-ENC', $this->_namespaces); // SOAP types
+
+        // XXX Refactor $namespace/$ns for Shane :-)
+        unset($this->wsdl->ns['urn:' . $service_name]);
+        $this->wsdl->ns += array_flip($this->wsdl->namespaces);
+
+        // Imports are not implemented in WSDL generation from classes.
+        // *** <wsdl:import> ***
+    }
+
+    /**
+     * Parser - takes a single object to add to tree (non-destructive).
+     *
+     * @access private
+     *
+     * @param object $object           Reference to the object to parse.
+     * @param string $schemaNamespace
+     * @param string $service_name     Name of the WSDL <service>.
+     */
+    function _parse(&$object, $schemaNamespace, $service_name)
+    {
+        // Create namespace prefix for the schema
+        list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}');
+
+        // Parse all the types defined by the object in whatever
+        // schema language we are using (currently __typedef arrays)
+        // *** <wsdl:types> ***
+        foreach ($object->__typedef as $typeName => $typeValue) {
+            // Get/create namespace definition
+            list($nsPrefix, $typeName) = $this->_getTypeNs($typeName);
+
+            // Create type definition
+            $this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName);
+            $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName];
+
+            // According to Dmitri's documentation, __typedef comes in two
+            // flavors:
+            // Array = array(array("item" => "value"))
+            // Struct = array("item1" => "value1", "item2" => "value2", ...)
+            if (is_array($typeValue)) {
+                if (is_array(current($typeValue)) && count($typeValue) == 1
+                    && count(current($typeValue)) == 1) {
+                    // It's an array
+                    $thisType['type'] = 'Array';
+                    $nsType = current(current($typeValue));
+                    list($nsPrefix, $typeName) = $this->_getTypeNs($nsType);
+                    $thisType['namespace'] = $nsPrefix;
+                    $thisType['arrayType'] = $typeName . '[]';
+                } elseif (!is_array(current($typeValue))) {
+                    // It's a struct
+                    $thisType['type'] = 'Struct';
+                    $thisType['order'] = 'all';
+                    $thisType['namespace'] = $nsPrefix;
+                    $thisType['elements'] = array();
+
+                    foreach ($typeValue as $elementName => $elementType) {
+                        list($nsPrefix, $typeName) = $this->_getTypeNs($elementType);
+                        $thisType['elements'][$elementName]['name'] = $elementName;
+                        $thisType['elements'][$elementName]['type'] = $typeName;
+                        $thisType['elements'][$elementName]['namespace'] = $nsPrefix;
+                    }
+                } else {
+                    // It's erroneous
+                    return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
+                }
+            } else {
+                // It's erroneous
+                return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
+            }
+        }
+
+        // Create an empty element array with the target namespace
+        // prefix, to match the results of WSDL parsing.
+        $this->wsdl->elements[$schPrefix] = array();
+
+        // Populate tree with message information
+        // *** <wsdl:message> ***
+        foreach ($object->__dispatch_map as $operationName => $messages) {
+            foreach ($messages as $messageType => $messageParts) {
+                unset($thisMessage);
+
+                switch ($messageType) {
+                case 'in':
+                    $this->wsdl->messages[$operationName . 'Request'] = array();
+                    $thisMessage =& $this->wsdl->messages[$operationName . 'Request'];
+                    break;
+
+                case 'out':
+                    $this->wsdl->messages[$operationName . 'Response'] = array();
+                    $thisMessage =& $this->wsdl->messages[$operationName . 'Response'];
+                    break;
+
+                case 'alias':
+                    // Do nothing
+                    break;
+
+                default:
+                    // Error condition
+                    break;
+                }
+
+                if (isset($thisMessage)) {
+                    foreach ($messageParts as $partName => $partType) {
+                        list ($nsPrefix, $typeName) = $this->_getTypeNs($partType);
+
+                        $thisMessage[$partName] = array(
+                            'name' => $partName,
+                            'type' => $typeName,
+                            'namespace' => $nsPrefix
+                            );
+                    }
+                }
+            }
+        }
+
+        // Populate tree with portType information
+        // XXX Current implementation only supports one portType that
+        // encompasses all of the operations available.
+        // *** <wsdl:portType> ***
+        if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) {
+            $this->wsdl->portTypes[$service_name . 'Port'] = array();
+        }
+        $thisPortType =& $this->wsdl->portTypes[$service_name . 'Port'];
+
+        foreach ($object->__dispatch_map as $operationName => $messages) {
+            $thisPortType[$operationName] = array('name' => $operationName);
+
+            foreach ($messages as $messageType => $messageParts) {
+                switch ($messageType) {
+                case 'in':
+                    $thisPortType[$operationName]['input'] = array(
+                        'message' => $operationName . 'Request',
+                        'namespace' => $this->tnsPrefix);
+                    break;
+
+                case 'out':
+                    $thisPortType[$operationName]['output'] = array(
+                        'message' => $operationName . 'Response',
+                        'namespace' => $this->tnsPrefix);
+                    break;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Takes all the abstract WSDL data and builds concrete bindings and
+     * services (destructive).
+     *
+     * @access private
+     * @todo Current implementation discards $service_desc.
+     *
+     * @param string $schemaNamespace  Namespace for types etc.
+     * @param string $service_name     Name of the WSDL <service>.
+     * @param string $service_desc     Optional description of the WSDL
+     *                                 <service>.
+     */
+    function _generateBindingsAndServices($schemaNamespace, $service_name,
+                                          $service_desc = '')
+    {
+        // Populate tree with bindings information
+        // XXX Current implementation only supports one binding that
+        // matches the single portType and all of its operations.
+        // XXX Is this the correct use of $schemaNamespace here?
+        // *** <wsdl:binding> ***
+        $this->wsdl->bindings[$service_name . 'Binding'] = array(
+                'type' => $service_name . 'Port',
+                'namespace' => $this->tnsPrefix,
+                'style' => 'rpc',
+                'transport' => SCHEMA_SOAP_HTTP,
+                'operations' => array());
+        $thisBinding =& $this->wsdl->bindings[$service_name . 'Binding'];
+
+        foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData) {
+            $thisBinding['operations'][$operationName] = array(
+                'soapAction' => $schemaNamespace . '#' . $operationName,
+                'style' => $thisBinding['style']);
+
+            foreach (array('input', 'output') as $messageType)
+                if (isset($operationData[$messageType])) {
+                    $thisBinding['operations'][$operationName][$messageType] = array(
+                            'use' => 'encoded',
+                            'namespace' => $schemaNamespace,
+                            'encodingStyle' => SOAP_SCHEMA_ENCODING);
+                }
+        }
+
+        // Populate tree with service information
+        // XXX Current implementation supports one service which groups
+        // all of the ports together, one port per binding
+        // *** <wsdl:service> ***
+
+        $this->wsdl->services[$service_name . 'Service'] = array('ports' => array());
+        $thisService =& $this->wsdl->services[$service_name . 'Service']['ports'];
+        $https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) ||
+            getenv('SSL_PROTOCOL_VERSION');
+
+        foreach ($this->wsdl->bindings as $bindingName => $bindingData) {
+            $thisService[$bindingData['type']] = array(
+                    'name' => $bindingData['type'],
+                    'binding' => $bindingName,
+                    'namespace' => $this->tnsPrefix,
+                    'address' => array('location' =>
+                        ($https ? 'https://' : 'http://') .
+                        $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] .
+                        (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')),
+                    'type' => 'soap');
+        }
+
+        // Set service
+        $this->wsdl->set_service($service_name . 'Service');
+        $this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix];
+
+        // Create WSDL definition
+        // *** <wsdl:definitions> ***
+
+        $this->wsdl->definition = array(
+                'name' => $service_name,
+                'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix],
+                'xmlns' => SCHEMA_WSDL);
+
+        foreach ($this->wsdl->namespaces as $nsPrefix => $namespace) {
+            $this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace;
+        }
+    }
+
+    /**
+     * This function is adapted from Dmitri V's implementation of
+     * DISCO/WSDL generation. It separates namespace from type name in
+     * a __typedef key and creates a new namespace entry in the WSDL
+     * structure if the namespace has not been used before. The
+     * namespace prefix and type name are returned. If no namespace is
+     * specified, xsd is assumed.
+     *
+     * We will not need this function anymore once __typedef is
+     * eliminated.
+     */
+    function _getTypeNs($type)
+    {
+        preg_match_all('/\{(.*)\}/sm', $type, $m);
+        if (!empty($m[1][0])) {
+            if (!isset($this->wsdl->ns[$m[1][0]])) {
+                $ns_pref = 'ns' . count($this->wsdl->namespaces);
+                $this->wsdl->ns[$m[1][0]] = $ns_pref;
+                $this->wsdl->namespaces[$ns_pref] = $m[1][0];
+            }
+            $typens = $this->wsdl->ns[$m[1][0]];
+            $type = str_replace($m[0][0], '', $type);
+        } else {
+            $typens = 'xsd';
+        }
+
+        return array($typens, $type);
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Base.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Base.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Base.php	(revision 16957)
@@ -0,0 +1,1116 @@
+<?php
+/**
+ * This file loads all required libraries, defines constants used across the
+ * SOAP package, and defines the base classes that most other classes of this
+ * package extend.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2007 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+define('MAIL_MIMEPART_CRLF', "\r\n");
+require_once 'PEAR.php';
+
+if (!defined('INF')) {
+    define('INF', 1.8e307);
+}
+if (!defined('NAN')) {
+    define('NAN', 0.0);
+}
+
+define('SOAP_LIBRARY_VERSION', '0.11.0');
+define('SOAP_LIBRARY_NAME',    'PEAR-SOAP 0.11.0-beta');
+
+// Set schema version.
+define('SOAP_XML_SCHEMA_VERSION',  'http://www.w3.org/2001/XMLSchema');
+define('SOAP_XML_SCHEMA_INSTANCE', 'http://www.w3.org/2001/XMLSchema-instance');
+define('SOAP_XML_SCHEMA_1999',     'http://www.w3.org/1999/XMLSchema');
+define('SOAP_SCHEMA',              'http://schemas.xmlsoap.org/wsdl/soap/');
+define('SOAP_SCHEMA_ENCODING',     'http://schemas.xmlsoap.org/soap/encoding/');
+define('SOAP_ENVELOP',             'http://schemas.xmlsoap.org/soap/envelope/');
+
+define('SCHEMA_DISCO',             'http://schemas.xmlsoap.org/disco/');
+define('SCHEMA_DISCO_SCL',         'http://schemas.xmlsoap.org/disco/scl/');
+
+define('SCHEMA_SOAP',              'http://schemas.xmlsoap.org/wsdl/soap/');
+define('SCHEMA_SOAP12',            'http://schemas.xmlsoap.org/wsdl/soap12/');
+define('SCHEMA_SOAP_HTTP',         'http://schemas.xmlsoap.org/soap/http');
+define('SCHEMA_WSDL_HTTP',         'http://schemas.xmlsoap.org/wsdl/http/');
+define('SCHEMA_MIME',              'http://schemas.xmlsoap.org/wsdl/mime/');
+define('SCHEMA_WSDL',              'http://schemas.xmlsoap.org/wsdl/');
+define('SCHEMA_DIME',              'http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/');
+define('SCHEMA_CONTENT',           'http://schemas.xmlsoap.org/ws/2002/04/content-type/');
+define('SCHEMA_REF',               'http://schemas.xmlsoap.org/ws/2002/04/reference/');
+
+define('SOAP_DEFAULT_ENCODING',  'UTF-8');
+
+class SOAP_Base_Object extends PEAR
+{
+
+    /**
+     * Supported encodings, limited by XML extension.
+     *
+     * @var array $_encodings
+     */
+    var $_encodings = array('ISO-8859-1', 'US-ASCII', 'UTF-8');
+
+    /**
+     * Fault code.
+     *
+     * @var string $_myfaultcode
+     */
+    var $_myfaultcode = '';
+
+    /**
+     * Recent PEAR_Error object.
+     *
+     * @var PEAR_Error $fault
+     */
+    var $fault = null;
+
+    /**
+     * Constructor.
+     *
+     * @param string $faultcode  Error code.
+     */
+    function SOAP_Base_Object($faultcode = 'Client')
+    {
+        $this->_myfaultcode = $faultcode;
+        parent::PEAR('SOAP_Fault');
+    }
+
+    /**
+     * Raises a SOAP error.
+     *
+     * Please refer to the SOAP definition for an impression of what a certain
+     * parameter stands for.
+     *
+     * @param string|object $str  Error message or object.
+     * @param string $detail      Detailed error message.
+     * @param string $actorURI
+     * @param mixed $code
+     * @param mixed $mode
+     * @param mixed $options
+     * @param boolean $skipmsg
+     */
+    function &_raiseSoapFault($str, $detail = '', $actorURI = '', $code = null,
+                              $mode = null, $options = null, $skipmsg = false)
+    {
+        // Pass through previous faults.
+        $is_instance = isset($this) && is_a($this, 'SOAP_Base_Object');
+        if (is_object($str)) {
+            $fault =& $str;
+        } else {
+            if (!$code) {
+                $code = $is_instance ? $this->_myfaultcode : 'Client';
+            }
+            require_once 'SOAP/Fault.php';
+            $fault =& new SOAP_Fault($str,
+                                     $code,
+                                     $actorURI,
+                                     $detail,
+                                     $mode,
+                                     $options);
+        }
+        if ($is_instance) {
+            $this->fault =& $fault;
+        }
+
+        return $fault;
+    }
+
+    function _isfault()
+    {
+        return $this->fault != null;
+    }
+
+    function &_getfault()
+    {
+        return $this->fault;
+    }
+
+}
+
+/**
+ * Common base class of all SOAP classes.
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ */
+class SOAP_Base extends SOAP_Base_Object
+{
+    var $_XMLSchema = array('http://www.w3.org/2001/XMLSchema',
+                            'http://www.w3.org/1999/XMLSchema');
+    var $_XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
+
+    // load types into typemap array
+    var $_typemap = array(
+        'http://www.w3.org/2001/XMLSchema' => array(
+            'string' => 'string',
+            'boolean' => 'boolean',
+            'float' => 'float',
+            'double' => 'float',
+            'decimal' => 'float',
+            'duration' => 'integer',
+            'dateTime' => 'string',
+            'time' => 'string',
+            'date' => 'string',
+            'gYearMonth' => 'integer',
+            'gYear' => 'integer',
+            'gMonthDay' => 'integer',
+            'gDay' => 'integer',
+            'gMonth' => 'integer',
+            'hexBinary' => 'string',
+            'base64Binary' => 'string',
+            // derived datatypes
+            'normalizedString' => 'string',
+            'token' => 'string',
+            'language' => 'string',
+            'NMTOKEN' => 'string',
+            'NMTOKENS' => 'string',
+            'Name' => 'string',
+            'NCName' => 'string',
+            'ID' => 'string',
+            'IDREF' => 'string',
+            'IDREFS' => 'string',
+            'ENTITY' => 'string',
+            'ENTITIES' => 'string',
+            'integer' => 'integer',
+            'nonPositiveInteger' => 'integer',
+            'negativeInteger' => 'integer',
+            // longs (64bit ints) are not supported cross-platform.
+            'long' => 'string',
+            'int' => 'integer',
+            'short' => 'integer',
+            'byte' => 'string',
+            'nonNegativeInteger' => 'integer',
+            'unsignedLong' => 'integer',
+            'unsignedInt' => 'integer',
+            'unsignedShort' => 'integer',
+            'unsignedByte' => 'integer',
+            'positiveInteger'  => 'integer',
+            'anyType' => 'string',
+            'anyURI' => 'string',
+            'QName' => 'string'
+        ),
+        'http://www.w3.org/1999/XMLSchema' => array(
+            'i4' => 'integer',
+            'int' => 'integer',
+            'boolean' => 'boolean',
+            'string' => 'string',
+            'double' => 'float',
+            'float' => 'float',
+            'dateTime' => 'string',
+            'timeInstant' => 'string',
+            'base64Binary' => 'string',
+            'base64' => 'string',
+            'ur-type' => 'string'
+        ),
+        'http://schemas.xmlsoap.org/soap/encoding/' => array(
+            'base64' => 'string',
+            'array' => 'array',
+            'Array' => 'array',
+            'Struct' => 'array')
+    );
+
+    /**
+     * Default class name to use for decoded response objects.
+     *
+     * @var string $_defaultObjectClassname
+     */
+    var $_defaultObjectClassname = 'stdClass';
+
+    /**
+     * Hash with used namespaces.
+     *
+     * @array
+     */
+    var $_namespaces;
+
+    /**
+     * The default namespace.
+     *
+     * @string
+     */
+    var $_namespace;
+
+    var $_xmlEntities = array('&' => '&amp;',
+                              '<' => '&lt;',
+                              '>' => '&gt;',
+                              "'" => '&apos;',
+                              '"' => '&quot;');
+
+    var $_doconversion = false;
+
+    var $_attachments = array();
+
+    var $_wsdl = null;
+
+    /**
+     * True if we use section 5 encoding, or false if this is literal.
+     *
+     * @var boolean $_section5
+     */
+    var $_section5 = true;
+
+    // Handle type to class mapping.
+    var $_auto_translation = false;
+    var $_type_translation = array();
+
+    /**
+     * Constructor.
+     *
+     * @param string $faultcode  Error code.
+     */
+    function SOAP_Base($faultcode = 'Client')
+    {
+        parent::SOAP_Base_Object($faultcode);
+        $this->_resetNamespaces();
+    }
+
+    /**
+     * Sets the default namespace.
+     *
+     * @param string $namespace  The default namespace.
+     */
+    function setDefaultNamespace($namespace)
+    {
+        $this->_namespace = $namespace;
+    }
+
+    function _resetNamespaces()
+    {
+        $this->_namespaces = array(
+            'http://schemas.xmlsoap.org/soap/envelope/' => 'SOAP-ENV',
+            'http://www.w3.org/2001/XMLSchema' => 'xsd',
+            'http://www.w3.org/2001/XMLSchema-instance' => 'xsi',
+            'http://schemas.xmlsoap.org/soap/encoding/' => 'SOAP-ENC');
+    }
+
+    /**
+     * Sets the schema version used in the SOAP message.
+     *
+     * @access private
+     * @see $_XMLSchema
+     *
+     * @param string $schemaVersion  The schema version.
+     */
+    function _setSchemaVersion($schemaVersion)
+    {
+        if (!in_array($schemaVersion, $this->_XMLSchema)) {
+            return $this->_raiseSoapFault("unsuported XMLSchema $schemaVersion");
+        }
+        $this->_XMLSchemaVersion = $schemaVersion;
+        $tmpNS = array_flip($this->_namespaces);
+        $tmpNS['xsd'] = $this->_XMLSchemaVersion;
+        $tmpNS['xsi'] = $this->_XMLSchemaVersion . '-instance';
+        $this->_namespaces = array_flip($tmpNS);
+    }
+
+    function _getNamespacePrefix($ns)
+    {
+        if ($this->_namespace && $ns == $this->_namespace) {
+            return '';
+        }
+        if (isset($this->_namespaces[$ns])) {
+            return $this->_namespaces[$ns];
+        }
+        $prefix = 'ns' . count($this->_namespaces);
+        $this->_namespaces[$ns] = $prefix;
+        return $prefix;
+    }
+
+    function _getNamespaceForPrefix($prefix)
+    {
+        $flipped = array_flip($this->_namespaces);
+        if (isset($flipped[$prefix])) {
+            return $flipped[$prefix];
+        }
+        return null;
+    }
+
+    function _isSoapValue(&$value)
+    {
+        return is_a($value, 'SOAP_Value');
+    }
+
+    function _serializeValue(&$value, $name = '', $type = false,
+                             $elNamespace = null, $typeNamespace = null,
+                             $options = array(), $attributes = array(),
+                             $artype = '')
+    {
+        $namespaces = array();
+        $arrayType = $array_depth = $xmlout_value = null;
+        $typePrefix = $elPrefix = $xmlout_offset = $xmlout_arrayType = '';
+        $xmlout_type = $xmlns = $ptype = $array_type_ns = '';
+
+        if (!$name || is_numeric($name)) {
+            $name = 'item';
+        }
+
+        if ($this->_wsdl) {
+            list($ptype, $arrayType, $array_type_ns, $array_depth)
+                = $this->_wsdl->getSchemaType($type, $name, $typeNamespace);
+        }
+
+        if (!$arrayType) {
+            $arrayType = $artype;
+        }
+        if (!$ptype) {
+            $ptype = $this->_getType($value);
+        }
+        if (!$type) {
+            $type = $ptype;
+        }
+
+        if (strcasecmp($ptype, 'Struct') == 0 ||
+            strcasecmp($type, 'Struct') == 0) {
+            // Struct
+            $vars = null;
+            if (is_object($value)) {
+                $vars = get_object_vars($value);
+            } else {
+                $vars = &$value;
+            }
+            if (is_array($vars)) {
+                foreach (array_keys($vars) as $k) {
+                    // Hide private vars.
+                    if ($k[0] == '_') {
+                        continue;
+                    }
+                    if (is_object($vars[$k])) {
+                        if (is_a($vars[$k], 'SOAP_Value')) {
+                            $xmlout_value .= $vars[$k]->serialize($this);
+                        } else {
+                            // XXX get the members and serialize them instead
+                            // converting to an array is more overhead than we
+                            // should really do.
+                            $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), $k, false, $this->_section5 ? null : $elNamespace);
+                        }
+                    } else {
+                        $xmlout_value .= $this->_serializeValue($vars[$k], $k, false, $this->_section5 ? null : $elNamespace);
+                    }
+                }
+            }
+        } elseif (strcasecmp($ptype, 'Array') == 0 ||
+                  strcasecmp($type, 'Array') == 0) {
+            // Array.
+            $typeNamespace = SOAP_SCHEMA_ENCODING;
+            $orig_type = $type;
+            $type = 'Array';
+            $numtypes = 0;
+            $value = (array)$value;
+            // XXX this will be slow on larger arrays.  Basically, it flattens
+            // arrays to allow us to serialize multi-dimensional arrays.  We
+            // only do this if arrayType is set, which will typically only
+            // happen if we are using WSDL
+            if (isset($options['flatten']) ||
+                ($arrayType &&
+                 (strchr($arrayType, ',') || strstr($arrayType, '][')))) {
+                $numtypes = $this->_multiArrayType($value, $arrayType,
+                                                   $ar_size, $xmlout_value);
+            }
+
+            $array_type = $array_type_prefix = '';
+            if ($numtypes != 1) {
+                $arrayTypeQName = new QName($arrayType);
+                $arrayType = $arrayTypeQName->name;
+                $array_types = array();
+                $array_val = null;
+
+                // Serialize each array element.
+                $ar_size = count($value);
+                foreach ($value as $array_val) {
+                    if ($this->_isSoapValue($array_val)) {
+                        $array_type = $array_val->type;
+                        $array_types[$array_type] = 1;
+                        $array_type_ns = $array_val->type_namespace;
+                        $xmlout_value .= $array_val->serialize($this);
+                    } else {
+                        $array_type = $this->_getType($array_val);
+                        $array_types[$array_type] = 1;
+                        $xmlout_value .= $this->_serializeValue($array_val, 'item', $array_type, $this->_section5 ? null : $elNamespace);
+                    }
+                }
+
+                $xmlout_offset = ' SOAP-ENC:offset="[0]"';
+                if (!$arrayType) {
+                    $numtypes = count($array_types);
+                    if ($numtypes == 1) {
+                        $arrayType = $array_type;
+                    }
+                    // Using anyType is more interoperable.
+                    if ($array_type == 'Struct') {
+                        $array_type = '';
+                    } elseif ($array_type == 'Array') {
+                        $arrayType = 'anyType';
+                        $array_type_prefix = 'xsd';
+                    } else {
+                        if (!$arrayType) {
+                            $arrayType = $array_type;
+                        }
+                    }
+                }
+            }
+            if (!$arrayType || $numtypes > 1) {
+                // Should reference what schema we're using.
+                $arrayType = 'xsd:anyType';
+            } else {
+                if ($array_type_ns) {
+                    $array_type_prefix = $this->_getNamespacePrefix($array_type_ns);
+                } elseif (isset($this->_typemap[$this->_XMLSchemaVersion][$arrayType])) {
+                    $array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion];
+                }
+                if ($array_type_prefix) {
+                    $arrayType = $array_type_prefix . ':' . $arrayType;
+                }
+            }
+
+            $xmlout_arrayType = ' SOAP-ENC:arrayType="' . $arrayType;
+            if ($array_depth != null) {
+                for ($i = 0; $i < $array_depth; $i++) {
+                    $xmlout_arrayType .= '[]';
+                }
+            }
+            $xmlout_arrayType .= "[$ar_size]\"";
+        } elseif ($this->_isSoapValue($value)) {
+            $xmlout_value = $value->serialize($this);
+        } elseif ($type == 'string') {
+            $xmlout_value = htmlspecialchars($value);
+        } elseif ($type == 'rawstring') {
+            $xmlout_value =& $value;
+        } elseif ($type == 'boolean') {
+            $xmlout_value = $value ? 'true' : 'false';
+        } else {
+            $xmlout_value =& $value;
+        }
+
+        // Add namespaces.
+        if ($elNamespace) {
+            $elPrefix = $this->_getNamespacePrefix($elNamespace);
+            if ($elPrefix) {
+                $xmlout_name = "$elPrefix:$name";
+            } else {
+                $xmlout_name = $name;
+            }
+        } else {
+            $xmlout_name = $name;
+        }
+
+        if ($typeNamespace) {
+            $typePrefix = $this->_getNamespacePrefix($typeNamespace);
+            if ($typePrefix) {
+                $xmlout_type = "$typePrefix:$type";
+            } else {
+                $xmlout_type = $type;
+            }
+        } elseif ($type &&
+                  isset($this->_typemap[$this->_XMLSchemaVersion][$type])) {
+            $typePrefix = $this->_namespaces[$this->_XMLSchemaVersion];
+            if ($typePrefix) {
+                $xmlout_type = "$typePrefix:$type";
+            } else {
+                $xmlout_type = $type;
+            }
+        }
+
+        // Handle additional attributes.
+        $xml_attr = '';
+        if (count($attributes)) {
+            foreach ($attributes as $k => $v) {
+                $kqn = new QName($k);
+                $vqn = new QName($v);
+                $xml_attr .= ' ' . $kqn->fqn() . '="' . $vqn->fqn() . '"';
+            }
+        }
+
+        // Store the attachment for mime encoding.
+        if (isset($options['attachment']) &&
+            !PEAR::isError($options['attachment'])) {
+            $this->_attachments[] = $options['attachment'];
+        }
+
+        if ($this->_section5) {
+            if ($xmlout_type) {
+                $xmlout_type = " xsi:type=\"$xmlout_type\"";
+            }
+            if (is_null($xmlout_value)) {
+                $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
+                    "$xml_attr xsi:nil=\"true\"/>";
+            } else {
+                $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
+                    "$xmlout_offset$xml_attr>$xmlout_value</$xmlout_name>";
+            }
+        } else {
+            if (is_null($xmlout_value)) {
+                $xml = "\r\n<$xmlout_name$xmlns$xml_attr/>";
+            } else {
+                $xml = "\r\n<$xmlout_name$xmlns$xml_attr>" .
+                    $xmlout_value . "</$xmlout_name>";
+            }
+        }
+
+        return $xml;
+    }
+
+    /**
+     * Converts a PHP type to a SOAP type.
+     *
+     * @access   private
+     *
+     * @param string $value  The value to inspect.
+     *
+     * @return string  The value's SOAP type.
+     */
+    function _getType(&$value)
+    {
+        $type = gettype($value);
+        switch ($type) {
+        case 'object':
+            if (is_a($value, 'soap_value')) {
+                $type = $value->type;
+            } else {
+                $type = 'Struct';
+            }
+            break;
+
+        case 'array':
+            // Hashes are always handled as structs.
+            if ($this->_isHash($value)) {
+                $type = 'Struct';
+            } else {
+                $ar_size = count($value);
+                reset($value);
+                $key1 = key($value);
+                if ($ar_size > 0 && is_a($key1, 'SOAP_Value')) {
+                    // FIXME: for non-wsdl structs that are all the same type
+                    $key2 = key($value);
+                    if ($ar_size > 1 &&
+                        $this->_isSoapValue($key1) &&
+                        $this->_isSoapValue($key2) &&
+                        $key1->name != $key2->name) {
+                        // This is a struct, not an array.
+                        $type = 'Struct';
+                    } else {
+                        $type = 'Array';
+                    }
+                } else {
+                    $type = 'Array';
+                }
+            }
+            break;
+
+        case 'integer':
+        case 'long':
+            $type = 'int';
+            break;
+
+        case 'boolean':
+            break;
+
+        case 'double':
+            // double is deprecated in PHP 4.2 and later.
+            $type = 'float';
+            break;
+
+        case 'null':
+            $type = '';
+            break;
+
+        case 'string':
+        default:
+            break;
+        }
+
+        return $type;
+    }
+
+    function _multiArrayType($value, &$type, &$size, &$xml)
+    {
+        if (is_array($value)) {
+            // Seems we have a multi dimensional array, figure it out if we
+            // do.
+            for ($i = 0, $c = count($value); $i < $c; ++$i) {
+                $this->_multiArrayType($value[$i], $type, $size, $xml);
+            }
+
+            $sz = count($value);
+            if ($size) {
+                $size = $sz . ',' . $size;
+            } else {
+                $size = $sz;
+            }
+            return 1;
+        } elseif (is_object($value)) {
+            $type = $value->type;
+            $xml .= $value->serialize($this);
+        } else {
+            $type = $this->_getType($value);
+            $xml .= $this->_serializeValue($value, 'item', $type);
+        }
+        $size = null;
+
+        return 1;
+    }
+
+    /**
+     * Returns whether a type is a base64 type.
+     *
+     * @param string $type  A type name.
+     *
+     * @return boolean  True if the type name is a base64 type.
+     */
+    function _isBase64Type($type)
+    {
+        return $type == 'base64' || $type == 'base64Binary';
+    }
+
+    /**
+     * Returns whether an array is a hash.
+     *
+     * @param array $a  An array to check.
+     *
+     * @return boolean  True if the specified array is a hash.
+     */
+    function _isHash(&$a)
+    {
+        // I really dislike having to loop through this in PHP code, really
+        // large arrays will be slow.  We need a C function to do this.
+        $it = 0;
+        foreach ($a as $k => $v) {
+            // Checking the type is faster than regexp.
+            if (!is_int($k)) {
+                return true;
+            }
+            // If someone has a large hash they should really be defining the
+            // type.
+            if ($it++ > 10) {
+                $this->_raiseSoapFault('Large associative array passed where a SOAP_Value was expected');
+                return false;
+            }
+        }
+        return false;
+    }
+
+    function _un_htmlentities($string)
+    {
+        $trans_tbl = get_html_translation_table(HTML_ENTITIES);
+        $trans_tbl = array_flip($trans_tbl);
+        return strtr($string, $trans_tbl);
+    }
+
+    /**
+    *   Converts a SOAP_Value object into a StdClass PHP object
+    */
+    function &_decode(&$soapval)
+    {
+        if (!$this->_isSoapValue($soapval)) {
+            return $soapval;
+        } elseif (is_array($soapval->value)) {
+            if ($soapval->type != 'Array') {
+                $classname = $this->_defaultObjectClassname;
+                if (isset($this->_type_translation[$soapval->tqn->fqn()])) {
+                    // This will force an error in PHP if the class does not
+                    // exist.
+                    $classname = $this->_type_translation[$soapval->tqn->fqn()];
+                } elseif (isset($this->_type_translation[$soapval->type])) {
+                    // This will force an error in PHP if the class does not
+                    // exist.
+                    $classname = $this->_type_translation[$soapval->type];
+                } elseif ($this->_auto_translation) {
+                    if (class_exists($soapval->type)) {
+                        $classname = $soapval->type;
+                    } elseif ($this->_wsdl) {
+                        $t = $this->_wsdl->getComplexTypeNameForElement($soapval->name, $soapval->namespace);
+                        if ($t && class_exists($t)) {
+                            $classname = $t;
+                        }
+                    }
+                }
+                $return =& new $classname;
+            } else {
+                $return = array();
+            }
+
+            $counter = 1;
+            $isstruct = !is_array($return);
+            foreach ($soapval->value as $item) {
+                if (is_object($return)) {
+                    if ($this->_wsdl) {
+                        // Get this child's WSDL information.
+                        // /$soapval->ns/$soapval->type/$item->ns/$item->name
+                        $child_type = $this->_wsdl->getComplexTypeChildType(
+                            $soapval->namespace,
+                            $soapval->name,
+                            $item->namespace,
+                            $item->name);
+                        if ($child_type) {
+                            $item->type = $child_type;
+                        }
+                    }
+                    if (!$isstruct || $item->type == 'Array') {
+                        if (isset($return->{$item->name}) &&
+                            is_object($return->{$item->name})) {
+                            $return->{$item->name} =& $this->_decode($item);
+                        } elseif (isset($return->{$item->name}) &&
+                                  is_array($return->{$item->name})) {
+                            $return->{$item->name}[] = $this->_decode($item);
+                        } elseif (isset($return->{$item->name})) {
+                            $return->{$item->name} = array(
+                                $return->{$item->name},
+                                $this->_decode($item)
+                            );
+                        } elseif (is_array($return)) {
+                            $return[] =& $this->_decode($item);
+                        } else {
+                            $return->{$item->name} =& $this->_decode($item);
+                        }
+                    } elseif (isset($return->{$item->name})) {
+                        //$isstruct = false;
+                        if (count(get_object_vars($return)) == 1) {
+                            $d =& $this->_decode($item);
+                            $return = array($return->{$item->name}, $d);
+                        } else {
+                            $d =& $this->_decode($item);
+                            $return->{$item->name} = array($return->{$item->name}, $d);
+                        }
+                    } else {
+                        $return->{$item->name} =& $this->_decode($item);
+                    }
+                    // Set the attributes as members in the class.
+                    if (method_exists($return, '__set_attribute')) {
+                        foreach ($soapval->attributes as $key => $value) {
+                            call_user_func_array(array(&$return,
+                                                       '__set_attribute'),
+                                                 array($key, $value));
+                        }
+                    }
+                } else {
+                    if ($soapval->arrayType && $this->_isSoapValue($item)) {
+                        if ($this->_isBase64Type($item->type) &&
+                            !$this->_isBase64Type($soapval->arrayType)) {
+                            // Decode the value if we're losing the base64
+                            // type information.
+                            $item->value = base64_decode($item->value);
+                        }
+                        $item->type = $soapval->arrayType;
+                    }
+                    if (!$isstruct) {
+                        $return[] = $this->_decode($item);
+                    } elseif (isset($return[$item->name])) {
+                        $isstruct = false;
+                        $d =& $this->_decode($item);
+                        $return = array($return[$item->name], $d);
+                    } else {
+                        $return[$item->name] = $this->_decode($item);
+                    }
+                }
+            }
+
+            return $return;
+        }
+
+        if ($soapval->type == 'boolean') {
+            if ($soapval->value != '0' &&
+                strcasecmp($soapval->value, 'false') != 0) {
+                $soapval->value = true;
+            } else {
+                $soapval->value = false;
+            }
+        } elseif ($soapval->type &&
+                  isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type])) {
+            // If we can, set variable type.
+            settype($soapval->value,
+                    $this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]);
+        }
+
+        return $soapval->value;
+    }
+
+    /**
+     * Creates the SOAP envelope with the SOAP envelop data.
+     *
+     * @param mixed $method
+     * @param array $headers
+     * @param string $encoding
+     * @param array $options
+     *
+     * @return string
+     */
+    function makeEnvelope(&$method, &$headers,
+                          $encoding = SOAP_DEFAULT_ENCODING,
+                          $options = array())
+    {
+        $smsg = $header_xml = $ns_string = '';
+
+        if ($headers) {
+            $c = count($headers);
+            for ($i = 0; $i < $c; $i++) {
+                $header_xml .= $headers[$i]->serialize($this);
+            }
+            $header_xml = "<SOAP-ENV:Header>\r\n$header_xml\r\n</SOAP-ENV:Header>\r\n";
+        }
+
+        if (!isset($options['input']) || $options['input'] == 'parse') {
+            if (is_array($method)) {
+                $c = count($method);
+                for ($i = 0; $i < $c; $i++) {
+                    $smsg .= $method[$i]->serialize($this);
+                }
+            } else {
+                $smsg = $method->serialize($this);
+            }
+        } else {
+            $smsg = $method;
+        }
+        $body = "<SOAP-ENV:Body>\r\n" . $smsg . "\r\n</SOAP-ENV:Body>\r\n";
+
+        foreach ($this->_namespaces as $k => $v) {
+            $ns_string .= " xmlns:$v=\"$k\"\r\n";
+        }
+        if ($this->_namespace) {
+            $ns_string .= " xmlns=\"{$this->_namespace}\"\r\n";
+        }
+
+        /* If 'use' == 'literal', we do not put in the encodingStyle.  This is
+         * denoted by $this->_section5 being false.  'use' can be defined at a
+         * more granular level than we are dealing with here, so this does not
+         * work for all services. */
+        $xml = "<?xml version=\"1.0\" encoding=\"$encoding\"?>\r\n\r\n".
+            "<SOAP-ENV:Envelope $ns_string".
+            ($this->_section5 ? ' SOAP-ENV:encodingStyle="' . SOAP_SCHEMA_ENCODING . '"' : '').
+            ">\r\n".
+            "$header_xml$body</SOAP-ENV:Envelope>\r\n";
+
+        return $xml;
+    }
+
+    function _makeMimeMessage($xml, $encoding = SOAP_DEFAULT_ENCODING)
+    {
+        if (!@include_once 'Mail/mimePart.php') {
+            return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
+        }
+
+        // Encode any attachments.  See http://www.w3.org/TR/SOAP-attachments
+        // Now we have to mime encode the message.
+        $params = array('content_type' => 'multipart/related; type="text/xml"');
+        $msg = new Mail_mimePart('', $params);
+
+        // Add the xml part.
+        $params['content_type'] = 'text/xml';
+        $params['charset'] = $encoding;
+        $params['encoding'] = 'base64';
+        $msg->addSubPart($xml, $params);
+
+        // Add the attachements
+        for ($i = 0, $c = count($this->_attachments); $i < $c; ++$i) {
+            $msg->addSubPart($this->_attachments[$i]['body'],
+                             $this->_attachments[$i]);
+        }
+
+        return $msg->encode();
+    }
+
+    // TODO: this needs to be used from the Transport system.
+    function _makeDIMEMessage($xml)
+    {
+        if (!@include_once 'Net/DIME.php') {
+            return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
+        }
+
+        // Encode any attachments.  See
+        // http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt
+        // Now we have to DIME encode the message
+        $dime = new Net_DIME_Message();
+        $msg = $dime->encodeData($xml, SOAP_ENVELOP, null, NET_DIME_TYPE_URI);
+
+        // Add the attachments.
+        $c = count($this->_attachments);
+        for ($i = 0; $i < $c; $i++) {
+            $msg .= $dime->encodeData($this->_attachments[$i]['body'],
+                                      $this->_attachments[$i]['content_type'],
+                                      $this->_attachments[$i]['cid'],
+                                      NET_DIME_TYPE_MEDIA);
+        }
+        $msg .= $dime->endMessage();
+
+        return $msg;
+    }
+
+    function _decodeMimeMessage(&$data, &$headers, &$attachments)
+    {
+        if (!@include_once 'Mail/mimeDecode.php') {
+            return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
+        }
+
+        $params['include_bodies'] = true;
+        $params['decode_bodies']  = true;
+        $params['decode_headers'] = true;
+
+        // Lame thing to have to do for decoding.
+        $decoder =& new Mail_mimeDecode($data);
+        $structure = $decoder->decode($params);
+
+        if (isset($structure->body)) {
+            $data = $structure->body;
+            $headers = $structure->headers;
+
+            return;
+        } elseif (isset($structure->parts)) {
+            $data = $structure->parts[0]->body;
+            $headers = array_merge($structure->headers,
+                                   $structure->parts[0]->headers);
+            if (count($structure->parts) > 1) {
+                $mime_parts = array_splice($structure->parts,1);
+                // Prepare the parts for the SOAP parser.
+
+                $c = count($mime_parts);
+                for ($i = 0; $i < $c; $i++) {
+                    $p =& $mime_parts[$i];
+                    if (isset($p->headers['content-location'])) {
+                        // TODO: modify location per SwA note section 3
+                        // http://www.w3.org/TR/SOAP-attachments
+                        $attachments[$p->headers['content-location']] = $p->body;
+                    } else {
+                        $cid = 'cid:' . substr($p->headers['content-id'], 1, -1);
+                        $attachments[$cid] = $p->body;
+                    }
+                }
+            }
+
+            return;
+        }
+
+        $this->_raiseSoapFault('Mime parsing error', '', '', 'Server');
+    }
+
+    function _decodeDIMEMessage(&$data, &$headers, &$attachments)
+    {
+        if (!@include_once 'Net/DIME.php') {
+            return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
+        }
+
+        // This SHOULD be moved to the transport layer, e.g. PHP itself should
+        // handle parsing DIME ;)
+        $dime =& new Net_DIME_Message();
+        $err = $dime->decodeData($data);
+        if (PEAR::isError($err)) {
+            $this->_raiseSoapFault('Failed to decode the DIME message!', '', '', 'Server');
+            return;
+        }
+        if (strcasecmp($dime->parts[0]['type'], SOAP_ENVELOP) != 0) {
+            $this->_raiseSoapFault('DIME record 1 is not a SOAP envelop!', '', '', 'Server');
+            return;
+        }
+
+        $data = $dime->parts[0]['data'];
+        // Fake it for now.
+        $headers['content-type'] = 'text/xml';
+        $c = count($dime->parts);
+        for ($i = 0; $i < $c; $i++) {
+            $part =& $dime->parts[$i];
+            // We need to handle URI's better.
+            $id = strncmp($part['id'], 'cid:', 4)
+                ? 'cid:' . $part['id']
+                : $part['id'];
+            $attachments[$id] = $part['data'];
+        }
+    }
+
+    /**
+     * @deprecated Use setTypeTranslation().
+     */
+    function __set_type_translation($type, $class = null)
+    {
+        $this->setTypeTranslation($type, $class);
+    }
+
+    /**
+     * Explicitly sets the translation for a specific class.
+     *
+     * Auto translation works for all cases, but opens ANY class in the script
+     * to be used as a data type, and may not be desireable.
+     *
+     * @param string $type   A SOAP type.
+     * @param string $class  A PHP class name.
+     */
+    function setTypeTranslation($type, $class = null)
+    {
+        $tq = new QName($type);
+        if (!$class) {
+            $class = $tq->name;
+        }
+        $this->_type_translation[$type]=$class;
+    }
+
+}
+
+/**
+ * Class used to handle QNAME values in XML.
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ */
+class QName
+{
+    var $name = '';
+    var $ns = '';
+    var $namespace='';
+
+    function QName($name, $namespace = '')
+    {
+        if ($name && $name[0] == '{') {
+            preg_match('/\{(.*?)\}(.*)/', $name, $m);
+            $this->name = $m[2];
+            $this->namespace = $m[1];
+        } elseif (substr_count($name, ':') == 1) {
+            $s = explode(':', $name);
+            $s = array_reverse($s);
+            $this->name = $s[0];
+            $this->ns = $s[1];
+            $this->namespace = $namespace;
+        } else {
+            $this->name = $name;
+            $this->namespace = $namespace;
+        }
+
+        // A little more magic than should be in a qname.
+        $p = strpos($this->name, '[');
+        if ($p) {
+            // TODO: Need to re-examine this logic later.
+            // Chop off [].
+            $this->arraySize = explode(',', substr($this->name, $p + 1, -$p - 2));
+            $this->arrayInfo = substr($this->name, $p);
+            $this->name = substr($this->name, 0, $p);
+        }
+    }
+
+    function fqn()
+    {
+        if ($this->namespace) {
+            return '{' . $this->namespace . '}' . $this->name;
+        } elseif ($this->ns) {
+            return $this->ns . ':' . $this->name;
+        }
+        return $this->name;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/tools/genproxy.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/tools/genproxy.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/tools/genproxy.php	(revision 16957)
@@ -0,0 +1,21 @@
+<?php
+error_reporting(E_ALL);
+require_once 'SOAP/WSDL.php';
+
+/**
+ * genproxy
+ *
+ * a command line tool for generating SOAP proxies from WSDL files
+ *
+ * genproxy.php http://site/foo.wsdl > foo.php
+ *
+ */
+
+function do_wsdl($uri) {
+    $wsdl =& new SOAP_WSDL($uri);
+    print $wsdl->generateAllProxies();
+}
+echo "<?php\n\nrequire_once 'SOAP/Client.php';\n\n";
+do_wsdl($_SERVER['argv'][1]);
+echo "\n?>";
+?>
Index: branches/feature-module-update/data/module/SOAP/Fault.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Fault.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Fault.php	(revision 16957)
@@ -0,0 +1,127 @@
+<?php
+/**
+ * This file contains the SOAP_Fault class, used for all error objects in this
+ * package.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2006 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'PEAR.php';
+
+/**
+ * PEAR::Error wrapper used to match SOAP Faults to PEAR Errors
+ *
+ * SOAP_Fault transmissions normally contain a complete backtrace of the
+ * error.  Revealing these details in a public web services is a bad idea
+ * because it can be used by attackers.  Backtrace information can be kept out
+ * of SOAP_Fault responses by putting the following code in your script after
+ * your "require_once 'SOAP/Server.php';" line:
+ *
+ * <code>
+ * $skiptrace =& PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
+ * $skiptrace = true;
+ * </code>
+ *
+ * @package  SOAP
+ * @access   public
+ * @author   Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author   Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_Fault extends PEAR_Error
+{
+    /**
+     * Constructor.
+     *
+     * @param string $faultstring  Message string for fault.
+     * @param mixed $faultcode     The faultcode.
+     * @param mixed $faultactor
+     * @param mixed $detail        @see PEAR_Error
+     * @param array $mode          @see PEAR_Error
+     * @param array $options       @see PEAR_Error
+     */
+    function SOAP_Fault($faultstring = 'unknown error', $faultcode = 'Client',
+                        $faultactor = null, $detail = null, $mode = null,
+                        $options = null)
+    {
+        parent::PEAR_Error($faultstring, $faultcode, $mode, $options, $detail);
+        if ($faultactor) {
+            $this->error_message_prefix = $faultactor;
+        }
+    }
+
+    /**
+     * Returns a SOAP XML message that can be sent as a server response.
+     *
+     * @return string
+     */
+    function message($encoding = SOAP_DEFAULT_ENCODING)
+    {
+        $msg = new SOAP_Base();
+        $params = array();
+        $params[] = new SOAP_Value('faultcode', 'QName', 'SOAP-ENV:' . $this->code);
+        $params[] = new SOAP_Value('faultstring', 'string', $this->message);
+        $params[] = new SOAP_Value('faultactor', 'anyURI', $this->error_message_prefix);
+        if (isset($this->backtrace)) {
+            $params[] = new SOAP_Value('detail', 'string', $this->backtrace);
+        } else {
+            $params[] = new SOAP_Value('detail', 'string', $this->userinfo);
+        }
+
+        $methodValue = new SOAP_Value('{' . SOAP_ENVELOP . '}Fault', 'Struct', $params);
+        $headers = null;
+        return $msg->makeEnvelope($methodValue, $headers, $encoding);
+    }
+
+    /**
+     * Returns a simple native PHP array containing the fault data.
+     *
+     * @return array
+     */
+    function getFault()
+    {
+        $fault = new stdClass();
+        $fault->faultcode = $this->code;
+        $fault->faultstring = $this->message;
+        $fault->faultactor = $this->error_message_prefix;
+        $fault->detail = $this->userinfo;
+        return $fault;
+    }
+
+    /**
+     * Returns the SOAP actor for the fault.
+     *
+     * @return string
+     */
+    function getActor()
+    {
+        return $this->error_message_prefix;
+    }
+
+    /**
+     * Returns the fault detail.
+     *
+     * @return string
+     */
+    function getDetail()
+    {
+        return $this->userinfo;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Type/hexBinary.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Type/hexBinary.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Type/hexBinary.php	(revision 16957)
@@ -0,0 +1,45 @@
+<?php
+/**
+ * This class provides methods to detect and convert binary data from an to
+ * hexadecimal strings.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @copyright  2003-2007 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+class SOAP_Type_hexBinary {
+
+    function to_bin($value)
+    {
+        return pack('H' . strlen($value), $value);
+    }
+
+    function to_hex($value)
+    {
+        return bin2hex($value);
+    }
+
+    function is_hexbin($value)
+    {
+        // First see if there are any invalid chars.
+        if (!strlen($value) || preg_match('/[^A-Fa-f0-9]/', $value)) {
+            return false;
+        }
+
+        return strcasecmp($value, SOAP_Type_hexBinary::to_hex(SOAP_Type_hexBinary::to_bin($value))) == 0;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Type/dateTime.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Type/dateTime.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Type/dateTime.php	(revision 16957)
@@ -0,0 +1,243 @@
+<?php
+/**
+ * This file contains the code for the SOAP date/time clas.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+/**
+ * This class converts from and to unix timestamps and ISO 8601 date/time.
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author   Shane Caraveo <shane@php.net>       Port to PEAR and more
+ * @author   Jan Schneider <jan@horde.org>       Maintenance
+ */
+class SOAP_Type_dateTime 
+{
+    var $_iso8601 =
+        '# 1: centuries & years CCYY-
+         (-?[0-9]{4})-
+         # 2: months MM-
+         ([0-9]{2})-
+         # 3: days DD
+         ([0-9]{2})
+         # 4: separator T
+         T
+         # 5: hours hh:
+         ([0-9]{2}):
+         # 6: minutes mm:
+         ([0-9]{2}):
+         # 7: seconds ss.ss...
+         ([0-9]{2})(\.[0-9]*)?
+         # 8: Z to indicate UTC, -+HH:MM:SS.SS... for local zones
+         (Z|[+\-][0-9]{4}|[+\-][0-9]{2}:[0-9]{2})?';
+
+    var $timestamp = -1;
+
+    /**
+     * Constructor.
+     *
+     * @param string|integer $date  The timestamp or ISO 8601 formatted
+     *                              date and time this object is going to
+     *                              represent.
+     */
+    function SOAP_Type_dateTime($date = -1)
+    {
+        if ($date == -1) {
+            $this->timestamp = time();
+        } elseif (is_int($date)) {
+            $this->timestamp = $date;
+        } else {
+            $this->timestamp = $this->toUnixtime($date);
+        }
+    }
+
+    /**
+     * Alias of {@link SOAP_Type_dateTime::toUTC}.
+     */
+    function toSOAP($date = NULL)
+    {
+        return $this->toUTC($date);
+    }
+
+    /**
+     * Converts this object or a timestamp to an ISO 8601 date/time string.
+     *
+     * @param integer $timestamp  A unix timestamp
+     *
+     * @return string  An ISO 8601 formatted date/time string.
+     */
+    function toString($timestamp = 0)
+    {
+        if (!$timestamp) {
+            $timestamp = $this->timestamp;
+        }
+        if ($timestamp < 0) {
+            return 0;
+        }
+
+        //simulate PHP5's P parameter
+        $zone = date('O', $timestamp);
+        if (strlen($zone) == 5) {
+            $zone = substr($zone, 0, 3) . ':' . substr($zone, 3);
+        }
+        return date('Y-m-d\TH:i:s', $timestamp) . $zone;
+    }
+
+    /**
+     * Splits a date/time into its components.
+     *
+     * @param string|integer $datestr  A unix timestamp or ISO 8601 date/time
+     *                                 string. If empty, this object is used.
+     *
+     * @return boolean|array  An array with the date and time components or
+     *                        false on failure.
+     */
+    function _split($datestr)
+    {
+        if (!$datestr) {
+            $datestr = $this->toString();
+        } elseif (is_int($datestr)) {
+            $datestr = $this->toString($datestr);
+        }
+
+        if (preg_match('/' . $this->_iso8601 . '/x', $datestr, $regs)) {
+            if (empty($regs[8])) {
+                $timestamp = strtotime(sprintf('%04d-%02d-%02d %02d:%02d:%02d',
+                                               $regs[1],
+                                               $regs[2],
+                                               $regs[3],
+                                               $regs[4],
+                                               $regs[5],
+                                               $regs[6]));
+                $regs[8] = date('O', $timestamp);
+            }
+            if ($regs[8] != 'Z') {
+                $op = substr($regs[8], 0, 1);
+                $h = substr($regs[8], 1, 2);
+                if (strstr($regs[8], ':')) {
+                    $m = substr($regs[8], 4, 2);
+                } else {
+                    $m = substr($regs[8], 3, 2);
+                }
+                if ($op == '+') {
+                    $regs[4] = $regs[4] - $h;
+                    if ($regs[4] < 0) {
+                        $regs[4] += 24;
+                    }
+                    $regs[5] = $regs[5] - $m;
+                    if ($regs[5] < 0) {
+                        $regs[5] += 60;
+                    }
+                } else {
+                    $regs[4] = $regs[4] + $h;
+                    if ($regs[4] > 23) {
+                        $regs[4] -= 24;
+                    }
+                    $regs[5] = $regs[5] + $m;
+                    if ($regs[5] > 59) {
+                        $regs[5] -= 60;
+                    }
+                }
+            }
+            return $regs;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an ISO 8601 formatted UTC date/time string.
+     *
+     * @param string|integer $datestr  @see SOAP_Type_dateTime::_split
+     *
+     * @return string  The ISO 8601 formatted UTC date/time string.
+     */
+    function toUTC($datestr = null)
+    {
+        $regs = $this->_split($datestr);
+
+        if ($regs) {
+            return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',
+                           $regs[1],
+                           $regs[2],
+                           $regs[3],
+                           $regs[4],
+                           $regs[5],
+                           $regs[6]);
+        }
+
+        return '';
+    }
+
+    /**
+     * Returns a unix timestamp.
+     *
+     * @param string|integer $datestr  @see SOAP_Type_dateTime::_split
+     *
+     * @return integer  The unix timestamp.
+     */
+    function toUnixtime($datestr = null)
+    {
+        $regs = $this->_split($datestr);
+        if ($regs) {
+            return strtotime(sprintf('%04d-%02d-%02d %02d:%02d:%02dZ',
+                                     $regs[1],
+                                     $regs[2],
+                                     $regs[3],
+                                     $regs[4],
+                                     $regs[5],
+                                     $regs[6]));
+        }
+        return -1;
+    }
+
+    /**
+     * Compares two dates or this object with a second date.
+     *
+     * @param string|integer $date1  A unix timestamp or ISO 8601 date/time
+     *                               string.
+     * @param string|integer $date2  A unix timestamp or ISO 8601 date/time
+     *                               string. If empty, this object is used.
+     *
+     * @return integer  The difference between the first and the second date.
+     */
+    function compare($date1, $date2 = null)
+    {
+        if (is_null($date2)) {
+            $date2 = $date1;
+            $date1 = $this->timestamp;
+        }
+        if (!is_int($date1)) {
+            $date1 = $this->toUnixtime($date1);
+        }
+        if (!is_int($date2)) {
+            $date2 = $this->toUnixtime($date2);
+        }
+
+        if ($date1 != -1 && $date2 != -1) {
+            return $date1 - $date2;
+        }
+
+        return -1;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Type/duration.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Type/duration.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Type/duration.php	(revision 16957)
@@ -0,0 +1,165 @@
+<?php
+/*
+http://www.w3.org/TR/xmlschema-2/
+
+[Definition:]   duration represents a duration of time. The value space of
+duration is a six-dimensional space where the coordinates designate the
+Gregorian year, month, day, hour, minute, and second components
+defined in  5.5.3.2 of [ISO 8601], respectively. These components are
+ordered in their significance by their order of appearance i.e. as year,
+month, day, hour, minute, and second. 
+
+3.2.6.1 Lexical representation
+The lexical representation for duration is the [ISO 8601] extended
+format PnYn MnDTnH nMnS, where nY represents the number of
+years, nM the number of months, nD the number of days, 'T' is the
+date/time separator, nH the number of hours, nM the number of
+minutes and nS the number of seconds. The number of seconds
+can include decimal digits to arbitrary precision.
+
+The values of the Year, Month, Day, Hour and Minutes components
+are not restricted but allow an arbitrary integer. Similarly, the
+value of the Seconds component allows an arbitrary decimal.
+Thus, the lexical representation of duration does not follow the
+alternative format of  5.5.3.2.1 of [ISO 8601].
+
+An optional preceding minus sign ('-') is allowed, to indicate a
+negative duration. If the sign is omitted a positive duration is
+indicated. See also ISO 8601 Date and Time Formats (D). 
+
+For example, to indicate a duration of 1 year, 2 months, 3 days,
+10 hours, and 30 minutes, one would write: P1Y2M3DT10H30M.
+One could also indicate a duration of minus 120 days as: -P120D. 
+
+Reduced precision and truncated representations of this format
+are allowed provided they conform to the following: 
+
+If the number of years, months, days, hours, minutes, or seconds
+in any expression equals zero, the number and its corresponding
+designator *may* be omitted. However, at least one number and
+its designator *must* be present. 
+The seconds part *may* have a decimal fraction. 
+The designator 'T' shall be absent if all of the time items are absent.
+The designator 'P' must always be present. 
+For example, P1347Y, P1347M and P1Y2MT2H are all allowed; P0Y1347M
+and P0Y1347M0D are allowed. P-1347M is not allowed although -P1347M
+is allowed. P1Y2MT is not allowed. 
+
+*/
+
+/* this is only an aproximation of duration, more work still to do.
+   see above schema url for more info on duration
+   
+   TODO: figure out best aproximation for year and month conversion to seconds
+*/
+   
+$ereg_duration = '(-)?P([0-9]+Y)?([0-9]+M)?([0-9]+D)?T?([0-9]+H)?([0-9]+M)?([0-9]+S)?';
+class SOAP_Type_duration
+{
+    // format PnYnMnDTnHnMnS
+    function unix_to_duration($seconds) {
+        return SOAP_Type_duration::getduration($seconds);
+    }
+    
+    function mod($a, $b, &$d, &$r) {
+        $d = floor( $a / $b );
+        $r = $a % $b;
+    }
+    
+    function getduration($seconds) {
+        $neg = '';
+        if ($seconds < 0) {
+            $neg = '-';
+            $seconds = $seconds * -1;
+        }
+        
+        $_mi = 60;
+        $_h = $_mi * 60;
+        $_d = $_h * 24;
+        // XXX how do we properly handle month and year values?
+        $_m = $_d * 30;
+        $_y = $_d * 365;
+
+        SOAP_Type_duration::mod($seconds, $_y, $y, $seconds);
+        SOAP_Type_duration::mod($seconds, $_m, $m, $seconds);
+        SOAP_Type_duration::mod($seconds, $_d, $d, $seconds);
+        SOAP_Type_duration::mod($seconds, $_h, $h, $seconds);
+        SOAP_Type_duration::mod($seconds, $_mi, $mi, $s);
+        
+        $duration = $neg.'P';
+        if ($y) $duration .= $y.'Y';
+        if ($m) $duration .= $m.'M';
+        if ($d) $duration .= $d.'D';
+        if ($h || $mi || $s) $duration .='T';
+        if ($h) $duration .= $h.'H';
+        if ($mi) $duration .= $mi.'M';
+        if ($s) $duration .= $s.'S';
+        if ($duration == 'P' || $duration == '-P') $duration = 'PT0S';
+        return $duration;
+    }
+    
+    function mkduration($n, $Y, $Mo, $D, $H, $Mi, $S) {
+        $_mi = 60;
+        $_h = $_mi * 60;
+        $_d = $_h * 24;
+        // XXX how do we properly handle month and year values?
+        $_m = $_d * 30;
+        $_y = $_d * 365;
+        
+        $sec = $Y * $_y + $Mo * $_m + $D * $_d + $H * $_h + $Mi * $_mi + $S;
+        if ($n == '-') $sec = $sec * -1;
+        return $sec;
+    }
+    
+    function duration_to_unix($duration) {
+        global $ereg_duration;
+        if (ereg($ereg_duration,$duration,$regs)) {
+            return SOAP_Type_duration::mkduration($regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6], $regs[7]);
+        }
+        return FALSE;
+    }
+    
+    function is_duration($duration) {
+        global $ereg_duration;
+        return ereg($ereg_duration,$duration,$regs);
+    }
+    
+    function _test($time) {
+        if (SOAP_Type_duration::is_duration($time)) {
+            $t = SOAP_Type_duration::duration_to_unix($time);
+            echo "Duration: $time is ".$t." seconds\n";
+        } else {
+            $t = SOAP_Type_duration::unix_to_duration($time);
+            echo "Seconds: $time is ".$t." duration\n";
+        }
+        return $t;
+    }
+    
+    function add($d1, $d2) {
+        $s1 = SOAP_Type_duration::duration_to_unix($d1);
+        $s2 = SOAP_Type_duration::duration_to_unix($d2);
+        return SOAP_Type_duration::unix_to_duration($s1 + $s2);
+    }
+    
+    function subtract($d1, $d2) {
+        $s1 = SOAP_Type_duration::duration_to_unix($d1);
+        $s2 = SOAP_Type_duration::duration_to_unix($d2);
+        return SOAP_Type_duration::unix_to_duration($s1 - $s2);
+    }
+
+}
+
+/* tests */
+
+$t = SOAP_Type_duration::_test('P1Y2M3DT10H30M');
+SOAP_Type_duration::_test($t);
+$t = SOAP_Type_duration::_test('-P120D');
+SOAP_Type_duration::_test($t);
+
+// duration since 1970
+$t = SOAP_Type_duration::_test(time());
+SOAP_Type_duration::_test($t);
+
+print "Add should be PT0S: ".SOAP_Type_duration::add('-P120D','P4M')."\n";
+print "Subtract should be PT0S: ".SOAP_Type_duration::subtract('P120D','P4M')."\n";
+?>
Index: branches/feature-module-update/data/module/SOAP/Parser.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Parser.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Parser.php	(revision 16957)
@@ -0,0 +1,504 @@
+<?php
+/**
+ * This file contains the code for the SOAP message parser.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Base.php';
+require_once 'SOAP/Value.php';
+
+/**
+ * SOAP Parser
+ *
+ * This class is used by SOAP::Message and SOAP::Server to parse soap
+ * packets. Originally based on SOAPx4 by Dietrich Ayala
+ * http://dietrich.ganx4.com/soapx4
+ *
+ * @access public
+ * @package SOAP
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_Parser extends SOAP_Base
+{
+    var $status = '';
+    var $position = 0;
+    var $depth = 0;
+    var $default_namespace = '';
+    var $message = array();
+    var $depth_array = array();
+    var $parent = 0;
+    var $root_struct_name = array();
+    var $header_struct_name = array();
+    var $curent_root_struct_name = '';
+    var $root_struct = array();
+    var $header_struct = array();
+    var $curent_root_struct = 0;
+    var $references = array();
+    var $need_references = array();
+
+    /**
+     * Used to handle non-root elements before root body element.
+     *
+     * @var integer
+     */
+    var $bodyDepth;
+
+    /**
+     * Constructor.
+     *
+     * @param string $xml         XML content.
+     * @param string $encoding    Character set encoding, defaults to 'UTF-8'.
+     * @param array $attachments  List of attachments.
+     */
+    function SOAP_Parser($xml, $encoding = SOAP_DEFAULT_ENCODING,
+                         $attachments = null)
+    {
+        parent::SOAP_Base('Parser');
+        $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
+
+        $this->attachments = $attachments;
+
+        // Check the XML tag for encoding.
+        if (preg_match('/<\?xml[^>]+encoding\s*?=\s*?(\'([^\']*)\'|"([^"]*)")[^>]*?[\?]>/', $xml, $m)) {
+            $encoding = strtoupper($m[2] ? $m[2] : $m[3]);
+        }
+
+        // Determine where in the message we are (envelope, header, body,
+        // method). Check whether content has been read.
+        if (!empty($xml)) {
+            // Prepare the XML parser.
+            $parser = xml_parser_create($encoding);
+            xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
+            xml_set_object($parser, $this);
+            xml_set_element_handler($parser, '_startElement', '_endElement');
+            xml_set_character_data_handler($parser, '_characterData');
+
+            // Some lame SOAP implementations add nul bytes at the end of the
+            // SOAP stream, and expat chokes on that.
+            if ($xml[strlen($xml) - 1] == 0) {
+                $xml = trim($xml);
+            }
+
+            // Parse the XML file.
+            if (!xml_parse($parser, $xml, true)) {
+                $err = sprintf('XML error on line %d col %d byte %d %s',
+                               xml_get_current_line_number($parser),
+                               xml_get_current_column_number($parser),
+                               xml_get_current_byte_index($parser),
+                               xml_error_string(xml_get_error_code($parser)));
+                $this->_raiseSoapFault($err, htmlspecialchars($xml));
+            }
+            xml_parser_free($parser);
+        }
+    }
+
+    /**
+     * Returns an array of responses.
+     *
+     * After parsing a SOAP message, use this to get the response.
+     *
+     * @return array
+     */
+    function getResponse()
+    {
+        if (!empty($this->root_struct[0])) {
+            return $this->_buildResponse($this->root_struct[0]);
+        } else {
+            return $this->_raiseSoapFault('Cannot build response');
+        }
+    }
+
+    /**
+     * Returns an array of header responses.
+     *
+     * After parsing a SOAP message, use this to get the response.
+     *
+     * @return array
+     */
+    function getHeaders()
+    {
+        if (!empty($this->header_struct[0])) {
+            return $this->_buildResponse($this->header_struct[0]);
+        } else {
+            // We don't fault if there are no headers; that can be handled by
+            // the application if necessary.
+            return null;
+        }
+    }
+
+    /**
+     * Recurses to build a multi dimensional array.
+     *
+     * @see _buildResponse()
+     */
+    function _domulti($d, &$ar, &$r, &$v, $ad = 0)
+    {
+        if ($d) {
+            $this->_domulti($d - 1, $ar, $r[$ar[$ad]], $v, $ad + 1);
+        } else {
+            $r = $v;
+        }
+    }
+
+    /**
+     * Loops through the message, building response structures.
+     *
+     * @param integer $pos  Position.
+     *
+     * @return SOAP_Value
+     */
+    function _buildResponse($pos)
+    {
+        $response = null;
+
+        if (isset($this->message[$pos]['children'])) {
+            $children = explode('|', $this->message[$pos]['children']);
+            foreach ($children as $c => $child_pos) {
+                if ($this->message[$child_pos]['type'] != null) {
+                    $response[] = $this->_buildResponse($child_pos);
+                }
+            }
+            if (isset($this->message[$pos]['arraySize'])) {
+                $ardepth = count($this->message[$pos]['arraySize']);
+                if ($ardepth > 1) {
+                    $ar = array_pad(array(), $ardepth, 0);
+                    if (isset($this->message[$pos]['arrayOffset'])) {
+                        for ($i = 0; $i < $ardepth; $i++) {
+                            $ar[$i] += $this->message[$pos]['arrayOffset'][$i];
+                        }
+                    }
+                    $elc = count($response);
+                    for ($i = 0; $i < $elc; $i++) {
+                        // Recurse to build a multi dimensional array.
+                        $this->_domulti($ardepth, $ar, $newresp, $response[$i]);
+
+                        // Increment our array pointers.
+                        $ad = $ardepth - 1;
+                        $ar[$ad]++;
+                        while ($ad > 0 &&
+                               $ar[$ad] >= $this->message[$pos]['arraySize'][$ad]) {
+                            $ar[$ad] = 0;
+                            $ad--;
+                            $ar[$ad]++;
+                        }
+                    }
+                    $response = $newresp;
+                } elseif (isset($this->message[$pos]['arrayOffset']) &&
+                          $this->message[$pos]['arrayOffset'][0] > 0) {
+                    // Check for padding.
+                    $pad = $this->message[$pos]['arrayOffset'][0] + count($response) * -1;
+                    $response = array_pad($response, $pad, null);
+                }
+            }
+        }
+
+        // Build attributes.
+        $attrs = array();
+        foreach ($this->message[$pos]['attrs'] as $atn => $atv) {
+            if (!strstr($atn, 'xmlns') && !strpos($atn, ':')) {
+                $attrs[$atn] = $atv;
+            }
+        }
+
+        // Add current node's value.
+        if ($response) {
+            $nqn = new QName($this->message[$pos]['name'],
+                             $this->message[$pos]['namespace']);
+            $tqn = new QName($this->message[$pos]['type'],
+                             $this->message[$pos]['type_namespace']);
+            $response = new SOAP_Value($nqn->fqn(),
+                                       $tqn->fqn(),
+                                       $response,
+                                       $attrs);
+            if (isset($this->message[$pos]['arrayType'])) {
+                $response->arrayType = $this->message[$pos]['arrayType'];
+            }
+        } else {
+            $nqn = new QName($this->message[$pos]['name'],
+                             $this->message[$pos]['namespace']);
+            $tqn = new QName($this->message[$pos]['type'],
+                             $this->message[$pos]['type_namespace']);
+            // Check if value is an empty array
+            if ($tqn->name == 'Array') {
+                $response =& new SOAP_Value($nqn->fqn(), $tqn->fqn(),
+                                            array(), $attrs);
+            } else {
+                $response = new SOAP_Value($nqn->fqn(),
+                                            $tqn->fqn(),
+                                            $this->message[$pos]['cdata'],
+                                            $attrs);
+            }
+        }
+
+        // Handle header attribute that we need.
+        if (array_key_exists('actor', $this->message[$pos])) {
+            $response->actor = $this->message[$pos]['actor'];
+        }
+        if (array_key_exists('mustUnderstand', $this->message[$pos])) {
+            $response->mustunderstand = $this->message[$pos]['mustUnderstand'];
+        }
+        return $response;
+    }
+
+    /**
+     * Start element handler used with the XML parser.
+     */
+    function _startElement($parser, $name, $attrs)
+    {
+        // Position in a total number of elements, starting from 0.
+        // Update class level position.
+        $pos = $this->position++;
+
+        // And set mine.
+        $this->message[$pos] = array(
+            'type' => '',
+            'type_namespace' => '',
+            'cdata' => '',
+            'pos' => $pos,
+            'id' => '');
+
+        // Parent/child/depth determinations.
+
+        // depth = How many levels removed from root?
+        // Set mine as current global depth and increment global depth value.
+        $this->message[$pos]['depth'] = $this->depth++;
+
+        // Else add self as child to whoever the current parent is.
+        if ($pos != 0) {
+            if (isset($this->message[$this->parent]['children'])) {
+                $this->message[$this->parent]['children'] .= '|' . $pos;
+            } else {
+                $this->message[$this->parent]['children'] = $pos;
+            }
+        }
+
+        // Set my parent.
+        $this->message[$pos]['parent'] = $this->parent;
+
+        // Set self as current value for this depth.
+        $this->depth_array[$this->depth] = $pos;
+        // Set self as current parent.
+        $this->parent = $pos;
+        $qname = new QName($name);
+        // Set status.
+        if (strcasecmp('envelope', $qname->name) == 0) {
+            $this->status = 'envelope';
+        } elseif (strcasecmp('header', $qname->name) == 0) {
+            $this->status = 'header';
+            $this->header_struct_name[] = $this->curent_root_struct_name = $qname->name;
+            $this->header_struct[] = $this->curent_root_struct = $pos;
+            $this->message[$pos]['type'] = 'Struct';
+        } elseif (strcasecmp('body', $qname->name) == 0) {
+            $this->status = 'body';
+            $this->bodyDepth = $this->depth;
+
+        // Set method
+        } elseif ($this->status == 'body') {
+            // Is this element allowed to be a root?
+            // TODO: this needs to be optimized, we loop through $attrs twice
+            // now.
+            $can_root = $this->depth == $this->bodyDepth + 1;
+            if ($can_root) {
+                foreach ($attrs as $key => $value) {
+                    if (stristr($key, ':root') && !$value) {
+                        $can_root = false;
+                    }
+                }
+            }
+
+            if ($can_root) {
+                $this->status = 'method';
+                $this->root_struct_name[] = $this->curent_root_struct_name = $qname->name;
+                $this->root_struct[] = $this->curent_root_struct = $pos;
+                $this->message[$pos]['type'] = 'Struct';
+            }
+        }
+
+        // Set my status.
+        $this->message[$pos]['status'] = $this->status;
+
+        // Set name.
+        $this->message[$pos]['name'] = htmlspecialchars($qname->name);
+
+        // Set attributes.
+        $this->message[$pos]['attrs'] = $attrs;
+
+        // Loop through attributes, logging ns and type declarations.
+        foreach ($attrs as $key => $value) {
+            // If ns declarations, add to class level array of valid
+            // namespaces.
+            $kqn = new QName($key);
+            if ($kqn->ns == 'xmlns') {
+                $prefix = $kqn->name;
+
+                if (in_array($value, $this->_XMLSchema)) {
+                    $this->_setSchemaVersion($value);
+                }
+
+                $this->_namespaces[$value] = $prefix;
+
+            // Set method namespace.
+            } elseif ($key == 'xmlns') {
+                $qname->ns = $this->_getNamespacePrefix($value);
+                $qname->namespace = $value;
+            } elseif ($kqn->name == 'actor') {
+                $this->message[$pos]['actor'] = $value;
+            } elseif ($kqn->name == 'mustUnderstand') {
+                $this->message[$pos]['mustUnderstand'] = $value;
+
+            // If it's a type declaration, set type.
+            } elseif ($kqn->name == 'type') {
+                $vqn = new QName($value);
+                $this->message[$pos]['type'] = $vqn->name;
+                $this->message[$pos]['type_namespace'] = $this->_getNamespaceForPrefix($vqn->ns);
+
+                // Should do something here with the namespace of specified
+                // type?
+
+            } elseif ($kqn->name == 'arrayType') {
+                $vqn = new QName($value);
+                $this->message[$pos]['type'] = 'Array';
+                if (isset($vqn->arraySize)) {
+                    $this->message[$pos]['arraySize'] = $vqn->arraySize;
+                }
+                $this->message[$pos]['arrayType'] = $vqn->name;
+
+            } elseif ($kqn->name == 'offset') {
+                $this->message[$pos]['arrayOffset'] = split(',', substr($value, 1, strlen($value) - 2));
+
+            } elseif ($kqn->name == 'id') {
+                // Save id to reference array.
+                $this->references[$value] = $pos;
+                $this->message[$pos]['id'] = $value;
+
+            } elseif ($kqn->name == 'href') {
+                if ($value[0] == '#') {
+                    $ref = substr($value, 1);
+                    if (isset($this->references[$ref])) {
+                        // cdata, type, inval.
+                        $ref_pos = $this->references[$ref];
+                        $this->message[$pos]['children'] = &$this->message[$ref_pos]['children'];
+                        $this->message[$pos]['cdata'] = &$this->message[$ref_pos]['cdata'];
+                        $this->message[$pos]['type'] = &$this->message[$ref_pos]['type'];
+                        $this->message[$pos]['arraySize'] = &$this->message[$ref_pos]['arraySize'];
+                        $this->message[$pos]['arrayType'] = &$this->message[$ref_pos]['arrayType'];
+                    } else {
+                        // Reverse reference, store in 'need reference'.
+                        if (!isset($this->need_references[$ref])) {
+                            $this->need_references[$ref] = array();
+                        }
+                        $this->need_references[$ref][] = $pos;
+                    }
+                } elseif (isset($this->attachments[$value])) {
+                    $this->message[$pos]['cdata'] = $this->attachments[$value];
+                }
+            }
+        }
+        // See if namespace is defined in tag.
+        if (isset($attrs['xmlns:' . $qname->ns])) {
+            $namespace = $attrs['xmlns:' . $qname->ns];
+        } elseif ($qname->ns && !$qname->namespace) {
+            $namespace = $this->_getNamespaceForPrefix($qname->ns);
+        } else {
+            // Get namespace.
+            $namespace = $qname->namespace ? $qname->namespace : $this->default_namespace;
+        }
+        $this->message[$pos]['namespace'] = $namespace;
+        $this->default_namespace = $namespace;
+    }
+
+    /**
+     * End element handler used with the XML parser.
+     */
+    function _endElement($parser, $name)
+    {
+        // Position of current element is equal to the last value left in
+        // depth_array for my depth.
+        $pos = $this->depth_array[$this->depth];
+
+        // Bring depth down a notch.
+        $this->depth--;
+        $qname = new QName($name);
+
+        // Get type if not explicitly declared in an xsi:type attribute.
+        // TODO: check on integrating WSDL validation here.
+        if ($this->message[$pos]['type'] == '') {
+            if (isset($this->message[$pos]['children'])) {
+                /* this is slow, need to look at some faster method
+                $children = explode('|', $this->message[$pos]['children']);
+                if (count($children) > 2 &&
+                    $this->message[$children[1]]['name'] == $this->message[$children[2]]['name']) {
+                    $this->message[$pos]['type'] = 'Array';
+                } else {
+                    $this->message[$pos]['type'] = 'Struct';
+                }*/
+                $this->message[$pos]['type'] = 'Struct';
+            } else {
+                $parent = $this->message[$pos]['parent'];
+                if ($this->message[$parent]['type'] == 'Array' &&
+                    isset($this->message[$parent]['arrayType'])) {
+                    $this->message[$pos]['type'] = $this->message[$parent]['arrayType'];
+                } else {
+                    $this->message[$pos]['type'] = 'string';
+                }
+            }
+        }
+
+        // If tag we are currently closing is the method wrapper.
+        if ($pos == $this->curent_root_struct) {
+            $this->status = 'body';
+        } elseif ($qname->name == 'Body' || $qname->name == 'Header') {
+            $this->status = 'envelope';
+        }
+
+        // Set parent back to my parent.
+        $this->parent = $this->message[$pos]['parent'];
+
+        // Handle any reverse references now.
+        $idref = $this->message[$pos]['id'];
+
+        if ($idref != '' && isset($this->need_references[$idref])) {
+            foreach ($this->need_references[$idref] as $ref_pos) {
+                // XXX is this stuff there already?
+                $this->message[$ref_pos]['children'] = &$this->message[$pos]['children'];
+                $this->message[$ref_pos]['cdata'] = &$this->message[$pos]['cdata'];
+                $this->message[$ref_pos]['type'] = &$this->message[$pos]['type'];
+                $this->message[$ref_pos]['arraySize'] = &$this->message[$pos]['arraySize'];
+                $this->message[$ref_pos]['arrayType'] = &$this->message[$pos]['arrayType'];
+            }
+        }
+    }
+
+    /**
+     * Element content handler used with the XML parser.
+     */
+    function _characterData($parser, $data)
+    {
+        $pos = $this->depth_array[$this->depth];
+        if (isset($this->message[$pos]['cdata'])) {
+            $this->message[$pos]['cdata'] .= $data;
+        } else {
+            $this->message[$pos]['cdata'] = $data;
+        }
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Transport.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Transport.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Transport.php	(revision 16957)
@@ -0,0 +1,151 @@
+<?php
+/**
+ * This file contains the code for an abstract transport layer.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com>
+ * @author     Shane Caraveo <Shane@Caraveo.com>
+ * @author     Jan Schneider <jan@horde.org>
+ * @copyright  2003-2006 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Base.php';
+
+/**
+ * SOAP Transport Layer
+ *
+ * This layer can use different protocols dependant on the endpoint url
+ * provided.
+ *
+ * No knowlege of the SOAP protocol is available at this level.
+ * No knowlege of the transport protocols is available at this level.
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net>
+ * @author   Jan Schneider <jan@horde.org>
+ */
+class SOAP_Transport extends SOAP_Base
+{
+    /**
+     * Connection endpoint URL.
+     *
+     * @var string
+     */
+    var $url = '';
+
+    /**
+     * Array containing urlparts.
+     *
+     * @see parse_url()
+     *
+     * @var mixed
+     */
+    var $urlparts = null;
+
+    /**
+     * Incoming payload.
+     *
+     * @var string
+     */
+    var $incoming_payload = '';
+
+    /**
+     * Outgoing payload.
+     *
+     * @var string
+     */
+    var $outgoing_payload = '';
+
+    /**
+     * Request encoding.
+     *
+     * @var string
+     */
+    var $encoding = SOAP_DEFAULT_ENCODING;
+
+    /**
+     * Response encoding.
+     *
+     * We assume UTF-8 if no encoding is set.
+     *
+     * @var string
+     */
+    var $result_encoding = 'UTF-8';
+
+    /**
+     * Decoded attachments from the reponse.
+     *
+     * @var array
+     */
+    var $attachments;
+
+    /**
+     * Request User-Agent.
+     *
+     * @var string
+     */
+    var $_userAgent = SOAP_LIBRARY_NAME;
+
+    /**
+     * Sends and receives SOAP data.
+     *
+     * @access public
+     * @abstract
+     *
+     * @param string  Outgoing SOAP data.
+     * @param array   Options.
+     *
+     * @return string|SOAP_Fault
+     */
+    function send($msg, $options = null)
+    {
+        return $this->_raiseSoapFault('SOAP_Transport::send() not implemented.');
+    }
+
+    function &getTransport($url, $encoding = SOAP_DEFAULT_ENCODING)
+    {
+        $urlparts = @parse_url($url);
+
+        if (!$urlparts['scheme']) {
+            $fault = SOAP_Base_Object::_raiseSoapFault("Invalid transport URI: $url");
+            return $fault;
+        }
+
+        if (strcasecmp($urlparts['scheme'], 'mailto') == 0) {
+            $transport_type = 'SMTP';
+        } elseif (strcasecmp($urlparts['scheme'], 'https') == 0) {
+            $transport_type = 'HTTP';
+        } else {
+            /* Handle other transport types */
+            $transport_type = strtoupper($urlparts['scheme']);
+        }
+        $transport_include = 'SOAP/Transport/' . basename($transport_type) . '.php';
+        $res = @include_once($transport_include);
+        if (!$res) {
+            $fault = SOAP_Base_Object::_raiseSoapFault("No Transport for {$urlparts['scheme']}");
+            return $fault;
+        }
+        $transport_class = "SOAP_Transport_$transport_type";
+        if (!class_exists($transport_class)) {
+            $fault = SOAP_Base_Object::_raiseSoapFault("No Transport class $transport_class");
+            return $fault;
+        }
+        $t =& new $transport_class($url, $encoding);
+
+        return $t;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Value.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Value.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Value.php	(revision 16957)
@@ -0,0 +1,238 @@
+<?php
+/**
+ * This file contains the code for converting values between SOAP and PHP.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2007 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Base.php';
+
+/**
+ * SOAP::Value
+ *
+ * This class converts values between PHP and SOAP.
+ *
+ * Originally based on SOAPx4 by Dietrich Ayala
+ * http://dietrich.ganx4.com/soapx4
+ *
+ * @access  public
+ * @package SOAP
+ * @author  Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ * @author  Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_Value
+{
+    /**
+     * @var string
+     */
+    var $value = null;
+
+    /**
+     * @var string
+     */
+    var $name = '';
+
+    /**
+     * @var string
+     */
+    var $type = '';
+
+    /**
+     * Namespace
+     *
+     * @var string
+     */
+    var $namespace = '';
+    var $type_namespace = '';
+
+    var $attributes = array();
+
+    /**
+     * @var string
+     */
+    var $arrayType = '';
+
+    var $options = array();
+
+    var $nqn;
+    var $tqn;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name       Name of the SOAP value {namespace}name.
+     * @param mixed $type        SOAP value {namespace}type. Determined
+     *                           automatically if not set.
+     * @param mixed $value       Value to set.
+     * @param array $attributes  Attributes.
+     */
+    function SOAP_Value($name = '', $type = false, $value = null,
+                        $attributes = array())
+    {
+        // Detect type if not passed.
+        $this->nqn = new QName($name);
+        $this->name = $this->nqn->name;
+        $this->namespace = $this->nqn->namespace;
+        $this->tqn = new QName($type);
+        $this->type = $this->tqn->name;
+        $this->type_prefix = $this->tqn->ns;
+        $this->type_namespace = $this->tqn->namespace;
+        $this->value = $value;
+        $this->attributes = $attributes;
+    }
+
+    /**
+     * Serializes this value.
+     *
+     * @param SOAP_Base $serializer  A SOAP_Base instance or subclass to
+     *                               serialize with.
+     *
+     * @return string  XML representation of $this.
+     */
+    function serialize(&$serializer)
+    {
+        return $serializer->_serializeValue($this->value,
+                                            $this->name,
+                                            $this->type,
+                                            $this->namespace,
+                                            $this->type_namespace,
+                                            $this->options,
+                                            $this->attributes,
+                                            $this->arrayType);
+    }
+
+}
+
+/**
+ * This class converts values between PHP and SOAP. It is a simple wrapper
+ * around SOAP_Value, adding support for SOAP actor and mustunderstand
+ * parameters.
+ *
+ * Originally based on SOAPx4 by Dietrich Ayala
+ * http://dietrich.ganx4.com/soapx4
+ *
+ * @access  public
+ * @package SOAP
+ * @author  Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ * @author  Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_Header extends SOAP_Value
+{
+    /**
+     * Constructor
+     *
+     * @param string $name             Name of the SOAP value {namespace}name.
+     * @param mixed $type              SOAP value {namespace}type. Determined
+     *                                 automatically if not set.
+     * @param mixed $value             Value to set
+     * @param integer $mustunderstand  Zero or one.
+     * @param mixed $attributes        Attributes.
+     */
+    function SOAP_Header($name = '', $type, $value, $mustunderstand = 0,
+                         $attributes = array())
+    {
+        if (!is_array($attributes)) {
+            $actor = $attributes;
+            $attributes = array();
+        }
+
+        parent::SOAP_Value($name, $type, $value, $attributes);
+
+        if (isset($actor)) {
+            $this->attributes['SOAP-ENV:actor'] = $actor;
+        } elseif (!isset($this->attributes['SOAP-ENV:actor'])) {
+            $this->attributes['SOAP-ENV:actor'] = 'http://schemas.xmlsoap.org/soap/actor/next';
+        }
+        $this->attributes['SOAP-ENV:mustUnderstand'] = (int)$mustunderstand;
+    }
+
+}
+
+/**
+ * This class handles MIME attachements per W3C's Note on Soap Attachements at
+ * http://www.w3.org/TR/SOAP-attachments
+ *
+ * @access  public
+ * @package SOAP
+ * @author  Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ */
+class SOAP_Attachment extends SOAP_Value
+{
+    /**
+     * Constructor.
+     *
+     * @param string $name      Name of the SOAP value <value_name>
+     * @param string $type      The attachment's MIME type.
+     * @param string $filename  The attachment's file name. Ignored if $file
+     *                          is provide.
+     * @param string $file      The attachment data.
+     */
+    function SOAP_Attachment($name = '', $type = 'application/octet-stream',
+                             $filename, $file = null)
+    {
+        parent::SOAP_Value($name, null, null);
+
+        $filedata = ($file === null) ? $this->_file2str($filename) : $file;
+        $filename = basename($filename);
+        if (PEAR::isError($filedata)) {
+            $this->options['attachment'] = $filedata;
+            return;
+        }
+
+        $cid = md5(uniqid(time()));
+
+        $this->attributes['href'] = 'cid:' . $cid; 
+
+        $this->options['attachment'] = array('body' => $filedata,
+                                             'disposition' => $filename,
+                                             'content_type' => $type,
+                                             'encoding' => 'base64',
+                                             'cid' => $cid);
+    }
+
+    /**
+     * Returns the contents of the given file name as string.
+     *
+     * @access private
+     *
+     * @param string $file_name  The file location.
+     *
+     * @return string  The file data or a PEAR_Error.
+     */
+    function _file2str($file_name)
+    {
+        if (!is_readable($file_name)) {
+            return PEAR::raiseError('File is not readable: ' . $file_name);
+        }
+
+        if (function_exists('file_get_contents')) {
+            return file_get_contents($file_name);
+        }
+
+        if (!$fd = fopen($file_name, 'rb')) {
+            return PEAR::raiseError('Could not open ' . $file_name);
+        }
+        $cont = fread($fd, filesize($file_name));
+        fclose($fd);
+
+        return $cont;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Client.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Client.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Client.php	(revision 16957)
@@ -0,0 +1,855 @@
+<?php
+/**
+ * This file contains the code for the SOAP client.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
+ * @author     Jan Schneider <jan@horde.org>       Maintenance
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Value.php';
+require_once 'SOAP/Base.php';
+require_once 'SOAP/Transport.php';
+require_once 'SOAP/WSDL.php';
+require_once 'SOAP/Fault.php';
+require_once 'SOAP/Parser.php';
+
+// Arnaud: the following code was taken from DataObject and adapted to suit
+
+// this will be horrifically slow!!!!
+// NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer
+// these two are BC/FC handlers for call in PHP4/5
+
+if (!class_exists('SOAP_Client_Overload')) {
+    if (substr(zend_version(), 0, 1) > 1) {
+        class SOAP_Client_Overload extends SOAP_Base {
+            function __call($method, $args)
+            {
+                $return = null;
+                $this->_call($method, $args, $return);
+                return $return;
+            }
+        }
+    } else {
+        if (!function_exists('clone')) {
+            eval('function clone($t) { return $t; }');
+        }
+        eval('
+            class SOAP_Client_Overload extends SOAP_Base {
+                function __call($method, $args, &$return)
+                {
+                    return $this->_call($method, $args, $return);
+                }
+            }');
+    }
+}
+
+/**
+ * SOAP Client Class
+ *
+ * This class is the main interface for making soap requests.
+ *
+ * basic usage:<code>
+ *   $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
+ *   echo $soapclient->call( string methodname [ , array parameters] );
+ * </code>
+ * or, if using PHP 5+ or the overload extension:<code>
+ *   $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
+ *   echo $soapclient->methodname( [ array parameters] );
+ * </code>
+ *
+ * Originally based on SOAPx4 by Dietrich Ayala
+ * http://dietrich.ganx4.com/soapx4
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
+ * @author   Stig Bakken <ssb@fast.no> Conversion to PEAR
+ * @author   Dietrich Ayala <dietrich@ganx4.com> Original Author
+ */
+class SOAP_Client extends SOAP_Client_Overload
+{
+    /**
+     * Communication endpoint.
+     *
+     * Currently the following transport formats are supported:
+     *  - HTTP
+     *  - SMTP
+     *
+     * Example endpoints:
+     *   http://www.example.com/soap/server.php
+     *   https://www.example.com/soap/server.php
+     *   mailto:soap@example.com
+     *
+     * @see SOAP_Client()
+     * @var string
+     */
+    var $_endpoint = '';
+
+    /**
+     * The SOAP PORT name that is used by the client.
+     *
+     * @var string
+     */
+    var $_portName = '';
+
+    /**
+     * Endpoint type e.g. 'wdsl'.
+     *
+     * @var string
+     */
+    var $_endpointType = '';
+
+    /**
+     * The received xml.
+     *
+     * @var string
+     */
+    var $xml;
+
+    /**
+     * The outgoing and incoming data stream for debugging.
+     *
+     * @var string
+     */
+    var $wire;
+
+    /**
+     * The outgoing data stream for debugging.
+     *
+     * @var string
+     */
+    var $_last_request = null;
+
+    /**
+     * The incoming data stream for debugging.
+     *
+     * @var string
+     */
+    var $_last_response = null;
+
+    /**
+     * Options.
+     *
+     * @var array
+     */
+    var $_options = array('trace' => false);
+
+    /**
+     * The character encoding used for XML parser, etc.
+     *
+     * @var string
+     */
+    var $_encoding = SOAP_DEFAULT_ENCODING;
+
+    /**
+     * The array of SOAP_Headers that we are sending.
+     *
+     * @var array
+     */
+    var $headersOut = null;
+
+    /**
+     * The headers we recieved back in the response.
+     *
+     * @var array
+     */
+    var $headersIn = null;
+
+    /**
+     * Options for the HTTP_Request class (see HTTP/Request.php).
+     *
+     * @var array
+     */
+    var $_proxy_params = array();
+
+    /**
+     * The SOAP_Transport instance.
+     *
+     * @var SOAP_Transport
+     */
+    var $_soap_transport = null;
+
+    /**
+     * Constructor.
+     *
+     * @access public
+     *
+     * @param string $endpoint       An URL.
+     * @param boolean $wsdl          Whether the endpoint is a WSDL file.
+     * @param string $portName       The service's port name to use.
+     * @param array $proxy_params    Options for the HTTP_Request class
+     *                               @see HTTP_Request
+     * @param boolean|string $cache  Use WSDL caching? The cache directory if
+     *                               a string.
+     */
+    function SOAP_Client($endpoint, $wsdl = false, $portName = false,
+                         $proxy_params = array(), $cache = false)
+    {
+        parent::SOAP_Base('Client');
+
+        $this->_endpoint = $endpoint;
+        $this->_portName = $portName;
+        $this->_proxy_params = $proxy_params;
+
+        // This hack should perhaps be removed as it might cause unexpected
+        // behaviour.
+        $wsdl = $wsdl
+            ? $wsdl
+            : strtolower(substr($endpoint, -4)) == 'wsdl';
+
+        // make values
+        if ($wsdl) {
+            $this->_endpointType = 'wsdl';
+            // instantiate wsdl class
+            $this->_wsdl = new SOAP_WSDL($this->_endpoint,
+                                         $this->_proxy_params,
+                                         $cache);
+            if ($this->_wsdl->fault) {
+                $this->_raiseSoapFault($this->_wsdl->fault);
+            }
+        }
+    }
+
+    function _reset()
+    {
+        $this->xml = null;
+        $this->wire = null;
+        $this->_last_request = null;
+        $this->_last_response = null;
+        $this->headersIn = null;
+        $this->headersOut = null;
+    }
+
+    /**
+     * Sets the character encoding.
+     *
+     * Limited to 'UTF-8', 'US_ASCII' and 'ISO-8859-1'.
+     *
+     * @access public
+     *
+     * @param string encoding
+     *
+     * @return mixed  SOAP_Fault on error.
+     */
+    function setEncoding($encoding)
+    {
+        if (in_array($encoding, $this->_encodings)) {
+            $this->_encoding = $encoding;
+            return;
+        }
+        return $this->_raiseSoapFault('Invalid Encoding');
+    }
+
+    /**
+     * Adds a header to the envelope.
+     *
+     * @access public
+     *
+     * @param SOAP_Header $soap_value  A SOAP_Header or an array with the
+     *                                 elements 'name', 'namespace',
+     *                                 'mustunderstand', and 'actor' to send
+     *                                 as a header.
+     */
+    function addHeader($soap_value)
+    {
+        // Add a new header to the message.
+        if (is_a($soap_value, 'SOAP_Header')) {
+            $this->headersOut[] = $soap_value;
+        } elseif (is_array($soap_value)) {
+            // name, value, namespace, mustunderstand, actor
+            $this->headersOut[] = new SOAP_Header($soap_value[0],
+                                                  null,
+                                                  $soap_value[1],
+                                                  $soap_value[2],
+                                                  $soap_value[3]);
+        } else {
+            $this->_raiseSoapFault('Invalid parameter provided to addHeader().  Must be an array or a SOAP_Header.');
+        }
+    }
+
+    /**
+     * Calls a method on the SOAP endpoint.
+     *
+     * The namespace parameter is overloaded to accept an array of options
+     * that can contain data necessary for various transports if it is used as
+     * an array, it MAY contain a namespace value and a soapaction value.  If
+     * it is overloaded, the soapaction parameter is ignored and MUST be
+     * placed in the options array.  This is done to provide backwards
+     * compatibility with current clients, but may be removed in the future.
+     * The currently supported values are:<pre>
+     *   namespace
+     *   soapaction
+     *   timeout (HTTP socket timeout)
+     *   transfer-encoding (SMTP, Content-Transfer-Encoding: header)
+     *   from (SMTP, From: header)
+     *   subject (SMTP, Subject: header)
+     *   headers (SMTP, hash of extra SMTP headers)
+     * </pre>
+     *
+     * @access public
+     *
+     * @param string $method           The method to call.
+     * @param array $params            The method parameters.
+     * @param string|array $namespace  Namespace or hash with options.
+     * @param string $soapAction
+     *
+     * @return mixed  The method result or a SOAP_Fault on error.
+     */
+    function &call($method, &$params, $namespace = false, $soapAction = false)
+    {
+        $this->headersIn = null;
+        $this->_last_request = null;
+        $this->_last_response = null;
+        $this->wire = null;
+        $this->xml = null;
+
+        $soap_data = $this->_generate($method, $params, $namespace, $soapAction);
+        if (PEAR::isError($soap_data)) {
+            $fault = $this->_raiseSoapFault($soap_data);
+            return $fault;
+        }
+
+        // _generate() may have changed the endpoint if the WSDL has more
+        // than one service, so we need to see if we need to generate a new
+        // transport to hook to a different URI.  Since the transport protocol
+        // can also change, we need to get an entirely new object.  This could
+        // probably be optimized.
+        if (!$this->_soap_transport ||
+            $this->_endpoint != $this->_soap_transport->url) {
+            $this->_soap_transport =& SOAP_Transport::getTransport($this->_endpoint);
+            if (PEAR::isError($this->_soap_transport)) {
+                $fault =& $this->_soap_transport;
+                $this->_soap_transport = null;
+                $fault = $this->_raiseSoapFault($fault);
+                return $fault;
+            }
+        }
+        $this->_soap_transport->encoding = $this->_encoding;
+
+        // Send the message.
+        $transport_options = array_merge_recursive($this->_proxy_params,
+                                                   $this->_options);
+        $this->xml = $this->_soap_transport->send($soap_data, $transport_options);
+
+        // Save the wire information for debugging.
+        if ($this->_options['trace']) {
+            $this->_last_request = $this->_soap_transport->outgoing_payload;
+            $this->_last_response = $this->_soap_transport->incoming_payload;
+            $this->wire = $this->getWire();
+        }
+        if ($this->_soap_transport->fault) {
+            $fault = $this->_raiseSoapFault($this->xml);
+            return $fault;
+        }
+
+        if (isset($this->_options['result']) &&
+            $this->_options['result'] != 'parse') {
+            return $this->xml;
+        }
+
+        $this->__result_encoding = $this->_soap_transport->result_encoding;
+
+        $result = &$this->parseResponse($this->xml, $this->__result_encoding,
+                                        $this->_soap_transport->attachments);
+        return $result;
+    }
+
+    /**
+     * Sets an option to use with the transport layers.
+     *
+     * For example:
+     * <code>
+     * $soapclient->setOpt('curl', CURLOPT_VERBOSE, 1)
+     * </code>
+     * to pass a specific option to curl if using an SSL connection.
+     *
+     * @access public
+     *
+     * @param string $category  Category to which the option applies or option
+     *                          name.
+     * @param string $option    An option name if $category is a category name,
+     *                          an option value if $category is an option name.
+     * @param string $value     An option value if $category is a category
+     *                          name.
+     */
+    function setOpt($category, $option, $value = null)
+    {
+        if (!is_null($value)) {
+            if (!isset($this->_options[$category])) {
+                $this->_options[$category] = array();
+            }
+            $this->_options[$category][$option] = $value;
+        } else {
+            $this->_options[$category] = $option;
+        }
+    }
+
+    /**
+     * Call method supporting the overload extension.
+     *
+     * If the overload extension is loaded, you can call the client class with
+     * a soap method name:
+     * <code>
+     * $soap = new SOAP_Client(....);
+     * $value = $soap->getStockQuote('MSFT');
+     * </code>
+     *
+     * @access public
+     *
+     * @param string $method        The method to call.
+     * @param array $params         The method parameters.
+     * @param mixed $return_value   Will get the method's return value
+     *                              assigned.
+     *
+     * @return boolean  Always true.
+     */
+    function _call($method, $params, &$return_value)
+    {
+        // Overloading lowercases the method name, we need to look into the
+        // WSDL and try to find the correct method name to get the correct
+        // case for the call.
+        if ($this->_wsdl) {
+            $this->_wsdl->matchMethod($method);
+        }
+
+        $return_value =& $this->call($method, $params);
+
+        return true;
+    }
+
+    /**
+     * @deprecated Use getLastRequest().
+     */
+    function &__getlastrequest()
+    {
+        $request = $this->getLastRequest();
+        return $request;
+    }
+
+    /**
+     * Returns the XML content of the last SOAP request.
+     *
+     * @return string  The last request.
+     */
+    function getLastRequest()
+    {
+        return $this->_last_request;
+    }
+
+    /**
+     * @deprecated Use getLastResponse().
+     */
+    function &__getlastresponse()
+    {
+        $response =& $this->getLastResponse;
+        return $response;
+    }
+
+    /**
+     * Returns the XML content of the last SOAP response.
+     *
+     * @return string  The last response.
+     */
+    function getLastResponse()
+    {
+        return $this->_last_response;
+    }
+
+    /**
+     * @deprecated Use setUse().
+     */
+    function __use($use)
+    {
+        $this->setUse($use);
+    }
+
+    /**
+     * Sets the SOAP encoding.
+     *
+     * @param string $use  Either 'literal' or 'encoded' (section 5).
+     */
+    function setUse($use)
+    {
+        $this->_options['use'] = $use;
+    }
+
+    /**
+     * @deprecated Use setStyle().
+     */
+    function __style($style)
+    {
+        $this->setStyle($style);
+    }
+
+    /**
+     * Sets the SOAP encoding style.
+     *
+     * @param string $style  Either 'document' or 'rpc'.
+     */
+    function setStyle($style)
+    {
+        $this->_options['style'] = $style;
+    }
+
+    /**
+     * @deprecated Use setTrace().
+     */
+    function __trace($level)
+    {
+        $this->setTrace($level);
+    }
+
+    /**
+     * Sets whether to trace the traffic on the transport level.
+     *
+     * @see getWire()
+     *
+     * @param boolean $trace
+     */
+    function setTrace($trace)
+    {
+        $this->_options['trace'] = $trace;
+    }
+
+    function _generate($method, &$params, $namespace = false,
+                       $soapAction = false)
+    {
+        $this->fault = null;
+        $this->_options['input'] = 'parse'; 
+        $this->_options['result'] = 'parse';
+        $this->_options['parameters'] = false;
+
+        if ($params && gettype($params) != 'array') {
+            $params = array($params);
+        }
+
+        if (gettype($namespace) == 'array') {
+            foreach ($namespace as $optname => $opt) {
+                $this->_options[strtolower($optname)] = $opt;
+            }
+            if (isset($this->_options['namespace'])) {
+                $namespace = $this->_options['namespace'];
+            } else {
+                $namespace = false;
+            }
+        } else {
+            // We'll place $soapAction into our array for usage in the
+            // transport.
+            $this->_options['soapaction'] = $soapAction;
+            $this->_options['namespace'] = $namespace;
+        }
+
+        if ($this->_endpointType == 'wsdl') {
+            $this->_setSchemaVersion($this->_wsdl->xsd);
+
+            // Get port name.
+            if (!$this->_portName) {
+                $this->_portName = $this->_wsdl->getPortName($method);
+            }
+            if (PEAR::isError($this->_portName)) {
+                return $this->_raiseSoapFault($this->_portName);
+            }
+
+            // Get endpoint.
+            $this->_endpoint = $this->_wsdl->getEndpoint($this->_portName);
+            if (PEAR::isError($this->_endpoint)) {
+                return $this->_raiseSoapFault($this->_endpoint);
+            }
+
+            // Get operation data.
+            $opData = $this->_wsdl->getOperationData($this->_portName, $method);
+
+            if (PEAR::isError($opData)) {
+                return $this->_raiseSoapFault($opData);
+            }
+            $namespace = $opData['namespace'];
+            $this->_options['style'] = $opData['style'];
+            $this->_options['use'] = $opData['input']['use'];
+            $this->_options['soapaction'] = $opData['soapAction'];
+
+            // Set input parameters.
+            if ($this->_options['input'] == 'parse') {
+                $this->_options['parameters'] = $opData['parameters'];
+                $nparams = array();
+                if (isset($opData['input']['parts']) &&
+                    count($opData['input']['parts'])) {
+                    foreach ($opData['input']['parts'] as $name => $part) {
+                        $xmlns = '';
+                        $attrs = array();
+                        // Is the name a complex type?
+                        if (isset($part['element'])) {
+                            $xmlns = $this->_wsdl->namespaces[$part['namespace']];
+                            $part = $this->_wsdl->elements[$part['namespace']][$part['type']];
+                            $name = $part['name'];
+                        }
+                        if (isset($params[$name]) ||
+                            $this->_wsdl->getDataHandler($name, $part['namespace'])) {
+                            $nparams[$name] =& $params[$name];
+                        } else {
+                            // We now force an associative array for
+                            // parameters if using WSDL.
+                            return $this->_raiseSoapFault("The named parameter $name is not in the call parameters.");
+                        }
+                        if (gettype($nparams[$name]) != 'object' ||
+                            !is_a($nparams[$name], 'SOAP_Value')) {
+                            // Type is likely a qname, split it apart, and get
+                            // the type namespace from WSDL.
+                            $qname = new QName($part['type']);
+                            if ($qname->ns) {
+                                $type_namespace = $this->_wsdl->namespaces[$qname->ns];
+                            } elseif (isset($part['namespace'])) {
+                                $type_namespace = $this->_wsdl->namespaces[$part['namespace']];
+                            } else {
+                                $type_namespace = null;
+                            }
+                            $qname->namespace = $type_namespace;
+                            $pqname = $name;
+                            if ($xmlns) {
+                                $pqname = '{' . $xmlns . '}' . $name;
+                            }
+                            $nparams[$name] = new SOAP_Value($pqname,
+                                                              $qname->fqn(),
+                                                              $nparams[$name],
+                                                              $attrs);
+                        } else {
+                            // WSDL fixups to the SOAP value.
+                        }
+                    }
+                }
+                $params =& $nparams;
+                unset($nparams);
+            }
+        } else {
+            $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
+        }
+
+        // Serialize the message.
+        $this->_section5 = (!isset($this->_options['use']) ||
+                            $this->_options['use'] != 'literal');
+
+        if (!isset($this->_options['style']) ||
+            $this->_options['style'] == 'rpc') {
+            $this->_options['style'] = 'rpc';
+            $this->docparams = true;
+            $mqname = new QName($method, $namespace);
+            $methodValue = new SOAP_Value($mqname->fqn(), 'Struct', $params);
+            $soap_msg = $this->makeEnvelope($methodValue,
+                                            $this->headersOut,
+                                            $this->_encoding,
+                                            $this->_options);
+        } else {
+            if (!$params) {
+                $mqname = new QName($method, $namespace);
+                $mynull = null;
+                $params = new SOAP_Value($mqname->fqn(), 'Struct', $mynull);
+            } elseif ($this->_options['input'] == 'parse') {
+                if (is_array($params)) {
+                    $nparams = array();
+                    $keys = array_keys($params);
+                    foreach ($keys as $k) {
+                        if (gettype($params[$k]) != 'object') {
+                            $nparams[] = new SOAP_Value($k,
+                                                        false,
+                                                        $params[$k]);
+                        } else {
+                            $nparams[] =& $params[$k];
+                        }
+                    }
+                    $params =& $nparams;
+                }
+                if ($this->_options['parameters']) {
+                    $mqname = new QName($method, $namespace);
+                    $params = new SOAP_Value($mqname->fqn(),
+                                             'Struct',
+                                             $params);
+                }
+            }
+            $soap_msg = $this->makeEnvelope($params,
+                                            $this->headersOut,
+                                            $this->_encoding,
+                                            $this->_options);
+        }
+        unset($this->headersOut);
+
+        if (PEAR::isError($soap_msg)) {
+            return $this->_raiseSoapFault($soap_msg);
+        }
+
+        // Handle MIME or DIME encoding.
+        // TODO: DIME encoding should move to the transport, do it here for
+        // now and for ease of getting it done.
+        if (count($this->_attachments)) {
+            if ((isset($this->_options['attachments']) &&
+                 $this->_options['attachments'] == 'Mime') ||
+                isset($this->_options['Mime'])) {
+                $soap_msg = $this->_makeMimeMessage($soap_msg, $this->_encoding);
+            } else {
+                // default is dime
+                $soap_msg = $this->_makeDIMEMessage($soap_msg, $this->_encoding);
+                $this->_options['headers']['Content-Type'] = 'application/dime';
+            }
+            if (PEAR::isError($soap_msg)) {
+                return $this->_raiseSoapFault($soap_msg);
+            }
+        }
+
+        // Instantiate client.
+        if (is_array($soap_msg)) {
+            $soap_data = $soap_msg['body'];
+            if (count($soap_msg['headers'])) {
+                if (isset($this->_options['headers'])) {
+                    $this->_options['headers'] = array_merge($this->_options['headers'], $soap_msg['headers']);
+                } else {
+                    $this->_options['headers'] = $soap_msg['headers'];
+                }
+            }
+        } else {
+            $soap_data = $soap_msg;
+        }
+
+        return $soap_data;
+    }
+
+    /**
+     * @deprecated Use parseResponse().
+     */
+    function &__parse(&$response, $encoding, &$attachments)
+    {
+        return $this->parseResponse($response, $encoding, $attachments);
+    }
+
+    /**
+     * Parses a SOAP response.
+     *
+     * @see SOAP_Parser::
+     *
+     * @param string $response    XML content of SOAP response.
+     * @param string $encoding    Character set encoding, defaults to 'UTF-8'.
+     * @param array $attachments  List of attachments.
+     */
+    function &parseResponse($response, $encoding, &$attachments)
+    {
+        // Parse the response.
+        $response =& new SOAP_Parser($response, $encoding, $attachments);
+        if ($response->fault) {
+            $fault =& $this->_raiseSoapFault($response->fault);
+            return $fault;
+        }
+
+        // Return array of parameters.
+        $return =& $response->getResponse();
+        $headers =& $response->getHeaders();
+        if ($headers) {
+            $this->headersIn =& $this->_decodeResponse($headers, false);
+        }
+
+        $decoded = &$this->_decodeResponse($return);
+        return $decoded;
+    }
+
+    /**
+    *   Converts a complex SOAP_Value into a PHP Array
+    *
+    *   @param SOAP_Value   $response   value object
+    *   @param boolean      $shift      FIXME
+    *   @return Array
+    */
+    function &_decodeResponse($response, $shift = true)
+    {
+        if (!$response) {
+            $decoded = null;
+            return $decoded;
+        }
+
+        // Check for valid response.
+        if (PEAR::isError($response)) {
+            $fault =& $this->_raiseSoapFault($response);
+            return $fault;
+        } elseif (!is_a($response, 'soap_value')) {
+            $fault =& $this->_raiseSoapFault("Didn't get SOAP_Value object back from client");
+            return $fault;
+        }
+
+        // Decode to native php datatype.
+        $returnArray =& $this->_decode($response);
+
+        // Fault?
+        if (PEAR::isError($returnArray)) {
+            $fault =& $this->_raiseSoapFault($returnArray);
+            return $fault;
+        }
+
+        if (is_object($returnArray) &&
+            strcasecmp(get_class($returnArray), 'stdClass') == 0) {
+            $returnArray = get_object_vars($returnArray);
+        }
+        if (is_array($returnArray)) {
+            if (isset($returnArray['faultcode']) ||
+                isset($returnArray['SOAP-ENV:faultcode'])) {
+                $faultcode = $faultstring = $faultdetail = $faultactor = '';
+                foreach ($returnArray as $k => $v) {
+                    if (stristr($k, 'faultcode')) $faultcode = $v;
+                    if (stristr($k, 'faultstring')) $faultstring = $v;
+                    if (stristr($k, 'detail')) $faultdetail = $v;
+                    if (stristr($k, 'faultactor')) $faultactor = $v;
+                }
+                $fault =& $this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
+                return $fault;
+            }
+            // Return array of return values.
+            if ($shift && count($returnArray) == 1) {
+                $decoded = array_shift($returnArray);
+                return $decoded;
+            }
+            return $returnArray;
+        }
+        return $returnArray;
+    }
+
+    /**
+     * @deprecated Use getWire().
+     */
+    function __get_wire()
+    {
+        return $this->getWire();
+    }
+
+    /**
+     * Returns the outgoing and incoming traffic on the transport level.
+     *
+     * Tracing has to be enabled.
+     *
+     * @see setTrace()
+     *
+     * @return string  The complete traffic between the client and the server.
+     */
+    function getWire()
+    {
+        if ($this->_options['trace'] &&
+            ($this->_last_request || $this->_last_response)) {
+            return "OUTGOING:\n\n" .
+                $this->_last_request .
+                "\n\nINCOMING\n\n" .
+                preg_replace("/></",">\r\n<", $this->_last_response);
+        }
+
+        return null;
+    }
+
+}
Index: branches/feature-module-update/data/module/SOAP/Server/TCP.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Server/TCP.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Server/TCP.php	(revision 16957)
@@ -0,0 +1,106 @@
+<?php
+/**
+ * This file contains the code for the TCP SOAP server.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Server.php';
+
+/**
+ * SOAP Server Class that implements a TCP SOAP Server.
+ * http://www.pocketsoap.com/specs/smtpbinding/
+ *
+ * This class overrides the default HTTP server, providing the ability to
+ * accept socket connections and execute SOAP calls.
+ *
+ * TODO:
+ *   use Net_Socket
+ *   implement some security scheme
+ *   implement support for attachments
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net>
+ */
+class SOAP_Server_TCP extends SOAP_Server {
+
+    var $headers = array();
+    var $localaddr;
+    var $port;
+    var $listen;
+    var $reuse;
+
+    function SOAP_Server_TCP($localaddr = '127.0.0.1', $port = 10000,
+                             $listen = 5, $reuse = true)
+    {
+        parent::SOAP_Server();
+        $this->localaddr = $localaddr;
+        $this->port = $port;
+        $this->listen = $listen;
+        $this->reuse = $reuse;
+    }
+
+    function run()
+    {
+        if (($sock = socket_create(AF_INET, SOCK_STREAM, 0)) < 0) {
+            return $this->_raiseSoapFault('socket_create() failed. Reason: ' . socket_strerror($sock));
+        }
+        if ($this->reuse &&
+            !@socket_setopt($sock, SOL_SOCKET, SO_REUSEADDR, 1)) {
+            return $this->_raiseSoapFault('socket_setopt() failed. Reason: ' . socket_strerror(socket_last_error($sock)));
+        }
+        if (($ret = socket_bind($sock, $this->localaddr, $this->port)) < 0) {
+            return $this->_raiseSoapFault('socket_bind() failed. Reason: ' . socket_strerror($ret));
+        }
+        if (($ret = socket_listen($sock, $this->listen)) < 0) {
+            return $this->_raiseSoapFault('socket_listen() failed. Reason: ' . socket_strerror($ret));
+        }
+
+        while (true) {
+            $data = null;
+            if (($msgsock = socket_accept($sock)) < 0) {
+                $this->_raiseSoapFault('socket_accept() failed. Reason: ' . socket_strerror($msgsock));
+                break;
+            }
+            while ($buf = socket_read($msgsock, 8192)) {
+                if (!$buf = trim($buf)) {
+                    continue;
+                }
+                $data .= $buf;
+            }
+
+            if ($data) {
+                $response = $this->service($data);
+                /* Write to the socket. */
+                if (!socket_write($msgsock, $response, strlen($response))) {
+                    return $this->_raiseSoapFault('Error sending response data reason ' . socket_strerror());
+                }
+            }
+
+            socket_close ($msgsock);
+        }
+
+        socket_close ($sock);
+    }
+
+    function service(&$data)
+    {
+        /* TODO: we need to handle attachments somehow. */
+        return $this->parseRequest($data, $attachments);
+    }
+}
Index: branches/feature-module-update/data/module/SOAP/Server/Email.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Server/Email.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Server/Email.php	(revision 16957)
@@ -0,0 +1,198 @@
+<?php
+/**
+ * This file contains the code for the email SOAP server.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Server.php';
+require_once 'SOAP/Client.php';
+require_once 'SOAP/Transport.php';
+require_once 'Mail/mimeDecode.php';
+
+/**
+ * SOAP Server Class that implements an email SOAP server.
+ * http://www.pocketsoap.com/specs/smtpbinding/
+ *
+ * This class overrides the default HTTP server, providing the ability to
+ * parse an email message and execute SOAP calls.  This class DOES NOT pop the
+ * message; the message, complete with headers, must be passed in as a
+ * parameter to the service function call.
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net>
+ */
+class SOAP_Server_Email extends SOAP_Server {
+
+    var $headers = array();
+
+    function SOAP_Server_Email($send_response = true)
+    {
+        parent::SOAP_Server();
+        $this->send_response = $send_response;
+    }
+
+    /**
+     * Removes HTTP headers from response.
+     *
+     * TODO: use PEAR email classes
+     *
+     * @return boolean
+     * @access private
+     */
+    function _parseEmail(&$data)
+    {
+        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $data, $match)) {
+
+            if (preg_match_all('/^(.*?):\s+(.*)$/m', $match[1], $matches)) {
+                $hc = count($matches[0]);
+                for ($i = 0; $i < $hc; $i++) {
+                    $this->headers[strtolower($matches[1][$i])] = trim($matches[2][$i]);
+                }
+            }
+
+            if (!stristr($this->headers['content-type'], 'text/xml')) {
+                    $this->_raiseSoapFault('Invalid Content Type', '', '', 'Client');
+                    return false;
+            }
+
+            if (strcasecmp($this->headers['content-transfer-encoding'], 'base64')==0) {
+                /* Unfold lines. */
+                $enctext = preg_replace("/[\r|\n]/", '', $match[2]);
+                $data = base64_decode($enctext);
+            } else {
+                $data = $match[2];
+            }
+
+            /* If no content, return false. */
+            return strlen($this->request) > 0;
+        }
+
+        $this->_raiseSoapFault('Invalid Email Format', '', '', 'Client');
+
+        return false;
+    }
+
+    function client(&$data)
+    {
+        $attachments = array();
+
+        /* If neither matches, we'll just try it anyway. */
+        if (stristr($data, 'Content-Type: application/dime')) {
+            $this->_decodeDIMEMessage($data, $this->headers, $attachments);
+        } elseif (stristr($data, 'MIME-Version:')) {
+            /* This is a mime message, let's decode it. */
+            $this->_decodeMimeMessage($data, $this->headers, $attachments);
+        } else {
+            /* The old fallback, but decodeMimeMessage handles things fine. */
+            $this->_parseEmail($data);
+        }
+
+        /* Get the character encoding of the incoming request treat incoming
+         * data as UTF-8 if no encoding set. */
+        if (!$this->soapfault &&
+            !$this->_getContentEncoding($this->headers['content-type'])) {
+            $this->xml_encoding = SOAP_DEFAULT_ENCODING;
+            /* An encoding we don't understand, return a fault. */
+            $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server');
+        }
+
+        if ($this->soapfault) {
+            return $this->soapfault->getFault();
+        }
+
+        $client =& new SOAP_Client(null);
+
+        return $client->parseResponse($data, $this->xml_encoding, $this->attachments);
+    }
+
+    function service(&$data, $endpoint = '', $send_response = true,
+                     $dump = false)
+    {
+        $this->endpoint = $endpoint;
+        $attachments = array();
+        $headers = array();
+
+        /* If neither matches, we'll just try it anyway. */
+        if (stristr($data, 'Content-Type: application/dime')) {
+            $this->_decodeDIMEMessage($data, $this->headers, $attachments);
+            $useEncoding = 'DIME';
+        } elseif (stristr($data, 'MIME-Version:')) {
+            /* This is a mime message, let's decode it. */
+            $this->_decodeMimeMessage($data, $this->headers, $attachments);
+            $useEncoding = 'Mime';
+        } else {
+            /* The old fallback, but decodeMimeMessage handles things fine. */
+            $this->_parseEmail($data);
+        }
+
+        /* Get the character encoding of the incoming request treat incoming
+         * data as UTF-8 if no encoding set. */
+        if (!$this->_getContentEncoding($this->headers['content-type'])) {
+            $this->xml_encoding = SOAP_DEFAULT_ENCODING;
+            /* An encoding we don't understand, return a fault. */
+            $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server');
+            $response = $this->getFaultMessage();
+        }
+
+        if ($this->soapfault) {
+            $response = $this->soapfault->message();
+        } else {
+            $soap_msg = $this->parseRequest($data,$attachments);
+
+            /* Handle Mime or DIME encoding. */
+            /* TODO: DIME Encoding should move to the transport, do it here
+             * for now and for ease of getting it done. */
+            if (count($this->_attachments)) {
+                if ($useEncoding == 'Mime') {
+                    $soap_msg = $this->_makeMimeMessage($soap_msg);
+                } else {
+                    /* Default is DIME. */
+                    $soap_msg = $this->_makeDIMEMessage($soap_msg);
+                    $soap_msg['headers']['Content-Type'] = 'application/dime';
+                }
+                if (PEAR::isError($soap_msg)) {
+                    return $this->raiseSoapFault($soap_msg);
+                }
+            }
+
+            if (is_array($soap_msg)) {
+                $response = $soap_msg['body'];
+                if (count($soap_msg['headers'])) {
+                    $headers = $soap_msg['headers'];
+                }
+            } else {
+                $response = $soap_msg;
+            }
+        }
+
+        if ($this->send_response) {
+            if ($dump) {
+                print $response;
+            } else {
+                $from = array_key_exists('reply-to', $this->headers) ? $this->headers['reply-to'] : $this->headers['from'];
+
+                $soap_transport =& SOAP_Transport::getTransport('mailto:' . $from, $this->response_encoding);
+                $from = $this->endpoint ? $this->endpoint : $this->headers['to'];
+                $headers['In-Reply-To'] = $this->headers['message-id'];
+                $options = array('from' => $from, 'subject' => $this->headers['subject'], 'headers' => $headers);
+                $soap_transport->send($response, $options);
+            }
+        }
+    }
+}
Index: branches/feature-module-update/data/module/SOAP/Server/Email_Gateway.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Server/Email_Gateway.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Server/Email_Gateway.php	(revision 16957)
@@ -0,0 +1,138 @@
+<?php
+/**
+ * This file contains the code for the email-HTTP SOAP gateway server.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Server/Email.php';
+require_once 'SOAP/Transport.php';
+
+/**
+ * SOAP Server Class that implements an email SOAP server.
+ * http://www.pocketsoap.com/specs/smtpbinding/
+ *
+ * This class overrides the default HTTP server, providing the ability to
+ * parse an email message and execute soap calls.  This class DOES NOT pop the
+ * message; the message, complete with headers, must be passed in as a
+ * parameter to the service function call.
+ *
+ * This class calls a provided HTTP SOAP server, forwarding the email request,
+ * then sending the HTTP response out as an email.
+ *
+ * @access   public
+ * @package  SOAP
+ * @author   Shane Caraveo <shane@php.net>
+ */
+class SOAP_Server_Email_Gateway extends SOAP_Server_Email {
+
+    var $gateway = null;
+    var $dump = false;
+
+    function SOAP_Server_Email_Gateway($gateway = '', $send_response = true,
+                                       $dump = false)
+    {
+        parent::SOAP_Server();
+        $this->send_response = $send_response;
+        $this->gateway = $gateway;
+        $this->dump = $dump;
+    }
+
+    function service(&$data, $gateway = '', $endpoint = '',
+                     $send_response = true, $dump = false)
+    {
+        $this->endpoint = $endpoint;
+        $response = '';
+        $useEncoding = 'Mime';
+        $options = array();
+        if (!$gateway) {
+            $gateway = $this->gateway;
+        }
+
+        /* We have a full set of headers, need to find the first blank
+         * line. */
+        $this->_parseEmail($data);
+        if ($this->fault) {
+            $response = $this->fault->message();
+        }
+        if ($this->headers['content-type'] == 'application/dime')
+            $useEncoding = 'DIME';
+
+        /* Call the HTTP Server. */
+        if (!$response) {
+            $soap_transport =& SOAP_Transport::getTransport($gateway, $this->xml_encoding);
+            if ($soap_transport->fault) {
+                $response = $soap_transport->fault->message();
+            }
+        }
+
+        /* Send the message. */
+        if (!$response) {
+            $options['soapaction'] = $this->headers['soapaction'];
+            $options['headers']['Content-Type'] = $this->headers['content-type'];
+
+            $response = $soap_transport->send($data, $options);
+            if (isset($this->headers['mime-version']))
+                $options['headers']['MIME-Version'] = $this->headers['mime-version'];
+
+            if ($soap_transport->fault) {
+                $response = $soap_transport->fault->message();
+            } else {
+                foreach ($soap_transport->transport->attachments as $cid => $body) {
+                    $this->attachments[] = array('body' => $body, 'cid' => $cid, 'encoding' => 'base64');
+                }
+                if (count($this->_attachments)) {
+                    if ($useEncoding == 'Mime') {
+                        $soap_msg = $this->_makeMimeMessage($response);
+                        $options['headers']['MIME-Version'] = '1.0';
+                    } else {
+                        /* Default is DIME. */
+                        $soap_msg = $this->_makeDIMEMessage($response);
+                        $options['headers']['Content-Type'] = 'application/dime';
+                    }
+                    if (PEAR::isError($soap_msg)) {
+                        return $this->_raiseSoapFault($soap_msg);
+                    }
+                    if (is_array($soap_msg)) {
+                        $response = $soap_msg['body'];
+                        if (count($soap_msg['headers'])) {
+                            if (isset($options['headers'])) {
+                                $options['headers'] = array_merge($options['headers'], $soap_msg['headers']);
+                            } else {
+                                $options['headers'] = $soap_msg['headers'];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if ($this->send_response) {
+            if ($this->dump || $dump) {
+                print $response;
+            } else {
+                $from = array_key_exists('reply-to', $this->headers) ? $this->headers['reply-to'] : $this->headers['from'];
+
+                $soap_transport =& SOAP_Transport::getTransport('mailto:' . $from, $this->response_encoding);
+                $from = $this->endpoint ? $this->endpoint : $this->headers['to'];
+                $headers = array('In-Reply-To' => $this->headers['message-id']);
+                $options = array('from' => $from, 'subject'=> $this->headers['subject'], 'headers' => $headers);
+                $soap_transport->send($response, $options);
+            }
+        }
+    }
+}
Index: branches/feature-module-update/data/module/SOAP/Disco.php
===================================================================
--- branches/feature-module-update/data/module/SOAP/Disco.php	(revision 16957)
+++ branches/feature-module-update/data/module/SOAP/Disco.php	(revision 16957)
@@ -0,0 +1,383 @@
+<?php
+/**
+ * This file contains the code for the DISCO server, providing DISO and WSDL
+ * services.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is available at
+ * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
+ * did not receive a copy of the PHP license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@php.net so we can
+ * mail you a copy immediately.
+ *
+ * @category   Web Services
+ * @package    SOAP
+ * @author     Dmitri Vinogradov <dimitri@vinogradov.de>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @author     Jan Schneider <jan@horde.org>
+ * @copyright  2003-2005 The PHP Group
+ * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
+ * @link       http://pear.php.net/package/SOAP
+ */
+
+require_once 'SOAP/Base.php';
+
+class SOAP_DISCO_Server extends SOAP_Base_Object {
+
+    var $namespaces     = array(SCHEMA_WSDL => 'wsdl', SCHEMA_SOAP => 'soap');
+    var $import_ns      = array();
+    var $wsdl           = '';
+    var $disco          = '';
+    var $_wsdl          = array();
+    var $_disco         = array();
+    var $_service_name  = '';
+    var $_service_ns    = '';
+    var $_service_desc  = '';
+    var $_portname      = '';
+    var $_bindingname   = '';
+    var $soap_server    = NULL;
+
+
+    function SOAP_DISCO_Server($soap_server, $service_name, $service_desc = '',
+                               $import_ns = null)
+    {
+        parent::SOAP_Base_Object('Server');
+
+        if ( !is_object($soap_server)
+            || !get_class($soap_server) == 'soap_server') return;
+
+        $this->_service_name = $service_name;
+        $this->_service_ns = "urn:$service_name";
+        $this->_service_desc = $service_desc;
+        $this->import_ns = isset($import_ns) ? $import_ns : $this->import_ns;
+        $this->soap_server = $soap_server;
+        $this->host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
+    }
+
+    function getDISCO()
+    {
+        $this->_generate_DISCO();
+        return $this->disco;
+    }
+
+    function getWSDL()
+    {
+        $this->_generate_WSDL();
+        return $this->wsdl;
+    }
+
+    function _generate_DISCO()
+    {
+        // DISCO
+        $this->_disco['disco:discovery']['attr']['xmlns:disco'] = SCHEMA_DISCO;
+        $this->_disco['disco:discovery']['attr']['xmlns:scl'] = SCHEMA_DISCO_SCL;
+        $this->_disco['disco:discovery']['scl:contractRef']['attr']['ref'] =
+            (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] == 'on')
+            ? 'https://' . $this->host . $_SERVER['PHP_SELF'] . '?wsdl'
+            : 'http://'  . $this->host . $_SERVER['PHP_SELF'] . '?wsdl';
+
+        // generate disco xml
+        $this->_generate_DISCO_XML($this->_disco);
+    }
+
+    function _generate_WSDL()
+    {
+        // WSDL
+        if (is_array($this->soap_server->_namespaces)) {
+            // need to get: typens, xsd & SOAP-ENC
+            $flipped = array_flip($this->soap_server->_namespaces);
+            $this->namespaces[$this->_service_ns] = 'tns';
+            $this->namespaces[$flipped['xsd']] = 'xsd';
+            $this->namespaces[$flipped['SOAP-ENC']] = 'SOAP-ENC';
+        }
+
+        // DEFINITIONS
+        $this->_wsdl['definitions']['attr']['name'] = $this->_service_name;
+        $this->_wsdl['definitions']['attr']['targetNamespace'] = $this->_service_ns;
+        foreach ($this->namespaces as $ns => $prefix) {
+            $this->_wsdl['definitions']['attr']['xmlns:' . $prefix] = $ns;
+        }
+        $this->_wsdl['definitions']['attr']['xmlns'] = SCHEMA_WSDL;
+
+        // Import namespaces. Seems to not work yet: wsdl.exe fom .NET can't
+        // handle imported complete wsdl-definitions.
+        if (count($this->import_ns)) {
+            $i = 0;
+            foreach ($this->import_ns as $_ns => $_location) {
+                $this->_wsdl['definitions']['import'][$i]['attr']['location'] = $_location;
+                $this->_wsdl['definitions']['import'][$i]['attr']['namespace'] = $_ns;
+                $i++;
+            }
+        }
+        $this->_wsdl['definitions']['types']['attr']['xmlns']='http://schemas.xmlsoap.org/wsdl/';
+        $this->_wsdl['definitions']['types']['schema']=array();
+
+        // Placeholder for messages
+        $this->_wsdl['definitions']['message'] = array();
+
+        // PORTTYPE-NAME
+        $this->_portname = $this->_service_name . 'Port';
+        $this->_wsdl['definitions']['portType']['attr']['name'] = $this->_portname;
+
+        // BINDING-NAME
+        $this->_bindingname = $this->_service_name . 'Binding';
+        $this->_wsdl['definitions']['binding']['attr']['name'] = $this->_bindingname;
+        $this->_wsdl['definitions']['binding']['attr']['type'] = 'tns:' . $this->_portname;
+        $this->_wsdl['definitions']['binding']['soap:binding']['attr']['style'] = 'rpc';
+        $this->_wsdl['definitions']['binding']['soap:binding']['attr']['transport'] = SCHEMA_SOAP_HTTP;
+
+        // SERVICE
+        $this->_wsdl['definitions']['service']['attr']['name'] = $this->_service_name . 'Service';
+        $this->_wsdl['definitions']['service']['documentation']['attr'] = '';
+        $this->_wsdl['definitions']['service']['documentation'] = htmlentities($this->_service_desc);
+        $this->_wsdl['definitions']['service']['port']['attr']['name'] = $this->_portname;
+        $this->_wsdl['definitions']['service']['port']['attr']['binding'] = 'tns:' . $this->_bindingname;
+        $this->_wsdl['definitions']['service']['port']['soap:address']['attr']['location'] =
+            (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] == 'on')
+            ? 'https://' . $this->host . $_SERVER['PHP_SELF']
+            : 'http://'  . $this->host . $_SERVER['PHP_SELF'];
+
+        //
+        $dispatch_keys = array_keys($this->soap_server->dispatch_objects);
+        $dc = count($dispatch_keys);
+        for ($di = 0; $di < $dc; $di++) {
+            $namespace = $dispatch_keys[$di];
+            $namespace_objects =& $this->soap_server->dispatch_objects[$namespace];
+            $oc = count($namespace_objects);
+            for ($oi = 0; $oi < $oc; $oi++) {
+                $object = $namespace_objects[$oi];
+                // types definitions
+                $this->addSchemaFromMap($object->__typedef);
+                // MESSAGES
+                $this->addMethodsFromMap($object->__dispatch_map, $namespace, get_class($object));
+            }
+        }
+        if (isset($this->soap_server->dispatch_map)) {
+            $this->addMethodsFromMap($this->soap_server->dispatch_map, $namespace);
+        }
+
+        // generate wsdl
+        $this->_generate_WSDL_XML();
+    }
+
+    function &_getSchema($namespace)
+    {
+        // SCHEMA
+        $c = count($this->_wsdl['definitions']['types']['schema']);
+        for($i = 0; $i < $c; $i++) {
+            if ($this->_wsdl['definitions']['types']['schema'][$i]['attr']['targetNamespace'] == $namespace) {
+                return $this->_wsdl['definitions']['types']['schema'][$i];
+            }
+        }
+
+        // don't have this namespace
+        $schema = array();
+        $schema['attr'] = array();
+        $schema['complexType'] = array();
+        $schema['attr']['xmlns'] = array_search('xsd',$this->namespaces);
+        $schema['attr']['targetNamespace'] = $namespace;
+        $this->_wsdl['definitions']['types']['schema'][] =& $schema;
+
+        return $schema;
+    }
+
+    function addSchemaFromMap(&$map)
+    {
+        if (!$map) {
+            return;
+        }
+
+        foreach ($map as $_type_name => $_type_def) {
+            list($typens,$type) = $this->_getTypeNs($_type_name);
+            if ($typens == 'xsd') {
+                // cannot add to xsd, lets use method_namespace
+                $typens = 'tns';
+            }
+            $schema =& $this->_getSchema(array_search($typens, $this->namespaces));
+            if (!$this->_ifComplexTypeExists($schema['complexType'], $type)) {
+                $ctype =& $schema['complexType'][];
+                $ctype['attr']['name'] = $type;
+                foreach ($_type_def as $_varname => $_vartype) {
+                    if (!is_int($_varname)) {
+                        list($_vartypens,$_vartype) = $this->_getTypeNs($_vartype);
+                        $ctype['all']['attr'] = '';
+                        $el =& $ctype['all']['element'][];
+                        $el['attr']['name'] = $_varname;
+                        $el['attr']['type'] = $_vartypens . ':' . $_vartype;
+                    } else {
+                        $ctype['complexContent']['attr'] = '';
+                        $ctype['complexContent']['restriction']['attr']['base'] = 'SOAP-ENC:Array';
+                        foreach ($_vartype as $array_type) {
+                            list($_vartypens, $_vartype) = $this->_getTypeNs($array_type);
+                            $ctype['complexContent']['restriction']['attribute']['attr']['ref'] = 'SOAP-ENC:arrayType';
+                            $ctype['complexContent']['restriction']['attribute']['attr']['wsdl:arrayType'] = $_vartypens . ':' . $_vartype . '[]';
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    function addMethodsFromMap(&$map, $namespace, $classname = null)
+    {
+        if (!$map) {
+            return;
+        }
+
+        foreach ($map as $method_name => $method_types) {
+            if (array_key_exists('namespace', $method_types)) {
+                $method_namespace = $method_types['namespace'];
+            } else {
+                $method_namespace = $namespace;
+            }
+
+            // INPUT
+            $input_message = array('attr' => array('name' => $method_name . 'Request'));
+            if (isset($method_types['in']) && is_array($method_types['in'])) {
+                foreach ($method_types['in'] as $name => $type) {
+                    list($typens, $type) = $this->_getTypeNs($type);
+                    $part = array();
+                    $part['attr']['name'] = $name;
+                    $part['attr']['type'] = $typens . ':' . $type;
+                    $input_message['part'][] = $part;
+                }
+            }
+            $this->_wsdl['definitions']['message'][] = $input_message;
+
+            // OUTPUT
+            $output_message = array('attr' => array('name' => $method_name . 'Response'));
+            if (isset($method_types['out']) && is_array($method_types['out'])) {
+                foreach ($method_types['out'] as $name => $type) {
+                    list($typens, $type) = $this->_getTypeNs($type);
+                    $part = array();
+                    $part['attr']['name'] = $name;
+                    $part['attr']['type'] = $typens . ':' . $type;
+                    $output_message['part'][] = $part;
+                }
+            }
+            $this->_wsdl['definitions']['message'][] = $output_message;
+
+            // PORTTYPES
+            $operation = array();
+            $operation['attr']['name'] = $method_name;
+            // INPUT
+            $operation['input']['attr']['message'] = 'tns:' . $input_message['attr']['name'];
+            // OUTPUT
+            $operation['output']['attr']['message'] = 'tns:' . $output_message['attr']['name'];
+            $this->_wsdl['definitions']['portType']['operation'][] = $operation;
+
+            // BINDING
+            $binding = array();
+            $binding['attr']['name'] = $method_name;
+            $action = $method_namespace . '#' . ($classname ? $classname . '#' : '') . $method_name;
+            $binding['soap:operation']['attr']['soapAction'] = $action;
+            // INPUT
+            $binding['input']['attr'] = '';
+            $binding['input']['soap:body']['attr']['use'] = 'encoded';
+            $binding['input']['soap:body']['attr']['namespace'] = $method_namespace;
+            $binding['input']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING;
+            // OUTPUT
+            $binding['output']['attr'] = '';
+            $binding['output']['soap:body']['attr']['use'] = 'encoded';
+            $binding['output']['soap:body']['attr']['namespace'] = $method_namespace;
+            $binding['output']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING;
+            $this->_wsdl['definitions']['binding']['operation'][] = $binding;
+        }
+    }
+
+    function _generate_DISCO_XML($disco_array)
+    {
+        $disco = '<?xml version="1.0"?>';
+        foreach ($disco_array as $key => $val) {
+            $disco .= $this->_arrayToNode($key,$val);
+        }
+        $this->disco = $disco;
+    }
+
+    function _generate_WSDL_XML()
+    {
+        $wsdl = '<?xml version="1.0"?>';
+        foreach ($this->_wsdl as $key => $val) {
+            $wsdl .= $this->_arrayToNode($key, $val);
+        }
+        $this->wsdl = $wsdl;
+    }
+
+    function _arrayToNode($node_name = '', $array)
+    {
+        $return = '';
+        if (is_array($array)) {
+            // we have a node if there's key 'attr'
+            if (array_key_exists('attr',$array)) {
+                $return .= "<$node_name";
+                if (is_array($array['attr'])) {
+                    foreach ($array['attr'] as $attr_name => $attr_value) {
+                        $return .= " $attr_name=\"$attr_value\"";
+                    }
+                }
+
+                // unset 'attr' and proceed other childs...
+                unset($array['attr']);
+
+                if (count($array) > 0) {
+                    $i = 0;
+                    foreach ($array as $child_node_name => $child_node_value) {
+                        $return .= $i == 0 ? ">\n" : '';
+                        $return .= $this->_arrayToNode($child_node_name,$child_node_value);
+                        $i++;
+                    }
+                    $return .= "</$node_name>\n";
+                } else {
+                    $return .= " />\n";
+                }
+            } else {
+                // we have no 'attr' key in array - so it's list of nodes with
+                // the same name ...
+                foreach ($array as $child_node_name => $child_node_value) {
+                    $return .= $this->_arrayToNode($node_name,$child_node_value);
+                }
+            }
+        } else {
+            // $array is not an array
+            if ($array !='') {
+                // and its not empty
+                $return .= "<$node_name>$array</$node_name>\n";
+            } else {
+                // and its empty...
+                $return .= "<$node_name />\n";
+            }
+        }
+        return $return;
+    }
+
+    function _getTypeNs($type)
+    {
+        preg_match_all("'\{(.*)\}'sm", $type, $m);
+        if (isset($m[1][0]) && $m[1][0] != '') {
+            if (!array_key_exists($m[1][0],$this->namespaces)) {
+                $ns_pref = 'ns' . count($this->namespaces);
+                $this->namespaces[$m[1][0]] = $ns_pref;
+                $this->_wsdl['definitions']['attr']['xmlns:' . $ns_pref] = $m[1][0];
+            }
+            $typens = $this->namespaces[$m[1][0]];
+            $type = ereg_replace($m[0][0],'',$type);
+        } else {
+            $typens = 'xsd';
+        }
+        return array($typens,$type);
+    }
+
+    function _ifComplexTypeExists($typesArray, $type_name)
+    {
+        if (is_array($typesArray)) {
+            foreach ($typesArray as $type_data) {
+                if ($type_data['attr']['name'] == $type_name) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
