fix tools

This commit is contained in:
ilvalle
2016-06-13 20:20:44 +02:00
parent a18e0e489f
commit 7259516627
8 changed files with 78 additions and 77 deletions
+5 -2
View File
@@ -21,7 +21,10 @@ if PY2:
import ConfigParser as configparser import ConfigParser as configparser
from email.MIMEBase import MIMEBase from email.MIMEBase import MIMEBase
from email.Header import Header from email.Header import Header
from email import MIMEMultipart, MIMEText, Encoders, Charset from email import Encoders, Charset
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.Charset import add_charset, QP as charset_QP
from urllib import FancyURLopener, urlencode, urlopen from urllib import FancyURLopener, urlencode, urlopen
from urllib import quote as urllib_quote, unquote as urllib_unquote from urllib import quote as urllib_quote, unquote as urllib_unquote
from string import maketrans from string import maketrans
@@ -88,7 +91,7 @@ else:
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email import encoders as Encoders from email import encoders as Encoders
from email.header import Header from email.header import Header
from email.charset import Charset from email.charset import Charset, add_charset, QP as charset_QP
from urllib.request import FancyURLopener, urlopen from urllib.request import FancyURLopener, urlopen
from urllib.parse import quote as urllib_quote, unquote as urllib_unquote, urlencode from urllib.parse import quote as urllib_quote, unquote as urllib_unquote, urlencode
import html import html
+2 -1
View File
@@ -19,6 +19,7 @@ Additions:
- .pickle: application/python-pickle - .pickle: application/python-pickle
- .w2p': application/w2p - .w2p': application/w2p
""" """
from gluon._compat import to_native
__all__ = ['contenttype'] __all__ = ['contenttype']
@@ -842,7 +843,7 @@ def contenttype(filename, default='text/plain'):
""" """
Returns the Content-Type string matching extension of the given filename. Returns the Content-Type string matching extension of the given filename.
""" """
filename=to_native(filename)
i = filename.rfind('.') i = filename.rfind('.')
if i >= 0: if i >= 0:
default = CONTENT_TYPE.get(filename[i:].lower(), default) default = CONTENT_TYPE.get(filename[i:].lower(), default)
+3 -3
View File
@@ -18,7 +18,7 @@ import pkgutil
import logging import logging
from cgi import escape from cgi import escape
from threading import RLock from threading import RLock
from gluon._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems from gluon._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems, _local_html_escape, to_native
from gluon.portalocker import read_locked, LockedFile from gluon.portalocker import read_locked, LockedFile
from gluon.utf8 import Utf8 from gluon.utf8 import Utf8
@@ -426,7 +426,7 @@ class lazyT(object):
return len(str(self)) return len(str(self))
def xml(self): def xml(self):
return str(self) if self.M else escape(str(self)) return str(self) if self.M else _local_html_escape(str(self), quote=False)
def encode(self, *a, **b): def encode(self, *a, **b):
return str(self).encode(*a, **b) return str(self).encode(*a, **b)
@@ -821,7 +821,7 @@ class translator(object):
self.language_file != self.default_language_file: self.language_file != self.default_language_file:
write_dict(self.language_file, self.t) write_dict(self.language_file, self.t)
return regex_backslash.sub( return regex_backslash.sub(
lambda m: m.group(1).translate(ttab_in), str(mt)) lambda m: m.group(1).translate(ttab_in), to_native(mt))
def params_substitution(self, message, symbols): def params_substitution(self, message, symbols):
""" """
+2
View File
@@ -83,6 +83,8 @@ def custom_json(o):
return int(o) return int(o)
elif isinstance(o, decimal.Decimal): elif isinstance(o, decimal.Decimal):
return str(o) return str(o)
elif isinstance(o, (bytes, bytearray)):
return str(o)
elif isinstance(o, lazyT): elif isinstance(o, lazyT):
return str(o) return str(o)
elif isinstance(o, XmlComponent): elif isinstance(o, XmlComponent):
+4 -4
View File
@@ -19,7 +19,7 @@ import urllib
import re import re
import os import os
from gluon._compat import StringIO, unichr, urllib_quote, iteritems from gluon._compat import StringIO, unichr, urllib_quote, iteritems, basestring, long, unicodeT
from gluon.http import HTTP, redirect from gluon.http import HTTP, redirect
from gluon.html import XmlComponent, truncate_string from gluon.html import XmlComponent, truncate_string
from gluon.html import XML, SPAN, TAG, A, DIV, CAT, UL, LI, TEXTAREA, BR, IMG from gluon.html import XML, SPAN, TAG, A, DIV, CAT, UL, LI, TEXTAREA, BR, IMG
@@ -1141,7 +1141,7 @@ class SQLFORM(FORM):
# try to retrieve the indicated record using its id # try to retrieve the indicated record using its id
# otherwise ignore it # otherwise ignore it
if record and isinstance(record, (int, long, str, unicode)): if record and isinstance(record, (int, long, str, unicodeT)):
if not str(record).isdigit(): if not str(record).isdigit():
raise HTTP(404, "Object not found") raise HTTP(404, "Object not found")
record = table._db(table._id == record).select().first() record = table._db(table._id == record).select().first()
@@ -1651,7 +1651,7 @@ class SQLFORM(FORM):
original_filename = os.path.split(f)[1] original_filename = os.path.split(f)[1]
elif hasattr(f, 'file'): elif hasattr(f, 'file'):
(source_file, original_filename) = (f.file, f.filename) (source_file, original_filename) = (f.file, f.filename)
elif isinstance(f, (str, unicode)): elif isinstance(f, (str, unicodeT)):
# do not know why this happens, it should not # do not know why this happens, it should not
(source_file, original_filename) = \ (source_file, original_filename) = \
(StringIO(f), 'file.txt') (StringIO(f), 'file.txt')
@@ -3426,7 +3426,7 @@ class ExportClass(object):
""" """
if value is None: if value is None:
return '<NULL>' return '<NULL>'
elif isinstance(value, unicode): elif isinstance(value, unicodeT):
return value.encode('utf8') return value.encode('utf8')
elif isinstance(value, Reference): elif isinstance(value, Reference):
return int(value) return int(value)
+2 -2
View File
@@ -14,14 +14,14 @@ from .test_contribs import *
from .test_routes import * from .test_routes import *
from .test_router import * from .test_router import *
from .test_validators import * from .test_validators import *
from .test_tools import *
if sys.version[:3] == '2.7': if sys.version[:3] == '2.7':
from .test_compileapp import * from .test_compileapp import *
from .test_is_url import * from .test_is_url import *
from .test_languages import * from .test_languages import *
from .test_serializers import * from .test_serializers import *
from .test_utils import * from .test_utils import *
from .test_tools import *
from .test_appadmin import * from .test_appadmin import *
from .test_scheduler import * from .test_scheduler import *
from .test_web import * from .test_web import *
+7 -7
View File
@@ -547,7 +547,7 @@ class TestAuth(unittest.TestCase):
def test_basic_blank_forms(self): def test_basic_blank_forms(self):
for f in ['login', 'retrieve_password', 'retrieve_username', 'register']: for f in ['login', 'retrieve_password', 'retrieve_username', 'register']:
html_form = getattr(self.auth, f)().xml() html_form = getattr(self.auth, f)().xml()
self.assertTrue('name="_formkey"' in html_form) self.assertTrue(b'name="_formkey"' in html_form)
for f in ['logout', 'verify_email', 'reset_password', 'change_password', 'profile', 'groups']: for f in ['logout', 'verify_email', 'reset_password', 'change_password', 'profile', 'groups']:
self.assertRaisesRegexp(HTTP, "303*", getattr(self.auth, f)) self.assertRaisesRegexp(HTTP, "303*", getattr(self.auth, f))
@@ -712,7 +712,7 @@ class TestAuth(unittest.TestCase):
self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare() self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare()
self.auth.settings.bulk_register_enabled = True self.auth.settings.bulk_register_enabled = True
bulk_register_form = self.auth.bulk_register(max_emails=10).xml() bulk_register_form = self.auth.bulk_register(max_emails=10).xml()
self.assertTrue('name="_formkey"' in bulk_register_form) self.assertTrue(b'name="_formkey"' in bulk_register_form)
# TODO: def test_manage_tokens(self): # TODO: def test_manage_tokens(self):
# TODO: def test_reset_password(self): # TODO: def test_reset_password(self):
@@ -723,12 +723,12 @@ class TestAuth(unittest.TestCase):
def test_change_password(self): def test_change_password(self):
self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare() self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare()
change_password_form = getattr(self.auth, 'change_password')().xml() change_password_form = getattr(self.auth, 'change_password')().xml()
self.assertTrue('name="_formkey"' in change_password_form) self.assertTrue(b'name="_formkey"' in change_password_form)
def test_profile(self): def test_profile(self):
self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare() self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare()
profile_form = getattr(self.auth, 'profile')().xml() profile_form = getattr(self.auth, 'profile')().xml()
self.assertTrue('name="_formkey"' in profile_form) self.assertTrue(b'name="_formkey"' in profile_form)
# TODO: def test_run_login_onaccept(self): # TODO: def test_run_login_onaccept(self):
# TODO: def test_jwt(self): # TODO: def test_jwt(self):
@@ -766,7 +766,7 @@ class TestAuth(unittest.TestCase):
# basic impersonate() test that return a read form # basic impersonate() test that return a read form
self.assertEqual(self.auth.impersonate().xml(), self.assertEqual(self.auth.impersonate().xml(),
'<form action="#" enctype="multipart/form-data" method="post"><table><tr id="no_table_user_id__row"><td class="w2p_fl"><label class="" for="no_table_user_id" id="no_table_user_id__label">User Id: </label></td><td class="w2p_fw"><input class="integer" id="no_table_user_id" name="user_id" type="text" value="" /></td><td class="w2p_fc"></td></tr><tr id="submit_record__row"><td class="w2p_fl"></td><td class="w2p_fw"><input type="submit" value="Submit" /></td><td class="w2p_fc"></td></tr></table></form>') b'<form action="#" enctype="multipart/form-data" method="post"><table><tr id="no_table_user_id__row"><td class="w2p_fl"><label class="" for="no_table_user_id" id="no_table_user_id__label">User Id: </label></td><td class="w2p_fw"><input class="integer" id="no_table_user_id" name="user_id" type="text" value="" /></td><td class="w2p_fc"></td></tr><tr id="submit_record__row"><td class="w2p_fl"></td><td class="w2p_fw"><input type="submit" value="Submit" /></td><td class="w2p_fc"></td></tr></table></form>')
# bart impersonate itself # bart impersonate itself
self.assertEqual(self.auth.impersonate(bart_id), None) self.assertEqual(self.auth.impersonate(bart_id), None)
self.assertFalse(self.auth.is_impersonating()) # User shouldn't impersonate itself? self.assertFalse(self.auth.is_impersonating()) # User shouldn't impersonate itself?
@@ -776,7 +776,7 @@ class TestAuth(unittest.TestCase):
self.assertTrue(self.auth.is_impersonating()) self.assertTrue(self.auth.is_impersonating())
self.assertEqual(self.auth.user_id, omer_id) # we make it really sure self.assertEqual(self.auth.user_id, omer_id) # we make it really sure
self.assertEqual(impersonate_form.xml(), self.assertEqual(impersonate_form.xml(),
'<form action="#" enctype="multipart/form-data" method="post"><table><tr id="auth_user_id__row"><td class="w2p_fl"><label class="readonly" for="auth_user_id" id="auth_user_id__label">Id: </label></td><td class="w2p_fw"><span id="auth_user_id">2</span></td><td class="w2p_fc"></td></tr><tr id="auth_user_first_name__row"><td class="w2p_fl"><label class="readonly" for="auth_user_first_name" id="auth_user_first_name__label">First name: </label></td><td class="w2p_fw">Omer</td><td class="w2p_fc"></td></tr><tr id="auth_user_last_name__row"><td class="w2p_fl"><label class="readonly" for="auth_user_last_name" id="auth_user_last_name__label">Last name: </label></td><td class="w2p_fw">Simpson</td><td class="w2p_fc"></td></tr><tr id="auth_user_email__row"><td class="w2p_fl"><label class="readonly" for="auth_user_email" id="auth_user_email__label">E-mail: </label></td><td class="w2p_fw">omer@test.com</td><td class="w2p_fc"></td></tr><tr id="auth_user_username__row"><td class="w2p_fl"><label class="readonly" for="auth_user_username" id="auth_user_username__label">Username: </label></td><td class="w2p_fw">omer</td><td class="w2p_fc"></td></tr></table><div style="display:none;"><input name="id" type="hidden" value="2" /></div></form>') b'<form action="#" enctype="multipart/form-data" method="post"><table><tr id="auth_user_id__row"><td class="w2p_fl"><label class="readonly" for="auth_user_id" id="auth_user_id__label">Id: </label></td><td class="w2p_fw"><span id="auth_user_id">2</span></td><td class="w2p_fc"></td></tr><tr id="auth_user_first_name__row"><td class="w2p_fl"><label class="readonly" for="auth_user_first_name" id="auth_user_first_name__label">First name: </label></td><td class="w2p_fw">Omer</td><td class="w2p_fc"></td></tr><tr id="auth_user_last_name__row"><td class="w2p_fl"><label class="readonly" for="auth_user_last_name" id="auth_user_last_name__label">Last name: </label></td><td class="w2p_fw">Simpson</td><td class="w2p_fc"></td></tr><tr id="auth_user_email__row"><td class="w2p_fl"><label class="readonly" for="auth_user_email" id="auth_user_email__label">E-mail: </label></td><td class="w2p_fw">omer@test.com</td><td class="w2p_fc"></td></tr><tr id="auth_user_username__row"><td class="w2p_fl"><label class="readonly" for="auth_user_username" id="auth_user_username__label">Username: </label></td><td class="w2p_fw">omer</td><td class="w2p_fc"></td></tr></table><div style="display:none;"><input name="id" type="hidden" value="2" /></div></form>')
self.auth.logout_bare() self.auth.logout_bare()
# Failing impersonation # Failing impersonation
# User lacking impersonate membership # User lacking impersonate membership
@@ -800,7 +800,7 @@ class TestAuth(unittest.TestCase):
def test_groups(self): def test_groups(self):
self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare() self.auth.login_user(self.db(self.db.auth_user.username == 'bart').select().first()) # bypass login_bare()
self.assertEqual(self.auth.groups().xml(), self.assertEqual(self.auth.groups().xml(),
'<table><tr><td><h3>user_1(1)</h3></td></tr><tr><td><p></p></td></tr></table>') b'<table><tr><td><h3>user_1(1)</h3></td></tr><tr><td><p></p></td></tr></table>')
def test_not_authorized(self): def test_not_authorized(self):
self.current.request.ajax = 'facke_ajax_request' self.current.request.ajax = 'facke_ajax_request'
+53 -58
View File
@@ -13,7 +13,8 @@ Auth, Mail, PluginManager and various utilities
import base64 import base64
from functools import reduce from functools import reduce
from gluon._compat import pickle, thread, urllib2, Cookie, StringIO, configparser, MIMEBase, MIMEMultipart, \ from gluon._compat import pickle, thread, urllib2, Cookie, StringIO, configparser, MIMEBase, MIMEMultipart, \
MIMEText, Encoders, Charset, long, urllib_quote, iteritems MIMEText, Encoders, Charset, long, urllib_quote, iteritems, to_bytes, to_native, add_charset, \
charset_QP, basestring, unicodeT, to_unicode
import datetime import datetime
import logging import logging
import sys import sys
@@ -241,6 +242,7 @@ class Mail(object):
if filename is None: if filename is None:
raise Exception('Missing attachment name') raise Exception('Missing attachment name')
payload = payload.read() payload = payload.read()
#FIXME PY3 can be used to_native?
filename = filename.encode(encoding) filename = filename.encode(encoding)
if content_type is None: if content_type is None:
content_type = contenttype(filename) content_type = contenttype(filename)
@@ -248,9 +250,9 @@ class Mail(object):
self.my_payload = payload self.my_payload = payload
MIMEBase.__init__(self, *content_type.split('/', 1)) MIMEBase.__init__(self, *content_type.split('/', 1))
self.set_payload(payload) self.set_payload(payload)
self['Content-Disposition'] = 'attachment; filename="%s"' % filename self['Content-Disposition'] = 'attachment; filename="%s"' % to_native(filename, encoding)
if content_id is not None: if content_id is not None:
self['Content-Id'] = '<%s>' % content_id.encode(encoding) self['Content-Id'] = '<%s>' % to_native(content_id, encoding)
Encoders.encode_base64(self) Encoders.encode_base64(self)
def __init__(self, server=None, sender=None, login=None, tls=True): def __init__(self, server=None, sender=None, login=None, tls=True):
@@ -405,7 +407,7 @@ class Mail(object):
""" """
# We don't want to use base64 encoding for unicode mail # We don't want to use base64 encoding for unicode mail
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') add_charset('utf-8', charset_QP, charset_QP, 'utf-8')
def encode_header(key): def encode_header(key):
if [c for c in key if 32 > ord(c) or ord(c) > 127]: if [c for c in key if 32 > ord(c) or ord(c) > 127]:
@@ -428,12 +430,12 @@ class Mail(object):
if not raw and attachments: if not raw and attachments:
# Use multipart/mixed if there is attachments # Use multipart/mixed if there is attachments
payload_in = MIMEMultipart.MIMEMultipart('mixed') payload_in = MIMEMultipart('mixed')
elif raw: elif raw:
# no encoding configuration for raw messages # no encoding configuration for raw messages
if not isinstance(message, basestring): if not isinstance(message, basestring):
message = message.read() message = message.read()
if isinstance(message, unicode): if isinstance(message, unicodeT):
text = message.encode('utf-8') text = message.encode('utf-8')
elif not encoding == 'utf-8': elif not encoding == 'utf-8':
text = message.decode(encoding).encode('utf-8') text = message.decode(encoding).encode('utf-8')
@@ -442,7 +444,7 @@ class Mail(object):
# No charset passed to avoid transport encoding # No charset passed to avoid transport encoding
# NOTE: some unicode encoded strings will produce # NOTE: some unicode encoded strings will produce
# unreadable mail contents. # unreadable mail contents.
payload_in = MIMEText.MIMEText(text) payload_in = MIMEText(text)
if to: if to:
if not isinstance(to, (list, tuple)): if not isinstance(to, (list, tuple)):
to = [to] to = [to]
@@ -471,14 +473,14 @@ class Mail(object):
if text is not None: if text is not None:
if not isinstance(text, basestring): if not isinstance(text, basestring):
text = text.read() text = text.read()
if isinstance(text, unicode): if isinstance(text, unicodeT):
text = text.encode('utf-8') text = text.encode('utf-8')
elif not encoding == 'utf-8': elif not encoding == 'utf-8':
text = text.decode(encoding).encode('utf-8') text = text.decode(encoding).encode('utf-8')
if html is not None: if html is not None:
if not isinstance(html, basestring): if not isinstance(html, basestring):
html = html.read() html = html.read()
if isinstance(html, unicode): if isinstance(html, unicodeT):
html = html.encode('utf-8') html = html.encode('utf-8')
elif not encoding == 'utf-8': elif not encoding == 'utf-8':
html = html.decode(encoding).encode('utf-8') html = html.decode(encoding).encode('utf-8')
@@ -486,15 +488,13 @@ class Mail(object):
# Construct mime part only if needed # Construct mime part only if needed
if text is not None and html: if text is not None and html:
# We have text and html we need multipart/alternative # We have text and html we need multipart/alternative
attachment = MIMEMultipart.MIMEMultipart('alternative') attachment = MIMEMultipart('alternative')
attachment.attach(MIMEText.MIMEText(text, _charset='utf-8')) attachment.attach(MIMEText(text, _charset='utf-8'))
attachment.attach( attachment.attach(MIMEText(html, 'html', _charset='utf-8'))
MIMEText.MIMEText(html, 'html', _charset='utf-8'))
elif text is not None: elif text is not None:
attachment = MIMEText.MIMEText(text, _charset='utf-8') attachment = MIMEText(text, _charset='utf-8')
elif html: elif html:
attachment = \ attachment = MIMEText(html, 'html', _charset='utf-8')
MIMEText.MIMEText(html, 'html', _charset='utf-8')
if attachments: if attachments:
# If there is attachments put text and html into # If there is attachments put text and html into
@@ -560,12 +560,11 @@ class Mail(object):
c.op_sign(plain, sig, mode.DETACH) c.op_sign(plain, sig, mode.DETACH)
sig.seek(0, 0) sig.seek(0, 0)
# make it part of the email # make it part of the email
payload = \ payload = MIMEMultipart('signed',
MIMEMultipart.MIMEMultipart('signed', boundary=None,
boundary=None, _subparts=None,
_subparts=None, **dict(micalg="pgp-sha1",
**dict(micalg="pgp-sha1", protocol="application/pgp-signature"))
protocol="application/pgp-signature"))
# insert the origin payload # insert the origin payload
payload.attach(payload_in) payload.attach(payload_in)
# insert the detached signature # insert the detached signature
@@ -605,10 +604,10 @@ class Mail(object):
c.op_encrypt(recipients, 1, plain, cipher) c.op_encrypt(recipients, 1, plain, cipher)
cipher.seek(0, 0) cipher.seek(0, 0)
# make it a part of the email # make it a part of the email
payload = MIMEMultipart.MIMEMultipart('encrypted', payload = MIMEMultipart('encrypted',
boundary=None, boundary=None,
_subparts=None, _subparts=None,
**dict(protocol="application/pgp-encrypted")) **dict(protocol="application/pgp-encrypted"))
p = MIMEBase("application", 'pgp-encrypted') p = MIMEBase("application", 'pgp-encrypted')
p.set_payload("Version: 1\r\n") p.set_payload("Version: 1\r\n")
payload.attach(p) payload.attach(p)
@@ -729,29 +728,29 @@ class Mail(object):
payload = payload_in payload = payload_in
if from_address: if from_address:
payload['From'] = encoded_or_raw(from_address.decode(encoding)) payload['From'] = encoded_or_raw(to_unicode(from_address, encoding))
else: else:
payload['From'] = encoded_or_raw(sender.decode(encoding)) payload['From'] = encoded_or_raw(to_unicode(sender, encoding))
origTo = to[:] origTo = to[:]
if to: if to:
payload['To'] = encoded_or_raw(', '.join(to).decode(encoding)) payload['To'] = encoded_or_raw(to_unicode(', '.join(to), encoding))
if reply_to: if reply_to:
payload['Reply-To'] = encoded_or_raw(reply_to.decode(encoding)) payload['Reply-To'] = encoded_or_raw(to_unicode(reply_to, encoding))
if cc: if cc:
payload['Cc'] = encoded_or_raw(', '.join(cc).decode(encoding)) payload['Cc'] = encoded_or_raw(to_unicode(', '.join(cc), encoding))
to.extend(cc) to.extend(cc)
if bcc: if bcc:
to.extend(bcc) to.extend(bcc)
payload['Subject'] = encoded_or_raw(subject.decode(encoding)) payload['Subject'] = encoded_or_raw(to_unicode(subject, encoding))
payload['Date'] = email.utils.formatdate() payload['Date'] = email.utils.formatdate()
for k, v in iteritems(headers): for k, v in iteritems(headers):
payload[k] = encoded_or_raw(v.decode(encoding)) payload[k] = encoded_or_raw(to_unicode(v, encoding))
result = {} result = {}
try: try:
if self.settings.server == 'logging': if self.settings.server == 'logging':
entry = 'email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \ entry = 'email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \
('-' * 40, sender, ', '.join(to), subject, text or html, '-' * 40) ('-' * 40, sender, ', '.join(to), subject, text or html, '-' * 40)
logger.warn(entry) logger.warning(entry)
elif self.settings.server.startswith('logging:'): elif self.settings.server.startswith('logging:'):
entry = 'email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \ entry = 'email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \
('-' * 40, sender, ', '.join(to), subject, text or html, '-' * 40) ('-' * 40, sender, ', '.join(to), subject, text or html, '-' * 40)
@@ -773,16 +772,16 @@ class Mail(object):
if attachments: if attachments:
result = mail.send_mail( result = mail.send_mail(
sender=sender, to=origTo, sender=sender, to=origTo,
subject=unicode(subject, encoding), body=unicode(text, encoding), html=html, subject=to_unicode(subject, encoding), body=to_unicode(text, encoding), html=html,
attachments=attachments, **xcc) attachments=attachments, **xcc)
elif html and (not raw): elif html and (not raw):
result = mail.send_mail( result = mail.send_mail(
sender=sender, to=origTo, sender=sender, to=origTo,
subject=unicode(subject, encoding), body=unicode(text, encoding), html=html, **xcc) subject=to_unicode(subject, encoding), body=to_unicode(text, encoding), html=html, **xcc)
else: else:
result = mail.send_mail( result = mail.send_mail(
sender=sender, to=origTo, sender=sender, to=origTo,
subject=unicode(subject, encoding), body=unicode(text, encoding), **xcc) subject=to_unicode(subject, encoding), body=to_unicode(text, encoding), **xcc)
else: else:
smtp_args = self.settings.server.split(':') smtp_args = self.settings.server.split(':')
kwargs = dict(timeout=self.settings.timeout) kwargs = dict(timeout=self.settings.timeout)
@@ -800,7 +799,7 @@ class Mail(object):
sender, to, payload.as_string()) sender, to, payload.as_string())
server.quit() server.quit()
except Exception as e: except Exception as e:
logger.warn('Mail.send failure:%s' % e) logger.warning('Mail.send failure:%s' % e)
self.result = result self.result = result
self.error = e self.error = e
return False return False
@@ -1250,8 +1249,7 @@ class AuthJWT(object):
@staticmethod @staticmethod
def jwt_b64e(string): def jwt_b64e(string):
if isinstance(string, unicode): string = to_bytes(string)
string = string.encode('utf-8', 'strict')
return base64.urlsafe_b64encode(string).strip(b'=') return base64.urlsafe_b64encode(string).strip(b'=')
@staticmethod @staticmethod
@@ -1260,47 +1258,44 @@ class AuthJWT(object):
called with a unicode string). called with a unicode string).
The result is also a bytestring. The result is also a bytestring.
""" """
if isinstance(string, unicode): string = to_bytes(string, 'ascii', 'ignore')
string = string.encode('ascii', 'ignore') return base64.urlsafe_b64decode(string + b'=' * (-len(string) % 4))
return base64.urlsafe_b64decode(string + '=' * (-len(string) % 4))
def generate_token(self, payload): def generate_token(self, payload):
secret = self.secret_key secret = to_bytes(self.secret_key)
if self.salt: if self.salt:
if callable(self.salt): if callable(self.salt):
secret = "%s$%s" % (secret, self.salt(payload)) secret = "%s$%s" % (secret, self.salt(payload))
else: else:
secret = "%s$%s" % (secret, self.salt) secret = "%s$%s" % (secret, self.salt)
if isinstance(secret, unicode): if isinstance(secret, unicodeT):
secret = secret.encode('ascii', 'ignore') secret = secret.encode('ascii', 'ignore')
b64h = self.cached_b64h b64h = self.cached_b64h
b64p = self.jwt_b64e(serializers.json(payload)) b64p = self.jwt_b64e(serializers.json(payload))
jbody = b64h + '.' + b64p jbody = b64h + b'.' + b64p
mauth = hmac.new(key=secret, msg=jbody, digestmod=self.digestmod) mauth = hmac.new(key=secret, msg=jbody, digestmod=self.digestmod)
jsign = self.jwt_b64e(mauth.digest()) jsign = self.jwt_b64e(mauth.digest())
return jbody + '.' + jsign return to_native(jbody + b'.' + jsign)
def verify_signature(self, body, signature, secret): def verify_signature(self, body, signature, secret):
mauth = hmac.new(key=secret, msg=body, digestmod=self.digestmod) mauth = hmac.new(key=secret, msg=body, digestmod=self.digestmod)
return compare(self.jwt_b64e(mauth.digest()), signature) return compare(self.jwt_b64e(mauth.digest()), signature)
def load_token(self, token): def load_token(self, token):
if isinstance(token, unicode): token = to_bytes(token, 'utf-8', 'strict')
token = token.encode('utf-8', 'strict') body, sig = token.rsplit(b'.', 1)
body, sig = token.rsplit('.', 1) b64h, b64b = body.split(b'.', 1)
b64h, b64b = body.split('.', 1)
if b64h != self.cached_b64h: if b64h != self.cached_b64h:
# header not the same # header not the same
raise HTTP(400, u'Invalid JWT Header') raise HTTP(400, u'Invalid JWT Header')
secret = self.secret_key secret = self.secret_key
tokend = serializers.loads_json(self.jwt_b64d(b64b)) tokend = serializers.loads_json(to_native(self.jwt_b64d(b64b)))
if self.salt: if self.salt:
if callable(self.salt): if callable(self.salt):
secret = "%s$%s" % (secret, self.salt(tokend)) secret = "%s$%s" % (secret, self.salt(tokend))
else: else:
secret = "%s$%s" % (secret, self.salt) secret = "%s$%s" % (secret, self.salt)
if isinstance(secret, unicode): secret = to_bytes(secret, 'ascii', 'ignore')
secret = secret.encode('ascii', 'ignore')
if not self.verify_signature(body, sig, secret): if not self.verify_signature(body, sig, secret):
# signature verification failed # signature verification failed
raise HTTP(400, u'Token signature is invalid') raise HTTP(400, u'Token signature is invalid')
@@ -3560,8 +3555,8 @@ class Auth(object):
password = '' password = ''
specials = r'!#$*' specials = r'!#$*'
for i in range(0, 3): for i in range(0, 3):
password += random.choice(string.lowercase) password += random.choice(string.ascii_lowercase)
password += random.choice(string.uppercase) password += random.choice(string.ascii_uppercase)
password += random.choice(string.digits) password += random.choice(string.digits)
password += random.choice(specials) password += random.choice(specials)
return ''.join(random.sample(password, len(password))) return ''.join(random.sample(password, len(password)))
@@ -3994,7 +3989,7 @@ class Auth(object):
requires = table_user[passfield].requires requires = table_user[passfield].requires
if not isinstance(requires, (list, tuple)): if not isinstance(requires, (list, tuple)):
requires = [requires] requires = [requires]
requires = filter(lambda t: isinstance(t, CRYPT), requires) requires = list(filter(lambda t: isinstance(t, CRYPT), requires))
if requires: if requires:
requires[0].min_length = 0 requires[0].min_length = 0
form = SQLFORM.factory( form = SQLFORM.factory(
@@ -5621,7 +5616,7 @@ class Service(object):
args = request.args args = request.args
def none_exception(value): def none_exception(value):
if isinstance(value, unicode): if isinstance(value, unicodeT):
return value.encode('utf8') return value.encode('utf8')
if hasattr(value, 'isoformat'): if hasattr(value, 'isoformat'):
return value.isoformat()[:19].replace('T', ' ') return value.isoformat()[:19].replace('T', ' ')