#author: june
#date: 12/25/2015

from lib.taskutil import *
from lib.locaUtil import *
from optparse import OptionParser
from lib.CommandUtil import switch
import lib.globals as G 


import atexit
import code
import os
import sys
import readline
import rlcompleter

#import logging
#LOG_FILENAME = '/tmp/completer.log'
#logging.basicConfig(filename=LOG_FILENAME,
#                    level=logging.DEBUG,
#                    )


class HistoryConsole(code.InteractiveConsole):
    def __init__(self, locals=None, filename="<console>",
                 histfile=os.path.expanduser("~/.console-history")):
        code.InteractiveConsole.__init__(self, locals, filename)
        self.init_history(histfile)

    def init_history(self, histfile):
    
        #readline.parse_and_bind("bind ^I rl_complete")
        
        # Register our completer function
        readline.set_completer(SimpleCompleter(G.cmmands.keys()).complete)
        
        
        #readline.set_completer(TabCompleter().complete)
        ### Add autocompletion
        if 'libedit' in readline.__doc__:
            readline.parse_and_bind("bind -e")
            readline.parse_and_bind("bind '\t' rl_complete")
        else:
            readline.parse_and_bind("tab: complete")
        
        # Use the tab key for completion
        #readline.parse_and_bind('tab: complete')
        if hasattr(readline, "read_history_file"):
            try:
                readline.read_history_file(histfile)
            except:
                pass
            
            atexit.register(self.save_history, histfile)
            

    def save_history(self, histfile):
        readline.write_history_file(histfile)


class FileCacher:
    "Cache the stdout text so we can analyze it before returning it"
    def __init__(self): self.reset()
    def reset(self): self.out = []
    def write(self,line): self.out.append(line)
    def flush(self):
        output = '\n'.join(self.out)
        self.reset()
        return output


class Shell(HistoryConsole):
    "Wrapper around Python that can filter input/output to the shell"
    def __init__(self):
        self.stdout = sys.stdout
        self.cache = FileCacher()
        HistoryConsole.__init__(self)
        return

    def get_output(self): sys.stdout = self.cache
    def return_output(self): sys.stdout = self.stdout

    def push(self, line):
        self.get_output()
        # you can filter input here by doing something like
        # line = filter(line)
        code = HistoryConsole.push(self,line)
        self.return_output()
        output = self.cache.flush()
        # you can filter the output here by doing something like
        # output = filter(output)
        print output # or do something else with it
        return code
    
    #----------------------------------------------------------------------
    def wait_for_input(self):
        """"""
        line = raw_input(G.prompt)
        encoding = getattr(sys.stdin, "encoding", None)
        if encoding and not isinstance(line, unicode):
            line = line.decode(encoding)
            
        return line
    
    def interact(self, banner=None):

        task = None
        cmd = ''
        bundle_id = None
        
        while True:
            try:
                try:
                    line = raw_input(G.prompt)
                    
                    # Can be None if sys.stdin was redefined
                    encoding = getattr(sys.stdin, "encoding", None)
                    if encoding and not isinstance(line, unicode):
                        line = line.decode(encoding)
                    
                    cmd = LocalUtils().resolve_cmd(line)#filter(None, line.split(' '))
                            
                    if len(cmd) < 1:
                        continue
                    
                    task, bundle_id = self.process_cmd(task, bundle_id, cmd)
                    
                except EOFError:
                    self.write("\n")
                    break
            except KeyboardInterrupt:
                self.write("\nKeyboardInterrupt\n")
    

    
    #----------------------------------------------------------------------
    def all_in_one(self, task):
        """"""
        ssh = G.SSH_CONFIG
        bundle_id = G.BUNDLE_IDENTIFER
        if task is None:
            task = TaskUtil(ssh[0], ssh[1], ssh[2], ssh[3], ssh[4])
            if task.device.ssh is None:
                print 'Connect to device failed.'
                return None
            
        if G.CHENV_ENVIROMENT:
            task.check_env()
        
        if G.LIST_APPLICATION:
            print '===>List all applications...'
            task.list_app()
        
        if bundle_id is not None:
            print '===>Get detail info of app:\'{}\''.format(bundle_id)
            task.app_detail_info(bundle_id)
        
        if bundle_id is not None:
            print '===>Analyze binary...'
            task.analyze_binary(bundle_id)
        
        if bundle_id is not None and G.BINARY_DUMP_PATH is not None:
            print '===>Dump binary...'
            task.dump_binary(bundle_id, G.BINARY_DUMP_PATH)
        
        if bundle_id is not None and G.IPA_DUMP_PATH is not None:
            print '===>Dump ipa...'
            task.dump_ipa(bundle_id, G.IPA_DUMP_PATH)        
    
        if bundle_id is not None and G.STORAGE_GREP_PATTERNS.count > 0:
            print '===>Do search job...'
            for pattern in G.STORAGE_GREP_PATTERNS:
                task.grep_storage_by_pattern(bundle_id, pattern)
                task.keychain_grep(pattern)
                
        if bundle_id is not None and G.APP_DIR_SAVE_PATH is not None:
            print 'Downloading application...'
            task.download_app_dict(bundle_id, G.APP_DIR_SAVE_PATH)
    
        if bundle_id is not None and G.APP_STORAGE_SAVE_PATH is not None:
            print 'Downloading application storage...'
            task.download_app_storage(bundle_id, G.APP_STORAGE_SAVE_PATH) 
            
        return task

    
    #----------------------------------------------------------------------
    def process_cmd(self, task, bundle_id, _args):
        """"""

        for case in switch(_args[0]):
            if case('h'):
                pass
            if case('help'):
                cmds = []
                if len(_args) > 1:
                    cmds = _args[1:]
                LocalUtils().usage(cmds)
                break
            if case('ssh'):#connect device
                #task = TaskUtil(host, user, password, sftp_port)
                if len(_args) < 4 or len(_args) > 5:
                    G.log(G.INFO, G.WRONG_ARGS)
                    #print 'Wrong args.'
                    break
                host = _args[1]
                user = _args[2]
                password = _args[3]
                sftp_port = 22
                if len(_args) == 5:
                    sftp_port = int(_args[4])
                task = TaskUtil(host, user, password, sftp_port, "lan")
                if task.device is None:
                    task = None
                break
            if case('usb'):#connect device
                #task = TaskUtil(host, user, password, sftp_port)
                if len(_args) == 4:
                    user = _args[1]
                    password = _args[2]
                    if _args[3].isdigit():
                        sftp_port = int(_args[3])
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                elif len(_args) == 3:
                    user = _args[1]
                    password = _args[2]
                    sftp_port = 2222
                else:
                    G.log(G.INFO, G.WRONG_ARGS)
                    break
                host = '127.0.0.1'
                task = TaskUtil(host, user, password, sftp_port, "usb")
                if task.device is None:
                    task = None
                break
            if case('la'):#list application
                if task is not None:
                    task.list_app()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('sd'):#show app detail
                if task is not None:
                    #com.taobaobj.moneyshield
                    if  len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.app_detail_info(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)

                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('ab'):#analyze binary
                if task is not None:
                    #task.analyze_binary("com.taobaobj.moneyshield")
                    if len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.analyze_binary(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break 
            if case('las'):#list all starage file
                if task is not None:
                    #task.list_starage_by_bundle_id("com.taobaobj.moneyshield")
                    if len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.list_starage_by_bundle_id(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('vpl'):#view a plist file
                if task is not None:
                    #task.view_plist_by_filename("com.taobaobj.moneyshield", "/moneyshield.app/PlugIns/widget.appex/Info.plist")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        path = _args[2].decode("utf-8")
                    elif len(_args) == 2:
                        path = _args[1].decode("utf-8")
                    if bundle_id is not None:
                        task.view_plist_by_filename(bundle_id, path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('gsp'):#grep pattern in a plist file
                if task is not None:
                    #task.grep_storage_by_pattern("com.taobaobj.moneyshield", "DTSDKName", "/moneyshield.app/PlugIns/widget.appex/Info.plist")
                    if len(_args) == 4:
                        bundle_id = _args[1]
                        pattern = _args[2]
                        path = _args[3]
                    elif len(_args) == 3:
                        pattern = _args[1]
                        path = _args[2]
                    if bundle_id is not None:
                        task.grep_storage_by_pattern(bundle_id, pattern, path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('gs'):#grep pattern in all storage
                if task is not None:
                    #task.grep_storage_by_pattern("com.taobaobj.moneyshield", "DTSDKName")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        pattern = _args[2]
                    elif len(_args) == 2:
                        pattern = _args[1]
                    if bundle_id is not None:
                        task.grep_storage_by_pattern(bundle_id, pattern)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('vdb'):#view all content of a db file
                if task is not None:
                    #task.view_sqlite_by_filename("com.taobaobj.moneyshield", "/Library/introspy-com.taobaobj.moneyshield.db")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        path = _args[2]
                    elif len(_args) == 2:
                        path = _args[1]
                    if bundle_id is not None:
                        task.view_sqlite_by_filename(bundle_id, path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('ltb'):#list tablename in a db file
                if task is not None:
                    #task.list_tablename_by_dbpath("com.taobaobj.moneyshield", "/Library/Caches/com.taobaobj.moneyshield/Cache.db")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        path = _args[2]
                    elif len(_args) == 2:
                        path = _args[1]
                    if bundle_id is not None:
                        task.list_tablename_by_dbpath(bundle_id, path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('vtb'):#view all content in a tablename
                if task is not None:
                    #task.select_all_from_tablename("com.taobaobj.moneyshield", "/Library/Caches/com.taobaobj.moneyshield/Cache.db", "cfurl_cache_response")
                    if len(_args) == 4:
                        bundle_id = _args[1]
                        path = _args[2]
                        tabname = _args[3]
                    elif len(_args) == 3:
                        path = _args[1]
                        tabname = _args[2]
                    if bundle_id is not None:
                        task.select_all_from_tablename(bundle_id, path, tabname)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('gtb'):#grep pattern in a tablename
                if task is not None:
                    #task.grep_pattern_in_tablename(
                    #"com.taobaobj.moneyshield", 
                    #"/Library/Caches/com.taobaobj.moneyshield/Cache.db", 
                    #"cfurl_cache_response", 
                    #"aliyun")
                    if len(_args) == 5:
                        bundle_id = _args[1]
                        path = _args[2]
                        tabname = _args[3]
                        pattern = _args[4]
                    elif len(_args) == 4:
                        path = _args[1]
                        tabname = _args[2]
                        pattern = _args[3]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                    if bundle_id is not None:
                        task.grep_pattern_in_tablename(bundle_id, path, tabname, pattern)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('gdbs'):#grep pattern in all db file
                if task is not None:
                    #task.grep_pattern_in_sqlite("com.taobaobj.moneyshield", "aliyun")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        pattern = _args[2]
                    elif len(_args) == 2:
                        pattern = _args[1]
                    if bundle_id is not None:
                        task.grep_pattern_in_sqlite(bundle_id, pattern)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('gdb'):#grep pattern in a db file
                if task is not None:
                    #task.grep_pattern_in_one_sqlite("com.taobaobj.moneyshield", "aliyun", "/Library/Caches/com.taobaobj.moneyshield/Cache.db")
                    if len(_args) == 4:
                        bundle_id = _args[1]
                        dbpath = _args[2]
                        pattern = _args[3]
                    elif len(_args) == 3:
                        dbpath = _args[1]
                        pattern = _args[2]
                    if bundle_id is not None:
                        task.grep_pattern_in_one_sqlite(bundle_id, pattern, dbpath)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('br'):#binary cookie reader
                if task is not None:
                    #task.read_binary_cookie()
                    task.read_binary_cookie()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('abr'):#application binary cookie reader
                if task is not None:
                    #task.read_binary_cookie("com.taobaobj.moneyshield")
                    if len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.read_binary_cookie(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('vkc'):#view keyboard cache
                if task is not None:
                    #task.read_keyboard_cache()
                    task.read_keyboard_cache()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('skc'):#search keyboard cache
                if task is not None:
                    #task.read_keyboard_cache("ert")
                    if len(_args) == 2:
                        pattern = _args[1]
                        task.read_keyboard_cache(pattern)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('panic'):#search keyboard cache
                if task is not None:
                    #task.get_panic_log(local_path)
                    if len(_args) == 2:
                        local_path = _args[1]
                        task.get_panic_log(local_path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break            
            if case('lsl'):#list shared libraries
                if task is not None:
                    #task.get_shared_libraries("com.taobaobj.moneyshield")
                    if len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.get_shared_libraries(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('lbs'):#list binary strings
                if task is not None:
                    #task.get_all_strings("com.taobaobj.moneyshield")
                    if len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.get_all_strings(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('gbs'):#grep pattern in binary strings
                if task is not None:
                    #task.get_all_strings("com.taobaobj.moneyshield", "webkit")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        pattern = _args[2]
                    elif len(_args) == 2:
                        pattern = _args[1]
                    if bundle_id is not None:
                        task.get_all_strings(bundle_id, pattern)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('upload'):#upload file to device
                if task is not None:
                    #task.upload_file("/var/root/debugserver", "~/tmp/x.ipa")
                    if len(_args) == 3:
                        remote_path = _args[1]
                        local_path = _args[2]
                        task.upload_file(local_path, remote_path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dnload'):#download file from device
                if task is not None:
                    #task.download_file('~/tmp/Clutch', '/var/root/Clutch1')
                    if len(_args) == 3:
                        remote_path = _args[1]
                        local_path = _args[2]
                        task.download_file(local_path, remote_path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break            
            if case('log'):#logging
                if task is not None:
                    #task.logging()
                    _filter = None
                    if len(_args) == 2:
                        _filter = _args[1]
                    task.logging(_filter)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('wpb'):#watch pasteboard
                if task is not None:
                    #task.watch_pasteboard()
                    task.watch_pasteboard()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('kcd'):#keychain dump
                if task is not None:
                    #task.keychain_dump()
                    task.keychain_dump()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('kce'):#keychain edit
                if task is not None:
                    #task.keychain_edit("198", "12345678")
                    #task.keychain_edit("198", "MTIwNzEyMDdsdQ==", True)
                    base64 = False
                    if len(_args) == 4:
                        dataID = _args[1]
                        newData = _args[2]
                        if _args[3] == 'base64':
                            base64 = True
                    elif len(_args) == 3:
                        dataID = _args[1]
                        newData = _args[2]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                    task.keychain_edit(dataID, newData, base64)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('kcdel'):#keychain delete
                if task is not None:
                    #task.keychain_delete("62")
                    if len(_args) == 2:
                        dataID = _args[1]
                        task.keychain_delete(dataID)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('kcs'):#keychain search pattern
                if task is not None:
                    #task.keychain_grep("xml")
                    if len(_args) == 2:
                        pattern = _args[1]
                        task.keychain_grep(pattern)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dbn'):#dump binary
                if task is not None:
                    #task.dump_binary("com.mimimix.tiaomabijia", "~/tmp/xxx")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        savePath = _args[2]
                    elif len(_args) == 2:
                        savePath = _args[1]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                    task.dump_binary(bundle_id, savePath)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dipa'):#dump ipa
                if task is not None:
                    #task.dump_ipa("com.mimimix.tiaomabijia", "~/tmp/")
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        savePath = _args[2]
                    elif len(_args) == 2:
                        savePath = _args[1]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                    task.dump_ipa(bundle_id, savePath)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('cipa'):#crach all ipa files in a folder
                if task is not None:
                    #task.crack_ipa_inpath('~/tmp/ipas', '~/tmp/dumppath')
                    if len(_args) == 3:
                        ipa_path = _args[1]
                        save_Path = _args[2]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                    task.crack_ipa_inpath(ipa_path, save_Path)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('iipa'):#install ipa
                if task is not None:
                    #task.install_ipa("~/.ihack/C7E834A6-19DC-4A73-AC88-14B89F3D8620/com.mimimix.tiaomabijia.ipa")
                    if len(_args) == 2:
                        path = _args[1]
                        task.install_ipa(path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('lapp'):#launch application
                if task is not None:
                    #task.launch_app("com.mimimix.tiaomabijia")
                    if  len(_args) == 2:
                        bundle_id = _args[1]
                    if bundle_id is not None:
                        task.launch_app(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('ibca'):#install burp cert
                if task is not None:
                    task.install_burp_cert()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('lca'):#list all certs
                if task is not None:
                    task.list_all_certs()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dca'):#delete ca cert
                if task is not None:
                    #task.delete_cert_by_id("1")
                    if  len(_args) == 2:
                        cert_id = _args[1]
                        task.delete_cert_by_id(cert_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('aca'):#import cert to device
                if task is not None:
                    #task.add_cert_by_path("/Users/june/Downloads/cacert.der")
                    if  len(_args) == 2:
                        cert_path = _args[1]
                        task.add_cert_by_path(cert_path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('pca'):#export cert to device
                if task is not None:
                    #task.export_cert('/Users/june/tmp/cert')
                    if  len(_args) == 2:
                        path = _args[1]
                        task.export_cert(path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break            
            if case('fus'):#fuzz url scheme
                if task is not None:
                    #task.fuzz_url_schema("com.taobaobj.moneyshield", 'qiandun://?token=%*%&registered=%*%')
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        template = _args[2]
                    elif len(_args) == 2:
                        template = _args[1]
                    if bundle_id is not None:
                        task.fuzz_url_schema(bundle_id, template)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('chenv'):#check enviroment
                #check enviroment
                if task is not None:
                    task.check_env()
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('nonfat'):#parse Fat header file to non-Fat
                #non fat
                _output = None
                if len(_args) == 3:
                    _output = _args[2]
                    _input = _args[1]
                elif len(_args) == 2:
                    _input = _args[1]
                else:
                    G.log(G.INFO, G.WRONG_ARGS)
                    break
                LocalUtils().nonFat(_input, _output)
                break
            if case('mport'):#mapping local port to remote port
                #LocalUtils().mapping_port(12346, 12346)
                _output = None
                if len(_args) == 3:
                    local_port = int(_args[2])
                    remote_port = int(_args[1])
                else:
                    G.log(G.INFO, G.WRONG_ARGS)
                    break
                LocalUtils().mapping_port(remote_port, local_port)
                break            
            if case('dwa'):#download whole application
                if task is not None:
                    #task.download_app_dict('com.taobaobj.moneyshield', '~/tmp')
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        dest_dict = _args[2]
                    elif len(_args) == 2:
                        dest_dict = _args[1]
                    if bundle_id is not None:
                        task.download_app_dict(bundle_id, dest_dict)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dws'):#download whole application storage
                if task is not None:
                    #task.download_app_storage('com.taobaobj.moneyshield', '~/tmp')
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        dest_dict = _args[2]
                    elif len(_args) == 2:
                        dest_dict = _args[1]
                        
                    if bundle_id is not None:
                        task.download_app_storage(bundle_id, dest_dict)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('pid'):#get pid 
                if task is not None:
                    #task.get_pid_by_bundle_id('com.fotoable.pipcam')
                    if len(_args) == 2:
                        bundle_id = _args[1]
                        
                    if bundle_id is not None:
                        task.get_pid_by_bundle_id(bundle_id)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dbgsvr'):#debugserver on a process, waiting for debugger 
                if task is not None:
                    #task.debugserver('com.fotoable.pipcam')
                    if len(_args) == 2:
                        pattern = _args[1]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    task.debugserver(pattern)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break            
            if case('clzdp'):#class-dump 
                if task is not None:
                    #task.clzdp('cn.xiaochuankeji.tieba', '~/tmp/')
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        save_path = _args[2]
                    elif len(_args) == 2:
                        save_path = _args[1]
                        
                    if bundle_id is not None:
                        task.clzdp(bundle_id, save_path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('stop'):#stop thread
                if task is not None:
                    #task.stop_thread_by_key(key)
                    if len(_args) == 2:
                        key = _args[1]
                        task.stop_thread_by_key(key)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('wclzdp'):#weak_classdump 
                if task is not None:
                    #task.weak_classdump('cn.xiaochuankeji.tieba', '~/tmp/')
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        save_path = _args[2]
                    elif len(_args) == 2:
                        save_path = _args[1]
                        
                    if bundle_id is not None:
                        task.weak_classdump(bundle_id, save_path)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('cycript'):#run cycript file on the remote device inside an application
                if task is not None:
                    #task.cycript_file('cn.xiaochuankeji.tieba', '~/tmp/test.cy')
                    if len(_args) == 3:
                        bundle_id = _args[1]
                        cy_file = _args[2]
                    elif len(_args) == 2:
                        cy_file = _args[1]
                    if bundle_id is not None:
                        task.cycript_file(bundle_id, cy_file)
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break
            if case('dlinj'):#inject a dylib into a binary
                #LocalUtils().inject_dylib_to_binary('~/tmp/xin.ipa', '~/tmp/libeeee.dylib')
                if len(_args) == 3:
                    binary = _args[1]
                    dylib = _args[2]
                else:
                    G.log(G.INFO, G.WRONG_ARGS)
                    break
                LocalUtils().inject_dylib_to_binary(binary, dylib)
                break
            if case('resign'):#resign an ipa file
                #LocalUtils().resign_ipa('~/tmp/xin.ipa', '~/tmp/entitlements.plist', '~/tmp/ios_development.mobileprovision', 'iPhone Developer: Name Name (xxxxxx)')
                if len(_args) == 5:
                    ipa_path = _args[1]
                    entitlements_path = _args[2]
                    mobileprovision_path = _args[3]
                    identity = _args[4]
                else:
                    G.log(G.INFO, G.WRONG_ARGS)
                    break
                LocalUtils().resign_ipa(ipa_path, entitlements_path, mobileprovision_path, identity)
                break
            if case('dlini'):#inject a dylib into an ipa file and resign 
                #LocalUtils().resign_ipa('~/tmp/xin.ipa', '~/tmp/entitlements.plist', '~/tmp/ios_development.mobileprovision', 'iPhone Developer: Name Name (xxxxxx)', '~/tmp/dylib')
                if len(_args) == 6:
                    ipa_path = _args[1]
                    entitlements_path = _args[2]
                    mobileprovision_path = _args[3]
                    identity = _args[4]
                    dylib = _args[5]
                else:
                    G.log(G.INFO, G.WRONG_ARGS)
                    break
                LocalUtils().resign_ipa(ipa_path, entitlements_path, mobileprovision_path, identity, [dylib])
                break
            if case('dlinji'):#inject a dylib into an ipa file, resign it, install it
                if task is not None:
                    #task.inject_dylib_resign_install_ipa('~/tmp/xin.ipa', '~/tmp/entitlements.plist', '~/tmp/ios_development.mobileprovision', 'iPhone Developer: Name Name (xxxxxx)')
                    if len(_args) == 6:
                        ipa_path = _args[1]
                        entitlements_path = _args[2]
                        mobileprovision_path = _args[3]
                        identity = _args[4]
                        dylib = _args[5]
                    else:
                        G.log(G.INFO, G.WRONG_ARGS)
                        break
                    task.inject_dylib_resign_install_ipa(ipa_path, entitlements_path, mobileprovision_path, identity, dylib)
                    break
                G.log(G.INFO, G.CONNECT_DEVICE_FIRST)
                break             
            if case('go'):#lazy is good.
                all_in_one(task)
                break
            if case('clche'):#clear local cache files
                task.clear_cache()
                break
            if case('e'):#quit
                pass            
            if case('exit'):#quit
                pass            
            if case('q'):#quit
                pass
            if case('quit'):#quit
                if task is not None:
                    task.exit_safely()
                G.log(G.INFO, 'Exit safely.')
                exit(0)
                break
            if case('cprt'):#copyright
                G.copyright()
                break
            if case(): # default, could also just omit condition or 'if True'
                G.log(G.INFO, "Command not support!")
                G.log(G.INFO, "Try \"help\".")
                
        return task, bundle_id
  
    

class SimpleCompleter(object):
    
    def __init__(self, options):
        self.options = sorted(options)
        return

    def complete(self, text, state):
        response = None

        if state == 0:
            # This is the first time for this text, so build a match list.
            if text:
                self.matches = [s 
                                for s in self.options
                                if s and s.startswith(text)]
                #logging.debug('%s matches: %s', repr(text), self.matches)
            else:
                self.matches = self.options[:]
                #logging.debug('(empty input) matches: %s', self.matches)
        
        # Return the state'th item from the match list,
        # if we have that many.
        try:
            response = self.matches[state]
        except IndexError:
            response = None
        #logging.debug('complete(%s, %s) => %s', repr(text), state, repr(response))
        return response
    



'''
#from: http://stackoverflow.com/questions/7116038/python-tab-completion-mac-osx-10-7-lion
#for python tab completer, history cmd


import readline,rlcompleter
### Indenting
class TabCompleter(rlcompleter.Completer):
    """Completer that supports indenting"""
    def complete(self, text, state):
        if not text:
            return ('    ', None)[state]
        else:
            return rlcompleter.Completer.complete(self, text, state)

readline.set_completer(TabCompleter().complete)

### Add autocompletion
if 'libedit' in readline.__doc__:
    readline.parse_and_bind("bind -e")
    readline.parse_and_bind("bind '\t' rl_complete")
else:
    readline.parse_and_bind("tab: complete")

### Add history
import os
histfile = os.path.join(os.environ["HOME"], ".pyhist")
try:
    readline.read_history_file(histfile)
except IOError:
    pass
import atexit
atexit.register(readline.write_history_file, histfile)
del histfile

'''