[X2Go-Commits] python-x2go.git - build-baikal (branch) updated: 79a34563e372ff34bc23ba3d6090c4175221f2ed

X2Go dev team git-admin at x2go.org
Wed Jan 8 15:25:20 CET 2014


The branch, build-baikal has been updated
       via  79a34563e372ff34bc23ba3d6090c4175221f2ed (commit)
      from  2111f148a5af2349cd426f4274561fa801bca9ff (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 x2go/__init__.py                                   |    2 +-
 {tests => x2go/backends}/__init__.py               |    9 +-
 test.py => x2go/backends/control/__init__.py       |   21 +-
 x2go/backends/control/stdout.py                    |  496 ++++++++
 test.py => x2go/backends/info/__init__.py          |   21 +-
 x2go/backends/info/stdout.py                       |  165 +++
 test.py => x2go/backends/profiles/__init__.py      |   21 +-
 .../profiles/https_broker.py}                      |   18 +-
 .../profiles/sessions_file.py}                     |   18 +-
 .../profiles/win_registry.py}                      |   19 +-
 {tests => x2go/backends/proxy}/__init__.py         |   13 +-
 x2go/{proxy.py => backends/proxy/base.py}          |  107 +-
 x2go/backends/proxy/nx3.py                         |  128 ++
 test.py => x2go/backends/terminal/__init__.py      |   18 +-
 x2go/backends/terminal/stdout.py                   |  720 +++++++++++
 x2go/client.py                                     |   28 +-
 x2go/defaults.py                                   |   37 +-
 x2go/printing.py                                   |    2 +-
 x2go/registry.py                                   |  415 +------
 x2go/rforward.py                                   |   14 +-
 x2go/session.py                                    | 1298 +++++---------------
 21 files changed, 1975 insertions(+), 1595 deletions(-)
 copy {tests => x2go/backends}/__init__.py (80%)
 copy test.py => x2go/backends/control/__init__.py (67%)
 create mode 100644 x2go/backends/control/stdout.py
 copy test.py => x2go/backends/info/__init__.py (59%)
 create mode 100644 x2go/backends/info/stdout.py
 copy test.py => x2go/backends/profiles/__init__.py (63%)
 copy x2go/{profiles.py => backends/profiles/https_broker.py} (94%)
 copy x2go/{profiles.py => backends/profiles/sessions_file.py} (94%)
 rename x2go/{profiles.py => backends/profiles/win_registry.py} (94%)
 copy {tests => x2go/backends/proxy}/__init__.py (73%)
 rename x2go/{proxy.py => backends/proxy/base.py} (62%)
 create mode 100644 x2go/backends/proxy/nx3.py
 copy test.py => x2go/backends/terminal/__init__.py (69%)
 create mode 100644 x2go/backends/terminal/stdout.py

The diff of changes is:
diff --git a/x2go/__init__.py b/x2go/__init__.py
index 77a5b2b..70c7ba2 100644
--- a/x2go/__init__.py
+++ b/x2go/__init__.py
@@ -166,7 +166,7 @@ _signal.signal (_signal.SIGTERM, guardian._sigterm_handle )
 _signal.signal (_signal.SIGINT, guardian._sigterm_handle )
 
 from client import X2goClient
-from profiles import X2goSessionProfiles
+from backends.profiles import X2goSessionProfiles
 from printing import X2goClientPrinting
 from settings import X2goClientSettings
 from x2go_exceptions import *
diff --git a/tests/__init__.py b/x2go/backends/__init__.py
similarity index 80%
copy from tests/__init__.py
copy to x2go/backends/__init__.py
index 6e5cefb..d2ad588 100644
--- a/tests/__init__.py
+++ b/x2go/backends/__init__.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 
 # Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
-# 
+#
 # Python X2go 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 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # Python X2go is distributed in the hope that it will be useful,
@@ -15,7 +15,4 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the
 # Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
-import runalltests
-import test_printing
\ No newline at end of file
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
diff --git a/test.py b/x2go/backends/control/__init__.py
similarity index 67%
copy from test.py
copy to x2go/backends/control/__init__.py
index 34b2284..e7780c4 100644
--- a/test.py
+++ b/x2go/backends/control/__init__.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
-# 
+#
 # Python X2go 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 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # Python X2go is distributed in the hope that it will be useful,
@@ -16,13 +15,13 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the
 # Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from gevent import monkey
+monkey.patch_all()
+
+from x2go.defaults import DEFAULT_CONTROLSESSION_BACKEND
 
-"""
-Unit tests for Python X2go.
-"""
-import os
+from stdout import X2goControlSessionSTDOUT
 
-if __name__ == "__main__":
-    os.chdir('tests')
-    os.system('./runalltests.py')
\ No newline at end of file
+X2goControlSession = eval(DEFAULT_CONTROLSESSION_BACKEND)
diff --git a/x2go/backends/control/stdout.py b/x2go/backends/control/stdout.py
new file mode 100644
index 0000000..31089c3
--- /dev/null
+++ b/x2go/backends/control/stdout.py
@@ -0,0 +1,496 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
+#
+# Python X2go 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.
+#
+# Python X2go 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""\
+X2goControlSessionSTDOUT class - core functions for handling your individual X2go sessions.
+
+This backend handles X2go server implementations that respond via server-side STDOUT.
+
+"""
+__NAME__ = 'x2gocontrolsession-pylib'
+
+# modules
+import types
+import paramiko
+
+import copy
+
+# Python X2go modules
+import x2go.log as log
+import x2go.utils as utils
+import x2go.x2go_exceptions as x2go_exceptions
+import x2go.defaults as defaults
+
+from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession
+from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
+from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
+
+class X2goControlSessionSTDOUT(paramiko.SSHClient):
+    """\
+    STILL UNDOCUMENTED
+
+    @param logger: you can pass an L{X2goLogger} object to the
+        L{X2goProxy} constructor
+    @type logger: L{X2goLogger} instance
+    @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+        constructed with the given loglevel
+    @type loglevel: int
+
+    """
+    associated_terminals = {}
+    terminated_terminals = []
+
+    _session_auth_rsakey = None
+    _remote_home = None
+    _remote_group = {}
+
+    def __init__(self,
+                 terminal_backend=_X2goTerminalSession,
+                 info_backend=_X2goServerSessionInfo,
+                 list_backend=_X2goServerSessionList,
+                 logger=None, loglevel=log.loglevel_DEFAULT,
+                 *args, **kwargs):
+        """\
+        Initialize an X2go session. With the X2goSession class you can start
+        new X2go sessions, resume suspended sessions or suspend resp. terminate
+        currently running sessions on a connected X2go server.
+
+        """
+        if logger is None:
+            self.logger = log.X2goLogger(loglevel=loglevel)
+        else:
+            self.logger = copy.deepcopy(logger)
+        self.logger.tag = __NAME__
+
+        self._terminal_backend = terminal_backend
+        self._info_backend = info_backend
+        self._list_backend = list_backend
+        paramiko.SSHClient.__init__(self, *args, **kwargs)
+
+    def __del__(self):
+
+        self.disconnect()
+
+    def _x2go_sftp_put(self, local_path, remote_path):
+
+        self.logger('sFTP-put: %s -> %s:%s' % (local_path, self.get_transport().getpeername(), remote_path), loglevel=log.loglevel_DEBUG)
+        self.sftp_client.put(local_path, remote_path)
+
+    def _x2go_sftp_write(self, remote_path, content):
+
+        self.logger('sFTP-write: opening remote file %s on host %s for writing' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_DEBUG)
+        remote_fileobj = self.sftp_client.open(remote_path, 'w')
+        self.logger('sFTP-write: writing content: %s' % content, loglevel=log.loglevel_DEBUG_SFTPXFER)
+        remote_fileobj.write(content)
+        remote_fileobj.close()
+
+    def _x2go_sftp_remove(self, remote_path):
+
+        self.logger('sFTP-write: removing remote file %s on host %s' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_DEBUG)
+        self.sftp_client.remove(remote_path)
+
+    def _x2go_exec_command(self, cmd_line, loglevel=log.loglevel_INFO, **kwargs):
+
+        if type(cmd_line) == types.ListType:
+            cmd = " ".join(cmd_line)
+        else:
+            cmd = cmd_line
+        if self.get_transport() is not None:
+
+            try:
+                self.logger('executing command on X2go server: %s' % cmd, loglevel)
+                return self.exec_command(cmd, **kwargs)
+            except AttributeError:
+                raise x2go_exceptions.X2goSessionException('a Paramiko/SSH control session has died')
+
+        else:
+            raise x2go_exceptions.X2goSessionException('the Paramiko/SSH client is not connected')
+
+    @property
+    def _x2go_remote_home(self):
+
+        if self._remote_home is None:
+            (stdin, stdout, stderr) = self._x2go_exec_command('echo $HOME')
+            self._remote_home = stdout.read().split()[0]
+            self.logger('remote user\' home directory: %s' % self._remote_home, loglevel=log.loglevel_DEBUG)
+            return self._remote_home
+        else:
+            return self._remote_home
+
+    def _x2go_remote_group(self, group):
+
+        if not self._remote_group.has_key(group):
+            (stdin, stdout, stderr) = self._x2go_exec_command('getent group %s | cut -d":" -f4' % group)
+            self._remote_group[group] = stdout.read().split('\n')[0].split(',')
+            self.logger('remote %s group: %s' % (group, self._remote_group[group]), loglevel=log.loglevel_DEBUG)
+            return self._remote_group[group]
+        else:
+            return self._remote_group[group]
+
+    @property
+    def _x2go_session_auth_rsakey(self):
+        if self._session_auth_rsakey is None:
+            self._session_auth_rsakey = paramiko.RSAKey.generate(defaults.RSAKEY_STRENGTH)
+        return self._session_auth_rsakey
+
+    def connect(self, hostname, port=22, username=None, password=None, pkey=None,
+                key_filename=None, timeout=None, allow_agent=False, look_for_keys=True,
+                add_to_known_hosts=False, force_password_auth=False):
+        """\
+        Connect to an X2go server and authenticate to it. This method is directly
+        inherited from the paramiko.SSHClient module. The features of the Paramiko 
+        SSH client connect method are recited here. The parameters C{add_to_known_hosts}
+        and C{force_password_auth} have been added as a parameter for X2go.
+
+        The server's host key
+        is checked against the system host keys (see C{load_system_host_keys})
+        and any local host keys (C{load_host_keys}).  If the server's hostname
+        is not found in either set of host keys, the missing host key policy
+        is used (see C{set_missing_host_key_policy}).  The default policy is
+        to reject the key and raise an C{SSHException}.
+
+        Authentication is attempted in the following order of priority:
+
+            - The C{pkey} or C{key_filename} passed in (if any)
+            - Any key we can find through an SSH agent
+            - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
+            - Plain username/password auth, if a password was given
+
+        If a private key requires a password to unlock it, and a password is
+        passed in, that password will be used to attempt to unlock the key.
+
+        @param hostname: the server to connect to
+        @type hostname: str
+        @param port: the server port to connect to
+        @type port: int
+        @param username: the username to authenticate as (defaults to the
+            current local username)
+        @type username: str
+        @param password: a password to use for authentication or for unlocking
+            a private key
+        @type password: str
+        @param pkey: an optional private key to use for authentication
+        @type pkey: C{PKey}
+        @param key_filename: the filename, or list of filenames, of optional
+            private key(s) to try for authentication
+        @type key_filename: str or list(str)
+        @param timeout: an optional timeout (in seconds) for the TCP connect
+        @type timeout: float
+        @param allow_agent: set to False to disable connecting to the SSH agent
+        @type allow_agent: C{bool}
+        @param look_for_keys: set to False to disable searching for discoverable
+            private key files in C{~/.ssh/}
+        @type look_for_keys: C{bool}
+        @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy() 
+            is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy() 
+            is used
+        @type add_to_known_hosts: C{bool}
+        @param force_password_auth: non-paramiko option, disable pub/priv key authentication 
+            completely, even if the C{pkey} or the C{key_filename} parameter is given
+        @type force_password_auth: C{bool}
+
+        @raise BadHostKeyException: if the server's host key could not be
+            verified
+        @raise AuthenticationException: if authentication failed
+        @raise SSHException: if there was any other error connecting or
+            establishing an SSH session
+        @raise socket.error: if a socket error occurred while connecting
+
+        """
+        if add_to_known_hosts:
+            self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+
+        # disable pub/priv key authentication if forced
+        if force_password_auth:
+            key_filename = None
+            pkey = None
+
+        self.logger('connecting to %s' % hostname, log.loglevel_NOTICE)
+
+        if (key_filename or pkey):
+            try:
+                self.logger('trying SSH pub/priv key authentication with server', log.loglevel_DEBUG)
+                paramiko.SSHClient.connect(self, hostname, port=port, username=username, pkey=pkey,
+                                           key_filename=key_filename, timeout=timeout, allow_agent=allow_agent, 
+                                           look_for_keys=look_for_keys)
+            except paramiko.AuthenticationException, e:
+                if password:
+                    self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', log.loglevel_DEBUG)
+                    paramiko.SSHClient.connect(self, hostname, port=port, username=username, password=password,
+                                               timeout=timeout, allow_agent=allow_agent, 
+                                               look_for_keys=look_for_keys)
+                else:
+                    raise(e)
+
+        # if there is not private key, we will use the given password
+        elif password:
+            self.logger('performing SSH keyboard-interactive authentication with server', log.loglevel_DEBUG)
+            paramiko.SSHClient.connect(self, hostname, port=port, username=username, password=password, 
+                                       timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys)
+
+        # authentication failed
+        else:
+            raise paramiko.AuthenticationException()
+
+        # if we succeed, we immediately grab us an sFTP client session
+        self.sftp_client = self.open_sftp()
+
+        # preparing reverse tunnels
+        ssh_transport = self.get_transport()
+        ssh_transport.reverse_tunnels = {}
+
+        # mark transport as X2goSession
+        ssh_transport._x2go_session_marker = True
+        self._session_password = password
+
+        return (self.get_transport() is not None)
+
+    def disconnect(self):
+        """\
+        STILL UNDOCUMENTED
+
+        """
+        for t in self.associated_terminals:
+            del t
+        if self.get_transport() is not None:
+            self.get_transport().close()
+
+    def start(self, **kwargs):
+        """\
+        Start a new X2go session. 
+
+        The L{X2goControlSession.start()} method accepts any parameter
+        that can be passed to any of the L{X2goTerminalSession} class 
+        constructors.
+
+        """
+        return self.resume(**kwargs)
+
+    def resume(self, session_name=None, **kwargs):
+        """\
+        Resume a running/suspended X2go session. 
+
+        The L{X2goControlSession.resume()} method accepts any parameter
+        that can be passed to the L{X2goTerminalSession} class constructor.
+
+        @return: True if the session could be successfully resumed
+        @rtype: C{bool}
+
+        """
+        if self.get_transport().get_username() not in self._x2go_remote_group('x2gousers'):
+            raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group x2gousers' % self.get_transport().get_username())
+
+        _terminal = self._terminal_backend(self, **kwargs)
+        if session_name is not None:
+            if self.is_running(session_name):
+                self.suspend(session_name)
+            _terminal.resume()
+        else:
+            _terminal.start()
+
+        if _terminal.ok():
+            self.associated_terminals[_terminal.get_session_name()] = _terminal
+            self.get_transport().reverse_tunnels[_terminal.get_session_name()] = {
+                'sshfs': (0, None),
+                'snd': (0, None),
+            }
+
+        return _terminal or None
+
+    def list_sessions(self, raw=False):
+        """\
+        List all sessions of current user on the connected server.
+
+        @param raw: if C{True}, the raw output of the server-side X2go command 
+            C{x2golistsessions} is returned.
+        @type raw: C{bool}
+
+        @return: normally an instance of L{X2goServerSessionList} is returned. However,
+            if the raw argument is set, the plain text output of the x2golistsessions 
+            command is returned
+        @rtype: L{X2goServerSessionList} instance or str
+
+        """
+        (stdin, stdout, stderr) = self._x2go_exec_command("x2golistsessions")
+
+        if raw:
+            return stdout.read(), stderr.read()
+
+        _stdout_read = stdout.read()
+        return self._list_backend(_stdout_read, info_backend=self._info_backend).sessions
+
+    def clean_sessions(self):
+        """\
+        Find X2go terminals that have previously been started by the
+        connected user on the remote X2go server and terminate them.
+
+        """
+        session_infos = self.list_sessions()
+        for session_info in session_infos.values():
+            self.terminate(session_name=session_info)
+
+    def is_connected(self):
+        """\
+        Returns C{True} if this X2go session is connected to the remote server (that
+        is if it has a valid Paramiko Transport object).
+
+        @param session_name: X2go name of the session to be queried
+        @type session_name: str
+
+        @return: X2go session connected
+        @rtype: C{bool}
+
+        """
+        return self.get_transport() is not None and self.get_transport().is_authenticated()
+
+    def is_running(self, session_name):
+        """\
+        Returns C{True} if the given X2go session is in running state,
+        C{False} else.
+
+        @param session_name: X2go name of the session to be queried
+        @type session_name: str
+
+        @return: X2go session running?
+        @rtype: C{bool}
+
+        """
+        session_infos = self.list_sessions()
+        if session_name in session_infos.keys():
+            return session_infos[session_name].is_running()
+        return False
+
+    def is_suspended(self, session_name):
+        """\
+        Returns C{True} if the given X2go session is in suspended state,
+        C{False} else.
+
+        @return: X2go session suspended?
+        @rtype: C{bool}
+
+        """
+        session_infos = self.list_sessions()
+        if session_name in session_infos.keys():
+            return session_infos[session_name].is_suspended()
+        return False
+
+    def has_terminated(self, session_name):
+        """\
+        Returns C{True} if this X2go session is not in the session list on the 
+        connected server, C{False} else.
+
+        Of course, if this command is called before session startup, it will also
+        return C{True}.
+
+        @return: X2go session has terminate?
+        @rtype: C{bool}
+
+        """
+        session_infos = self.list_sessions()
+
+
+        if session_name not in session_infos.keys():
+            if session_name in self.terminated_terminals:
+                return True
+            else:
+                # do a post-mortem tidy up
+                if session_name in self.associated_terminals.keys():
+                    self.terminate(session_name)
+                return True
+
+        return False
+
+    def suspend(self, session_name):
+        """\
+        Suspend either this or another available X2go session on the connected
+        server.
+
+        If L{session_name} is given, L{X2goSession.suspend()} tries to suspend the
+        corresponding session.
+
+        @param session_name: X2go name of the session to be suspended
+        @type session_name: str
+
+        @return: True if the session could be successfully suspended
+        @rtype: C{bool}
+
+        """
+        _ret = False
+        _session_names = [ t.get_session_name() for t in associated_terminals.values() ]
+        if session_name in _session_names:
+
+            self.logger('suspending associated terminal session: %s' % session_name, log.loglevel_DEBUG)
+            (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
+            dummy_stdout = stdout.read()
+            dummy_stderr = stderr.read()
+            associated_terminals[session_name].__del__()
+            del associated_terminals[session_name]
+            _ret = True
+
+        else:
+
+            self.logger('suspending non-associated terminal session: %s' % session_name, log.loglevel_DEBUG)
+            (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
+            dummy_stdout = stdout.read()
+            dummy_stderr = stderr.read()
+            _ret = True
+
+        return _ret
+
+    def terminate(self, session_name):
+        """\
+        Terminate either this or another available X2go session on the connected
+        server.
+
+        If L{session_name} is given, L{X2goSession.terminate()} tries to terminate the
+        corresponding session.
+
+        @param session_name: X2go name of the session to be terminated
+        @type session_name: str
+
+        @return: True if the session could be successfully terminate
+        @rtype: C{bool}
+
+        """
+
+        _ret = False
+        _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ]
+        if session_name in _session_names:
+
+            self.logger('suspending associated session: %s' % session_name, log.loglevel_DEBUG)
+            (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
+            dummy_stdout = stdout.read()
+            dummy_stderr = stderr.read()
+            if self.associated_terminals[session_name] is not None:
+                self.associated_terminals[session_name].__del__()
+            del self.associated_terminals[session_name]
+            self.terminated_terminals.append(session_name)
+            _ret = True
+
+        else:
+
+            self.logger('suspending non-associated session: %s' % session_name, log.loglevel_DEBUG)
+            (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
+            dummy_stdout = stdout.read()
+            dummy_stderr = stderr.read()
+            _ret = True
+
+        return _ret
+
+
diff --git a/test.py b/x2go/backends/info/__init__.py
similarity index 59%
copy from test.py
copy to x2go/backends/info/__init__.py
index 34b2284..0606d7b 100644
--- a/test.py
+++ b/x2go/backends/info/__init__.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
-# 
+#
 # Python X2go 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 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # Python X2go is distributed in the hope that it will be useful,
@@ -16,13 +15,13 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the
 # Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from x2go.defaults import DEFAULT_SERVERSESSIONINFO_BACKEND
+from x2go.defaults import DEFAULT_SERVERSESSIONLIST_BACKEND
 
-"""
-Unit tests for Python X2go.
-"""
-import os
+from stdout import X2goServerSessionInfoSTDOUT
+from stdout import X2goServerSessionListSTDOUT
 
-if __name__ == "__main__":
-    os.chdir('tests')
-    os.system('./runalltests.py')
\ No newline at end of file
+X2goServerSessionInfo = eval(DEFAULT_SERVERSESSIONINFO_BACKEND)
+X2goServerSessionList = eval(DEFAULT_SERVERSESSIONLIST_BACKEND)
diff --git a/x2go/backends/info/stdout.py b/x2go/backends/info/stdout.py
new file mode 100644
index 0000000..b675bb9
--- /dev/null
+++ b/x2go/backends/info/stdout.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
+#
+# Python X2go 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.
+#
+# Python X2go 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""\
+X2goServerSessionList and X2goServerSessionInfo classes - data handling for 
+X2go server sessions.
+
+This backend handles X2go server implementations that respond with session infos 
+via server-side STDOUT.
+
+"""
+__NAME__ = 'x2goserversessioninfo-pylib'
+
+class X2goServerSessionInfoSTDOUT(object):
+    """\
+    L{X2goServerSessionInfo} is used to store all information
+    that is retrieved from the connected X2go server on 
+    L{X2goSession.start()} resp. L{X2goSession.resume()}.
+
+    """
+    def __str__(self):
+        return self.name
+    def __repr__(self):
+        return "<%s instance: %s>" % (self.__class__, self.name)
+
+    def _parse_x2golistsessions_line(self, x2go_output):
+        """\
+        Parse a single line of X2go's listsessions output.
+
+        """
+        l = x2go_output.split("|")
+        self.name = l[1]
+        self.cookie = l[6]
+        self.agent_pid = int(l[0])
+        self.display = int(l[2])
+        self.status = l[4]
+        self.graphics_port = int(l[8])
+        self.snd_port = int(l[9])
+        self.sshfs_port = int(l[13])
+        self.username = l[11]
+        self.hostname = l[3]
+        # TODO: turn into datetime object
+        self.date_created = l[5]
+        # TODO: turn into datetime object
+        self.date_suspended = l[10]
+        self.local_container = ''
+
+    def is_running(self):
+
+        return self.status == 'R'
+
+    def is_suspended(self):
+
+        return self.status == 'S'
+
+    def _parse_x2gostartagent_output(self, x2go_output):
+        """\
+        Parse x2gostartagent output.
+
+        """
+        l = x2go_output.split("\n")
+        self.name = l[3]
+        self.cookie = l[1]
+        self.agent_pid = int(l[2])
+        self.display = int(l[0])
+        self.graphics_port = int(l[4])
+        self.snd_port = int(l[5])
+        self.sshfs_port = int(l[6])
+        self.username = ''
+        self.hostname = ''
+        # TODO: we have to see how we fill these fields here...
+        self.date_created = ''
+        self.date_suspended = ''
+        # TODO: presume session is running after x2gostartagent, this could be better
+        self.status = 'R'
+        self.local_container = ''
+        self.remote_container = ''
+
+    def initialize(self, x2go_output, username='', hostname='', local_container='', remote_container=''):
+        """\
+        Parse X2go server's C{x2gostartagent} stdout values.
+
+        @param x2go_output: X2go server's C{x2gostartagent} command output, each value 
+            separated by a newline character.
+        @type x2go_output: str
+        @param username: session user name
+        @type username: str
+        @param hostname: hostname of X2go server
+        @type hostname: str
+        @param local_container: X2go client session directory for config files, cache and session logs
+        @type local_container: str
+        @param remote_container: X2go server session directory for config files, cache and session logs
+        @type remote_container: str
+
+        """
+        self._parse_x2gostartagent_output(x2go_output)
+        self.username = username
+        self.hostname = hostname
+        self.local_container = local_container
+        self.remote_container = remote_container
+
+    def clear(self):
+        """\
+        Clear all properties of a L{X2goServerSessionInfo} object.
+
+        """
+        self.name = ''
+        self.cookie = ''
+        self.agent_pid = ''
+        self.display = ''
+        self.graphics_port = ''
+        self.snd_port = ''
+        self.sshfs_port = ''
+        self.username = ''
+        self.hostname = ''
+        self.date_created = ''
+        self.date_suspended = ''
+        self.status = ''
+        self.local_container = ''
+        self.remote_container = ''
+
+    __init__ = clear
+
+
+class X2goServerSessionListSTDOUT(object):
+    """\
+    L{X2goServerSessionList} is used to store all information
+    that is retrieved from a connected X2go server on a
+    L{X2goSession.list_sessions()} call.
+
+    """
+    def __init__(self, x2go_output, info_backend=X2goServerSessionInfoSTDOUT):
+        """\
+        @param x2go_output: X2go server's C{x2golistsessions} command output, each 
+            session separated by a newline character. Session values are separated 
+            by Unix Pipe Symbols ('|')
+        @type x2go_output: str
+
+        """
+        self.sessions = {}
+        lines = x2go_output.split("\n")
+        for line in lines:
+            if not line:
+                continue
+            s_info = info_backend()
+            s_info._parse_x2golistsessions_line(line)
+            self.sessions[s_info.name] = s_info
+
+
diff --git a/test.py b/x2go/backends/profiles/__init__.py
similarity index 63%
copy from test.py
copy to x2go/backends/profiles/__init__.py
index 34b2284..8eab147 100644
--- a/test.py
+++ b/x2go/backends/profiles/__init__.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
-# 
+#
 # Python X2go 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 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # Python X2go is distributed in the hope that it will be useful,
@@ -16,13 +15,13 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the
 # Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from x2go.defaults import DEFAULT_SESSIONPROFILES_BACKEND
+
+from sessions_file import X2goSessionProfilesFILE
+from win_registry import X2goSessionProfilesWINREG
+from https_broker import X2goSessionProfilesHTTP
 
-"""
-Unit tests for Python X2go.
-"""
-import os
+X2goSessionProfiles = eval(DEFAULT_SESSIONPROFILES_BACKEND)
 
-if __name__ == "__main__":
-    os.chdir('tests')
-    os.system('./runalltests.py')
\ No newline at end of file
diff --git a/x2go/profiles.py b/x2go/backends/profiles/https_broker.py
similarity index 94%
copy from x2go/profiles.py
copy to x2go/backends/profiles/https_broker.py
index b6e4e6b..8355e81 100644
--- a/x2go/profiles.py
+++ b/x2go/backends/profiles/https_broker.py
@@ -29,15 +29,15 @@ __NAME__ = 'x2gosessionprofiles-pylib'
 import copy
 
 # Python X2go modules
-from defaults import X2GO_SESSIONPROFILES_CONFIGFILES
-from defaults import X2GO_SESSIONPROFILE_DEFAULTS
-import inifiles
-import log
-import utils
-from x2go_exceptions import X2goProfileException
+from x2go.defaults import X2GO_SESSIONPROFILES_CONFIGFILES
+from x2go.defaults import X2GO_SESSIONPROFILE_DEFAULTS
+import x2go.inifiles as inifiles
+import x2go.log as log
+import x2go.utils as utils
+from x2go.x2go_exceptions import X2goProfileException
 
 
-class X2goSessionProfiles(inifiles.X2goIniFile):
+class X2goSessionProfilesHTTP(inifiles.X2goIniFile):
 
     defaultValues = {}
     defaultSessionProfile = X2GO_SESSIONPROFILE_DEFAULTS
@@ -151,11 +151,13 @@ class X2goSessionProfiles(inifiles.X2goIniFile):
         """
         return self.get_profile_config(profile_id)['name']
 
-    def add_profile(self, profile_id, **kwargs):
+    def add_profile(self, profile_id=None, **kwargs):
         """\
         STILL UNDOCUMENTED
 
         """
+        if profile_id is None:
+            profile_id = utils._get_SessionProfileId()
         for key, value in kwargs.items():
             if key in self.defaultSessionProfile:
                 self.update_value(profile_id, key, value)
diff --git a/x2go/profiles.py b/x2go/backends/profiles/sessions_file.py
similarity index 94%
copy from x2go/profiles.py
copy to x2go/backends/profiles/sessions_file.py
index b6e4e6b..d6fe03d 100644
--- a/x2go/profiles.py
+++ b/x2go/backends/profiles/sessions_file.py
@@ -29,15 +29,15 @@ __NAME__ = 'x2gosessionprofiles-pylib'
 import copy
 
 # Python X2go modules
-from defaults import X2GO_SESSIONPROFILES_CONFIGFILES
-from defaults import X2GO_SESSIONPROFILE_DEFAULTS
-import inifiles
-import log
-import utils
-from x2go_exceptions import X2goProfileException
+from x2go.defaults import X2GO_SESSIONPROFILES_CONFIGFILES
+from x2go.defaults import X2GO_SESSIONPROFILE_DEFAULTS
+import x2go.inifiles as inifiles
+import x2go.log as log
+import x2go.utils as utils
+from x2go.x2go_exceptions import X2goProfileException
 
 
-class X2goSessionProfiles(inifiles.X2goIniFile):
+class X2goSessionProfilesFILE(inifiles.X2goIniFile):
 
     defaultValues = {}
     defaultSessionProfile = X2GO_SESSIONPROFILE_DEFAULTS
@@ -151,11 +151,13 @@ class X2goSessionProfiles(inifiles.X2goIniFile):
         """
         return self.get_profile_config(profile_id)['name']
 
-    def add_profile(self, profile_id, **kwargs):
+    def add_profile(self, profile_id=None, **kwargs):
         """\
         STILL UNDOCUMENTED
 
         """
+        if profile_id is None:
+            profile_id = utils._get_SessionProfileId()
         for key, value in kwargs.items():
             if key in self.defaultSessionProfile:
                 self.update_value(profile_id, key, value)
diff --git a/x2go/profiles.py b/x2go/backends/profiles/win_registry.py
similarity index 94%
rename from x2go/profiles.py
rename to x2go/backends/profiles/win_registry.py
index b6e4e6b..380c37a 100644
--- a/x2go/profiles.py
+++ b/x2go/backends/profiles/win_registry.py
@@ -29,15 +29,16 @@ __NAME__ = 'x2gosessionprofiles-pylib'
 import copy
 
 # Python X2go modules
-from defaults import X2GO_SESSIONPROFILES_CONFIGFILES
-from defaults import X2GO_SESSIONPROFILE_DEFAULTS
-import inifiles
-import log
-import utils
-from x2go_exceptions import X2goProfileException
+from x2go.defaults import X2GO_SESSIONPROFILES_CONFIGFILES
+from x2go.defaults import X2GO_SESSIONPROFILE_DEFAULTS
+import x2go.inifiles as inifiles
+import x2go.log as log
+import x2go.utils as hostname 
 
+from x2go.x2go_exceptions import X2goProfileException
 
-class X2goSessionProfiles(inifiles.X2goIniFile):
+
+class X2goSessionProfilesWINREG(inifiles.X2goIniFile):
 
     defaultValues = {}
     defaultSessionProfile = X2GO_SESSIONPROFILE_DEFAULTS
@@ -151,11 +152,13 @@ class X2goSessionProfiles(inifiles.X2goIniFile):
         """
         return self.get_profile_config(profile_id)['name']
 
-    def add_profile(self, profile_id, **kwargs):
+    def add_profile(self, profile_id=None, **kwargs):
         """\
         STILL UNDOCUMENTED
 
         """
+        if profile_id is None:
+            profile_id = utils._get_SessionProfileId()
         for key, value in kwargs.items():
             if key in self.defaultSessionProfile:
                 self.update_value(profile_id, key, value)
diff --git a/tests/__init__.py b/x2go/backends/proxy/__init__.py
similarity index 73%
copy from tests/__init__.py
copy to x2go/backends/proxy/__init__.py
index 6e5cefb..5ce688d 100644
--- a/tests/__init__.py
+++ b/x2go/backends/proxy/__init__.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 
 # Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
-# 
+#
 # Python X2go 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 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # Python X2go is distributed in the hope that it will be useful,
@@ -15,7 +15,10 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the
 # Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from x2go.defaults import DEFAULT_PROXY_BACKEND
+
+from nx3 import X2goProxyNX3
 
-import runalltests
-import test_printing
\ No newline at end of file
+X2goProxy = eval(DEFAULT_PROXY_BACKEND)
diff --git a/x2go/proxy.py b/x2go/backends/proxy/base.py
similarity index 62%
rename from x2go/proxy.py
rename to x2go/backends/proxy/base.py
index 5dd8c35..8353d3c 100644
--- a/x2go/proxy.py
+++ b/x2go/backends/proxy/base.py
@@ -18,7 +18,7 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """\
-X2goProxy classes - proxying your connection through NX3 and others.
+X2goProxyBASE class - proxying your connection through NX3 and others.
 
 """
 __NAME__ = 'x2goproxy-pylib'
@@ -33,20 +33,20 @@ import copy
 import threading
 
 # Python X2go modules
-import forward
-import log 
+import x2go.forward as forward
+import x2go.log as log
 
-from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
+from x2go.defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
 if _X2GOCLIENT_OS in ("Windows"):
     import subprocess
 else:
-    import gevent_subprocess as subprocess
+    import x2go.gevent_subprocess as subprocess
 
-from defaults import LOCAL_HOME as _LOCAL_HOME
-from defaults import X2GO_SESSION_ROOTDIR as _X2GO_SESSION_ROOTDIR
+from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
+from x2go.defaults import X2GO_SESSION_ROOTDIR as _X2GO_SESSION_ROOTDIR
 
 
-class X2goProxy(threading.Thread):
+class X2goProxyBASE(threading.Thread):
     """\
     X2goProxy is an abstract class for X2go proxy connections.
 
@@ -106,8 +106,6 @@ class X2goProxy(threading.Thread):
 
         """
         self.stop_thread()
-        #if p.poll() is None:
-        #    p.kill()
 
     def _tidy_up(self):
         """\
@@ -134,8 +132,6 @@ class X2goProxy(threading.Thread):
         self._keepalive = False
         gevent.sleep(1)
         self._tidy_up()
-        #if p.poll() is None:
-        #    p.kill()
 
     def run(self):
         """\
@@ -203,90 +199,3 @@ class X2goProxy(threading.Thread):
         return self.proxy
 
 
-class X2goNX3Proxy(X2goProxy):
-    """\
-    X2goNX3Proxy is a NX version 3 based X2go proxy connection class.
-
-    It basically fills X2goProxy variables with sensible content. Its 
-    methods mostly wrap around the corresponding methods of the parent class.
-
-    """
-    def __init__(self, *args, **kwargs):
-        """\
-        For available parameters refer to L{X2goProxy} class documentation.
-
-        """
-        X2goProxy.__init__(self, *args, **kwargs)
-
-        # setting some default environment variables, nxproxy paths etc.
-        if _X2GOCLIENT_OS == "Windows":
-            self.PROXY_CMD = os.path.join(os.environ["ProgramFiles"], os.path.normpath("x2goclient/nxproxy.exe"))
-        else:
-            self.PROXY_CMD = "/usr/bin/nxproxy"
-        self.PROXY_ENV.update({
-            "NX_CLIENT": "/bin/true",
-            "NX_ROOT": os.path.join(_LOCAL_HOME, _X2GO_SESSION_ROOTDIR)
-        })
-        self.PROXY_MODE = '-S'
-        if _X2GOCLIENT_OS == "Windows":
-            self.PROXY_OPTIONS = [
-                "nx/nx" ,
-                "retry=5",
-                "composite=1",
-                "connect=localhost",
-                "cookie=%s" % self.session_info.cookie,
-                "port=%d" % self.session_info.graphics_port,
-                "errors=%s" % os.path.join(".", "..", "S-%s" % self.session_info.name, self.session_log, ),
-            ]
-        else:
-            self.PROXY_OPTIONS = [
-                "nx/nx" ,
-                "retry=5",
-                "composite=1",
-                "connect=localhost",
-                "cookie=%s" % self.session_info.cookie,
-                "port=%d" % self.session_info.graphics_port,
-                "errors=%s" % os.path.join(self.session_info.local_container, self.session_log, ),
-            ]
-
-        self.PROXY_DISPLAY = self.session_info.display
-
-    def _update_local_proxy_socket(self, port):
-        for idx, a in enumerate(self.PROXY_OPTIONS):
-            if a.startswith('port='):
-                self.PROXY_OPTIONS[idx] = 'port=%s' % port
-
-    def _generate_cmdline(self):
-
-        if (_X2GOCLIENT_OS == "Windows") and (len(",".join(self.PROXY_OPTIONS)) >= 250):
-            _options_filename = os.path.join(self.session_info.local_container, 'options')
-            options = open(_options_filename, 'w')
-            options.write('%s:%s' % (','.join(self.PROXY_OPTIONS), self.PROXY_DISPLAY))
-            options.close()
-            self.PROXY_OPTIONS= [ 'nx/nx', 'options=%s' % os.path.join(".", "..", "S-%s" % self.session_info.name, 'options'), ]
-
-        cmd_line = [ self.PROXY_CMD, ]
-        cmd_line.append(self.PROXY_MODE)
-        _proxy_options = "%s:%s" % (",".join(self.PROXY_OPTIONS), self.PROXY_DISPLAY)
-        cmd_line.append(_proxy_options)
-        return cmd_line
-
-
-    def start_proxy(self):
-        self.logger('starting local NX3 proxy...', loglevel=log.loglevel_INFO)
-        self.logger('NX3 Proxy mode is server, cookie=%s, host=localhost, port=%s.' % (self.session_info.cookie, self.session_info.graphics_port,), loglevel=log.loglevel_DEBUG)
-        self.logger('NX3 proxy writes session log to %s.' % os.path.join(self.session_info.local_container, 'session.log'), loglevel=log.loglevel_DEBUG)
-
-        p = X2goProxy.start_proxy(self)
-
-        if p is not None:
-            self.logger('NX3 proxy is up and running.', loglevel=log.loglevel_INFO)
-        else:
-            self.logger('Bringing up NX3 proxy failed.', loglevel=log.loglevel_ERROR)
-
-        return p
-
-
-# this is our default proxy: NX3 Proxy
-DEFAULT_PROXY_CLASS = X2goNX3Proxy
-"""Currently L{X2goNX3Proxy} is Python X2go's default proxy class."""
\ No newline at end of file
diff --git a/x2go/backends/proxy/nx3.py b/x2go/backends/proxy/nx3.py
new file mode 100644
index 0000000..a6b73a3
--- /dev/null
+++ b/x2go/backends/proxy/nx3.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
+#
+# Python X2go 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.
+#
+# Python X2go 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""\
+X2goProxy classes - proxying your connection through NX3 and others.
+
+"""
+__NAME__ = 'x2goproxynx3-pylib'
+
+# modules
+import gevent
+import os
+import sys
+import types
+import time
+import copy
+import threading
+
+# Python X2go modules
+import x2go.forward as forward
+import x2go.log as log
+import base
+
+from x2go.defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
+from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
+from x2go.defaults import X2GO_SESSION_ROOTDIR as _X2GO_SESSION_ROOTDIR
+
+class X2goProxyNX3(base.X2goProxyBASE):
+    """\
+    X2goNX3Proxy is a NX version 3 based X2go proxy connection class.
+
+    It basically fills L{X2goProxyBASE} variables with sensible content. Its 
+    methods mostly wrap around the corresponding methods of the parent class.
+
+    """
+    def __init__(self, *args, **kwargs):
+        """\
+        For available parameters refer to L{X2goProxyBASE} class documentation.
+
+        """
+        base.X2goProxyBASE.__init__(self, *args, **kwargs)
+
+        # setting some default environment variables, nxproxy paths etc.
+        if _X2GOCLIENT_OS == "Windows":
+            self.PROXY_CMD = os.path.join(os.environ["ProgramFiles"], os.path.normpath("x2goclient/nxproxy.exe"))
+        else:
+            self.PROXY_CMD = "/usr/bin/nxproxy"
+        self.PROXY_ENV.update({
+            "NX_CLIENT": "/bin/true",
+            "NX_ROOT": os.path.join(_LOCAL_HOME, _X2GO_SESSION_ROOTDIR)
+        })
+        self.PROXY_MODE = '-S'
+        if _X2GOCLIENT_OS == "Windows":
+            self.PROXY_OPTIONS = [
+                "nx/nx" ,
+                "retry=5",
+                "composite=1",
+                "connect=localhost",
+                "cookie=%s" % self.session_info.cookie,
+                "port=%d" % self.session_info.graphics_port,
+                "errors=%s" % os.path.join(".", "..", "S-%s" % self.session_info.name, self.session_log, ),
+            ]
+        else:
+            self.PROXY_OPTIONS = [
+                "nx/nx" ,
+                "retry=5",
+                "composite=1",
+                "connect=localhost",
+                "cookie=%s" % self.session_info.cookie,
+                "port=%d" % self.session_info.graphics_port,
+                "errors=%s" % os.path.join(self.session_info.local_container, self.session_log, ),
+            ]
+
+        self.PROXY_DISPLAY = self.session_info.display
+
+    def _update_local_proxy_socket(self, port):
+        for idx, a in enumerate(self.PROXY_OPTIONS):
+            if a.startswith('port='):
+                self.PROXY_OPTIONS[idx] = 'port=%s' % port
+
+    def _generate_cmdline(self):
+
+        if (_X2GOCLIENT_OS == "Windows") and (len(",".join(self.PROXY_OPTIONS)) >= 250):
+            _options_filename = os.path.join(self.session_info.local_container, 'options')
+            options = open(_options_filename, 'w')
+            options.write('%s:%s' % (','.join(self.PROXY_OPTIONS), self.PROXY_DISPLAY))
+            options.close()
+            self.PROXY_OPTIONS= [ 'nx/nx', 'options=%s' % os.path.join(".", "..", "S-%s" % self.session_info.name, 'options'), ]
+
+        cmd_line = [ self.PROXY_CMD, ]
+        cmd_line.append(self.PROXY_MODE)
+        _proxy_options = "%s:%s" % (",".join(self.PROXY_OPTIONS), self.PROXY_DISPLAY)
+        cmd_line.append(_proxy_options)
+        return cmd_line
+
+    def start_proxy(self):
+        self.logger('starting local NX3 proxy...', loglevel=log.loglevel_INFO)
+        self.logger('NX3 Proxy mode is server, cookie=%s, host=localhost, port=%s.' % (self.session_info.cookie, self.session_info.graphics_port,), loglevel=log.loglevel_DEBUG)
+        self.logger('NX3 proxy writes session log to %s.' % os.path.join(self.session_info.local_container, 'session.log'), loglevel=log.loglevel_DEBUG)
+
+        p = base.X2goProxyBASE.start_proxy(self)
+
+        if p is not None:
+            self.logger('NX3 proxy is up and running.', loglevel=log.loglevel_INFO)
+        else:
+            self.logger('Bringing up NX3 proxy failed.', loglevel=log.loglevel_ERROR)
+
+        return p
+
+
+
+#
diff --git a/test.py b/x2go/backends/terminal/__init__.py
similarity index 69%
copy from test.py
copy to x2go/backends/terminal/__init__.py
index 34b2284..91c158c 100644
--- a/test.py
+++ b/x2go/backends/terminal/__init__.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
-# 
+#
 # Python X2go 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 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # Python X2go is distributed in the hope that it will be useful,
@@ -16,13 +15,10 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the
 # Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from x2go.defaults import DEFAULT_TERMINALSESSION_BACKEND
 
-"""
-Unit tests for Python X2go.
-"""
-import os
+from stdout import X2goTerminalSessionSTDOUT
 
-if __name__ == "__main__":
-    os.chdir('tests')
-    os.system('./runalltests.py')
\ No newline at end of file
+X2goTerminalSession = eval(DEFAULT_TERMINALSESSION_BACKEND)
diff --git a/x2go/backends/terminal/stdout.py b/x2go/backends/terminal/stdout.py
new file mode 100644
index 0000000..7e6d61a
--- /dev/null
+++ b/x2go/backends/terminal/stdout.py
@@ -0,0 +1,720 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
+#
+# Python X2go 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.
+#
+# Python X2go 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""\
+X2goTerminalSession class - core functions for handling your individual X2go sessions.
+
+This backend handles X2go server implementations that respond with session infos 
+via server-side STDOUT and use NX3 as graphical proxy.
+
+"""
+__NAME__ = 'x2goterminalsession-pylib'
+
+# modules
+import os, sys, types
+import gevent
+import threading
+import signal
+import cStringIO
+import copy
+
+# Python X2go modules
+import x2go.rforward as rforward
+import x2go.sftpserver as sftpserver
+import x2go.printing as printing
+import x2go.log as log
+import x2go.defaults as defaults
+import x2go.utils as utils
+import x2go.x2go_exceptions as x2go_exceptions
+import x2go.guardian as guardian
+
+from x2go.cleanup import x2go_cleanup 
+
+# we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables)
+from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
+from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
+from x2go.defaults import X2GO_SESSION_ROOTDIR as _X2GO_SESSION_ROOTDIR
+
+from x2go.backends.info import X2goServerSessionInfo
+from x2go.backends.info import X2goServerSessionList
+from x2go.backends.proxy import X2goProxy 
+
+def _rewrite_cmd(cmd):
+
+    # start with an empty string
+    cmd = cmd or ''
+
+    # find window manager commands
+    if cmd in defaults.window_managers.keys():
+        cmd = defaults.window_managers[cmd]
+
+    # X2go run command replace X2GO_SPACE_CHAR string with blanks
+    cmd.replace(" ", "X2GO_SPACE_CHAR")
+
+    # place quot marks around cmd if not empty string
+    if cmd:
+        cmd = '"%s"' % cmd
+    return cmd
+
+
+class X2goSessionParams(object):
+    """\
+    The L{X2goSessionParams} class is used to store all parameters that
+    L{X2goSession} objects are constructed with.
+
+    """
+    def rewrite_session_type(self):
+        """\
+        Rewrite the X2go session type, so that the X2go server
+        can understand it (C{desktop} -> C{D}).
+
+        Also if the object's C{command} property is a known window 
+        manager, the session type will be set to 'D' 
+        (i.e. desktop).
+
+        @return: 'D' if session should probably a desktop session,
+            'R' (for rootless) else
+        @rtype: str
+
+        """
+        session_type = self.session_type
+        cmd = self.cmd
+        if session_type == "desktop":
+            self.session_type = 'D'
+            return
+        if cmd:
+            if cmd in defaults.window_managers.keys():
+                self.session_type = 'D'
+                return
+            if os.path.basename(cmd) in defaults.window_managers.values():
+                self.session_type = 'D'
+                return
+        self.session_type = 'R'
+
+    def update(self, properties_to_be_updated={}):
+        """\
+        Update all properties in the object L{X2goSessionParams} object from
+        the passed on dictionary.
+
+        @param properties_to_be_updated: a dictionary with L{X2goSessionParams}
+            property names as keys und their values to be update in 
+            L{X2goSessionParams} object.
+        @type properties_to_be_updated: dict
+
+        """
+        for key in properties_to_be_updated.keys():
+            setattr(self, key, properties_to_be_updated[key] or '')
+        self.rewrite_session_type()
+
+
+class X2goTerminalSessionSTDOUT(object):
+    """\
+    Class for managing X2go sessions on a remote X2go server via Paramiko/SSH. 
+    With the X2goSession class you can start new X2go sessions, resume suspended 
+    sessions or suspend resp. terminate currently running sessions on a 
+    connected X2go server.
+
+    When suspending or terminating sessions there are two possible ways:
+
+        1. Initialize an X2go session object, start a new session (or resume)
+        and use the L{X2goSession.suspend()} or L{X2goSession.terminate()} method
+        to suspend/terminate the current session object.
+        2. Alternatively, you can pass a session name to L{X2goSession.suspend()}
+        or L{X2goSession.terminate()}. If a session of this name exists on the
+        X2go server the respective action will be performed on the session.
+
+    An L{X2goSession} object uses two main data structure classes: 
+
+        - L{X2goSessionParams}: stores all parameters that have been passed to the 
+        constructor method.
+
+        - L{X2goServerSessionInfo}: when starting or resuming a session, an object of this class 
+        will be used to store all information retrieved from the X2go server.
+
+    @param geometry: screen geometry of the X2go session. Can be either C{<width>x<height>}
+        or C{fullscreen}
+    @type geometry: str
+    @param depth: color depth in bits (common values: C{16}, C{24})
+    @type depth: int
+    @param link: network link quality (either one of C{modem}, C{isdn}, C{adsl}, C{wan} or C{lan})
+    @type link: str
+    @param pack: compression method for NX based session proxying
+    @type pack: str
+    @param cache_type: a dummy parameter that is passed to the L{X2goProxy}. In NX Proxy 
+        (class C{X2goNX3Proxy}) this originally is the session name. With X2go it 
+        defines the name of the NX cache directory. Best is to leave it untouched.
+    @type cache_type: str
+    @param kblayout: keyboard layout, e.g. C{us} (default), C{de}, C{fr}, ...
+    @type kblayout: str
+    @param kbtype: keyboard type, e.g. C{pc105/us} (default), C{pc105/de}, ...
+    @type kbtype: str
+    @param session_type: either C{desktop} or C{application} (rootless session)
+    @type session_type: str
+    @param snd_system: sound system to be used on server (C{none}, C{pulse} (default), 
+        C{arts} (obsolete) or C{esd})
+    @type snd_system: str
+    @param cmd: command to be run on X2go server after session start (only used
+        when L{X2goSession.start()} is called, ignored on resume, suspend etc.
+    @type cmd: str
+    @param rootdir: X2go session directory, normally C{~/.x2go}
+    @type rootdir: str
+    @param proxy_class: other than the default L{X2goProxy} class
+    @type proxy_class: L{X2goProxy} related instance
+    @param print_action: either a print action short name (PDFVIEW, PDFSAVE, PRINT, PRINTCMD) or the
+        resp. C{X2goPrintActionXXX} class (where XXX equals one of the given short names)
+    @type print_action: str or class
+    @param print_action_args: optional arguments for a given print_action (for further info refer to
+        L{X2goPrintActionPDFVIEW}, L{X2goPrintActionPDFSAVE}, L{X2goPrintActionPRINT} and L{X2goPrintActionPRINTCMD})
+    @type print_action_args: dict
+    @param logger: you can pass an L{X2goLogger} object to the
+        L{X2goProxy} constructor
+    @type logger: L{X2goLogger} instance
+    @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+        constructed with the given loglevel
+    @type loglevel: int
+
+    """
+    params = None
+    session_info = None
+    control_session = None
+
+    proxy_class = None
+    proxy = None
+    proxy_subprocess = None
+
+    guardian_thread = None
+    reverse_tunnels = {}
+
+    print_queue = None
+
+    def __init__(self, control_session, session_info=None,
+                 geometry="800x600", depth=24, link="adsl", pack="16m-jpeg-9", 
+                 cache_type="unix-kde", kblayout='us', kbtype='pc105/us',
+                 session_type="application", snd_system='pulse', cmd=None,
+                 rootdir=None,
+                 profile_name='UNKNOWN', profile_id=utils._genSessionProfileId(),
+                 print_action=None, print_action_args={},
+                 info_backend=X2goServerSessionInfo,
+                 list_backend=X2goServerSessionList,
+                 proxy_backend=X2goProxy,
+                 logger = None, loglevel=log.loglevel_DEFAULT):
+        """\
+        Initialize an X2go session. With the X2goSession class you can start
+        new X2go sessions, resume suspended sessions or suspend resp. terminate
+        currently running sessions on a connected X2go server.
+
+        """
+        if logger is None:
+            self.logger = log.X2goLogger(loglevel=loglevel)
+        else:
+            self.logger = copy.deepcopy(logger)
+        self.logger.tag = __NAME__
+
+        self.control_session = control_session
+        self.reverse_tunnels = self.control_session.get_transport().reverse_tunnels
+
+        self.params = X2goSessionParams()
+
+        if session_info is not None:
+            if self.session_info.name:
+                self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
+            else:
+                raise X2goSessionException('no valid session info availble')
+        else:
+            self.session_info = info_backend()
+
+        self.params.geometry = geometry
+        self.params.depth = str(depth)
+        self.params.link = link
+        self.params.pack = pack
+        self.params.cache_type = cache_type
+        self.params.session_type = session_type
+        self.params.kblayout = kblayout
+        self.params.kbtype = kbtype
+        self.params.snd_system = snd_system
+        self.params.cmd = cmd
+        self.params.rootdir = (type(rootdir) is types.StringType) and rootdir or os.path.join(_LOCAL_HOME,_X2GO_SESSION_ROOTDIR)
+        self.params.update()
+
+        self.proxy_class = proxy_backend
+
+        self.print_action = print_action
+        self.print_action_args = print_action_args
+
+        self._mk_session_rootdir(self.params.rootdir)
+
+        # each terminal session has its own guardian
+        self.guardian_thread = guardian.X2goSessionGuardian(self, logger=self.logger)
+        self.guardian_thread.start()
+
+
+    def __del__(self):
+        self._x2go_tidy_up()
+
+    def _x2go_tidy_up(self):
+
+        if self.proxy is not None:
+            self.proxy.__del__()
+
+        try:
+
+            if self.control_session.get_transport() is not None:
+                for _tunnel in [ _tun[1] for _tun in self.reverse_tunnels[self.session_info.name].values() ]:
+                    if _tunnel is not None:
+                        _tunnel.__del__()
+
+            if self.print_queue is not None:
+                self.print_queue.__del__()
+
+        except AttributeError:
+            pass
+
+    def _mk_session_rootdir(self, d):
+
+        try:
+            os.mkdir(d)
+        except OSError, e:
+            if e.errno == 17:
+                # file exists
+                pass
+            else:
+                raise OSError, e
+
+    def get_session_name(self):
+        """\
+        STILL UNDOCUMENTED
+
+        """
+        return self.session_info.name
+
+    def start_sound(self):
+        """\
+        Initialize Paramiko/SSH reverse forwarding tunnel for X2go sound.
+
+        Currently supported audio protocols:
+
+            - Pulse Audio
+            - Esound 
+
+        """
+        _tunnel = None
+        if self.reverse_tunnels[self.session_info.name]['snd'][1] is None:
+            if self.params.snd_system == 'pulse':
+                self.logger('initializing Pulse Audio sound support in X2go session', loglevel=log.loglevel_INFO)
+                ###
+                ### PULSE AUDIO
+                ###
+                # setup pulse client config file on X2go server
+                cmd_line = "echo 'default-server=localhost:%s'>%s/.pulse-client.conf;" % (self.session_info.snd_port, self.session_info.remote_container) + \
+                           "echo 'cookie-file=%s/.pulse-cookie'>>%s/.pulse-client.conf" % (self.session_info.remote_container, self.session_info.remote_container)
+                (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
+
+                self.control_session._x2go_sftp_put(local_path='%s/.pulse-cookie' % _LOCAL_HOME, remote_path='%s/.pulse-cookie' % self.session_info.remote_container)
+
+                # start reverse SSH tunnel for pulse stream
+                _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 
+                                                   remote_host='localhost', 
+                                                   remote_port=4713, 
+                                                   ssh_transport=self.control_session.get_transport(),
+                                                   logger=self.logger
+                                                  )
+
+            elif self.params.snd_system == 'arts':
+                ###
+                ### ARTSD AUDIO
+                ###
+                self.logger('the ArtsD sound server (as in KDE3) is obsolete and will not be supported by Python X2go...', loglevel=log.loglevel_WARNING)
+
+            elif self.params.snd_system == 'esd':
+                ###
+                ### ESD AUDIO
+                ###
+
+                self.logger('initializing ESD sound support in X2go session', loglevel=log.loglevel_INFO)
+                self.control_session._x2go_sftp_put(local_path='%s/.esd_auth' % _LOCAL_HOME, remote_path='%s/.esd_auth' % self.control_session._x2go_remote_home)
+
+                # start reverse SSH tunnel for pulse stream
+                _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 
+                                                   remote_host='localhost', 
+                                                   remote_port=16001, 
+                                                   ssh_transport=self.control_session.get_transport(),
+                                                   logger=self.logger
+                                                  )
+
+
+            if _tunnel is not None:
+                self.reverse_tunnels[self.session_info.name]['snd'] = (self.session_info.snd_port, _tunnel)
+                _tunnel.start()
+                self.guardian_thread.active_threads.append(_tunnel)
+
+        else:
+            # tunnel has already been started and might simply need a resume call
+            self.reverse_tunnels[self.session_info.name]['snd'][1].resume()
+
+    def start_sshfs(self):
+        """\
+        Initialize Paramiko/SSH reverse forwarding tunnel for X2go folder sharing.
+
+        """
+        # start reverse SSH tunnel for sshfs (folder sharing, printing)
+        ssh_transport = self.control_session.get_transport()
+        if self.reverse_tunnels[self.session_info.name]['sshfs'][1] is None:
+
+            _tunnel = sftpserver.X2goRevFwTunnelToSFTP(server_port=self.session_info.sshfs_port,
+                                                       ssh_transport=ssh_transport,
+                                                       auth_key=self.control_session._x2go_session_auth_rsakey,
+                                                       logger=self.logger
+                                                      )
+
+            if _tunnel is not None:
+                self.reverse_tunnels[self.session_info.name]['sshfs'] = (self.session_info.sshfs_port, _tunnel)
+                _tunnel.start()
+                self.guardian_thread.active_threads.append(_tunnel)
+
+        else:
+            # tunnel has already been started and might simply need a resume call
+            self.reverse_tunnels[self.session_info.name]['sshfs'][1].resume()
+
+    def _x2go_pause_rev_fw_tunnel(self, name):
+        # pause reverse SSH tunnel of name <name>
+        ssh_transport = self.get_transport()
+        _tunnel = self.reverse_tunnels[self.session_info.name][name][1]
+        if _tunnel is not None:
+            _tunnel.pause()
+
+    def stop_sound(self):
+        """\
+        Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go sound.
+
+        """
+        self._x2go_pause_rev_fw_tunnel('snd')
+
+    def stop_sshfs(self):
+        """\
+        Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go folder sharing.
+
+        """
+        self._x2go_pause_rev_fw_tunnel('sshfs')
+
+    def start_printing(self):
+        """\
+        Initialize X2go print spooling.
+
+        """
+        if self.session_info.username not in self.control_session._x2go_remote_group('x2goprint'):
+            raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group x2goprint' % self.session_info.username)
+
+        spool_dir = os.path.join(self.session_info.local_container, 'spool')
+        if not os.path.exists(spool_dir):
+            os.mkdir(spool_dir)
+        self.share_local_folder(folder_name=spool_dir, folder_type='spool')
+        self.print_queue = printing.X2goPrintQueue(spool_dir=spool_dir,
+                                                   print_action=self.print_action, 
+                                                   print_action_args=self.print_action_args, 
+                                                   logger=self.logger,
+                                                  )
+        self.print_queue.start()
+        self.guardian_thread.active_threads.append(self.print_queue)
+
+    def set_print_action(self, print_action, **kwargs):
+        """\
+        STILL UNDOCUMENTED
+
+        """
+        self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
+
+
+    def stop_printing(self):
+        """\
+        Shutdown (pause) the X2go Print Queue thread.
+
+        """
+        if self.print_queue is not None:
+            self.print_queue.pause()
+
+    def share_local_folder(self, folder_name=None, folder_type='disk'):
+        """\
+        Share a local folder with the X2go session.
+
+        @param folder_name: the full path to an existing folder on the local 
+            file system
+        @type folder_name: str
+        @param folder_type: one of 'disk' (a folder on your local hard drive), 'rm' (removeable device), 
+            'cdrom' (CD/DVD Rom) or 'spool' (for X2go print spooling)
+        @type folder_type: str
+
+        @return: returns C{True} if the local folder has been successfully mounted within the X2go server session
+        @rtype: bool
+
+        """
+        if self.session_info.username not in self.control_session._x2go_remote_group('fuse'):
+            raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group fuse' % self.session_info.username)
+
+        if folder_name is None:
+            self.logger('no folder name given...', log.loglevel_WARN)
+            return False
+
+        if type(folder_name) is not types.StringType:
+            self.logger('folder name needs to be of type StringType...', log.loglevel_WARN)
+            return False
+
+        if not os.path.exists(folder_name):
+            self.logger('local folder does not exist: %s' % folder_name, log.loglevel_WARN)
+            return False
+
+        self.logger('sharing local folder: %s' % folder_name, log.loglevel_INFO)
+
+        _auth_rsakey = self.control_session._x2go_session_auth_rsakey
+        _host_rsakey = defaults.RSAHostKey
+
+        _tmp_io_object = cStringIO.StringIO()
+        _auth_rsakey.write_private_key(_tmp_io_object)
+        _tmp_io_object.write('----BEGIN RSA IDENTITY----')
+        _tmp_io_object.write('%s %s' % (_host_rsakey.get_name(),_host_rsakey.get_base64(),))
+
+        _x2go_key_fname = os.path.join(os.path.dirname(self.session_info.remote_container), 'ssh', 'key.z%s' % self.session_info.agent_pid)
+        _x2go_key_bundle = _tmp_io_object.getvalue()
+
+        self.control_session._x2go_sftp_write(_x2go_key_fname, _x2go_key_bundle)
+
+        if folder_type is 'disk':
+
+            cmd_line = [ 'export HOSTNAME &&',
+                         'x2gomountdirs', 
+                         'dir',
+                         str(self.session_info.name), 
+                         _CURRENT_LOCAL_USER,
+                         _x2go_key_fname,
+                         '%s__REVERSESSH_PORT__%s; ' % (folder_name, self.session_info.sshfs_port),
+                         'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
+                       ]
+
+        elif folder_type is 'spool':
+
+            cmd_line = [ 'export HOSTNAME &&',
+                         'x2gomountdirs', 
+                         'dir',
+                         str(self.session_info.name), 
+                         _CURRENT_LOCAL_USER,
+                         _x2go_key_fname,
+                         '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (folder_name, self.session_info.sshfs_port),
+                         'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
+                       ]
+
+        (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
+        self.logger('x2gomountdirs output is : %s' % stdout.read().split('\n'), log.loglevel_INFO)
+
+    def run_command(self, cmd=None):
+        """\
+        Run a command in this session.
+
+        After L{X2goSession.start()} has been called 
+        one or more commands can be executed with L{X2goSession.run_command()}
+        within the current X2go session.
+
+        @param cmd: Command to be run
+        @type cmd: str
+
+        @return: stdout.read() and stderr.read() as returned by the run command
+            on the X2go server
+        @rtype: tuple of str
+
+        """
+        if cmd in ("", None):
+            if self.params.cmd is None:
+                cmd = 'TERMINAL'
+            else:
+                cmd = self.params.cmd
+
+        self.params.update({'cmd': cmd})
+
+        cmd_line = [ "setsid x2goruncommand", 
+                     str(self.session_info.display),
+                     str(self.session_info.agent_pid),
+                     str(self.session_info.name), 
+                     str(self.session_info.snd_port),
+                     _rewrite_cmd(self.params.cmd),
+                     str(self.params.snd_system),
+                     str(self.params.session_type),
+                     ">& /dev/null & exit",
+                   ]
+
+        if self.params.snd_system is 'pulse':
+            cmd_line = [ 'PULSE_CLIENTCONFIG=%s/.pulse-client.conf' % self.session_info.remote_container ] + cmd_line
+
+        (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
+
+        return stdout.read(), stderr.read()
+
+    def ok(self):
+        """\
+        Returns C{True} if this X2go session is up and running, 
+        C{False} else
+
+        @return: X2go session OK?
+        @rtype: bool
+
+        """
+        return bool(self.session_info.name and (self.proxy_subprocess and self.proxy_subprocess.poll() is None))
+
+    def is_running(self):
+        """\
+        Returns C{True} if this X2go session is in running state,
+        C{False} else.
+
+        @return: X2go session running?
+        @rtype: bool
+
+        """
+        return self.session_info.is_running()
+
+    def is_suspended(self):
+        """\
+        Returns C{True} if this X2go session is in suspended state,
+        C{False} else.
+
+        @return: X2go session suspended?
+        @rtype: bool
+
+        """
+        return self.session_info.is_suspended()
+
+    def start(self):
+        """\
+        Start a new X2go session.
+
+        The L{X2goTerminalSession.start()} method accepts any parameter
+        that can be passed to the class constructor.
+
+        """
+        setkbd = "0"
+        if self.params.kblayout or self.params.kbtype:
+            setkbd = "1"
+
+        cmd_line = [ "x2gostartagent",
+                     str(self.params.geometry),
+                     str(self.params.link),
+                     str(self.params.pack),
+                     str(self.params.cache_type+'-depth_'+self.params.depth),
+                     str(self.params.kblayout),
+                     str(self.params.kbtype),
+                     str(setkbd),
+                     str(self.params.session_type),
+                     self.params.cmd,
+                   ]
+
+        (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
+
+        self.session_info.initialize(stdout.read(),
+                                     username=self.control_session.get_transport().get_username(),
+                                     hostname=self.control_session.get_transport().getpeername(),
+                                    )
+
+        # local path may be a Windows path, so we use the path separator of the local system
+        self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
+        # remote path is always a UniX path...
+        self.session_info.remote_container = '%s/%s/C-%s' % (self.control_session._x2go_remote_home,
+                                                             _X2GO_SESSION_ROOTDIR,
+                                                             self.session_info.name,
+                                                            )
+
+        # set up SSH tunnel for X11 graphical elements
+        self.proxy = self.proxy_class(session_info=self.session_info, ssh_transport=self.control_session.get_transport(), logger=self.logger)
+        self.proxy_subprocess = self.proxy.start_proxy()
+        self.guardian_thread.active_threads.append(self.proxy)
+
+        self.associated = True
+        return self.ok()
+
+    def resume(self):
+        """\
+        Resume a running/suspended X2go session. 
+
+        The L{X2goSession.resume()} method accepts any parameter
+        that can be passed to the class constructor.
+
+        @return: True if the session could be successfully resumed
+        @rtype: bool
+
+        """
+        setkbd = "0"
+        if self.params.kblayout or self.params.kbtype:
+            setkbd = "1"
+
+        cmd_line = [ "x2goresume-session", self.session_info.name,
+                     self.params.geometry,
+                     self.params.link,
+                     self.params.pack,
+                     self.params.kblayout,
+                     self.params.kbtype,
+                     setkbd,
+                   ]
+
+        (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
+
+        self.proxy = self.proxy_class(self.session_info, self.control_session.get_transport(), logger=self.logger)
+        self.proxy_subprocess = self.proxy.start_proxy()
+
+        # local path may be a Windows path, so we use the path separator of the local system
+        self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
+        # remote path is always a UniX path...
+        self.session_info.remote_container = '%s/%s/C-%s' % (self.control_session._x2go_remote_home, 
+                                                             _X2GO_SESSION_ROOTDIR,
+                                                             self.session_info.name,
+                                                            )
+        return self.ok()
+
+    def suspend(self):
+        """\
+        Suspend this X2go session terminal.
+
+        @return: True if the session terminal could be successfully suspended
+        @rtype: bool
+
+        """
+        self.logger('suspending associated session: %s' % self.session_info, log.loglevel_DEBUG)
+        (stdin, stdout, stderr) = self.control_session._x2go_exec_command("x2gosuspend-session %s" % self.session_info, loglevel=log.loglevel_DEBUG)
+        dummy_stdout = stdout.read()
+        dummy_stderr = stderr.read()
+        self.associated = False
+        self._x2go_tidy_up()
+        # TODO: check if session has really suspended
+        _ret = True
+
+        return _ret
+
+    def terminate(self, session_name=None):
+        """\
+        Terminate this X2go session.
+
+        @return: True if the session terminal could be successfully terminate
+        @rtype: bool
+
+        """
+        self.logger('terminating associated session: %s' % self.session_info, log.loglevel_INFO)
+        (stdin, stdout, stderr) = self.control_session._x2go_exec_command("x2goterminate-session %s" % self.session_info, loglevel=log.loglevel_DEBUG)
+        dummy_stdout = stdout.read()
+        dummy_stderr = stderr.read()
+        self.session_info.clear()
+        self.associated = False
+        self._x2go_tidy_up()
+        # TODO: check if session has really suspended
+        _ret = True
+
+        return _ret
diff --git a/x2go/client.py b/x2go/client.py
index 9f2a18d..74c813d 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -124,9 +124,7 @@ import sys
 # Python X2go modules
 from settings import X2goClientSettings
 from printing import X2goClientPrinting
-from profiles import X2goSessionProfiles
 from registry import X2goSessionRegistry
-from session import X2goSession, _X2GO_SESSION_OPTIONS
 import log
 import utils
 
@@ -135,6 +133,8 @@ from defaults import LOCAL_HOME as _LOCAL_HOME
 from defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
 from defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
 
+from x2go.backends.profiles import X2goSessionProfiles
+
 class X2goClient(object):
     """\
     The X2goClient implements _THE_ public Python X2go API. With it you can
@@ -278,20 +278,22 @@ class X2goClient(object):
                 if k in kwargs.keys():
                     _params[k] = kwargs[k]
 
+            server = _params['server']
+            del _params['server']
+
         else:
             if server is None:
                 return None
             _profile_id = utils._genSessionProfileId()
             _profile_name = profile_name or sys.argv[0]
             _params = kwargs
-            _params['server'] = server
             _params['printing'] = printing
             _params['share_local_folders'] = share_local_folders
 
-        session_uuid = self.session_registry.register(_profile_id, _profile_name, **_params )
+        session_uuid = self.session_registry.register(server=server, profile_id=_profile_id, profile_name=_profile_name, **_params )
 
-        connect_options = self.session_registry(session_uuid).connect_options
-        session_options = self.session_registry(session_uuid).session_options
+        control_params = self.session_registry(session_uuid).control_params
+        terminal_params = self.session_registry(session_uuid).terminal_params
 
         self.logger('initializing X2go session...', log.loglevel_NOTICE, tag=self._logger_tag)
         if return_object:
@@ -356,7 +358,7 @@ class X2goClient(object):
         @rtype: obj
 
         """
-        return self.session_registry(session_uuid).session_object
+        return self.session_registry(session_uuid)
     __get_session = get_session
     with_session = __get_session
     """Alias for L{get_session()}."""
@@ -548,7 +550,7 @@ class X2goClient(object):
             for session in self.session_registry.running_sessions:
                 if session_name == session.get_session_name():
                     return session.suspend()
-        return self.session_registry(session_uuid).session_object.suspend(session_name=session_name)
+        return self.session_registry(session_uuid).suspend(session_name=session_name)
     __suspend_session = suspend_session
 
     def terminate_session(self, session_uuid, session_name=None):
@@ -587,7 +589,7 @@ class X2goClient(object):
             for session in self.session_registry.running_sessions + self.session_registry.suspended_sessions:
                 if session_name == session.get_session_name():
                     return session.terminate()
-        return self.session_registry(session_uuid).session_object.terminate(session_name=session_name)
+        return self.session_registry(session_uuid).terminate(session_name=session_name)
     __terminate_session = terminate_session
 
     def get_session_profile_name(self, session_uuid):
@@ -887,10 +889,8 @@ class X2goClient(object):
         @type session_uuid: C{str}
 
         """
-        session = self.session_registry(session_uuid).session_object
-        session_infos = session.list_sessions()
-        for session_info in session_infos.values():
-            session.terminate(session_name=session_info)
+        session = self.session_registry(session_uuid)
+        session.clean_sessions()
     __clean_sessions = clean_sessions
 
     def list_sessions(self, session_uuid):
@@ -908,7 +908,7 @@ class X2goClient(object):
         @type session_uuid: C{str}
 
         """
-        session = self.session_registry(session_uuid).session_object
+        session = self.session_registry(session_uuid)
         return session.list_sessions()
     __list_sessions = list_sessions
 
diff --git a/x2go/defaults.py b/x2go/defaults.py
index 5df419c..a75e059 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -59,6 +59,23 @@ else:
 
 
 ##
+## control and terminal session backend as well as session info backend defaults
+##
+
+DEFAULT_CONTROLSESSION_BACKEND =  'X2goControlSessionSTDOUT'
+DEFAULT_TERMINALSESSION_BACKEND = 'X2goTerminalSessionSTDOUT'
+DEFAULT_SERVERSESSIONINFO_BACKEND = 'X2goServerSessionInfoSTDOUT'
+DEFAULT_SERVERSESSIONLIST_BACKEND = 'X2goServerSessionListSTDOUT'
+DEFAULT_PROXY_BACKEND = 'X2goProxyNX3'
+
+##
+## profile backend defaults
+##
+
+DEFAULT_SESSIONPROFILES_BACKEND = 'X2goSessionProfilesFILE'
+
+
+##
 ## X2go Printing
 ##
 
@@ -214,13 +231,6 @@ _pack_methods_nx3 = [ m for m in pack_methods_nx3 if "%" not in m ]
 for meth in [ m for m in pack_methods_nx3 if "%" in m ]:
     _pack_methods_nx3 += [ meth.replace('%','%s' % str(i)) for i in range(0,10) ]
 
-
-##
-## THESE ARE NOT NEEDED!!!! THERE IS A METHOD IN utils.py that does the job...
-##
-X2GO_INIPARMS_TO_SESSION_PARMS = (('soundsystem','snd_system'), ('command','cmd'),('host','server'),('user', 'username'),
-                        ('key', 'key_filename'),('layout','kblayout'),('type','kbtype'), ('sshport', 'port'))
-
 ##
 ## X2go session defaults
 ##
@@ -239,16 +249,11 @@ window_managers={
 RSAKEY_STRENGTH = 1024
 RSAHostKey = paramiko.RSAKey.generate(RSAKEY_STRENGTH)
 
-from printing import X2goPrintActionPDFVIEW
-from printing import X2goPrintActionPDFSAVE
-from printing import X2goPrintActionPRINT
-from printing import X2goPrintActionPRINTCMD
-
 X2GO_PRINT_ACTIONS = {
-    'PDFVIEW': X2goPrintActionPDFVIEW,
-    'PDFSAVE': X2goPrintActionPDFSAVE,
-    'PRINT': X2goPrintActionPRINT,
-    'PRINTCMD': X2goPrintActionPRINTCMD,
+    'PDFVIEW': 'X2goPrintActionPDFVIEW',
+    'PDFSAVE': 'X2goPrintActionPDFSAVE',
+    'PRINT': 'X2goPrintActionPRINT',
+    'PRINTCMD': 'X2goPrintActionPRINTCMD',
 }
 """Relating print action names and classes."""
 
diff --git a/x2go/printing.py b/x2go/printing.py
index 3091975..41bc685 100644
--- a/x2go/printing.py
+++ b/x2go/printing.py
@@ -531,7 +531,7 @@ class X2goPrintQueue(threading.Thread):
             print_action = defaults.X2GO_PRINT_ACTIONS[print_action]
 
         if print_action in defaults.X2GO_PRINT_ACTIONS.values():
-            self.print_action = print_action(**kwargs)
+            self.print_action = eval ('%s(**kwargs)' % print_action)
 
     def run(self):
         """\
diff --git a/x2go/registry.py b/x2go/registry.py
index 79c5da9..89727cf 100644
--- a/x2go/registry.py
+++ b/x2go/registry.py
@@ -29,351 +29,17 @@ import time
 import threading
 
 # Python X2go modules
-import profiles
 import log
 import utils
 import session
 from x2go_exceptions import *
 
-
-class X2goRegisteredSession():
-
-    def __init__(self, logger=None, loglevel=log.loglevel_DEFAULT):
-
-        if logger is None:
-            self.logger = log.X2goLogger(loglevel=loglevel)
-        else:
-            self.logger = copy.deepcopy(logger)
-        self.logger.tag = __NAME__
-
-        self._keep_alive = True
-
-        self.uuid = uuid.uuid1()
-        self.connected = False
-        self.running = False
-        self.suspended = False
-        self.terminated = False
-        self.logger('starting threaded X2goRegisteredSession', loglevel=log.loglevel_DEBUG)
-
-    def __str__(self):
-        return self.__get_uuid()
-    def __repr__(self):
-        result = 'X2goRegisteredSession('
-        for p in dir(self):
-            if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
-            result += p + '=' + str(self.__dict__[p]) + ', '
-        return result + ')'
-    def __call__(self):
-        return self.__get_uuid()
-
-    def get_uuid(self):
-        """\
-        STILL UNDOCUMENTED
-
-        """
-        return str(self.uuid)
-    __get_uuid = get_uuid
-
-    def get_username(self):
-        """\
-        After a session has been setup up you can query the
-        username the sessions runs as.
-
-        @return: the remote username the X2go session runs as
-        @rtype: C{str}
-
-        """
-        return self.session_object.get_transport().get_username()
-    __get_username = get_username
-
-    def get_password(self):
-        """\
-        After a session has been setup up you can query the
-        username's password from the session.
-
-        @return: the username's password
-        @rtype: C{str}
-
-        """
-        return self.session_object._session_password
-
-    def get_server(self):
-        """\
-        After a session has been setup up you can query the
-        hostname of the host the sessions is connected to (or
-        about to connect to).
-
-        @return: the hostname of the server the X2go session is
-            connected to (as an C{(addr,port)} tuple)
-        @rtype: tuple
-
-        """
-        return self.session_object.get_transport().getpeername()
-    __get_server = get_server
-
-    def get_session_name(self):
-        """\
-        Retrieve the server-side X2go session name for the session that has
-        been registered under C{profile_id}.
-
-        @return: X2go session name
-        @rtype: C{str}
-
-        """
-        return str(self.session_object.session_info) or None
-    __get_session_name = get_session_name
-
-    def connect(self, username='', password='', add_to_known_hosts=False, force_password_auth=False):
-        """\
-        Connect to a registered X2go session with registry hash C{<session_uuid>}.
-        This method basically wraps around paramiko.SSHClient.connect() for the
-        corresponding session.
-
-        @param username: the username for the X2go server that is going to be
-            connected to (as a last minute way of changing the session username)
-        @type username: C{str}
-        @param password: the user's password for the X2go server that is going to be
-            connected to
-        @type password: C{str}
-        @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
-            is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
-            is used
-        @type add_to_known_hosts: C{bool}
-        @param force_password_auth: disable SSH pub/priv key authentication mechanisms
-            completely
-        @type force_password_auth: C{bool}
-
-        """
-        # do connect
-        connect_options = self.connect_options
-        connect_options['password'] = password
-        if username:
-            connect_options['username'] = username
-        connect_options['force_password_auth'] = force_password_auth
-        self.connected = self.session_object.connect(self.server, **connect_options)
-        return self.connected
-    __connect = connect
-
-    def disconnect(self):
-        """\
-        STILL UNDOCUMENTED
-
-        """
-        self.session_object.disconnect()
-        self.connected = False
-        self.running = False
-        self.suspended = False
-        self.terminated = False
-    __disconnect = disconnect
-
-    def set_print_action(self, print_action, **kwargs):
-        """\
-        STILL UNDOCUMENTED
-
-        """
-        if type(print_action) is not types.StringType:
-            return False
-        self.session_object.set_print_action(print_action, **kwargs)
-    __set_print_action = set_print_action
-
-    def start(self):
-        """\
-        Start a new X2go session on the remote X2go server.
-
-        """
-        session = self.session_object
-        if session.start():
-
-            if session.params.snd_system is not 'none':
-                session.start_sound()
-
-            session.start_sshfs()
-            if self.printing:
-                session.start_printing()
-
-            if self.share_local_folders:
-                if session.get_transport().reverse_tunnels['sshfs'][1] is not None:
-                    for _folder in self.share_local_folders:
-                        session.share_local_folder(_folder)
-
-            session.run_command()
-            self.suspended = False
-            self.running = True
-            self.terminated = False
-
-        return self.running
-    __start = start
-
-    def resume(self, session_name):
-        """\
-        Resume or continue a suspended / running X2go session on the
-        remote X2go server.
-
-        @param session_name: the server-side name of an X2go session
-        @type session_name: C{str}
-
-        """
-        self.session_object.associate(session_name)
-        if self.session_object.resume():
-
-            if self.session_object.params.snd_system is not 'none':
-                self.session_object.start_sound()
-
-            self.session_object.start_sshfs()
-            if self.printing:
-                self.session_object.start_printing()
-
-            self.suspended = False
-            self.running = True
-            self.terminated = False
-            return True
-
-        return False
-    __resume = resume
-
-    def suspend(self):
-        """\
-        Suspend an X2go session.
-
-        You can either suspend a session that you have formerly
-        started/resumed the current X2goClient instance.
-
-        Or you can suspend a non-attached session by simply
-        registering an X2go server session and then passing the
-        server-side X2go session name to this method.
-
-        """
-        if self.session_object.suspend():
-
-            self.running = False
-            self.suspended = True
-            return True
-
-        return False
-    __suspend = suspend
-
-    def terminate(self):
-        """\
-        Terminate an X2go session.
-
-        You can either terminate a session that you have formerly
-        started/resumed within the current X2goClient instance.
-
-        Or you can terminate a non-attached session by simply
-        registering an X2go server session and then passing the
-        server-side X2go session name to this method.
-
-        """
-        if self.session_object.terminate():
-
-            self.running = False
-            self.suspended = False
-            self.terminated = True
-            return True
-
-        return False
-    __terminate = terminate
-
-    def get_profile_name(self):
-        """\
-        Retrieve the profile name of this registered session.
-
-        @return: X2go client profile name of the session
-        @rtype: C{str}
-
-        """
-        return self.profile_name
-    __get_profile_name = get_profile_name
-
-    def get_profile_id(self):
-        """\
-        Retrieve this registered session's profile id.
-
-        @return: the session profile's id
-        @rtype: C{str}
-
-        """
-        return self.profile_id
-    __get_profile_id = get_profile_id
-
-    ###
-    ### QUERYING INFORMATION
-    ###
-
-    def session_ok(self):
-        """\
-        Test if this registered X2go session is
-        in a healthy state.
-
-        @return: C{True} if session is ok, C{False} otherwise
-        @rtype: C{bool}
-
-        """
-        return self.session_object.ok()
-    __session_ok = session_ok
-
-
-    def is_connected(self):
-        """\
-        Test if this registered X2go session is connected to the 
-        remote server.
-
-        @return: C{True} if session is connected, C{False} otherwise
-        @rtype: C{bool}
-
-        """
-        return self.session_object.is_connected()
-    _is_connected = is_connected
-
-    def is_running(self):
-        """\
-        Test if this registered X2go session is up and running.
-
-        @return: C{True} if session is running, C{False} otherwise
-        @rtype: C{bool}
-
-        """
-        return self.is_connected() and self.session_object.is_running()
-    _is_running = is_running
-
-    def is_suspended(self):
-        """\
-        Test if this registered X2go session is in suspended state.
-
-        @return: C{True} if session is suspended, C{False} otherwise
-        @rtype: C{bool}
-
-        """
-        return self.is_connected() and self.session_object.is_suspended()
-    __is_suspended = is_suspended
-
-    def has_terminated(self):
-        """\
-        Test if this registered X2go session has terminated.
-
-        @return: C{True} if session has terminated, C{False} otherwise
-        @rtype: C{bool}
-
-        """
-        return self.is_connected() and self.session_object.has_terminated()
-    __has_terminated = has_terminated
-
-    def share_local_folder(self, folder_name):
-        """\
-        Share a local folder with this registered X2go session.
-
-        @param folder_name: the full path to an existing folder on the local
-            file system
-        @type folder_name: C{str}
-
-        @return: returns C{True} if the local folder has been successfully mounted within 
-            this registered X2go server session
-        @rtype: C{bool}
-
-        """
-        return self.session_object.share_local_folder(folder_name=folder_name)
-    __share_local_folder = share_local_folder
-
+# import the default terminal session backend
+from x2go.backends.control import X2goControlSession
+from x2go.backends.terminal import X2goTerminalSession
+from x2go.backends.proxy import X2goProxy
+from x2go.backends.info import X2goServerSessionInfo
+from x2go.backends.info import X2goServerSessionList
 
 class X2goSessionRegistry(object):
     """\
@@ -392,6 +58,7 @@ class X2goSessionRegistry(object):
         self.logger.tag = __NAME__
 
         self.registry = {}
+        self.control_sessions = {}
 
     def __repr__(self):
         result = 'X2goSessionRegistry('
@@ -421,53 +88,39 @@ class X2goSessionRegistry(object):
         """
         return self(session_uuid).profile.profile_name
 
-    def register(self, profile_id, profile_name, **kwargs):
-
-        _r = X2goRegisteredSession(logger=self.logger)
-        session_uuid = _r()
-        self.registry[session_uuid] = _r
-        self(session_uuid).profile_id = profile_id
-        self(session_uuid).profile_name = profile_name
-        self(session_uuid).session_params = kwargs
-        self(session_uuid).server = kwargs['server']
-        del kwargs['server']
-        self(session_uuid).printing = kwargs['printing']
-        del kwargs['printing']
-        self(session_uuid).share_local_folders = kwargs['share_local_folders']
-        del kwargs['share_local_folders']
-        self(session_uuid).session_params = kwargs
-        # differentiate SSH options from X2go options
-        _session_options = copy.deepcopy(kwargs)
-        _connect_options = copy.deepcopy(kwargs)
-
-        for k in kwargs.keys():
-            if k in session._X2GO_SESSION_OPTIONS:
-                del _connect_options[k]
-            else:
-                del _session_options[k]
-
+    def register(self, server, profile_id, profile_name, 
+                 control_backend=X2goControlSession,
+                 terminal_backend=X2goTerminalSession,
+                 info_backend=X2goServerSessionInfo,
+                 list_backend=X2goServerSessionList,
+                 proxy_backend=X2goProxy,
+                 **kwargs):
+
+        control_session = None
+        if profile_id in self.control_sessions.keys():
+            control_session = control_sessions[profile_id]
+
+        s = session.X2goSession(server=server, control_session=control_session,
+                                profile_id=profile_id, profile_name=profile_name, 
+                                control_backend=control_backend,
+                                terminal_backend=terminal_backend,
+                                info_backend=info_backend,
+                                list_backend=list_backend,
+                                proxy_backend=proxy_backend,
+                                logger=self.logger, **kwargs)
+
+        session_uuid = s._X2goSession__get_uuid()
         self.logger('registering X2go session %s...' % profile_name, log.loglevel_NOTICE)
         self.logger('registering X2go session with UUID %s' % session_uuid, log.loglevel_DEBUG)
-        self.logger('X2go session options for profile %s:' % profile_name, log.loglevel_DEBUG)
-        for k in _session_options:
-            self.logger('    %s: %s' % (k, _session_options[k]), log.loglevel_DEBUG)
-
-        self.logger('Paramiko connect options for profile %s are:' % profile_name, log.loglevel_DEBUG)
-        for k in _connect_options:
-            self.logger('    %s: %s' % (k,_connect_options[k]), log.loglevel_DEBUG)
-
-        self(session_uuid).session_object = session.X2goSession(logger=self.logger, **_session_options)
-        self(session_uuid).connected = False
-        self(session_uuid).running = False
-        self(session_uuid).suspended = False
-        self(session_uuid).terminated = False
-        self(session_uuid).connect_options = _connect_options
-        self(session_uuid).session_options = _session_options
+
+        self.registry[session_uuid] = s
+        if profile_id not in self.control_sessions.keys():
+            self.control_sessions[profile_id] = s.get_control_session()
 
         return session_uuid
 
     def _sessionsWithState(self, state):
-        return [ session for session in self.registry.values() if eval('session.%s' % state) ]
+        return [ ts for ts in self.registry.values() if eval('ts.%s' % state) ]
 
     @property
     def connected_sessions(self):
diff --git a/x2go/rforward.py b/x2go/rforward.py
index 8161338..9c9ee19 100644
--- a/x2go/rforward.py
+++ b/x2go/rforward.py
@@ -57,13 +57,16 @@ def x2go_transport_tcp_handler(chan, (origin_addr, origin_port), (server_addr, s
     transport = chan.get_transport()
     transport._queue_incoming_channel(chan)
     rev_tuns = transport.reverse_tunnels
-    if int(server_port) in [ int(tunnel[0]) for tunnel in rev_tuns.values() ]:
 
-        if rev_tuns['snd'] is not None and int(server_port) == int(rev_tuns['snd'][0]):
-            rev_tuns['snd'][1].notify()
+    for session_name in rev_tuns.keys():
 
-        elif rev_tuns['sshfs'] is not None and int(server_port) == int(rev_tuns['sshfs'][0]):
-            rev_tuns['sshfs'][1].notify()
+        if int(server_port) in [ int(tunnel[0]) for tunnel in rev_tuns[session_name].values() ]:
+
+            if rev_tuns[session_name]['snd'] is not None and int(server_port) == int(rev_tuns[session_name]['snd'][0]):
+                rev_tuns[session_name]['snd'][1].notify()
+
+            elif rev_tuns[session_name]['sshfs'] is not None and int(server_port) == int(rev_tuns[session_name]['sshfs'][0]):
+                rev_tuns[session_name]['sshfs'][1].notify()
 
 
 class X2goRevFwTunnel(threading.Thread):
@@ -201,6 +204,7 @@ class X2goRevFwTunnel(threading.Thread):
 
             self.logger('waiting for incoming data channel on X2go server port: [localhost]:%s' % self.server_port, loglevel=log.loglevel_DEBUG)
             self.incoming_channel.wait()
+
             if self._keepalive:
                 self.logger('detected incoming data channel on X2go server port: [localhost]:%s' % self.server_port, loglevel=log.loglevel_DEBUG)
                 _chan = self.ssh_transport.accept()
diff --git a/x2go/session.py b/x2go/session.py
index dbe7ab6..77c2dfa 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -18,1143 +18,443 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """\
-X2goSession class - core functions for handling your individual X2go sessions.
-
+X2goSession class - the X2goClient's session backend
 """
 __NAME__ = 'x2gosession-pylib'
 
-# modules
-import os, sys, types
-import paramiko
-import gevent
-import threading
-import signal
-import cStringIO
 import copy
+import types
+import uuid
+import time
+import threading
 
 # Python X2go modules
-import proxy
-import rforward
-import sftpserver
-import printing
 import log
-import defaults
 import utils
-import x2go_exceptions
-import guardian
+import session
+from x2go_exceptions import *
 
-from cleanup import x2go_cleanup 
-
-# for debugging code
-import pprint
-
-# we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables)
-from defaults import LOCAL_HOME as _LOCAL_HOME
-from defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
-from defaults import X2GO_SESSION_ROOTDIR as _X2GO_SESSION_ROOTDIR
+from x2go.backends.control import X2goControlSession
+from x2go.backends.terminal import X2goTerminalSession
+from x2go.backends.info import X2goServerSessionInfo
+from x2go.backends.info import X2goServerSessionList
+from x2go.backends.proxy import X2goProxy
+from x2go.backends.profiles import X2goSessionProfiles
 
 # options of the paramiko.SSHClient().connect()
-_X2GO_SESSION_OPTIONS = ('geometry', 'depth', 'link', 'pack',
-                         'cache_type', 'kblayout', 'kbtype',
-                         'session_type', 'snd_system', 'cmd',
-                         'rootdir', 'loglevel', 'profile_name', 'profile_id',
-                         'print_action', 'print_action_args',
-                         'proxy_class', 'logger',
+_X2GO_SESSION_PARAMS = ('geometry', 'depth', 'link', 'pack',
+                        'cache_type', 'kblayout', 'kbtype',
+                        'session_type', 'snd_system', 'cmd',
+                        'rootdir', 'loglevel', 'profile_name', 'profile_id',
+                        'print_action', 'print_action_args',
+                        'proxy_class', 'logger',
                        )
 
+class X2goSession(object):
 
-def _rewrite_cmd(cmd):
-
-    # start with an empty string
-    cmd = cmd or ''
-
-    # find window manager commands
-    if cmd in defaults.window_managers.keys():
-        cmd = defaults.window_managers[cmd]
-
-    # X2go run command replace X2GO_SPACE_CHAR string with blanks
-    cmd.replace(" ", "X2GO_SPACE_CHAR")
-
-    # place quot marks around cmd if not empty string
-    if cmd:
-        cmd = '"%s"' % cmd
-    return cmd
-
-
-class X2goSessionParams(object):
-    """\
-    The L{X2goSessionParams} class is used to store all parameters that
-    L{X2goSession} objects are constructed with.
-
-    """
-    def rewrite_session_type(self):
-        """\
-        Rewrite the X2go session type, so that the X2go server
-        can understand it (C{desktop} -> C{D}).
-
-        Also if the object's C{command} property is a known window 
-        manager, the session type will be set to 'D' 
-        (i.e. desktop).
-
-        @return: 'D' if session should probably a desktop session,
-            'R' (for rootless) else
-        @rtype: str
-
-        """
-        session_type = self.session_type
-        cmd = self.cmd
-        if session_type == "desktop":
-            self.session_type = 'D'
-            return
-        if cmd:
-            if cmd in defaults.window_managers.keys():
-                self.session_type = 'D'
-                return
-            if os.path.basename(cmd) in defaults.window_managers.values():
-                self.session_type = 'D'
-                return
-        self.session_type = 'R'
-
-    def update(self, properties_to_be_updated={}):
-        """\
-        Update all properties in the object L{X2goSessionParams} object from
-        the passed on dictionary.
-
-        @param properties_to_be_updated: a dictionary with L{X2goSessionParams}
-            property names as keys und their values to be update in 
-            L{X2goSessionParams} object.
-        @type properties_to_be_updated: dict
-
-        """
-        for key in properties_to_be_updated.keys():
-            setattr(self, key, properties_to_be_updated[key] or '')
-        self.rewrite_session_type()
-
-
-class X2goServerSessionInfo(object):
-    """\
-    L{X2goServerSessionInfo} is used to store all information
-    that is retrieved from the connected X2go server on 
-    L{X2goSession.start()} resp. L{X2goSession.resume()}.
-
-    """
-    def __str__(self):
-        return self.name
-    def __repr__(self):
-        return "<%s instance: %s>" % (self.__class__, self.name)
-
-    def _parse_x2golistsessions_line(self, x2go_output):
-        """\
-        Parse a single line of X2go's listsessions output.
-
-        """
-        l = x2go_output.split("|")
-        self.name = l[1]
-        self.cookie = l[6]
-        self.agent_pid = int(l[0])
-        self.display = int(l[2])
-        self.status = l[4]
-        self.graphics_port = int(l[8])
-        self.snd_port = int(l[9])
-        self.sshfs_port = int(l[13])
-        self.username = l[11]
-        self.hostname = l[3]
-        # TODO: turn into datetime object
-        self.date_created = l[5]
-        # TODO: turn into datetime object
-        self.date_suspended = l[10]
-        self.local_container = ''
-
-    def _parse_x2gostartagent_output(self, x2go_output):
-        """\
-        Parse x2gostartagent output.
-
-        """
-        l = x2go_output.split("\n")
-        self.name = l[3]
-        self.cookie = l[1]
-        self.agent_pid = int(l[2])
-        self.display = int(l[0])
-        self.graphics_port = int(l[4])
-        self.snd_port = int(l[5])
-        self.sshfs_port = int(l[6])
-        self.username = ''
-        self.hostname = ''
-        # TODO: we have to see how we fill these fields here...
-        self.date_created = ''
-        self.date_suspended = ''
-        # TODO: presume session is running after x2gostartagent, this could be better
-        self.status = 'R'
-        self.local_container = ''
-        self.remote_container = ''
-
-    def initialize(self, x2go_output, username='', hostname='', local_container='', remote_container=''):
-        """\
-        Parse X2go server's C{x2gostartagent} stdout values.
-
-        @param x2go_output: X2go server's C{x2gostartagent} command output, each value 
-            separated by a newline character.
-        @type x2go_output: str
-        @param username: session user name
-        @type username: str
-        @param hostname: hostname of X2go server
-        @type hostname: str
-        @param local_container: X2go client session directory for config files, cache and session logs
-        @type local_container: str
-        @param remote_container: X2go server session directory for config files, cache and session logs
-        @type remote_container: str
-
-        """
-        self._parse_x2gostartagent_output(x2go_output)
-        self.username = username
-        self.hostname = hostname
-        self.local_container = local_container
-        self.remote_container = remote_container
-
-    def clear(self):
-        """\
-        Clear all properties of a L{X2goServerSessionInfo} object.
-
-        """
-        self.name = ''
-        self.cookie = ''
-        self.agent_pid = ''
-        self.display = ''
-        self.graphics_port = ''
-        self.snd_port = ''
-        self.sshfs_port = ''
-        self.username = ''
-        self.hostname = ''
-        self.date_created = ''
-        self.date_suspended = ''
-        self.status = ''
-        self.local_container = ''
-        self.remote_container = ''
-
-    __init__ = clear
-
-
-class X2goServerSessionList(object):
-    """\
-    L{X2goServerSessionList} is used to store all information
-    that is retrieved from a connected X2go server on a
-    L{X2goSession.list_sessions()} call.
-
-    """
-    def __init__(self, x2go_output):
-        """\
-        @param x2go_output: X2go server's C{x2golistsessions} command output, each 
-            session separated by a newline character. Session values are separated 
-            by Unix Pipe Symbols ('|')
-        @type x2go_output: str
-
-        """
-        self.sessions = {}
-        lines = x2go_output.split("\n")
-        for line in lines:
-            if not line:
-                continue
-            s_info = X2goServerSessionInfo()
-            s_info._parse_x2golistsessions_line(line)
-            self.sessions[s_info.name] = s_info
-
-
-class X2goSession(paramiko.SSHClient):
-    """\
-    Class for managing X2go sessions on a remote X2go server via Paramiko/SSH. 
-    With the X2goSession class you can start new X2go sessions, resume suspended 
-    sessions or suspend resp. terminate currently running sessions on a 
-    connected X2go server.
-
-    When suspending or terminating sessions there are two possible ways:
-
-        1. Initialize an X2go session object, start a new session (or resume)
-        and use the L{X2goSession.suspend()} or L{X2goSession.terminate()} method
-        to suspend/terminate the current session object.
-        2. Alternatively, you can pass a session name to L{X2goSession.suspend()}
-        or L{X2goSession.terminate()}. If a session of this name exists on the
-        X2go server the respective action will be performed on the session.
-
-    An L{X2goSession} object uses two main data structure classes: 
-
-        - L{X2goSessionParams}: stores all parameters that have been passed to the 
-        constructor method.
-
-        - L{X2goServerSessionInfo}: when starting or resuming a session, an object of this class 
-        will be used to store all information retrieved from the X2go server.
-
-    @param geometry: screen geometry of the X2go session. Can be either C{<width>x<height>}
-        or C{fullscreen}
-    @type geometry: str
-    @param depth: color depth in bits (common values: C{16}, C{24})
-    @type depth: int
-    @param link: network link quality (either one of C{modem}, C{isdn}, C{adsl}, C{wan} or C{lan})
-    @type link: str
-    @param pack: compression method for NX based session proxying
-    @type pack: str
-    @param cache_type: a dummy parameter that is passed to the L{X2goProxy}. In NX Proxy 
-        (class C{X2goNX3Proxy}) this originally is the session name. With X2go it 
-        defines the name of the NX cache directory. Best is to leave it untouched.
-    @type cache_type: str
-    @param kblayout: keyboard layout, e.g. C{us} (default), C{de}, C{fr}, ...
-    @type kblayout: str
-    @param kbtype: keyboard type, e.g. C{pc105/us} (default), C{pc105/de}, ...
-    @type kbtype: str
-    @param session_type: either C{desktop} or C{application} (rootless session)
-    @type session_type: str
-    @param snd_system: sound system to be used on server (C{none}, C{pulse} (default), 
-        C{arts} (obsolete) or C{esd})
-    @type snd_system: str
-    @param cmd: command to be run on X2go server after session start (only used
-        when L{X2goSession.start()} is called, ignored on resume, suspend etc.
-    @type cmd: str
-    @param rootdir: X2go session directory, normally C{~/.x2go}
-    @type rootdir: str
-    @param proxy_class: other than the default L{X2goProxy} class
-    @type proxy_class: L{X2goProxy} related instance
-    @param print_action: either a print action short name (PDFVIEW, PDFSAVE, PRINT, PRINTCMD) or the
-        resp. C{X2goPrintActionXXX} class (where XXX equals one of the given short names)
-    @type print_action: str or class
-    @param print_action_args: optional arguments for a given print_action (for further info refer to
-        L{X2goPrintActionPDFVIEW}, L{X2goPrintActionPDFSAVE}, L{X2goPrintActionPRINT} and L{X2goPrintActionPRINTCMD})
-    @type print_action_args: dict
-    @param logger: you can pass an L{X2goLogger} object to the
-        L{X2goProxy} constructor
-    @type logger: L{X2goLogger} instance
-    @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
-        constructed with the given loglevel
-    @type loglevel: int
-
-    """
-    associated = False
-    params = None
-    session_info = None
-
-    proxy_class = None
-    proxy = None
-    proxy_subprocess = None
-
-    guardian_thread = None
-    reverse_tunnels = None
-
-    print_queue = None
-
-    _session_auth_rsakey = None
-    _remote_home = None
-    _remote_group = {}
-
-    def __init__(self,
-                 geometry="800x600", depth=24, link="adsl", pack="16m-jpeg-9", 
-                 cache_type="unix-kde", kblayout='us', kbtype='pc105/us',
-                 session_type="application", snd_system='pulse', cmd=None,
-                 rootdir=None, proxy_class=None,
-                 profile_name='UNKNOWN', profile_id=utils._genSessionProfileId(),
-                 print_action=None, print_action_args={},
-                 logger = None, loglevel=log.loglevel_DEFAULT,
-                 *args, **kwargs):
-        """\
-        Initialize an X2go session. With the X2goSession class you can start
-        new X2go sessions, resume suspended sessions or suspend resp. terminate
-        currently running sessions on a connected X2go server.
+    def __init__(self, server, control_session=None,
+                 profile_id=None, profile_name=None,
+                 printing=None, share_local_folders=[],
+                 control_backend=X2goControlSession,
+                 terminal_backend=X2goTerminalSession,
+                 info_backend=X2goServerSessionInfo,
+                 list_backend=X2goServerSessionList,
+                 proxy_backend=X2goProxy,
+		 known_hosts=None,
+                 logger=None, loglevel=log.loglevel_DEFAULT,
+                 **params):
 
-        """
         if logger is None:
             self.logger = log.X2goLogger(loglevel=loglevel)
         else:
             self.logger = copy.deepcopy(logger)
         self.logger.tag = __NAME__
 
-        if proxy_class is None:
-            proxy_class = proxy.DEFAULT_PROXY_CLASS
-
-        self.session_info = X2goServerSessionInfo()
-        self.params = X2goSessionParams()
-
-        self.params.geometry = geometry
-        self.params.depth = str(depth)
-        self.params.link = link
-        self.params.pack = pack
-        self.params.cache_type = cache_type
-        self.params.session_type = session_type
-        self.params.kblayout = kblayout
-        self.params.kbtype = kbtype
-        self.params.snd_system = snd_system
-        self.params.cmd = cmd
-        self.params.rootdir = (type(rootdir) is types.StringType) and rootdir or os.path.join(_LOCAL_HOME,_X2GO_SESSION_ROOTDIR)
-        self.params.update()
-
-        self.proxy_class = proxy_class
-
-        self.print_action = print_action
-        self.print_action_args = print_action_args
-
-        self._mk_session_rootdir(self.params.rootdir)
-        paramiko.SSHClient.__init__(self, *args, **kwargs)
-
-    def __del__(self):
-        self._x2go_tidy_up()
-
-    def _x2go_tidy_up(self):
-
-        if self.proxy is not None:
-            self.proxy.__del__()
-
-        try:
-            if self.get_transport() is not None:
-                for _tunnel in [ _tun[1] for _tun in self.get_transport().reverse_tunnels.values() ]:
-                    if _tunnel is not None:
-                        _tunnel.__del__()
-
-                self.get_transport().stop_thread()
-
-            if self.print_queue is not None:
-                self.print_queue.__del__()
-
-        except AttributeError:
-            pass
-
-    def _mk_session_rootdir(self, d):
-
-        try:
-            os.mkdir(d)
-        except OSError, e:
-            if e.errno == 17:
-                # file exists
-                pass
+        self._keep_alive = True
+
+        self.uuid = uuid.uuid1()
+        self.connected = False
+        self.running = False
+        self.suspended = False
+        self.terminated = False
+
+        self.profile_id = profile_id
+        self.profile_name = profile_name
+        self.server = server
+        self.printing = printing
+        self.share_local_folders = share_local_folders
+        self._control_backend = control_backend
+        self._terminal_backend = terminal_backend
+        self._info_backend = info_backend
+        self._list_backend = list_backend
+        self._proxy_backend = proxy_backend
+        _terminal_params = copy.deepcopy(params)
+        _control_params = copy.deepcopy(params)
+
+        for p in params.keys():
+            if p in session._X2GO_SESSION_PARAMS:
+                del _control_params[p]
             else:
-                raise OSError, e
-
-    def _x2go_exec_command(self, cmd_line, loglevel=log.loglevel_INFO, **kwargs):
-
-        if type(cmd_line) == types.ListType:
-            cmd = " ".join(cmd_line)
-        else:
-            cmd = cmd_line
-        if self.get_transport() is not None:
-
-            try:
-                self.logger('executing command on X2go server: %s' % cmd, loglevel)
-                return self.exec_command(cmd, **kwargs)
-            except AttributeError:
-                raise x2go_exceptions.X2goSessionException('the Paramiko/SSH session of X2go session %s has died' % self.session_info)
-
+                del _terminal_params[p]
+
+        self.logger('X2go control session parameters for profile %s:' % profile_name, log.loglevel_DEBUG)
+        for p in _control_params:
+            self.logger('    %s: %s' % (p, _control_params[p]), log.loglevel_DEBUG)
+        self.logger('X2go terminal session parameters for profile %s:' % profile_name, log.loglevel_DEBUG)
+        for p in _terminal_params:
+            self.logger('    %s: %s' % (p,_terminal_params[p]), log.loglevel_DEBUG)
+
+        self.control_params = _control_params
+        self.terminal_params = _terminal_params
+ 
+        self.logger('starting X2goSession', loglevel=log.loglevel_DEBUG)
+        if control_session is None:
+            self.control_session = control_backend(terminal_backend=terminal_backend,
+                                                   info_backend=info_backend,
+                                                   list_backend=list_backend,
+                                                   logger=logger)
         else:
-            raise x2go_exceptions.X2goSessionException('the Paramiko/SSH client is not connected')
-
-    def _x2go_sftp_put(self, local_path, remote_path):
-
-        self.logger('sFTP-put: %s -> %s:%s' % (local_path, self.session_info.hostname, remote_path), loglevel=log.loglevel_DEBUG)
-        self.sftp_client.put(local_path, remote_path)
-
-    def _x2go_sftp_write(self, remote_path, content):
-
-        self.logger('sFTP-write: opening remote file %s on host %s for writing' % (remote_path, self.session_info.hostname), loglevel=log.loglevel_DEBUG)
-        remote_fileobj = self.sftp_client.open(remote_path, 'w')
-        self.logger('sFTP-write: writing content: %s' % content, loglevel=log.loglevel_DEBUG_SFTPXFER)
-        remote_fileobj.write(content)
-        remote_fileobj.close()
-
-    def _x2go_sftp_remove(self, remote_path):
+            self.control_session = control_session
 
-        self.logger('sFTP-write: removing remote file %s on host %s' % (remote_path, self.session_info.hostname), loglevel=log.loglevel_DEBUG)
-        self.sftp_client.remove(remote_path)
+        self.terminal_session = None
+        self.logger('starting X2goSession', loglevel=log.loglevel_DEBUG)
+        if known_hosts:
+            self.control_session.load_host_keys(known_hosts)
 
-    @property
-    def _x2go_remote_home(self):
-
-        if self._remote_home is None:
-            (stdin, stdout, stderr) = self._x2go_exec_command('echo $HOME')
-            self._remote_home = stdout.read().split()[0]
-            self.logger('remote user\' home directory: %s' % self._remote_home, loglevel=log.loglevel_DEBUG)
-            return self._remote_home
-        else:
-            return self._remote_home
-
-    def _x2go_remote_group(self, group):
-
-        if not self._remote_group.has_key(group):
-            (stdin, stdout, stderr) = self._x2go_exec_command('getent group %s | cut -d":" -f4' % group)
-            self._remote_group[group] = stdout.read().split('\n')[0].split(',')
-            self.logger('remote %s group: %s' % (group, self._remote_group[group]), loglevel=log.loglevel_DEBUG)
-            return self._remote_group[group]
-        else:
-            return self._remote_group[group]
-
-    @property
-    def _x2go_session_auth_rsakey(self):
-        if self._session_auth_rsakey is None:
-            self._session_auth_rsakey = paramiko.RSAKey.generate(defaults.RSAKEY_STRENGTH)
-        return self._session_auth_rsakey
-
-    def connect(self, hostname, port=22, username=None, password=None, pkey=None,
-                key_filename=None, timeout=None, allow_agent=False, look_for_keys=True,
-                add_to_known_hosts=False, force_password_auth=False):
+    def __str__(self):
+        return self.__get_uuid()
+    def __repr__(self):
+        result = 'X2goRegisteredSession('
+        for p in dir(self):
+            if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
+            result += p + '=' + str(self.__dict__[p]) + ', '
+        return result + ')'
+    def __call__(self):
+        return self.__get_uuid()
+
+    def get_uuid(self):
         """\
-        Connect to an X2go server and authenticate to it. This method is directly
-        inherited from the paramiko.SSHClient module. The features of the Paramiko 
-        SSH client connect method are recited here. The parameters C{add_to_known_hosts}
-        and C{force_password_auth} have been added as a parameter for X2go.
-
-        The server's host key
-        is checked against the system host keys (see C{load_system_host_keys})
-        and any local host keys (C{load_host_keys}).  If the server's hostname
-        is not found in either set of host keys, the missing host key policy
-        is used (see C{set_missing_host_key_policy}).  The default policy is
-        to reject the key and raise an C{SSHException}.
-
-        Authentication is attempted in the following order of priority:
-
-            - The C{pkey} or C{key_filename} passed in (if any)
-            - Any key we can find through an SSH agent
-            - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
-            - Plain username/password auth, if a password was given
-
-        If a private key requires a password to unlock it, and a password is
-        passed in, that password will be used to attempt to unlock the key.
-
-        @param hostname: the server to connect to
-        @type hostname: str
-        @param port: the server port to connect to
-        @type port: int
-        @param username: the username to authenticate as (defaults to the
-            current local username)
-        @type username: str
-        @param password: a password to use for authentication or for unlocking
-            a private key
-        @type password: str
-        @param pkey: an optional private key to use for authentication
-        @type pkey: C{PKey}
-        @param key_filename: the filename, or list of filenames, of optional
-            private key(s) to try for authentication
-        @type key_filename: str or list(str)
-        @param timeout: an optional timeout (in seconds) for the TCP connect
-        @type timeout: float
-        @param allow_agent: set to False to disable connecting to the SSH agent
-        @type allow_agent: bool
-        @param look_for_keys: set to False to disable searching for discoverable
-            private key files in C{~/.ssh/}
-        @type look_for_keys: bool
-        @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy() 
-            is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy() 
-            is used
-        @type add_to_known_hosts: bool
-        @param force_password_auth: non-paramiko option, disable pub/priv key authentication 
-            completely, even if the C{pkey} or the C{key_filename} parameter is given
-        @type force_password_auth: bool
-
-        @raise BadHostKeyException: if the server's host key could not be
-            verified
-        @raise AuthenticationException: if authentication failed
-        @raise SSHException: if there was any other error connecting or
-            establishing an SSH session
-        @raise socket.error: if a socket error occurred while connecting
+        STILL UNDOCUMENTED
 
         """
-        if add_to_known_hosts:
-            self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-
-        # disable pub/priv key authentication if forced
-        if force_password_auth:
-            key_filename = None
-            pkey = None
-
-        self.logger('connecting to %s' % hostname, log.loglevel_NOTICE)
-
-        if (key_filename or pkey):
-            try:
-                self.logger('trying SSH pub/priv key authentication with server', log.loglevel_DEBUG)
-                paramiko.SSHClient.connect(self, hostname, port=port, username=username, pkey=pkey,
-                                           key_filename=key_filename, timeout=timeout, allow_agent=allow_agent, 
-                                           look_for_keys=look_for_keys)
-            except paramiko.AuthenticationException, e:
-                if password:
-                    self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', log.loglevel_DEBUG)
-                    paramiko.SSHClient.connect(self, hostname, port=port, username=username, passwort=password,
-                                               timeout=timeout, allow_agent=allow_agent, 
-                                               look_for_keys=look_for_keys)
-                else:
-                    raise(e)
-
-        # if there is not private key, we will use the given password
-        elif password:
-            self.logger('performing SSH keyboard-interactive authentication with server', log.loglevel_DEBUG)
-            paramiko.SSHClient.connect(self, hostname, port=port, username=username, password=password, 
-                                       timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys)
-
-        # authentication failed
-        else:
-            raise paramiko.AuthenticationException()
-
-        # if we succeed, we immediately grab us an sFTP client session
-        self.sftp_client = self.open_sftp()
+        return str(self.uuid)
+    __get_uuid = get_uuid
 
-        # preparing reverse tunnels
-        ssh_transport = self.get_transport()
-        ssh_transport.reverse_tunnels = {
-            'snd': (0, None),
-            'sshfs': (0, None),
-        }
-
-        # mark transport as X2goSession
-        ssh_transport._x2go_session_marker = True
+    def get_username(self):
+        """\
+        After a session has been setup up you can query the
+        username the sessions runs as.
 
-        # once connected start the X2goSession guardian
-        self.guardian_thread = guardian.X2goSessionGuardian(self, logger=self.logger)
-        self.guardian_thread.start()
-        self.guardian_thread.active_threads.append(self.get_transport())
-        self._session_password = password
+        @return: the remote username the X2go session runs as
+        @rtype: C{str}
 
-        return (self.get_transport() is not None)
+        """
+        return self.control_session.get_transport().get_username()
+    __get_username = get_username
 
-    def disconnect(self):
+    def get_password(self):
         """\
-        STILL UNDOCUMENTED
+        After a session has been setup up you can query the
+        username's password from the session.
+
+        @return: the username's password
+        @rtype: C{str}
 
         """
-        self.get_transport().close()
+        return self.control_session._session_password
 
-    def start(self, **kwargs):
+    def get_server(self):
         """\
-        Start a new X2go session. 
+        After a session has been setup up you can query the
+        hostname of the host the sessions is connected to (or
+        about to connect to).
 
-        The L{X2goSession.start()} method accepts any parameter
-        that can be passed to the class constructor.
+        @return: the hostname of the server the X2go session is
+            connected to (as an C{(addr,port)} tuple)
+        @rtype: tuple
 
         """
-        self.params.update(kwargs)
-
-        _remote_username = self.get_transport().get_username()
-        if _remote_username not in self._x2go_remote_group('x2gousers'):
-            raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group x2gousers' % _remote_username)
-
-        setkbd = "0"
-        if self.params.kblayout or self.params.kbtype:
-            setkbd = "1"
-
-        cmd_line = [ "x2gostartagent", 
-                     str(self.params.geometry),
-                     str(self.params.link),
-                     str(self.params.pack),
-                     str(self.params.cache_type+'-depth_'+self.params.depth),
-                     str(self.params.kblayout),
-                     str(self.params.kbtype),
-                     str(setkbd),
-                     str(self.params.session_type),
-                     self.params.cmd,
-                   ]
-
-        (stdin, stdout, stderr) = self._x2go_exec_command(cmd_line)
-
-        self.session_info.initialize(stdout.read(),
-                                     username=_remote_username,
-                                     hostname=self.get_transport().getpeername(),
-                                    )
-
-        # local path may be a Windows path, so we use the path separator of the local system
-        self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
-        # remote path is always a UniX path...
-        self.session_info.remote_container = '%s/%s/C-%s' % (self._x2go_remote_home, 
-                                                             _X2GO_SESSION_ROOTDIR,
-                                                             self.session_info.name,
-                                                            )
-
-        # set up SSH tunnel for X11 graphical elements
-        self.proxy = self.proxy_class(session_info=self.session_info, ssh_transport=self.get_transport(), logger=self.logger)
-        self.proxy_subprocess = self.proxy.start_proxy()
-        self.guardian_thread.active_threads.append(self.proxy)
-
-        self.associated = True
-        return self.ok()
-
-    def start_sound(self):
-        """\
-        Initialize Paramiko/SSH reverse forwarding tunnel for X2go sound.
+        return self.control_session.get_transport().getpeername()
+    __get_server = get_server
 
-        Currently supported audio protocols:
+    def get_session_name(self):
+        """\
+        Retrieve the server-side X2go session name for the session that has
+        been registered under C{profile_id}.
 
-            - Pulse Audio
-            - Esound 
+        @return: X2go session name
+        @rtype: C{str}
 
         """
-        _tunnel = None
-        ssh_transport = self.get_transport()
-        if ssh_transport.reverse_tunnels['snd'][1] is None:
-            if self.params.snd_system == 'pulse':
-                self.logger('initializing Pulse Audio sound support in X2go session', loglevel=log.loglevel_INFO)
-                ###
-                ### PULSE AUDIO
-                ###
-                # setup pulse client config file on X2go server
-                cmd_line = "echo 'default-server=localhost:%s'>%s/.pulse-client.conf;" % (self.session_info.snd_port, self.session_info.remote_container) + \
-                           "echo 'cookie-file=%s/.pulse-cookie'>>%s/.pulse-client.conf" % (self.session_info.remote_container, self.session_info.remote_container)
-                (stdin, stdout, stderr) = self._x2go_exec_command(cmd_line)
-
-                self._x2go_sftp_put(local_path='%s/.pulse-cookie' % _LOCAL_HOME, remote_path='%s/.pulse-cookie' % self.session_info.remote_container)
-
-                # start reverse SSH tunnel for pulse stream
-                _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 
-                                                   remote_host='localhost', 
-                                                   remote_port=4713, 
-                                                   ssh_transport=ssh_transport,
-                                                   logger=self.logger
-                                                  )
-
-            elif self.params.snd_system == 'arts':
-                ###
-                ### ARTSD AUDIO
-                ###
-                self.logger('the ArtsD sound server (as in KDE3) is obsolete and will not be supported by Python X2go...', loglevel=log.loglevel_WARNING)
-
-            elif self.params.snd_system == 'esd':
-                ###
-                ### ESD AUDIO
-                ###
-
-                self.logger('initializing ESD sound support in X2go session', loglevel=log.loglevel_INFO)
-                self._x2go_sftp_put(local_path='%s/.esd_auth' % _LOCAL_HOME, remote_path='%s/.esd_auth' % self._x2go_remote_home)
-
-                # start reverse SSH tunnel for pulse stream
-                _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 
-                                                   remote_host='localhost', 
-                                                   remote_port=16001, 
-                                                   ssh_transport=ssh_transport,
-                                                   logger=self.logger
-                                                  )
-
-
-            if _tunnel is not None:
-                ssh_transport.reverse_tunnels['snd'] = (self.session_info.snd_port, _tunnel)
-                _tunnel.start()
-                self.guardian_thread.active_threads.append(_tunnel)
+        if self.terminal_session is not None:
+            return self.terminal_session.get_session_name() or None
+    __get_session_name = get_session_name
 
-        else:
-            # tunnel has already been started and might simply need a resume call
-            ssh_transport.reverse_tunnels['snd'][1].resume()
-
-    def start_sshfs(self):
+    def get_session_cmd(self):
         """\
-        Initialize Paramiko/SSH reverse forwarding tunnel for X2go folder sharing.
+        STILL UNDOCUMENTED
 
         """
-        # start reverse SSH tunnel for sshfs (folder sharing, printing)
-        ssh_transport = self.get_transport()
-        if ssh_transport.reverse_tunnels['sshfs'][1] is None:
+        if self.terminal_params.has_key('cmd'):
+            return self.terminal_params['cmd']
+        return None
 
-            _tunnel = sftpserver.X2goRevFwTunnelToSFTP(server_port=self.session_info.sshfs_port,
-                                                       ssh_transport=ssh_transport,
-                                                       auth_key=self._x2go_session_auth_rsakey,
-                                                       logger=self.logger
-                                                      )
+    def get_control_session(self):
+        return self.control_session
+    __get_control_session = get_control_session
 
-            if _tunnel is not None:
-                ssh_transport.reverse_tunnels['sshfs'] = (self.session_info.sshfs_port, _tunnel)
-                _tunnel.start()
-                self.guardian_thread.active_threads.append(_tunnel)
-
-        else:
-            # tunnel has already been started and might simply need a resume call
-            ssh_transport.reverse_tunnels['sshfs'][1].resume()
+    def get_terminal_session(self):
+        return self.terminal_session
+    __get_terminal_session = get_terminal_session
 
-    def _x2go_pause_rev_fw_tunnel(self, name):
-        # pause reverse SSH tunnel of name <name>
-        ssh_transport = self.get_transport()
-        _tunnel = ssh_transport.reverse_tunnels[name][1]
-        if _tunnel is not None:
-            _tunnel.pause()
-
-    def stop_sound(self):
+    def connect(self, username='', password='', add_to_known_hosts=False, force_password_auth=False):
         """\
-        Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go sound.
-
-        """
-        self._x2go_pause_rev_fw_tunnel('sound')
+        Connect to a registered X2go session with registry hash C{<session_uuid>}.
+        This method basically wraps around paramiko.SSHClient.connect() for the
+        corresponding session.
 
-    def stop_sshfs(self):
-        """\
-        Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go folder sharing.
+        @param username: the username for the X2go server that is going to be
+            connected to (as a last minute way of changing the session username)
+        @type username: C{str}
+        @param password: the user's password for the X2go server that is going to be
+            connected to
+        @type password: C{str}
+        @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
+            is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
+            is used
+        @type add_to_known_hosts: C{bool}
+        @param force_password_auth: disable SSH pub/priv key authentication mechanisms
+            completely
+        @type force_password_auth: C{bool}
 
         """
-        self._x2go_pause_rev_fw_tunnel('sshfs')
+        if self.control_session.is_connected():
+            self.logger('control session is already connected, skipping authentication', loglevel=log.loglevel_DEBUG)
+            self.connected = True
+        else:
+            if username:
+                self.control_params['username'] = username
+            self.control_params['password'] = password
+            self.connected = self.control_session.connect(self.server, **self.control_params)
+        return self.connected
+    __connect = connect
 
-    def start_printing(self):
+    def disconnect(self):
         """\
-        Initialize X2go print spooling.
+        STILL UNDOCUMENTED
 
         """
-        if self.session_info.username not in self._x2go_remote_group('x2goprint'):
-            raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group x2goprint' % self.session_info.username)
-
-        spool_dir = os.path.join(self.session_info.local_container, 'spool')
-        if not os.path.exists(spool_dir):
-            os.mkdir(spool_dir)
-        self.share_local_folder(folder_name=spool_dir, folder_type='spool')
-        self.print_queue = printing.X2goPrintQueue(spool_dir=spool_dir,
-                                                   print_action=self.print_action, 
-                                                   print_action_args=self.print_action_args, 
-                                                   logger=self.logger,
-                                                  )
-        self.print_queue.start()
-        self.guardian_thread.active_threads.append(self.print_queue)
+        self.terminal_session.disconnect()
+        self.connected = False
+        self.running = False
+        self.suspended = False
+        self.terminated = False
+    __disconnect = disconnect
 
     def set_print_action(self, print_action, **kwargs):
         """\
         STILL UNDOCUMENTED
 
         """
-        self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
-
+        if type(print_action) is not types.StringType:
+            return False
+        self.terminal_session.set_print_action(print_action, **kwargs)
+    __set_print_action = set_print_action
 
-    def stop_printing(self):
-        """\
-        Shutdown (pause) the X2go Print Queue thread.
+    def clean_sessions(self):
+        self.control_session.clean_sessions()
 
-        """
-        if self.print_queue is not None:
-            self.print_queue.pause()
+    def list_sessions(self):
+        return self.control_session.list_sessions()
 
-    def share_local_folder(self, folder_name=None, folder_type='disk'):
+    def resume(self, session_name=None):
         """\
-        Share a local folder with the X2go session.
+        Resume or continue a suspended / running X2go session on the
+        remote X2go server.
 
-        @param folder_name: the full path to an existing folder on the local 
-            file system
-        @type folder_name: str
-        @param folder_type: one of 'disk' (a folder on your local hard drive), 'rm' (removeable device), 
-            'cdrom' (CD/DVD Rom) or 'spool' (for X2go print spooling)
-        @type folder_type: str
-
-        @return: returns C{True} if the local folder has been successfully mounted within the X2go server session
-        @rtype: bool
+        @param session_name: the server-side name of an X2go session
+        @type session_name: C{str}
 
         """
-        if self.session_info.username not in self._x2go_remote_group('fuse'):
-            raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group fuse' % self.session_info.username)
-
-        if folder_name is None:
-            self.logger('no folder name given...', log.loglevel_WARN)
-            return False
-
-        if type(folder_name) is not types.StringType:
-            self.logger('folder name needs to be of type StringType...', log.loglevel_WARN)
-            return False
-
-        if not os.path.exists(folder_name):
-            self.logger('local folder does not exist: %s' % folder_name, log.loglevel_WARN)
-            return False
+        _control = self.control_session
+        _terminal = _control.resume(session_name=session_name, logger=self.logger, **self.terminal_params)
+        if _terminal is not None:
 
-        self.logger('sharing local folder: %s' % folder_name, log.loglevel_INFO)
+            if _terminal.params.snd_system is not 'none':
+                _terminal.start_sound()
 
-        _auth_rsakey = self._x2go_session_auth_rsakey
-        _host_rsakey = defaults.RSAHostKey
+            if self.printing or self.share_local_folders:
+                _terminal.start_sshfs()
 
-        _tmp_io_object = cStringIO.StringIO()
-        _auth_rsakey.write_private_key(_tmp_io_object)
-        _tmp_io_object.write('----BEGIN RSA IDENTITY----')
-        _tmp_io_object.write('%s %s' % (_host_rsakey.get_name(),_host_rsakey.get_base64(),))
+            if self.printing:
+                _terminal.start_printing()
 
-        _x2go_key_fname = os.path.join(os.path.dirname(self.session_info.remote_container), 'ssh', 'key.z%s' % self.session_info.agent_pid)
-        _x2go_key_bundle = _tmp_io_object.getvalue()
+            if self.share_local_folders:
+                if _control.get_transport().reverse_tunnels[_terminal.get_session_name()]['sshfs'][1] is not None:
+                    for _folder in self.share_local_folders:
+                        _terminal.share_local_folder(_folder)
 
-        self._x2go_sftp_write(_x2go_key_fname, _x2go_key_bundle)
+            _terminal.run_command()
+            self.suspended = False
+            self.running = True
+            self.terminated = False
 
-        if folder_type is 'disk':
+            self.terminal_session = _terminal
 
-            cmd_line = [ 'export HOSTNAME &&',
-                         'x2gomountdirs', 
-                         'dir',
-                         str(self.session_info.name), 
-                         _CURRENT_LOCAL_USER,
-                         _x2go_key_fname,
-                         '%s__REVERSESSH_PORT__%s; ' % (folder_name, self.session_info.sshfs_port),
-                         'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
-                       ]
+        return self.running
+    __resume = resume
 
-        elif folder_type is 'spool':
-
-            cmd_line = [ 'export HOSTNAME &&',
-                         'x2gomountdirs', 
-                         'dir',
-                         str(self.session_info.name), 
-                         _CURRENT_LOCAL_USER,
-                         _x2go_key_fname,
-                         '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (folder_name, self.session_info.sshfs_port),
-                         'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
-                       ]
+    def start(self):
+        """\
+        Start a new X2go session on the remote X2go server.
 
-        (stdin, stdout, stderr) = self._x2go_exec_command(cmd_line)
-        self.logger('x2gomountdirs output is : %s' % stdout.read().split('\n'), log.loglevel_INFO)
+        """
+        self.resume()
+    __start = start
 
-    def run_command(self, cmd=None):
+    def suspend(self):
         """\
-        Run a command in this session.
+        Suspend an X2go session.
 
-        After L{X2goSession.start()} has been called 
-        one or more commands can be executed with L{X2goSession.run_command()}
-        within the current X2go session.
+        You can either suspend a session that you have formerly
+        started/resumed the current X2goClient instance.
 
-        @param cmd: Command to be run
-        @type cmd: str
-
-        @return: stdout.read() and stderr.read() as returned by the run command
-            on the X2go server
-        @rtype: tuple of str
+        Or you can suspend a non-attached session by simply
+        registering an X2go server session and then passing the
+        server-side X2go session name to this method.
 
         """
-        if cmd in ("", None):
-            if self.params.cmd is None:
-                cmd = 'TERMINAL'
-            else:
-                cmd = self.params.cmd
+        if self.terminal_session.suspend():
 
-        self.params.update({'cmd': cmd})
+            self.running = False
+            self.suspended = True
+            return True
 
-        cmd_line = [ "setsid x2goruncommand", 
-                     str(self.session_info.display),
-                     str(self.session_info.agent_pid),
-                     str(self.session_info.name), 
-                     str(self.session_info.snd_port),
-                     _rewrite_cmd(self.params.cmd),
-                     str(self.params.snd_system),
-                     str(self.params.session_type),
-                     ">& /dev/null & exit",
-                   ]
-
-        if self.params.snd_system is 'pulse':
-            cmd_line = [ 'PULSE_CLIENTCONFIG=%s/.pulse-client.conf' % self.session_info.remote_container ] + cmd_line
-
-        (stdin, stdout, stderr) = self._x2go_exec_command(cmd_line)
-
-        return stdout.read(), stderr.read()
+        return False
+    __suspend = suspend
 
-    def list_sessions(self, raw=False):
+    def terminate(self):
         """\
-        List all sessions of current user on the connected server.
+        Terminate an X2go session.
 
-        @param raw: if C{True}, the raw output of the server-side X2go command 
-            C{x2golistsessions} is returned.
-        @type raw: bool
+        You can either terminate a session that you have formerly
+        started/resumed within the current X2goClient instance.
 
-        @return: normally an instance of L{X2goServerSessionList} is returned. However,
-            if the raw argument is set, the plain text output of the x2golistsessions 
-            command is returned
-        @rtype: L{X2goServerSessionList} instance or str
+        Or you can terminate a non-attached session by simply
+        registering an X2go server session and then passing the
+        server-side X2go session name to this method.
 
         """
-        (stdin, stdout, stderr) = self._x2go_exec_command("x2golistsessions")
+        if self.terminal_session.terminate():
 
-        if raw:
-            return stdout.read(), stderr.read()
+            self.running = False
+            self.suspended = False
+            self.terminated = True
+            return True
 
-        _stdout_read = stdout.read()
-        return X2goServerSessionList(_stdout_read).sessions
+        return False
+    __terminate = terminate
 
-    def ok(self):
+    def get_profile_name(self):
         """\
-        Returns C{True} if this X2go session is up and running, 
-        C{False} else
+        Retrieve the profile name of this registered session.
 
-        @return: X2go session OK?
-        @rtype: bool
+        @return: X2go client profile name of the session
+        @rtype: C{str}
 
         """
-        return bool(self.session_info.name and (self.proxy_subprocess and self.proxy_subprocess.poll() is None))
+        return self.profile_name
+    __get_profile_name = get_profile_name
 
-    def is_connected(self):
+    def get_profile_id(self):
         """\
-        Returns C{True} if this X2go session is connected to the remote server (that
-        is if it has a valid Paramiko Transport object).
+        Retrieve this registered session's profile id.
 
-        @return: X2go session connected
-        @rtype: bool
+        @return: the session profile's id
+        @rtype: C{str}
 
         """
-        return self.get_transport() is not None
-
-    def is_running(self):
-        """\
-        Returns C{True} if this X2go session is in running state ('R'), 
-        C{False} else.
+        return self.profile_id
+    __get_profile_id = get_profile_id
 
-        @return: X2go session running?
-        @rtype: bool
+    ###
+    ### QUERYING INFORMATION
+    ###
 
-        """
-        session_infos = self.list_sessions()
-        if self.session_info.name in session_infos.keys():
-            return session_infos[self.session_info.name].status == "R"
-        return False
-
-    def is_suspended(self):
+    def session_ok(self):
         """\
-        Returns C{True} if this X2go session is in suspended state ('S'), 
-        C{False} else.
+        Test if this registered X2go session is
+        in a healthy state.
 
-        @return: X2go session suspended?
-        @rtype: bool
+        @return: C{True} if session is ok, C{False} otherwise
+        @rtype: C{bool}
 
         """
-        session_infos = self.list_sessions()
-        if self.session_info.name in session_infos.keys():
-            return session_infos[self.session_info.name].status == "S"
+        if self.terminal_session is not None:
+            return self.terminal_session.ok()
         return False
+    __session_ok = session_ok
 
-    def has_terminated(self):
-        """\
-        Returns C{True} if this X2go session is not in the session list on the 
-        connected server, C{False} else.
 
-        Of course, if this command is called before session startup, it will also
-        return C{True}.
+    def is_connected(self):
+        """\
+        Test if this registered X2go session is connected to the 
+        remote server.
 
-        @return: X2go session has terminate?
-        @rtype: bool
+        @return: C{True} if session is connected, C{False} otherwise
+        @rtype: C{bool}
 
         """
-        session_infos = self.list_sessions()
-        return self.session_info.name not in session_infos.keys()
+        return self.control_session.is_connected()
+    _is_connected = is_connected
 
-    def associate(self, session_name):
+    def is_running(self):
         """\
-        Associate L{session_name} with an available (state 'R' or 'S')
-        X2go session on the server.
+        Test if this registered X2go session is up and running.
 
-        @param session_name: X2go name of an available session.
-        @type session_name: str
+        @return: C{True} if session is running, C{False} otherwise
+        @rtype: C{bool}
 
         """
-        self.associated = False
-        try:
-            self.session_info = self.list_sessions()[session_name]
-            if self.session_info.name:
-                self.associated = True
-                self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
-        except KeyError:
-            pass
-        return self.associated
-
-    def resume(self, **kwargs):
-        """\
-        Resume a running/suspended X2go session. 
+        return self.is_connected() and self.terminal_session.is_running()
+    _is_running = is_running
 
-        The L{X2goSession.resume()} method accepts any parameter
-        that can be passed to the class constructor.
+    def is_suspended(self):
+        """\
+        Test if this registered X2go session is in suspended state.
 
-        @return: True if the session could be successfully resumed
-        @rtype: bool
+        @return: C{True} if session is suspended, C{False} otherwise
+        @rtype: C{bool}
 
         """
-        if self.associated:
-
-            if self.session_info.username not in self._x2go_remote_group('x2gousers'):
-                raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group x2gousers' % self.session_info.username)
-
-            self.params.update(kwargs)
-
-            # if the session is still running, suspend it first
-            if self.session_info.status == "R":
-                self.suspend()
-
-            setkbd = "0"
-            if self.params.kblayout or self.params.kbtype:
-                setkbd = "1"
-
-            cmd_line = [ "x2goresume-session", self.session_info.name,
-                         self.params.geometry,
-                         self.params.link,
-                         self.params.pack,
-                         self.params.kblayout,
-                         self.params.kbtype,
-                         setkbd,
-                       ]
+        return self.is_connected() and self.terminal_session.is_suspended()
+    __is_suspended = is_suspended
 
-            (stdin, stdout, stderr) = self._x2go_exec_command(cmd_line)
-
-            self.proxy = self.proxy_class(self.session_info, self.get_transport(), logger=self.logger)
-            self.proxy_subprocess = self.proxy.start()
-
-            # local path may be a Windows path, so we use the path separator of the local system
-            self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
-            # remote path is always a UniX path...
-            self.session_info.remote_container = '%s/%s/C-%s' % (self._x2go_remote_home, 
-                                                                 _X2GO_SESSION_ROOTDIR,
-                                                                 self.session_info.name,
-                                                                )
-            return self.ok()
-
-        else:
-            raise x2go_exceptions.X2goSessionException('This X2go session instance is not associated to any server-side X2go session.')
-
-    def suspend(self, session_name=None):
+    def has_terminated(self):
         """\
-        Suspend either this or another available X2go session on the connected
-        server.
-
-        If L{session_name} is given, L{X2goSession.suspend()} tries to suspend the
-        corresponding session.
+        Test if this registered X2go session has terminated.
 
-        @param session_name: X2go name of the session to be suspended
-        @type session_name: str
-
-        @return: True if the session could be successfully suspended
-        @rtype: bool
+        @return: C{True} if session has terminated, C{False} otherwise
+        @rtype: C{bool}
 
         """
-        _ret = False
-        if session_name is not None:
-
-            self.logger('suspending non-associated session: %s' % session_name, log.loglevel_DEBUG)
-            (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
-            dummy_stdout = stdout.read()
-            dummy_stderr = stderr.read()
-            _ret = True
+        return self.is_connected() and self.control_session.has_terminated(self.get_session_name())
+    __has_terminated = has_terminated
 
-        elif self.associated:
-
-            self.logger('suspending associated session: %s' % self.session_info, log.loglevel_DEBUG)
-            (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % self.session_info, loglevel=log.loglevel_DEBUG)
-            dummy_stdout = stdout.read()
-            dummy_stderr = stderr.read()
-            self.associated = False
-            _ret = True
-
-            self._x2go_tidy_up()
-
-        return _ret
-
-    def terminate(self, session_name=None):
+    def share_local_folder(self, folder_name):
         """\
-        Terminate either this or another available X2go session on the connected
-        server.
-
-        If L{session_name} is given, L{X2goSession.terminate()} tries to terminate the
-        corresponding session.
+        Share a local folder with this registered X2go session.
 
-        @param session_name: X2go name of the session to be terminated
-        @type session_name: str
+        @param folder_name: the full path to an existing folder on the local
+            file system
+        @type folder_name: C{str}
 
-        @return: True if the session could be successfully terminate
-        @rtype: bool
+        @return: returns C{True} if the local folder has been successfully mounted within 
+            this registered X2go server session
+        @rtype: C{bool}
 
         """
-        _ret = False
-        if session_name is not None:
-
-            self.logger('terminating non-associated session: %s' % session_name, log.loglevel_INFO)
-            (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
-            dummy_stdout = stdout.read()
-            dummy_stderr = stderr.read()
-            _ret = True
-
-        elif self.associated:
-
-            self.logger('terminating associated session: %s' % self.session_info, log.loglevel_INFO)
-            (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % self.session_info, loglevel=log.loglevel_DEBUG)
-            dummy_stdout = stdout.read()
-            dummy_stderr = stderr.read()
-            self.session_info.clear()
-            self.associated = False
-            _ret = True
+        return self.session_object.share_local_folder(folder_name=folder_name)
+    __share_local_folder = share_local_folder
 
-            self._x2go_tidy_up()
 
-        return _ret


hooks/post-receive
-- 
python-x2go.git (Python X2Go Client API)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-x2go.git" (Python X2Go Client API).




More information about the x2go-commits mailing list