summaryrefslogtreecommitdiff
path: root/wrappers/python/eduvpn_common/types.py
blob: ca798cae3c67faf840bba9b22d6f2d5b11234b91 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
from ctypes import (
    CDLL,
    CFUNCTYPE,
    POINTER,
    Structure,
    byref,
    c_char,
    c_char_p,
    c_int,
    c_longlong,
    c_size_t,
    c_ulonglong,
    c_void_p,
    cast,
)
from typing import Any, Iterator, List, Optional, Tuple


class DataError(Structure):
    """The C type that represents a tuple of data and error (both strings) as returned by the Go library

    :meta private:
    """

    _fields_ = [("data", c_void_p), ("error", c_void_p)]


class HandlerError(Structure):
    """The C type that represents a tuple of a CGO handler and error (string) as returned by the Go library

    :meta private:
    """

    _fields_ = [("handler", c_int), ("error", c_void_p)]


class BoolError(Structure):
    """The C type that represents a tuple of boolean and error as returned by the Go library

    :meta private:
    """

    _fields_ = [("boolean", c_int), ("error", c_void_p)]


# The type for a Go state change callback
VPNStateChange = CFUNCTYPE(c_int, c_int, c_int, c_char_p)
ProxySetup = CFUNCTYPE(c_void_p, c_int)
ReadRxBytes = CFUNCTYPE(c_ulonglong)
TokenGetter = CFUNCTYPE(c_void_p, c_char_p, c_int, POINTER(c_char), c_size_t)
TokenSetter = CFUNCTYPE(c_void_p, c_char_p, c_int, c_char_p)


def conv_longlongp(val: Optional[int]) -> POINTER(c_longlong):
    if val is None:
        return None
    return byref(c_longlong(val))


def encode_args(args: List[Any], types: List[Any]) -> Iterator[Any]:
    """Encode the arguments ready to be used by the Go library

    :param args: List[Any]: The list of arguments
    :param types: List[Any]: The list of the types of the arguments

    :meta private:

    :return: The arg generator
    :rtype: Iterator[Any]
    """
    for arg, t in zip(args, types):
        # c_char_p needs the str to be encoded to bytes
        encode_map = {
            c_char_p: lambda x: x.encode("utf-8"),
            POINTER(c_longlong): conv_longlongp,
        }
        if t in encode_map:
            arg = encode_map[t](arg)
        yield arg


def decode_res(res: Any) -> Any:
    """Decode a result as obtained by the Go library

    :param res: Any: The result

    :meta private:

    :return: The argument decoded
    :rtype: Any
    """
    decode_map = {
        c_void_p: get_ptr_string,
        HandlerError: get_handler_error,
        DataError: get_data_error,
        BoolError: get_bool_error,
    }
    return decode_map.get(res, lambda lib, x: x)


def get_ptr_string(lib: CDLL, ptr: c_void_p) -> str:
    """Convert a C string pointer to a Python usable string.
    This makes sure to free all memory allocated by the Go library

    :param lib: CDLL: The Go shared library
    :param ptr: c_void_p: The pointer to the C string

    :meta private:

    :return: The string converted to Python
    :rtype: str
    """
    if ptr:
        string = cast(ptr, c_char_p).value
        lib.FreeString(ptr)
        if string:
            return string.decode("utf-8")
    return ""


def get_data_error(lib: CDLL, data_error: DataError) -> Tuple[str, str]:
    """Convert a C data+error structure to a Python usable data+error structure

    :param lib: CDLL: The Go shared library
    :param data_error: DataError: The data error C structure

    :meta private:

    :return: The data and error
    :rtype: Tuple[str, str]
    """
    data = get_ptr_string(lib, data_error.data)
    error = get_ptr_string(lib, data_error.error)
    return data, error


def get_handler_error(lib: CDLL, handler_error: HandlerError) -> Tuple[int, str]:
    """Convert a C handler+error structure to a Python usable handler+error structure

    :param lib: CDLL: The Go shared library
    :param handler_error: HandlerError: The handler error C structure

    :meta private:

    :return: The handler and error
    :rtype: Tuple[int, str]
    """
    handler = int(handler_error.handler)
    error = get_ptr_string(lib, handler_error.error)
    return handler, error


def get_bool_error(lib: CDLL, bool_error: BoolError) -> Tuple[bool, str]:
    """Convert a C boolean (c int)+error structure to a Python usable boolean+error structure

    :param lib: CDLL: The Go shared library
    :param bool_error: BoolError: The boolean and error C structure

    :meta private:

    :return: The bool and error
    :rtype: Tuple[bool, str]
    """
    boolean = get_bool(lib, bool_error.boolean)
    error = get_ptr_string(lib, bool_error.error)
    return boolean, error


def get_bool(lib: CDLL, boolInt: c_int) -> bool:
    """Get a bool from the Go shared library. Essentially just checking if an int represents 'True'

    :param lib: CDLL: The Go shared library
    :param boolInt: c_int: The C integer that needs to be converted to the Python bool

    :meta private:

    :return: The boolean converted to Python
    :rtype: bool
    """
    return int(boolInt) != 0