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
 * REST web service implementation classes and methods.
20
 *
21
 * @package    webservice_rest
22
 * @copyright  2009 Jerome Mouneyrac
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
use core_external\external_multiple_structure;
27
use core_external\external_single_structure;
28
use core_external\external_value;
29
 
30
require_once("$CFG->dirroot/webservice/lib.php");
31
 
32
/**
33
 * REST service server implementation.
34
 *
35
 * @package    webservice_rest
36
 * @copyright  2009 Petr Skoda (skodak)
37
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
class webservice_rest_server extends webservice_base_server {
40
 
41
    /** @var string return method ('xml' or 'json') */
42
    protected $restformat;
43
 
44
    /**
45
     * Contructor
46
     *
47
     * @param string $authmethod authentication method of the web service (WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN, ...)
48
     * @param string $restformat Format of the return values: 'xml' or 'json'
49
     */
50
    public function __construct($authmethod) {
51
        parent::__construct($authmethod);
52
        $this->wsname = 'rest';
53
    }
54
 
55
    /**
56
     * Set the request format to.
57
     */
58
    public function set_rest_format(): void {
59
        // Get GET and POST parameters.
60
        $methodvariables = array_merge($_GET, $_POST);
61
 
62
        // Retrieve REST format parameter - 'xml' (default) or 'json'.
63
        $restformatisset = isset($methodvariables['moodlewsrestformat'])
64
                && (($methodvariables['moodlewsrestformat'] == 'xml' || $methodvariables['moodlewsrestformat'] == 'json'));
65
        $this->restformat = $restformatisset ? $methodvariables['moodlewsrestformat'] : 'xml';
66
    }
67
 
68
    /**
69
     * This method parses the $_POST and $_GET superglobals and looks for
70
     * the following information:
71
     *  1/ user authentication - username+password or token (wsusername, wspassword and wstoken parameters)
72
     *  2/ function name (wsfunction parameter)
73
     *  3/ function parameters (all other parameters except those above)
74
     *  4/ text format parameters
75
     *  5/ return rest format xml/json
76
     */
77
    protected function parse_request() {
78
 
79
        // Retrieve and clean the POST/GET parameters from the parameters specific to the server.
80
        parent::set_web_service_call_settings();
81
 
82
        // Get GET and POST parameters.
83
        $methodvariables = array_merge($_GET, $_POST);
84
        $this->set_rest_format();
85
        unset($methodvariables['moodlewsrestformat']);
86
 
87
        if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) {
88
            $this->username = isset($methodvariables['wsusername']) ? $methodvariables['wsusername'] : null;
89
            unset($methodvariables['wsusername']);
90
 
91
            $this->password = isset($methodvariables['wspassword']) ? $methodvariables['wspassword'] : null;
92
            unset($methodvariables['wspassword']);
93
 
94
            $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null;
95
            unset($methodvariables['wsfunction']);
96
 
97
            $this->parameters = $methodvariables;
98
 
99
        } else {
100
            $this->token = isset($methodvariables['wstoken']) ? $methodvariables['wstoken'] : null;
101
            unset($methodvariables['wstoken']);
102
 
103
            $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null;
104
            unset($methodvariables['wsfunction']);
105
 
106
            $this->parameters = $methodvariables;
107
        }
108
    }
109
 
110
    /**
111
     * Send the result of function call to the WS client
112
     * formatted as XML document.
113
     */
114
    protected function send_response() {
115
 
116
        //Check that the returned values are valid
117
        try {
118
            if ($this->function->returns_desc != null) {
119
                $validatedvalues = \core_external\external_api::clean_returnvalue(
120
                    $this->function->returns_desc,
121
                    $this->returns
122
                );
123
            } else {
124
                $validatedvalues = null;
125
            }
126
        } catch (Exception $ex) {
127
            $exception = $ex;
128
        }
129
 
130
        if (!empty($exception)) {
131
            $response =  $this->generate_error($exception);
132
        } else {
133
            //We can now convert the response to the requested REST format
134
            if ($this->restformat == 'json') {
135
                $response = json_encode($validatedvalues);
136
            } else {
137
                $response = '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
138
                $response .= '<RESPONSE>'."\n";
139
                $response .= self::xmlize_result($validatedvalues, $this->function->returns_desc);
140
                $response .= '</RESPONSE>'."\n";
141
            }
142
        }
143
 
144
        $this->send_headers();
145
        echo $response;
146
    }
147
 
148
    /**
149
     * Send the error information to the WS client
150
     * formatted as XML document.
151
     * Note: the exception is never passed as null,
152
     *       it only matches the abstract function declaration.
153
     * @param exception $ex the exception that we are sending
154
     */
155
    protected function send_error($ex=null) {
156
        // Unless debugging is completely off, log the error to server error log.
157
        if (debugging('', DEBUG_MINIMAL)) {
158
            $info = get_exception_info($ex);
159
            // This format is the same as default_exception_handler() in setuplib.php but with the
160
            // word 'REST' instead of 'Default', to make it easy to reuse any existing processing.
161
            error_log('REST exception handler: ' . $info->message . ' Debug: ' .
162
                    $info->debuginfo . "\n" . format_backtrace($info->backtrace, true));
163
        }
164
 
165
        $this->send_headers();
166
        echo $this->generate_error($ex);
167
    }
168
 
169
    /**
170
     * Build the error information matching the REST returned value format (JSON or XML)
171
     * @param exception $ex the exception we are converting in the server rest format
172
     * @return string the error in the requested REST format
173
     */
174
    protected function generate_error($ex) {
175
        if ($this->restformat == 'json') {
176
            $errorobject = new stdClass;
177
            $errorobject->exception = get_class($ex);
178
            if (isset($ex->errorcode)) {
179
                $errorobject->errorcode = $ex->errorcode;
180
            }
181
            $errorobject->message = $ex->getMessage();
182
            if (debugging() and isset($ex->debuginfo)) {
183
                $errorobject->debuginfo = $ex->debuginfo;
184
            }
185
            $error = json_encode($errorobject);
186
        } else {
187
            $error = '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
188
            $error .= '<EXCEPTION class="'.get_class($ex).'">'."\n";
189
            if (isset($ex->errorcode)) {
190
                $error .= '<ERRORCODE>' . htmlspecialchars($ex->errorcode, ENT_COMPAT, 'UTF-8')
191
                        . '</ERRORCODE>' . "\n";
192
            }
193
            $error .= '<MESSAGE>'.htmlspecialchars($ex->getMessage(), ENT_COMPAT, 'UTF-8').'</MESSAGE>'."\n";
194
            if (debugging() and isset($ex->debuginfo)) {
195
                $error .= '<DEBUGINFO>'.htmlspecialchars($ex->debuginfo, ENT_COMPAT, 'UTF-8').'</DEBUGINFO>'."\n";
196
            }
197
            $error .= '</EXCEPTION>'."\n";
198
        }
199
        return $error;
200
    }
201
 
202
    /**
203
     * Internal implementation - sending of page headers.
204
     */
205
    protected function send_headers() {
206
        if ($this->restformat == 'json') {
207
            header('Content-type: application/json');
208
        } else {
209
            header('Content-Type: application/xml; charset=utf-8');
210
            header('Content-Disposition: inline; filename="response.xml"');
211
        }
212
        header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
213
        header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
214
        header('Pragma: no-cache');
215
        header('Accept-Ranges: none');
216
        // Allow cross-origin requests only for Web Services.
217
        // This allow to receive requests done by Web Workers or webapps in different domains.
218
        header('Access-Control-Allow-Origin: *');
219
    }
220
 
221
    /**
222
     * Internal implementation - recursive function producing XML markup.
223
     *
224
     * @param mixed $returns the returned values
225
     * @param external_description $desc
226
     * @return string
227
     */
228
    protected static function xmlize_result($returns, $desc) {
229
        if ($desc === null) {
230
            return '';
231
 
232
        } else if ($desc instanceof external_value) {
233
            if (is_bool($returns)) {
234
                // we want 1/0 instead of true/false here
235
                $returns = (int)$returns;
236
            }
237
            if (is_null($returns)) {
238
                return '<VALUE null="null"/>'."\n";
239
            } else {
240
                return '<VALUE>'.htmlspecialchars($returns, ENT_COMPAT, 'UTF-8').'</VALUE>'."\n";
241
            }
242
 
243
        } else if ($desc instanceof external_multiple_structure) {
244
            $mult = '<MULTIPLE>'."\n";
245
            if (!empty($returns)) {
246
                foreach ($returns as $val) {
247
                    $mult .= self::xmlize_result($val, $desc->content);
248
                }
249
            }
250
            $mult .= '</MULTIPLE>'."\n";
251
            return $mult;
252
 
253
        } else if ($desc instanceof external_single_structure) {
254
            $single = '<SINGLE>'."\n";
255
            foreach ($desc->keys as $key=>$subdesc) {
256
                $value = isset($returns[$key]) ? $returns[$key] : null;
257
                $single .= '<KEY name="'.$key.'">'.self::xmlize_result($value, $subdesc).'</KEY>'."\n";
258
            }
259
            $single .= '</SINGLE>'."\n";
260
            return $single;
261
        }
262
    }
263
}
264
 
265
 
266
/**
267
 * REST test client class
268
 *
269
 * @package    webservice_rest
270
 * @copyright  2009 Petr Skoda (skodak)
271
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
272
 */
273
class webservice_rest_test_client implements webservice_test_client_interface {
274
    /**
275
     * Execute test client WS request
276
     * @param string $serverurl server url (including token parameter or username/password parameters)
277
     * @param string $function function name
278
     * @param array $params parameters of the called function
279
     * @return mixed
280
     */
281
    public function simpletest($serverurl, $function, $params) {
282
        return download_file_content($serverurl.'&wsfunction='.$function, null, $params);
283
    }
284
}