From 8eb9f9e8fd43551f65da53ae9ffaf737f4b2cb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Perras?= Date: Tue, 22 Dec 2009 23:52:06 -0500 Subject: [PATCH] Implenting IPv6 validation using filter_var(), with a regex-based fallback if the aforementioned function does not exist (PHP < 5.2). Fixes #38. --- cake/libs/validation.php | 49 +++++++++++++++++++---- cake/tests/cases/libs/validation.test.php | 40 ++++++++++++++++++ 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/cake/libs/validation.php b/cake/libs/validation.php index 19c8c4d2f..f90a3a8d3 100644 --- a/cake/libs/validation.php +++ b/cake/libs/validation.php @@ -53,7 +53,6 @@ class Validation extends Object { * @access private */ var $__pattern = array( - 'ip' => '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])', 'hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)' ); @@ -548,10 +547,15 @@ class Validation extends Object { * @return boolean Success * @access public */ - function ip($check) { + function ip($check, $type = 'IPv4') { + if (function_exists('filter_var')) { + return filter_var($check, FILTER_VALIDATE_IP); + } + $_this =& Validation::getInstance(); $_this->check = $check; - $_this->regex = '/^' . $_this->__pattern['ip'] . '$/'; + $_this->__populateIp(); + $_this->regex = '/^' . $_this->__pattern[$type] . '$/'; return $_this->_check(); } @@ -604,8 +608,8 @@ class Validation extends Object { /** * Validate a multiple select. * - * Valid Options - * + * Valid Options + * * - in => provide a list of choices that selections must be made from * - max => maximun number of non-zero choices that can be made * - min => minimum number of non-zero choices that can be made @@ -794,7 +798,7 @@ class Validation extends Object { * The regex checks for the following component parts: * * - a valid, optional, scheme - * - a valid ip address OR + * - a valid ip address OR * a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt * with an optional port number * - an optional valid path @@ -808,10 +812,11 @@ class Validation extends Object { */ function url($check, $strict = false) { $_this =& Validation::getInstance(); + $_this->__populateIp(); $_this->check = $check; $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z]|(%[0-9a-f]{2}))'; $_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') . - '(?:' . $_this->__pattern['ip'] . '|' . $_this->__pattern['hostname'] . ')(?::[1-9][0-9]{0,3})?' . + '(?:' . $_this->__pattern['IPv4'] . '|' . $_this->__pattern['hostname'] . ')(?::[1-9][0-9]{0,3})?' . '(?:\/?|\/' . $validChars . '*)?' . '(?:\?' . $validChars . '*)?' . '(?:#' . $validChars . '*)?$/i'; @@ -945,6 +950,36 @@ class Validation extends Object { return ($sum % 10 == 0); } +/* + * Lazily popualate the IP address patterns used for validations + * + * @return void + */ + function __populateIp() { + if (!isset($this->__pattern['IPv6'])) { + $pattern = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}'; + $pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})'; + $pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})'; + $pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)'; + $pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))'; + $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}'; + $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|'; + $pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}'; + $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))'; + $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})'; + $pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)'; + $pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]'; + $pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})'; + $pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?'; + + $this->__pattern['IPv6'] = $pattern; + } + if (!isset($this->__pattern['IPv4'])) { + $pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])'; + $this->__pattern['IPv4'] = $pattern; + } + } + /** * Reset internal variables for another validation run. * diff --git a/cake/tests/cases/libs/validation.test.php b/cake/tests/cases/libs/validation.test.php index 72ceb4861..5a5732f68 100644 --- a/cake/tests/cases/libs/validation.test.php +++ b/cake/tests/cases/libs/validation.test.php @@ -1734,6 +1734,46 @@ class ValidationTest extends CakeTestCase { $this->assertFalse(Validation::ip('127.0.0.256')); } +/** + * testIp v6 + * + * @access public + * @return void + */ + function testIpv6() { + $this->assertTrue(Validation::ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'IPv6')); + $this->assertTrue(Validation::ip('2001:db8:85a3:0:0:8a2e:370:7334', 'IPv6')); + $this->assertTrue(Validation::ip('2001:db8:85a3::8a2e:370:7334', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8:0000:0000:0000:0000:1428:57ab', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8:0000:0000:0000::1428:57ab', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8:0:0:0:0:1428:57ab', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8:0:0::1428:57ab', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8::1428:57ab', 'IPv6')); + $this->assertTrue(Validation::ip('2001:db8::1428:57ab', 'IPv6')); + $this->assertTrue(Validation::ip('0000:0000:0000:0000:0000:0000:0000:0001', 'IPv6')); + $this->assertTrue(Validation::ip('::1', 'IPv6')); + $this->assertTrue(Validation::ip('::ffff:12.34.56.78', 'IPv6')); + $this->assertTrue(Validation::ip('::ffff:0c22:384e', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8:1234:0000:0000:0000:0000:0000', 'IPv6')); + $this->assertTrue(Validation::ip('2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', 'IPv6')); + $this->assertTrue(Validation::ip('2001:db8:a::123', 'IPv6')); + $this->assertTrue(Validation::ip('fe80::', 'IPv6')); + $this->assertTrue(Validation::ip('::ffff:192.0.2.128', 'IPv6')); + $this->assertTrue(Validation::ip('::ffff:c000:280', 'IPv6')); + + $this->assertFalse(Validation::ip('123', 'IPv6')); + $this->assertFalse(Validation::ip('ldkfj', 'IPv6')); + $this->assertFalse(Validation::ip('2001::FFD3::57ab', 'IPv6')); + $this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:37023:7334', 'IPv6')); + $this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:370k:7334', 'IPv6')); + $this->assertFalse(Validation::ip('1:2:3:4:5:6:7:8:9', 'IPv6')); + $this->assertFalse(Validation::ip('1::2::3', 'IPv6')); + $this->assertFalse(Validation::ip('1:::3:4:5', 'IPv6')); + $this->assertFalse(Validation::ip('1:2:3::4:5:6:7:8:9', 'IPv6')); + $this->assertFalse(Validation::ip('::ffff:2.3.4', 'IPv6')); + $this->assertFalse(Validation::ip('::ffff:257.1.2.3', 'IPv6')); + } + /** * testMaxLength method *