Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
// This file is part of Moodle - http://moodle.org/
4
//
5
// Moodle is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// Moodle is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
/**
19
 * Private url module utility functions
20
 *
21
 * @package    mod_url
22
 * @copyright  2009 Petr Skoda  {@link http://skodak.org}
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
defined('MOODLE_INTERNAL') || die;
27
 
28
require_once("$CFG->libdir/filelib.php");
29
require_once("$CFG->libdir/resourcelib.php");
30
require_once("$CFG->dirroot/mod/url/lib.php");
31
 
32
/**
33
 * This methods does weak url validation, we are looking for major problems only,
34
 * no strict RFE validation.
35
 *
36
 * @param $url
37
 * @return bool true is seems valid, false if definitely not valid URL
38
 */
39
function url_appears_valid_url($url) {
40
    if (preg_match('/^(\/|https?:|ftp:)/i', $url)) {
41
        // note: this is not exact validation, we look for severely malformed URLs only
42
        return (bool) preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[^ @]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
43
    } else {
44
        return (bool)preg_match('/^[a-z]+:\/\/...*$/i', $url);
45
    }
46
}
47
 
48
/**
49
 * Fix common URL problems that we want teachers to see fixed
50
 * the next time they edit the resource.
51
 *
52
 * This function does not include any XSS protection.
53
 *
54
 * @param string $url
55
 * @return string
56
 */
57
function url_fix_submitted_url($url) {
58
    // note: empty urls are prevented in form validation
59
    $url = trim($url);
60
 
61
    // remove encoded entities - we want the raw URI here
62
    $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
63
 
64
    if (!preg_match('|^[a-z]+:|i', $url) and !preg_match('|^/|', $url)) {
65
        // invalid URI, try to fix it by making it normal URL,
66
        // please note relative urls are not allowed, /xx/yy links are ok
67
        $url = 'http://'.$url;
68
    }
69
 
70
    return $url;
71
}
72
 
73
/**
74
 * Return full url with all extra parameters
75
 *
76
 * This function does not include any XSS protection.
77
 *
78
 * @param stdClass $url
79
 * @param object $cm
80
 * @param object $course
81
 * @param object $config
82
 * @return string url with & encoded as &amp;
83
 */
84
function url_get_full_url($url, $cm, $course, $config=null) {
85
 
86
    $parameters = empty($url->parameters) ? [] : (array) unserialize_array($url->parameters);
87
 
88
    // make sure there are no encoded entities, it is ok to do this twice
89
    $fullurl = html_entity_decode($url->externalurl, ENT_QUOTES, 'UTF-8');
90
 
91
    $letters = '\pL';
92
    $latin = 'a-zA-Z';
93
    $digits = '0-9';
94
    $symbols = '\x{20E3}\x{00AE}\x{00A9}\x{203C}\x{2047}\x{2048}\x{2049}\x{3030}\x{303D}\x{2139}\x{2122}\x{3297}\x{3299}' .
95
               '\x{2300}-\x{23FF}\x{2600}-\x{27BF}\x{2B00}-\x{2BF0}';
96
    $arabic = '\x{FE00}-\x{FEFF}';
97
    $math = '\x{2190}-\x{21FF}\x{2900}-\x{297F}';
98
    $othernumbers = '\x{2460}-\x{24FF}';
99
    $geometric = '\x{25A0}-\x{25FF}';
100
    $emojis = '\x{1F000}-\x{1F6FF}';
101
 
102
    if (preg_match('/^(\/|https?:|ftp:)/i', $fullurl) or preg_match('|^/|', $fullurl)) {
103
        // encode extra chars in URLs - this does not make it always valid, but it helps with some UTF-8 problems
104
        // Thanks to 💩.la emojis count as valid, too.
105
        $allowed = "[" . $letters . $latin . $digits . $symbols . $arabic . $math . $othernumbers . $geometric .
106
            $emojis . "]" . preg_quote(';/?:@=&$_.+!*(),-#%', '/');
107
        $fullurl = preg_replace_callback("/[^$allowed]/u", 'url_filter_callback', $fullurl);
108
    } else {
109
        // encode special chars only
110
        $fullurl = str_replace('"', '%22', $fullurl);
111
        $fullurl = str_replace('\'', '%27', $fullurl);
112
        $fullurl = str_replace(' ', '%20', $fullurl);
113
        $fullurl = str_replace('<', '%3C', $fullurl);
114
        $fullurl = str_replace('>', '%3E', $fullurl);
115
    }
116
 
117
    if (!$config) {
118
        $config = get_config('url');
119
    }
120
 
121
    // add variable url parameters
122
    if ($config->allowvariables && !empty($parameters)) {
123
        $paramvalues = url_get_variable_values($url, $cm, $course, $config);
124
 
125
        foreach ($parameters as $parse=>$parameter) {
126
            if (isset($paramvalues[$parameter])) {
127
                $parameters[$parse] = rawurlencode($parse).'='.rawurlencode($paramvalues[$parameter]);
128
            } else {
129
                unset($parameters[$parse]);
130
            }
131
        }
132
 
133
        if (!empty($parameters)) {
134
            if (stripos($fullurl, 'teamspeak://') === 0) {
135
                $fullurl = $fullurl.'?'.implode('?', $parameters);
136
            } else {
137
                $join = (strpos($fullurl, '?') === false) ? '?' : '&';
138
                $fullurl = $fullurl.$join.implode('&', $parameters);
139
            }
140
        }
141
    }
142
 
143
    // encode all & to &amp; entity
144
    $fullurl = str_replace('&', '&amp;', $fullurl);
145
 
146
    return $fullurl;
147
}
148
 
149
/**
150
 * Unicode encoding helper callback
151
 * @internal
152
 * @param array $matches
153
 * @return string
154
 */
155
function url_filter_callback($matches) {
156
    return rawurlencode($matches[0]);
157
}
158
 
159
/**
160
 * Print url header.
161
 * @param object $url
162
 * @param object $cm
163
 * @param object $course
164
 * @return void
165
 */
166
function url_print_header($url, $cm, $course) {
167
    global $PAGE, $OUTPUT;
168
 
169
    $PAGE->set_title($course->shortname.': '.$url->name);
170
    $PAGE->set_heading($course->fullname);
171
    $PAGE->set_activity_record($url);
172
    echo $OUTPUT->header();
173
}
174
 
175
/**
176
 * Get url introduction.
177
 *
178
 * @param object $url
179
 * @param object $cm
180
 * @param bool $ignoresettings print even if not specified in modedit
181
 * @return string
182
 */
183
function url_get_intro(object $url, object $cm, bool $ignoresettings = false): string {
184
    $options = empty($url->displayoptions) ? [] : (array) unserialize_array($url->displayoptions);
185
    if ($ignoresettings or !empty($options['printintro'])) {
186
        if (trim(strip_tags($url->intro))) {
187
            return format_module_intro('url', $url, $cm->id);
188
        }
189
    }
190
 
191
    return '';
192
}
193
 
194
/**
195
 * Display url frames.
196
 * @param object $url
197
 * @param object $cm
198
 * @param object $course
199
 * @return does not return
200
 */
201
function url_display_frame($url, $cm, $course) {
202
    global $PAGE, $OUTPUT, $CFG;
203
 
204
    $frame = optional_param('frameset', 'main', PARAM_ALPHA);
205
 
206
    if ($frame === 'top') {
207
        $PAGE->set_pagelayout('frametop');
208
        $PAGE->activityheader->set_attrs([
209
            'description' => url_get_intro($url, $cm),
210
            'title' => format_string($url->name)
211
        ]);
212
        url_print_header($url, $cm, $course);
213
        echo $OUTPUT->footer();
214
        die;
215
 
216
    } else {
217
        $config = get_config('url');
218
        $context = context_module::instance($cm->id);
219
        $exteurl = url_get_full_url($url, $cm, $course, $config);
220
        $navurl = "$CFG->wwwroot/mod/url/view.php?id=$cm->id&amp;frameset=top";
221
        $coursecontext = context_course::instance($course->id);
222
        $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
223
        $title = strip_tags($courseshortname.': '.format_string($url->name));
224
        $framesize = $config->framesize;
225
        $modulename = s(get_string('modulename','url'));
226
        $contentframetitle = s(format_string($url->name));
227
        $dir = get_string('thisdirection', 'langconfig');
228
 
229
        $extframe = <<<EOF
230
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
231
<html dir="$dir">
232
  <head>
233
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
234
    <title>$title</title>
235
  </head>
236
  <frameset rows="$framesize,*">
237
    <frame src="$navurl" title="$modulename"/>
238
    <frame src="$exteurl" title="$contentframetitle"/>
239
  </frameset>
240
</html>
241
EOF;
242
 
243
        @header('Content-Type: text/html; charset=utf-8');
244
        echo $extframe;
245
        die;
246
    }
247
}
248
 
249
/**
250
 * Print url info and link.
251
 * @param object $url
252
 * @param object $cm
253
 * @param object $course
254
 */
255
function url_print_workaround($url, $cm, $course) {
256
    global $OUTPUT, $PAGE, $USER;
257
 
258
    $PAGE->activityheader->set_description(url_get_intro($url, $cm, true));
259
    url_print_header($url, $cm, $course);
260
 
261
    $fullurl = new moodle_url(url_get_full_url($url, $cm, $course));
262
 
263
    $display = url_get_final_display_type($url);
264
    if ($display == RESOURCELIB_DISPLAY_POPUP) {
265
        $jsfullurl = addslashes_js($fullurl->out(false));
266
        $options = empty($url->displayoptions) ? [] : (array) unserialize_array($url->displayoptions);
267
        $width  = empty($options['popupwidth'])  ? 620 : $options['popupwidth'];
268
        $height = empty($options['popupheight']) ? 450 : $options['popupheight'];
269
        $wh = "width=$width,height=$height,toolbar=no,location=no,menubar=no,copyhistory=no,status=no,directories=no,scrollbars=yes,resizable=yes";
270
        $attributes = ['onclick' => "window.open('$jsfullurl', '', '$wh'); return false;"];
271
 
272
    } else if ($display == RESOURCELIB_DISPLAY_NEW) {
273
        $attributes = ['onclick' => "this.target='_blank';"];
274
 
275
    } else {
276
        $attributes = [];
277
    }
278
 
279
    echo '<div class="urlworkaround">';
280
    print_string('clicktoopen', 'url', html_writer::link($fullurl, format_string($cm->name), $attributes));
281
    echo '</div>';
282
 
283
    echo $OUTPUT->footer();
284
    die;
285
}
286
 
287
/**
288
 * Display embedded url file.
289
 * @param object $url
290
 * @param object $cm
291
 * @param object $course
292
 */
293
function url_display_embed($url, $cm, $course) {
294
    global $PAGE, $OUTPUT;
295
 
296
    $mimetype = resourcelib_guess_url_mimetype($url->externalurl);
297
    $fullurl  = url_get_full_url($url, $cm, $course);
298
    $title    = $url->name;
299
 
300
    $moodleurl = new moodle_url($fullurl);
301
    $link = html_writer::link($moodleurl, format_string($cm->name));
302
    $clicktoopen = get_string('clicktoopen', 'url', $link);
303
 
304
    $extension = resourcelib_get_extension($url->externalurl);
305
 
306
    $mediamanager = core_media_manager::instance($PAGE);
307
    $embedoptions = array(
308
        core_media_manager::OPTION_TRUSTED => true,
309
        core_media_manager::OPTION_BLOCK => true
310
    );
311
 
312
    if (in_array($mimetype, array('image/gif','image/jpeg','image/png'))) {  // It's an image
313
        $code = resourcelib_embed_image($fullurl, $title);
314
 
315
    } else if ($mediamanager->can_embed_url($moodleurl, $embedoptions)) {
316
        // Media (audio/video) file.
317
        $code = $mediamanager->embed_url($moodleurl, $title, 0, 0, $embedoptions);
318
 
319
    } else {
320
        // anything else - just try object tag enlarged as much as possible
321
        $code = resourcelib_embed_general($fullurl, $title, $clicktoopen, $mimetype);
322
    }
323
 
324
    $PAGE->activityheader->set_description(url_get_intro($url, $cm));
325
    url_print_header($url, $cm, $course);
326
 
327
    echo $code;
328
 
329
    echo $OUTPUT->footer();
330
    die;
331
}
332
 
333
/**
334
 * Decide the best display format.
335
 * @param object $url
336
 * @return int display type constant
337
 */
338
function url_get_final_display_type($url) {
339
    global $CFG;
340
 
341
    if ($url->display != RESOURCELIB_DISPLAY_AUTO) {
342
        return $url->display;
343
    }
344
 
345
    // detect links to local moodle pages
346
    if (strpos($url->externalurl, $CFG->wwwroot) === 0) {
347
        if (strpos($url->externalurl, 'file.php') === false and strpos($url->externalurl, '.php') !== false ) {
348
            // most probably our moodle page with navigation
349
            return RESOURCELIB_DISPLAY_OPEN;
350
        }
351
    }
352
 
353
    // Binaries and other formats that are known to cause trouble for external links.
354
    static $download = ['application/zip', 'application/x-tar', 'application/g-zip',
355
                        'application/pdf', 'text/html', 'document/unknown'];
356
    static $embed    = array('image/gif', 'image/jpeg', 'image/png', 'image/svg+xml',         // images
357
                             'application/x-shockwave-flash', 'video/x-flv', 'video/x-ms-wm', // video formats
358
                             'video/quicktime', 'video/mpeg', 'video/mp4',
359
                             'audio/mp3', 'audio/x-realaudio-plugin', 'x-realaudio-plugin',   // audio formats,
360
                            );
361
 
362
    $mimetype = resourcelib_guess_url_mimetype($url->externalurl);
363
 
364
    if (in_array($mimetype, $download)) {
365
        return RESOURCELIB_DISPLAY_DOWNLOAD;
366
    }
367
    if (in_array($mimetype, $embed)) {
368
        return RESOURCELIB_DISPLAY_EMBED;
369
    }
370
 
371
    // let the browser deal with it somehow
372
    return RESOURCELIB_DISPLAY_OPEN;
373
}
374
 
375
/**
376
 * Get the parameters that may be appended to URL
377
 * @param object $config url module config options
378
 * @return array array describing opt groups
379
 */
380
function url_get_variable_options($config) {
381
    global $CFG;
382
 
383
    $options = array();
384
    $options[''] = array('' => get_string('chooseavariable', 'url'));
385
 
386
    $options[get_string('course')] = array(
387
        'courseid'        => 'id',
388
        'coursefullname'  => get_string('fullnamecourse'),
389
        'courseshortname' => get_string('shortnamecourse'),
390
        'courseidnumber'  => get_string('idnumbercourse'),
391
        'coursesummary'   => get_string('summary'),
392
        'courseformat'    => get_string('format'),
393
    );
394
 
395
    $options[get_string('modulename', 'url')] = array(
396
        'urlinstance'     => 'id',
397
        'urlcmid'         => 'cmid',
398
        'urlname'         => get_string('name'),
399
        'urlidnumber'     => get_string('idnumbermod'),
400
    );
401
 
402
    $options[get_string('miscellaneous')] = array(
403
        'sitename'        => get_string('fullsitename'),
404
        'serverurl'       => get_string('serverurl', 'url'),
405
        'currenttime'     => get_string('time'),
406
        'lang'            => get_string('language'),
407
    );
408
    if (!empty($config->secretphrase)) {
409
        $options[get_string('miscellaneous')]['encryptedcode'] = get_string('encryptedcode');
410
    }
411
 
412
    $options[get_string('user')] = array(
413
        'userid'          => 'id',
414
        'userusername'    => get_string('username'),
415
        'useridnumber'    => get_string('idnumber'),
416
        'userfirstname'   => get_string('firstname'),
417
        'userlastname'    => get_string('lastname'),
418
        'userfullname'    => get_string('fullnameuser'),
419
        'useremail'       => get_string('email'),
420
        'userphone1'      => get_string('phone1'),
421
        'userphone2'      => get_string('phone2'),
422
        'userinstitution' => get_string('institution'),
423
        'userdepartment'  => get_string('department'),
424
        'useraddress'     => get_string('address'),
425
        'usercity'        => get_string('city'),
426
        'usertimezone'    => get_string('timezone'),
427
    );
428
 
429
    if ($config->rolesinparams) {
430
        $roles = role_fix_names(get_all_roles());
431
        $roleoptions = array();
432
        foreach ($roles as $role) {
433
            $roleoptions['course'.$role->shortname] = get_string('yourwordforx', '', $role->localname);
434
        }
435
        $options[get_string('roles')] = $roleoptions;
436
    }
437
 
438
    return $options;
439
}
440
 
441
/**
442
 * Get the parameter values that may be appended to URL
443
 * @param object $url module instance
444
 * @param object $cm
445
 * @param object $course
446
 * @param object $config module config options
447
 * @return array of parameter values
448
 */
449
function url_get_variable_values($url, $cm, $course, $config) {
450
    global $USER, $CFG;
451
 
452
    $site = get_site();
453
 
454
    $coursecontext = context_course::instance($course->id);
455
 
456
    $values = array (
457
        'courseid'        => $course->id,
458
        'coursefullname'  => format_string($course->fullname, true, array('context' => $coursecontext)),
459
        'courseshortname' => format_string($course->shortname, true, array('context' => $coursecontext)),
460
        'courseidnumber'  => $course->idnumber,
461
        'coursesummary'   => $course->summary,
462
        'courseformat'    => $course->format,
463
        'lang'            => current_language(),
464
        'sitename'        => format_string($site->fullname, true, array('context' => $coursecontext)),
465
        'serverurl'       => $CFG->wwwroot,
466
        'currenttime'     => time(),
467
        'urlinstance'     => $url->id,
468
        'urlcmid'         => $cm->id,
469
        'urlname'         => format_string($url->name, true, array('context' => $coursecontext)),
470
        'urlidnumber'     => $cm->idnumber,
471
    );
472
 
473
    if (isloggedin()) {
474
        $values['userid']          = $USER->id;
475
        $values['userusername']    = $USER->username;
476
        $values['useridnumber']    = $USER->idnumber;
477
        $values['userfirstname']   = $USER->firstname;
478
        $values['userlastname']    = $USER->lastname;
479
        $values['userfullname']    = fullname($USER);
480
        $values['useremail']       = $USER->email;
481
        $values['userphone1']      = $USER->phone1;
482
        $values['userphone2']      = $USER->phone2;
483
        $values['userinstitution'] = $USER->institution;
484
        $values['userdepartment']  = $USER->department;
485
        $values['useraddress']     = $USER->address;
486
        $values['usercity']        = $USER->city;
487
        $now = new DateTime('now', core_date::get_user_timezone_object());
488
        $values['usertimezone']    = $now->getOffset() / 3600.0; // Value in hours for BC.
489
    }
490
 
491
    // weak imitation of Single-Sign-On, for backwards compatibility only
492
    // NOTE: login hack is not included in 2.0 any more, new contrib auth plugin
493
    //       needs to be createed if somebody needs the old functionality!
494
    if (!empty($config->secretphrase)) {
495
        $values['encryptedcode'] = url_get_encrypted_parameter($url, $config);
496
    }
497
 
498
    //hmm, this is pretty fragile and slow, why do we need it here??
499
    if ($config->rolesinparams) {
500
        $coursecontext = context_course::instance($course->id);
501
        $roles = role_fix_names(get_all_roles($coursecontext), $coursecontext, ROLENAME_ALIAS);
502
        foreach ($roles as $role) {
503
            $values['course'.$role->shortname] = $role->localname;
504
        }
505
    }
506
 
507
    return $values;
508
}
509
 
510
/**
511
 * BC internal function
512
 * @param object $url
513
 * @param object $config
514
 * @return string
515
 */
516
function url_get_encrypted_parameter($url, $config) {
517
    global $CFG;
518
 
519
    if (file_exists("$CFG->dirroot/local/externserverfile.php")) {
520
        require_once("$CFG->dirroot/local/externserverfile.php");
521
        if (function_exists('extern_server_file')) {
522
            return extern_server_file($url, $config);
523
        }
524
    }
525
    return md5(getremoteaddr().$config->secretphrase);
526
}
527
 
528
/**
529
 * Optimised mimetype detection from general URL
530
 * @param $fullurl
531
 * @param null $unused This parameter has been deprecated since 4.3 and should not be used anymore.
532
 * @return string|null mimetype or null when the filetype is not relevant.
533
 */
534
function url_guess_icon($fullurl, $unused = null) {
535
    global $CFG;
536
    require_once("$CFG->libdir/filelib.php");
537
 
538
    if ($unused !== null) {
539
        debugging('Deprecated argument passed to ' . __FUNCTION__, DEBUG_DEVELOPER);
540
    }
541
 
542
    if (substr_count($fullurl, '/') < 3 or substr($fullurl, -1) === '/') {
543
        // Most probably default directory - index.php, index.html, etc. Return null because
544
        // we want to use the default module icon instead of the HTML file icon.
545
        return null;
546
    }
547
 
548
    try {
549
        // There can be some cases where the url is invalid making parse_url() to return false.
550
        // That will make moodle_url class to throw an exception, so we need to catch the exception to prevent errors.
551
        $moodleurl = new moodle_url($fullurl);
552
        $fullurl = $moodleurl->out_omit_querystring();
553
    } catch (\moodle_exception $e) {
554
        // If an exception is thrown, means the url is invalid. No need to log exception.
555
        return null;
556
    }
557
 
558
    $icon = file_extension_icon($fullurl);
559
    $htmlicon = file_extension_icon('.htm');
560
    $unknownicon = file_extension_icon('');
561
    $phpicon = file_extension_icon('.php'); // Exception for php files.
562
 
563
    // We do not want to return those icon types, the module icon is more appropriate.
564
    if ($icon === $unknownicon || $icon === $htmlicon || $icon === $phpicon) {
565
        return null;
566
    }
567
 
568
    return $icon;
569
}