MobileBlur

Diff
Login

Differences From Artifact [0b976e1d6d]:

To Artifact [59f9c2fdd4]:


15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29







-
+







import time
import cgi
import urllib
import struct
import decimal
import unicodedata
from cStringIO import StringIO
from utils import simple_hash, hmac_hash
from utils import simple_hash, hmac_hash, web2py_uuid

__all__ = [
    'CLEANUP',
    'CRYPT',
    'IS_ALPHANUMERIC',
    'IS_DATE_IN_RANGE',
    'IS_DATE',
117
118
119
120
121
122
123


















124
125
126
127
128
129

130
131
132



133
134
135
136
137

138
139
140
141
142
143
144
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+



+
+
+




-
+








        INPUT(_type='text', _name='name', requires=IS_MATCH('.+'))

    the argument of IS_MATCH is a regular expression::

        >>> IS_MATCH('.+')('hello')
        ('hello', None)

        >>> IS_MATCH('hell')('hello')
        ('hello', 'invalid expression')

        >>> IS_MATCH('hell.*', strict=False)('hello')
        ('hello', None)

        >>> IS_MATCH('hello')('shello')
        ('shello', 'invalid expression')

        >>> IS_MATCH('hello', search=True)('shello')
        ('hello', None)

        >>> IS_MATCH('hello', search=True, strict=False)('shellox')
        ('hello', None)

        >>> IS_MATCH('.*hello.*', search=True, strict=False)('shellox')
        ('shellox', None)

        >>> IS_MATCH('.+')('')
        ('', 'invalid expression')
    """

    def __init__(self, expression, error_message='invalid expression', strict=True):
    def __init__(self, expression, error_message='invalid expression', strict=True, search=False):
        if strict:
            if not expression.endswith('$'):
                expression = '(%s)$' % expression
        if not search:
            if not expression.startswith('^'):
                expression = '^(%s)' % expression
        self.regex = re.compile(expression)
        self.error_message = error_message

    def __call__(self, value):
        match = self.regex.match(value)
        match = self.regex.search(value)
        if match:
            return (match.group(), None)
        return (value, translate(self.error_message))


class IS_EQUAL_TO(Validator):
    """
314
315
316
317
318
319
320
321

322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

339
340
341
342
343
344
345
335
336
337
338
339
340
341

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

359
360
361
362
363
364
365
366







-
+
















-
+







    def options(self,zero=True):
        if not self.labels:
            items = [(k, k) for (i, k) in enumerate(self.theset)]
        else:
            items = [(k, self.labels[i]) for (i, k) in enumerate(self.theset)]
        if self.sort:
            items.sort(options_sorter)
        if zero and self.zero != None and not self.multiple:
        if zero and not self.zero is None and not self.multiple:
            items.insert(0,('',self.zero))
        return items

    def __call__(self, value):
        if self.multiple:
            ### if below was values = re.compile("[\w\-:]+").findall(str(value))
            if isinstance(value, (str,unicode)):
                values = [value]
            elif isinstance(value, (tuple, list)):
                values = value
            elif not value:
                values = []
        else:
            values = [value]
        failures = [x for x in values if not x in self.theset]
        if failures and self.theset:
            if self.multiple and (value == None or value == ''):
            if self.multiple and (value is None or value == ''):
                return ([], None)
            return (value, translate(self.error_message))
        if self.multiple:
            if isinstance(self.multiple,(tuple,list)) and \
                    not self.multiple[0]<=len(values)<self.multiple[1]:
                return (values, translate(self.error_message))
            return (values, None)
435
436
437
438
439
440
441
442

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460

461
462
463
464
465
466
467
456
457
458
459
460
461
462

463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488







-
+

















-
+







            self.labels = [self.label(r) for r in records]

    def options(self, zero=True):
        self.build_set()
        items = [(k, self.labels[i]) for (i, k) in enumerate(self.theset)]
        if self.sort:
            items.sort(options_sorter)
        if zero and self.zero != None and not self.multiple:
        if zero and not self.zero is None and not self.multiple:
            items.insert(0,('',self.zero))
        return items

    def __call__(self, value):
        if self.multiple:
            if isinstance(value,list):
                values=value
            elif value:
                values = [value]
            else:
                values = []
            if isinstance(self.multiple,(tuple,list)) and \
                    not self.multiple[0]<=len(values)<self.multiple[1]:
                return (values, translate(self.error_message))
            if not [x for x in values if not x in self.theset]:
                return (values, None)
        elif self.theset:
            if value in self.theset:
            if str(value) in self.theset:
                if self._and:
                    return self._and(value)
                else:
                    return (value, None)
        else:
            (ktable, kfield) = str(self.field).split('.')
            field = self.dbset.db[ktable][kfield]
605
606
607
608
609
610
611





612
613
614
615
616
617
618
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644







+
+
+
+
+







                    return (value, None)
            elif self.minimum <= value < self.maximum:
                    return (value, None)
        except ValueError:
            pass
        return (value, self.error_message)

def str2dec(number):
    s = str(number)
    if not '.' in s: s+='.00'
    else: s+='0'*(2-len(s.split('.')[1]))
    return s

class IS_FLOAT_IN_RANGE(Validator):
    """
    Determine that the argument is (or can be represented as) a float,
    and that it falls within the specified inclusive range.
    The comparison is made with native arithmetic.

693
694
695
696
697
698
699
700
701
702
703

704
705
706
707
708
709
710
719
720
721
722
723
724
725




726
727
728
729
730
731
732
733







-
-
-
-
+







            elif self.minimum <= fvalue <= self.maximum:
                    return (fvalue, None)
        except (ValueError, TypeError):
            pass
        return (value, self.error_message)

    def formatter(self,value):
        if self.dot=='.':
            return str(value)
        else:
            return str(value).replace('.',self.dot)
        return str2dec(value).replace('.',self.dot)


class IS_DECIMAL_IN_RANGE(Validator):
    """
    Determine that the argument is (or can be represented as) a Python Decimal,
    and that it falls within the specified inclusive range.
    The comparison is made with Python Decimal arithmetic.
800
801
802
803
804
805
806
807

808
809
810
811
812
813
814
815

816
817
818
819
820
821
822
823
824
825
826
827
828
829

830
831
832
833
834
835
836
837

838
839
840
841
842
843
844
845







-
+







-
+







            elif self.minimum <= v <= self.maximum:
                    return (v, None)
        except (ValueError, TypeError, decimal.InvalidOperation):
            pass
        return (value, self.error_message)

    def formatter(self, value):
        return str(value).replace('.',self.dot)
        return str2dec(value).replace('.',self.dot)

def is_empty(value, empty_regex=None):
    "test empty field"
    if isinstance(value, (str, unicode)):
        value = value.strip()
        if empty_regex is not None and empty_regex.match(value):
            value = ''
    if value == None or value == '' or value == []:
    if value is None or value == '' or value == []:
        return (value, True)
    return (value, False)

class IS_NOT_EMPTY(Validator):
    """
    example::

1342
1343
1344
1345
1346
1347
1348
1349

1350
1351
1352
1353
1354
1355
1356
1365
1366
1367
1368
1369
1370
1371

1372
1373
1374
1375
1376
1377
1378
1379







-
+







        :param allowed_schemes: a list containing strings or None. Each element
            is a scheme the inputed URL is allowed to use
        :param prepend_scheme: a string, this scheme is prepended if it's
            necessary to make the URL valid
        """

        self.error_message = error_message
        if allowed_schemes == None:
        if allowed_schemes is None:
            self.allowed_schemes = all_url_schemes
        else:
            self.allowed_schemes = allowed_schemes
        self.prepend_scheme = prepend_scheme
        if self.prepend_scheme not in self.allowed_schemes:
            raise SyntaxError, \
                "prepend_scheme='%s' is not in allowed_schemes=%s" \
1371
1372
1373
1374
1375
1376
1377
1378

1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395

1396
1397
1398
1399
1400
1401
1402
1394
1395
1396
1397
1398
1399
1400

1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417

1418
1419
1420
1421
1422
1423
1424
1425







-
+
















-
+







                # if the URL is only composed of valid characters
                if re.compile(
                    r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%#]+$").match(value):
                    # Then split up the URL into its components and check on
                    # the scheme
                    scheme = url_split_regex.match(value).group(2)
                    # Clean up the scheme before we check it
                    if scheme != None:
                    if not scheme is None:
                        scheme = urllib.unquote(scheme).lower()
                    # If the scheme really exists
                    if scheme in self.allowed_schemes:
                        # Then the URL is valid
                        return (value, None)
                    else:
                        # else, for the possible case of abbreviated URLs with
                        # ports, check to see if adding a valid scheme fixes
                        # the problem (but only do this if it doesn't have
                        # one already!)
                        if not re.compile('://').search(value) and None\
                             in self.allowed_schemes:
                            schemeToUse = self.prepend_scheme or 'http'
                            prependTest = self.__call__(schemeToUse
                                     + '://' + value)
                            # if the prepend test succeeded
                            if prependTest[1] == None:
                            if prependTest[1] is None:
                                # if prepending in the output is enabled
                                if self.prepend_scheme:
                                    return prependTest
                                else:
                                    # else return the original,
                                    #  non-prepended value
                                    return (value, None)
1757
1758
1759
1760
1761
1762
1763
1764

1765
1766
1767
1768
1769
1770
1771
1780
1781
1782
1783
1784
1785
1786

1787
1788
1789
1790
1791
1792
1793
1794







-
+







        :param allowed_schemes: a list containing strings or None. Each element
            is a scheme the inputed URL is allowed to use
        :param prepend_scheme: a string, this scheme is prepended if it's
            necessary to make the URL valid
        """

        self.error_message = error_message
        if allowed_schemes == None:
        if allowed_schemes is None:
            self.allowed_schemes = http_schemes
        else:
            self.allowed_schemes = allowed_schemes
        self.prepend_scheme = prepend_scheme

        for i in self.allowed_schemes:
            if i not in http_schemes:
1787
1788
1789
1790
1791
1792
1793
1794

1795
1796
1797
1798
1799
1800
1801
1810
1811
1812
1813
1814
1815
1816

1817
1818
1819
1820
1821
1822
1823
1824







-
+







        """

        try:
            # if the URL passes generic validation
            x = IS_GENERIC_URL(error_message=self.error_message,
                               allowed_schemes=self.allowed_schemes,
                               prepend_scheme=self.prepend_scheme)
            if x(value)[1] == None:
            if x(value)[1] is None:
                componentsMatch = url_split_regex.match(value)
                authority = componentsMatch.group(4)
                # if there is an authority component
                if authority:
                    # if authority is a valid IP address
                    if re.compile(
                        "([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$").match(authority):
1826
1827
1828
1829
1830
1831
1832
1833

1834
1835
1836
1837
1838
1839
1840
1849
1850
1851
1852
1853
1854
1855

1856
1857
1858
1859
1860
1861
1862
1863







-
+







                        # abbreviated case: if we haven't already, prepend a
                        # scheme and see if it fixes the problem
                        if not re.compile('://').search(value):
                            schemeToUse = self.prepend_scheme or 'http'
                            prependTest = self.__call__(schemeToUse
                                     + '://' + value)
                            # if the prepend test succeeded
                            if prependTest[1] == None:
                            if prependTest[1] is None:
                                # if prepending in the output is enabled
                                if self.prepend_scheme:
                                    return prependTest
                                else:
                                    # else return the original, non-prepended
                                    # value
                                    return (value, None)
1982
1983
1984
1985
1986
1987
1988
1989

1990
1991
1992
1993
1994
1995
1996
2005
2006
2007
2008
2009
2010
2011

2012
2013
2014
2015
2016
2017
2018
2019







-
+







            except Exception:
                #If we are not able to convert the unicode url into a
                # US-ASCII URL, then the URL is not valid
                return (value, translate(self.error_message))

            methodResult = subMethod(asciiValue)
            #if the validation of the US-ASCII version of the value failed
            if methodResult[1] != None:
            if not methodResult[1] is None:
                # then return the original input value, not the US-ASCII version
                return (value, methodResult[1])
            else:
                return methodResult


regex_time = re.compile(
2042
2043
2044
2045
2046
2047
2048
2049

2050
2051

2052
2053
2054
2055
2056
2057
2058
2065
2066
2067
2068
2069
2070
2071

2072
2073

2074
2075
2076
2077
2078
2079
2080
2081







-
+

-
+







        self.error_message = error_message

    def __call__(self, value):
        try:
            ivalue = value
            value = regex_time.match(value.lower())
            (h, m, s) = (int(value.group('h')), 0, 0)
            if value.group('m') != None:
            if not value.group('m') is None:
                m = int(value.group('m'))
            if value.group('s') != None:
            if not value.group('s') is None:
                s = int(value.group('s'))
            if value.group('d') == 'pm' and 0 < h < 12:
                h = h + 12
            if not (h in range(24) and m in range(60) and s
                     in range(60)):
                raise ValueError\
                    ('Hours or minutes or seconds are outside of allowed range')
2476
2477
2478
2479
2480
2481
2482






2483
2484
2485

2486
2487


2488
2489




2490
2491
2492
2493
2494
2495
2496
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513

2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531







+
+
+
+
+
+


-
+


+
+


+
+
+
+







    encodes the value on validation with a digest.

    If no arguments are provided CRYPT uses the MD5 algorithm.
    If the key argument is provided the HMAC+MD5 algorithm is used.
    If the digest_alg is specified this is used to replace the
    MD5 with, for example, SHA512. The digest_alg can be
    the name of a hashlib algorithm as a string or the algorithm itself.
    
    min_length is the minimal password length (default 4) - IS_STRONG for serious security
    error_message is the message if password is too short
    
    Notice that an empty password is accepted but invalid. It will not allow login back.
    Stores junk as hashed password.
    """

    def __init__(self, key=None, digest_alg='md5'):
    def __init__(self, key=None, digest_alg='md5', min_length=0, error_message='too short'):
        self.key = key
        self.digest_alg = digest_alg
        self.min_length = min_length
        self.error_message = error_message

    def __call__(self, value):
        if not value and self.min_length>0:
            value = web2py_uuid()
        elif len(value)<self.min_length:
            return ('',translate(self.error_message))
        if self.key:
            return (hmac_hash(value, self.key, self.digest_alg), None)
        else:
            return (simple_hash(value, self.digest_alg), None)


class IS_STRONG(object):
2911
2912
2913
2914
2915
2916
2917
2918

2919
2920
2921

2922
2923
2924

2925
2926
2927
2928
2929
2930
2931
2932
2933
2934


2946
2947
2948
2949
2950
2951
2952

2953
2954
2955

2956
2957
2958

2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971







-
+


-
+


-
+










+
+
            number = 0
            for i, j in zip(self.numbers, value.split('.')):
                number += i * int(j)
            ok = False
            for bottom, top in zip(self.minip, self.maxip):
                if self.invert != (bottom <= number <= top):
                    ok = True
            if not (self.is_localhost == None or self.is_localhost == \
            if not (self.is_localhost is None or self.is_localhost == \
                (number == self.localhost)):
                    ok = False
            if not (self.is_private == None or self.is_private == \
            if not (self.is_private is None or self.is_private == \
                (sum([number[0] <= number <= number[1] for number in self.private]) > 0)):
                    ok = False
            if not (self.is_automatic == None or self.is_automatic == \
            if not (self.is_automatic is None or self.is_automatic == \
                (self.automatic[0] <= number <= self.automatic[1])):
                    ok = False
            if ok:
                return (value, None)
        return (value, translate(self.error_message))

if __name__ == '__main__':
    import doctest
    doctest.testmod()