def post_response_as_dict(path, target, data, descend_once=None, printer=StatusPrinter()): uri = make_uri(path, target) headers = _headers(target, printer=printer) response = post(uri, headers, data, printer=printer) return _finish(response, 'POST', uri, descend_once)
def get_response_as_dict(path, target, descend_once='elements', printer=StatusPrinter()): uri = make_uri(path, target) headers = _headers(target, printer=printer) response = get(uri, headers, printer=printer) return _finish(response, 'GET', uri, descend_once)
def provision(): args = parse_serial_ssh_merch() printer = StatusPrinter(indent=0) printer("Provisioning Device") with Indent(printer): endpoint = 'https://{}/v3/partner/pp/merchants/{}/devices/{}/provision'.format( args.ssh_config.hostname, args.merchant, args.serial_num) auth_token = get_auth_token( args.ssh_config, '/v3/partner/pp/merchants/{mId}/devices/{serialNumber}/provision') headers = {'Authorization': 'Bearer ' + auth_token} data = { 'mId': get_mid(args.ssh_config, args.merchant), 'merchantUuid': args.merchant, 'serial': args.serial_num, 'chipUid': args.cpuid } print_request(printer, endpoint, headers, data) response = requests.put(endpoint, headers=headers, data=data) print_response(printer, response) if response.status_code == 200: printer('OK') else: printer('Error') sys.exit(20)
def get_acceptedness(ssh_config, serial_num, printer=StatusPrinter()): q = Query( ssh_config, 'metaRO', 'test321', """ SELECT value FROM setting WHERE merchant_id IN (SELECT merchant_id FROM device_provision WHERE serial_number = '{}') AND name = 'ACCEPTED_BILLING_TERMS'; """.format(serial_num)) q.on_empty("this device is not associated with a merchant on {}".format( ssh_config.ssh_host)) try: value = q.get_from_first_row(lambda row: row['value']) except ValueError as ex: printer(str(ex)) sys.exit(40) if value is None: return None else: return value.decode('utf-8')
def deprovision(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Deprovisioning Device") with Indent(printer): auth_token = get_auth_token( args.ssh_config, '/v3/partner/pp/merchants/{mId}/devices/{serialNumber}/deprovision' ) mid = get_merchant(args.serial_num, args.ssh_config, printer=printer).uuid endpoint = 'https://{}/v3/partner/pp/merchants/{}/devices/{}/deprovision'.format( args.ssh_config.hostname, mid, args.serial_num) headers = {'Authorization': 'Bearer ' + auth_token} print_request(printer, endpoint, headers, {}) response = requests.put(endpoint, headers=headers) print_response(printer, response) # TODO: server/scripts/disassociate_device.py also DELETEs '/v3/resellers/{rId}/devices/{serial}' # maybe this function should do that also? if response.status_code == 200: printer('OK') else: printer('Error') sys.exit(10)
def print_or_warn(string, max_length=500, printer=StatusPrinter()): if len(string) > max_length and sys.stdout.isatty(): validJson = False try: json.loads(string) validJson = True except ValueError: pass if validJson: printer("Output is {} chars (json) and stdout is a tty.".format( len(string))) else: printer("Output is {} chars and stdout is a tty.".format( len(string))) with Indent(printer): printer( "\nIf you really want that much garbage in your terminal, write to a pipe, like so:" ) with Indent(printer): printer(clistring() + " | cat") if validJson: printer( "Or better yet, use `jq` to query it:" ) # because humans shouldn't have to read non-pretty json with Indent(printer): printer(clistring() + " | jq '.someKey[3]'") sys.exit(15) else: print(string)
def set_target(): parsed_args = parse(Parseable.target_type, Parseable.server) printer = StatusPrinter(indent=0) printer("Targeting attached device to {} {}".format(parsed_args.targettype, parsed_args.server)) with Indent(printer): get_connected_device(printer=printer).set_target(parsed_args.targettype, parsed_args.server)
def print_merchant_id(): parser = ArgumentParser() parser.add_argument("serial_num", type=str, help="the device serial number") parser.add_argument( "ssh_config_host", type=str, help="ssh host of the server (specified in ~/.ssh/config)") parser.add_argument( "merchant_uuid", type=str, help="the merchant uuid that we want a merchant id for") args = parser.parse_args() printer = StatusPrinter(indent=0) printer("Finding {}'s id according to {}".format(args.merchant_uuid, args.ssh_config_host)) if not re.match(r'[A-Za-z0-9]{13}', args.merchant_uuid): raise ValueError("{} doesn't look like a merchant UUID".format( args.merchant_uuid)) ssh_config = SshConfig(args.ssh_config_host) print(get_mid(ssh_config, args.merchant_uuid))
def ManyRows(cursor, rowtransform, print_transform=False, printer=StatusPrinter()): # exit early if response is empty rows = cursor.fetchall() if not rows: printer("...got empty result") return None printer('[Rows]') with Indent(printer): printer(shorten(rows)) # exit early if transform is trivial if is_identity(rowtransform): return rows rows = list(map(rowtransform, rows)) if print_transform: printer('[Transformed Rows]') with Indent(printer): printer(pretty_shorten(rows)) return rows
def OneRow(cursor, rowtransform, print_transform=False, printer=StatusPrinter()): row = cursor.fetchone() next_row = cursor.fetchone() if next_row: raise Exception("OneRow feedback strategy saw at least two rows: {} and {}".format(row, next_row)) # this function refuses to break ties for you # if you expected multiple rows, use the ManyRows feedback strategy and break them yourself # exit early if response is empty if not row: printer("...got empty result") return None printer('[Row]') with Indent(printer): printer(shorten(row)) # exit early if transform is trivial if is_identity(rowtransform): return row row = rowtransform(row) if print_transform: printer('[Transformed Row]') with Indent(printer): printer(pretty_shorten(row)) return row
def post(endpoint, headers, data, obfuscate_pass=False, printer=StatusPrinter()): if obfuscate_pass: safe_data = deepcopy(data) safe_data['password'] = '******' * len(data['password']) return _do_request(Verb.post, endpoint, headers, data, print_data=safe_data, printer=printer) else: return _do_request(Verb.post, endpoint, headers, data, printer=printer)
def print_local_ip(): printer = StatusPrinter(indent=0) printer("Seeking local IP accessable by device") with Indent(printer): local_ip = probe_network(selector = lambda x : x['local_ip'], printer=printer) print(local_ip)
def print_device_ip(): printer = StatusPrinter(indent=0) printer("Seeking accessable device IP") with Indent(printer): device_ip = probe_network(selector = lambda x : x['device_ip'], printer=printer) print(device_ip)
def _finish(response, verb_str, uri, descend_once, printer=StatusPrinter()): if response.status_code < 200 or response.status_code > 299: raise Exception("{} on {} returned code {}".format(verb_str, uri, response.status_code)) if descend_once: return json.loads(response.content.decode('utf-8'))[descend_once] return json.loads(response.content.decode('utf-8'))
def print_activation_code(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Getting Activation Code") with Indent(printer): print( get_activation_code(args.ssh_config, args.serial_num, printer=printer))
def master_clear(): printer = StatusPrinter(indent=0) printer("Clearing Device") with Indent(printer): with Indent(printer): d = get_connected_device(printer=printer) cmd = ['shell', 'am', 'broadcast', '-a', 'android.intent.action.MASTER_CLEAR', '-n', 'android/com.android.server.MasterClearReceiver'] printer('\'' + ' '.join(cmd) + '\'') adb(cmd) sleep(d.get_shutdown_delay())
def probe_network(selector=lambda x : x, printer=StatusPrinter()): with Indent(printer): local_remote = get_local_remote_ip() if local_remote: return selector({ "local_ip" : local_remote[0], "device_ip" : local_remote[1] }) else: printer("No connectivity between local machine and device") sys.exit(40)
def ChangeCount(cursor, rowtransform, print_transform=False, printer=StatusPrinter()): if not is_identity(rowtransform): printer("ChangeCount got nontrivial row transform. It will be ignored.") change_ct = cursor.rowcount printer('[Rows Changed]') with Indent(printer): printer(change_ct) return change_ct
def wait_ready(printer=StatusPrinter()): if not ready(): printer('waiting for device ', end='') spinner = itertools.cycle(['-', '\\', '|', '/']) while not ready(): sys.stdout.write(next(spinner)) sys.stdout.flush() sleep(1) sys.stdout.write('\b') sys.stdout.flush() sleep(1) printer(' ... ready')
def set_acceptedness(ssh_config, serial_num, value, printer=StatusPrinter()): q = Query( ssh_config, 'metaRW', 'test789', """ UPDATE setting SET value = {} WHERE merchant_id IN (SELECT merchant_id FROM device_provision WHERE serial_number = '{}') AND name = 'ACCEPTED_BILLING_TERMS'; """.format(value, serial_num)) q.execute()
def get_activation_code(ssh_config, serial_num, printer=StatusPrinter()): q = Query( ssh_config, 'metaRO', 'test321', """ SELECT activation_code FROM device_provision WHERE serial_number = '{}'; """.format(serial_num)) q.on_empty("This device is not known to {}".format(ssh_config.ssh_host)) return int( q.get_from_first_row( lambda row: row['activation_code'].decode('utf-8'), printer=printer))
def accept(): args = parse_serial_ssh() printer = StatusPrinter() printer("Accepting Terms") set_acceptedness(args.ssh_config, args.serial_num, "1") new_val = get_acceptedness(args.ssh_config, args.serial_num) if new_val == "1": print("OK", file=sys.stderr) else: raise ValueError( "Failed to set acceptedness to {}. Final value: {}".format( "1", new_val))
def __init__(self, config, printer=StatusPrinter()): self.print = printer # not a true hostname, this is the alias ssh config uses for each config entry self.host = config.ssh_host self.mysql_port = config.mysql_port if port_open(self.mysql_port): raise OSError( "The local port ({}) you're trying to forward is already open. " .format(self.mysql_port) + "Close it first.") self.process = ssh(self.host, _bg=True)
def unaccept(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Clearing Terms Acceptance") set_acceptedness(args.ssh_config, args.serial_num, "NULL", printer) new_val = get_acceptedness(args.ssh_config, args.serial_num, printer) if new_val is None: printer("OK") else: raise ValueError( "Failed to set acceptedness to {}. Final value: {}".format( "NULL", new_val))
def get_merchant(serial_num, ssh_config, printer=StatusPrinter()): q = Query( ssh_config, 'metaRO', 'test321', """ SELECT id, uuid FROM merchant WHERE id = (SELECT merchant_id FROM device_provision WHERE serial_number = '{}'); """.format(serial_num)) q.on_empty("this device is not associated with a merchant on {}".format( ssh_config.ssh_host)) return q.get_from_first_row( lambda row: Merchant(row['id'], row['uuid'].decode('utf-8')), printer=printer)
def print_merchant(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Finding {}'s merchant according to {}".format( args.serial_num, args.ssh_config.ssh_host)) try: with Indent(printer): merchant = get_merchant(args.serial_num, args.ssh_config, printer=printer) print(json.dumps(merchant._asdict())) except ValueError as ex: printer(str(ex)) sys.exit(30)
def screenshot(device, printer=StatusPrinter()): printer("Dumping screenshot for Device: {}".format(device.serial)) with Indent(printer): # make way for new file outfile_name = "{}_{}.png".format(device.serial, datetime.now().strftime("%Y-%m-%d_%H%M%S")) outfile_path = os.path.join(os.getcwd(), outfile_name) rm('-f', outfile_path) # get the screencap tempfile_path = '/sdcard/{}'.format(outfile_name) adb.shell('screencap', '-p', tempfile_path) adb.pull(tempfile_path) adb.shell('rm', tempfile_path) printer("Wrote " + outfile_path)
def _do_request(verb, endpoint, headers, data, print_data=None, printer=StatusPrinter()): # for obfuscating passwords if not print_data: print_data = data printer("[Http]") with Indent(printer): print_request(printer, endpoint, headers, print_data) if data: if 'json' in ''.join(headers.values()).lower(): response = verb(endpoint, headers=headers, json=data) else: response = verb(endpoint, headers=headers, data=data) else: response = verb(endpoint, headers=headers) print_response(printer, response) return response
def __init__(self, target, printer=StatusPrinter()): # target is either a properties file (local) or an ssh config (remote) assert (isinstance(target, ServerTarget)) self.print = printer self.target = target self._connect = False if isinstance(target, SshConfig): self._connect = True if port_open(self.target.get_mysql_port()): raise OSError( "The local port ({}) you're trying to forward is already open. " .format(self.target.get_mysql_port()) + "Close it first.") # begin connecting self._process = ssh(self.target.get_name(), _bg=True)
def execute(self, then=lambda db: None, printer=StatusPrinter()): # open an ssh tunnel with SshTunnel(self.ssh_config, printer) as tun: with Indent(printer): # open a mysql connection db = _mysql.connect(user=self.mysql_user, host='127.0.0.1', port=tun.mysql_port, db='meta', passwd=self.mysql_pass) # show the query then run it printer("[Query]") with Indent(printer): printer(dedent(self.sql).strip()) db.query(self.sql) # do what the caller wanted return then(db)