pymobiledevice3 6.1.6__tar.gz → 7.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/python-app.yml +1 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/PKG-INFO +3 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/understanding_idevice_protocol_layers.md +10 -5
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/__main__.py +136 -44
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/_version.py +3 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/bonjour.py +19 -20
- pymobiledevice3-7.0.0/pymobiledevice3/cli/activation.py +46 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/afc.py +64 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/amfi.py +33 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/apps.py +104 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/backup.py +241 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/bonjour.py +31 -29
- pymobiledevice3-7.0.0/pymobiledevice3/cli/cli_common.py +313 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/companion_proxy.py +22 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/crash.py +131 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/__init__.py +387 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/lockdown.py +190 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/mounter.py +99 -57
- pymobiledevice3-7.0.0/pymobiledevice3/cli/notification.py +68 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/pcap.py +36 -20
- pymobiledevice3-7.0.0/pymobiledevice3/cli/power_assertion.py +26 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/processes.py +11 -17
- pymobiledevice3-7.0.0/pymobiledevice3/cli/profile.py +236 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/provision.py +59 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/remote.py +108 -99
- pymobiledevice3-7.0.0/pymobiledevice3/cli/restore.py +252 -0
- pymobiledevice3-7.0.0/pymobiledevice3/cli/springboard.py +90 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/syslog.py +138 -74
- pymobiledevice3-7.0.0/pymobiledevice3/cli/usbmux.py +108 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/version.py +2 -5
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/webinspector.py +149 -103
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/remote_service_discovery.py +11 -10
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/device.py +28 -4
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/service_connection.py +1 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobilebackup2.py +4 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/screenshot.py +2 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/automation_session.py +4 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/cdp_screencast.py +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/element.py +3 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/PKG-INFO +3 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/SOURCES.txt +16 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/requires.txt +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pyproject.toml +20 -6
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pytest.ini +1 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/requirements.txt +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/cli/test_cli.py +4 -4
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/conftest.py +3 -7
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_core_profile_session.py +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_dvt_secure_socket_proxy.py +13 -14
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_fetch_symbols.py +4 -5
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_location.py +4 -5
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_screenshot.py +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_accessibility.py +4 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_afc.py +31 -30
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_apps.py +3 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_backup2.py +3 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_bonjour.py +4 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_crash_reports.py +9 -9
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_house_arrest.py +1 -1
- pymobiledevice3-7.0.0/tests/services/test_list_devices.py +6 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_lockdown.py +3 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_pcapd.py +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_springboard_services_relay.py +4 -3
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_start_tunnel.py +1 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_syslog_relay.py +2 -1
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_tcp_forwarder.py +2 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/test_element.py +3 -2
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_webinspector.py +2 -1
- pymobiledevice3-6.1.6/pymobiledevice3/cli/activation.py +0 -44
- pymobiledevice3-6.1.6/pymobiledevice3/cli/afc.py +0 -56
- pymobiledevice3-6.1.6/pymobiledevice3/cli/amfi.py +0 -38
- pymobiledevice3-6.1.6/pymobiledevice3/cli/apps.py +0 -98
- pymobiledevice3-6.1.6/pymobiledevice3/cli/backup.py +0 -200
- pymobiledevice3-6.1.6/pymobiledevice3/cli/cli_common.py +0 -366
- pymobiledevice3-6.1.6/pymobiledevice3/cli/companion_proxy.py +0 -22
- pymobiledevice3-6.1.6/pymobiledevice3/cli/completions.py +0 -50
- pymobiledevice3-6.1.6/pymobiledevice3/cli/crash.py +0 -88
- pymobiledevice3-6.1.6/pymobiledevice3/cli/developer.py +0 -1560
- pymobiledevice3-6.1.6/pymobiledevice3/cli/diagnostics.py +0 -110
- pymobiledevice3-6.1.6/pymobiledevice3/cli/idam.py +0 -46
- pymobiledevice3-6.1.6/pymobiledevice3/cli/lockdown.py +0 -195
- pymobiledevice3-6.1.6/pymobiledevice3/cli/notification.py +0 -56
- pymobiledevice3-6.1.6/pymobiledevice3/cli/power_assertion.py +0 -27
- pymobiledevice3-6.1.6/pymobiledevice3/cli/profile.py +0 -191
- pymobiledevice3-6.1.6/pymobiledevice3/cli/provision.py +0 -58
- pymobiledevice3-6.1.6/pymobiledevice3/cli/restore.py +0 -247
- pymobiledevice3-6.1.6/pymobiledevice3/cli/springboard.py +0 -90
- pymobiledevice3-6.1.6/pymobiledevice3/cli/usbmux.py +0 -69
- pymobiledevice3-6.1.6/tests/services/test_list_devices.py +0 -5
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.gitattributes +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/FUNDING.yml +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/pull_request_template.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/codeql.yml +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/generate-executable.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/markdown-lint.yml +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/python-publish.yml +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.gitignore +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.pre-commit-config.yaml +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/CODE_OF_CONDUCT.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/CONTRIBUTING.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/LICENSE +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/README.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/example.gif +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/markdownlint-config.json +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/DTServices-14.2.txt +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/DTServices-14.5.txt +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/RemoteXPC.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/plist_sniffer.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/pyinstaller.md +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/remotexpc_sniffer.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/usbmux_sniff.sh +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/ca.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/common.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/exceptions.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/irecv.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/irecv_devices.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/lockdown.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/lockdown_service_provider.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/os_utils.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/posix_util.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/win_util.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/pair_records.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/common.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/app_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/core_device_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/device_info.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/diagnostics_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/file_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/module_imports.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/remote_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/remotexpc.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/tunnel_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/utils.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/xpc_message.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/dsc_uuid_map.json +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/dsc_uuid_map.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/firmware_notifications.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/notifications.txt +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/element_attribute.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/element_clear.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/enter_fullscreen.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/find_nodes.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/focus.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/get_attribute.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/is_displayed.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/is_editable.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/is_enabled.js +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/asr.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/base_restore.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/consts.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/fdr.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/ftab.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/img4.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/mbn.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/recovery.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/restore.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/restore_options.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/restored_client.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/tss.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/accessibilityaudit.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/afc.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/amfi.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/companion.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/crash_reports.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/debugserver_applist.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/device_arbitration.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/device_link.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/diagnostics.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dtfetchsymbols.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/application_listing.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/condition_inducer.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/device_info.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/energy_monitor.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/graphics.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/location_simulation.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/location_simulation_base.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/network_monitor.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/notifications.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/process_control.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/screenshot.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/sysmontap.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/testmanaged/xcuitest.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/file_relay.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/heartbeat.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/house_arrest.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/idam.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/installation_proxy.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/lockdown_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/misagent.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobile_activation.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobile_config.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobile_image_mounter.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/notification_proxy.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/os_trace.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/pcapd.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/power_assertion.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/preboard.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/remote_fetch_symbols.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/remote_server.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/restore_service.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/simulate_location.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/springboard.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/syslog.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/alert.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/cdp_server.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/cdp_target.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/driver.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/inspector_session.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/selenium_api.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/session_protocol.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/switch_to.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/webinspector.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tcp_forwarder.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tunneld/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tunneld/api.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tunneld/server.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/usbmux.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/utils.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/dependency_links.txt +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/entry_points.txt +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/top_level.txt +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/setup.cfg +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/cli/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/__init__.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/common.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/conftest.py +0 -0
- {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/test_driver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymobiledevice3
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.0
|
|
4
4
|
Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
|
|
5
5
|
Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
6
6
|
Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
@@ -23,7 +23,6 @@ License-File: LICENSE
|
|
|
23
23
|
Requires-Dist: construct>=2.9.29
|
|
24
24
|
Requires-Dist: construct-typing>=0.7.0
|
|
25
25
|
Requires-Dist: asn1
|
|
26
|
-
Requires-Dist: click
|
|
27
26
|
Requires-Dist: coloredlogs
|
|
28
27
|
Requires-Dist: IPython
|
|
29
28
|
Requires-Dist: bpylist2>=4.0.1
|
|
@@ -61,6 +60,8 @@ Requires-Dist: sslpsk-pmd3>=1.0.3; python_version < "3.13"
|
|
|
61
60
|
Requires-Dist: python-pcapng>=2.1.1
|
|
62
61
|
Requires-Dist: plumbum
|
|
63
62
|
Requires-Dist: pyimg4>=0.8.8
|
|
63
|
+
Requires-Dist: typer>=0.20.0
|
|
64
|
+
Requires-Dist: typer-injector>=0.2.0
|
|
64
65
|
Provides-Extra: test
|
|
65
66
|
Requires-Dist: pytest; extra == "test"
|
|
66
67
|
Requires-Dist: pytest-asyncio; extra == "test"
|
{pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/understanding_idevice_protocol_layers.md
RENAMED
|
@@ -287,14 +287,14 @@ pymobiledevice3 developer dvt launch com.apple.mobilesafari
|
|
|
287
287
|
|
|
288
288
|
## DVT
|
|
289
289
|
|
|
290
|
-
One of the more interesting developer services
|
|
290
|
+
One of the more interesting developer services is the one exposed by `DTServiceHub`. It is using DTX protocol messages,
|
|
291
291
|
but since it mainly wraps and allows access to stuff in `DVTFoundation.framework` we called it DVT in our
|
|
292
292
|
implementation (probably standing for DeveloperTools).
|
|
293
293
|
|
|
294
294
|
We don't delve too much into this protocol, but we'll say in general it allows us to invoke a whitelist of ObjC methods
|
|
295
295
|
in different ObjC objects. The terminology used by DVT to each such ObjC object is called "channels".
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
To access this different object use the following APIs:
|
|
298
298
|
|
|
299
299
|
```python
|
|
300
300
|
from pymobiledevice3.lockdown import create_using_usbmux
|
|
@@ -313,17 +313,22 @@ dvt_channel = Screenshot(dvt)
|
|
|
313
313
|
open('/tmp/screen.png', 'wb').write(dvt_channel.get_screenshot())
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
-
Looking for an unimplemented feature/channel? Feel free to play with it (and submit a PR
|
|
316
|
+
Looking for an unimplemented feature/channel? Feel free to play with it (and submit a PR afterward 🙏) using the
|
|
317
317
|
following shell:
|
|
318
318
|
|
|
319
319
|
```shell
|
|
320
320
|
pymobiledevice3 developer dvt shell
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
+
> **NOTE:** The full list of the methods that can be invoked on a DVT channel can be found by looking at all ObjC
|
|
324
|
+
> classes in `DVTInstrumentsFoundation.framework` implementing the `DTXAllowedRPC` protocol.
|
|
325
|
+
> There is an existing [Anubis rule](https://github.com/netanelc305/anubis/blob/9da337178ebd7e9f168e9df2d82b192eba4f1b30/example_rules.yaml#L14-L17)
|
|
326
|
+
> I use to diff new methods against the existing ones.
|
|
327
|
+
|
|
323
328
|
## RemoteXPC
|
|
324
329
|
|
|
325
330
|
Starting at iOS 17.0, Apple made a large refactor in the manner we all interact with the developer services. There can
|
|
326
|
-
be multiple reasons for that decision, but in general
|
|
331
|
+
be multiple reasons for that decision, but in general these refactor main key points are:
|
|
327
332
|
|
|
328
333
|
- Create a single standard for interacting with the new lockdown services (XPC Messages, Apple's proprietary IPC)
|
|
329
334
|
- Optimize the protocol for large file transfers (such as the dyld_shared_cache)
|
|
@@ -347,7 +352,7 @@ Since all this communication is IP-based, but without any additional exported TC
|
|
|
347
352
|
help us here. Instead, starting at iOS 16.0, when connecting an iDevice, it exports another non-standard USB-Ethernet
|
|
348
353
|
adapter (with IPv6 link-local address), placing us in a subnet with the device's `remoted`.
|
|
349
354
|
|
|
350
|
-
As we've said this communication is non-standard
|
|
355
|
+
As we've said, this communication is non-standard and requires either:
|
|
351
356
|
|
|
352
357
|
- macOS Monterey or higher
|
|
353
358
|
- Special driver on your linux/Windows machine
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import difflib
|
|
3
|
+
import importlib
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
@@ -7,13 +8,18 @@ import sys
|
|
|
7
8
|
import textwrap
|
|
8
9
|
import traceback
|
|
9
10
|
import warnings
|
|
10
|
-
from
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
from typing import Annotated, Optional, Union
|
|
11
13
|
|
|
12
14
|
import click
|
|
13
15
|
import coloredlogs
|
|
16
|
+
import typer
|
|
17
|
+
import typer.core
|
|
14
18
|
from packaging.version import Version
|
|
19
|
+
from typer.core import TyperGroup
|
|
20
|
+
from typer_injector import InjectingTyper
|
|
15
21
|
|
|
16
|
-
from pymobiledevice3.cli.cli_common import TUNNEL_ENV_VAR, isatty
|
|
22
|
+
from pymobiledevice3.cli.cli_common import TUNNEL_ENV_VAR, isatty, set_color_flag, set_verbosity
|
|
17
23
|
from pymobiledevice3.exceptions import (
|
|
18
24
|
AccessDeniedError,
|
|
19
25
|
CloudConfigurationAlreadyPresentError,
|
|
@@ -112,25 +118,26 @@ CLI_GROUPS = {
|
|
|
112
118
|
"webinspector": "webinspector",
|
|
113
119
|
"idam": "idam",
|
|
114
120
|
"version": "version",
|
|
115
|
-
"install-completions": "completions",
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
# Set if used the `--reconnect` option
|
|
119
124
|
RECONNECT = False
|
|
120
125
|
|
|
121
126
|
|
|
122
|
-
class
|
|
123
|
-
def list_commands(self, ctx):
|
|
124
|
-
|
|
127
|
+
class Pmd3TyperGroup(TyperGroup):
|
|
128
|
+
def list_commands(self, ctx: click.Context) -> list[str]:
|
|
129
|
+
# Order is preserved by dict insertion; adjust if you want alphabetical
|
|
130
|
+
return list(CLI_GROUPS.keys())
|
|
125
131
|
|
|
126
|
-
def get_command(self, ctx: click.Context,
|
|
127
|
-
if
|
|
128
|
-
self.handle_invalid_command(ctx,
|
|
129
|
-
return self.import_and_get_command(ctx,
|
|
132
|
+
def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command:
|
|
133
|
+
if cmd_name not in CLI_GROUPS:
|
|
134
|
+
self.handle_invalid_command(ctx, cmd_name)
|
|
135
|
+
return self.import_and_get_command(ctx, cmd_name)
|
|
130
136
|
|
|
131
|
-
def handle_invalid_command(self, ctx
|
|
137
|
+
def handle_invalid_command(self, ctx, name: str) -> None:
|
|
132
138
|
suggested_commands = self.search_commands(name)
|
|
133
139
|
suggestion = self.format_suggestions(suggested_commands)
|
|
140
|
+
# ctx.fail raises a ClickException underneath, which Typer displays nicely
|
|
134
141
|
ctx.fail(f"No such command {name!r}{suggestion}")
|
|
135
142
|
|
|
136
143
|
@staticmethod
|
|
@@ -138,70 +145,109 @@ class Pmd3Cli(click.Group):
|
|
|
138
145
|
if not suggestions:
|
|
139
146
|
return ""
|
|
140
147
|
cmds = textwrap.indent("\n".join(suggestions), " " * 4)
|
|
141
|
-
return f"\nDid you mean
|
|
148
|
+
return f"\nDid you mean:\n{cmds}"
|
|
142
149
|
|
|
143
150
|
@staticmethod
|
|
144
151
|
def import_and_get_command(ctx: click.Context, name: str) -> click.Command:
|
|
145
152
|
module_name = f"pymobiledevice3.cli.{CLI_GROUPS[name]}"
|
|
146
|
-
mod =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
command = mod.cli.get_command(ctx, command_name)
|
|
151
|
-
return command
|
|
153
|
+
mod = importlib.import_module(module_name)
|
|
154
|
+
# submodules expose a Typer Group named "cli"
|
|
155
|
+
cli: typer.Typer = mod.cli
|
|
156
|
+
return typer.main.get_command(cli)
|
|
152
157
|
|
|
153
158
|
@staticmethod
|
|
154
159
|
def highlight_keyword(text: str, keyword: str) -> str:
|
|
155
|
-
return re.sub(f"({keyword})",
|
|
160
|
+
return re.sub(f"({keyword})", typer.style("\\1", bold=True), text, flags=re.IGNORECASE)
|
|
156
161
|
|
|
157
162
|
@staticmethod
|
|
158
|
-
def collect_commands(command: click.Command) -> Union[str, list[str]]:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
for
|
|
162
|
-
|
|
163
|
-
if isinstance(
|
|
164
|
-
|
|
163
|
+
def collect_commands(command: Union[TyperGroup, click.Command]) -> Union[str, list[str]]:
|
|
164
|
+
if isinstance(command, TyperGroup): # group
|
|
165
|
+
cmds = []
|
|
166
|
+
for v in command.commands.values():
|
|
167
|
+
child = Pmd3TyperGroup.collect_commands(v)
|
|
168
|
+
if isinstance(child, list):
|
|
169
|
+
cmds.extend([f"{command.name} {c}" for c in child])
|
|
165
170
|
else:
|
|
166
|
-
|
|
167
|
-
return
|
|
168
|
-
return
|
|
171
|
+
cmds.append(f"{command.name} {child}")
|
|
172
|
+
return cmds
|
|
173
|
+
return command.name or ""
|
|
169
174
|
|
|
170
175
|
@staticmethod
|
|
171
176
|
def search_commands(pattern: str) -> list[str]:
|
|
172
|
-
all_commands =
|
|
177
|
+
all_commands = Pmd3TyperGroup.load_all_commands()
|
|
173
178
|
matched = sorted(filter(lambda cmd: re.search(pattern, cmd), all_commands))
|
|
174
179
|
if not matched:
|
|
175
180
|
matched = difflib.get_close_matches(pattern, all_commands, n=20, cutoff=0.4)
|
|
176
181
|
if isatty():
|
|
177
|
-
matched = [
|
|
182
|
+
matched = [Pmd3TyperGroup.highlight_keyword(cmd, pattern) for cmd in matched]
|
|
178
183
|
return matched
|
|
179
184
|
|
|
180
185
|
@staticmethod
|
|
181
186
|
def load_all_commands() -> list[str]:
|
|
182
|
-
all_commands = []
|
|
187
|
+
all_commands: list[str] = []
|
|
183
188
|
for key in CLI_GROUPS:
|
|
184
189
|
module_name = f"pymobiledevice3.cli.{CLI_GROUPS[key]}"
|
|
185
|
-
mod =
|
|
186
|
-
|
|
190
|
+
mod = importlib.import_module(module_name)
|
|
191
|
+
if isinstance(mod.cli, typer.Typer):
|
|
192
|
+
cmd = Pmd3TyperGroup.collect_commands(typer.main.get_group(mod.cli))
|
|
193
|
+
else:
|
|
194
|
+
cmd = Pmd3TyperGroup.collect_commands(mod.cli.commands[key])
|
|
187
195
|
if isinstance(cmd, list):
|
|
188
196
|
all_commands.extend(cmd)
|
|
189
197
|
else:
|
|
190
198
|
all_commands.append(cmd)
|
|
191
199
|
return all_commands
|
|
192
200
|
|
|
201
|
+
def resolve_command(
|
|
202
|
+
self, ctx: click.Context, args: list[str]
|
|
203
|
+
) -> tuple[Optional[str], Optional[click.Command], list[str]]:
|
|
204
|
+
return super().resolve_command(ctx, args)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
app = InjectingTyper(
|
|
208
|
+
cls=Pmd3TyperGroup,
|
|
209
|
+
context_settings=CONTEXT_SETTINGS,
|
|
210
|
+
no_args_is_help=True,
|
|
211
|
+
# add_completion=False,
|
|
212
|
+
rich_markup_mode="markdown",
|
|
213
|
+
help=(
|
|
214
|
+
"Swiss-army CLI for pairing, inspecting, backing up, and automating iOS devices.\n\n"
|
|
215
|
+
"Docs and examples: https://github.com/doronz88/pymobiledevice3"
|
|
216
|
+
),
|
|
217
|
+
)
|
|
218
|
+
|
|
193
219
|
|
|
194
|
-
@
|
|
195
|
-
|
|
196
|
-
|
|
220
|
+
@app.callback()
|
|
221
|
+
def _root(
|
|
222
|
+
reconnect: Annotated[
|
|
223
|
+
bool,
|
|
224
|
+
typer.Option(
|
|
225
|
+
"--reconnect",
|
|
226
|
+
help="Automatically reconnect if the device disconnects mid-command.",
|
|
227
|
+
show_default=False,
|
|
228
|
+
),
|
|
229
|
+
] = False,
|
|
230
|
+
verbosity: Annotated[
|
|
231
|
+
int,
|
|
232
|
+
typer.Option(
|
|
233
|
+
"--verbose",
|
|
234
|
+
"-v",
|
|
235
|
+
count=True,
|
|
236
|
+
help="Increase logging verbosity (repeat for more detail).",
|
|
237
|
+
),
|
|
238
|
+
] = 0,
|
|
239
|
+
color: Annotated[
|
|
240
|
+
bool,
|
|
241
|
+
typer.Option(help="Colorize output; disable with --no-color for plain logs."),
|
|
242
|
+
] = True,
|
|
243
|
+
) -> None:
|
|
197
244
|
"""
|
|
198
|
-
|
|
199
|
-
Interact with a connected iDevice (iPhone, iPad, ...)
|
|
200
|
-
For more information please look at:
|
|
201
|
-
https://github.com/doronz88/pymobiledevice3
|
|
245
|
+
Top-level options for pymobiledevice3.
|
|
202
246
|
"""
|
|
203
247
|
global RECONNECT
|
|
204
248
|
RECONNECT = reconnect
|
|
249
|
+
set_verbosity(verbosity)
|
|
250
|
+
set_color_flag(color)
|
|
205
251
|
|
|
206
252
|
|
|
207
253
|
def device_might_need_tunneld(identifier: str) -> bool:
|
|
@@ -221,13 +267,53 @@ def device_might_need_tunneld(identifier: str) -> bool:
|
|
|
221
267
|
return Version(lockdown.product_version) >= Version("17.0")
|
|
222
268
|
|
|
223
269
|
|
|
270
|
+
class PossiblyMisplacedOption(click.NoSuchOption):
|
|
271
|
+
def __init__(
|
|
272
|
+
self,
|
|
273
|
+
option_name: str,
|
|
274
|
+
message: Optional[str] = None,
|
|
275
|
+
possibilities: Optional[Sequence[str]] = None,
|
|
276
|
+
ctx: Optional[click.Context] = None,
|
|
277
|
+
suggested_ctx: Optional[click.Context] = None,
|
|
278
|
+
) -> None:
|
|
279
|
+
super().__init__(option_name, message, possibilities, ctx)
|
|
280
|
+
if suggested_ctx is not None:
|
|
281
|
+
if ctx is not None:
|
|
282
|
+
self.message += f" for subcommand: {ctx.command_path}"
|
|
283
|
+
|
|
284
|
+
suggestion = f"{suggested_ctx.command_path} {option_name}"
|
|
285
|
+
suggestion += ctx.command_path.removeprefix(suggested_ctx.command_path) if ctx is not None else " ..."
|
|
286
|
+
|
|
287
|
+
self.message += f"\nDid you mean: {suggestion}?"
|
|
288
|
+
|
|
289
|
+
@staticmethod
|
|
290
|
+
def from_no_such_option(e: click.NoSuchOption) -> "PossiblyMisplacedOption":
|
|
291
|
+
ctx = e.ctx
|
|
292
|
+
while ctx:
|
|
293
|
+
for param in ctx.command.params:
|
|
294
|
+
if isinstance(param, typer.core.TyperOption) and (
|
|
295
|
+
e.option_name in param.opts or e.option_name in param.secondary_opts
|
|
296
|
+
):
|
|
297
|
+
break
|
|
298
|
+
else:
|
|
299
|
+
ctx = ctx.parent
|
|
300
|
+
continue
|
|
301
|
+
break
|
|
302
|
+
|
|
303
|
+
return PossiblyMisplacedOption(e.option_name, e.message, e.possibilities, e.ctx, ctx)
|
|
304
|
+
|
|
305
|
+
|
|
224
306
|
def invoke_cli_with_error_handling() -> bool:
|
|
225
307
|
"""
|
|
226
308
|
Invoke the command line interface and return `True` if the failure reason of the command was that the device was
|
|
227
309
|
disconnected.
|
|
228
310
|
"""
|
|
229
311
|
try:
|
|
230
|
-
|
|
312
|
+
# Typer apps are callable; this executes the CLI with current sys.argv
|
|
313
|
+
try:
|
|
314
|
+
app(standalone_mode=False)
|
|
315
|
+
except click.NoSuchOption as e:
|
|
316
|
+
raise PossiblyMisplacedOption.from_no_such_option(e) from e
|
|
231
317
|
except NoDeviceConnectedError:
|
|
232
318
|
logger.error("Device is not connected")
|
|
233
319
|
return True
|
|
@@ -277,9 +363,10 @@ def invoke_cli_with_error_handling() -> bool:
|
|
|
277
363
|
logger.warning("Got an InvalidServiceError. Trying again over tunneld since it is a developer command")
|
|
278
364
|
should_retry_over_tunneld = True
|
|
279
365
|
if should_retry_over_tunneld:
|
|
280
|
-
# use a single space because
|
|
366
|
+
# use a single space because Typer/Click will ignore envvars of empty strings
|
|
281
367
|
os.environ[TUNNEL_ENV_VAR] = e.identifier or " "
|
|
282
|
-
|
|
368
|
+
main()
|
|
369
|
+
return False
|
|
283
370
|
logger.error(INVALID_SERVICE_MESSAGE)
|
|
284
371
|
except PasswordRequiredError:
|
|
285
372
|
logger.error("Device is password protected. Please unlock and retry")
|
|
@@ -315,6 +402,11 @@ def invoke_cli_with_error_handling() -> bool:
|
|
|
315
402
|
)
|
|
316
403
|
except QuicProtocolNotSupportedError:
|
|
317
404
|
logger.error("Encountered a QUIC protocol error.")
|
|
405
|
+
except click.ClickException as e:
|
|
406
|
+
from typer import rich_utils
|
|
407
|
+
|
|
408
|
+
rich_utils.rich_format_error(e)
|
|
409
|
+
sys.exit(e.exit_code)
|
|
318
410
|
|
|
319
411
|
return False
|
|
320
412
|
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '
|
|
32
|
-
__version_tuple__ = version_tuple = (
|
|
31
|
+
__version__ = version = '7.0.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (7, 0, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g363ad89c4'
|
|
@@ -300,10 +300,9 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
|
|
|
300
300
|
adapters = _Adapters()
|
|
301
301
|
|
|
302
302
|
ptr_targets: set[str] = set()
|
|
303
|
-
srv_map: dict[str, dict] = {}
|
|
303
|
+
srv_map: dict[str, list[dict]] = defaultdict(list) # instance_name -> list of {"target", "port"}
|
|
304
304
|
txt_map: dict[str, dict] = {}
|
|
305
|
-
# host -> list[(ip, iface)]
|
|
306
|
-
host_addrs: dict[str, list[Address]] = defaultdict(list)
|
|
305
|
+
host_addrs: dict[str, list[Address]] = defaultdict(list) # host -> list[(ip, iface)]
|
|
307
306
|
|
|
308
307
|
def _record_addr(rr_name: str, ip_str: str, pkt_addr):
|
|
309
308
|
# Determine family and possible scopeid from the packet that delivered this RR
|
|
@@ -314,7 +313,7 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
|
|
|
314
313
|
iface = adapters.pick_iface_for_ip(ip_str, family, scopeid)
|
|
315
314
|
if iface is None:
|
|
316
315
|
return
|
|
317
|
-
#
|
|
316
|
+
# Avoid duplicates for the same host/ip
|
|
318
317
|
existing = host_addrs[rr_name]
|
|
319
318
|
if not any(a.ip == ip_str for a in existing):
|
|
320
319
|
existing.append(Address(ip=ip_str, iface=iface))
|
|
@@ -333,11 +332,10 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
|
|
|
333
332
|
if t == QTYPE_PTR and rr.get("name") == service_type:
|
|
334
333
|
ptr_targets.add(rr.get("ptrdname"))
|
|
335
334
|
elif t == QTYPE_SRV:
|
|
336
|
-
srv_map[rr["name"]]
|
|
337
|
-
"target": rr.get("target"),
|
|
338
|
-
"port": rr.get("port"),
|
|
339
|
-
}
|
|
335
|
+
srv_map[rr["name"]].append({"target": rr.get("target"), "port": rr.get("port")})
|
|
340
336
|
elif t == QTYPE_TXT:
|
|
337
|
+
# TODO: This could possibly mix the properties of multiple TXT records for the same instance.
|
|
338
|
+
# However, it's currently unused.
|
|
341
339
|
txt_map[rr["name"]] = rr.get("txt", {})
|
|
342
340
|
elif (t == QTYPE_A and rr.get("address")) or (t == QTYPE_AAAA and rr.get("address")):
|
|
343
341
|
_record_addr(rr["name"], rr["address"], pkt_addr)
|
|
@@ -348,20 +346,21 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
|
|
|
348
346
|
# Assemble dataclasses
|
|
349
347
|
results: list[ServiceInstance] = []
|
|
350
348
|
for inst in sorted(ptr_targets):
|
|
351
|
-
|
|
352
|
-
target = srv.get("target")
|
|
353
|
-
host = (target[:-1] if target and target.endswith(".") else target) or None
|
|
354
|
-
addrs = host_addrs.get(target, []) if target else []
|
|
349
|
+
srv_entries = srv_map.get(inst, [])
|
|
355
350
|
props = txt_map.get(inst, {})
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
351
|
+
for srv in srv_entries:
|
|
352
|
+
target = srv.get("target")
|
|
353
|
+
host = (target[:-1] if target and target.endswith(".") else target) or None
|
|
354
|
+
addrs = host_addrs.get(target, []) if target else []
|
|
355
|
+
results.append(
|
|
356
|
+
ServiceInstance(
|
|
357
|
+
instance=inst,
|
|
358
|
+
host=host,
|
|
359
|
+
port=srv.get("port"),
|
|
360
|
+
addresses=addrs,
|
|
361
|
+
properties=props,
|
|
362
|
+
)
|
|
363
363
|
)
|
|
364
|
-
)
|
|
365
364
|
return results
|
|
366
365
|
|
|
367
366
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from typer_injector import InjectingTyper
|
|
5
|
+
|
|
6
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep
|
|
7
|
+
from pymobiledevice3.services.mobile_activation import MobileActivationService
|
|
8
|
+
|
|
9
|
+
cli = InjectingTyper(
|
|
10
|
+
name="activation",
|
|
11
|
+
help="Perform iCloud activation/deactivation or query the current state",
|
|
12
|
+
no_args_is_help=True,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@cli.command()
|
|
17
|
+
def state(service_provider: ServiceProviderDep) -> None:
|
|
18
|
+
"""Get current activation state"""
|
|
19
|
+
print(MobileActivationService(service_provider).state)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@cli.command()
|
|
23
|
+
def activate(
|
|
24
|
+
service_provider: ServiceProviderDep,
|
|
25
|
+
now: Annotated[
|
|
26
|
+
bool,
|
|
27
|
+
typer.Option(help="do not wait for next nonce cycle"),
|
|
28
|
+
] = False,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Activate device"""
|
|
31
|
+
activation_service = MobileActivationService(service_provider)
|
|
32
|
+
if not now:
|
|
33
|
+
activation_service.wait_for_activation_session()
|
|
34
|
+
activation_service.activate()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@cli.command()
|
|
38
|
+
def deactivate(service_provider: ServiceProviderDep) -> None:
|
|
39
|
+
"""Deactivate device"""
|
|
40
|
+
MobileActivationService(service_provider).deactivate()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@cli.command()
|
|
44
|
+
def itunes(service_provider: ServiceProviderDep) -> None:
|
|
45
|
+
"""Tell the device that it has been connected to iTunes (useful for < iOS 4)"""
|
|
46
|
+
service_provider.set_value(True, key="iTunesHasConnected")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from typer_injector import InjectingTyper
|
|
6
|
+
|
|
7
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep
|
|
8
|
+
from pymobiledevice3.services.afc import AfcService, AfcShell
|
|
9
|
+
|
|
10
|
+
cli = InjectingTyper(
|
|
11
|
+
name="afc",
|
|
12
|
+
help="Browse, push, and pull files via the AFC service (/var/mobile/Media).",
|
|
13
|
+
no_args_is_help=True,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@cli.command("shell")
|
|
18
|
+
def afc_shell(service_provider: ServiceProviderDep) -> None:
|
|
19
|
+
"""Open an interactive AFC shell rooted at /var/mobile/Media."""
|
|
20
|
+
AfcShell.create(service_provider)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@cli.command("pull")
|
|
24
|
+
def afc_pull(
|
|
25
|
+
service_provider: ServiceProviderDep,
|
|
26
|
+
remote_file: Path,
|
|
27
|
+
local_file: Path,
|
|
28
|
+
ignore_errors: Annotated[
|
|
29
|
+
bool,
|
|
30
|
+
typer.Option(
|
|
31
|
+
"--ignore-errors",
|
|
32
|
+
"-i",
|
|
33
|
+
help="Continue downloading even if some files error (best-effort pull).",
|
|
34
|
+
),
|
|
35
|
+
],
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Download a remote path under /var/mobile/Media to the local filesystem."""
|
|
38
|
+
AfcService(lockdown=service_provider).pull(str(remote_file), str(local_file), ignore_errors=ignore_errors)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@cli.command("push")
|
|
42
|
+
def afc_push(service_provider: ServiceProviderDep, local_file: Path, remote_file: Path) -> None:
|
|
43
|
+
"""Upload a local file into /var/mobile/Media."""
|
|
44
|
+
AfcService(lockdown=service_provider).push(str(local_file), str(remote_file))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@cli.command("ls")
|
|
48
|
+
def afc_ls(
|
|
49
|
+
service_provider: ServiceProviderDep,
|
|
50
|
+
remote_file: Path,
|
|
51
|
+
recursive: Annotated[
|
|
52
|
+
bool,
|
|
53
|
+
typer.Option("--recursive", "-r", help="Recurse into subdirectories when listing."),
|
|
54
|
+
] = False,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""List files under /var/mobile/Media (optionally recursively)."""
|
|
57
|
+
for path in AfcService(lockdown=service_provider).dirlist(str(remote_file), -1 if recursive else 1):
|
|
58
|
+
print(path)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@cli.command("rm")
|
|
62
|
+
def afc_rm(service_provider: ServiceProviderDep, remote_file: Path) -> None:
|
|
63
|
+
"""Delete a file under /var/mobile/Media."""
|
|
64
|
+
AfcService(lockdown=service_provider).rm(str(remote_file))
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from typer_injector import InjectingTyper
|
|
4
|
+
|
|
5
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json
|
|
6
|
+
from pymobiledevice3.services.amfi import AmfiService
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
cli = InjectingTyper(
|
|
12
|
+
name="amfi",
|
|
13
|
+
help="Enable developer-mode or query its state",
|
|
14
|
+
no_args_is_help=True,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@cli.command()
|
|
19
|
+
def reveal_developer_mode(service_provider: ServiceProviderDep) -> None:
|
|
20
|
+
"""reveal developer mode option in device's UI"""
|
|
21
|
+
AmfiService(service_provider).reveal_developer_mode_option_in_ui()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@cli.command()
|
|
25
|
+
def enable_developer_mode(service_provider: ServiceProviderDep) -> None:
|
|
26
|
+
"""enable developer mode"""
|
|
27
|
+
AmfiService(service_provider).enable_developer_mode()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@cli.command()
|
|
31
|
+
def developer_mode_status(service_provider: ServiceProviderDep) -> None:
|
|
32
|
+
"""query developer mode status"""
|
|
33
|
+
print_json(service_provider.developer_mode_status)
|