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

X2Go dev team git-admin at x2go.org
Tue Jan 7 16:19:28 CET 2014


The branch, brokerclient 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