summaryrefslogtreecommitdiffstats
path: root/cython/lockdown.pxi
blob: 25edb4c26157b8ba3dc76b4c3be3d316ff586bfa (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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
cdef extern from "libimobiledevice/lockdown.h":
    ctypedef enum lockdownd_error_t:
        LOCKDOWN_E_SUCCESS
        LOCKDOWN_E_INVALID_ARG
        LOCKDOWN_E_INVALID_CONF
        LOCKDOWN_E_PLIST_ERROR
        LOCKDOWN_E_PAIRING_FAILED
        LOCKDOWN_E_SSL_ERROR
        LOCKDOWN_E_DICT_ERROR
        LOCKDOWN_E_RECEIVE_TIMEOUT
        LOCKDOWN_E_SET_VALUE_PROHIBITED
        LOCKDOWN_E_GET_VALUE_PROHIBITED
        LOCKDOWN_E_MUX_ERROR
        LOCKDOWN_E_NO_RUNNING_SESSION
        LOCKDOWN_E_INVALID_RESPONSE
        LOCKDOWN_E_MISSING_KEY
        LOCKDOWN_E_MISSING_VALUE
        LOCKDOWN_E_GET_PROHIBITED
        LOCKDOWN_E_SET_PROHIBITED
        LOCKDOWN_E_REMOVE_PROHIBITED
        LOCKDOWN_E_IMMUTABLE_VALUE
        LOCKDOWN_E_PASSWORD_PROTECTED
        LOCKDOWN_E_USER_DENIED_PAIRING
        LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING
        LOCKDOWN_E_MISSING_HOST_ID
        LOCKDOWN_E_INVALID_HOST_ID
        LOCKDOWN_E_SESSION_ACTIVE
        LOCKDOWN_E_SESSION_INACTIVE
        LOCKDOWN_E_MISSING_SESSION_ID
        LOCKDOWN_E_INVALID_SESSION_ID
        LOCKDOWN_E_MISSING_SERVICE
        LOCKDOWN_E_INVALID_SERVICE
        LOCKDOWN_E_SERVICE_LIMIT
        LOCKDOWN_E_MISSING_PAIR_RECORD
        LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED
        LOCKDOWN_E_INVALID_PAIR_RECORD
        LOCKDOWN_E_INVALID_ACTIVATION_RECORD
        LOCKDOWN_E_MISSING_ACTIVATION_RECORD
        LOCKDOWN_E_SERVICE_PROHIBITED
        LOCKDOWN_E_ESCROW_LOCKED
        LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION
        LOCKDOWN_E_FMIP_PROTECTED
        LOCKDOWN_E_MC_PROTECTED
        LOCKDOWN_E_MC_CHALLENGE_REQUIRED
        LOCKDOWN_E_UNKNOWN_ERROR

    lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, char *label)
    lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, char *label)
    lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)

    lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **tp)
    lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, char *domain, char *key, plist.plist_t *value)
    lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, char *domain, char *key, plist.plist_t value)
    lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, char *domain, char *key)
    lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, char *identifier, lockdownd_service_descriptor_t *service)
    lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, char *host_id, char **session_id, int *ssl_enabled)
    lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, char *session_id)
    lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist.plist_t plist)
    lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist.plist_t *plist)
    lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
    lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
    lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
    lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist.plist_t activation_record)
    lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
    lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
    lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
    lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count)
    lockdownd_error_t lockdownd_data_classes_free(char **classes)
    lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service)

cdef class LockdownError(BaseError):
    def __init__(self, *args, **kwargs):
        self._lookup_table = {
            LOCKDOWN_E_SUCCESS: "Success",
            LOCKDOWN_E_INVALID_ARG: "Invalid argument",
            LOCKDOWN_E_INVALID_CONF: "Invalid configuration",
            LOCKDOWN_E_PLIST_ERROR: "Property list error",
            LOCKDOWN_E_PAIRING_FAILED: "Pairing failed",
            LOCKDOWN_E_SSL_ERROR: "SSL error",
            LOCKDOWN_E_DICT_ERROR: "Dictionary error",
            LOCKDOWN_E_RECEIVE_TIMEOUT: "Receive timeout",
            LOCKDOWN_E_MUX_ERROR: "Mux Protocol Error",
            LOCKDOWN_E_NO_RUNNING_SESSION: "No running session",
            LOCKDOWN_E_INVALID_RESPONSE: "Invalid response",
            LOCKDOWN_E_MISSING_KEY: "Missing key",
            LOCKDOWN_E_MISSING_VALUE: "Missing value",
            LOCKDOWN_E_GET_PROHIBITED: "Get value prohibited",
            LOCKDOWN_E_SET_PROHIBITED: "Set value prohibited",
            LOCKDOWN_E_REMOVE_PROHIBITED: "Remove value prohibited",
            LOCKDOWN_E_IMMUTABLE_VALUE: "Immutable value",
            LOCKDOWN_E_PASSWORD_PROTECTED: "Password protected",
            LOCKDOWN_E_USER_DENIED_PAIRING: "User denied pairing",
            LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING: "Pairing dialog response pending",
            LOCKDOWN_E_MISSING_HOST_ID: "Missing host ID",
            LOCKDOWN_E_INVALID_HOST_ID: "Invalid host ID",
            LOCKDOWN_E_SESSION_ACTIVE: "Session active",
            LOCKDOWN_E_SESSION_INACTIVE: "Session inactive",
            LOCKDOWN_E_MISSING_SESSION_ID: "Missing session ID",
            LOCKDOWN_E_INVALID_SESSION_ID: "Invalid session ID",
            LOCKDOWN_E_MISSING_SERVICE: "Missing service",
            LOCKDOWN_E_INVALID_SERVICE: "Invalid service",
            LOCKDOWN_E_SERVICE_LIMIT: "Service limit reached",
            LOCKDOWN_E_MISSING_PAIR_RECORD: "Missing pair record",
            LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED: "Saving pair record failed",
            LOCKDOWN_E_INVALID_PAIR_RECORD: "Invalid pair record",
            LOCKDOWN_E_INVALID_ACTIVATION_RECORD: "Invalid activation record",
            LOCKDOWN_E_MISSING_ACTIVATION_RECORD: "Missing activation record",
            LOCKDOWN_E_SERVICE_PROHIBITED: "Service prohibited",
            LOCKDOWN_E_ESCROW_LOCKED: "Escrow locked",
            LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION: "Pairing prohibited over this connection",
            LOCKDOWN_E_FMIP_PROTECTED: "Find My iPhone/iPod/iPad protected",
            LOCKDOWN_E_MC_PROTECTED: "MC protected",
            LOCKDOWN_E_MC_CHALLENGE_REQUIRED: "MC challenge required",
            LOCKDOWN_E_UNKNOWN_ERROR: "Unknown error"
        }
        BaseError.__init__(self, *args, **kwargs)

cdef class LockdownPairRecord:
    #def __cinit__(self, bytes device_certificate, bytes host_certificate, bytes host_id, bytes root_certificate, *args, **kwargs):
    property device_certificate:
        def __get__(self):
            cdef bytes result = self._c_record.device_certificate
            return result
    property host_certificate:
        def __get__(self):
            cdef bytes result = self._c_record.host_certificate
            return result
    property host_id:
        def __get__(self):
            cdef bytes result = self._c_record.host_id
            return result
    property root_certificate:
        def __get__(self):
            cdef bytes result = self._c_record.root_certificate
            return result

cdef class LockdownServiceDescriptor(Base):
    #def __cinit__(self, uint16_t port, uint8_t ssl_enabled, *args, **kwargs):
    def __dealloc__(self):
        cdef lockdownd_error_t err
        if self._c_service_descriptor is not NULL:
            err = lockdownd_service_descriptor_free(self._c_service_descriptor)
            self._c_service_descriptor = NULL
            self.handle_error(err)
    property port:
        def __get__(self):
            return self._c_service_descriptor.port
    property ssl_enabled:
        def __get__(self):
            return self._c_service_descriptor.ssl_enabled

cdef class LockdownClient(PropertyListService):
    def __cinit__(self, iDevice device not None, bytes label=b'', bint handshake=True, *args, **kwargs):
        cdef:
            lockdownd_error_t err
            char* c_label = NULL
        if label:
            c_label = label
        if handshake:
            err = lockdownd_client_new_with_handshake(device._c_dev, &self._c_client, c_label)
        else:
            err = lockdownd_client_new(device._c_dev, &self._c_client, c_label)
        self.handle_error(err)

        self.device = device

    def __dealloc__(self):
        cdef lockdownd_error_t err
        if self._c_client is not NULL:
            err = lockdownd_client_free(self._c_client)
            self.handle_error(err)

    cpdef bytes query_type(self):
        cdef:
            lockdownd_error_t err
            char* c_type = NULL
            bytes result
        err = lockdownd_query_type(self._c_client, &c_type)
        try:
            self.handle_error(err)
            result = c_type

            return result
        except BaseError, e:
            raise
        finally:
            if c_type != NULL:
                free(c_type)

    cpdef plist.Node get_value(self, bytes domain=None, bytes key=None):
        cdef:
            lockdownd_error_t err
            plist.plist_t c_node = NULL
            char* c_domain = NULL
            char* c_key = NULL
        if domain is not None:
            c_domain = domain
        if key is not None:
            c_key = key

        err = lockdownd_get_value(self._c_client, c_domain, c_key, &c_node)

        try:
            self.handle_error(err)

            return plist.plist_t_to_node(c_node)
        except BaseError, e:
            if c_node != NULL:
                plist.plist_free(c_node)
            raise

    cpdef set_value(self, bytes domain, bytes key, object value):
        cdef: 
            plist.plist_t c_node = NULL
            char* c_domain = NULL
            char* c_key = NULL

        c_node = plist.native_to_plist_t(value)
        if domain is not None:
            c_domain = domain
        if key is not None:
            c_key = key
        try:
            self.handle_error(lockdownd_set_value(self._c_client, c_domain, c_key, c_node))
        except BaseError, e:
            raise
        finally:
            if c_node != NULL:
                c_node = NULL

    cpdef remove_value(self, bytes domain, bytes key):
        self.handle_error(lockdownd_remove_value(self._c_client, domain, key))

    cpdef object start_service(self, object service):
        cdef:
            char* c_service_name = NULL
            lockdownd_service_descriptor_t c_descriptor = NULL
            LockdownServiceDescriptor result

        if issubclass(service, BaseService) and \
            service.__service_name__ is not None \
            and isinstance(service.__service_name__, (str, bytes)):
            c_service_name_str = service.__service_name__.encode('utf-8')
        elif isinstance(service, (str, bytes)):
            c_service_name_str = service.encode('utf-8')
        else:
            raise TypeError("LockdownClient.start_service() takes a BaseService or string as its first argument")
        c_service_name = c_service_name_str

        try:
            self.handle_error(lockdownd_start_service(self._c_client, c_service_name, &c_descriptor))

            result = LockdownServiceDescriptor.__new__(LockdownServiceDescriptor)
            result._c_service_descriptor = c_descriptor

            return result
        except BaseError, e:
            raise

    cpdef object get_service_client(self, object service_class):
        cdef:
            LockdownServiceDescriptor descriptor

        if not hasattr(service_class, '__service_name__') and \
            not service_class.__service_name__ is not None \
            and not isinstance(service_class.__service_name__, (str, bytes)):
            raise TypeError("LockdownClient.get_service_client() takes a BaseService as its first argument")

        descriptor = self.start_service(service_class)
        return service_class(self.device, descriptor)

    cpdef tuple start_session(self, bytes host_id):
        cdef:
            lockdownd_error_t err
            char* c_session_id = NULL
            bint ssl_enabled
            bytes session_id
        err = lockdownd_start_session(self._c_client, host_id, &c_session_id, <int *>&ssl_enabled)
        try:
            self.handle_error(err)

            session_id = c_session_id
            return (session_id, ssl_enabled)
        except BaseError, e:
            raise
        finally:
            if c_session_id != NULL:
                free(c_session_id)

    cpdef stop_session(self, bytes session_id):
        self.handle_error(lockdownd_stop_session(self._c_client, session_id))

    cpdef pair(self, object pair_record=None):
        cdef lockdownd_pair_record_t c_pair_record = NULL
        if pair_record is not None:
            c_pair_record = (<LockdownPairRecord>pair_record)._c_record
        self.handle_error(lockdownd_pair(self._c_client, c_pair_record))

    cpdef validate_pair(self, object pair_record=None):
        cdef lockdownd_pair_record_t c_pair_record = NULL
        if pair_record is not None:
            c_pair_record = (<LockdownPairRecord>pair_record)._c_record
        self.handle_error(lockdownd_validate_pair(self._c_client, c_pair_record))

    cpdef unpair(self, object pair_record=None):
        cdef lockdownd_pair_record_t c_pair_record = NULL
        if pair_record is not None:
            c_pair_record = (<LockdownPairRecord>pair_record)._c_record
        self.handle_error(lockdownd_unpair(self._c_client, c_pair_record))

    cpdef activate(self, plist.Node activation_record):
        self.handle_error(lockdownd_activate(self._c_client, activation_record._c_node))

    cpdef deactivate(self):
        self.handle_error(lockdownd_deactivate(self._c_client))

    cpdef enter_recovery(self):
        self.handle_error(lockdownd_enter_recovery(self._c_client))

    cpdef goodbye(self):
        self.handle_error(lockdownd_goodbye(self._c_client))

    cpdef list get_sync_data_classes(self):
        cdef:
            char **classes = NULL
            int count = 0
            list result = []
            bytes data_class

        try:
            self.handle_error(lockdownd_get_sync_data_classes(self._c_client, &classes, &count))

            for i from 0 <= i < count:
                data_class = classes[i]
                result.append(data_class)

            return result
        except Exception, e:
            raise
        finally:
            if classes != NULL:
                lockdownd_data_classes_free(classes)

    cdef inline int16_t _send(self, plist.plist_t node):
        return lockdownd_send(self._c_client, node)

    cdef inline int16_t _receive(self, plist.plist_t* node):
        return lockdownd_receive(self._c_client, node)

    cdef inline BaseError _error(self, int16_t ret):
        return LockdownError(ret)