Changed URL validation to use urlparse instead of regex for spliting the URL
Enabled test_is_url in Python 3 since it is now passing This might be one of the last fixes to #1353 Fixes #1598
This commit is contained in:
@@ -24,7 +24,7 @@ from .test_web import *
|
|||||||
from .test_sqlhtml import *
|
from .test_sqlhtml import *
|
||||||
from .test_scheduler import *
|
from .test_scheduler import *
|
||||||
from .test_cron import *
|
from .test_cron import *
|
||||||
|
from .test_is_url import *
|
||||||
|
|
||||||
if sys.version[:3] == '2.7':
|
if sys.version[:3] == '2.7':
|
||||||
from .test_is_url import *
|
|
||||||
from .test_old_doctests import *
|
from .test_old_doctests import *
|
||||||
|
|||||||
+39
-39
@@ -586,81 +586,81 @@ class TestUnicode(unittest.TestCase):
|
|||||||
# disables prepending the scheme in the return value
|
# disables prepending the scheme in the return value
|
||||||
|
|
||||||
def testUnicodeToAsciiUrl(self):
|
def testUnicodeToAsciiUrl(self):
|
||||||
self.assertEquals(unicode_to_ascii_authority(u'www.Alliancefran\xe7aise.nu'), 'www.xn--alliancefranaise-npb.nu')
|
self.assertEqual(unicode_to_ascii_authority(u'www.Alliancefran\xe7aise.nu'), 'www.xn--alliancefranaise-npb.nu')
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
unicode_to_ascii_authority(u'www.benn.ca'), 'www.benn.ca')
|
unicode_to_ascii_authority(u'www.benn.ca'), 'www.benn.ca')
|
||||||
self.assertRaises(UnicodeError, unicode_to_ascii_authority,
|
self.assertRaises(UnicodeError, unicode_to_ascii_authority,
|
||||||
u'\u4e2d' * 1000) # label is too long
|
u'\u4e2d' * 1000) # label is too long
|
||||||
|
|
||||||
def testValidUrls(self):
|
def testValidUrls(self):
|
||||||
self.assertEquals(self.x(u'www.Alliancefrancaise.nu'), (
|
self.assertEqual(self.x(u'www.Alliancefrancaise.nu'), (
|
||||||
'http://www.Alliancefrancaise.nu', None))
|
'http://www.Alliancefrancaise.nu', None))
|
||||||
self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu'), (
|
self.assertEqual(self.x(u'www.Alliancefran\xe7aise.nu'), (
|
||||||
'http://www.xn--alliancefranaise-npb.nu', None))
|
'http://www.xn--alliancefranaise-npb.nu', None))
|
||||||
self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu:8080'), (
|
self.assertEqual(self.x(u'www.Alliancefran\xe7aise.nu:8080'), (
|
||||||
'http://www.xn--alliancefranaise-npb.nu:8080', None))
|
'http://www.xn--alliancefranaise-npb.nu:8080', None))
|
||||||
self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu'),
|
self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu'),
|
||||||
('http://www.xn--alliancefranaise-npb.nu', None))
|
('http://www.xn--alliancefranaise-npb.nu', None))
|
||||||
self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue', None))
|
self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue', None))
|
||||||
self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue#fragment', None))
|
self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue#fragment', None))
|
||||||
self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None))
|
self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None))
|
||||||
self.assertEquals(self.x(u'http://www.Alliancefran\xe7aise.nu:8080/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu:8080/parnaise/blue?query=value#fragment', None))
|
self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu:8080/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu:8080/parnaise/blue?query=value#fragment', None))
|
||||||
self.assertEquals(self.x(u'www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None))
|
self.assertEqual(self.x(u'www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None))
|
||||||
self.assertEquals(self.x(
|
self.assertEqual(self.x(
|
||||||
u'http://\u4e2d\u4fd4.com'), ('http://xn--fiq13b.com', None))
|
u'http://\u4e2d\u4fd4.com'), ('http://xn--fiq13b.com', None))
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86'),
|
self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com/\u4e86'),
|
||||||
('http://xn--fiq13b.com/%4e%86', None))
|
('http://xn--fiq13b.com/%4e%86', None))
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86', None))
|
self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86', None))
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86#fragment'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86#fragment', None))
|
self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86#fragment'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86#fragment', None))
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.com?query=\u4e86#fragment'), ('http://xn--fiq13b.com?query=%4e%86#fragment', None))
|
self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com?query=\u4e86#fragment'), ('http://xn--fiq13b.com?query=%4e%86#fragment', None))
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
self.x(u'http://B\xfccher.ch'), ('http://xn--bcher-kva.ch', None))
|
self.x(u'http://B\xfccher.ch'), ('http://xn--bcher-kva.ch', None))
|
||||||
self.assertEquals(self.x(u'http://\xe4\xf6\xfc\xdf.com'), (
|
self.assertEqual(self.x(u'http://\xe4\xf6\xfc\xdf.com'), (
|
||||||
'http://xn--ss-uia6e4a.com', None))
|
'http://xn--ss-uia6e4a.com', None))
|
||||||
self.assertEquals(self.x(
|
self.assertEqual(self.x(
|
||||||
u'http://visegr\xe1d.com'), ('http://xn--visegrd-mwa.com', None))
|
u'http://visegr\xe1d.com'), ('http://xn--visegrd-mwa.com', None))
|
||||||
self.assertEquals(self.x(u'http://h\xe1zipatika.com'), (
|
self.assertEqual(self.x(u'http://h\xe1zipatika.com'), (
|
||||||
'http://xn--hzipatika-01a.com', None))
|
'http://xn--hzipatika-01a.com', None))
|
||||||
self.assertEquals(self.x(u'http://www.\xe7ukurova.com'), (
|
self.assertEqual(self.x(u'http://www.\xe7ukurova.com'), (
|
||||||
'http://www.xn--ukurova-txa.com', None))
|
'http://www.xn--ukurova-txa.com', None))
|
||||||
self.assertEquals(self.x(u'http://nixier\xf6hre.nixieclock-tube.com'), ('http://xn--nixierhre-57a.nixieclock-tube.com', None))
|
self.assertEqual(self.x(u'http://nixier\xf6hre.nixieclock-tube.com'), ('http://xn--nixierhre-57a.nixieclock-tube.com', None))
|
||||||
self.assertEquals(self.x(u'google.ca.'), ('http://google.ca.', None))
|
self.assertEqual(self.x(u'google.ca.'), ('http://google.ca.', None))
|
||||||
|
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
self.y(u'https://google.ca'), ('https://google.ca', None))
|
self.y(u'https://google.ca'), ('https://google.ca', None))
|
||||||
self.assertEquals(self.y(
|
self.assertEqual(self.y(
|
||||||
u'https://\u4e2d\u4fd4.com'), ('https://xn--fiq13b.com', None))
|
u'https://\u4e2d\u4fd4.com'), ('https://xn--fiq13b.com', None))
|
||||||
|
|
||||||
self.assertEquals(self.z(u'google.ca'), ('google.ca', None))
|
self.assertEqual(self.z(u'google.ca'), ('google.ca', None))
|
||||||
|
|
||||||
def testInvalidUrls(self):
|
def testInvalidUrls(self):
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
self.x(u'://ABC.com'), (u'://ABC.com', 'Enter a valid URL'))
|
self.x(u'://ABC.com'), (u'://ABC.com', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'http://\u4e2d\u4fd4.dne'), (
|
self.assertEqual(self.x(u'http://\u4e2d\u4fd4.dne'), (
|
||||||
u'http://\u4e2d\u4fd4.dne', 'Enter a valid URL'))
|
u'http://\u4e2d\u4fd4.dne', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'https://google.dne'), (
|
self.assertEqual(self.x(u'https://google.dne'), (
|
||||||
u'https://google.dne', 'Enter a valid URL'))
|
u'https://google.dne', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'https://google..ca'), (
|
self.assertEqual(self.x(u'https://google..ca'), (
|
||||||
u'https://google..ca', 'Enter a valid URL'))
|
u'https://google..ca', 'Enter a valid URL'))
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
self.x(u'google..ca'), (u'google..ca', 'Enter a valid URL'))
|
self.x(u'google..ca'), (u'google..ca', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'http://' + u'\u4e2d' * 1000 + u'.com'), (
|
self.assertEqual(self.x(u'http://' + u'\u4e2d' * 1000 + u'.com'), (
|
||||||
u'http://' + u'\u4e2d' * 1000 + u'.com', 'Enter a valid URL'))
|
u'http://' + u'\u4e2d' * 1000 + u'.com', 'Enter a valid URL'))
|
||||||
|
|
||||||
self.assertEquals(self.x(u'http://google.com#fragment_\u4e86'), (
|
self.assertEqual(self.x(u'http://google.com#fragment_\u4e86'), (
|
||||||
u'http://google.com#fragment_\u4e86', 'Enter a valid URL'))
|
u'http://google.com#fragment_\u4e86', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'http\u4e86://google.com'), (
|
self.assertEqual(self.x(u'http\u4e86://google.com'), (
|
||||||
u'http\u4e86://google.com', 'Enter a valid URL'))
|
u'http\u4e86://google.com', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.x(u'http\u4e86://google.com#fragment_\u4e86'), (
|
self.assertEqual(self.x(u'http\u4e86://google.com#fragment_\u4e86'), (
|
||||||
u'http\u4e86://google.com#fragment_\u4e86', 'Enter a valid URL'))
|
u'http\u4e86://google.com#fragment_\u4e86', 'Enter a valid URL'))
|
||||||
|
|
||||||
self.assertEquals(self.y(u'http://\u4e2d\u4fd4.com/\u4e86'), (
|
self.assertEqual(self.y(u'http://\u4e2d\u4fd4.com/\u4e86'), (
|
||||||
u'http://\u4e2d\u4fd4.com/\u4e86', 'Enter a valid URL'))
|
u'http://\u4e2d\u4fd4.com/\u4e86', 'Enter a valid URL'))
|
||||||
#self.assertEquals(self.y(u'google.ca'), (u'google.ca', 'Enter a valid URL'))
|
#self.assertEqual(self.y(u'google.ca'), (u'google.ca', 'Enter a valid URL'))
|
||||||
|
|
||||||
self.assertEquals(self.z(u'invalid.domain..com'), (
|
self.assertEqual(self.z(u'invalid.domain..com'), (
|
||||||
u'invalid.domain..com', 'Enter a valid URL'))
|
u'invalid.domain..com', 'Enter a valid URL'))
|
||||||
self.assertEquals(self.z(u'invalid.\u4e2d\u4fd4.blargg'), (
|
self.assertEqual(self.z(u'invalid.\u4e2d\u4fd4.blargg'), (
|
||||||
u'invalid.\u4e2d\u4fd4.blargg', 'Enter a valid URL'))
|
u'invalid.\u4e2d\u4fd4.blargg', 'Enter a valid URL'))
|
||||||
|
|
||||||
# ##############################################################################
|
# ##############################################################################
|
||||||
|
|||||||
+81
-89
@@ -10,7 +10,6 @@
|
|||||||
Validators
|
Validators
|
||||||
-----------
|
-----------
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import datetime
|
import datetime
|
||||||
@@ -21,7 +20,7 @@ import urllib
|
|||||||
import struct
|
import struct
|
||||||
import decimal
|
import decimal
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from gluon._compat import StringIO, long, basestring, unicodeT, to_unicode, urllib_unquote, unichr, to_bytes, PY2, to_unicode, to_native
|
from gluon._compat import StringIO, long, basestring, unicodeT, to_unicode, urllib_unquote, unichr, to_bytes, PY2, to_unicode, to_native, urlparse
|
||||||
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
|
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
|
||||||
from pydal.objects import Field, FieldVirtual, FieldMethod
|
from pydal.objects import Field, FieldVirtual, FieldMethod
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
@@ -195,7 +194,7 @@ class IS_MATCH(Validator):
|
|||||||
self.is_unicode = is_unicode or (not(PY2))
|
self.is_unicode = is_unicode or (not(PY2))
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
if not(PY2): # PY3 convert bytes to unicode
|
if not(PY2): # PY3 convert bytes to unicode
|
||||||
value = to_unicode(value)
|
value = to_unicode(value)
|
||||||
|
|
||||||
if self.is_unicode or not(PY2):
|
if self.is_unicode or not(PY2):
|
||||||
@@ -270,7 +269,7 @@ class IS_EXPR(Validator):
|
|||||||
return (value, self.expression(value))
|
return (value, self.expression(value))
|
||||||
# for backward compatibility
|
# for backward compatibility
|
||||||
self.environment.update(value=value)
|
self.environment.update(value=value)
|
||||||
exec ('__ret__=' + self.expression, self.environment)
|
exec('__ret__=' + self.expression, self.environment)
|
||||||
if self.environment['__ret__']:
|
if self.environment['__ret__']:
|
||||||
return (value, None)
|
return (value, None)
|
||||||
return (value, translate(self.error_message))
|
return (value, translate(self.error_message))
|
||||||
@@ -1185,7 +1184,6 @@ class IS_EMAIL(Validator):
|
|||||||
|
|
||||||
regex_proposed_but_failed = re.compile('^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$', re.VERBOSE | re.IGNORECASE)
|
regex_proposed_but_failed = re.compile('^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$', re.VERBOSE | re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
banned=None,
|
banned=None,
|
||||||
forced=None,
|
forced=None,
|
||||||
@@ -1200,7 +1198,7 @@ class IS_EMAIL(Validator):
|
|||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
if not(isinstance(value, (basestring, unicodeT))) or not value or '@' not in value:
|
if not(isinstance(value, (basestring, unicodeT))) or not value or '@' not in value:
|
||||||
return (value, translate(self.error_message))
|
return (value, translate(self.error_message))
|
||||||
|
|
||||||
body, domain = value.rsplit('@', 1)
|
body, domain = value.rsplit('@', 1)
|
||||||
|
|
||||||
@@ -1388,19 +1386,6 @@ unofficial_url_schemes = [
|
|||||||
all_url_schemes = [None] + official_url_schemes + unofficial_url_schemes
|
all_url_schemes = [None] + official_url_schemes + unofficial_url_schemes
|
||||||
http_schemes = [None, 'http', 'https']
|
http_schemes = [None, 'http', 'https']
|
||||||
|
|
||||||
|
|
||||||
# This regex comes from RFC 2396, Appendix B. It's used to split a URL into
|
|
||||||
# its component parts
|
|
||||||
# Here are the regex groups that it extracts:
|
|
||||||
# scheme = group(2)
|
|
||||||
# authority = group(4)
|
|
||||||
# path = group(5)
|
|
||||||
# query = group(7)
|
|
||||||
# fragment = group(9)
|
|
||||||
|
|
||||||
url_split_regex = \
|
|
||||||
re.compile('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?')
|
|
||||||
|
|
||||||
# Defined in RFC 3490, Section 3.1, Requirement #1
|
# Defined in RFC 3490, Section 3.1, Requirement #1
|
||||||
# Use this regex to split the authority component of a unicode URL into
|
# Use this regex to split the authority component of a unicode URL into
|
||||||
# its component labels
|
# its component labels
|
||||||
@@ -1470,18 +1455,15 @@ def unicode_to_ascii_authority(authority):
|
|||||||
# We use the ToASCII operation because we are about to put the authority
|
# We use the ToASCII operation because we are about to put the authority
|
||||||
# into an IDN-unaware slot
|
# into an IDN-unaware slot
|
||||||
asciiLabels = []
|
asciiLabels = []
|
||||||
try:
|
import encodings.idna
|
||||||
import encodings.idna
|
for label in labels:
|
||||||
for label in labels:
|
if label:
|
||||||
if label:
|
asciiLabels.append(to_native(encodings.idna.ToASCII(label)))
|
||||||
asciiLabels.append(to_native(encodings.idna.ToASCII(label)))
|
else:
|
||||||
else:
|
# encodings.idna.ToASCII does not accept an empty string, but
|
||||||
# encodings.idna.ToASCII does not accept an empty string, but
|
# it is necessary for us to allow for empty labels so that we
|
||||||
# it is necessary for us to allow for empty labels so that we
|
# don't modify the URL
|
||||||
# don't modify the URL
|
asciiLabels.append('')
|
||||||
asciiLabels.append('')
|
|
||||||
except:
|
|
||||||
asciiLabels = [str(label) for label in labels]
|
|
||||||
# RFC 3490, Section 4, Step 5
|
# RFC 3490, Section 4, Step 5
|
||||||
return str(reduce(lambda x, y: x + unichr(0x002E) + y, asciiLabels))
|
return str(reduce(lambda x, y: x + unichr(0x002E) + y, asciiLabels))
|
||||||
|
|
||||||
@@ -1520,33 +1502,35 @@ def unicode_to_ascii_url(url, prepend_scheme):
|
|||||||
"""
|
"""
|
||||||
# convert the authority component of the URL into an ASCII punycode string,
|
# convert the authority component of the URL into an ASCII punycode string,
|
||||||
# but encode the rest using the regular URI character encoding
|
# but encode the rest using the regular URI character encoding
|
||||||
|
components = urlparse.urlparse(url)
|
||||||
groups = url_split_regex.match(url).groups()
|
prepended = False
|
||||||
# If no authority was found
|
# If no authority was found
|
||||||
if not groups[3]:
|
if not components.netloc:
|
||||||
# Try appending a scheme to see if that fixes the problem
|
# Try appending a scheme to see if that fixes the problem
|
||||||
scheme_to_prepend = prepend_scheme or 'http'
|
scheme_to_prepend = prepend_scheme or 'http'
|
||||||
groups = url_split_regex.match(
|
components = urlparse.urlparse(to_unicode(scheme_to_prepend) + u'://' + url)
|
||||||
to_unicode(scheme_to_prepend) + u'://' + url).groups()
|
prepended = True
|
||||||
|
|
||||||
# if we still can't find the authority
|
# if we still can't find the authority
|
||||||
if not groups[3]:
|
if not components.netloc:
|
||||||
raise Exception('No authority component found, ' +
|
raise Exception('No authority component found, ' +
|
||||||
'could not decode unicode to US-ASCII')
|
'could not decode unicode to US-ASCII')
|
||||||
|
|
||||||
# We're here if we found an authority, let's rebuild the URL
|
# We're here if we found an authority, let's rebuild the URL
|
||||||
scheme = groups[1]
|
scheme = components.scheme
|
||||||
authority = groups[3]
|
authority = components.netloc
|
||||||
path = groups[4] or ''
|
path = components.path
|
||||||
query = groups[5] or ''
|
query = components.query
|
||||||
fragment = groups[7] or ''
|
fragment = components.fragment
|
||||||
|
|
||||||
if prepend_scheme:
|
if prepended:
|
||||||
scheme = str(scheme) + '://'
|
|
||||||
else:
|
|
||||||
scheme = ''
|
scheme = ''
|
||||||
|
|
||||||
return scheme + unicode_to_ascii_authority(authority) +\
|
unparsed = urlparse.urlunparse((scheme, unicode_to_ascii_authority(authority), escape_unicode(path), '', escape_unicode(query), str(fragment)))
|
||||||
escape_unicode(path) + escape_unicode(query) + str(fragment)
|
if unparsed.startswith('//'):
|
||||||
|
unparsed = unparsed[2:] # Remove the // urlunparse puts in the beginning
|
||||||
|
return unparsed
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IS_GENERIC_URL(Validator):
|
class IS_GENERIC_URL(Validator):
|
||||||
@@ -1607,7 +1591,8 @@ class IS_GENERIC_URL(Validator):
|
|||||||
% (self.prepend_scheme, self.allowed_schemes))
|
% (self.prepend_scheme, self.allowed_schemes))
|
||||||
|
|
||||||
GENERIC_URL = re.compile(r"%[^0-9A-Fa-f]{2}|%[^0-9A-Fa-f][0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]|%$|%[0-9A-Fa-f]$|%[^0-9A-Fa-f]$")
|
GENERIC_URL = re.compile(r"%[^0-9A-Fa-f]{2}|%[^0-9A-Fa-f][0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]|%$|%[0-9A-Fa-f]$|%[^0-9A-Fa-f]$")
|
||||||
GENERIC_URL_VALID = re.compile(r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%#]+$")
|
GENERIC_URL_VALID = re.compile(r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%]+$")
|
||||||
|
URL_FRAGMENT_VALID = re.compile(r"[|A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%]+$")
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
"""
|
"""
|
||||||
@@ -1619,41 +1604,49 @@ class IS_GENERIC_URL(Validator):
|
|||||||
prepended with prepend_scheme), and tuple[1] is either
|
prepended with prepend_scheme), and tuple[1] is either
|
||||||
None (success!) or the string error_message
|
None (success!) or the string error_message
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
# if the URL does not misuse the '%' character
|
# if we dont have anything or the URL misuses the '%' character
|
||||||
if not self.GENERIC_URL.search(value):
|
|
||||||
# if the URL is only composed of valid characters
|
if not value or self.GENERIC_URL.search(value):
|
||||||
if self.GENERIC_URL_VALID.match(value):
|
return (value, translate(self.error_message))
|
||||||
# Then split up the URL into its components and check on
|
|
||||||
# the scheme
|
if '#' in value:
|
||||||
scheme = url_split_regex.match(value).group(2)
|
url, fragment_part = value.split('#')
|
||||||
# Clean up the scheme before we check it
|
else:
|
||||||
if not scheme is None:
|
url, fragment_part = value, ''
|
||||||
scheme = urllib_unquote(scheme).lower()
|
# if the URL is only composed of valid characters
|
||||||
# If the scheme really exists
|
if self.GENERIC_URL_VALID.match(url) and (not fragment_part or self.URL_FRAGMENT_VALID.match(fragment_part)):
|
||||||
if scheme in self.allowed_schemes:
|
# Then parse the URL into its components and check on
|
||||||
# Then the URL is valid
|
try:
|
||||||
return (value, None)
|
components = urlparse.urlparse(urllib_unquote(value))._asdict()
|
||||||
else:
|
except ValueError:
|
||||||
# else, for the possible case of abbreviated URLs with
|
return (value, translate(self.error_message))
|
||||||
# ports, check to see if adding a valid scheme fixes
|
|
||||||
# the problem (but only do this if it doesn't have
|
# Clean up the scheme before we check it
|
||||||
# one already!)
|
scheme = components['scheme']
|
||||||
if value.find('://') < 0 and None in self.allowed_schemes:
|
if len(scheme) == 0:
|
||||||
schemeToUse = self.prepend_scheme or 'http'
|
scheme = None
|
||||||
prependTest = self.__call__(
|
else:
|
||||||
schemeToUse + '://' + value)
|
scheme = components['scheme'].lower()
|
||||||
# if the prepend test succeeded
|
# If the scheme doesn't really exists
|
||||||
if prependTest[1] is None:
|
if scheme not in self.allowed_schemes or not scheme and ':' in components['path']:
|
||||||
# if prepending in the output is enabled
|
# for the possible case of abbreviated URLs with
|
||||||
if self.prepend_scheme:
|
# ports, check to see if adding a valid scheme fixes
|
||||||
return prependTest
|
# the problem (but only do this if it doesn't have
|
||||||
else:
|
# one already!)
|
||||||
# else return the original,
|
if '://' not in value and None in self.allowed_schemes:
|
||||||
# non-prepended value
|
schemeToUse = self.prepend_scheme or 'http'
|
||||||
return (value, None)
|
prependTest = self.__call__(
|
||||||
except:
|
schemeToUse + '://' + value)
|
||||||
pass
|
# if the prepend test succeeded
|
||||||
|
if prependTest[1] is None:
|
||||||
|
# if prepending in the output is enabled
|
||||||
|
if self.prepend_scheme:
|
||||||
|
return prependTest
|
||||||
|
else:
|
||||||
|
return (value, None)
|
||||||
|
else:
|
||||||
|
return (value, None)
|
||||||
# else the URL is not valid
|
# else the URL is not valid
|
||||||
return (value, translate(self.error_message))
|
return (value, translate(self.error_message))
|
||||||
|
|
||||||
@@ -1920,15 +1913,14 @@ class IS_HTTP_URL(Validator):
|
|||||||
(possible prepended with prepend_scheme), and tuple[1] is either
|
(possible prepended with prepend_scheme), and tuple[1] is either
|
||||||
None (success!) or the string error_message
|
None (success!) or the string error_message
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# if the URL passes generic validation
|
# if the URL passes generic validation
|
||||||
x = IS_GENERIC_URL(error_message=self.error_message,
|
x = IS_GENERIC_URL(error_message=self.error_message,
|
||||||
allowed_schemes=self.allowed_schemes,
|
allowed_schemes=self.allowed_schemes,
|
||||||
prepend_scheme=self.prepend_scheme)
|
prepend_scheme=self.prepend_scheme)
|
||||||
if x(value)[1] is None:
|
if x(value)[1] is None:
|
||||||
componentsMatch = url_split_regex.match(value)
|
components = urlparse.urlparse(value)
|
||||||
authority = componentsMatch.group(4)
|
authority = components.netloc
|
||||||
# if there is an authority component
|
# if there is an authority component
|
||||||
if authority:
|
if authority:
|
||||||
# if authority is a valid IP address
|
# if authority is a valid IP address
|
||||||
@@ -1948,7 +1940,7 @@ class IS_HTTP_URL(Validator):
|
|||||||
else:
|
else:
|
||||||
# else this is a relative/abbreviated URL, which will parse
|
# else this is a relative/abbreviated URL, which will parse
|
||||||
# into the URL's path component
|
# into the URL's path component
|
||||||
path = componentsMatch.group(5)
|
path = components.path
|
||||||
# relative case: if this is a valid path (if it starts with
|
# relative case: if this is a valid path (if it starts with
|
||||||
# a slash)
|
# a slash)
|
||||||
if path.startswith('/'):
|
if path.startswith('/'):
|
||||||
@@ -1957,7 +1949,7 @@ class IS_HTTP_URL(Validator):
|
|||||||
else:
|
else:
|
||||||
# abbreviated case: if we haven't already, prepend a
|
# abbreviated case: if we haven't already, prepend a
|
||||||
# scheme and see if it fixes the problem
|
# scheme and see if it fixes the problem
|
||||||
if value.find('://') < 0:
|
if '://' not in value and None in self.allowed_schemes:
|
||||||
schemeToUse = self.prepend_scheme or 'http'
|
schemeToUse = self.prepend_scheme or 'http'
|
||||||
prependTest = self.__call__(schemeToUse
|
prependTest = self.__call__(schemeToUse
|
||||||
+ '://' + value)
|
+ '://' + value)
|
||||||
@@ -2124,7 +2116,6 @@ class IS_URL(Validator):
|
|||||||
# If we are not able to convert the unicode url into a
|
# If we are not able to convert the unicode url into a
|
||||||
# US-ASCII URL, then the URL is not valid
|
# US-ASCII URL, then the URL is not valid
|
||||||
return (value, translate(self.error_message))
|
return (value, translate(self.error_message))
|
||||||
|
|
||||||
methodResult = subMethod(asciiValue)
|
methodResult = subMethod(asciiValue)
|
||||||
# if the validation of the US-ASCII version of the value failed
|
# if the validation of the US-ASCII version of the value failed
|
||||||
if not methodResult[1] is None:
|
if not methodResult[1] is None:
|
||||||
@@ -2494,6 +2485,7 @@ class IS_LOWER(Validator):
|
|||||||
('\\xc3\\xb1', None)
|
('\\xc3\\xb1', None)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
cast_back = lambda x: x
|
cast_back = lambda x: x
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
|
|||||||
Reference in New Issue
Block a user