public function test_is_valid_plugin_name()
 {
     $this->assertTrue(is_valid_plugin_name('forum'));
     $this->assertTrue(is_valid_plugin_name('forum2'));
     $this->assertTrue(is_valid_plugin_name('feedback360'));
     $this->assertTrue(is_valid_plugin_name('online_users'));
     $this->assertTrue(is_valid_plugin_name('blond_online_users'));
     $this->assertFalse(is_valid_plugin_name('online__users'));
     $this->assertFalse(is_valid_plugin_name('forum '));
     $this->assertFalse(is_valid_plugin_name('forum.old'));
     $this->assertFalse(is_valid_plugin_name('xx-yy'));
     $this->assertFalse(is_valid_plugin_name('2xx'));
     $this->assertFalse(is_valid_plugin_name('Xx'));
     $this->assertFalse(is_valid_plugin_name('_xx'));
     $this->assertFalse(is_valid_plugin_name('xx_'));
 }
Exemple #2
0
 /**
  * Returns an array of plugins without using core methods.
  *
  * This function explicitly does NOT use core functions as it will in some circumstances be called before Moodle has
  * finished initialising. This happens when loading configuration for instance.
  *
  * @return string
  */
 public static function early_get_cache_plugins()
 {
     global $CFG;
     $result = array();
     $ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests');
     $fulldir = $CFG->dirroot . '/cache/stores';
     $items = new DirectoryIterator($fulldir);
     foreach ($items as $item) {
         if ($item->isDot() or !$item->isDir()) {
             continue;
         }
         $pluginname = $item->getFilename();
         if (in_array($pluginname, $ignored)) {
             continue;
         }
         if (!is_valid_plugin_name($pluginname)) {
             // Better ignore plugins with problematic names here.
             continue;
         }
         $result[$pluginname] = $fulldir . '/' . $pluginname;
         unset($item);
     }
     unset($items);
     return $result;
 }
Exemple #3
0
/**
 * Used by {@link optional_param()} and {@link required_param()} to
 * clean the variables and/or cast to specific types, based on
 * an options field.
 * <code>
 * $course->format = clean_param($course->format, PARAM_ALPHA);
 * $selectedgradeitem = clean_param($selectedgradeitem, PARAM_INT);
 * </code>
 *
 * @param mixed $param the variable we are cleaning
 * @param string $type expected format of param after cleaning.
 * @return mixed
 * @throws coding_exception
 */
function clean_param($param, $type)
{
    global $CFG;
    if (is_array($param)) {
        throw new coding_exception('clean_param() can not process arrays, please use clean_param_array() instead.');
    } else {
        if (is_object($param)) {
            if (method_exists($param, '__toString')) {
                $param = $param->__toString();
            } else {
                throw new coding_exception('clean_param() can not process objects, please use clean_param_array() instead.');
            }
        }
    }
    switch ($type) {
        case PARAM_RAW:
            // No cleaning at all.
            $param = fix_utf8($param);
            return $param;
        case PARAM_RAW_TRIMMED:
            // No cleaning, but strip leading and trailing whitespace.
            $param = fix_utf8($param);
            return trim($param);
        case PARAM_CLEAN:
            // General HTML cleaning, try to use more specific type if possible this is deprecated!
            // Please use more specific type instead.
            if (is_numeric($param)) {
                return $param;
            }
            $param = fix_utf8($param);
            // Sweep for scripts, etc.
            return clean_text($param);
        case PARAM_CLEANHTML:
            // Clean html fragment.
            $param = fix_utf8($param);
            // Sweep for scripts, etc.
            $param = clean_text($param, FORMAT_HTML);
            return trim($param);
        case PARAM_INT:
            // Convert to integer.
            return (int) $param;
        case PARAM_FLOAT:
            // Convert to float.
            return (double) $param;
        case PARAM_ALPHA:
            // Remove everything not `a-z`.
            return preg_replace('/[^a-zA-Z]/i', '', $param);
        case PARAM_ALPHAEXT:
            // Remove everything not `a-zA-Z_-` (originally allowed "/" too).
            return preg_replace('/[^a-zA-Z_-]/i', '', $param);
        case PARAM_ALPHANUM:
            // Remove everything not `a-zA-Z0-9`.
            return preg_replace('/[^A-Za-z0-9]/i', '', $param);
        case PARAM_ALPHANUMEXT:
            // Remove everything not `a-zA-Z0-9_-`.
            return preg_replace('/[^A-Za-z0-9_-]/i', '', $param);
        case PARAM_SEQUENCE:
            // Remove everything not `0-9,`.
            return preg_replace('/[^0-9,]/i', '', $param);
        case PARAM_BOOL:
            // Convert to 1 or 0.
            $tempstr = strtolower($param);
            if ($tempstr === 'on' or $tempstr === 'yes' or $tempstr === 'true') {
                $param = 1;
            } else {
                if ($tempstr === 'off' or $tempstr === 'no' or $tempstr === 'false') {
                    $param = 0;
                } else {
                    $param = empty($param) ? 0 : 1;
                }
            }
            return $param;
        case PARAM_NOTAGS:
            // Strip all tags.
            $param = fix_utf8($param);
            return strip_tags($param);
        case PARAM_TEXT:
            // Leave only tags needed for multilang.
            $param = fix_utf8($param);
            // If the multilang syntax is not correct we strip all tags because it would break xhtml strict which is required
            // for accessibility standards please note this cleaning does not strip unbalanced '>' for BC compatibility reasons.
            do {
                if (strpos($param, '</lang>') !== false) {
                    // Old and future mutilang syntax.
                    $param = strip_tags($param, '<lang>');
                    if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                        break;
                    }
                    $open = false;
                    foreach ($matches[0] as $match) {
                        if ($match === '</lang>') {
                            if ($open) {
                                $open = false;
                                continue;
                            } else {
                                break 2;
                            }
                        }
                        if (!preg_match('/^<lang lang="[a-zA-Z0-9_-]+"\\s*>$/u', $match)) {
                            break 2;
                        } else {
                            $open = true;
                        }
                    }
                    if ($open) {
                        break;
                    }
                    return $param;
                } else {
                    if (strpos($param, '</span>') !== false) {
                        // Current problematic multilang syntax.
                        $param = strip_tags($param, '<span>');
                        if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                            break;
                        }
                        $open = false;
                        foreach ($matches[0] as $match) {
                            if ($match === '</span>') {
                                if ($open) {
                                    $open = false;
                                    continue;
                                } else {
                                    break 2;
                                }
                            }
                            if (!preg_match('/^<span(\\s+lang="[a-zA-Z0-9_-]+"|\\s+class="multilang"){2}\\s*>$/u', $match)) {
                                break 2;
                            } else {
                                $open = true;
                            }
                        }
                        if ($open) {
                            break;
                        }
                        return $param;
                    }
                }
            } while (false);
            // Easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string().
            return strip_tags($param);
        case PARAM_COMPONENT:
            // We do not want any guessing here, either the name is correct or not
            // please note only normalised component names are accepted.
            if (!preg_match('/^[a-z]+(_[a-z][a-z0-9_]*)?[a-z0-9]+$/', $param)) {
                return '';
            }
            if (strpos($param, '__') !== false) {
                return '';
            }
            if (strpos($param, 'mod_') === 0) {
                // Module names must not contain underscores because we need to differentiate them from invalid plugin types.
                if (substr_count($param, '_') != 1) {
                    return '';
                }
            }
            return $param;
        case PARAM_PLUGIN:
        case PARAM_AREA:
            // We do not want any guessing here, either the name is correct or not.
            if (!is_valid_plugin_name($param)) {
                return '';
            }
            return $param;
        case PARAM_SAFEDIR:
            // Remove everything not a-zA-Z0-9_- .
            return preg_replace('/[^a-zA-Z0-9_-]/i', '', $param);
        case PARAM_SAFEPATH:
            // Remove everything not a-zA-Z0-9/_- .
            return preg_replace('/[^a-zA-Z0-9\\/_-]/i', '', $param);
        case PARAM_FILE:
            // Strip all suspicious characters from filename.
            $param = fix_utf8($param);
            $param = preg_replace('~[[:cntrl:]]|[&<>"`\\|\':\\\\/]~u', '', $param);
            if ($param === '.' || $param === '..') {
                $param = '';
            }
            return $param;
        case PARAM_PATH:
            // Strip all suspicious characters from file path.
            $param = fix_utf8($param);
            $param = str_replace('\\', '/', $param);
            // Explode the path and clean each element using the PARAM_FILE rules.
            $breadcrumb = explode('/', $param);
            foreach ($breadcrumb as $key => $crumb) {
                if ($crumb === '.' && $key === 0) {
                    // Special condition to allow for relative current path such as ./currentdirfile.txt.
                } else {
                    $crumb = clean_param($crumb, PARAM_FILE);
                }
                $breadcrumb[$key] = $crumb;
            }
            $param = implode('/', $breadcrumb);
            // Remove multiple current path (./././) and multiple slashes (///).
            $param = preg_replace('~//+~', '/', $param);
            $param = preg_replace('~/(\\./)+~', '/', $param);
            return $param;
        case PARAM_HOST:
            // Allow FQDN or IPv4 dotted quad.
            $param = preg_replace('/[^\\.\\d\\w-]/', '', $param);
            // Match ipv4 dotted quad.
            if (preg_match('/(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/', $param, $match)) {
                // Confirm values are ok.
                if ($match[0] > 255 || $match[1] > 255 || $match[3] > 255 || $match[4] > 255) {
                    // Hmmm, what kind of dotted quad is this?
                    $param = '';
                }
            } else {
                if (preg_match('/^[\\w\\d\\.-]+$/', $param) && !preg_match('/^[\\.-]/', $param) && !preg_match('/[\\.-]$/', $param)) {
                    // All is ok - $param is respected.
                } else {
                    // All is not ok...
                    $param = '';
                }
            }
            return $param;
        case PARAM_URL:
            // Allow safe ftp, http, mailto urls.
            $param = fix_utf8($param);
            include_once $CFG->dirroot . '/lib/validateurlsyntax.php';
            if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) {
                // All is ok, param is respected.
            } else {
                // Not really ok.
                $param = '';
            }
            return $param;
        case PARAM_LOCALURL:
            // Allow http absolute, root relative and relative URLs within wwwroot.
            $param = clean_param($param, PARAM_URL);
            if (!empty($param)) {
                // Simulate the HTTPS version of the site.
                $httpswwwroot = str_replace('http://', 'https://', $CFG->wwwroot);
                if ($param === $CFG->wwwroot) {
                    // Exact match;
                } else {
                    if (!empty($CFG->loginhttps) && $param === $httpswwwroot) {
                        // Exact match;
                    } else {
                        if (preg_match(':^/:', $param)) {
                            // Root-relative, ok!
                        } else {
                            if (preg_match('/^' . preg_quote($CFG->wwwroot . '/', '/') . '/i', $param)) {
                                // Absolute, and matches our wwwroot.
                            } else {
                                if (!empty($CFG->loginhttps) && preg_match('/^' . preg_quote($httpswwwroot . '/', '/') . '/i', $param)) {
                                    // Absolute, and matches our httpswwwroot.
                                } else {
                                    // Relative - let's make sure there are no tricks.
                                    if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?')) {
                                        // Looks ok.
                                    } else {
                                        $param = '';
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return $param;
        case PARAM_PEM:
            $param = trim($param);
            // PEM formatted strings may contain letters/numbers and the symbols:
            //   forward slash: /
            //   plus sign:     +
            //   equal sign:    =
            //   , surrounded by BEGIN and END CERTIFICATE prefix and suffixes.
            if (preg_match('/^-----BEGIN CERTIFICATE-----([\\s\\w\\/\\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
                list($wholething, $body) = $matches;
                unset($wholething, $matches);
                $b64 = clean_param($body, PARAM_BASE64);
                if (!empty($b64)) {
                    return "-----BEGIN CERTIFICATE-----\n{$b64}\n-----END CERTIFICATE-----\n";
                } else {
                    return '';
                }
            }
            return '';
        case PARAM_BASE64:
            if (!empty($param)) {
                // PEM formatted strings may contain letters/numbers and the symbols
                //   forward slash: /
                //   plus sign:     +
                //   equal sign:    =.
                if (0 >= preg_match('/^([\\s\\w\\/\\+=]+)$/', trim($param))) {
                    return '';
                }
                $lines = preg_split('/[\\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
                // Each line of base64 encoded data must be 64 characters in length, except for the last line which may be less
                // than (or equal to) 64 characters long.
                for ($i = 0, $j = count($lines); $i < $j; $i++) {
                    if ($i + 1 == $j) {
                        if (64 < strlen($lines[$i])) {
                            return '';
                        }
                        continue;
                    }
                    if (64 != strlen($lines[$i])) {
                        return '';
                    }
                }
                return implode("\n", $lines);
            } else {
                return '';
            }
        case PARAM_TAG:
            $param = fix_utf8($param);
            // Please note it is not safe to use the tag name directly anywhere,
            // it must be processed with s(), urlencode() before embedding anywhere.
            // Remove some nasties.
            $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
            // Convert many whitespace chars into one.
            $param = preg_replace('/\\s+/u', ' ', $param);
            $param = core_text::substr(trim($param), 0, TAG_MAX_LENGTH);
            return $param;
        case PARAM_TAGLIST:
            $param = fix_utf8($param);
            $tags = explode(',', $param);
            $result = array();
            foreach ($tags as $tag) {
                $res = clean_param($tag, PARAM_TAG);
                if ($res !== '') {
                    $result[] = $res;
                }
            }
            if ($result) {
                return implode(',', $result);
            } else {
                return '';
            }
        case PARAM_CAPABILITY:
            if (get_capability_info($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_PERMISSION:
            $param = (int) $param;
            if (in_array($param, array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT))) {
                return $param;
            } else {
                return CAP_INHERIT;
            }
        case PARAM_AUTH:
            $param = clean_param($param, PARAM_PLUGIN);
            if (empty($param)) {
                return '';
            } else {
                if (exists_auth_plugin($param)) {
                    return $param;
                } else {
                    return '';
                }
            }
        case PARAM_LANG:
            $param = clean_param($param, PARAM_SAFEDIR);
            if (get_string_manager()->translation_exists($param)) {
                return $param;
            } else {
                // Specified language is not installed or param malformed.
                return '';
            }
        case PARAM_THEME:
            $param = clean_param($param, PARAM_PLUGIN);
            if (empty($param)) {
                return '';
            } else {
                if (file_exists("{$CFG->dirroot}/theme/{$param}/config.php")) {
                    return $param;
                } else {
                    if (!empty($CFG->themedir) and file_exists("{$CFG->themedir}/{$param}/config.php")) {
                        return $param;
                    } else {
                        // Specified theme is not installed.
                        return '';
                    }
                }
            }
        case PARAM_USERNAME:
            $param = fix_utf8($param);
            $param = trim($param);
            // Convert uppercase to lowercase MDL-16919.
            $param = core_text::strtolower($param);
            if (empty($CFG->extendedusernamechars)) {
                $param = str_replace(" ", "", $param);
                // Regular expression, eliminate all chars EXCEPT:
                // alphanum, dash (-), underscore (_), at sign (@) and period (.) characters.
                $param = preg_replace('/[^-\\.@_a-z0-9]/', '', $param);
            }
            return $param;
        case PARAM_EMAIL:
            $param = fix_utf8($param);
            if (validate_email($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_STRINGID:
            if (preg_match('|^[a-zA-Z][a-zA-Z0-9\\.:/_-]*$|', $param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_TIMEZONE:
            // Can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'.
            $param = fix_utf8($param);
            $timezonepattern = '/^(([+-]?(0?[0-9](\\.[5|0])?|1[0-3](\\.0)?|1[0-2]\\.5))|(99)|[[:alnum:]]+(\\/?[[:alpha:]_-])+)$/';
            if (preg_match($timezonepattern, $param)) {
                return $param;
            } else {
                return '';
            }
        default:
            // Doh! throw error, switched parameters in optional_param or another serious problem.
            print_error("unknownparamtype", '', '', $type);
    }
}
/**
 * Simplified version of get_list_of_plugins()
 * @param string $plugintype type of plugin
 * @return array name=>fulllocation pairs of plugins of given type
 */
function get_plugin_list($plugintype)
{
    global $CFG;
    // We use the dirroot as an identifier here because if it has changed the whole cache
    // can be considered invalid.
    $cache = cache::make('core', 'pluginlist', array('dirroot' => $CFG->dirroot));
    $cached = $cache->get($plugintype);
    if ($cached !== false) {
        return $cached;
    }
    $ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests');
    if ($plugintype == 'auth') {
        // Historically we have had an auth plugin called 'db', so allow a special case.
        $key = array_search('db', $ignored);
        if ($key !== false) {
            unset($ignored[$key]);
        }
    }
    if ($plugintype === '') {
        $plugintype = 'mod';
    }
    $fulldirs = array();
    if ($plugintype === 'mod') {
        // mod is an exception because we have to call this function from get_plugin_types()
        $fulldirs[] = $CFG->dirroot . '/mod';
    } else {
        if ($plugintype === 'editor') {
            // Exception also needed for editor for same reason.
            $fulldirs[] = $CFG->dirroot . '/lib/editor';
        } else {
            if ($plugintype === 'theme') {
                $fulldirs[] = $CFG->dirroot . '/theme';
                // themes are special because they may be stored also in separate directory
                if (!empty($CFG->themedir) and file_exists($CFG->themedir) and is_dir($CFG->themedir)) {
                    $fulldirs[] = $CFG->themedir;
                }
            } else {
                $types = get_plugin_types(true);
                if (!array_key_exists($plugintype, $types)) {
                    $cache->set($plugintype, array());
                    return array();
                }
                $fulldir = $types[$plugintype];
                if (!file_exists($fulldir)) {
                    $cache->set($plugintype, array());
                    return array();
                }
                $fulldirs[] = $fulldir;
            }
        }
    }
    $result = array();
    foreach ($fulldirs as $fulldir) {
        if (!is_dir($fulldir)) {
            continue;
        }
        $items = new DirectoryIterator($fulldir);
        foreach ($items as $item) {
            if ($item->isDot() or !$item->isDir()) {
                continue;
            }
            $pluginname = $item->getFilename();
            if (in_array($pluginname, $ignored)) {
                continue;
            }
            if (!is_valid_plugin_name($pluginname)) {
                // Better ignore plugins with problematic names here.
                continue;
            }
            $result[$pluginname] = $fulldir . '/' . $pluginname;
            unset($item);
        }
        unset($items);
    }
    //TODO: implement better sorting once we migrated all plugin names to 'pluginname', ksort does not work for unicode, that is why we have to sort by the dir name, not the strings!
    ksort($result);
    $cache->set($plugintype, $result);
    return $result;
}