Add new IS_FILE validator
This commit is contained in:
@@ -10,7 +10,7 @@ Web2Py framework modules
|
||||
========================
|
||||
"""
|
||||
|
||||
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
|
||||
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FILE', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
|
||||
|
||||
#: add pydal to sys.modules
|
||||
import os
|
||||
|
||||
@@ -166,7 +166,7 @@ class Highlighter(object):
|
||||
+ r'from|True|False)(?![a-zA-Z0-9_])'),
|
||||
'color:#185369; font-weight: bold'),
|
||||
('WEB2PY',
|
||||
re.compile(r'(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])'
|
||||
re.compile(r'(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FILE|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])'
|
||||
), 'link:%(link)s;text-decoration:None;color:#FF5C1F;'),
|
||||
('MAGIC', re.compile(r'self|None'),
|
||||
'color:#185369; font-weight: bold'),
|
||||
|
||||
@@ -1021,6 +1021,65 @@ class TestValidators(unittest.TestCase):
|
||||
rtn = IS_IMAGE(error_message='oops')(img)
|
||||
self.assertEqual(rtn, (img, 'oops'))
|
||||
|
||||
def test_IS_FILE(self):
|
||||
import cgi
|
||||
from io import BytesIO
|
||||
|
||||
def gen_fake(filename):
|
||||
formdata_file_data = """
|
||||
---123
|
||||
Content-Disposition: form-data; name="key2"
|
||||
|
||||
value2y
|
||||
---123
|
||||
Content-Disposition: form-data; name="file_attach"; filename="%s"
|
||||
Content-Type: text/plain
|
||||
|
||||
this is the content of the fake file
|
||||
|
||||
---123--
|
||||
""" % filename
|
||||
formdata_file_environ = {
|
||||
'CONTENT_LENGTH': str(len(formdata_file_data)),
|
||||
'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
|
||||
'QUERY_STRING': 'key1=value1&key2=value2x',
|
||||
'REQUEST_METHOD': 'POST',
|
||||
}
|
||||
return cgi.FieldStorage(fp=BytesIO(to_bytes(formdata_file_data)), environ=formdata_file_environ)['file_attach']
|
||||
|
||||
fake = gen_fake('example.pdf')
|
||||
rtn = IS_FILE(extension='pdf')(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
fake = gen_fake('example.gif')
|
||||
rtn = IS_FILE(extension='pdf')(fake)
|
||||
self.assertEqual(rtn, (fake, 'Enter valid filename'))
|
||||
fake = gen_fake('multiple.pdf')
|
||||
rtn = IS_FILE(extension=['pdf', 'png'])(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
fake = gen_fake('multiple.png')
|
||||
rtn = IS_FILE(extension=['pdf', 'png'])(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
fake = gen_fake('multiple.gif')
|
||||
rtn = IS_FILE(extension=['pdf', 'png'])(fake)
|
||||
self.assertEqual(rtn, (fake, 'Enter valid filename'))
|
||||
fake = gen_fake('backup2014.tar.gz')
|
||||
rtn = IS_FILE(filename=re.compile('backup.*'), extension='tar.gz', lastdot=False)(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
fake = gen_fake('README')
|
||||
rtn = IS_FILE(filename='README', extension='', case=0)(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
fake = gen_fake('readme')
|
||||
rtn = IS_FILE(filename='README', extension='', case=0)(fake)
|
||||
self.assertEqual(rtn, (fake, 'Enter valid filename'))
|
||||
fake = gen_fake('readme')
|
||||
rtn = IS_FILE(filename='README', case=2)(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
fake = gen_fake('README')
|
||||
rtn = IS_FILE(filename='README', case=2)(fake)
|
||||
self.assertEqual(rtn, (fake, None))
|
||||
rtn = IS_FILE(extension='pdf')('example.pdf')
|
||||
self.assertEqual(rtn, ('example.pdf', 'Enter valid filename'))
|
||||
|
||||
def test_IS_UPLOAD_FILENAME(self):
|
||||
import cgi
|
||||
from io import BytesIO
|
||||
|
||||
@@ -48,6 +48,7 @@ __all__ = [
|
||||
'IS_LIST_OF_EMAILS',
|
||||
'IS_EMPTY_OR',
|
||||
'IS_EXPR',
|
||||
'IS_FILE',
|
||||
'IS_FLOAT_IN_RANGE',
|
||||
'IS_IMAGE',
|
||||
'IS_IN_DB',
|
||||
@@ -3339,8 +3340,100 @@ class IS_IMAGE(Validator):
|
||||
return (-1, -1)
|
||||
|
||||
|
||||
class IS_FILE(Validator):
|
||||
"""
|
||||
Checks if name and extension of file uploaded through file input matches
|
||||
given criteria.
|
||||
|
||||
Does *not* ensure the file type in any way. Returns validation failure
|
||||
if no data was uploaded.
|
||||
|
||||
Args:
|
||||
filename: string/compiled regex or a list of strings/regex of valid filenames
|
||||
extension: string/compiled regex or a list of strings/regex of valid extensions
|
||||
lastdot: which dot should be used as a filename / extension separator:
|
||||
True means last dot, eg. file.jpg.png -> file.jpg / png
|
||||
False means first dot, eg. file.tar.gz -> file / tar.gz
|
||||
case: 0 - keep the case, 1 - transform the string into lowercase (default),
|
||||
2 - transform the string into uppercase
|
||||
|
||||
If there is no dot present, extension checks will be done against empty
|
||||
string and filename checks against whole value.
|
||||
|
||||
Examples:
|
||||
Check if file has a pdf extension (case insensitive):
|
||||
|
||||
INPUT(_type='file', _name='name',
|
||||
requires=IS_FILE(extension='pdf'))
|
||||
|
||||
Check if file is called 'thumbnail' and has a jpg or png extension
|
||||
(case insensitive):
|
||||
|
||||
INPUT(_type='file', _name='name',
|
||||
requires=IS_FILE(filename='thumbnail',
|
||||
extension=['jpg', 'png']))
|
||||
|
||||
Check if file has a tar.gz extension and name starting with backup:
|
||||
|
||||
INPUT(_type='file', _name='name',
|
||||
requires=IS_FILE(filename=re.compile('backup.*'),
|
||||
extension='tar.gz', lastdot=False))
|
||||
|
||||
Check if file has no extension and name matching README
|
||||
(case sensitive):
|
||||
|
||||
INPUT(_type='file', _name='name',
|
||||
requires=IS_FILE(filename='README',
|
||||
extension='', case=0)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, filename=None, extension=None, lastdot=True, case=1,
|
||||
error_message='Enter valid filename'):
|
||||
self.filename = filename
|
||||
self.extension = extension
|
||||
self.lastdot = lastdot
|
||||
self.case = case
|
||||
self.error_message = error_message
|
||||
|
||||
def match(self, value1, value2):
|
||||
if isinstance(value1, (list, tuple)):
|
||||
for v in value1:
|
||||
if self.match(v, value2):
|
||||
return True
|
||||
return False
|
||||
elif isinstance(value1, type(regex_isint)):
|
||||
return value1.match(value2)
|
||||
elif isinstance(value1, str):
|
||||
return value1 == value2
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
string = value.filename
|
||||
except:
|
||||
return (value, translator(self.error_message))
|
||||
if self.case == 1:
|
||||
string = string.lower()
|
||||
elif self.case == 2:
|
||||
string = string.upper()
|
||||
if self.lastdot:
|
||||
dot = string.rfind('.')
|
||||
else:
|
||||
dot = string.find('.')
|
||||
if dot == -1:
|
||||
dot = len(string)
|
||||
if self.filename and not self.match(self.filename, string[:dot]):
|
||||
return (value, translator(self.error_message))
|
||||
elif self.extension and not self.match(self.extension, string[dot + 1:]):
|
||||
return (value, translator(self.error_message))
|
||||
else:
|
||||
return (value, None)
|
||||
|
||||
|
||||
class IS_UPLOAD_FILENAME(Validator):
|
||||
"""
|
||||
For new applications, use IS_FILE().
|
||||
|
||||
Checks if name and extension of file uploaded through file input matches
|
||||
given criteria.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user