Example #1
0
    def __init__(self, name, qapp, gui_parent, model_controller,
                 plugin_controller, close_callback=None):

        ModelObject.__init__(self)
        self._id = self.get_id()
        self.name = name

        # a reference used to add new hosts
        self._model_controller = model_controller

        # create the widget
        self.widget = ShellWidget(qapp,gui_parent,name)
        self.widget.setupLayout()

        # For safesty we don't use the user shell, we force it to bash
        progname = "/bin/bash"
        # create the session
        #Session(gui, pgm, args, term, sessionid='session-1', cwd=None):
        #self.process_controller = ProcessController()
        self.session = Session(self.widget, progname, [], "xterm", name);

        self.session.setConnect(True)
        self.session.setHistory(HistoryTypeBuffer(1000))
        self._setUpSessionSinalHandlers()

        #self.__last_user_input = None
        #self.__user_input_signal = False

        # flag that determines if output has to be ignored
        self.__ignore_process_output = False
        self.__ignore_process_output_once = False
        self.__first_process_output = True
        self.__save_output_prompt_format = False

        # determines if input is for an interactive command or not
        self.__interactive = False

        #TODO: check if we need to connect to this signal
        self.session.myconnect('done', self.close)

        # instance a new plugin controller
        self.plugin_controller = plugin_controller(self.id)

        self._initial_prompt = ""
        #self._custom_prompt_format = re.compile("\\x1B\[1;32m\[.+@.+\s.+\]>(\$|#)\s+\\x1B\[m")
        #self._custom_prompt_format = re.compile("\[.+@.+\s.+\]>(\$|#)\s+")
        self._custom_prompt_format = re.compile("\[(?P<user>.+)@(?P<host>.+):(?P<path>.+)\]>(\$|#)")
        #TODO: somewhere in the config there should be a list of regexes used to match
        # prompts. Be careful with this because if regexes are too generic they will
        # match more that is needed.
        self._generic_prompt_formats = []
        #XXX: adding this format just as a test! This should be in the config somehow
        # Also this may be is too generic to use...
        self._generic_prompt_formats.append(re.compile("^.+@.+:.+(\$|#)$"))
        
        #This is a reference to MainApplication.deleteShellEnvironment
        self._close_callback = close_callback 
        
        # flag that determines if shell environment is running
        self.__running = False
        
        #Autocomplete
        self._options=[] #only keys
        self._tname="" # last tool executed
        self._lcount=0
        self._optionsh={} #keys + help
        self.__command = ''
Example #2
0
    def __init__(self, name, qapp, gui_parent, model_controller,
                 plugin_controller, close_callback=None):

        ModelObject.__init__(self)
        self._id = self.get_id()
        self.name = name

        # a reference used to add new hosts
        self._model_controller = model_controller

        # create the widget
        self.widget = ShellWidget(qapp,gui_parent,name)
        self.widget.setupLayout()

        # For safesty we don't use the user shell, we force it to bash
        progname = "/bin/bash"
        # create the session
        #Session(gui, pgm, args, term, sessionid='session-1', cwd=None):
        #self.process_controller = ProcessController()
        self.session = Session(self.widget, progname, [], "xterm", name);

        self.session.setConnect(True)
        self.session.setHistory(HistoryTypeBuffer(1000))
        self._setUpSessionSinalHandlers()

        #self.__last_user_input = None
        #self.__user_input_signal = False

        # flag that determines if output has to be ignored
        self.__ignore_process_output = False
        self.__ignore_process_output_once = False
        self.__first_process_output = True
        self.__save_output_prompt_format = False

        # determines if input is for an interactive command or not
        self.__interactive = False

        #TODO: check if we need to connect to this signal
        self.session.myconnect('done', self.close)

        # instance a new plugin controller
        self.plugin_controller = plugin_controller(self.id)

        self._initial_prompt = ""
        #self._custom_prompt_format = re.compile("\\x1B\[1;32m\[.+@.+\s.+\]>(\$|#)\s+\\x1B\[m")
        #self._custom_prompt_format = re.compile("\[.+@.+\s.+\]>(\$|#)\s+")
        self._custom_prompt_format = re.compile("\[(?P<user>.+)@(?P<host>.+):(?P<path>.+)\]>(\$|#)")
        #TODO: somewhere in the config there should be a list of regexes used to match
        # prompts. Be careful with this because if regexes are too generic they will
        # match more that is needed.
        self._generic_prompt_formats = []
        #XXX: adding this format just as a test! This should be in the config somehow
        # Also this may be is too generic to use...
        self._generic_prompt_formats.append(re.compile("^.+@.+:.+(\$|#)$"))
        
        #This is a reference to MainApplication.deleteShellEnvironment
        self._close_callback = close_callback 
        
        # flag that determines if shell environment is running
        self.__running = False
        
        #Autocomplete
        self._options=[] #only keys
        self._tname="" # last tool executed
        self._lcount=0
        self._optionsh={} #keys + help
        self.__command = ''
Example #3
0
class ShellEnvironment(ModelObject):
    """
    Shell environment is really a group of components that need to work together
    The environment is composed by:
        * a ShellWidget used in the GUI
        * a Session (which is the real shell)
        * a PluginController needed to handle all shell input and output
    The shell environment interacts with the Model Controller to add new hosts
    """
    def __init__(self, name, qapp, gui_parent, model_controller,
                 plugin_controller, close_callback=None):

        ModelObject.__init__(self)
        self._id = self.get_id()
        self.name = name

        # a reference used to add new hosts
        self._model_controller = model_controller

        # create the widget
        self.widget = ShellWidget(qapp,gui_parent,name)
        self.widget.setupLayout()

        # For safesty we don't use the user shell, we force it to bash
        progname = "/bin/bash"
        # create the session
        #Session(gui, pgm, args, term, sessionid='session-1', cwd=None):
        #self.process_controller = ProcessController()
        self.session = Session(self.widget, progname, [], "xterm", name);

        self.session.setConnect(True)
        self.session.setHistory(HistoryTypeBuffer(1000))
        self._setUpSessionSinalHandlers()

        #self.__last_user_input = None
        #self.__user_input_signal = False

        # flag that determines if output has to be ignored
        self.__ignore_process_output = False
        self.__ignore_process_output_once = False
        self.__first_process_output = True
        self.__save_output_prompt_format = False

        # determines if input is for an interactive command or not
        self.__interactive = False

        #TODO: check if we need to connect to this signal
        self.session.myconnect('done', self.close)

        # instance a new plugin controller
        self.plugin_controller = plugin_controller(self.id)

        self._initial_prompt = ""
        #self._custom_prompt_format = re.compile("\\x1B\[1;32m\[.+@.+\s.+\]>(\$|#)\s+\\x1B\[m")
        #self._custom_prompt_format = re.compile("\[.+@.+\s.+\]>(\$|#)\s+")
        self._custom_prompt_format = re.compile("\[(?P<user>.+)@(?P<host>.+):(?P<path>.+)\]>(\$|#)")
        #TODO: somewhere in the config there should be a list of regexes used to match
        # prompts. Be careful with this because if regexes are too generic they will
        # match more that is needed.
        self._generic_prompt_formats = []
        #XXX: adding this format just as a test! This should be in the config somehow
        # Also this may be is too generic to use...
        self._generic_prompt_formats.append(re.compile("^.+@.+:.+(\$|#)$"))
        
        #This is a reference to MainApplication.deleteShellEnvironment
        self._close_callback = close_callback 
        
        # flag that determines if shell environment is running
        self.__running = False
        
        #Autocomplete
        self._options=[] #only keys
        self._tname="" # last tool executed
        self._lcount=0
        self._optionsh={} #keys + help
        self.__command = ''
    def __del__(self):
        """
        When deleteing the environment we have to delete all the components
        """
        #print "ShellEnvironment __del__ called"
        del self.session
        #TODO: delete tab holding current ShellWidget
        #tabmgr = self.widget.parentWidget()
        #tabmgr.removeView(self.widget)
        #FIXME: check the __del__ method in ShellWidget
        #del self.widget
        #TODO: make sure the plugin controller correctly finishes all processes
        del self.plugin_controller

    def close(self, session, status, *args):
        #TODO: por alguna razon queda colgado un QSocketNotifier
        # QSocketNotifier: invalid socket 17 and type 'Read', disabling...
        # y eso cuelga la aplicacion
        api.devlog("ShellEnvironment close was called - session = %r, status = %r , *args = %r" % (session, status, args))
        if self._close_callback is not None:
            self._close_callback(self.name, self)
        else:
            api.devlog("close was call but callback is not set")

    #def setCloseCallback(self, ref):
    #    self._close_callback = ref

    def get_id(self):
        global next_id
        # just to make sure each env has a differente id because names can be the same
        id = next_id
        next_id += 1
        return id

    def run(self):
        self.session.run()
        self.__running = True
        #self.plugin_controller.run()

    def _setUpSessionSinalHandlers(self):
        """
        Connects some signal handlers to different signals emmited inside shell
        emulation and pty.
        These signals are used to handle user input and child process output
        """
        #IMPORTANT ABOUT USER INPUT AND PROCESS OUPUT (Session class)
        #PtyProcess in method dataReceived gets the child output
        #in method sendBytes it has the user input
        #
        #Emulation in method sendString also has the user input
        #also check method onKeyPressed to identify when an ENTER key is pressed
        #
        #
        #OUTPUT
        # emulation onRcvBlock le llega la salida del proceso
        # self.myconnect('receivedStdout', self.dataReceived)
        # Este se llama desde childOutput en pty_
        #    self.myemit("receivedStdout", (fdno, lenlist))
        #
        # self.myemit('block_in', (buf,)) <--- se llama desde dataReceived
        # self.sh.myconnect('block_in', self.em.onRcvBlock)
        #
        #INPUT
        # self.myemit("sndBlock", (string,))
        # self.em.myconnect('sndBlock', self.sh.sendBytes)
        #
        # otra opcion es agregar una senal propia en los metodos sendString
        # o en onKeyPressed de emuVt102

        # connect signals to handle shell I/O
        self.session.sh.myconnect('processOutput', self.processOutputHandler)

        #XXX: nasty hack to be able to send a return value
        # Using myconnect and emitting singals won't allow us to return a value
        self.session.em.sendENTER = self.processUserInputBuffer
        self.widget.myconnect('ignoreShellWidgetResize', self.ignoreDueResize)
        
        #handle ctrl+space
        self.session.em.sendCTRLSPACE = self.processCtrlSpace
        self.session.em.sendRIGHT = self.processMove
        self.session.em.sendLEFT = self.processMove
        self.session.em.sendUP = self.processMove
        self.session.em.sendDOWN = self.processMove
        


    #---------------------------------------------------------------------------
    # methods to handle signals for shell I/O
    # these methods must be async to avoid blocking shell
    # XXX: signalable object is used.. and it is not really async
    #---------------------------------------------------------------------------


    def replaceWord(self, source, dest, output):
        #matchesWord = re.findall("(\x1b+\x1b?.*?%s\x1b?)"%source, output)
        matchesWord = re.findall("(\x1b\[01;34m127.0.0.1\x1b)", output)

        for w in matchesWord:
            output = output.replace(w, dest)

        return output

    def highligthword(self, w, output):
        highlighted_word = "\x1b[02;44m%s\x1b[0m" % w
        output = self.replaceWord(w, highlighted_word, output)
        return output

    def processOutputHandler(self, output):
        """
        This method is called when processOutput signal is emitted
        It sends the process output to the plugin controller so it can
        pass it to the corresponding plugin
        """
        # the output comes with escape chars for example to show things with colors
        # those escape chars are messing the text and plugins may not handle that
        # correctly.
        #TODO: implement some way of removing the escape sequences

        # if we get the output from the screen image we have some issues when the
        # output is longer than the actual size and scrolls the window
        #TODO: check how to handle the window scrolling

        #output = self.session.em.getLastOutputFromScreenImage(1)
        #api.devlog("-"*50)
        #api.devlog("processOutputHandler called - output =\n%r" % self.session.em.getLastOutputFromScreenImage(1))
        #api.devlog("processOutputHandler called - hist lines = %r" % self.session.em._scr.getHistLines())
        #api.devlog("-"*50)

        #TODO: is this really needed??? (to save first prompt output)
        if self.__first_process_output:
            # save the output as prompt
            #api.devlog("Saving prompt for the first time\n\tPROMPT: %r" % output)
            # then mark flag because it won't be the first anymore
            self._initial_prompt = output.strip()
            self.__first_process_output = False
            # after getting first output which is the default prompt
            # we change it and clear screen
            self.__setCurrentShellPromptFormat()
            self.session.updateLastUserInputLine()
            return

        if self.__save_output_prompt_format:
            # means the output is the PS1 format and we have to store it
            # The output is the result of running "echo $PS1" so 2 lines are
            # generated: one with the actual value of PS1 and one more
            # that is the prompt just because the echo finished
            # So we have to keep the first line only
            self._initial_prompt = ouput.splitlines()[0].strip()
            self.__save_output_prompt_format = False


        #strip_control_sequences(output)


        #print "AAAAAAAAAAAAAaaa: ", repr(output)
        #for word in wordsFound:
        #    output = self.highligthword(word, output)


        # check if output has to be ignored
        if not self.__ignore_process_output and not self.__ignore_process_output_once:
            api.devlog("processOutputHandler (PROCESSED):\n%r" % output)

            command_finished, output = self.check_command_end(output)

            #IMPORTANT: if no plugin was selected to process this output
            # we don't need to send to controller
            if self.plugin_controller.getActivePluginStatus():
                # always send all output to the plugin controller
                self.plugin_controller.storeCommandOutput(output)
                # if command ended we notify the plugin
                if command_finished:
                    api.devlog("calling plugin_controller.onCommandFinished()")
                    self.plugin_controller.onCommandFinished()
            else:
                api.devlog("<<< no active plugin...IGNORING OUTPUT >>>")

        else:
            #if re.search("export PS1",output) == None:
            #    self.__command += output

            #if re.search("export PS1",output) == None:
            #    self.__command += output
            #
            #if self.__command != "":
            #    api.devlog("processOutputHandler (Allcommand): (%s)" % self.__command)
            #    
            #    #TODO: hacer un regex inicial, y verificar si es el lugar exacto para poner esto.
            #    #TODO: No soporta el backspace o caracteres especiales
            #    #TODO: Recorrer todo no es performante, hay que revisar
            #    for h in self._model_controller._hosts.itervalues():
            #        if re.search(self.__command,h.name,flags=re.IGNORECASE):
            #            api.devlog("Host name found: " + h.name + " id ("+h.id+")");
            #        for o in h.getAllInterfaces():
            #            if re.search(self.__command,o.name,flags=re.IGNORECASE):
            #                api.devlog("Host name found: " + h.name + " id ("+h.id+") - Interface ("+o.name+") id ("+o.id+")");
            #        for o in h.getAllApplications():
            #            if re.search(self.__command,o.name,flags=re.IGNORECASE):
            #                api.devlog("Host name found: " + h.name + " id ("+h.id+") - Application ("+o.name+") id ("+o.id+")");
                    
                
            api.devlog("processOutputHandler (IGNORED by flags): \n%r" % output)
            #api.devlog("self.__ignore_process_output_once = %s" % self.__ignore_process_output_once)
            #api.devlog("self.__ignore_process_output = %s" % self.__ignore_process_output)
            self.__ignore_process_output_once = False


        



    def processMove(self):
        """
        this method is called when up/down/left/right
        """
        
        if not self.__interactive:
            self._options=[]
            self._optionsh={}
            self._tname = ""
            
    def processCtrlSpace(self):
        """
        this method is called when the Ctrl+Space is pressed
        """
        if not self.__interactive:
            # get the complete user input from screen image (this is done so we don't
            # have to worry about handling any key)
            user_input = self.session.em.getLastOutputFromScreenImage(get_spaces=True)
            # parse input to get the prompt and command in separated parts
            
            prompt, username, current_path, command_string, command_len = self.__parseUserInput(user_input,get_spaces=True)
            api.devlog("processCtrlSpace info("+user_input+")("+command_string+")")
            api.devlog("-"*60)
            api.devlog("CTRL + SPACE \nprompt = %r\ncommand = %r" % (prompt, command_string))
            api.devlog("self.__interactive = %s" % self.__interactive )
            api.devlog("-"*60)
            
            
            words=command_string.split(" ")
            #words2=command_string.split(" ")
            cword=words[len(words)-1] #obtengo la ultima palabra
            #words.remove(cword) #elimino la ultima palabra
            options=[]
            search=0
            mindex=0
            
            try: # si encuentra la palabra significa que se encuentra en una interaccion
                mindex = self._options.index(cword)
                #api.devlog("El tname es:" + self._tname)
                # Si no es la misma herramienta o cambio la cantidad de palabra significa que tengo que empezar de nuevo
                if (self._tname != words[1] and self._tname != "") or (self._lcount != len(words)): 
                    mindex = -1
            except ValueError:
                mindex = -1

            if mindex == -1: # si no la encuentra inicia de nuevo.
                self._options=[]
                self._optionsh={}
                search=1
            else:
                options=self._options                    
            
            #Guardo la cantidad palabras para comparar despues
            self._lcount = len(words)
            
            #save first command
            if len(words) >2:
                self._tname = words[1] #guardo el nombre de la tool
            else:
                self._tname = ""

            
            if search ==1 and cword !="":
                #Busqueda de Hosts (ignore si el comando que escribi es blanco)
                for h in self._model_controller.getAllHosts():
                    if re.search(str("^"+cword),h.name,flags=re.IGNORECASE):
                        if len(options) == 0:
                            options.append(cword)
                        api.devlog("Host name found: " + h.name + " id ("+h.id+")");
                        options.append(h.name)
                    #Busqueda de Hostname dentro de las interfaces
                    for i in h.getAllInterfaces():
                        for hostname in i.getHostnames():                            
                            if re.search(str("^"+cword),hostname,flags=re.IGNORECASE):
                                if len(options) == 0:
                                    options.append(cword)
                                api.devlog("Hostname found: " + hostname + " id ("+i.id+")");
                                options.append(hostname)

                self._options = options
            
            new_options={}
            api.devlog("Cantidad de _options" + str(len(self._options)))
            
            #Si no se encontro nada, busco opciones en el plugin
            if len(self._options) == 0:
                #try:
                if 1==1:
                    #Llamo al controller para ver si hay algun plugin que pueda dar opciones
                    #Devuelve un dict del estilo 'option' : 'help de la option'
                    new_options = self.plugin_controller.getPluginAutocompleteOptions(prompt, username,
                                                                         current_path,
                                                                         command_string,
                                                                         self.__interactive)
                    
                    
                    if new_options != None: 
                        if len(new_options) >= 1: #Si encontro plugin que maneje y  trae opciones hago iteracciones.
                            api.devlog("Options de plugin encontradas: ("+str(len(new_options))+") valores ("+str(new_options)+")")
                            options = [cword]+new_options.keys() #Guardo las opciones (agrego la word inicial)
                            self._options = options 
                            self._optionsh = new_options
                                
                            api.devlog("getPluginAutocompleteOptions: %r" % user_input)
                            api.devlog("new_options:" + str(options))
                if 1==2:
                #except Exception:
                    api.devlog("Exception: Plugin")
                    # if anything in the plugins fails and raises an exception we continue wihout doing anything
                    new_cmd = None                
            
            # Recorro las opciones disponibles
            #TODO: Reemplazar esto por una ventana desplegable o 
            i=0
            newword=""
            if len(options) > 1: # Reemplazar solo si hay opciones
                for w in options:
                    #api.devlog("Por la palabra ("+ w +") (" + str(i)+") la palabra(" + cword+")")
                    if cword==w:
                        if len(options) > i+1:
                            newword=options[i+1]
                            #api.devlog("La encontre next ("+ newword +") (" + str(i)+")"+ str(options) )
                        else:
                            newword=options[0]
                            #api.devlog("La encontre last ("+ newword +") (" + str(i)+")"+ str(options) )
                            #newword="-h"
                    i+=1
                    
                if self._optionsh.has_key(newword):
                    #TODO: reemplazar esto por un help distinto no usar el devlog
                    api.showPopup( newword + " :" + self._optionsh[newword])
                    #api.devlog("pluginhelp: " + newword + " :" + self._optionsh[newword])
                
                #Hago el cambio en la shell
                self.session.sh.sendBytes("\b" * len(cword) +  newword)

    def processUserInputBuffer(self):
        """
        this method is called when the ENTER is pressed
        It processes the user input buffer and then it clears it for future
        if a new command is returned by a plugin this is returned to the caller
        (which is onKeyPress in module emuVt102)
        """

        command_string=""
        command_len = 0
        if not self.__interactive:
            # get the complete user input from screen image (this is done so we don't
            # have to worry about handling any key)
            user_input = self.session.em.getLastOutputFromScreenImage(get_full_content=True)
            api.devlog("user_input parsed from screen(0) = %s" % user_input)
            # parse input to get the prompt and command in separated parts
            prompt, username, current_path, command_string, command_len = self.__parseUserInput(user_input)
            
            api.devlog("user_input parsed from screen(1) =%s" % self.session.em.getLastOutputFromScreenImage(index=1, get_full_content=True))

            # we send the buffer to the plugin controller to determine
            # if there is a plugin suitable to handle it
            api.devlog("-"*60)
            api.devlog("about to call plugin controller\nprompt = %r\ncommand = %r" % (prompt, command_string))
            api.devlog("self.__interactive = %s" % self.__interactive )
            api.devlog("-"*60)
            # when calling the plugin, the command string may be changed
            # if the configuration allows this we send it instead of the typed one
            #TODO: validate config to allow this

            try:
                new_cmd = self.plugin_controller.processCommandInput(prompt, username,
                                                                     current_path,
                                                                     command_string,
                                                                     self.__interactive)
                
                # we set it to interactive until we make sure the command has finished
                # this check is done in processOutputHandler
                self.__interactive = True
                api.devlog("processUserInputBuffer: %r" % user_input)
                api.devlog("new_cmd: %r" % new_cmd)
            except Exception, e:
                # if anything in the plugins fails and raises an exception we continue wihout doing anything
                api.devlog("ERROR: processCommandString")
                api.devlog(e)
                new_cmd = None

            if new_cmd is None:
                # the output MUST BE processed
                self.__ignore_process_output = False
                self.__ignore_process_output_once = False
            else:
                # means the plugin changed command and we are going to send
                # ALT + r to delete current line. That produces an empty output
                # which has to be ignored
                    
                self.__ignore_process_output_once = True
                self.__ignore_process_output = False
        else:
Example #4
0
class ShellEnvironment(ModelObject):
    """
    Shell environment is really a group of components that need to work together
    The environment is composed by:
        * a ShellWidget used in the GUI
        * a Session (which is the real shell)
        * a PluginController needed to handle all shell input and output
    The shell environment interacts with the Model Controller to add new hosts
    """
    def __init__(self, name, qapp, gui_parent, model_controller,
                 plugin_controller, close_callback=None):

        ModelObject.__init__(self)
        self._id = self.get_id()
        self.name = name

        # a reference used to add new hosts
        self._model_controller = model_controller

        # create the widget
        self.widget = ShellWidget(qapp,gui_parent,name)
        self.widget.setupLayout()

        # For safesty we don't use the user shell, we force it to bash
        progname = "/bin/bash"
        # create the session
        #Session(gui, pgm, args, term, sessionid='session-1', cwd=None):
        #self.process_controller = ProcessController()
        self.session = Session(self.widget, progname, [], "xterm", name);

        self.session.setConnect(True)
        self.session.setHistory(HistoryTypeBuffer(1000))
        self._setUpSessionSinalHandlers()

        #self.__last_user_input = None
        #self.__user_input_signal = False

        # flag that determines if output has to be ignored
        self.__ignore_process_output = False
        self.__ignore_process_output_once = False
        self.__first_process_output = True
        self.__save_output_prompt_format = False

        # determines if input is for an interactive command or not
        self.__interactive = False

        #TODO: check if we need to connect to this signal
        self.session.myconnect('done', self.close)

        # instance a new plugin controller
        self.plugin_controller = plugin_controller(self.id)

        self._initial_prompt = ""
        #self._custom_prompt_format = re.compile("\\x1B\[1;32m\[.+@.+\s.+\]>(\$|#)\s+\\x1B\[m")
        #self._custom_prompt_format = re.compile("\[.+@.+\s.+\]>(\$|#)\s+")
        self._custom_prompt_format = re.compile("\[(?P<user>.+)@(?P<host>.+):(?P<path>.+)\]>(\$|#)")
        #TODO: somewhere in the config there should be a list of regexes used to match
        # prompts. Be careful with this because if regexes are too generic they will
        # match more that is needed.
        self._generic_prompt_formats = []
        #XXX: adding this format just as a test! This should be in the config somehow
        # Also this may be is too generic to use...
        self._generic_prompt_formats.append(re.compile("^.+@.+:.+(\$|#)$"))
        
        #This is a reference to MainApplication.deleteShellEnvironment
        self._close_callback = close_callback 
        
        # flag that determines if shell environment is running
        self.__running = False
        
        #Autocomplete
        self._options=[] #only keys
        self._tname="" # last tool executed
        self._lcount=0
        self._optionsh={} #keys + help
        self.__command = ''
    def __del__(self):
        """
        When deleteing the environment we have to delete all the components
        """
        #print "ShellEnvironment __del__ called"
        del self.session
        #TODO: delete tab holding current ShellWidget
        #tabmgr = self.widget.parentWidget()
        #tabmgr.removeView(self.widget)
        #FIXME: check the __del__ method in ShellWidget
        #del self.widget
        #TODO: make sure the plugin controller correctly finishes all processes
        del self.plugin_controller

    def close(self, session, status, *args):
        #TODO: por alguna razon queda colgado un QSocketNotifier
        # QSocketNotifier: invalid socket 17 and type 'Read', disabling...
        # y eso cuelga la aplicacion
        api.devlog("ShellEnvironment close was called - session = %r, status = %r , *args = %r" % (session, status, args))
        if self._close_callback is not None:
            self._close_callback(self.name, self)
        else:
            api.devlog("close was call but callback is not set")

    #def setCloseCallback(self, ref):
    #    self._close_callback = ref

    def get_id(self):
        global next_id
        # just to make sure each env has a differente id because names can be the same
        id = next_id
        next_id += 1
        return id

    def run(self):
        self.session.run()
        self.__running = True
        #self.plugin_controller.run()

    def _setUpSessionSinalHandlers(self):
        """
        Connects some signal handlers to different signals emmited inside shell
        emulation and pty.
        These signals are used to handle user input and child process output
        """
        #IMPORTANT ABOUT USER INPUT AND PROCESS OUPUT (Session class)
        #PtyProcess in method dataReceived gets the child output
        #in method sendBytes it has the user input
        #
        #Emulation in method sendString also has the user input
        #also check method onKeyPressed to identify when an ENTER key is pressed
        #
        #
        #OUTPUT
        # emulation onRcvBlock le llega la salida del proceso
        # self.myconnect('receivedStdout', self.dataReceived)
        # Este se llama desde childOutput en pty_
        #    self.myemit("receivedStdout", (fdno, lenlist))
        #
        # self.myemit('block_in', (buf,)) <--- se llama desde dataReceived
        # self.sh.myconnect('block_in', self.em.onRcvBlock)
        #
        #INPUT
        # self.myemit("sndBlock", (string,))
        # self.em.myconnect('sndBlock', self.sh.sendBytes)
        #
        # otra opcion es agregar una senal propia en los metodos sendString
        # o en onKeyPressed de emuVt102

        # connect signals to handle shell I/O
        self.session.sh.myconnect('processOutput', self.processOutputHandler)

        #XXX: nasty hack to be able to send a return value
        # Using myconnect and emitting singals won't allow us to return a value
        self.session.em.sendENTER = self.processUserInputBuffer
        self.widget.myconnect('ignoreShellWidgetResize', self.ignoreDueResize)
        
        #handle ctrl+space
        self.session.em.sendCTRLSPACE = self.processCtrlSpace
        self.session.em.sendRIGHT = self.processMove
        self.session.em.sendLEFT = self.processMove
        self.session.em.sendUP = self.processMove
        self.session.em.sendDOWN = self.processMove
        


    #---------------------------------------------------------------------------
    # methods to handle signals for shell I/O
    # these methods must be async to avoid blocking shell
    # XXX: signalable object is used.. and it is not really async
    #---------------------------------------------------------------------------


    def replaceWord(self, source, dest, output):
        #matchesWord = re.findall("(\x1b+\x1b?.*?%s\x1b?)"%source, output)
        matchesWord = re.findall("(\x1b\[01;34m127.0.0.1\x1b)", output)

        for w in matchesWord:
            output = output.replace(w, dest)

        return output

    def highligthword(self, w, output):
        highlighted_word = "\x1b[02;44m%s\x1b[0m" % w
        output = self.replaceWord(w, highlighted_word, output)
        return output

    def processOutputHandler(self, output):
        """
        This method is called when processOutput signal is emitted
        It sends the process output to the plugin controller so it can
        pass it to the corresponding plugin
        """
        # the output comes with escape chars for example to show things with colors
        # those escape chars are messing the text and plugins may not handle that
        # correctly.
        #TODO: implement some way of removing the escape sequences

        # if we get the output from the screen image we have some issues when the
        # output is longer than the actual size and scrolls the window
        #TODO: check how to handle the window scrolling

        #output = self.session.em.getLastOutputFromScreenImage(1)
        #api.devlog("-"*50)
        #api.devlog("processOutputHandler called - output =\n%r" % self.session.em.getLastOutputFromScreenImage(1))
        #api.devlog("processOutputHandler called - hist lines = %r" % self.session.em._scr.getHistLines())
        #api.devlog("-"*50)

        #TODO: is this really needed??? (to save first prompt output)
        if self.__first_process_output:
            # save the output as prompt
            #api.devlog("Saving prompt for the first time\n\tPROMPT: %r" % output)
            # then mark flag because it won't be the first anymore
            self._initial_prompt = output.strip()
            self.__first_process_output = False
            # after getting first output which is the default prompt
            # we change it and clear screen
            self.__setCurrentShellPromptFormat()
            self.session.updateLastUserInputLine()
            return

        if self.__save_output_prompt_format:
            # means the output is the PS1 format and we have to store it
            # The output is the result of running "echo $PS1" so 2 lines are
            # generated: one with the actual value of PS1 and one more
            # that is the prompt just because the echo finished
            # So we have to keep the first line only
            self._initial_prompt = ouput.splitlines()[0].strip()
            self.__save_output_prompt_format = False


        #strip_control_sequences(output)


        #print "AAAAAAAAAAAAAaaa: ", repr(output)
        #for word in wordsFound:
        #    output = self.highligthword(word, output)


        # check if output has to be ignored
        if not self.__ignore_process_output and not self.__ignore_process_output_once:
            api.devlog("processOutputHandler (PROCESSED):\n%r" % output)

            command_finished, output = self.check_command_end(output)

            #IMPORTANT: if no plugin was selected to process this output
            # we don't need to send to controller
            if self.plugin_controller.getActivePluginStatus():
                # always send all output to the plugin controller
                self.plugin_controller.storeCommandOutput(output)
                # if command ended we notify the plugin
                if command_finished:
                    api.devlog("calling plugin_controller.onCommandFinished()")
                    self.plugin_controller.onCommandFinished()
            else:
                api.devlog("<<< no active plugin...IGNORING OUTPUT >>>")

        else:
            #if re.search("export PS1",output) == None:
            #    self.__command += output

            #if re.search("export PS1",output) == None:
            #    self.__command += output
            #
            #if self.__command != "":
            #    api.devlog("processOutputHandler (Allcommand): (%s)" % self.__command)
            #    
            #    #TODO: hacer un regex inicial, y verificar si es el lugar exacto para poner esto.
            #    #TODO: No soporta el backspace o caracteres especiales
            #    #TODO: Recorrer todo no es performante, hay que revisar
            #    for h in self._model_controller._hosts.itervalues():
            #        if re.search(self.__command,h.name,flags=re.IGNORECASE):
            #            api.devlog("Host name found: " + h.name + " id ("+h.id+")");
            #        for o in h.getAllInterfaces():
            #            if re.search(self.__command,o.name,flags=re.IGNORECASE):
            #                api.devlog("Host name found: " + h.name + " id ("+h.id+") - Interface ("+o.name+") id ("+o.id+")");
            #        for o in h.getAllApplications():
            #            if re.search(self.__command,o.name,flags=re.IGNORECASE):
            #                api.devlog("Host name found: " + h.name + " id ("+h.id+") - Application ("+o.name+") id ("+o.id+")");
                    
                
            api.devlog("processOutputHandler (IGNORED by flags): \n%r" % output)
            #api.devlog("self.__ignore_process_output_once = %s" % self.__ignore_process_output_once)
            #api.devlog("self.__ignore_process_output = %s" % self.__ignore_process_output)
            self.__ignore_process_output_once = False


        



    def processMove(self):
        """
        this method is called when up/down/left/right
        """
        
        if not self.__interactive:
            self._options=[]
            self._optionsh={}
            self._tname = ""
            
    def processCtrlSpace(self):
        """
        this method is called when the Ctrl+Space is pressed
        """
        if not self.__interactive:
            # get the complete user input from screen image (this is done so we don't
            # have to worry about handling any key)
            user_input = self.session.em.getLastOutputFromScreenImage(get_spaces=True)
            # parse input to get the prompt and command in separated parts
            
            prompt, username, current_path, command_string, command_len = self.__parseUserInput(user_input,get_spaces=True)
            api.devlog("processCtrlSpace info("+user_input+")("+command_string+")")
            api.devlog("-"*60)
            api.devlog("CTRL + SPACE \nprompt = %r\ncommand = %r" % (prompt, command_string))
            api.devlog("self.__interactive = %s" % self.__interactive )
            api.devlog("-"*60)
            
            
            words=command_string.split(" ")
            #words2=command_string.split(" ")
            cword=words[len(words)-1] #obtengo la ultima palabra
            #words.remove(cword) #elimino la ultima palabra
            options=[]
            search=0
            mindex=0
            
            try: # si encuentra la palabra significa que se encuentra en una interaccion
                mindex = self._options.index(cword)
                #api.devlog("El tname es:" + self._tname)
                # Si no es la misma herramienta o cambio la cantidad de palabra significa que tengo que empezar de nuevo
                if (self._tname != words[1] and self._tname != "") or (self._lcount != len(words)): 
                    mindex = -1
            except ValueError:
                mindex = -1

            if mindex == -1: # si no la encuentra inicia de nuevo.
                self._options=[]
                self._optionsh={}
                search=1
            else:
                options=self._options                    
            
            #Guardo la cantidad palabras para comparar despues
            self._lcount = len(words)
            
            #save first command
            if len(words) >2:
                self._tname = words[1] #guardo el nombre de la tool
            else:
                self._tname = ""

            
            if search ==1 and cword !="":
                #Busqueda de Hosts (ignore si el comando que escribi es blanco)
                for h in self._model_controller._hosts.itervalues():
                    if re.search(str("^"+cword),h.name,flags=re.IGNORECASE):
                        if len(options) == 0:
                            options.append(cword)
                        api.devlog("Host name found: " + h.name + " id ("+h.id+")");
                        options.append(h.name)
                    #Busqueda de Hostname dentro de las interfaces
                    for i in h.getAllInterfaces():
                        for hostname in i.getHostnames():                            
                            if re.search(str("^"+cword),hostname,flags=re.IGNORECASE):
                                if len(options) == 0:
                                    options.append(cword)
                                api.devlog("Hostname found: " + hostname + " id ("+i.id+")");
                                options.append(hostname)

                self._options = options
            
            new_options={}
            api.devlog("Cantidad de _options" + str(len(self._options)))
            
            #Si no se encontro nada, busco opciones en el plugin
            if len(self._options) == 0:
                #try:
                if 1==1:
                    #Llamo al controller para ver si hay algun plugin que pueda dar opciones
                    #Devuelve un dict del estilo 'option' : 'help de la option'
                    new_options = self.plugin_controller.getPluginAutocompleteOptions(prompt, username,
                                                                         current_path,
                                                                         command_string,
                                                                         self.__interactive)
                    
                    
                    if new_options != None: 
                        if len(new_options) >= 1: #Si encontro plugin que maneje y  trae opciones hago iteracciones.
                            api.devlog("Options de plugin encontradas: ("+str(len(new_options))+") valores ("+str(new_options)+")")
                            options = [cword]+new_options.keys() #Guardo las opciones (agrego la word inicial)
                            self._options = options 
                            self._optionsh = new_options
                                
                            api.devlog("getPluginAutocompleteOptions: %r" % user_input)
                            api.devlog("new_options:" + str(options))
                if 1==2:
                #except Exception:
                    api.devlog("Exception: Plugin")
                    # if anything in the plugins fails and raises an exception we continue wihout doing anything
                    new_cmd = None                
            
            # Recorro las opciones disponibles
            #TODO: Reemplazar esto por una ventana desplegable o 
            i=0
            newword=""
            if len(options) > 1: # Reemplazar solo si hay opciones
                for w in options:
                    #api.devlog("Por la palabra ("+ w +") (" + str(i)+") la palabra(" + cword+")")
                    if cword==w:
                        if len(options) > i+1:
                            newword=options[i+1]
                            #api.devlog("La encontre next ("+ newword +") (" + str(i)+")"+ str(options) )
                        else:
                            newword=options[0]
                            #api.devlog("La encontre last ("+ newword +") (" + str(i)+")"+ str(options) )
                            #newword="-h"
                    i+=1
                    
                if self._optionsh.has_key(newword):
                    #TODO: reemplazar esto por un help distinto no usar el devlog
                    api.showPopup( newword + " :" + self._optionsh[newword])
                    #api.devlog("pluginhelp: " + newword + " :" + self._optionsh[newword])
                
                #Hago el cambio en la shell
                self.session.sh.sendBytes("\b" * len(cword) +  newword)

    def processUserInputBuffer(self):
        """
        this method is called when the ENTER is pressed
        It processes the user input buffer and then it clears it for future
        if a new command is returned by a plugin this is returned to the caller
        (which is onKeyPress in module emuVt102)
        """

        command_string=""
        command_len = 0
        if not self.__interactive:
            # get the complete user input from screen image (this is done so we don't
            # have to worry about handling any key)
            user_input = self.session.em.getLastOutputFromScreenImage(get_full_content=True)
            api.devlog("user_input parsed from screen(0) = %s" % user_input)
            # parse input to get the prompt and command in separated parts
            prompt, username, current_path, command_string, command_len = self.__parseUserInput(user_input)
            
            api.devlog("user_input parsed from screen(1) =%s" % self.session.em.getLastOutputFromScreenImage(index=1, get_full_content=True))

            # we send the buffer to the plugin controller to determine
            # if there is a plugin suitable to handle it
            api.devlog("-"*60)
            api.devlog("about to call plugin controller\nprompt = %r\ncommand = %r" % (prompt, command_string))
            api.devlog("self.__interactive = %s" % self.__interactive )
            api.devlog("-"*60)
            # when calling the plugin, the command string may be changed
            # if the configuration allows this we send it instead of the typed one
            #TODO: validate config to allow this

            try:
                new_cmd = self.plugin_controller.processCommandInput(prompt, username,
                                                                     current_path,
                                                                     command_string,
                                                                     self.__interactive)
                
                # we set it to interactive until we make sure the command has finished
                # this check is done in processOutputHandler
                self.__interactive = True
                api.devlog("processUserInputBuffer: %r" % user_input)
                api.devlog("new_cmd: %r" % new_cmd)
            except Exception, e:
                # if anything in the plugins fails and raises an exception we continue wihout doing anything
                api.devlog("ERROR: processCommandString")
                api.devlog(e)
                new_cmd = None

            if new_cmd is None:
                # the output MUST BE processed
                self.__ignore_process_output = False
                self.__ignore_process_output_once = False
            else:
                # means the plugin changed command and we are going to send
                # ALT + r to delete current line. That produces an empty output
                # which has to be ignored
                    
                self.__ignore_process_output_once = True
                self.__ignore_process_output = False
        else: