From fd3f9219f4e7178435f312300f933abe25608cf3 Mon Sep 17 00:00:00 2001 From: Bryan Forbes Date: Tue, 16 Mar 2010 03:40:55 -0500 Subject: Added cython bindings. --- .gitignore | 2 + Makefile.am | 2 +- configure.ac | 43 ++++-- cython/Makefile.am | 32 +++++ cython/imobiledevice.pyx | 336 +++++++++++++++++++++++++++++++++++++++++++++++ cython/stdint.pxi | 10 ++ 6 files changed, 415 insertions(+), 10 deletions(-) create mode 100644 cython/Makefile.am create mode 100644 cython/imobiledevice.pyx create mode 100644 cython/stdint.pxi diff --git a/.gitignore b/.gitignore index a40d060..82daaf9 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,5 @@ tools/idevicesyslog tools/idevicebackup tools/ideviceimagemounter tools/idevicescreenshot +cython/.libs/* +cython/*.c diff --git a/Makefile.am b/Makefile.am index 39840d6..9032f79 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src include swig dev tools docs +SUBDIRS = src include $(SWIG_SUB) $(CYTHON_SUB) dev tools docs DISTCHECK_CONFIGURE_FLAGS = --enable-dev-tools diff --git a/configure.ac b/configure.ac index 7dcf87b..b12ee61 100644 --- a/configure.ac +++ b/configure.ac @@ -69,12 +69,12 @@ if test "x$ac_cv_have_endian" = "xno"; then fi fi - +# SWIG Python Bindings AC_ARG_WITH([swig], [AS_HELP_STRING([--without-swig], - [build Python bindings using swig (default is yes)])], + [build Python bindings using SWIG (default is yes)])], [build_swig=false], - [build_swig=true]) + [build_swig=true]) if test "$build_swig" = "true"; then AM_PATH_PYTHON(2.3) AC_PROG_SWIG(1.3.21) @@ -83,13 +83,13 @@ if test "$build_swig" = "true"; then else SWIG=false fi - if [test "x$SWIG" != "xfalse"]; then - python_bindings=yes + SWIG_SUB=swig + swig_python_bindings=yes else - python_bindings=no + SWIG_SUB= + swig_python_bindings=no fi - AM_CONDITIONAL([HAVE_SWIG],[test "x$SWIG" != "xfalse"]) AC_ARG_ENABLE([openssl], @@ -124,9 +124,32 @@ else ssl_provider="GnuTLS" fi +# Cython Python Bindings +AC_SUBST([CYTHON_PLIST_INCLUDE_DIR]) -AC_SUBST([DEV_SUB]) +AC_ARG_WITH([cython], + [AS_HELP_STRING([--without-cython], + [build Python bindings using Cython (default is yes)])], + [build_cython=false], + [build_cython=true]) +if test "$build_cython" = "true"; then + AM_PATH_PYTHON(2.3) + AC_PROG_CYTHON(0.12.1) + CYTHON_PYTHON +else + CYTHON=false +fi +if [test "x$CYTHON" != "xfalse"]; then + CYTHON_SUB=cython + CYTHON_PLIST_INCLUDE_DIR=$($PKG_CONFIG --variable=includedir libplist)/plist/cython +else + CYTHON_SUB= + cython_python_bindings=no +fi +AM_CONDITIONAL([HAVE_CYTHON],[test "x$CYTHON" = "xcython"]) + +AC_SUBST([DEV_SUB]) AC_ARG_ENABLE([dev-tools], [AS_HELP_STRING([--enable-dev-tools], @@ -200,6 +223,7 @@ include/Makefile dev/Makefile tools/Makefile swig/Makefile +cython/Makefile docs/Makefile libimobiledevice-1.0.pc doxygen.cfg @@ -212,7 +236,8 @@ Configuration for $PACKAGE $VERSION: Install prefix: .........: $prefix Debug code ..............: $building_debug_code Dev tools ...............: $building_dev_tools - Python bindings .........: $python_bindings + SWIG Python bindings ....: $swig_python_bindings + Cython Python bindgins ..: $cython_python_bindings SSL support .............: $ssl_provider Now type 'make' to build $PACKAGE $VERSION, diff --git a/cython/Makefile.am b/cython/Makefile.am new file mode 100644 index 0000000..3cada6c --- /dev/null +++ b/cython/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusbmuxd_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS) +AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) $(libplist_LIBS) $(libusbmuxd_LIBS) $(libgcrypt_LIBS) + +if HAVE_CYTHON + +BUILT_SOURCES = imobiledevice.c + +CLEANFILES = \ + *.pyc \ + *.pyo \ + imobiledevice.so \ + imobiledevice.c + +EXTRA_DIST = \ + imobiledevice.pyx + +imobiledevicedir = $(pyexecdir) +imobiledevice_LTLIBRARIES = imobiledevice.la +nodist_imobiledevice_la_SOURCES = imobiledevice.c #imobiledevice_private.c +imobiledevice_la_CFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/src $(PYTHON_CPPFLAGS) +imobiledevice_la_LDFLAGS = -module -avoid-version $(PYTHON_LDFLAGS) +imobiledevice_la_LIBADD = $(top_builddir)/src/libimobiledevice.la + +imobiledevice.c: imobiledevice.pyx $(CYTHON_PLIST_INCLUDE_DIR)/plist.pxd + $(CYTHON) --line-directives -I$(CYTHON_PLIST_INCLUDE_DIR) -I$(top_srcdir)/src -o $@ $< + +# imobiledevice_private.c: $(IMOBILEDEVICE_PRIVATE_SOURCES) $(IMOBILEDEVICE_INCLUDES) $(PLIST_INCLUDES) +# $(CYTHON) $(IMOBILEDEVICE_CPPFLAGS) -I$(top_srcdir)/src -o $@ $< + +endif diff --git a/cython/imobiledevice.pyx b/cython/imobiledevice.pyx new file mode 100644 index 0000000..7190def --- /dev/null +++ b/cython/imobiledevice.pyx @@ -0,0 +1,336 @@ +cdef extern from *: + ctypedef unsigned char uint8_t + ctypedef short int int16_t + ctypedef unsigned short int uint16_t + ctypedef unsigned int uint32_t + ctypedef int int32_t +IF UNAME_MACHINE == 'x86_64': + ctypedef unsigned long int uint64_t +ELSE: + ctypedef unsigned long long int uint64_t + +cimport plist + +cdef extern from "pyerrors.h": + ctypedef class __builtin__.Exception [object PyBaseExceptionObject]: + pass + +cdef class BaseError(Exception): + cdef dict _lookup_table + cdef int16_t _c_errcode + cpdef get_message(self) + + def __cinit__(self, int16_t errcode): + self._c_errcode = errcode + Exception.__init__(self, errcode) + + def __nonzero__(self): + return self._c_errcode != 0 + + cpdef get_message(self): + return '%s (%s)' % (self._lookup_table[self._c_errcode], self._c_errcode) + + def __str__(self): + return self.get_message() + + def __repr__(self): + return self.__str__() + +cdef extern from "libimobiledevice/libimobiledevice.h": + cdef struct idevice_int: + pass + ctypedef idevice_int* idevice_t + ctypedef int16_t idevice_error_t + int16_t IDEVICE_E_SUCCESS + int16_t IDEVICE_E_INVALID_ARG + int16_t IDEVICE_E_UNKNOWN_ERROR + int16_t IDEVICE_E_NO_DEVICE + int16_t IDEVICE_E_NOT_ENOUGH_DATA + int16_t IDEVICE_E_BAD_HEADER + int16_t IDEVICE_E_SSL_ERROR + cdef enum idevice_event_type: + IDEVICE_DEVICE_ADD = 1, + IDEVICE_DEVICE_REMOVE + ctypedef struct idevice_event_t: + idevice_event_type event + char *uuid + int conn_type + ctypedef void (*idevice_event_cb_t) (idevice_event_t *event, void *user_data) + cdef extern idevice_error_t c_idevice_event_subscribe "idevice_event_subscribe" (idevice_event_cb_t callback, void *user_data) + cdef extern idevice_error_t c_idevice_event_unsubscribe "idevice_event_unsubscribe" () + void idevice_set_debug_level(int level) + idevice_error_t idevice_new(idevice_t *device, char *uuid) + idevice_error_t idevice_free(idevice_t device) + idevice_error_t idevice_get_uuid(idevice_t device, char** uuid) + idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) + +def set_debug_level(level): + idevice_set_debug_level(level) + +#cdef void idevice_event_cb(idevice_event_t *c_event, void *user_data): + #event = iDeviceEvent() + #event._c_event = c_event + #(user_data)(event) + +#def idevice_event_subscribe(callback): + #c_idevice_event_subscribe(idevice_event_cb, callback) + +cdef class iDeviceError(BaseError): + def __cinit__(self, *args, **kwargs): + self._lookup_table = { + IDEVICE_E_SUCCESS: 'Success', + IDEVICE_E_INVALID_ARG: 'Invalid argument', + IDEVICE_E_UNKNOWN_ERROR: 'Unknown error', + IDEVICE_E_NO_DEVICE: 'No device', + IDEVICE_E_NOT_ENOUGH_DATA: 'Not enough data', + IDEVICE_E_BAD_HEADER: 'Bad header', + IDEVICE_E_SSL_ERROR: 'SSL Error' + } + BaseError.__init__(self, *args, **kwargs) + +cdef class iDeviceEvent: + cdef idevice_event_t* _c_event + +cdef class iDevice: + cdef idevice_t _c_dev + + def __cinit__(self, uuid=None, *args, **kwargs): + cdef char* c_uuid = NULL + if uuid is not None: + c_uuid = uuid + err = iDeviceError(idevice_new(&(self._c_dev), c_uuid)) + if err: raise err + + def __dealloc__(self): + if self._c_dev is not NULL: + err = iDeviceError(idevice_free(self._c_dev)) + if err: raise err + + property uuid: + def __get__(self): + cdef char* uuid + err = iDeviceError(idevice_get_uuid(self._c_dev, &uuid)) + if err: raise err + return uuid + property handle: + def __get__(self): + cdef uint32_t handle + err = iDeviceError(idevice_get_handle(self._c_dev, &handle)) + if err: raise err + return handle + +cdef extern from "libimobiledevice/lockdown.h": + cdef struct lockdownd_client_int: + pass + ctypedef lockdownd_client_int *lockdownd_client_t + ctypedef int16_t lockdownd_error_t + int16_t LOCKDOWN_E_SUCCESS + int16_t LOCKDOWN_E_INVALID_ARG + int16_t LOCKDOWN_E_INVALID_CONF + int16_t LOCKDOWN_E_PLIST_ERROR + int16_t LOCKDOWN_E_PAIRING_FAILED + int16_t LOCKDOWN_E_SSL_ERROR + int16_t LOCKDOWN_E_DICT_ERROR + int16_t LOCKDOWN_E_START_SERVICE_FAILED + int16_t LOCKDOWN_E_NOT_ENOUGH_DATA + int16_t LOCKDOWN_E_SET_VALUE_PROHIBITED + int16_t LOCKDOWN_E_GET_VALUE_PROHIBITED + int16_t LOCKDOWN_E_REMOVE_VALUE_PROHIBITED + int16_t LOCKDOWN_E_MUX_ERROR + int16_t LOCKDOWN_E_ACTIVATION_FAILED + int16_t LOCKDOWN_E_PASSWORD_PROTECTED + int16_t LOCKDOWN_E_NO_RUNNING_SESSION + int16_t LOCKDOWN_E_INVALID_HOST_ID + int16_t LOCKDOWN_E_INVALID_SERVICE + int16_t LOCKDOWN_E_INVALID_ACTIVATION_RECORD + int16_t LOCKDOWN_E_UNKNOWN_ERROR + + 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_start_service(lockdownd_client_t client, char *service, uint16_t *port) + +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: "PList error", + LOCKDOWN_E_PAIRING_FAILED: "Pairing failed", + LOCKDOWN_E_SSL_ERROR: "SSL error", + LOCKDOWN_E_DICT_ERROR: "Dict error", + LOCKDOWN_E_START_SERVICE_FAILED: "Start service failed", + LOCKDOWN_E_NOT_ENOUGH_DATA: "Not enough data", + LOCKDOWN_E_SET_VALUE_PROHIBITED: "Set value prohibited", + LOCKDOWN_E_GET_VALUE_PROHIBITED: "Get value prohibited", + LOCKDOWN_E_REMOVE_VALUE_PROHIBITED: "Remove value prohibited", + LOCKDOWN_E_MUX_ERROR: "MUX Error", + LOCKDOWN_E_ACTIVATION_FAILED: "Activation failed", + LOCKDOWN_E_PASSWORD_PROTECTED: "Password protected", + LOCKDOWN_E_NO_RUNNING_SESSION: "No running session", + LOCKDOWN_E_INVALID_HOST_ID: "Invalid host ID", + LOCKDOWN_E_INVALID_SERVICE: "Invalid service", + LOCKDOWN_E_INVALID_ACTIVATION_RECORD: "Invalid activation record", + LOCKDOWN_E_UNKNOWN_ERROR: "Unknown error" + } + BaseError.__init__(self, *args, **kwargs) + +cdef class LockdownClient: + cdef lockdownd_client_t _c_client + + def __cinit__(self, iDevice device not None, char *label=NULL, *args, **kwargs): + cdef iDevice dev = device + err = LockdownError(lockdownd_client_new_with_handshake(dev._c_dev, &(self._c_client), label)) + if err: raise err + + def __dealloc__(self): + if self._c_client is not NULL: + err = LockdownError(lockdownd_client_free(self._c_client)) + if err: raise err + + cpdef start_service(self, service): + cdef uint16_t port + err = LockdownError(lockdownd_start_service(self._c_client, service, &port)) + if err: raise err + return port + + def goodbye(self): + pass + + +cdef extern from "libimobiledevice/mobilesync.h": + cdef struct mobilesync_client_int: + pass + ctypedef mobilesync_client_int *mobilesync_client_t + + ctypedef int16_t mobilesync_error_t + int16_t MOBILESYNC_E_SUCCESS + int16_t MOBILESYNC_E_INVALID_ARG + int16_t MOBILESYNC_E_PLIST_ERROR + int16_t MOBILESYNC_E_MUX_ERROR + int16_t MOBILESYNC_E_BAD_VERSION + int16_t MOBILESYNC_E_UNKNOWN_ERROR + + mobilesync_error_t mobilesync_client_new(idevice_t device, uint16_t port, mobilesync_client_t * client) + mobilesync_error_t mobilesync_client_free(mobilesync_client_t client) + mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist.plist_t *plist) + mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist.plist_t plist) + +cdef class MobileSyncError(BaseError): + def __init__(self, *args, **kwargs): + self._lookup_table = { + MOBILESYNC_E_SUCCESS: "Success", + MOBILESYNC_E_INVALID_ARG: "Invalid argument", + MOBILESYNC_E_PLIST_ERROR: "PList Error", + MOBILESYNC_E_MUX_ERROR: "MUX Error", + MOBILESYNC_E_BAD_VERSION: "Bad Version", + MOBILESYNC_E_UNKNOWN_ERROR: "Unknown error" + } + BaseError.__init__(self, *args, **kwargs) + +cdef class MobileSyncClient: + cdef mobilesync_client_t _c_client + + def __cinit__(self, iDevice device not None, LockdownClient lockdown not None, *args, **kwargs): + cdef iDevice dev = device + cdef LockdownClient lckd = lockdown + port = lckd.start_service("com.apple.mobilesync") + err = MobileSyncError(mobilesync_client_new(dev._c_dev, port, &(self._c_client))) + if err: raise err + + def __dealloc__(self): + if self._c_client is not NULL: + err = MobileSyncError(mobilesync_client_free(self._c_client)) + if err: raise err + + cpdef send(self, object obj): + cdef plist.Node node + cdef plist.plist_t c_node + if isinstance(obj, plist.Node): + node = obj + c_node = node._c_node + else: + c_node = plist.native_to_plist_t(obj) + err = MobileSyncError(mobilesync_send(self._c_client, c_node)) + if err: raise err + + cpdef receive(self): + cdef plist.plist_t c_node = NULL + err = MobileSyncError(mobilesync_receive(self._c_client, &(c_node))) + if err: raise err + + return plist.plist_t_to_node(c_node) + +cdef extern from *: + ctypedef char* const_char_ptr "const char*" + +cdef extern from "libimobiledevice/notification_proxy.h": + cdef struct np_client_int: + pass + ctypedef np_client_int *np_client_t + ctypedef int16_t np_error_t + ctypedef void (*np_notify_cb_t) (const_char_ptr notification, void *userdata) + np_error_t np_client_new(idevice_t device, uint16_t port, np_client_t *client) + np_error_t np_client_free(np_client_t client) + np_error_t np_post_notification(np_client_t client, char *notification) + np_error_t np_observe_notification(np_client_t client, char *notification) + np_error_t np_observe_notifications(np_client_t client, char **notification_spec) + np_error_t np_set_notify_callback(np_client_t client, np_notify_cb_t notify_cb, void *userdata) + +cdef void np_notify_cb(const_char_ptr notification, void *py_callback): + (py_callback)(notification) + +cdef class NotificationProxyError(BaseError): + pass + +cdef class NotificationProxy: + cdef np_client_t _c_client + + def __cinit__(self, iDevice device not None, LockdownClient lockdown not None, *args, **kwargs): + cdef iDevice dev = device + cdef LockdownClient lckd = lockdown + port = lckd.start_service("com.apple.mobile.notification_proxy") + err = NotificationProxyError(np_client_new(dev._c_dev, port, &(self._c_client))) + if err: raise err + + def __dealloc__(self): + if self._c_client is not NULL: + err = NotificationProxyError(np_client_free(self._c_client)) + if err: raise err + + def set_notify_callback(self, callback): + err = NotificationProxyError(np_set_notify_callback(self._c_client, np_notify_cb, callback)) + if err: raise err + + def observe_notification(self, notification): + err = NotificationProxyError(np_observe_notification(self._c_client, notification)) + if err: raise err + +cdef extern from "libimobiledevice/sbservices.h": + cdef struct sbservices_client_int: + pass + ctypedef sbservices_client_int *sbservices_client_t + ctypedef int16_t sbservices_error_t + sbservices_error_t sbservices_client_new(idevice_t device, uint16_t port, sbservices_client_t *client) + sbservices_error_t sbservices_client_free(sbservices_client_t client) + sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist.plist_t *state) + sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist.plist_t newstate) + sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, char *bundleId, char **pngdata, uint64_t *pngsize) + +cdef class SpringboardServicesError(BaseError): + pass + +cdef class SpringboardServices: + cdef sbservices_client_t _c_client + + def __cinit__(self, iDevice device not None, LockdownClient lockdown not None, *args, **kwargs): + cdef iDevice dev = device + cdef LockdownClient lckd = lockdown + port = lockdown.start_service("com.apple.springboardservices") + err = SpringboardServicesError(sbservices_client_new(dev._c_dev, port, &(self._c_client))) + if err: raise err + + def __dealloc__(self): + if self._c_client is not NULL: + err = SpringboardServicesError(sbservices_client_free(self._c_client)) + if err: raise err diff --git a/cython/stdint.pxi b/cython/stdint.pxi new file mode 100644 index 0000000..518c3d1 --- /dev/null +++ b/cython/stdint.pxi @@ -0,0 +1,10 @@ +cdef extern from "stdint.h": + ctypedef unsigned char uint8_t + ctypedef short int int16_t + ctypedef unsigned short int uint16_t + ctypedef unsigned int uint32_t + ctypedef int int32_t +IF UNAME_MACHINE == 'x86_64': + ctypedef unsigned long int uint64_t +ELSE: + ctypedef unsigned long long int uint64_t -- cgit v1.1-32-gdbae