1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Form
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 use Joomla\Registry\Registry;
13 use Joomla\String\StringHelper;
14 use Joomla\Uri\UriHelper;
15
16 /**
17 * Form Rule class for the Joomla Platform.
18 *
19 * @since 11.1
20 */
21 class JFormRuleUrl extends JFormRule
22 {
23 /**
24 * Method to test an external or internal url for all valid parts.
25 *
26 * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object.
27 * @param mixed $value The form field value to validate.
28 * @param string $group The field name group control value. This acts as an array container for the field.
29 * For example if the field has name="foo" and the group value is set to "bar" then the
30 * full field name would end up being "bar[foo]".
31 * @param Registry $input An optional Registry object with the entire data set to validate against the entire form.
32 * @param JForm $form The form object for which the field is being tested.
33 *
34 * @return boolean True if the value is valid, false otherwise.
35 *
36 * @since 11.1
37 * @link http://www.w3.org/Addressing/URL/url-spec.txt
38 * @see JString
39 */
40 public function test(SimpleXMLElement $element, $value, $group = null, Registry $input = null, JForm $form = null)
41 {
42 // If the field is empty and not required, the field is valid.
43 $required = ((string) $element['required'] == 'true' || (string) $element['required'] == 'required');
44
45 if (!$required && empty($value))
46 {
47 return true;
48 }
49
50 $urlParts = UriHelper::parse_url($value);
51
52 // See http://www.w3.org/Addressing/URL/url-spec.txt
53 // Use the full list or optionally specify a list of permitted schemes.
54 if ($element['schemes'] == '')
55 {
56 $scheme = array('http', 'https', 'ftp', 'ftps', 'gopher', 'mailto', 'news', 'prospero', 'telnet', 'rlogin', 'sftp', 'tn3270', 'wais', 'url',
57 'mid', 'cid', 'nntp', 'tel', 'urn', 'ldap', 'file', 'fax', 'modem', 'git');
58 }
59 else
60 {
61 $scheme = explode(',', $element['schemes']);
62 }
63
64 /*
65 * Note that parse_url() does not always parse accurately without a scheme,
66 * but at least the path should be set always. Note also that parse_url()
67 * returns False for seriously malformed URLs instead of an associative array.
68 * @link https://secure.php.net/manual/en/function.parse-url.php
69 */
70 if ($urlParts === false or !array_key_exists('scheme', $urlParts))
71 {
72 /*
73 * The function parse_url() returned false (seriously malformed URL) or no scheme
74 * was found and the relative option is not set: in both cases the field is not valid.
75 */
76 if ($urlParts === false or !$element['relative'])
77 {
78 $element->addAttribute('message', JText::sprintf('JLIB_FORM_VALIDATE_FIELD_URL_SCHEMA_MISSING', $value, implode(', ', $scheme)));
79
80 return false;
81 }
82 // The best we can do for the rest is make sure that the path exists and is valid UTF-8.
83 if (!array_key_exists('path', $urlParts) || !StringHelper::valid((string) $urlParts['path']))
84 {
85 return false;
86 }
87 // The internal URL seems to be good.
88 return true;
89 }
90
91 // Scheme found, check all parts found.
92 $urlScheme = (string) $urlParts['scheme'];
93 $urlScheme = strtolower($urlScheme);
94
95 if (in_array($urlScheme, $scheme) == false)
96 {
97 return false;
98 }
99
100 // For some schemes here must be two slashes.
101 $scheme = array('http', 'https', 'ftp', 'ftps', 'gopher', 'wais', 'prospero', 'sftp', 'telnet', 'git');
102
103 if (in_array($urlScheme, $scheme) && substr($value, strlen($urlScheme), 3) !== '://')
104 {
105 return false;
106 }
107
108 // The best we can do for the rest is make sure that the strings are valid UTF-8
109 // and the port is an integer.
110 if (array_key_exists('host', $urlParts) && !StringHelper::valid((string) $urlParts['host']))
111 {
112 return false;
113 }
114
115 if (array_key_exists('port', $urlParts) && !is_int((int) $urlParts['port']))
116 {
117 return false;
118 }
119
120 if (array_key_exists('path', $urlParts) && !StringHelper::valid((string) $urlParts['path']))
121 {
122 return false;
123 }
124
125 return true;
126 }
127 }
128