diff --git a/frida/__init__.py b/frida/__init__.py index a8af9ae..483a51b 100644 --- a/frida/__init__.py +++ b/frida/__init__.py @@ -23,6 +23,7 @@ PortalService = core.PortalService EndpointParameters = core.EndpointParameters Compiler = core.Compiler +PackageManager = core.PackageManager FileMonitor = _frida.FileMonitor Cancellable = core.Cancellable diff --git a/frida/_frida/__init__.pyi b/frida/_frida/__init__.pyi index c2d3a9b..8d1d152 100644 --- a/frida/_frida/__init__.pyi +++ b/frida/_frida/__init__.pyi @@ -774,4 +774,94 @@ class Compiler(Object): """ ... +class PackageManager(Object): + @property + def registry(self) -> str: + """ + The registry being used. + """ + ... + + @registry.setter + def registry(self, value: str) -> None: + """ + Change the registry to use. + """ + ... + + def search( + self, + query: str, + offset: Optional[int] = None, + limit: Optional[int] = None, + ) -> PackageSearchResult: + """ + Search for packages to install. + """ + ... + + def install( + self, + project_root: Optional[str] = None, + role: Optional[str] = None, + specs: Optional[Sequence[str]] = None, + omits: Optional[Sequence[str]] = None, + ) -> PackageInstallResult: + """ + Install one or more packages. + """ + ... + +class Package(Object): + @property + def name(self) -> str: + """ + Package name. + """ + ... + + @property + def version(self) -> str: + """ + Package version. + """ + ... + + @property + def description(self) -> Optional[str]: + """ + Package description. + """ + ... + + @property + def url(self) -> Optional[str]: + """ + Package URL. + """ + ... + +class PackageSearchResult(Object): + @property + def packages(self) -> List[Package]: + """ + Batch of matching packages. + """ + ... + + @property + def total(self) -> int: + """ + Total matching packages. + """ + ... + +class PackageInstallResult(Object): + @property + def packages(self) -> List[Package]: + """ + The toplevel packages that are installed. + """ + ... + __version__: str diff --git a/frida/_frida/extension.c b/frida/_frida/extension.c index ee5b9ce..218238e 100644 --- a/frida/_frida/extension.c +++ b/frida/_frida/extension.c @@ -145,6 +145,10 @@ typedef struct _PyPortalMembership PyPortalMembership; typedef struct _PyPortalService PyPortalService; typedef struct _PyEndpointParameters PyEndpointParameters; typedef struct _PyCompiler PyCompiler; +typedef struct _PyPackageManager PyPackageManager; +typedef struct _PyPackage PyPackage; +typedef struct _PyPackageSearchResult PyPackageSearchResult; +typedef struct _PyPackageInstallResult PyPackageInstallResult; typedef struct _PyFileMonitor PyFileMonitor; typedef struct _PyIOStream PyIOStream; typedef struct _PyCancellable PyCancellable; @@ -298,6 +302,33 @@ struct _PyCompiler PyGObject parent; }; +struct _PyPackageManager +{ + PyGObject parent; +}; + +struct _PyPackage +{ + PyGObject parent; + PyObject * name; + PyObject * version; + PyObject * description; + PyObject * url; +}; + +struct _PyPackageSearchResult +{ + PyGObject parent; + PyObject * packages; + guint total; +}; + +struct _PyPackageInstallResult +{ + PyGObject parent; + PyObject * packages; +}; + struct _PyFileMonitor { PyGObject parent; @@ -498,12 +529,42 @@ static gchar * frida_python_authentication_service_authenticate_finish (FridaAut static void frida_python_authentication_service_do_authenticate (GTask * task, FridaPythonAuthenticationService * self); static int PyCompiler_init (PyCompiler * self, PyObject * args, PyObject * kw); +static void PyCompiler_dealloc (PyCompiler * self); static PyObject * PyCompiler_build (PyCompiler * self, PyObject * args, PyObject * kw); static PyObject * PyCompiler_watch (PyCompiler * self, PyObject * args, PyObject * kw); -static gboolean PyCompiler_set_options (FridaCompilerOptions * options, const gchar * project_root_value, const gchar * source_maps_value, - const gchar * compression_value); +static gboolean PyCompiler_set_options (FridaCompilerOptions * options, const gchar * project_root_value, const gchar * output_format_value, + const gchar * bundle_format_value, const gchar * type_check_value, const gchar * source_maps_value, const gchar * compression_value); + +static int PyPackageManager_init (PyPackageManager * self, PyObject * args, PyObject * kw); +static void PyPackageManager_dealloc (PyPackageManager * self); +static PyObject * PyPackageManager_repr (PyPackageManager * self); +static PyObject * PyPackageManager_get_registry (PyPackageManager * self, void * closure); +static int PyPackageManager_set_registry (PyPackageManager * self, PyObject * val, void * closure); +static PyObject * PyPackageManager_search (PyPackageManager * self, PyObject * args, PyObject * kw); +static PyObject * PyPackageManager_install (PyPackageManager * self, PyObject * args, PyObject * kw); +static FridaPackageInstallOptions * PyPackageManager_parse_install_options (const gchar * project_root, const char * role_value, + PyObject * specs_value, PyObject * omits_value); + +static PyObject * PyPackage_new_take_handle (FridaPackage * handle); +static int PyPackage_init (PyPackage * self, PyObject * args, PyObject * kw); +static void PyPackage_init_from_handle (PyPackage * self, FridaPackage * handle); +static void PyPackage_dealloc (PyPackage * self); +static PyObject * PyPackage_repr (PyPackage * self); + +static PyObject * PyPackageSearchResult_new_take_handle (FridaPackageSearchResult * handle); +static int PyPackageSearchResult_init (PyPackageSearchResult * self, PyObject * args, PyObject * kw); +static void PyPackageSearchResult_init_from_handle (PyPackageSearchResult * self, FridaPackageSearchResult * handle); +static void PyPackageSearchResult_dealloc (PyPackageSearchResult * self); +static PyObject * PyPackageSearchResult_repr (PyPackageSearchResult * self); + +static PyObject * PyPackageInstallResult_new_take_handle (FridaPackageInstallResult * handle); +static int PyPackageInstallResult_init (PyPackageInstallResult * self, PyObject * args, PyObject * kw); +static void PyPackageInstallResult_init_from_handle (PyPackageInstallResult * self, FridaPackageInstallResult * handle); +static void PyPackageInstallResult_dealloc (PyPackageInstallResult * self); +static PyObject * PyPackageInstallResult_repr (PyPackageInstallResult * self); static int PyFileMonitor_init (PyFileMonitor * self, PyObject * args, PyObject * kw); +static void PyFileMonitor_dealloc (PyFileMonitor * self); static PyObject * PyFileMonitor_enable (PyFileMonitor * self); static PyObject * PyFileMonitor_disable (PyFileMonitor * self); @@ -725,6 +786,41 @@ static PyMethodDef PyCompiler_methods[] = { NULL } }; +static PyGetSetDef PyPackageManager_getset[] = +{ + { "registry", (getter) PyPackageManager_get_registry, (setter) PyPackageManager_set_registry, "The registry to use.", NULL }, + { NULL } +}; + +static PyMethodDef PyPackageManager_methods[] = +{ + { "search", (PyCFunction) PyPackageManager_search, METH_VARARGS | METH_KEYWORDS, "Search for packages to install." }, + { "install", (PyCFunction) PyPackageManager_install, METH_VARARGS | METH_KEYWORDS, "Install one or more packages." }, + { NULL } +}; + +static PyMemberDef PyPackage_members[] = +{ + { "name", T_OBJECT_EX, G_STRUCT_OFFSET (PyPackage, name), READONLY, "Package name." }, + { "version", T_OBJECT_EX, G_STRUCT_OFFSET (PyPackage, version), READONLY, "Package version." }, + { "description", T_OBJECT_EX, G_STRUCT_OFFSET (PyPackage, description), READONLY, "Package description." }, + { "url", T_OBJECT_EX, G_STRUCT_OFFSET (PyPackage, url), READONLY, "Package URL." }, + { NULL } +}; + +static PyMemberDef PyPackageSearchResult_members[] = +{ + { "packages", T_OBJECT_EX, G_STRUCT_OFFSET (PyPackageSearchResult, packages), READONLY, "Batch of matching packages." }, + { "total", T_UINT, G_STRUCT_OFFSET (PyPackageSearchResult, total), READONLY, "Total matching packages." }, + { NULL } +}; + +static PyMemberDef PyPackageInstallResult_members[] = +{ + { "packages", T_OBJECT_EX, G_STRUCT_OFFSET (PyPackageInstallResult, packages), READONLY, "The toplevel packages that are installed." }, + { NULL } +}; + static PyMethodDef PyFileMonitor_methods[] = { { "enable", (PyCFunction) PyFileMonitor_enable, METH_NOARGS, "Enable the file monitor." }, @@ -871,14 +967,49 @@ PYFRIDA_DEFINE_TYPE ("_frida.EndpointParameters", EndpointParameters, GObject, N ); PYFRIDA_DEFINE_TYPE ("_frida.Compiler", Compiler, GObject, NULL, frida_unref, - { Py_tp_doc, "Frida File Monitor" }, + { Py_tp_doc, "Frida Compiler" }, { Py_tp_init, PyCompiler_init }, + { Py_tp_dealloc, PyCompiler_dealloc }, { Py_tp_methods, PyCompiler_methods }, ); +PYFRIDA_DEFINE_TYPE ("_frida.PackageManager", PackageManager, GObject, NULL, frida_unref, + { Py_tp_doc, "Frida Package Manager" }, + { Py_tp_init, PyPackageManager_init }, + { Py_tp_dealloc, PyPackageManager_dealloc }, + { Py_tp_repr, PyPackageManager_repr }, + { Py_tp_getset, PyPackageManager_getset }, + { Py_tp_methods, PyPackageManager_methods }, +); + +PYFRIDA_DEFINE_TYPE ("_frida.Package", Package, GObject, PyPackage_init_from_handle, g_object_unref, + { Py_tp_doc, "Frida Package" }, + { Py_tp_init, PyPackage_init }, + { Py_tp_dealloc, PyPackage_dealloc }, + { Py_tp_repr, PyPackage_repr }, + { Py_tp_members, PyPackage_members }, +); + +PYFRIDA_DEFINE_TYPE ("_frida.PackageSearchResult", PackageSearchResult, GObject, PyPackageSearchResult_init_from_handle, g_object_unref, + { Py_tp_doc, "Frida Package Search Result" }, + { Py_tp_init, PyPackageSearchResult_init }, + { Py_tp_dealloc, PyPackageSearchResult_dealloc }, + { Py_tp_repr, PyPackageSearchResult_repr }, + { Py_tp_members, PyPackageSearchResult_members }, +); + +PYFRIDA_DEFINE_TYPE ("_frida.PackageInstallResult", PackageInstallResult, GObject, PyPackageInstallResult_init_from_handle, g_object_unref, + { Py_tp_doc, "Frida Package Install Result" }, + { Py_tp_init, PyPackageInstallResult_init }, + { Py_tp_dealloc, PyPackageInstallResult_dealloc }, + { Py_tp_repr, PyPackageInstallResult_repr }, + { Py_tp_members, PyPackageInstallResult_members }, +); + PYFRIDA_DEFINE_TYPE ("_frida.FileMonitor", FileMonitor, GObject, NULL, frida_unref, { Py_tp_doc, "Frida File Monitor" }, { Py_tp_init, PyFileMonitor_init }, + { Py_tp_dealloc, PyFileMonitor_dealloc }, { Py_tp_methods, PyFileMonitor_methods }, ); @@ -4751,37 +4882,48 @@ frida_python_authentication_service_do_authenticate (GTask * task, FridaPythonAu static int PyCompiler_init (PyCompiler * self, PyObject * args, PyObject * kw) { - PyDeviceManager * manager; - if (PyGObject_tp_init ((PyObject *) self, args, kw) < 0) return -1; - if (!PyArg_ParseTuple (args, "O!", PYFRIDA_TYPE_OBJECT (DeviceManager), &manager)) - return -1; + g_atomic_int_inc (&toplevel_objects_alive); - PyGObject_take_handle (&self->parent, frida_compiler_new (PY_GOBJECT_HANDLE (manager)), PYFRIDA_TYPE (Compiler)); + PyGObject_take_handle (&self->parent, frida_compiler_new (NULL), PYFRIDA_TYPE (Compiler)); return 0; } +static void +PyCompiler_dealloc (PyCompiler * self) +{ + g_atomic_int_dec_and_test (&toplevel_objects_alive); + + PyGObject_tp_dealloc ((PyObject *) self); +} + static PyObject * PyCompiler_build (PyCompiler * self, PyObject * args, PyObject * kw) { PyObject * result; - static char * keywords[] = { "entrypoint", "project_root", "source_maps", "compression", NULL }; + static char * keywords[] = + { "entrypoint", "project_root", "output_format", "bundle_format", "type_check", "source_maps", "compression", NULL }; const char * entrypoint; const char * project_root = NULL; + const char * output_format = NULL; + const char * bundle_format = NULL; + const char * type_check = NULL; const char * source_maps = NULL; const char * compression = NULL; FridaBuildOptions * options; GError * error = NULL; gchar * bundle; - if (!PyArg_ParseTupleAndKeywords (args, kw, "s|sss", keywords, &entrypoint, &project_root, &source_maps, &compression)) + if (!PyArg_ParseTupleAndKeywords (args, kw, "s|ssssss", keywords, &entrypoint, &project_root, &output_format, &bundle_format, &type_check, + &source_maps, &compression)) return NULL; options = frida_build_options_new (); - if (!PyCompiler_set_options (FRIDA_COMPILER_OPTIONS (options), project_root, source_maps, compression)) + if (!PyCompiler_set_options (FRIDA_COMPILER_OPTIONS (options), project_root, output_format, bundle_format, type_check, source_maps, + compression)) goto invalid_option_value; Py_BEGIN_ALLOW_THREADS @@ -4808,19 +4950,25 @@ PyCompiler_build (PyCompiler * self, PyObject * args, PyObject * kw) static PyObject * PyCompiler_watch (PyCompiler * self, PyObject * args, PyObject * kw) { - static char * keywords[] = { "entrypoint", "project_root", "source_maps", "compression", NULL }; + static char * keywords[] = + { "entrypoint", "project_root", "output_format", "bundle_format", "type_check", "source_maps", "compression", NULL }; const char * entrypoint; const char * project_root = NULL; + const char * output_format = NULL; + const char * bundle_format = NULL; + const char * type_check = NULL; const char * source_maps = NULL; const char * compression = NULL; FridaWatchOptions * options; GError * error = NULL; - if (!PyArg_ParseTupleAndKeywords (args, kw, "s|sss", keywords, &entrypoint, &project_root, &source_maps, &compression)) + if (!PyArg_ParseTupleAndKeywords (args, kw, "s|ssssss", keywords, &entrypoint, &project_root, &output_format, &bundle_format, &type_check, + &source_maps, &compression)) return NULL; options = frida_watch_options_new (); - if (!PyCompiler_set_options (FRIDA_COMPILER_OPTIONS (options), project_root, source_maps, compression)) + if (!PyCompiler_set_options (FRIDA_COMPILER_OPTIONS (options), project_root, output_format, bundle_format, type_check, source_maps, + compression)) goto invalid_option_value; Py_BEGIN_ALLOW_THREADS @@ -4842,12 +4990,42 @@ PyCompiler_watch (PyCompiler * self, PyObject * args, PyObject * kw) } static gboolean -PyCompiler_set_options (FridaCompilerOptions * options, const gchar * project_root_value, const gchar * source_maps_value, - const gchar * compression_value) +PyCompiler_set_options (FridaCompilerOptions * options, const gchar * project_root_value, const gchar * output_format_value, + const gchar * bundle_format_value, const gchar * type_check_value, const gchar * source_maps_value, const gchar * compression_value) { if (project_root_value != NULL) frida_compiler_options_set_project_root (options, project_root_value); + if (output_format_value != NULL) + { + FridaOutputFormat output_format; + + if (!PyGObject_unmarshal_enum (output_format_value, FRIDA_TYPE_OUTPUT_FORMAT, &output_format)) + return FALSE; + + frida_compiler_options_set_output_format (options, output_format); + } + + if (bundle_format_value != NULL) + { + FridaBundleFormat bundle_format; + + if (!PyGObject_unmarshal_enum (bundle_format_value, FRIDA_TYPE_BUNDLE_FORMAT, &bundle_format)) + return FALSE; + + frida_compiler_options_set_bundle_format (options, bundle_format); + } + + if (type_check_value != NULL) + { + FridaTypeCheckMode type_check; + + if (!PyGObject_unmarshal_enum (type_check_value, FRIDA_TYPE_TYPE_CHECK_MODE, &type_check)) + return FALSE; + + frida_compiler_options_set_type_check (options, type_check); + } + if (source_maps_value != NULL) { FridaSourceMaps source_maps; @@ -4872,6 +5050,414 @@ PyCompiler_set_options (FridaCompilerOptions * options, const gchar * project_ro } +static int +PyPackageManager_init (PyPackageManager * self, PyObject * args, PyObject * kw) +{ + if (PyGObject_tp_init ((PyObject *) self, args, kw) < 0) + return -1; + + g_atomic_int_inc (&toplevel_objects_alive); + + PyGObject_take_handle (&self->parent, frida_package_manager_new (), PYFRIDA_TYPE (PackageManager)); + + return 0; +} + +static void +PyPackageManager_dealloc (PyPackageManager * self) +{ + g_atomic_int_dec_and_test (&toplevel_objects_alive); + + PyGObject_tp_dealloc ((PyObject *) self); +} + +static PyObject * +PyPackageManager_repr (PyPackageManager * self) +{ + PyObject * result; + gchar * repr; + + repr = g_strdup_printf ("PackageManager(registry=\"%s\")", frida_package_manager_get_registry (PY_GOBJECT_HANDLE (self))); + result = PyUnicode_FromString (repr); + g_free (repr); + + return result; +} + +static PyObject * +PyPackageManager_get_registry (PyPackageManager * self, void * closure) +{ + return PyUnicode_FromString (frida_package_manager_get_registry (PY_GOBJECT_HANDLE (self))); +} + +static int +PyPackageManager_set_registry (PyPackageManager * self, PyObject * val, void * closure) +{ + gchar * registry; + + if (!PyGObject_unmarshal_string (val, ®istry)) + return -1; + frida_package_manager_set_registry (PY_GOBJECT_HANDLE (self), registry); + g_free (registry); + + return 0; +} + +static PyObject * +PyPackageManager_search (PyPackageManager * self, PyObject * args, PyObject * kw) +{ + FridaPackageSearchResult * result; + static char * keywords[] = { "query", "offset", "limit", NULL }; + const char * query; + guint offset = G_MAXUINT; + guint limit = G_MAXUINT; + FridaPackageSearchOptions * options; + GError * error = NULL; + + if (!PyArg_ParseTupleAndKeywords (args, kw, "s|II", keywords, &query, &offset, &limit)) + return NULL; + + options = frida_package_search_options_new (); + + if (offset != G_MAXUINT) + frida_package_search_options_set_offset (options, offset); + + if (limit != G_MAXUINT) + frida_package_search_options_set_limit (options, limit); + + Py_BEGIN_ALLOW_THREADS + result = frida_package_manager_search_sync (PY_GOBJECT_HANDLE (self), query, options, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + + g_object_unref (options); + + if (error != NULL) + return PyFrida_raise (error); + + return PyPackageSearchResult_new_take_handle (result); +} + +static PyObject * +PyPackageManager_install (PyPackageManager * self, PyObject * args, PyObject * kw) +{ + FridaPackageInstallResult * result; + static char * keywords[] = { "project_root", "role", "specs", "omits", NULL }; + const char * project_root = NULL; + const char * role_value = NULL; + PyObject * specs = NULL; + PyObject * omits = NULL; + FridaPackageInstallOptions * options; + GError * error = NULL; + + if (!PyArg_ParseTupleAndKeywords (args, kw, "|ssOO", keywords, &project_root, &role_value, &specs, &omits)) + return NULL; + + options = PyPackageManager_parse_install_options (project_root, role_value, specs, omits); + + Py_BEGIN_ALLOW_THREADS + result = frida_package_manager_install_sync (PY_GOBJECT_HANDLE (self), options, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + + g_object_unref (options); + + if (error != NULL) + return PyFrida_raise (error); + + return PyPackageInstallResult_new_take_handle (result); +} + +static FridaPackageInstallOptions * +PyPackageManager_parse_install_options (const gchar * project_root, const char * role_value, PyObject * specs_value, PyObject * omits_value) +{ + FridaPackageInstallOptions * options; + + options = frida_package_install_options_new (); + + if (project_root != NULL) + frida_package_install_options_set_project_root (options, project_root); + + if (role_value != NULL) + { + FridaPackageRole role; + + if (!PyGObject_unmarshal_enum (role_value, FRIDA_TYPE_PACKAGE_ROLE, &role)) + goto propagate_error; + + frida_package_install_options_set_role (options, role); + } + + if (specs_value != NULL) + { + gint n, i; + + n = PySequence_Size (specs_value); + if (n == -1) + goto propagate_error; + + for (i = 0; i != n; i++) + { + PyObject * element; + gchar * spec = NULL; + + element = PySequence_GetItem (specs_value, i); + if (element == NULL) + goto propagate_error; + PyGObject_unmarshal_string (element, &spec); + Py_DecRef (element); + if (spec == NULL) + goto propagate_error; + + frida_package_install_options_add_spec (options, spec); + } + } + + if (omits_value != NULL) + { + gint n, i; + + n = PySequence_Size (omits_value); + if (n == -1) + goto propagate_error; + + for (i = 0; i != n; i++) + { + PyObject * element; + gchar * str = NULL; + FridaPackageRole role; + + element = PySequence_GetItem (omits_value, i); + if (element == NULL) + goto propagate_error; + PyGObject_unmarshal_string (element, &str); + Py_DecRef (element); + if (str == NULL) + goto propagate_error; + + if (!PyGObject_unmarshal_enum (str, FRIDA_TYPE_PACKAGE_ROLE, &role)) + goto propagate_error; + + frida_package_install_options_add_omit (options, role); + } + } + + return options; + +propagate_error: + { + g_object_unref (options); + + return NULL; + } +} + + +static PyObject * +PyPackage_new_take_handle (FridaPackage * handle) +{ + return PyGObject_new_take_handle (handle, PYFRIDA_TYPE (Package)); +} + +static int +PyPackage_init (PyPackage * self, PyObject * args, PyObject * kw) +{ + if (PyGObject_tp_init ((PyObject *) self, args, kw) < 0) + return -1; + + self->name = NULL; + self->version = NULL; + self->description = NULL; + self->url = NULL; + + return 0; +} + +static void +PyPackage_init_from_handle (PyPackage * self, FridaPackage * handle) +{ + self->name = PyUnicode_FromString (frida_package_get_name (handle)); + self->version = PyUnicode_FromString (frida_package_get_version (handle)); + self->description = PyGObject_marshal_string (frida_package_get_description (handle)); + self->url = PyGObject_marshal_string (frida_package_get_url (handle)); +} + +static void +PyPackage_dealloc (PyPackage * self) +{ + Py_DecRef (self->url); + Py_DecRef (self->description); + Py_DecRef (self->version); + Py_DecRef (self->name); + + PyGObject_tp_dealloc ((PyObject *) self); +} + +static PyObject * +PyPackage_repr (PyPackage * self) +{ + PyObject * result; + FridaPackage * handle; + GString * repr; + const gchar * description, * url; + + handle = PY_GOBJECT_HANDLE (self); + + repr = g_string_sized_new (256); + + g_string_append_printf (repr, "Package(name=\"%s\", version=\"%s\"", + frida_package_get_name (handle), + frida_package_get_version (handle)); + + description = frida_package_get_description (handle); + if (description != NULL) + { + gchar * escaped = g_strescape (description, NULL); + g_string_append_printf (repr, ", description=\"%s\"", escaped); + g_free (escaped); + } + + url = frida_package_get_url (handle); + if (url != NULL) + g_string_append_printf (repr, ", url=\"%s\"", url); + + g_string_append (repr, ")"); + + result = PyUnicode_FromString (repr->str); + + g_string_free (repr, TRUE); + + return result; +} + + +static PyObject * +PyPackageList_marshal (FridaPackageList * list) +{ + PyObject * result; + gint n, i; + + n = frida_package_list_size (list); + result = PyList_New (n); + for (i = 0; i != n; i++) + PyList_SetItem (result, i, PyPackage_new_take_handle (frida_package_list_get (list, i))); + + return result; +} + + +static PyObject * +PyPackageSearchResult_new_take_handle (FridaPackageSearchResult * handle) +{ + return PyGObject_new_take_handle (handle, PYFRIDA_TYPE (PackageSearchResult)); +} + +static int +PyPackageSearchResult_init (PyPackageSearchResult * self, PyObject * args, PyObject * kw) +{ + if (PyGObject_tp_init ((PyObject *) self, args, kw) < 0) + return -1; + + self->packages = NULL; + self->total = 0; + + return 0; +} + +static void +PyPackageSearchResult_init_from_handle (PyPackageSearchResult * self, FridaPackageSearchResult * handle) +{ + self->packages = PyPackageList_marshal (frida_package_search_result_get_packages (handle)); + self->total = frida_package_search_result_get_total (handle); +} + +static void +PyPackageSearchResult_dealloc (PyPackageSearchResult * self) +{ + Py_DecRef (self->packages); + + PyGObject_tp_dealloc ((PyObject *) self); +} + +static PyObject * +PyPackageSearchResult_repr (PyPackageSearchResult * self) +{ + PyObject * result; + GString * repr; + gint num_packages; + + repr = g_string_new ("PackageSearchResult(packages="); + + num_packages = frida_package_list_size (frida_package_search_result_get_packages (PY_GOBJECT_HANDLE (self))); + if (num_packages != 0) + g_string_append_printf (repr, "[<%u package%s>]", num_packages, (num_packages == 1) ? "" : "s"); + else + g_string_append (repr, "[]"); + + g_string_append_printf (repr, ", total=%u)", self->total); + + result = PyUnicode_FromString (repr->str); + + g_string_free (repr, TRUE); + + return result; +} + + +static PyObject * +PyPackageInstallResult_new_take_handle (FridaPackageInstallResult * handle) +{ + return PyGObject_new_take_handle (handle, PYFRIDA_TYPE (PackageInstallResult)); +} + +static int +PyPackageInstallResult_init (PyPackageInstallResult * self, PyObject * args, PyObject * kw) +{ + if (PyGObject_tp_init ((PyObject *) self, args, kw) < 0) + return -1; + + self->packages = NULL; + + return 0; +} + +static void +PyPackageInstallResult_init_from_handle (PyPackageInstallResult * self, FridaPackageInstallResult * handle) +{ + self->packages = PyPackageList_marshal (frida_package_install_result_get_packages (handle)); +} + +static void +PyPackageInstallResult_dealloc (PyPackageInstallResult * self) +{ + Py_DecRef (self->packages); + + PyGObject_tp_dealloc ((PyObject *) self); +} + +static PyObject * +PyPackageInstallResult_repr (PyPackageInstallResult * self) +{ + PyObject * result; + GString * repr; + gint num_packages; + + repr = g_string_new ("PackageInstallResult(packages="); + + num_packages = frida_package_list_size (frida_package_install_result_get_packages (PY_GOBJECT_HANDLE (self))); + if (num_packages != 0) + g_string_append_printf (repr, "[<%u package%s>]", num_packages, (num_packages == 1) ? "" : "s"); + else + g_string_append (repr, "[]"); + + g_string_append (repr, ")"); + + result = PyUnicode_FromString (repr->str); + + g_string_free (repr, TRUE); + + return result; +} + + static int PyFileMonitor_init (PyFileMonitor * self, PyObject * args, PyObject * kw) { @@ -4883,11 +5469,21 @@ PyFileMonitor_init (PyFileMonitor * self, PyObject * args, PyObject * kw) if (!PyArg_ParseTuple (args, "s", &path)) return -1; + g_atomic_int_inc (&toplevel_objects_alive); + PyGObject_take_handle (&self->parent, frida_file_monitor_new (path), PYFRIDA_TYPE (FileMonitor)); return 0; } +static void +PyFileMonitor_dealloc (PyFileMonitor * self) +{ + g_atomic_int_dec_and_test (&toplevel_objects_alive); + + PyGObject_tp_dealloc ((PyObject *) self); +} + static PyObject * PyFileMonitor_enable (PyFileMonitor * self) { @@ -5438,6 +6034,10 @@ PyInit__frida (void) PYFRIDA_REGISTER_TYPE (PortalService, FRIDA_TYPE_PORTAL_SERVICE); PYFRIDA_REGISTER_TYPE (EndpointParameters, FRIDA_TYPE_ENDPOINT_PARAMETERS); PYFRIDA_REGISTER_TYPE (Compiler, FRIDA_TYPE_COMPILER); + PYFRIDA_REGISTER_TYPE (PackageManager, FRIDA_TYPE_PACKAGE_MANAGER); + PYFRIDA_REGISTER_TYPE (Package, FRIDA_TYPE_PACKAGE); + PYFRIDA_REGISTER_TYPE (PackageSearchResult, FRIDA_TYPE_PACKAGE_SEARCH_RESULT); + PYFRIDA_REGISTER_TYPE (PackageInstallResult, FRIDA_TYPE_PACKAGE_INSTALL_RESULT); PYFRIDA_REGISTER_TYPE (FileMonitor, FRIDA_TYPE_FILE_MONITOR); PYFRIDA_REGISTER_TYPE (IOStream, G_TYPE_IO_STREAM); PYFRIDA_REGISTER_TYPE (Cancellable, G_TYPE_CANCELLABLE); diff --git a/frida/core.py b/frida/core.py index 0c65155..830ccee 100644 --- a/frida/core.py +++ b/frida/core.py @@ -1557,7 +1557,7 @@ class CompilerDiagnostic(TypedDict): class Compiler: def __init__(self) -> None: - self._impl = _frida.Compiler(get_device_manager()._impl) + self._impl = _frida.Compiler() def __repr__(self) -> str: return repr(self._impl) @@ -1567,10 +1567,20 @@ def build( self, entrypoint: str, project_root: Optional[str] = None, + output_format: Optional[str] = None, + bundle_format: Optional[str] = None, + type_check: Optional[str] = None, source_maps: Optional[str] = None, compression: Optional[str] = None, ) -> str: - kwargs = {"project_root": project_root, "source_maps": source_maps, "compression": compression} + kwargs = { + "project_root": project_root, + "output_format": output_format, + "bundle_format": bundle_format, + "type_check": type_check, + "source_maps": source_maps, + "compression": compression, + } _filter_missing_kwargs(kwargs) return self._impl.build(entrypoint, **kwargs) @@ -1579,10 +1589,20 @@ def watch( self, entrypoint: str, project_root: Optional[str] = None, + output_format: Optional[str] = None, + bundle_format: Optional[str] = None, + type_check: Optional[str] = None, source_maps: Optional[str] = None, compression: Optional[str] = None, ) -> None: - kwargs = {"project_root": project_root, "source_maps": source_maps, "compression": compression} + kwargs = { + "project_root": project_root, + "output_format": output_format, + "bundle_format": bundle_format, + "type_check": type_check, + "source_maps": source_maps, + "compression": compression, + } _filter_missing_kwargs(kwargs) return self._impl.watch(entrypoint, **kwargs) @@ -1623,6 +1643,93 @@ def off(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.off(signal, callback) +PackageManagerInstallProgressCallback = Callable[ + [ + Literal[ + "initializing", + "preparing-dependencies", + "resolving-package", + "fetching-resource", + "package-already-installed", + "downloading-package", + "package-installed", + "resolving-and-installing-all", + "complete", + ], + float, + Optional[str], + ], + None, +] + +PackageRole = Literal["runtime", "development", "optional", "peer"] + + +class PackageManager: + def __init__(self) -> None: + self._impl = _frida.PackageManager() + + def __repr__(self) -> str: + return repr(self._impl) + + @property + def registry(self): + return self._impl.registry + + @registry.setter + def registry(self, value): + self._impl.registry = value + + @cancellable + def search( + self, + query: str, + offset: Optional[int] = None, + limit: Optional[int] = None, + ) -> _frida.PackageSearchResult: + kwargs = { + "offset": offset, + "limit": limit, + } + _filter_missing_kwargs(kwargs) + return self._impl.search(query, **kwargs) + + @cancellable + def install( + self, + project_root: Optional[str] = None, + role: Optional[PackageRole] = None, + specs: Optional[Sequence[str]] = None, + omits: Optional[Sequence[PackageRole]] = None, + ) -> _frida.PackageInstallResult: + kwargs: Dict[str, Any] = { + "project_root": project_root, + "role": role, + "specs": specs, + "omits": omits, + } + _filter_missing_kwargs(kwargs) + return self._impl.install(**kwargs) + + @overload + def on(self, signal: Literal["install-progress"], callback: PackageManagerInstallProgressCallback) -> None: ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: ... + + def on(self, signal: str, callback: Callable[..., Any]) -> None: + self._impl.on(signal, callback) + + @overload + def off(self, signal: Literal["install-progress"], callback: PackageManagerInstallProgressCallback) -> None: ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: ... + + def off(self, signal: str, callback: Callable[..., Any]) -> None: + self._impl.off(signal, callback) + + class CancellablePollFD: def __init__(self, cancellable: _Cancellable) -> None: self.handle = cancellable.get_fd()