1<?php
2/**
3 * The Horde_Rpc_Soap class provides a PHP 5 Soap implementation
4 * of the Horde RPC system.
5 *
6 * Copyright 2003-2017 Horde LLC (http://www.horde.org/)
7 *
8 * See the enclosed file COPYING for license information (LGPL). If you
9 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
10 *
11 * @author  Chuck Hagenbuch <chuck@horde.org>
12 * @package Rpc
13 */
14class Horde_Rpc_Soap extends Horde_Rpc
15{
16    /**
17     * Resource handler for the SOAP server.
18     *
19     * @var object
20     */
21    var $_server;
22
23    /**
24     * List of types to emit in the WSDL.
25     *
26     * @var array
27     */
28    var $_allowedTypes = array();
29
30    /**
31     * List of method names to allow.
32     *
33     * @var array
34     */
35    var $_allowedMethods = array();
36
37    /**
38     * Name of the SOAP service to use in the WSDL.
39     *
40     * @var string
41     */
42    var $_serviceName = null;
43
44    /**
45     * SOAP server constructor
46     *
47     * @access private
48     */
49    public function __construct($request, $params = array())
50    {
51        parent::__construct($request, $params);
52
53        if (!empty($params['allowedTypes'])) {
54            $this->_allowedTypes = $params['allowedTypes'];
55        }
56        if (!empty($params['allowedMethods'])) {
57            $this->_allowedMethods = $params['allowedMethods'];
58        }
59        if (!empty($params['serviceName'])) {
60            $this->_serviceName = $params['serviceName'];
61        }
62
63        $this->_server = new SoapServer(null, array('uri' => (string)Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/rpc.php', true, -1)));
64        $this->_server->addFunction(SOAP_FUNCTIONS_ALL);
65        $this->_server->setClass('Horde_Rpc_Soap_Caller', $params);
66    }
67
68    /**
69     * Takes a SOAP request and returns the result.
70     *
71     * @param string  The raw request string.
72     *
73     * @return string  The XML encoded response from the server.
74     */
75    function getResponse($request)
76    {
77        if ($request == 'disco' || $request == 'wsdl') {
78            /*@TODO Replace with subcalls for disco and wsdl generation from the old SOAP driver. */
79            //$handler = new Horde_Rpc_Soap($this->_params);
80            //return $handler->getResponse($request);
81        }
82
83        /* We can't use Horde_Util::bufferOutput() here for some reason. */
84        $beginTime = time();
85        ob_start();
86        $this->_server->handle($request);
87        Horde::log(
88            sprintf('SOAP call: %s(%s) by %s serviced in %d seconds, sent %d bytes in response',
89                    $GLOBALS['__horde_rpc_PhpSoap']['lastMethodCalled'],
90                    implode(', ', array_map(function ($a) { return is_array($a) ? 'Array' : $a; },
91                                            $GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'])),
92                    $GLOBALS['registry']->getAuth(),
93                    time() - $beginTime,
94                    ob_get_length()),
95            'INFO');
96        return ob_get_clean();
97    }
98
99    /**
100     * Builds a SOAP request and sends it to the SOAP server.
101     *
102     * This statically called method is actually the SOAP client.
103     *
104     * @param string|Horde_Url $url  Ignored.
105     * @param string $method         The method to call.
106     * @param array $params          A hash containing any necessary parameters
107     *                               for the method call.
108     * @param SoapClient $soap       A configured SoapClient object.
109     *
110     * @return mixed  The returned result from the method
111     * @throws Horde_Rpc_Exception
112     */
113    public static function request($url, $method, $params, $soap)
114    {
115        try {
116            return $soap->__soapCall($method, $params);
117        } catch (Exception $e) {
118            throw new Horde_Rpc_Exception($e);
119        }
120    }
121
122}
123
124class Horde_Rpc_Soap_Caller {
125
126    /**
127     * List of method names to allow.
128     *
129     * @var array
130     */
131    protected $_allowedMethods = array();
132
133    /**
134     */
135    public function __construct($params = array())
136    {
137        if (!empty($params['allowedMethods'])) {
138            $this->_allowedMethods = $params['allowedMethods'];
139        }
140    }
141
142    /**
143     * Will be registered as the handler for all methods called in the
144     * SOAP server and will call the appropriate function through the registry.
145     *
146     * @todo  PEAR SOAP operates on a copy of this object at some unknown
147     *        point and therefore doesn't have access to instance
148     *        variables if they're set here. Instead, globals are used
149     *        to track the method name and args for the logging code.
150     *        Once this is PHP 5-only, the globals can go in favor of
151     *        instance variables.
152     *
153     * @access private
154     *
155     * @param string $method    The name of the method called by the RPC request.
156     * @param array $params     The passed parameters.
157     * @param mixed $data       Unknown.
158     *
159     * @return mixed            The result of the called registry method.
160     */
161    public function __call($method, $params)
162    {
163        $method = str_replace('.', '/', $method);
164
165        if (!empty($this->_params['allowedMethods']) &&
166            !in_array($method, $this->_params['allowedMethods'])) {
167            return sprintf(Horde_Rpc_Translation::t("Method \"%s\" is not defined"), $method);
168        }
169
170        $GLOBALS['__horde_rpc_PhpSoap']['lastMethodCalled'] = $method;
171        $GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'] =
172            !empty($params) ? $params : array();
173
174        if (!$GLOBALS['registry']->hasMethod($method)) {
175            return sprintf(Horde_Rpc_Translation::t("Method \"%s\" is not defined"), $method);
176        }
177
178        return $GLOBALS['registry']->call($method, $params);
179    }
180
181}
182