"""
ajaxWM - the free web based window manager for remote terminals
Copyright (C) 2007 Dennis Felsing, Andreas Waidler

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

$Id: Session.py 76 2007-12-15 11:26:24Z r0q $
"""

#{{{ Imports
import time
import pty
import fcntl
import os

from Window import Window
from Error import Login_Error
from Ssh import Ssh
import ajaxwm
#}}}
class Session:#{{{
    """
Each client conects to exactly one Session object. This object manages 
the windows of the client.
    """
    WINDOW_MAX = 2 ** 6
    TIMEOUT = 10 * 60 # in seconds
    LOGIN_SLEEP = 0.005

    def __init__(self, host, username, password):#{{{
        # Do not permit spaces!
        self.host = host.translate({32: None})
        if self.host.find(':') != -1:
            self.host, self.port = host.split(':')
        else:
            self.port = u'22'
        self.username = username
        self.password = password
        self.windows  = []
        self.time = time.time()
    #}}}
    def tryLogin(self):#{{{
        """
Checks if logging in with the entered data is possible. Works
nearly the same way as the method run of the Window class, look there 
for more details.
        """
        pid, fd = pty.fork()
        if pid != 0:
            content = ''
            timeout = time.time()
            if self.host in (u'localhost', u'127.0.0.1'):
                # On localhost delays normally aren't that big.
                timeout += 1
            else:
                # But they might be if we are working as a gateway.
                timeout += 5
            
            fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
            while 1:
                try:
                    content = ajaxwm.read(fd)
                except OSError:
                    pass
                
                try:
                    if 'assword:' in content:
                        os.write(fd, self.password + '\n')
                        while 1:
                            try:
                                content = ajaxwm.read(fd)
                            except OSError:
                                pass

                            if ('Connection to %s closed.' 
                            % self.host.encode(ajaxwm.ENCODING)) in content:
                                # Login worked.
                                raise Login_Error, 0
                            if timeout < time.time():
                                # Wrong username or password.
                                raise Login_Error, -1
                            time.sleep(self.LOGIN_SLEEP)
                    if timeout < time.time():
                        # Wrong host or sshd not running.
                        raise Login_Error, -2

                except Login_Error, e:
                    os.close(fd)
                    return e.value
                
                time.sleep(self.LOGIN_SLEEP)

        ssh = Ssh()
        cmd = ssh.tryLogin(self.username, self.host, self.port)
        os.execvpe(cmd[0], cmd, None)                         
    #}}}
    def newWindow(self, width=80, height=25):#{{{
        """Create a new Window and return its ID."""
        win = Window(self.username, self.host, self.port, self.password, 
                     width, height)
        # To improve performance append windows normally.
        if len(self.windows) < Session.WINDOW_MAX:
            self.windows.append(win)
            self.windows[-1].start()
            self.windows[-1].join()
            return len(self.windows) - 1
        # Else search for free window from 0 to limit (no need in most cases).
        else:
            for windowId, wind in enumerate(self.windows):
                if wind == None:
                    wind = win
                    wind.start()
                    return windowId
        # All windows are used.
        return -1
    #}}}
    def deleteWindow(self, window):#{{{
        """Delete an existing window and return 1 on success, 0 on failure."""
        self.windows[window] = None
    #}}}
    def sync(self, window):#{{{
        """
Reads the contents of a specified pty to a Terminal object.
The time for the loop is set to the time.
        """
        if window < len(self.windows) and self.windows[window]:
            self.time = time.time()
            self.windows[window].read()
    #}}}
#}}}

#------------------------------------------------------------------------------
# vim: fdm=marker ts=4 sw=4 expandtab 
