Skip to content

Commit

Permalink
cli: make almost all outputs json colored
Browse files Browse the repository at this point in the history
  • Loading branch information
doronz88 committed Jun 3, 2021
1 parent a991859 commit 1ce5cf8
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 50 deletions.
13 changes: 6 additions & 7 deletions pymobiledevice3/cli/apps.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from pprint import pprint

import click

from pymobiledevice3.cli.cli_common import Command
from pymobiledevice3.cli.cli_common import Command, print_json
from pymobiledevice3.services.house_arrest import HouseArrestService
from pymobiledevice3.services.installation_proxy import InstallationProxyService

Expand All @@ -20,30 +18,31 @@ def apps():


@apps.command('list', cls=Command)
@click.option('--nocolor', is_flag=True)
@click.option('-u', '--user', is_flag=True, help='include user apps')
@click.option('-s', '--system', is_flag=True, help='include system apps')
def apps_list(lockdown, user, system):
def apps_list(lockdown, nocolor, user, system):
""" list installed apps """
app_types = []
if user:
app_types.append('User')
if system:
app_types.append('System')
pprint(InstallationProxyService(lockdown=lockdown).get_apps(app_types))
print_json(InstallationProxyService(lockdown=lockdown).get_apps(app_types), colored=not nocolor)


@apps.command('uninstall', cls=Command)
@click.argument('bundle_id')
def uninstall(lockdown, bundle_id):
""" uninstall app by given bundle_id """
pprint(InstallationProxyService(lockdown=lockdown).uninstall(bundle_id))
InstallationProxyService(lockdown=lockdown).uninstall(bundle_id)


@apps.command('install', cls=Command)
@click.argument('ipa_path', type=click.Path(exists=True))
def install(lockdown, ipa_path):
""" install given .ipa """
pprint(InstallationProxyService(lockdown=lockdown).install_from_local(ipa_path))
InstallationProxyService(lockdown=lockdown).install_from_local(ipa_path)


@apps.command('afc', cls=Command)
Expand Down
10 changes: 5 additions & 5 deletions pymobiledevice3/cli/cli_common.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import json
import os
from pprint import pprint

import click
from pygments import highlight, lexers, formatters

from pymobiledevice3.lockdown import LockdownClient


def print_object(buf, colored=True, default=None):
def print_json(buf, colored=True, default=None):
formatted_json = json.dumps(buf, sort_keys=True, indent=4, default=default)
if colored:
formatted_json = json.dumps(buf, sort_keys=True, indent=4, default=default)
colorful_json = highlight(formatted_json, lexers.JsonLexer(), formatters.TerminalFormatter())
colorful_json = highlight(formatted_json, lexers.JsonLexer(),
formatters.TerminalTrueColorFormatter(style='stata-dark'))
print(colorful_json)
else:
pprint(buf)
print(formatted_json)


class Command(click.Command):
Expand Down
12 changes: 6 additions & 6 deletions pymobiledevice3/cli/developer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import click
from termcolor import colored

from pymobiledevice3.cli.cli_common import print_object, Command
from pymobiledevice3.cli.cli_common import print_json, Command
from pymobiledevice3.exceptions import DvtDirListError
from pymobiledevice3.lockdown import LockdownClient
from pymobiledevice3.services.dvt.dvt_secure_socket_proxy import DvtSecureSocketProxyService
Expand Down Expand Up @@ -64,7 +64,7 @@ def proclist(lockdown, nocolor):
if 'startDate' in process:
process['startDate'] = str(process['startDate'])

print_object(processes, colored=not nocolor)
print_json(processes, colored=not nocolor)


@developer.command('applist', cls=Command)
Expand All @@ -73,7 +73,7 @@ def applist(lockdown, nocolor):
""" show application list """
with DvtSecureSocketProxyService(lockdown=lockdown) as dvt:
apps = ApplicationListing(dvt).applist()
print_object(apps, colored=not nocolor)
print_json(apps, colored=not nocolor)


@developer.command('kill', cls=Command)
Expand Down Expand Up @@ -156,7 +156,7 @@ def device_information(lockdown, nocolor):
""" Print system information. """
with DvtSecureSocketProxyService(lockdown=lockdown) as dvt:
device_info = DeviceInfo(dvt)
print_object({
print_json({
'system': device_info.system_information(),
'hardware': device_info.hardware_information(),
'network': device_info.network_information(),
Expand Down Expand Up @@ -371,7 +371,7 @@ def stackshot(lockdown, out, nocolor):
if out is not None:
json.dump(data, out, indent=4)
else:
print_object(data, colored=not nocolor)
print_json(data, colored=not nocolor)


def parse_live_print(tap, pid, show_tid, parsed):
Expand Down Expand Up @@ -427,7 +427,7 @@ def trace_codes(lockdown, nocolor):
""" Print system information. """
with DvtSecureSocketProxyService(lockdown=lockdown) as dvt:
device_info = DeviceInfo(dvt)
print_object({hex(k): v for k, v in device_info.trace_codes().items()}, colored=not nocolor)
print_json({hex(k): v for k, v in device_info.trace_codes().items()}, colored=not nocolor)


@developer.command('oslog', cls=Command)
Expand Down
7 changes: 4 additions & 3 deletions pymobiledevice3/cli/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import click

from pymobiledevice3.cli.cli_common import Command
from pymobiledevice3.cli.cli_common import Command, print_json
from pymobiledevice3.services.diagnostics import DiagnosticsService


Expand Down Expand Up @@ -37,9 +37,10 @@ def diagnostics_sleep(lockdown):


@diagnostics.command('info', cls=Command)
def diagnostics_info(lockdown):
@click.option('--nocolor', is_flag=True)
def diagnostics_info(lockdown, nocolor):
""" get diagnostics info """
pprint(DiagnosticsService(lockdown=lockdown).info())
print_json(DiagnosticsService(lockdown=lockdown).info(), colored=not nocolor)


@diagnostics.command('ioregistry', cls=Command)
Expand Down
4 changes: 2 additions & 2 deletions pymobiledevice3/cli/list_devices.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import click

from pymobiledevice3 import usbmux
from pymobiledevice3.cli.cli_common import print_object
from pymobiledevice3.cli.cli_common import print_json
from pymobiledevice3.lockdown import LockdownClient


Expand All @@ -23,4 +23,4 @@ def list_devices(nocolor):
lockdown = LockdownClient(udid)
connected_devices.append(lockdown.all_values)

print_object(connected_devices, colored=not nocolor, default=lambda x: '<non-serializable>')
print_json(connected_devices, colored=not nocolor, default=lambda x: '<non-serializable>')
51 changes: 39 additions & 12 deletions pymobiledevice3/cli/mounter.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from pprint import pprint
import logging
import re

import click

from pymobiledevice3.cli.cli_common import Command
from pymobiledevice3.cli.cli_common import Command, print_json
from pymobiledevice3.exceptions import DeviceVersionFormatError, PyMobileDevice3Exception, NotMountedError, \
UnsupportedCommandError
from pymobiledevice3.services.mobile_image_mounter import MobileImageMounterService
from pymobiledevice3.exceptions import DeviceVersionFormatError


@click.group()
Expand All @@ -22,16 +22,31 @@ def mounter():


@mounter.command('list', cls=Command)
def mounter_list(lockdown):
""" lookup mounter image type """
pprint(MobileImageMounterService(lockdown=lockdown).list_images())
@click.option('--nocolor', is_flag=True)
def mounter_list(lockdown, nocolor):
""" list all mounted images """
output = []

images = MobileImageMounterService(lockdown=lockdown).list_images()['EntryList']
for image in images:
image['ImageSignature'] = image['ImageSignature'].hex()
output.append(image)

print_json(output, colored=not nocolor)


@mounter.command('lookup', cls=Command)
@click.option('--nocolor', is_flag=True)
@click.argument('image_type')
def mounter_lookup(lockdown, image_type):
def mounter_lookup(lockdown, nocolor, image_type):
""" lookup mounter image type """
pprint(MobileImageMounterService(lockdown=lockdown).lookup_image(image_type))
output = []

signatures = MobileImageMounterService(lockdown=lockdown).lookup_image(image_type)['ImageSignature']
for signature in signatures:
output.append(signature.hex())

print_json(output, colored=not nocolor)


@mounter.command('umount', cls=Command)
Expand All @@ -40,12 +55,18 @@ def mounter_umount(lockdown):
image_type = 'Developer'
mount_path = '/Developer'
image_mounter = MobileImageMounterService(lockdown=lockdown)
image_mounter.umount(image_type, mount_path, b'')
try:
image_mounter.umount(image_type, mount_path, b'')
logging.info('DeveloperDiskImage umounted successfully')
except NotMountedError:
logging.error('DeveloperDiskImage isn\'t currently mounted')
except UnsupportedCommandError:
logging.error('Your iOS version doesn\'t support this command')


def sanitize_version(version):
try:
return re.match('\d*\.\d*', version)[0]
return re.match(r'\d*\.\d*', version)[0]
except TypeError as e:
raise DeviceVersionFormatError from e

Expand Down Expand Up @@ -80,5 +101,11 @@ def mounter_mount(lockdown, image, signature, xcode, version):

image_mounter = MobileImageMounterService(lockdown=lockdown)
image_mounter.upload_image(image_type, image, signature)
image_mounter.mount(image_type, signature)
logging.info('DeveloperDiskImage mounted successfully')
try:
image_mounter.mount(image_type, signature)
logging.info('DeveloperDiskImage mounted successfully')
except PyMobileDevice3Exception as e:
if 'is already mounted' in str(e):
logging.error('DeveloperDiskImage is already mounted')
else:
raise e
10 changes: 4 additions & 6 deletions pymobiledevice3/cli/profile.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from pprint import pprint

import click

from pymobiledevice3.cli.cli_common import Command
from pymobiledevice3.cli.cli_common import Command, print_json
from pymobiledevice3.services.mobile_config import MobileConfigService


Expand All @@ -21,18 +19,18 @@ def profile_group():
@profile_group.command('list', cls=Command)
def profile_list(lockdown):
""" list installed profiles """
pprint(MobileConfigService(lockdown=lockdown).get_profile_list())
print_json(MobileConfigService(lockdown=lockdown).get_profile_list())


@profile_group.command('install', cls=Command)
@click.argument('profile', type=click.File('rb'))
def profile_install(lockdown, profile):
""" install given profile file """
pprint(MobileConfigService(lockdown=lockdown).install_profile(profile.read()))
print_json(MobileConfigService(lockdown=lockdown).install_profile(profile.read()))


@profile_group.command('remove', cls=Command)
@click.argument('name')
def profile_remove(lockdown, name):
""" remove profile by name """
pprint(MobileConfigService(lockdown=lockdown).remove_profile(name))
print_json(MobileConfigService(lockdown=lockdown).remove_profile(name))
9 changes: 4 additions & 5 deletions pymobiledevice3/cli/ps.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from pprint import pprint

import click

from pymobiledevice3.cli.cli_common import Command
from pymobiledevice3.cli.cli_common import Command, print_json
from pymobiledevice3.services.os_trace import OsTraceService


Expand All @@ -13,6 +11,7 @@ def cli():


@cli.command(cls=Command)
def ps(lockdown):
@click.option('--nocolor', is_flag=True)
def ps(lockdown, nocolor):
""" show process list """
pprint(OsTraceService(lockdown=lockdown).get_pid_list())
print_json(OsTraceService(lockdown=lockdown).get_pid_list(), colored=not nocolor)
10 changes: 10 additions & 0 deletions pymobiledevice3/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,13 @@ class DvtException(PyMobileDevice3Exception):
class DvtDirListError(DvtException):
""" Raise when directory listing fails. """
pass


class NotMountedError(PyMobileDevice3Exception):
""" Given image for umount wasn't mounted in the first place """
pass


class UnsupportedCommandError(PyMobileDevice3Exception):
""" Given command isn't supported for this iOS version """
pass
11 changes: 7 additions & 4 deletions pymobiledevice3/services/mobile_image_mounter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging

from pymobiledevice3.exceptions import PyMobileDevice3Exception
from pymobiledevice3.exceptions import PyMobileDevice3Exception, NotMountedError, UnsupportedCommandError
from pymobiledevice3.lockdown import LockdownClient


Expand Down Expand Up @@ -36,9 +36,12 @@ def umount(self, image_type, mount_path, signature):
'MountPath': mount_path,
'ImageSignature': signature})
response = self.service.recv_plist()

if response.get('Error'):
raise PyMobileDevice3Exception('unsupported command')
error = response.get('Error')
if error:
if error == 'UnknownCommand':
raise UnsupportedCommandError()
else:
raise NotMountedError()

def mount(self, image_type, signature):
""" Upload image into device. """
Expand Down

0 comments on commit 1ce5cf8

Please sign in to comment.