Move stringprep and idna support in a different module than slixmpp.jid.
This commit is contained in:
		
				
					committed by
					
						
						Emmanuel Gil Peyrot
					
				
			
			
				
	
			
			
			
						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.')
 | 
				
			||||||
		Reference in New Issue
	
	Block a user