summaryrefslogtreecommitdiff
path: root/slixmpp/stringprep.pyx
blob: e751c8ea7634cafca74471d4dae8e8df50268f13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# -*- coding: utf-8 -*-
# cython: language_level = 3
# distutils: libraries = idn
"""
    slixmpp.stringprep
    ~~~~~~~~~~~~~~~~~~~~~~~

    This module wraps libidn’s stringprep and idna functions using Cython.

    Part of Slixmpp: The Slick XMPP Library

    :copyright: (c) 2015 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
    :license: MIT, see LICENSE for more details
"""

from libc.stdlib cimport free


# Those are Cython declarations for the C function we’ll be using.

cdef extern from "stringprep.h" nogil:
    int stringprep_profile(const char* in_, char** out, const char* profile,
                           int flags)

cdef extern from "idna.h" nogil:
    int idna_to_ascii_8z(const char* in_, char** out, int flags)
    int idna_to_unicode_8z8z(const char* in_, char** out, int flags)


class StringprepError(Exception):
    pass


cdef str _stringprep(str in_, const char* profile):
    """Python wrapper for libidn’s stringprep."""
    cdef char* out
    ret = stringprep_profile(in_.encode('utf-8'), &out, profile, 0)
    if ret != 0:
        raise StringprepError(ret)
    unicode_out = out.decode('utf-8')
    free(out)
    return unicode_out


def nodeprep(str node):
    """The nodeprep profile of stringprep used to validate the local, or
    username, portion of a JID."""
    return _stringprep(node, 'Nodeprep')


def resourceprep(str resource):
    """The resourceprep profile of stringprep, which is used to validate the
    resource portion of a JID."""
    return _stringprep(resource, 'Resourceprep')


def idna(str domain):
    """The idna conversion functions, which are used to validate the domain
    portion of a JID."""

    cdef char* ascii_domain
    cdef char* utf8_domain

    ret = idna_to_ascii_8z(domain.encode('utf-8'), &ascii_domain, 0)
    if ret != 0:
        raise StringprepError(ret)

    ret = idna_to_unicode_8z8z(ascii_domain, &utf8_domain, 0)
    free(ascii_domain)
    if ret != 0:
        raise StringprepError(ret)

    unicode_domain = utf8_domain.decode('utf-8')
    free(utf8_domain)
    return unicode_domain


def punycode(str domain):
    """Converts a domain name to its punycode representation."""

    cdef char* ascii_domain
    cdef bytes bytes_domain

    ret = idna_to_ascii_8z(domain.encode('utf-8'), &ascii_domain, 0)
    if ret != 0:
        raise StringprepError(ret)
    bytes_domain = ascii_domain
    free(ascii_domain)
    return bytes_domain