Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
 
18
/**
19
 * SOAP web service implementation classes and methods.
20
 *
21
 * @package    webservice_soap
22
 * @copyright  2009 Petr Skodak
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
global $CFG;
26
require_once($CFG->dirroot . '/webservice/lib.php');
27
use webservice_soap\wsdl;
28
 
29
/**
30
 * SOAP service server implementation.
31
 *
32
 * @package    webservice_soap
33
 * @copyright  2009 Petr Skodak
34
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 * @since Moodle 2.0
36
 */
37
class webservice_soap_server extends webservice_base_server {
38
 
39
    /** @var moodle_url The server URL. */
40
    protected $serverurl;
41
 
42
    /** @var  SoapServer The Soap */
43
    protected $soapserver;
44
 
45
    /** @var  string The response. */
46
    protected $response;
47
 
48
    /** @var  string The class name of the virtual class generated for this web service. */
49
    protected $serviceclass;
50
 
51
    /** @var bool WSDL mode flag. */
52
    protected $wsdlmode;
53
 
54
    /** @var \webservice_soap\wsdl The object for WSDL generation. */
55
    protected $wsdl;
56
 
57
    /**
58
     * Contructor.
59
     *
60
     * @param string $authmethod authentication method of the web service (WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN, ...)
61
     */
62
    public function __construct($authmethod) {
63
        parent::__construct($authmethod);
64
         // Must not cache wsdl - the list of functions is created on the fly.
65
        ini_set('soap.wsdl_cache_enabled', '0');
66
        $this->wsname = 'soap';
67
        $this->wsdlmode = false;
68
    }
69
 
70
    /**
71
     * This method parses the $_POST and $_GET superglobals and looks for the following information:
72
     * - User authentication parameters:
73
     *   - Username + password (wsusername and wspassword), or
74
     *   - Token (wstoken)
75
     */
76
    protected function parse_request() {
77
        // Retrieve and clean the POST/GET parameters from the parameters specific to the server.
78
        parent::set_web_service_call_settings();
79
 
80
        if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) {
81
            $this->username = optional_param('wsusername', null, PARAM_RAW);
82
            $this->password = optional_param('wspassword', null, PARAM_RAW);
83
 
84
            if (!$this->username or !$this->password) {
85
                // Workaround for the trouble with & in soap urls.
86
                $authdata = get_file_argument();
87
                $authdata = explode('/', trim($authdata, '/'));
88
                if (count($authdata) == 2) {
89
                    list($this->username, $this->password) = $authdata;
90
                }
91
            }
92
            $this->serverurl = new moodle_url('/webservice/soap/simpleserver.php/' . $this->username . '/' . $this->password);
93
        } else {
94
            $this->token = optional_param('wstoken', null, PARAM_RAW);
95
 
96
            $this->serverurl = new moodle_url('/webservice/soap/server.php');
97
            $this->serverurl->param('wstoken', $this->token);
98
        }
99
 
100
        if ($wsdl = optional_param('wsdl', 0, PARAM_INT)) {
101
            $this->wsdlmode = true;
102
        }
103
    }
104
 
105
    /**
106
     * Runs the SOAP web service.
107
     *
108
     * @throws coding_exception
109
     * @throws moodle_exception
110
     * @throws webservice_access_exception
111
     */
112
    public function run() {
113
        // We will probably need a lot of memory in some functions.
114
        raise_memory_limit(MEMORY_EXTRA);
115
 
116
        // Set some longer timeout since operations may need longer time to finish.
117
        \core_external\external_api::set_timeout();
118
 
119
        // Set up exception handler.
120
        set_exception_handler(array($this, 'exception_handler'));
121
 
122
        // Init all properties from the request data.
123
        $this->parse_request();
124
 
125
        // Authenticate user, this has to be done after the request parsing. This also sets up $USER and $SESSION.
126
        $this->authenticate_user();
127
 
128
        // Make a list of all functions user is allowed to execute.
129
        $this->init_service_class();
130
 
131
        if ($this->wsdlmode) {
132
            // Generate the WSDL.
133
            $this->generate_wsdl();
134
        }
135
 
136
        // Log the web service request.
137
        $params = array(
138
            'other' => array(
139
                'function' => 'unknown'
140
            )
141
        );
142
        /** @var \core\event\webservice_function_called $event */
143
        $event = \core\event\webservice_function_called::create($params);
144
        $event->trigger();
145
 
146
        // Handle the SOAP request.
147
        $this->handle();
148
 
149
        // Session cleanup.
150
        $this->session_cleanup();
151
        die;
152
    }
153
 
154
    /**
155
     * Generates the WSDL.
156
     */
157
    protected function generate_wsdl() {
158
        // Initialise WSDL.
159
        $this->wsdl = new wsdl($this->serviceclass, $this->serverurl);
160
        // Register service struct classes as complex types.
161
        foreach ($this->servicestructs as $structinfo) {
162
            $this->wsdl->add_complex_type($structinfo->classname, $structinfo->properties);
163
        }
164
        // Register the method for the WSDL generation.
165
        foreach ($this->servicemethods as $methodinfo) {
166
            $this->wsdl->register($methodinfo->name, $methodinfo->inputparams, $methodinfo->outputparams, $methodinfo->description);
167
        }
168
    }
169
 
170
    /**
171
     * Handles the web service function call.
172
     */
173
    protected function handle() {
174
        if ($this->wsdlmode) {
175
            // Prepare the response.
176
            $this->response = $this->wsdl->to_xml();
177
 
178
            // Send the results back in correct format.
179
            $this->send_response();
180
        } else {
181
            $wsdlurl = clone($this->serverurl);
182
            $wsdlurl->param('wsdl', 1);
183
 
184
            $options = array(
185
                'uri' => $this->serverurl->out(false)
186
            );
187
            // Initialise the SOAP server.
188
            $this->soapserver = new SoapServer($wsdlurl->out(false), $options);
189
            if (!empty($this->serviceclass)) {
190
                $this->soapserver->setClass($this->serviceclass);
191
                // Get all the methods for the generated service class then register to the SOAP server.
192
                $functions = get_class_methods($this->serviceclass);
193
                $this->soapserver->addFunction($functions);
194
            }
195
 
196
            // Get soap request from raw POST data.
197
            $soaprequest = file_get_contents('php://input');
198
            // Handle the request.
199
            try {
200
                $this->soapserver->handle($soaprequest);
201
            } catch (Exception $e) {
202
                $this->fault($e);
203
            }
204
        }
205
    }
206
 
207
    /**
208
     * Send the error information to the WS client formatted as an XML document.
209
     *
210
     * @param Exception $ex the exception to send back
211
     */
212
    protected function send_error($ex = null) {
213
        if ($ex) {
214
            $info = $ex->getMessage();
215
            if (debugging() and isset($ex->debuginfo)) {
216
                $info .= ' - '.$ex->debuginfo;
217
            }
218
        } else {
219
            $info = 'Unknown error';
220
        }
221
 
222
        // Initialise new DOM document object.
223
        $dom = new DOMDocument('1.0', 'UTF-8');
224
 
225
        // Fault node.
226
        $fault = $dom->createElement('SOAP-ENV:Fault');
227
        // Faultcode node.
228
        $fault->appendChild($dom->createElement('faultcode', 'MOODLE:error'));
229
        // Faultstring node.
230
        $fault->appendChild($dom->createElement('faultstring', $info));
231
 
232
        // Body node.
233
        $body = $dom->createElement('SOAP-ENV:Body');
234
        $body->appendChild($fault);
235
 
236
        // Envelope node.
237
        $envelope = $dom->createElement('SOAP-ENV:Envelope');
238
        $envelope->setAttribute('xmlns:SOAP-ENV', 'http://schemas.xmlsoap.org/soap/envelope/');
239
        $envelope->appendChild($body);
240
        $dom->appendChild($envelope);
241
 
242
        $this->response = $dom->saveXML();
243
        $this->send_response();
244
    }
245
 
246
    /**
247
     * Send the result of function call to the WS client.
248
     */
249
    protected function send_response() {
250
        $this->send_headers();
251
        echo $this->response;
252
    }
253
 
254
    /**
255
     * Internal implementation - sending of page headers.
256
     */
257
    protected function send_headers() {
258
        header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
259
        header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT');
260
        header('Pragma: no-cache');
261
        header('Accept-Ranges: none');
262
        header('Content-Length: ' . strlen($this->response));
263
        header('Content-Type: application/xml; charset=utf-8');
264
        header('Content-Disposition: inline; filename="response.xml"');
265
    }
266
 
267
    /**
268
     * Generate a server fault.
269
     *
270
     * Note that the parameter order is the reverse of SoapFault's constructor parameters.
271
     *
272
     * Moodle note: basically we return the faultactor (errorcode) and faultdetails (debuginfo).
273
     *
274
     * If an exception is passed as the first argument, its message and code
275
     * will be used to create the fault object.
276
     *
277
     * @link   http://www.w3.org/TR/soap12-part1/#faultcodes
278
     * @param  string|Exception $fault
279
     * @param  string $code SOAP Fault Codes
280
     */
281
    public function fault($fault = null, $code = 'Receiver') {
282
        $allowedfaultmodes = array(
283
            'VersionMismatch', 'MustUnderstand', 'DataEncodingUnknown',
284
            'Sender', 'Receiver', 'Server'
285
        );
286
        if (!in_array($code, $allowedfaultmodes)) {
287
            $code = 'Receiver';
288
        }
289
 
290
        // Intercept any exceptions and add the errorcode and debuginfo (optional).
291
        $actor = null;
292
        $details = null;
293
        $errorcode = 'unknownerror';
294
        $message = get_string($errorcode);
295
        if ($fault instanceof Exception) {
296
            // Add the debuginfo to the exception message if debuginfo must be returned.
297
            $actor = isset($fault->errorcode) ? $fault->errorcode : null;
298
            $errorcode = $actor;
299
            if (debugging()) {
300
                $message = $fault->getMessage();
301
                $details = isset($fault->debuginfo) ? $fault->debuginfo : null;
302
            }
303
        } else if (is_string($fault)) {
304
            $message = $fault;
305
        }
306
 
307
        $this->soapserver->fault($code, $message . ' | ERRORCODE: ' . $errorcode, $actor, $details);
308
    }
309
}
310
 
311
/**
312
 * SOAP test client class
313
 *
314
 * @package    webservice_soap
315
 * @copyright  2009 Petr Skodak
316
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
317
 * @since Moodle 2.0
318
 */
319
class webservice_soap_test_client implements webservice_test_client_interface {
320
 
321
    /**
322
     * Execute test client WS request
323
     *
324
     * @param string $serverurl server url (including token parameter or username/password parameters)
325
     * @param string $function function name
326
     * @param array $params parameters of the called function
327
     * @return mixed
328
     */
329
    public function simpletest($serverurl, $function, $params) {
330
        global $CFG;
331
 
332
        require_once($CFG->dirroot . '/webservice/soap/lib.php');
333
        $client = new webservice_soap_client($serverurl);
334
        return $client->call($function, $params);
335
    }
336
}