Move stringprep and idna support in a different module than slixmpp.jid.
This commit is contained in:
parent
c29fc39ef1
commit
bbce16d526
@ -15,18 +15,10 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import stringprep
|
|
||||||
import encodings.idna
|
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from slixmpp.util import stringprep_profiles
|
from slixmpp.stringprep import nodeprep, resourceprep, idna, StringprepError
|
||||||
|
|
||||||
#: These characters are not allowed to appear in a JID.
|
|
||||||
ILLEGAL_CHARS = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r' + \
|
|
||||||
'\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19' + \
|
|
||||||
'\x1a\x1b\x1c\x1d\x1e\x1f' + \
|
|
||||||
' !"#$%&\'()*+,./:;<=>?@[\\]^_`{|}~\x7f'
|
|
||||||
|
|
||||||
HAVE_INET_PTON = hasattr(socket, 'inet_pton')
|
HAVE_INET_PTON = hasattr(socket, 'inet_pton')
|
||||||
|
|
||||||
@ -67,50 +59,6 @@ JID_UNESCAPE_TRANSFORMATIONS = {'\\20': ' ',
|
|||||||
'\\40': '@',
|
'\\40': '@',
|
||||||
'\\5c': '\\'}
|
'\\5c': '\\'}
|
||||||
|
|
||||||
# pylint: disable=c0103
|
|
||||||
#: The nodeprep profile of stringprep used to validate the local,
|
|
||||||
#: or username, portion of a JID.
|
|
||||||
nodeprep = stringprep_profiles.create(
|
|
||||||
nfkc=True,
|
|
||||||
bidi=True,
|
|
||||||
mappings=[
|
|
||||||
stringprep_profiles.b1_mapping,
|
|
||||||
stringprep.map_table_b2],
|
|
||||||
prohibited=[
|
|
||||||
stringprep.in_table_c11,
|
|
||||||
stringprep.in_table_c12,
|
|
||||||
stringprep.in_table_c21,
|
|
||||||
stringprep.in_table_c22,
|
|
||||||
stringprep.in_table_c3,
|
|
||||||
stringprep.in_table_c4,
|
|
||||||
stringprep.in_table_c5,
|
|
||||||
stringprep.in_table_c6,
|
|
||||||
stringprep.in_table_c7,
|
|
||||||
stringprep.in_table_c8,
|
|
||||||
stringprep.in_table_c9,
|
|
||||||
lambda c: c in ' \'"&/:<>@'],
|
|
||||||
unassigned=[stringprep.in_table_a1])
|
|
||||||
|
|
||||||
# pylint: disable=c0103
|
|
||||||
#: The resourceprep profile of stringprep, which is used to validate
|
|
||||||
#: the resource portion of a JID.
|
|
||||||
resourceprep = stringprep_profiles.create(
|
|
||||||
nfkc=True,
|
|
||||||
bidi=True,
|
|
||||||
mappings=[stringprep_profiles.b1_mapping],
|
|
||||||
prohibited=[
|
|
||||||
stringprep.in_table_c12,
|
|
||||||
stringprep.in_table_c21,
|
|
||||||
stringprep.in_table_c22,
|
|
||||||
stringprep.in_table_c3,
|
|
||||||
stringprep.in_table_c4,
|
|
||||||
stringprep.in_table_c5,
|
|
||||||
stringprep.in_table_c6,
|
|
||||||
stringprep.in_table_c7,
|
|
||||||
stringprep.in_table_c8,
|
|
||||||
stringprep.in_table_c9],
|
|
||||||
unassigned=[stringprep.in_table_a1])
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_jid(data):
|
def _parse_jid(data):
|
||||||
"""
|
"""
|
||||||
@ -143,17 +91,17 @@ def _validate_node(node):
|
|||||||
|
|
||||||
:returns: The local portion of a JID, as validated by nodeprep.
|
:returns: The local portion of a JID, as validated by nodeprep.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
if node is not None:
|
if node is not None:
|
||||||
|
try:
|
||||||
node = nodeprep(node)
|
node = nodeprep(node)
|
||||||
|
except StringprepError:
|
||||||
|
raise InvalidJID('Nodeprep failed')
|
||||||
|
|
||||||
if not node:
|
if not node:
|
||||||
raise InvalidJID('Localpart must not be 0 bytes')
|
raise InvalidJID('Localpart must not be 0 bytes')
|
||||||
if len(node) > 1023:
|
if len(node) > 1023:
|
||||||
raise InvalidJID('Localpart must be less than 1024 bytes')
|
raise InvalidJID('Localpart must be less than 1024 bytes')
|
||||||
return node
|
return node
|
||||||
except stringprep_profiles.StringPrepError:
|
|
||||||
raise InvalidJID('Invalid local part')
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_domain(domain):
|
def _validate_domain(domain):
|
||||||
@ -194,31 +142,19 @@ def _validate_domain(domain):
|
|||||||
if domain and domain[-1] == '.':
|
if domain and domain[-1] == '.':
|
||||||
domain = domain[:-1]
|
domain = domain[:-1]
|
||||||
|
|
||||||
domain_parts = []
|
|
||||||
for label in domain.split('.'):
|
|
||||||
try:
|
try:
|
||||||
label = encodings.idna.nameprep(label)
|
domain = idna(domain)
|
||||||
encodings.idna.ToASCII(label)
|
except StringprepError:
|
||||||
pass_nameprep = True
|
raise InvalidJID('idna validation failed')
|
||||||
except UnicodeError:
|
|
||||||
pass_nameprep = False
|
|
||||||
|
|
||||||
if not pass_nameprep:
|
|
||||||
raise InvalidJID('Could not encode domain as ASCII')
|
|
||||||
|
|
||||||
if label.startswith('xn--'):
|
|
||||||
label = encodings.idna.ToUnicode(label)
|
|
||||||
|
|
||||||
for char in label:
|
|
||||||
if char in ILLEGAL_CHARS:
|
|
||||||
raise InvalidJID('Domain contains illegal characters')
|
|
||||||
|
|
||||||
|
if ':' in domain:
|
||||||
|
raise InvalidJID('Domain containing a port')
|
||||||
|
for label in domain.split('.'):
|
||||||
|
if not label:
|
||||||
|
raise InvalidJID('Domain containing too many dots')
|
||||||
if '-' in (label[0], label[-1]):
|
if '-' in (label[0], label[-1]):
|
||||||
raise InvalidJID('Domain started or ended with -')
|
raise InvalidJID('Domain started or ended with -')
|
||||||
|
|
||||||
domain_parts.append(label)
|
|
||||||
domain = '.'.join(domain_parts)
|
|
||||||
|
|
||||||
if not domain:
|
if not domain:
|
||||||
raise InvalidJID('Domain must not be 0 bytes')
|
raise InvalidJID('Domain must not be 0 bytes')
|
||||||
if len(domain) > 1023:
|
if len(domain) > 1023:
|
||||||
@ -234,17 +170,17 @@ def _validate_resource(resource):
|
|||||||
|
|
||||||
:returns: The local portion of a JID, as validated by resourceprep.
|
:returns: The local portion of a JID, as validated by resourceprep.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
if resource is not None:
|
if resource is not None:
|
||||||
|
try:
|
||||||
resource = resourceprep(resource)
|
resource = resourceprep(resource)
|
||||||
|
except StringprepError:
|
||||||
|
raise InvalidJID('Resourceprep failed')
|
||||||
|
|
||||||
if not resource:
|
if not resource:
|
||||||
raise InvalidJID('Resource must not be 0 bytes')
|
raise InvalidJID('Resource must not be 0 bytes')
|
||||||
if len(resource) > 1023:
|
if len(resource) > 1023:
|
||||||
raise InvalidJID('Resource must be less than 1024 bytes')
|
raise InvalidJID('Resource must be less than 1024 bytes')
|
||||||
return resource
|
return resource
|
||||||
except stringprep_profiles.StringPrepError:
|
|
||||||
raise InvalidJID('Invalid resource')
|
|
||||||
|
|
||||||
|
|
||||||
def _escape_node(node):
|
def _escape_node(node):
|
||||||
|
105
slixmpp/stringprep.py
Normal file
105
slixmpp/stringprep.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
slixmpp.stringprep
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This module is a fallback using python’s stringprep instead of libidn’s.
|
||||||
|
|
||||||
|
Part of Slixmpp: The Slick XMPP Library
|
||||||
|
|
||||||
|
:copyright: (c) 2015 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||||
|
:license: MIT, see LICENSE for more details
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import stringprep
|
||||||
|
from slixmpp.util import stringprep_profiles
|
||||||
|
import encodings.idna
|
||||||
|
|
||||||
|
class StringprepError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
#: These characters are not allowed to appear in a domain part.
|
||||||
|
ILLEGAL_CHARS = ('\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r'
|
||||||
|
'\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19'
|
||||||
|
'\x1a\x1b\x1c\x1d\x1e\x1f'
|
||||||
|
' !"#$%&\'()*+,./:;<=>?@[\\]^_`{|}~\x7f')
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=c0103
|
||||||
|
#: The nodeprep profile of stringprep used to validate the local,
|
||||||
|
#: or username, portion of a JID.
|
||||||
|
_nodeprep = stringprep_profiles.create(
|
||||||
|
nfkc=True,
|
||||||
|
bidi=True,
|
||||||
|
mappings=[
|
||||||
|
stringprep_profiles.b1_mapping,
|
||||||
|
stringprep.map_table_b2],
|
||||||
|
prohibited=[
|
||||||
|
stringprep.in_table_c11,
|
||||||
|
stringprep.in_table_c12,
|
||||||
|
stringprep.in_table_c21,
|
||||||
|
stringprep.in_table_c22,
|
||||||
|
stringprep.in_table_c3,
|
||||||
|
stringprep.in_table_c4,
|
||||||
|
stringprep.in_table_c5,
|
||||||
|
stringprep.in_table_c6,
|
||||||
|
stringprep.in_table_c7,
|
||||||
|
stringprep.in_table_c8,
|
||||||
|
stringprep.in_table_c9,
|
||||||
|
lambda c: c in ' \'"&/:<>@'],
|
||||||
|
unassigned=[stringprep.in_table_a1])
|
||||||
|
|
||||||
|
def nodeprep(node):
|
||||||
|
try:
|
||||||
|
return _nodeprep(node)
|
||||||
|
except stringprep_profiles.StringPrepError:
|
||||||
|
raise StringprepError
|
||||||
|
|
||||||
|
# pylint: disable=c0103
|
||||||
|
#: The resourceprep profile of stringprep, which is used to validate
|
||||||
|
#: the resource portion of a JID.
|
||||||
|
_resourceprep = stringprep_profiles.create(
|
||||||
|
nfkc=True,
|
||||||
|
bidi=True,
|
||||||
|
mappings=[stringprep_profiles.b1_mapping],
|
||||||
|
prohibited=[
|
||||||
|
stringprep.in_table_c12,
|
||||||
|
stringprep.in_table_c21,
|
||||||
|
stringprep.in_table_c22,
|
||||||
|
stringprep.in_table_c3,
|
||||||
|
stringprep.in_table_c4,
|
||||||
|
stringprep.in_table_c5,
|
||||||
|
stringprep.in_table_c6,
|
||||||
|
stringprep.in_table_c7,
|
||||||
|
stringprep.in_table_c8,
|
||||||
|
stringprep.in_table_c9],
|
||||||
|
unassigned=[stringprep.in_table_a1])
|
||||||
|
|
||||||
|
def resourceprep(resource):
|
||||||
|
try:
|
||||||
|
return _resourceprep(resource)
|
||||||
|
except stringprep_profiles.StringPrepError:
|
||||||
|
raise StringprepError
|
||||||
|
|
||||||
|
def idna(domain):
|
||||||
|
domain_parts = []
|
||||||
|
for label in domain.split('.'):
|
||||||
|
try:
|
||||||
|
label = encodings.idna.nameprep(label)
|
||||||
|
encodings.idna.ToASCII(label)
|
||||||
|
except UnicodeError:
|
||||||
|
raise StringprepError
|
||||||
|
|
||||||
|
if label.startswith('xn--'):
|
||||||
|
label = encodings.idna.ToUnicode(label)
|
||||||
|
|
||||||
|
for char in label:
|
||||||
|
if char in ILLEGAL_CHARS:
|
||||||
|
raise StringprepError
|
||||||
|
|
||||||
|
domain_parts.append(label)
|
||||||
|
return '.'.join(domain_parts)
|
||||||
|
|
||||||
|
logging.getLogger(__name__).warning('Using slower stringprep, consider '
|
||||||
|
'compiling the faster cython/libidn one.')
|
Loading…
Reference in New Issue
Block a user