[X2go-Commits] python-x2go.git - build-main (branch) updated: 0.1.1.0

X2go dev team git-admin at x2go.org
Fri Jun 24 01:49:32 CEST 2011


The branch, build-main has been updated
       via  0b8784cb8777bcb486e32a87b754ca93b98a7871 (commit)
       via  fd7f5a812840885bdbbd04cb2081a8a055aaba43 (commit)
       via  c86a2cd4b0db04d25b01f7a6ce84159fe9e7ec06 (commit)
       via  a27899e1b0669f46a6d9522ab52ebdf7a05a17c7 (commit)
       via  f684138dba366d5906d10dbdeb049607af66b296 (commit)
       via  1876820177d1e159a17e7b9315306b777e10238e (commit)
       via  aab424884da4f4dc1a188afc3bfa7194b9836ea6 (commit)
       via  fe00a4d4a04d28fe242e054879d9a5a9ceaad357 (commit)
       via  bcbc75f490c112b47e2c30ce889f23f0ce0822ef (commit)
       via  009a7d9f70834ad66f535b71ac4d1670a6009b0e (commit)
       via  c9357984185ea987a4378fba4c3b5236e440cb4c (commit)
       via  bb675f711f1c336be6959f0a97608c5acfd92f85 (commit)
       via  ff2be3893729257f9e03ef61a743ef3b8dd9cc2c (commit)
       via  51fcf9104c866bb962277925a6c451a5854d71a1 (commit)
       via  aaa397d3fb7760faef93327028199cc242fcb194 (commit)
       via  ddce72c987c36e2adf9746c257d993d6dcd960cd (commit)
       via  912ce42c8e9222d6c31a4ef9409e20d3f692f9cb (commit)
       via  ccbabb821c111c7bd01eae23c6e1a156029d97bc (commit)
       via  f046c26dbdb42ac59cdf994ba6e53791391704d7 (commit)
       via  b60599dc6cb45440fdced438873022d43942434f (commit)
       via  4ccf6c292b3576265e6947d689e8fd88dda387fc (commit)
       via  cf7fdf0842919dff58c0733ddfd083d4583cd40b (commit)
       via  a7da6a7a674e23e797661ca9fa561c190b40fe79 (commit)
       via  21c4a93cb38b98a7c20a39b91d5faeaab88c77db (commit)
       via  2051ce5c73f9abe4d0124173f1bb784684202560 (commit)
       via  d43d6a81e68a9cf3d392ff232e481a599ff82119 (commit)
       via  982d86f3ccf7d17f6590036fd49a4e8bf127df6a (commit)
       via  1864f206b495fe38b2b9e6e2569de1b215279de0 (commit)
       via  40032c72120c5b58f9135488f3120902fce77e7a (commit)
       via  81b1dbb0b4b0689c7170e8e6c6dda89315e99ba7 (commit)
       via  b43c50f7a6d7a9810f7ef5b7d7e5e53bc10f550d (commit)
       via  5a148fdf12b44d6b8f0398f8cb9dcabaeb39064d (commit)
       via  9dbd64ed33bd983ac81693493fa56eb5b7827f1e (commit)
       via  d5f0709a91e364d88ae76d95235d6f02860f3d68 (commit)
       via  476adefe5f6f1c81e03d6cc3be01e8b8777eff67 (commit)
       via  7f32339cfcfcabdbb701decef90a08f9d797b421 (commit)
       via  b310692a365a7803d58ab686ed95cb52a2db92c0 (commit)
       via  2e6f672fc9127f84cbc0b9d8f42c39f0a44a0565 (commit)
       via  49301f618fa80662ce7b1ababc36f7c1e1d5abf3 (commit)
       via  a93a3515718f8308feff5a704bfded92df932e1f (commit)
       via  116df2c20b8f779dacec59d7b389911c9f053c70 (commit)
       via  8da942dc36a58af3aefc730ace7c7af91ad587c0 (commit)
       via  972d8cf6f428e3183beedbcaa9e4c6db1704cde1 (commit)
       via  ec6440b91b3c0156c5c555800edb5e3fb53a417e (commit)
       via  160d07f57f2b950cb51e0c4d181af1815a3afc9c (commit)
       via  5014b32c3ff00c326699108a8470443c8abd43da (commit)
       via  fa0d562da07ec11e299edc3b98a56121b4e0c25e (commit)
       via  27d7b87c686844af7b63b6045aff296a9b8da04d (commit)
       via  94a3a09921f8ece7076a485c127aafa55e42d2cd (commit)
       via  e0a1241c3982f8d637ccda7f2fe9c0f3a3ed21af (commit)
       via  04ecca3b0c80a420af49df7cb06dd5cb24b52dc9 (commit)
       via  2eb15478ddd7b8fc737cdc8a1ba0c77e469a56bd (commit)
       via  81d1fe7e7ea5eedc85c96ccfdd29fca3d16fff6a (commit)
       via  e3aec5868789074d4f7166e29684df09cfe3099a (commit)
       via  5af424a7e9c6a693b56b1986a125d4b2abf37c12 (commit)
      from  84a760d5e08db07be76c05d361ed2024ad0d7632 (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:
 debian/changelog                  |   37 +++++
 x2go/__init__.py                  |    2 +-
 x2go/backends/control/_stdout.py  |  179 ++++++++++++++++++------
 x2go/backends/proxy/_nx3.py       |    8 +-
 x2go/backends/proxy/base.py       |   47 +++++-
 x2go/backends/terminal/_stdout.py |   72 +++++++---
 x2go/cache.py                     |   99 +++++++++++---
 x2go/client.py                    |  195 ++++++++++++++++++++++++--
 x2go/defaults.py                  |    8 +-
 x2go/forward.py                   |  105 +++++++++++---
 x2go/guardian.py                  |   11 ++-
 x2go/registry.py                  |   97 ++++++++++---
 x2go/rforward.py                  |   10 +-
 x2go/session.py                   |  280 ++++++++++++++++++++++++++++++-------
 x2go/sshproxy.py                  |   13 ++-
 x2go/utils.py                     |   36 +++++
 x2go/x2go_exceptions.py           |    2 +
 17 files changed, 995 insertions(+), 206 deletions(-)

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index 29a2aec..8b30409 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,40 @@
+python-x2go (0.1.1.0-0~x2go1) unstable; urgency=low
+
+  * New upstream version (0.1.1.0):
+    - Add X2go desktop sharing support.
+    - Fix SSH authentication failures (close session on failure).
+    - Close SSH connection first, then close down SSH proxy.
+    - Make sure SSH proxy password gets forgotten between two sessions.
+    - Add X2goSession status property ,,faulty''.
+    - Make sure list session and list desktop commands always return.
+    - Rely on X2goSessionListInfo backend to handle exceptions appropriately.
+    - Assure that rev forwarding tunnels use IPv4 (replace localhost with 127.0.0.1).
+    - Explicitly tunnel over IPv4 for NX proxy.
+    - Make cache more configurable (session list updates, desktop list updates).
+    - Adds an auto_update_listdesktops_cache to X2goClient constructor kwargs.
+    - Fix multiple notifications for the same session state change, reliably
+      differentiate between found sessions after connect and newly started 
+      sessions from another client.
+    - Mark terminals as PENDING even before the X2goTerminalSession object is
+      created.
+    - Change of sleep times when starting/stopping NX proxy.
+    - Make fw tunneling more robust against failures.
+    - Test focus put on client inter-operation. It is reliably possible now to move
+      sessions between different clients without resume failures.
+    - Add X2goSession lock support.
+    - Skip session auto registration during startups of new sessions (avoids
+      duplicate sessions in the session registry.
+    - Do not start X2go service tunnels (audio, sshfs) if session startup failed.
+    - Fix NX proxy startup post-check.
+    - Force 16bit colour depth for RDP-proxy sessions.
+    - Faulty sessions (without a NX proxy fw tunnel) will get terminated whenever
+      the X2go server (SSHd) denies the tunnel setup.
+    - Detect local color depth and use it as default for new sessions.
+    - Add compatibility check methods for color depth.
+    - Provide X2goClient method for retrieval of session by session name.
+
+ -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Fri, 24 Jun 2011 01:48:57 +0200
+
 python-x2go (0.1.0.3-0~x2go1) unstable; urgency=low
 
   * New upstream version (0.1.0.3):
diff --git a/x2go/__init__.py b/x2go/__init__.py
index c10b32c..d2893d7 100644
--- a/x2go/__init__.py
+++ b/x2go/__init__.py
@@ -158,7 +158,7 @@ Contact
 """
 
 __NAME__    = 'python-x2go'
-__VERSION__ = '0.1.0.3'
+__VERSION__ = '0.1.1.0'
 
 from gevent import monkey
 monkey.patch_all()
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index 1daa82f..059d091 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -185,23 +185,31 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             timeout = gevent.Timeout(20)
             timeout.start()
             try:
-                self.logger('executing command on X2go server: %s' % _rerewrite_blanks(cmd), loglevel)
+                self.logger("executing command on X2go server ,,%s'': %s" % (self.profile_name, _rerewrite_blanks(cmd)), loglevel)
                 _retval = self.exec_command(_rewrite_password(cmd, user=self.get_transport().get_username(), password=self._session_password), **kwargs)
-                self.locked = False
             except AttributeError:
                 self.session_died = True
                 if self.sshproxy_session:
                     self.sshproxy_session.stop_thread()
+                self.locked = False
                 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
             except EOFError:
                 self.session_died = True
                 if self.sshproxy_session:
                     self.sshproxy_session.stop_thread()
+                self.locked = False
+                raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
+            except x2go_exceptions.SSHException:
+                self.session_died = True
+                if self.sshproxy_session:
+                    self.sshproxy_session.stop_thread()
+                self.locked = False
                 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
             except gevent.timeout.Timeout:
                 self.session_died = True
                 if self.sshproxy_session:
                     self.sshproxy_session.stop_thread()
+                self.locked = False
                 raise x2go_exceptions.X2goControlSessionException('the X2go control session command timed out')
             finally:
                 self.locked = False
@@ -350,13 +358,11 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                                                               logger=self.logger,
                                                              )
 
-            except x2go_exceptions.X2goSSHProxyAuthenticationException, e:
-                self.sshproxy_session = None
-                raise(e)
-
-            except x2go_exceptions.X2goSSHProxyException, e:
+            except:
+                if self.sshproxy_session:
+                    self.sshproxy_session.stop_thread()
                 self.sshproxy_session = None
-                raise(e)
+                raise
 
             if self.sshproxy_session is not None:
                 self.sshproxy_session.start()
@@ -388,7 +394,6 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                                            look_for_keys=look_for_keys)
 
             except paramiko.AuthenticationException, e:
-
                 self.close()
                 if password:
                     self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', loglevel=log.loglevel_DEBUG)
@@ -397,16 +402,27 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                                                    timeout=timeout, allow_agent=allow_agent, 
                                                    look_for_keys=look_for_keys)
                     except paramiko.AuthenticationException, e:
+                        self.close()
                         if self.sshproxy_session:
                             self.sshproxy_session.stop_thread()
+                        raise e
+                    except:
                         self.close()
-                        raise(e)
+                        if self.sshproxy_session:
+                            self.sshproxy_session.stop_thread()
+                        raise
                 else:
+                    self.close()
                     if self.sshproxy_session:
                         self.sshproxy_session.stop_thread()
-                    self.close()
                     raise(e)
 
+            except:
+                self.close()
+                if self.sshproxy_session:
+                    self.sshproxy_session.stop_thread()
+                raise
+
         # if there is not private key, we will use the given password, if any
         else:
             # create a random password if password is empty to trigger host key validity check
@@ -417,15 +433,15 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                 paramiko.SSHClient.connect(self, hostname, port=port, username=username, password=password,
                                            timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys)
             except paramiko.AuthenticationException, e:
+                self.close()
                 if self.sshproxy_session:
                     self.sshproxy_session.stop_thread()
+                raise e
+            except:
                 self.close()
-                raise(e)
-            except x2go_exceptions.X2goHostKeyException, e:
                 if self.sshproxy_session:
                     self.sshproxy_session.stop_thread()
-                self.close()
-                raise(e)
+                raise
 
         self.set_missing_host_key_policy(paramiko.RejectPolicy())
 
@@ -480,9 +496,6 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                 except KeyError:
                     pass
 
-        if self.sshproxy_session is not None:
-            self.sshproxy_session.stop_thread()
-
         self._remote_home = None
         self._remote_group = {}
 
@@ -491,7 +504,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         try:
             if self.get_transport() is not None:
                 still_active = self.get_transport().is_active()
-                self.get_transport().close()
+                self.close()
+                if self.sshproxy_session is not None:
+                    self.sshproxy_session.stop_thread()
                 return still_active
             return False
         except AttributeError:
@@ -499,6 +514,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             # but state that this method call did not close the SSH client, but was already closed
             return False
 
+
     def is_alive(self):
         if self._x2go_exec_command('echo', loglevel=log.loglevel_DEBUG):
             return True
@@ -545,32 +561,104 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                                            sessions_rootdir=self.sessions_rootdir,
                                            **kwargs)
 
+        _success = False
         if session_name is not None:
-            if self.is_running(session_name):
-                self.suspend(session_name)
-                gevent.sleep(3)
+            try:
+                _success = _terminal.resume()
+            except x2go_exceptions.X2goFwTunnelException:
+                pass
 
+        else:
+            try:
+                _success = _terminal.start()
+            except x2go_exceptions.X2goFwTunnelException:
+                pass
+
+        if _success:
             while not _terminal.ok():
+                gevent.sleep(.2)
 
-                try:
-                    _terminal.resume()
-                except x2go_exceptions.X2goFwTunnelException:
-                    pass
+            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
+
+        return None
+
+    def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, **kwargs):
+        """\
+        Share another already running desktop session. Desktop sharing can be run
+        in two different modes: view-only and full-access mode.
+
+        @param desktop: desktop ID of a sharable desktop in format <user>@<display>
+        @type desktop: C{str}
+        @param user: user name and display number can be given separately, here give the
+            name of the user who wants to share a session with you.
+        @type user: C{str}
+        @param display: user name and display number can be given separately, here give the
+            number of the display that a user allows you to be shared with.
+        @type display: C{str}
+        @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
+        @type share_mode: C{int}
+
+        @return: True if the session could be successfully shared.
+        @rtype: C{bool}
+
+        """
+        if desktop:
+            user = desktop.split('@')[0]
+            display = desktop.split('@')[1]
+        if not (user and display):
+            raise x2go_exceptions.X2goDesktopSharingException('Need user name and display number of sharable desktop.')
+
+        cmd = '%sXSHAD%sXSHAD%s' % (share_mode, user, display)
+
+        kwargs['cmd'] = cmd
+        kwargs['session_type'] = 'shared'
+
+        return self.start(**kwargs)
+
+    def list_desktops(self, raw=False, maxwait=20):
+        """\
+        List all desktop-like sessions of current user (or of users that have 
+        granted desktop sharing) on the connected server.
+
+        @param raw: if C{True}, the raw output of the server-side X2go command 
+            C{x2godesktopsharing} is returned.
+        @type raw: C{bool}
+
+        @return: a list of X2go desktops available for sharing
+        @rtype: C{list}
+
+        """
+        if raw:
+            (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
+            return stdout.read(), stderr.read()
 
         else:
-            _terminal.start()
 
-        while not _terminal.ok():
-            gevent.sleep(.2)
+            # this _success loop will catch errors in case the x2golistsessions output is corrupt
+            # this should not be needed and is a workaround for the current X2go server implementation
 
-        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),
-            }
+            timeout = gevent.Timeout(maxwait)
+            timeout.start()
+            try:
+                (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
+                _stdout_read = stdout.read()
+                _listdesktops = _stdout_read.split('\n')
+            except gevent.timeout.Timeout:
+                # if we do not get a reply here after <maxwait> seconds we will raise a time out, we have to
+                # make sure that we catch this at places where we want to ignore timeouts (e.g. in the 
+                # desktop list cache)
+                raise x2go_exceptions.X2goTimeOutException('x2golistdesktop command timed out')
+            finally:
+                timeout.cancel()
 
-        return _terminal or None
+            return _listdesktops
 
     def list_sessions(self, raw=False):
         """\
@@ -587,21 +675,25 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
 
         """
         if raw:
-            (stdin, stdout, stderr) = self._x2go_exec_command("x2golistsessions")
+            (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
             return stdout.read(), stderr.read()
 
         else:
 
             # this _success loop will catch errors in case the x2golistsessions output is corrupt
             # this should not be needed and is a workaround for the current X2go server implementation
+            _listsessions = {}
             _success = False
-            while not _success:
-
-                (stdin, stdout, stderr) = self._x2go_exec_command("x2golistsessions")
-
-                _stdout_read = stdout.read()
+            _count = 0
+            _maxwait = 20
 
+            # we will try this 20 times before giving up... we might simply catch the x2golistsessions
+            # output in the middle of creating a session in the database...
+            while not _success and _count < _maxwait:
+                _count += 1
                 try:
+                    (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
+                    _stdout_read = stdout.read()
                     _listsessions = self._list_backend(_stdout_read, info_backend=self._info_backend).sessions
                     _success = True
                 except KeyError:
@@ -611,6 +703,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                 except ValueError:
                     gevent.sleep(1)
 
+            if _count >= _maxwait:
+                raise x2go_exceptions.X2goControlSessionException('x2golistsessions command failed after we have tried 20 times')
+
             # update internal variables when list_sessions() is called
             for _session_name, _session_info in self.associated_terminals.items():
                 if _session_name not in _listsessions.keys():
diff --git a/x2go/backends/proxy/_nx3.py b/x2go/backends/proxy/_nx3.py
index 38786af..2a5b8a0 100644
--- a/x2go/backends/proxy/_nx3.py
+++ b/x2go/backends/proxy/_nx3.py
@@ -80,7 +80,7 @@ class X2goProxyNX3(base.X2goProxyBASE):
                 "nx/nx" ,
                 "retry=5",
                 "composite=1",
-                "connect=localhost",
+                "connect=127.0.0.1",
                 "clipboard=1",
                 "cookie=%s" % self.session_info.cookie,
                 "port=%d" % self.session_info.graphics_port,
@@ -91,7 +91,7 @@ class X2goProxyNX3(base.X2goProxyBASE):
                 "nx/nx" ,
                 "retry=5",
                 "composite=1",
-                "connect=localhost",
+                "connect=127.0.0.1",
                 "clipboard=1",
                 "cookie=%s" % self.session_info.cookie,
                 "port=%d" % self.session_info.graphics_port,
@@ -167,12 +167,12 @@ options=%s""" % ( self.proxy_options['xkbrules'],
 
     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 mode is server, cookie=%s, host=127.0.0.1, 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:
+        if self.ok():
             self.logger('NX3 proxy is up and running.', loglevel=log.loglevel_INFO)
         else:
             self.logger('Bringing up NX3 proxy failed.', loglevel=log.loglevel_ERROR)
diff --git a/x2go/backends/proxy/base.py b/x2go/backends/proxy/base.py
index ab48b39..9558b83 100644
--- a/x2go/backends/proxy/base.py
+++ b/x2go/backends/proxy/base.py
@@ -131,12 +131,17 @@ class X2goProxyBASE(threading.Thread):
         if left open.
 
         """
-        if self.proxy is not None and self.proxy.poll() is None:
+        if self.proxy:
             self.logger('Shutting down X2go proxy subprocess', loglevel=log.loglevel_DEBUG)
-            self.proxy.kill()
+            try:
+                self.proxy.kill()
+            except OSError, e:
+                self.logger('X2go proxy shutdown gave a message that we may ignore: %s' % str(e), loglevel=log.loglevel_WARN)
+            self.proxy = None
         if self.fw_tunnel is not None:
             self.logger('Shutting down Paramiko/SSH forwarding tunnel', loglevel=log.loglevel_DEBUG)
             forward.stop_forward_tunnel(self.fw_tunnel)
+            self.fw_tunnel = None
         if self.session_log_stdout is not None:
             self.session_log_stdout.close()
         if self.session_log_stderr is not None:
@@ -148,7 +153,8 @@ class X2goProxyBASE(threading.Thread):
 
         """
         self._keepalive = False
-        gevent.sleep(1)
+        # wait for thread loop to finish...
+        gevent.sleep(.5)
         self._tidy_up()
 
     def run(self):
@@ -177,7 +183,8 @@ class X2goProxyBASE(threading.Thread):
                                                       remote_port=self.session_info.graphics_port, 
                                                       ssh_transport=self.ssh_transport, 
                                                       session_instance=self.session_instance,
-                                                      logger=self.logger, )
+                                                      logger=self.logger,
+                                                     )
 
         # update the proxy port in PROXY_ARGS
         self._update_local_proxy_socket(local_graphics_port)
@@ -206,13 +213,13 @@ class X2goProxyBASE(threading.Thread):
                                               shell=_shell)
 
         while self._keepalive:
-            gevent.sleep(.5)
+            gevent.sleep(.2)
 
         if _X2GOCLIENT_OS == 'Windows':
             _stdin.close()
-        self.logger('terminating proxy: %s' % p, loglevel=log.loglevel_DEBUG)
         try:
             p.terminate()
+            self.logger('terminating proxy: %s' % p, loglevel=log.loglevel_DEBUG)
         except OSError, e:
             if e.errno == 3:
                 # No such process
@@ -254,7 +261,31 @@ class X2goProxyBASE(threading.Thread):
 
         """
         threading.Thread.start(self)
-        while self.proxy is None:
-            gevent.sleep(.1)
+
+        # wait for proxy to get started
+        _count = 0
+        _maxwait = 40
+        while self.proxy is None and _count < _maxwait:
+            _count += 1
+            self.logger('waiting for proxy to come up: 0.4s x %s' % _count, loglevel=log.loglevel_DEBUG)
+            gevent.sleep(.4)
+
+        # also wait for fw_tunnel to become active
+        _count = 0
+        _maxwait = 40
+        while not self.fw_tunnel.is_active and _count < _maxwait:
+            _count += 1
+            self.logger('waiting for port fw tunnel to come up: 0.5s x %s' % _count, loglevel=log.loglevel_DEBUG)
+            gevent.sleep(.5)
 
         return self.proxy
+
+    def ok(self):
+        """\
+        Check if a proxy instance is up and running.
+
+        @return: Proxy state (C{True} or C{False})
+        @rtype C{bool}
+
+        """
+        return bool(self.proxy and self.proxy.poll() is None)
diff --git a/x2go/backends/terminal/_stdout.py b/x2go/backends/terminal/_stdout.py
index 91a8059..4c439fd 100644
--- a/x2go/backends/terminal/_stdout.py
+++ b/x2go/backends/terminal/_stdout.py
@@ -46,7 +46,7 @@ import x2go.defaults as defaults
 import x2go.utils as utils
 import x2go.x2go_exceptions as x2go_exceptions
 
-from x2go.cleanup import x2go_cleanup 
+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 X2GOCLIENT_OS as _X2GOCLIENT_OS
@@ -61,6 +61,8 @@ from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
 from x2go.backends.proxy import X2goProxy as _X2goProxy
 from x2go.backends.printing import X2goClientPrinting as _X2goClientPrinting
 
+_local_color_depth = utils.local_color_depth()
+
 def _rewrite_cmd(cmd, params=None):
 
     # start with an empty string
@@ -72,9 +74,9 @@ def _rewrite_cmd(cmd, params=None):
 
     if (cmd == 'RDP') and (type(params) == X2goSessionParams):
         if params.geometry == 'fullscreen':
-            cmd = 'rdesktop -f -N %s %s' % (params.rdp_options, params.rdp_server)
+            cmd = 'rdesktop -f -N %s %s -a %s' % (params.rdp_options, params.rdp_server, params.depth)
         else:
-            cmd = 'rdesktop -g %s -N %s %s' % (params.geometry, params.rdp_options, params.rdp_server)
+            cmd = 'rdesktop -g %s -N %s %s -a %s' % (params.geometry, params.rdp_options, params.rdp_server, params.depth)
 
     # place quot marks around cmd if not empty string
     if cmd:
@@ -112,10 +114,13 @@ class X2goSessionParams(object):
         session_type = self.session_type
         cmd = self.cmd
 
-        if session_type == "desktop":
+        if session_type in ("D", "desktop"):
             self.session_type = 'D'
             return
-        if cmd:
+        elif session_type in ("S", "shared", "shadow"):
+            self.session_type = 'S'
+            return
+        elif cmd:
             if cmd == 'RDP':
                 self.session_type = 'R'
                 return
@@ -176,7 +181,7 @@ class X2goTerminalSessionSTDOUT(object):
 
     """
     def __init__(self, control_session, session_info=None,
-                 geometry="800x600", depth=24, link="adsl", pack="16m-jpeg-9", 
+                 geometry="800x600", depth=_local_color_depth, link="adsl", pack="16m-jpeg-9", 
                  cache_type="unix-kde", 
                  keyboard='', kblayout='null', kbtype='null/null',
                  session_type="application", snd_system='pulse', snd_port=4713, cmd=None,
@@ -216,7 +221,7 @@ class X2goTerminalSessionSTDOUT(object):
         @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)
+        @param session_type: either C{desktop}, C{application} (rootless session) or C{shared}
         @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})
@@ -257,6 +262,7 @@ class X2goTerminalSessionSTDOUT(object):
         self.reverse_tunnels = {}
 
         self.print_queue = None
+        self.mimebox_queue = None
 
         if logger is None:
             self.logger = log.X2goLogger(loglevel=loglevel)
@@ -273,7 +279,6 @@ class X2goTerminalSessionSTDOUT(object):
         self.params = X2goSessionParams()
 
         self.params.geometry = str(geometry)
-        self.params.depth = str(depth)
         self.params.link = str(link)
         self.params.pack = str(pack)
         self.params.cache_type = str(cache_type)
@@ -283,6 +288,7 @@ class X2goTerminalSessionSTDOUT(object):
         self.params.kbtype = str(kbtype)
         self.params.snd_system = str(snd_system)
         self.params.cmd = str(cmd)
+        self.params.depth = str(depth)
 
         self.params.rdp_server = str(rdp_server)
         self.params.rdp_options = str(rdp_options)
@@ -324,8 +330,7 @@ class X2goTerminalSessionSTDOUT(object):
 
     def _x2go_tidy_up(self):
 
-        if self.proxy is not None:
-            self.release_proxy()
+        self.release_proxy()
 
         try:
             if self.control_session.get_transport() is not None:
@@ -339,6 +344,9 @@ class X2goTerminalSessionSTDOUT(object):
             if self.print_queue is not None:
                 self.print_queue.__del__()
 
+            if self.mimebox_queue is not None:
+                self.mimebox_queue.__del__()
+
         except AttributeError:
             pass
 
@@ -381,7 +389,7 @@ class X2goTerminalSessionSTDOUT(object):
                 ###
                 if os.path.exists(os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)):
                     # 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) + \
+                    cmd_line = "echo 'default-server=127.0.0.1:%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)
 
@@ -389,7 +397,7 @@ class X2goTerminalSessionSTDOUT(object):
 
                     # start reverse SSH tunnel for pulse stream
                     _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 
-                                                       remote_host='localhost', 
+                                                       remote_host='127.0.0.1', 
                                                        remote_port=self.snd_port, 
                                                        ssh_transport=self.control_session.get_transport(),
                                                        session_instance=self.session_instance,
@@ -398,7 +406,6 @@ class X2goTerminalSessionSTDOUT(object):
                 else:
                     if self.client_instance:
                         self.client_instance.HOOK_on_sound_tunnel_failed(profile_name=self.profile_name, session_name=self.session_info.name)
-
             elif self.params.snd_system == 'arts':
                 ###
                 ### ARTSD AUDIO
@@ -415,7 +422,7 @@ class X2goTerminalSessionSTDOUT(object):
 
                 # start reverse SSH tunnel for pulse stream
                 _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 
-                                                   remote_host='localhost', 
+                                                   remote_host='127.0.0.1', 
                                                    remote_port=self.snd_port, 
                                                    ssh_transport=self.control_session.get_transport(),
                                                    session_instance=self.session_instance,
@@ -668,6 +675,16 @@ class X2goTerminalSessionSTDOUT(object):
         (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
         self.logger('x2gomountdirs output is : %s' % stdout.read().split('\n'), log.loglevel_NOTICE)
 
+    def color_depth(self):
+        """\
+        Retrieve the session's color depth.
+
+        @return: the session's color depth
+        @rtype: C{int}
+
+        """
+        return self.params.depth
+
     def has_command(self, cmd):
         """\
         Verify if the command <cmd> exists on the X2go server.
@@ -679,6 +696,8 @@ class X2goTerminalSessionSTDOUT(object):
 
         if cmd in _X2GO_GENERIC_APPLICATIONS:
             return True
+        elif 'XSHAD' in cmd:
+            return True
         elif cmd:
             test_cmd = 'which %s && echo OK' % os.path.basename(cmd.split()[0])
 
@@ -720,6 +739,10 @@ class X2goTerminalSessionSTDOUT(object):
             # do not run command when in XDMCP mode...
             return None
 
+        if 'XSHAD' in cmd:
+            # do not run command when in DESKTOP SHARING mode...
+            return None
+
         self.params.update({'cmd': cmd})
 
         cmd_line = [ "setsid x2goruncommand", 
@@ -753,7 +776,7 @@ class X2goTerminalSessionSTDOUT(object):
         @rtype: bool
 
         """
-        return bool(self.session_info.name and (self.proxy_subprocess and self.proxy_subprocess.poll() is None))
+        return bool(self.session_info.name and self.proxy.ok())
 
     def is_running(self):
         """\
@@ -804,8 +827,6 @@ class X2goTerminalSessionSTDOUT(object):
         if self.params.kblayout or self.params.kbtype:
             setkbd = "1"
 
-        self.params.update()
-
         cmd_line = [ "x2gostartagent",
                      str(self.params.geometry),
                      str(self.params.link),
@@ -823,7 +844,15 @@ class X2goTerminalSessionSTDOUT(object):
 
         (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
 
-        self.session_info.initialize(stdout.read(),
+        _stdout = stdout.read()
+        _stderr = stderr.read()
+
+        # if the first line of stdout is a "DEN(Y)" string then we will presume that
+        # we tried to use X2go desktop sharing and the sharing was rejected
+        if "ACCESS DENIED" in _stderr and "XSHAD" in _stderr:
+            raise x2go_exceptions.X2goDesktopSharingException('X2go desktop sharing has been denied by the remote user')
+
+        self.session_info.initialize(_stdout,
                                      username=self.control_session.remote_username(),
                                      hostname=self.control_session.get_transport().getpeername(),
                                     )
@@ -891,6 +920,7 @@ class X2goTerminalSessionSTDOUT(object):
         self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home, 
                                                                 self.session_info.name,
                                                                )
+        self.params.depth = self.session_info.name.split('_')[2][2:]
         # on a session resume the user name comes in as a user ID. We have to translate this...
         self.session_info.username = self.control_session.remote_username()
         return self.ok()
@@ -932,7 +962,5 @@ class X2goTerminalSessionSTDOUT(object):
         STILL UNDOCUMENTED
 
         """
-        try:
-            self.proxy.__del__()
-        except:
-            pass
+        self.proxy.__del__()
+
diff --git a/x2go/cache.py b/x2go/cache.py
index e91e54b..2a8643b 100644
--- a/x2go/cache.py
+++ b/x2go/cache.py
@@ -37,14 +37,15 @@ class X2goListSessionsCache(object):
     recommended to enable the L{X2goListSessionsCache}. This can be done by calling
     the constructor of the L{X2goClient} class.
 
-    The session list cache gets updated in regularly intervals by a threaded
-    L{X2goSessionGuardian} instance. For the session list update, the X2go server
-    command C{x2golistsessions} is called and the command's stdout is cached
-    in the session list cache.
+    The session list and desktop cache gets updated in regular intervals by a threaded
+    L{X2goSessionGuardian} instance. For the session list and desktop list update, the
+    X2go server commands C{x2golistsessions} and C{x2godesktopsessions} are called and
+    the command's stdout is cached in the session list cache.
 
-    Whenever your client application needs access to the server's session list,
-    the session list cache is queried instead. This assures that the server's
-    session list is available without delay, even on slow internet connections.
+    Whenever your client application needs access to either the server's session list
+    or the server's desktop list the session cache is queried instead. This assures that
+    the server's session/desktop list is available without delay, even on slow internet
+    connections.
 
     """
     x2go_listsessions_cache = {}
@@ -93,28 +94,69 @@ class X2goListSessionsCache(object):
             if profile_name not in self.client_instance.client_connected_profiles(return_profile_names=True):
                 del self.x2go_listsessions_cache[profile_name]
 
-    def update_all(self):
+    def update_all(self, update_sessions=True, update_desktops=False):
         """\
         Update L{X2goListSessionsCache} for all connected session profiles.
 
+        @param update_sessions: cache recent session lists from all connected servers
+        @type update_session: C{bool}
+        @param update_desktops: cache recent desktop lists from all connected servers
+        @type update_desktops: C{bool}
+
         """
         for profile_name in self.client_instance.client_connected_profiles(return_profile_names=True):
-            self.update(profile_name)
+            self.update(profile_name, update_sessions=update_sessions, update_desktops=update_desktops)
 
         self.check_cache()
 
-    def update(self, profile_name):
+    def update(self, profile_name, update_sessions=True, update_desktops=False):
         """\
-        Update the L{X2goListSessionsCache} for session profile C{profile_name}.
+        Update L{X2goListSessionsCache} (i.e. session/desktops) for session profile C{profile_name}.
 
         @param profile_name: name of profile to update
         @type profile_name: C{str}
+        @param update_sessions: cache recent session list from server
+        @type update_session: C{bool}
+        @param update_desktops: cache recent desktop list from server
+        @type update_desktops: C{bool}
 
         """
         self.last_listsessions_cache = copy.deepcopy(self.x2go_listsessions_cache)
         control_session = self.client_instance.client_control_session_of_profile_name(profile_name)
+        if not self.x2go_listsessions_cache.has_key(profile_name):
+            self.x2go_listsessions_cache[profile_name] = {'sessions': None, 'desktops': None, }
+        if update_sessions:
+            self._update_sessions(profile_name, control_session)
+        if update_desktops:
+            self._update_desktops(profile_name, control_session)
+
+    def _update_desktops(self, profile_name, control_session):
+        """\
+        Update session lists of L{X2goListSessionsCache} for session profile C{profile_name}.
+
+        @param profile_name: name of profile to update
+        @type profile_name: C{str}
+
+        """
         try:
-            self.x2go_listsessions_cache[profile_name] = control_session.list_sessions()
+            self.x2go_listsessions_cache[profile_name]['desktops'] = control_session.list_desktops()
+        except x2go_exceptions.X2goControlSessionException, e:
+            try:
+                del self.x2go_listsessions_cache[profile_name]
+            except KeyError:
+                pass
+            raise e
+
+    def _update_sessions(self, profile_name, control_session):
+        """\
+        Update desktop list of L{X2goListSessionsCache} for session profile C{profile_name}.
+
+        @param profile_name: name of profile to update
+        @type profile_name: C{str}
+
+        """
+        try:
+            self.x2go_listsessions_cache[profile_name]['sessions'] = control_session.list_sessions()
         except x2go_exceptions.X2goControlSessionException, e:
             try:
                 del self.x2go_listsessions_cache[profile_name]
@@ -124,8 +166,8 @@ class X2goListSessionsCache(object):
 
     def list_sessions(self, session_uuid):
         """\
-        Retrieve the current cache content of L{X2goListSessionsCache} for a given L{X2goSession} instance
-        (specified by its unique session UUID).
+        Retrieve a session list from the current cache content of L{X2goListSessionsCache}
+        for a given L{X2goSession} instance (specified by its unique session UUID).
 
         @param session_uuid: unique identifier of session to query cache for
         @type session_uuid: C{str}
@@ -135,11 +177,29 @@ class X2goListSessionsCache(object):
         """
         profile_name = self.client_instance.get_session_profile_name(session_uuid)
         if self.is_cached(session_uuid=session_uuid):
-            return self.x2go_listsessions_cache[profile_name]
+            return self.x2go_listsessions_cache[profile_name]['sessions']
+        else:
+            return None
+
+    def list_desktops(self, session_uuid):
+        """\
+        Retrieve a list of available desktop sessions from the current cache content of
+        L{X2goListSessionsCache} for a given L{X2goSession} instance (specified by its 
+        unique session UUID).
+
+        @param session_uuid: unique identifier of session to query cache for
+        @type session_uuid: C{str}
+        @return: a list of strings representing X2go desktop sessions available for sharing
+        @rtype: C{list}
+
+        """
+        profile_name = self.client_instance.get_session_profile_name(session_uuid)
+        if self.is_cached(session_uuid=session_uuid):
+            return self.x2go_listsessions_cache[profile_name]['desktops']
         else:
             return None
 
-    def is_cached(self, profile_name=None, session_uuid=None):
+    def is_cached(self, profile_name=None, session_uuid=None, cache_type=None):
         """\
         Check if session list is cached.
 
@@ -151,4 +211,9 @@ class X2goListSessionsCache(object):
         """
         if profile_name is None and session_uuid:
             profile_name = self.client_instance.get_session_profile_name(session_uuid)
-        return self.x2go_listsessions_cache.has_key(profile_name)
+        _is_profile_cached = self.x2go_listsessions_cache.has_key(profile_name)
+        _is_cache_type_cached = _is_profile_cached and self.x2go_listsessions_cache[profile_name].has_key(cache_type)
+        if cache_type is None:
+            return _is_profile_cached
+        else:
+            return _is_cache_type_cached
\ No newline at end of file
diff --git a/x2go/client.py b/x2go/client.py
index c6b41ff..49abce2 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -193,6 +193,7 @@ class X2goClient(object):
                  start_pulseaudio=False,
                  use_listsessions_cache=False, 
                  auto_update_listsessions_cache=False,
+                 auto_update_listdesktops_cache=False,
                  auto_update_sessionregistry=False,
                  auto_register_sessions=False,
                  refresh_interval=5,
@@ -225,10 +226,12 @@ class X2goClient(object):
         @type start_xserver: C{bool}
         @param start_pulseaudio: start Pulseaudio daemon when registering an L{X2goClient} instance
         @type start_pulseaudio: C{bool}
-        @param use_listsessions_cache: activate the X2go session list cache (L{X2goListSessionsCache})
+        @param use_listsessions_cache: activate the X2go session list cache in (L{X2goListSessionsCache})
         @type use_listsessions_cache: C{bool}
         @param auto_update_listsessions_cache: activate automatic updates of the X2go session list cache (L{X2goListSessionsCache})
         @type auto_update_listsessions_cache: C{bool}
+        @param auto_update_listdesktops_cache: activate automatic updates of desktop lists in (L{X2goListSessionsCache})
+        @type auto_update_listdesktops_cache: C{bool}
         @param auto_update_sessionregistry: activate automatic updates of the X2go session registry
         @type auto_update_sessionregistry: C{bool}
         @param auto_register_sessions: activate automatic X2go session registration
@@ -309,16 +312,20 @@ class X2goClient(object):
 
         self.auto_register_sessions = auto_register_sessions
         self.session_registry = X2goSessionRegistry(self, logger=self.logger)
-        self.session_guardian = X2goSessionGuardian(self, auto_update_listsessions_cache=auto_update_listsessions_cache & use_listsessions_cache, 
+        self.session_guardian = X2goSessionGuardian(self, auto_update_listsessions_cache=auto_update_listsessions_cache & use_listsessions_cache,
+                                                    auto_update_listdesktops_cache=auto_update_listdesktops_cache & use_listsessions_cache,
                                                     auto_update_sessionregistry=auto_update_sessionregistry,
                                                     auto_register_sessions=auto_register_sessions, 
                                                     refresh_interval=refresh_interval,
                                                     logger=self.logger
                                                    )
+
         if use_listsessions_cache:
             self.listsessions_cache = X2goListSessionsCache(self, logger=self.logger)
 
         self.use_listsessions_cache = use_listsessions_cache
+        self.auto_update_listsessions_cache = auto_update_listsessions_cache
+        self.auto_update_listdesktops_cache = auto_update_listdesktops_cache
 
     # user hooks for detecting/notifying what happened during application runtime
     def HOOK_no_known_xserver_found(self):
@@ -963,6 +970,23 @@ class X2goClient(object):
     with_session = __get_session
     """Alias for L{get_session()}."""
 
+    def get_session_of_session_name(self, session_name, return_object=False):
+        """\
+        Retrieve session UUID or L{X2goSession} for session name
+        <session_name> from the session registry.
+
+        @param session_name: the X2go session's UUID registry hash
+        @type session_name: C{str}
+        @param return_object: session UUID hash or L{X2goSession} instance wanted?
+        @type return_object: C{bool}
+
+        @return: the X2go session's UUID registry hash or L{X2goSession} instance
+        @rtype: C{str} or L{X2goSession} instance
+
+        """
+        return self.session_registry.get_session_of_session_name(session_name=session_name, return_object=return_object)
+    __get_session_of_session_name = get_session_of_session_name
+
     def get_session_name(self, session_uuid):
         """\
         Retrieve the server-side X2go session name for the session that has
@@ -1049,7 +1073,9 @@ class X2goClient(object):
                                                                force_password_auth=force_password_auth,
                                                               )
         if self.auto_register_sessions:
-            self.session_registry.register_available_server_sessions(profile_name=self.get_session_profile_name(session_uuid))
+            self.session_registry.register_available_server_sessions(profile_name=self.get_session_profile_name(session_uuid),
+                                                                     newly_connected=True,
+                                                                    )
     __connect_session = connect_session
 
     def disconnect_session(self, session_uuid):
@@ -1117,9 +1143,51 @@ class X2goClient(object):
         @rtype: C{bool}
 
         """
-        return self.session_registry(session_uuid).start()
+        # prevent the newly started session from being registered twice
+        if self.auto_register_sessions:
+            self.session_registry.disable_session_auto_registration()
+
+        # start the actual session
+        _retval = self.session_registry(session_uuid).start()
+
+        # re-enable session auto-registration...
+        if self.auto_register_sessions:
+            self.session_registry.enable_session_auto_registration()
+
+        return _retval
     __start_session = start_session
 
+    def share_desktop_session(self, session_uuid, desktop=None, user=None, display=None, share_mode=0):
+        """\
+        Share another already running desktop session. Desktop sharing can be run
+        in two different modes: view-only and full-access mode. Like new sessions
+        a to-be-shared session has be registered first with the L{X2goClient}
+        instance.
+
+        @param desktop: desktop ID of a sharable desktop in format <user>@<display>
+        @type desktop: C{str}
+        @param user: user name and display number can be given separately, here give the
+            name of the user who wants to share a session with you.
+        @type user: C{str}
+        @param display: user name and display number can be given separately, here give the
+            number of the display that a user allows you to be shared with.
+        @type display: C{str}
+        @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
+        @type share_mode: C{int}
+
+        @return: True if the session could be successfully shared.
+        @rtype: C{bool}
+
+        """
+        _desktop = desktop or "%s@%s" % (user, display)
+
+        # X2goClient.list_desktops() uses caching (if enabled, so we prefer lookups here...
+        if _desktop not in self.list_desktops(session_uuid):
+            raise x2go_exceptions.X2goDesktopSharingException('No such desktop ID: %s' % _desktop)
+
+        return self.session_registry(session_uuid).share_desktop(desktop=desktop, user=user, display=display, share_mode=share_mode, check_desktop_list=False)
+    __share_desktop_session = share_desktop_session
+
     def resume_session(self, session_uuid=None, session_name=None):
         """\
         Resume or continue a suspended / running X2go session on a
@@ -1844,7 +1912,7 @@ class X2goClient(object):
         if raw:
             return self.session_registry(session_uuid).list_sessions(raw=raw)
 
-        if not self.use_listsessions_cache or no_cache:
+        if not self.use_listsessions_cache or not self.auto_update_listsessions_cache or no_cache:
             _session_list = self.session_registry(session_uuid).list_sessions()
         elif refresh_cache:
             self.update_cache_by_session_uuid(session_uuid)
@@ -1852,7 +1920,7 @@ class X2goClient(object):
         else:
             # if there is no cache for this session_uuid available, make sure the cache gets updated
             # before reading from it...
-            if self.use_listsessions_cache and (not self.listsessions_cache.is_cached(session_uuid=session_uuid)):
+            if self.use_listsessions_cache and (not self.listsessions_cache.is_cached(session_uuid=session_uuid, cache_type=('sessions'))):
                 self.__update_cache_by_session_uuid(session_uuid)
             _session_list = self.listsessions_cache.list_sessions(session_uuid)
 
@@ -1866,6 +1934,63 @@ class X2goClient(object):
         return _session_list
     __list_sessions = list_sessions
 
+    def list_desktops(self, session_uuid=None, 
+                      profile_name=None, profile_id=None,
+                      no_cache=False, refresh_cache=False,
+                      raw=False):
+        """\
+        Use the X2go session registered under C{session_uuid} to
+        retrieve a list of X2go desktop sessions that are available
+        for desktop sharing.
+
+        Before calling this method you have to setup a pro forma remote X2go session
+        with L{X2goClient.register_session()} (even if you do not intend to open
+        a real X2go session window on the remote server) and connect to this session (with
+        L{X2goClient.connect_session()}.
+
+        @param session_uuid: the X2go session's UUID registry hash
+        @type session_uuid: C{str}
+        @param profile_name: use profile name instead of <session_uuid>
+        @type profile_name: C{str}
+        @param profile_id: use profile id instead of <profile_name> or <session_uuid>
+        @type profile_id: C{str}
+        @param no_cache: do not get the session list from cache, query the X2go server directly
+        @type no_cache: C{bool}
+        @param raw: output the session list in X2go's raw C{x2golistsessions} format
+        @type raw: C{bool}
+
+        """
+        if profile_id is not None:
+            profile_name = self.to_profile_name(profile_id)
+
+        if profile_name is not None:
+
+            _connected_sessions = self.client_connected_sessions_of_profile_name(profile_name, return_objects=True)
+            if _connected_sessions:
+                # it does not really matter which session to use for getting a server-side session list
+                # thus, we simply grab the first that comes in...
+                session_uuid = _connected_sessions[0].get_uuid()
+            else:
+                raise x2go_exceptions.X2goClientException('profile ,,%s\'\' is not connected' % profile_name)
+
+        elif session_uuid is not None:
+            pass
+        else:
+            raise x2go_exceptions.X2goClientException('must either specify session UUID or profile name')
+
+        if raw:
+            return self.session_registry(session_uuid).list_desktops(raw=raw)
+
+        if not self.use_listsessions_cache or not self.auto_update_listdesktops_cache or no_cache:
+            _desktop_list = self.session_registry(session_uuid).list_desktops()
+        else:
+            if self.use_listsessions_cache and (not self.listsessions_cache.is_cached(session_uuid=session_uuid, cache_types=('desktops'))):
+                self.__update_cache_by_session_uuid(session_uuid, update_sessions=False, update_desktops=True)
+            _desktop_list = self.listsessions_cache.list_desktops(session_uuid)
+
+        return _desktop_list
+    __list_desktops = list_desktops
+
     ###
     ### Provide access to config file class objects
     ### 
@@ -2020,8 +2145,17 @@ class X2goClient(object):
 
         """
         _retval = False
+        _session_uuid_list = []
+        # disconnect individual sessions and make a list of session UUIDs for later cleanup (s. below)
         for s in self.session_registry.registered_sessions_of_profile_name(profile_name, return_objects=True):
+            _session_uuid_list.append(s.get_uuid())
             _retval = s.disconnect() | _retval
+
+        # tell session registry to forget attached sessions completely on disconnect action
+        for uuid in _session_uuid_list:
+            self.session_registry.forget(uuid)
+
+        # clear cache, as well...
         if self.use_listsessions_cache:
             self.listsessions_cache.delete(profile_name)
         return _retval
@@ -2076,45 +2210,82 @@ class X2goClient(object):
     __update_sessionregistry_status_all_profiles = update_sessionregistry_status_all_profiles
 
 
-    def update_cache_by_profile_name(self, profile_name):
+    def update_cache_by_profile_name(self, profile_name, cache_types=('sessions'), update_sessions=None, update_desktops=None):
         """\
         Update the session list cache by profile name.
 
         @param profile_name: the X2go session profile name
         @type profile_name: C{str}
+        @param cache_types: specify what cache type to update (available: C{sessions}, C{desktops})
+        @type cache_types: C{tuple} or C{list}
+        @param update_sessions: instead of giving a list of cache types, plainly say C{True} here, if 
+            you want to update sessions in the session list cache.
+        @type update_session: C{bool}
+        @param update_desktops: instead of giving a list of cache types, plainly say C{True} here, if 
+            you want to update available desktops in the desktop list cache.
+        @type update_desktops: C{bool}
 
         """
         if self.listsessions_cache is not None:
+            _update_sessions = ('sessions' in cache_types) or update_sessions
+            _update_desktops = ('desktops' in cache_types) or update_desktops
             try:
-                self.listsessions_cache.update(profile_name)
+                self.listsessions_cache.update(profile_name, update_sessions=_update_sessions, update_desktops=_update_desktops)
             except x2go_exceptions.X2goControlSessionException:
                 if self.disconnect_profile(profile_name):
                     self.HOOK_on_control_session_death(profile_name)
     __update_cache_by_profile_name = update_cache_by_profile_name
 
-    def update_cache_by_session_uuid(self, session_uuid):
+    def update_cache_by_session_uuid(self, session_uuid, cache_types=('sessions'), update_sessions=None, update_desktops=None):
         """\
         Update the session list cache of a specific L{X2goSession} instance with
         session identifier <session_uuid>.
 
         @param session_uuid: the X2go session's UUID registry hash
         @type session_uuid: C{str}
+        @param cache_types: specify what cache type to update (available: C{sessions}, C{desktops})
+        @type cache_types: C{tuple} or C{list}
+        @param update_sessions: instead of giving a list of cache types, plainly say C{True} here, if 
+            you want to update sessions in the session list cache.
+        @type update_session: C{bool}
+        @param update_desktops: instead of giving a list of cache types, plainly say C{True} here, if 
+            you want to update available desktops in the desktop list cache.
+        @type update_desktops: C{bool}
 
         """
         profile_name = self.get_session_profile_name(session_uuid)
-        self.__update_cache_by_profile_name(profile_name)
+        self.__update_cache_by_profile_name(profile_name,
+                                            cache_types=cache_types,
+                                            update_sessions=update_sessions,
+                                            update_desktops=update_desktops,
+                                           )
     __update_cache_by_session_uuid = update_cache_by_session_uuid
 
-    def update_cache_all_profiles(self):
+    def update_cache_all_profiles(self, cache_types=('sessions'), update_sessions=None, update_desktops=None):
         """\
         Update the session list cache of all session profiles.
 
+        @param cache_types: specify what cache type to update (available: C{sessions}, C{desktops})
+        @type cache_types: C{tuple} or C{list}
+        @param update_sessions: instead of giving a list of cache types, plainly say C{True} here, if 
+            you want to update sessions in the session list cache.
+        @type update_session: C{bool}
+        @param update_desktops: instead of giving a list of cache types, plainly say C{True} here, if 
+            you want to update available desktops in the desktop list cache.
+        @type update_desktops: C{bool}
+
         """
         if self.listsessions_cache is not None:
             for profile_name in self.client_connected_profiles(return_profile_names=True):
-                self.__update_cache_by_profile_name(profile_name)
+                self.__update_cache_by_profile_name(profile_name,
+                                                    cache_types=cache_types,
+                                                    update_sessions=update_sessions,
+                                                    update_desktops=update_desktops,
+                                                   )
 
+            # remove profiles that are not connected any more from cache object
             self.listsessions_cache.check_cache()
+
     __update_cache_all_profiles = update_cache_all_profiles
 
     def register_available_server_sessions_by_profile_name(self, profile_name):
diff --git a/x2go/defaults.py b/x2go/defaults.py
index 3ad6fa8..37bb434 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -29,6 +29,9 @@ import os
 import paramiko
 import platform
 
+## X2go imports
+import utils
+
 ##
 ## Common X2go defaults
 ##
@@ -81,7 +84,6 @@ else:
     class OSNotSupportedException(exceptions.StandardError): pass
     raise OSNotSupportedException('Platform %s is not supported' % platform.system())
 
-
 ##
 ## control and terminal session backend as well as session info and proxy backend defaults
 ##
@@ -393,3 +395,7 @@ X2GO_MIMEBOX_EXTENSIONS_BLACKLIST = [
     'JS', 'PY', 'PL', 'SH',
 ]
 """Black-listed MIME box file extenstions."""
+
+# X2go desktop sharing
+X2GO_SHARE_VIEWONLY=0
+X2GO_SHARE_FULLACCESS=1
diff --git a/x2go/forward.py b/x2go/forward.py
index e683904..34466d0 100644
--- a/x2go/forward.py
+++ b/x2go/forward.py
@@ -34,6 +34,7 @@ from gevent.server import StreamServer
 
 # Python X2go modules
 import log
+from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
 import x2go_exceptions
 
 class X2goFwServer(StreamServer):
@@ -49,7 +50,7 @@ class X2goFwServer(StreamServer):
         """\
         @param listener: listen on TCP/IP socket C{(<IP>, <Port>)}
         @type listener: C{tuple}
-        @param remote_host: hostname or IP of remote host (in case of X2go mostly localhost)
+        @param remote_host: hostname or IP of remote host (in case of X2go mostly 127.0.0.1)
         @type remote_host: C{str}
         @param remote_port: port of remote host
         @type remote_port: C{int}
@@ -77,6 +78,8 @@ class X2goFwServer(StreamServer):
         self.ssh_transport = ssh_transport
         self.session_instance = session_instance
 
+        self.fw_socket = None
+
         StreamServer.__init__(self, listener, self.x2go_forward_tunnel_handle)
 
     def x2go_forward_tunnel_handle(self, fw_socket, address):
@@ -89,15 +92,37 @@ class X2goFwServer(StreamServer):
         @type address: C{tuple}
 
         """
-        try:
-            self.chan = self.ssh_transport.open_channel('direct-tcpip',
-                                                        (self.chain_host, self.chain_port),
-                                                        fw_socket.getpeername())
-            chan_peername = self.chan.getpeername()
-        except Exception, e:
-            self.logger('incoming request to %s:%d failed: %s' % (self.chain_host,
-                                                                  self.chain_port,
-                                                                  repr(e)), loglevel=log.loglevel_ERROR)
+        self.fw_socket = fw_socket
+
+        _success = False
+        _count = 0
+        _maxwait = 20
+
+        while not _success and _count < _maxwait:
+
+            # it is recommended here to have passed on the session instance to this object...
+            if self.session_instance:
+                if not self.session_instance.is_connected():
+                    print 'HALLO'
+                    break
+
+            _count += 1
+            try:
+                self.chan = self.ssh_transport.open_channel('direct-tcpip',
+                                                            (self.chain_host, self.chain_port),
+                                                            self.fw_socket.getpeername())
+                chan_peername = self.chan.getpeername()
+                _success = True
+            except Exception, e:
+                self.logger('incoming request to %s:%d failed on attempt %d: %s' % (self.chain_host,
+                                                                                    self.chain_port,
+                                                                                    _count,
+                                                                                    repr(e)), 
+                                                                                    loglevel=log.loglevel_ERROR)
+                gevent.sleep(.4)
+
+        # once we are here, we can presume the tunnel to be active...
+        self.is_active = True
 
         if self.chan is None:
             self.logger('incoming request to [%s]:%d was rejected by the SSH server.' %
@@ -105,14 +130,14 @@ class X2goFwServer(StreamServer):
             if self.session_instance:
                 self.session_instance.HOOK_forwarding_tunnel_setup_failed(chain_host=self.chain_host, chain_port=self.chain_port)
             return
-        self.logger('connected!  Tunnel open %r -> %r -> %r' % (fw_socket.getpeername(),
+        else:
+            self.logger('connected!  Tunnel open %r -> %r -> %r' % (self.fw_socket.getpeername(),
                                                                 chan_peername, (self.chain_host, self.chain_port)),
                                                                 loglevel=log.loglevel_INFO)
+        self.keepalive = True
         try:
-            self.is_active = True
-            self.keepalive = True
             while self.keepalive:
-                r, w, x = select.select([fw_socket, self.chan], [], [])
+                r, w, x = select.select([self.fw_socket, self.chan], [], [])
                 if fw_socket in r:
                     data = fw_socket.recv(1024)
                     if len(data) == 0:
@@ -123,9 +148,8 @@ class X2goFwServer(StreamServer):
                     if len(data) == 0:
                         break
                     fw_socket.send(data)
-            try: self.chan.close()
-            except EOFError: pass
-            fw_socket.close()
+            self.close_channel()
+            self.close_socket()
         except socket.error:
             pass
 
@@ -138,12 +162,50 @@ class X2goFwServer(StreamServer):
         Close an open channel again.
 
         """
+        #if self.chan is not None and _X2GOCLIENT_OS != "Windows":
         if self.chan is not None:
-            self.chan.close()
+            try:
+                if _X2GOCLIENT_OS != 'Windows':
+                    self.chan.close()
+                self.chan = None
+            except EOFError:
+                pass
+
+    def close_socket(self):
+        """\
+        Close the forwarding tunnel's socket again.
+
+        """
+        _success = False
+        _count = 0
+        _maxwait = 20
+
+        # try at least <_maxwait> times
+        while not _success and _count < _maxwait:
+            _count += 1
+            try:
+                self.close_channel()
+                if self.fw_socket is not None:
+                    self.fw_socket.close()
+                _success = True
+            except socket.error:
+                gevent.sleep(.2)
+                self.logger('could not close fw_tunnel socket, try again (%s of %s)' % (_count, _maxwait), loglevel=log.loglevel_WARN)
+
+        if _count >= _maxwait:
+            self.logger('forwarding tunnel to [%s]:%d could not be closed properly' % (self.chain_host, self.chain_port), loglevel=log.loglevel_WARN)
 
+    def stop(self):
+        """\
+        Stop the forwarding tunnel.
+
+        """
+        self.close_socket()
+        StreamServer.stop(self)
 
-def start_forward_tunnel(local_host='localhost', local_port=22022,
-                         remote_host='localhost', remote_port=22,
+
+def start_forward_tunnel(local_host='127.0.0.1', local_port=22022,
+                         remote_host='127.0.0.1', remote_port=22,
                          ssh_transport=None, 
                          session_instance=None,
                          logger=None, ):
@@ -198,8 +260,9 @@ def stop_forward_tunnel(fw_server):
     """
     if fw_server is not None:
         fw_server.keepalive = False
-        fw_server.close_channel()
+        gevent.sleep(.5)
         fw_server.stop()
 
+
 if __name__ == '__main__':
     pass
diff --git a/x2go/guardian.py b/x2go/guardian.py
index 42943bd..a5791e3 100644
--- a/x2go/guardian.py
+++ b/x2go/guardian.py
@@ -54,6 +54,7 @@ class X2goSessionGuardian(threading.Thread):
 
     def __init__(self, client_instance, 
                  auto_update_listsessions_cache=False, 
+                 auto_update_listdesktops_cache=False, 
                  auto_update_sessionregistry=False,
                  auto_register_sessions=False,
                  refresh_interval=5,
@@ -61,6 +62,8 @@ class X2goSessionGuardian(threading.Thread):
         """\
         @param auto_update_listsessions_cache: let L{X2goSessionGuardian} refresh the session list cache for all L{X2goSession} objects
         @type auto_update_listsessions_cache: C{bool}
+        @param auto_update_listdesktops_cache: let L{X2goSessionGuardian} refresh desktop lists in the session list cache for all L{X2goSession} objects
+        @type auto_update_listdesktops_cache: C{bool}
         @param auto_update_sessionregistry: if set to C{True} the session status will be updated in regular intervals
         @type auto_update_sessionregistry: C{bool}
         @param auto_register_sessions: register new sessions automatically once they appear in the X2go session (e.g. 
@@ -83,6 +86,7 @@ class X2goSessionGuardian(threading.Thread):
 
         self.client_instance = client_instance
         self.auto_update_listsessions_cache = auto_update_listsessions_cache
+        self.auto_update_listdesktops_cache = auto_update_listdesktops_cache
         self.auto_update_sessionregistry = auto_update_sessionregistry
         self.auto_register_sessions = auto_register_sessions
         self.refresh_interval = refresh_interval
@@ -106,8 +110,13 @@ class X2goSessionGuardian(threading.Thread):
 
             if seconds % self.refresh_interval == 0:
 
+                self.logger('Entering X2go Guardian client management loop...', loglevel=log.loglevel_DEBUG)
+
+
                 if self.auto_update_listsessions_cache:
-                    self.client_instance.update_cache_all_profiles()
+                    self.client_instance.update_cache_all_profiles(update_sessions=self.auto_update_listsessions_cache, 
+                                                                   update_desktops=self.auto_update_listdesktops_cache,
+                                                                  )
 
                 if self.auto_update_sessionregistry and not self.auto_register_sessions:
                     self.client_instance.update_sessionregistry_status_all_profiles()
diff --git a/x2go/registry.py b/x2go/registry.py
index 7d3ee3b..5be0d1d 100644
--- a/x2go/registry.py
+++ b/x2go/registry.py
@@ -79,6 +79,9 @@ class X2goSessionRegistry(object):
         self.registry = {}
         self.control_sessions = {}
 
+        self._last_available_session_registration = None
+        self._skip_auto_registration = False
+
     def keys(self):
         """\
         A list of session registry keys.
@@ -106,7 +109,42 @@ class X2goSessionRegistry(object):
         @rtype: L{X2goSession} instance
 
         """
-        return self.registry[session_uuid]
+        try:
+            return self.registry[session_uuid]
+        except KeyError:
+            raise X2goSessionRegistryException('No session found for UUID %s' % session_uuid)
+
+    def disable_session_auto_registration(self):
+        """\
+        This method is used to temporarily skip auto-registration of newly appearing
+        X2go session on the server side. This is necessary during session startups to
+        assure that the session registry does not get filled with session UUID 
+        duplicates.
+
+        """
+        self._skip_auto_registration = True
+
+    def enable_session_auto_registration(self):
+        """\
+        This method is used to temporarily (re-)enable auto-registration of newly appearing
+        X2go session on the server side.
+
+        """
+        self._skip_auto_registration = False
+
+    def forget(self, session_uuid):
+        """\
+        Forget the complete record for session UUID C{session_uuid}.
+
+        @param session_uuid: the X2go session's UUID registry hash
+        @type session_uuid: C{str}
+
+        """
+        try:
+            del self.registry[session_uuid]
+            self.logger('Forgetting session UUID %s' % session_uuid, loglevel=log.loglevel_DEBUG)
+        except IndexError:
+            pass
 
     def get_profile_id(self, session_uuid):
         """\
@@ -179,7 +217,7 @@ class X2goSessionRegistry(object):
             _session_summary['terminated'] = None
         return _session_summary
 
-    def update_status(self, session_uuid=None, profile_name=None, profile_id=None, session_list=None, force_update=False):
+    def update_status(self, session_uuid=None, profile_name=None, profile_id=None, session_list=None, force_update=False, newly_connected=False):
         """\
         Update the session status for L{X2goSession} that is represented by a given session UUID hash,
         profile name or profile ID.
@@ -213,7 +251,9 @@ class X2goSessionRegistry(object):
 
         for _session_uuid in session_uuids:
 
-            self(_session_uuid).update_status(session_list=session_list, force_update=force_update)
+            if not self(_session_uuid).update_status(session_list=session_list, force_update=force_update):
+                # skip this run, as nothing has changed since the last time...
+                return False
             _last_status = copy.deepcopy(self(_session_uuid)._last_status)
             _current_status = copy.deepcopy(self(_session_uuid)._current_status)
 
@@ -231,14 +271,18 @@ class X2goSessionRegistry(object):
                 if len(self.virgin_sessions_of_profile_name(profile_name)) > 1:
                     del self.registry[_session_uuid]
 
-            elif _last_status['running'] == False and _current_status['running'] == True:
+            elif not _last_status['running'] and _current_status['running'] and not _current_status['faulty']:
                 # session has started
-                if _last_status['connected']:
-                    if self(_session_uuid).has_terminal_session():
+                if newly_connected:
+                    # from a suspended state
+                    self.client_instance.HOOK_on_found_session_running_after_connect(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
+                else:
+                    # explicitly ask for the terminal_session object directly here, so we also get 'PENDING' terminal sessions here...
+                    if self(_session_uuid).terminal_session:
                         if _last_status['suspended']:
                             # from a suspended state
                             self.client_instance.HOOK_on_session_has_resumed_by_me(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
-                        else:
+                        elif _last_status['virgin']:
                             # as a new session
                             self.client_instance.HOOK_on_session_has_started_by_me(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
                     else:
@@ -248,23 +292,24 @@ class X2goSessionRegistry(object):
                         else:
                             # as a new session
                             self.client_instance.HOOK_on_session_has_started_by_other(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
-                else:
-                    # from a suspended state
-                    self.client_instance.HOOK_on_found_session_running_after_connect(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
 
-            elif _last_status['connected'] == True and (_last_status['suspended'] == False and _current_status['suspended'] == True):
+            elif _last_status['connected'] and (not _last_status['suspended'] and _current_status['suspended']) and not _current_status['faulty']:
                 # session has been suspended
                 self(_session_uuid).session_cleanup()
                 self.client_instance.HOOK_on_session_has_been_suspended(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
-            elif _last_status['connected'] == True and (_last_status['terminated'] == False and _current_status['terminated'] == True):
+            elif _last_status['connected'] and (not _last_status['terminated'] and _current_status['terminated']) and not _current_status['faulty']:
                 # session has terminated
                 self.client_instance.HOOK_on_session_has_terminated(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
-                self(_session_uuid).session_cleanup()
-                self(_session_uuid).__del__()
+                try: self(_session_uuid).session_cleanup()
+                except X2goSessionException: pass
+                try: self(_session_uuid).__del__()
+                except X2goSessionException: pass
                 if len(self.virgin_sessions_of_profile_name(profile_name)) > 1:
-                    del self.registry[_session_uuid]
+                    self.forget(_session_uuid)
+
+        return True
 
-    def register_available_server_sessions(self, profile_name, session_list=None):
+    def register_available_server_sessions(self, profile_name, session_list=None, newly_connected=False):
         """\
         Register server-side available X2go sessions with this L{X2goSessionRegistry} instance for a given profile name.
 
@@ -275,6 +320,14 @@ class X2goSessionRegistry(object):
         @type session_list: C{X2goServerSessionList*} instance
 
         """
+        if self._last_available_session_registration is not None:
+            _now = time.time()
+            _time_delta = _now - self._last_available_session_registration
+            if _time_delta < 2:
+                self.logger('registration interval too short (%s), skipping automatic session registration...' % _timedelta, loglevel=log.loglevel_DEBUG)
+                return
+            self._last_available_session_registration = _now
+
         _connected_sessions = self.connected_sessions_of_profile_name(profile_name=profile_name, return_objects=False)
         _registered_sessions = self.registered_sessions_of_profile_name(profile_name=profile_name, return_objects=False)
         _session_names = [ self(s_uuid).session_name for s_uuid in _registered_sessions if self(s_uuid).session_name is not None ]
@@ -283,6 +336,7 @@ class X2goSessionRegistry(object):
             # any of the connected sessions is valuable for accessing the profile's control 
             # session commands, so we simply take the first that comes in...
             _ctrl_session = self(_connected_sessions[0])
+
             if session_list is None:
                 session_list = _ctrl_session.list_sessions()
 
@@ -290,7 +344,7 @@ class X2goSessionRegistry(object):
             # (if the server name has changed, this will kick out obsolete X2goSessions)
             self.update_status(profile_name=profile_name, session_list=session_list, force_update=True)
             for session_name in session_list.keys():
-                if session_name not in _session_names:
+                if session_name not in _session_names and not self._skip_auto_registration:
                     server = _ctrl_session.get_server_hostname()
                     profile_id = _ctrl_session.get_profile_id()
 
@@ -321,12 +375,11 @@ class X2goSessionRegistry(object):
                     # this if clause catches problems when x2golistsessions commands give weird results
                     if not self.has_session_of_session_name(session_name):
                         session_uuid = self.register(server, profile_id, profile_name,
-                                                     session_name=session_name,
-                                                     virgin=False, running=False, suspended=True, terminated=None,
+                                                     session_name=session_name, virgin=False,
                                                      **kwargs
                                                     )
                         self(session_uuid).connected = True
-                        self.update_status(profile_name=profile_name, session_list=session_list, force_update=True)
+                        self.update_status(session_uuid=session_uuid, force_update=True, newly_connected=newly_connected)
 
     def register(self, server, profile_id, profile_name,
                  session_name=None,
@@ -399,7 +452,7 @@ class X2goSessionRegistry(object):
             self(session_uuid).update_params(_params)
             self(session_uuid).set_server(server)
             self(session_uuid).set_profile_name(profile_name)
-            self.logger('using already initially-registered yet-unused session %s' % session_uuid, log.loglevel_NOTICE)
+            self.logger('using already initially-registered yet-unused session %s' % session_uuid, loglevel=log.loglevel_NOTICE)
             return session_uuid
 
         try:
@@ -409,7 +462,7 @@ class X2goSessionRegistry(object):
             self(session_uuid).update_params(_params)
             self(session_uuid).set_server(server)
             self(session_uuid).set_profile_name(profile_name)
-            self.logger('using already registered-by-session-name session %s' % session_uuid, log.loglevel_NOTICE)
+            self.logger('using already registered-by-session-name session %s' % session_uuid, loglevel=log.loglevel_NOTICE)
             return session_uuid
 
         except X2goSessionException:
diff --git a/x2go/rforward.py b/x2go/rforward.py
index 6259a83..0388b85 100644
--- a/x2go/rforward.py
+++ b/x2go/rforward.py
@@ -91,7 +91,7 @@ class X2goRevFwTunnel(threading.Thread):
             normally some number above 30000
         @type server_port: int
         @param remote_host: the target address for reversely tunneled traffic. With X2go this should 
-            always be set to the localhost address.
+            always be set to the localhost (IPv4) address.
         @type remote_host: str
         @param remote_port: the TCP/IP port on the X2go client (end point of the tunnel),
             normally an application's standard port (22 for SSH, 4713 for pulse audio, etc.)
@@ -246,15 +246,15 @@ class X2goRevFwTunnel(threading.Thread):
 
             self.incoming_channel.acquire()
 
-            self.logger('waiting for incoming data channel on X2go server port: [localhost]:%s' % self.server_port, loglevel=log.loglevel_DEBUG)
+            self.logger('waiting for incoming data channel on X2go server port: [127.0.0.1]:%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)
+                self.logger('detected incoming data channel on X2go server port: [127.0.0.1]:%s' % self.server_port, loglevel=log.loglevel_DEBUG)
                 _chan = self.ssh_transport.accept()
-                self.logger('data channel %s for server port [localhost]:%s is up' % (_chan, self.server_port), loglevel=log.loglevel_DEBUG)
+                self.logger('data channel %s for server port [127.0.0.1]:%s is up' % (_chan, self.server_port), loglevel=log.loglevel_DEBUG)
             else:
-                self.logger('closing down rev forwarding tunnel on remote end [localhost]:%s' % self.server_port, loglevel=log.loglevel_DEBUG)
+                self.logger('closing down rev forwarding tunnel on remote end [127.0.0.1]:%s' % self.server_port, loglevel=log.loglevel_DEBUG)
 
             self.incoming_channel.release()
             if self._accept_channels and self._keepalive:
diff --git a/x2go/session.py b/x2go/session.py
index 3857d72..44986e3 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -34,6 +34,7 @@ import types
 import uuid
 import time
 import threading
+import gevent
 
 # Python X2go modules
 import log
@@ -114,7 +115,7 @@ class X2goSession(object):
                  add_to_known_hosts=False,
                  known_hosts=None,
                  logger=None, loglevel=log.loglevel_DEFAULT,
-                 virgin=True, running=None, suspended=None, terminated=None,
+                 connected=False, virgin=True, running=None, suspended=None, terminated=None, faulty=None,
                  client_instance=None,
                  **params):
         """\
@@ -168,6 +169,8 @@ class X2goSession(object):
         @type add_to_known_hosts: C{bool}
         @param known_hosts: the underlying Paramiko/SSH systems C{known_hosts} file
         @type known_hosts: C{str}
+        @param connected: manipulate session state »connected« by giving a pre-set value
+        @type connected: C{bool}
         @param virgin: manipulate session state »virgin« by giving a pre-set value
         @type virgin: C{bool}
         @param running: manipulate session state »running« by giving a pre-set value
@@ -176,6 +179,8 @@ class X2goSession(object):
         @type suspended: C{bool}
         @param terminated: manipulate session state »terminated« by giving a pre-set value
         @type terminated: C{bool}
+        @param faulty: manipulate session state »faulty« by giving a pre-set value
+        @type faulty: C{bool}
         @param client_instance: if available, the underlying L{X2goClient} instance
         @type client_instance: C{X2goClient} instance
         @param params: further control session, terminal session and SSH proxy class options
@@ -191,12 +196,13 @@ class X2goSession(object):
         self._keep = None
 
         self.uuid = uuid.uuid1()
-        self.connected = False
+        self.connected = connected
 
         self.virgin = virgin
         self.running = running
         self.suspended = suspended
         self.terminated = terminated
+        self.faulty = faulty
         self.keep_controlsession_alive = keep_controlsession_alive
 
         self.profile_id = profile_id
@@ -204,17 +210,10 @@ class X2goSession(object):
         self.session_name = session_name
         self.server = server
 
-        self._current_status = {
-            'timestamp': time.time(),
-            'server': self.server,
-            'virgin': self.virgin,
-            'connected': self.connected,
-            'running': self.running,
-            'suspended': self.suspended,
-            'terminated': self.terminated,
-        }
         self._last_status = None
 
+        self.locked = False
+
         self.printing = printing
         self.allow_share_local_folders = allow_share_local_folders
         self.share_local_folders = share_local_folders
@@ -260,6 +259,17 @@ class X2goSession(object):
         self.known_hosts = known_hosts
         self.use_sshproxy = use_sshproxy
 
+        self._current_status = {
+            'timestamp': time.time(),
+            'server': self.server,
+            'virgin': self.virgin,
+            'connected': self.connected,
+            'running': self.running,
+            'suspended': self.suspended,
+            'terminated': self.terminated,
+            'faulty': self.faulty,
+        }
+
         self.init_control_session()
         self.terminal_session = None
 
@@ -286,11 +296,17 @@ class X2goSession(object):
         @type chain_port: C{str}
 
         """
+        # mark session as faulty
+        self.faulty = True
+
         if self.client_instance:
             self.client_instance.HOOK_forwarding_tunnel_setup_failed(profile_name=self.profile_name, session_name=self.session_name, chain_host=chain_host, chain_port=chain_port)
         else:
             self.logger('HOOK_forwarding_tunnel_setup_failed: Forwarding tunnel request to [%s]:%s for session %s (%s) was denied by remote X2go/SSH server. Session startup failed.' % (chain_host, chain_port, self.session_name, self.profile_name), loglevel=log.loglevel_WARN)
 
+        # get rid of the faulty session...
+        self.terminate()
+
     def HOOK_check_host_dialog(self, host, port, fingerprint='no fingerprint', fingerprint_type='RSA'):
         """\
         HOOK method: called if a host check is requested. This hook has to either return C{True} (default) or C{False}.
@@ -391,6 +407,7 @@ class X2goSession(object):
                     'running': self.running,
                     'suspended': self.suspended,
                     'terminated': self.terminated,
+                    'faulty': self.faulty,
                 }
                 self._last_status = None
                 self.session_name = None
@@ -613,6 +630,8 @@ class X2goSession(object):
         @rtype: C{X2goControlTerminal*} instance
 
         """
+        if self.terminal_session == 'PENDING':
+            return None
         return self.terminal_session
     __get_terminal_session = get_terminal_session
 
@@ -625,7 +644,7 @@ class X2goSession(object):
 
 
         """
-        return self.terminal_session is not None
+        return self.terminal_session not in (None, 'PENDING')
     __has_terminal_session = has_terminal_session
 
     def check_host(self):
@@ -696,24 +715,32 @@ class X2goSession(object):
             _params.update(self.control_params)
             _params.update(self.sshproxy_params)
 
-            self.connected = self.control_session.connect(self.server,
-                                                          use_sshproxy=self.use_sshproxy, 
-                                                          session_instance=self, 
-                                                          **_params)
-            # remove credentials immediately
-            self.control_params['password'] = ''
-            try: del self.control_params['sshproxy_user']
-            except KeyError: pass
-            try: del self.control_params['sshproxy_password']
-            except KeyError: pass
+            try:
+                self.connected = self.control_session.connect(self.server,
+                                                              use_sshproxy=self.use_sshproxy, 
+                                                              session_instance=self, 
+                                                              **_params)
+            except:
+                # remove credentials immediately
+                self.control_params['password'] = ''
+                if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
+                    del self.sshproxy_params['sshproxy_password']
+                raise
+            finally:
+                # remove credentials immediately
+                self.control_params['password'] = ''
+                if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
+                    del self.sshproxy_params['sshproxy_password']
 
             if not self.connected:
                 # then tidy up...
                 self.disconnect()
 
             _dummy = self.get_server_hostname()
+
         if self.connected:
             self.update_status()
+
         return self.connected
     __connect = connect
 
@@ -729,11 +756,12 @@ class X2goSession(object):
         self.running = None
         self.suspended = None
         self.terminated = None
-        retval = self.control_session.disconnect()
+        self.faults = None
         try:
-            self.update_status()
+            self.update_status(force_update=True)
         except X2goControlSessionException:
             pass
+        retval = self.control_session.disconnect()
         return retval
     __disconnect = disconnect
 
@@ -792,16 +820,40 @@ class X2goSession(object):
         @type raw: C{bool}
 
         @return: a session list (as data object or list of strings when called with C{raw=True} option)
-        @rtype C{X2goServerSessionList*} instance or C{list}
+        @rtype: C{X2goServerSessionList*} instance or C{list}
 
         """
         try:
             return self.control_session.list_sessions(raw=raw)
-        except x2go_exceptions.X2goControlSessionException:
+        except X2goControlSessionException:
             self._X2goSession_disconnect()
             return None
     __list_sessions = list_sessions
 
+    def list_desktops(self, raw=False):
+        """\
+        List X2go desktops sessions available for desktop sharing on the remote X2go server.
+
+        @param raw: if C{True} the output of this method equals
+            the output of the server-side C{x2golistdesktops} command
+        @type raw: C{bool}
+
+        @return: a list of strings representing available desktop sessions
+        @rtype: C{list}
+
+        """
+        try:
+            return self.control_session.list_desktops(raw=raw)
+        except X2goDesktopSharingException:
+            if raw:
+                return ('','')
+            else:
+                return []
+        except X2goControlSessionException:
+            self._X2goSession_disconnect()
+            return None
+    __list_desktops = list_desktops
+
     def update_status(self, session_list=None, force_update=False):
         """\
         Update the current session status. The L{X2goSession} instance uses an internal
@@ -829,7 +881,7 @@ class X2goSession(object):
             # skip this session status update if not longer than a second ago...
             if  _status_update_timedelta < 1:
                 self.logger('status update interval too short (%s), skipping status about this time...' % _status_update_timedelta, loglevel=log.loglevel_DEBUG)
-                return
+                return False
 
         e = None
         self._last_status = copy.deepcopy(self._current_status)
@@ -842,6 +894,7 @@ class X2goSession(object):
                 self.running = None
                 self.suspended = None
                 self.terminated = None
+                self.faulty = None
 
         if self.connected:
             try:
@@ -850,7 +903,7 @@ class X2goSession(object):
                 self.running = _session_info.is_running()
                 self.suspended = _session_info.is_suspended()
                 if not self.virgin:
-                    self.terminated = not (_session_info.is_running() or _session_info.is_suspended())
+                    self.terminated = not (self.running or self.suspended)
                 else:
                     self.terminated = None
             except KeyError:
@@ -858,6 +911,8 @@ class X2goSession(object):
                 self.suspended = False
                 if not self.virgin:
                     self.terminated = True
+            self.faulty = not (self.running or self.suspended or self.terminated or self.virgin)
+
 
         self._current_status = {
             'timestamp': time.time(),
@@ -867,11 +922,14 @@ class X2goSession(object):
             'running': self.running,
             'suspended': self.suspended,
             'terminated': self.terminated,
+            'faulty': self.faulty,
         }
 
-        if not self.connected and e:
+        if (not self.connected or self.faulty) and e:
             raise e
 
+        return True
+
     __update_status = update_status
 
     def resume(self, session_name=None):
@@ -886,44 +944,55 @@ class X2goSession(object):
         @rtype: C{bool}
 
         """
+        self.terminal_session == 'PENDING'
         _new_session = False
         if self.session_name is None:
             self.session_name = session_name
 
         if self.is_alive():
             _control = self.control_session
-            _terminal = _control.resume(session_name=self.session_name, 
-                                        session_instance=self,
-                                        logger=self.logger, **self.terminal_params)
-            self.terminal_session = _terminal
+
+            # FIXME: normally this part gets called if you suspend a session that is associated to another client
+            # we do not have a possibility to really check if SSH has released port forwarding channels or
+            # sockets, thus  we plainly have to wait a while
+            if self.is_running():
+                self.suspend()
+                gevent.sleep(10)
+
+            self.terminal_session = _control.resume(session_name=self.session_name,
+                                                    session_instance=self,
+                                                    logger=self.logger, **self.terminal_params)
 
             if self.session_name is None:
                 _new_session = True
-                self.session_name = self.terminal_session.session_info.name
+                try:
+                    self.session_name = self.terminal_session.session_info.name
+                except AttributeError:
+                    raise X2goSessionException('start of new X2go session failed')
 
-            if _terminal is not None:
+            if self.has_terminal_session() and not self.faulty:
 
-                if SUPPORTED_SOUND and _terminal.params.snd_system is not 'none':
-                    _terminal.start_sound()
+                if SUPPORTED_SOUND and self.terminal_session.params.snd_system is not 'none':
+                    self.terminal_session and not self.faulty and self.terminal_session.start_sound()
 
                 if (SUPPORTED_PRINTING and self.printing) or \
                    (SUPPORTED_MIMEBOX and self.allow_mimebox) or \
                    (SUPPORTED_FOLDERSHARING and self.allow_share_local_folders):
-                    _terminal.start_sshfs()
+                    self.terminal_session and not self.faulty and self.terminal_session.start_sshfs()
 
                 try:
                     if SUPPORTED_PRINTING and self.printing:
-                        _terminal.start_printing()
-                        self.session_environment.update({'X2GO_SPOOLDIR': _terminal.get_printing_spooldir(), })
+                        self.terminal_session and not self.faulty and self.terminal_session.start_printing()
+                        self.terminal_session and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
                 except X2goUserException:
                     pass
 
                 if SUPPORTED_MIMEBOX and self.allow_mimebox:
-                        _terminal.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
-                        self.session_environment.update({'X2GO_MIMEBOX': _terminal.get_mimebox_spooldir(), })
+                    self.terminal_session and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
+                    self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
 
                 if SUPPORTED_FOLDERSHARING and self.share_local_folders:
-                    if _control.get_transport().reverse_tunnels[_terminal.get_session_name()]['sshfs'][1] is not None:
+                    if _control.get_transport().reverse_tunnels[self.terminal_session.get_session_name()]['sshfs'][1] is not None:
                         for _folder in self.share_local_folders:
                             self.share_local_folder(_folder)
 
@@ -935,13 +1004,19 @@ class X2goSession(object):
                 self.suspended = False
                 self.running = True
                 self.terminated = False
+                self.faulty = False
 
-                self.terminal_session = _terminal
+                return True
+
+            else:
+                self.terminal_session = None
+                return False
 
             return self.running
         else:
             self._X2goSession__disconnect()
             return False
+
     __resume = resume
 
     def start(self):
@@ -956,6 +1031,64 @@ class X2goSession(object):
         return self.resume()
     __start = start
 
+    def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True):
+        """\
+        Share an already running X2go session on the remote X2go server locally. The shared session may be either
+        owned by the same user or by a user that grants access to his/her desktop session by the local user.
+
+        @param desktop: desktop ID of a sharable desktop in format <user>@<display>
+        @type desktop: C{str}
+        @param user: user name and display number can be given separately, here give the
+            name of the user who wants to share a session with you.
+        @type user: C{str}
+        @param display: user name and display number can be given separately, here give the
+            number of the display that a user allows you to be shared with.
+        @type display: C{str}
+        @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
+        @type share_mode: C{int}
+        @param check_desktop_list: check if the given desktop is available on the X2go server; handle with care as
+            the server-side C{x2golistdesktops} command might block client I/O.
+        @type check_desktop_list: C{bool}
+
+        @return: returns C{True} if starting the session has been successful, C{False} otherwise
+        @rtype: C{bool}
+
+        """
+        self.terminal_session = 'PENDING'
+
+        _desktop = desktop or '%s@%s' % (user, display)
+        if check_desktop_list and _desktop in self.list_desktops():
+            raise X2goDesktopSharingException('No such desktop ID: %s' % _desktop)
+
+        if self.is_alive():
+            _control = self.control_session
+            self.terminal_session = _control.share_desktop(desktop=desktop, user=user, display=display, share_mode=share_mode,
+                                                           logger=self.logger, **self.terminal_params)
+
+            if self.has_terminal_session():
+                self.terminal_session = _terminal
+                self.session_name = self.terminal_session.session_info.name
+
+                # shared desktop sessions get their startup command set by the control
+                # session, run this pre-set command now...
+                self.terminal_session.run_command(env=self.session_environment)
+
+                self.virgin = False
+                self.suspended = False
+                self.running = True
+                self.terminated = False
+                self.faulty = False
+
+                return self.running
+            else:
+                self.terminal_session = None
+                return False
+
+        else:
+            self._X2goSession__disconnect()
+            return False
+    __share_desktop = share_desktop
+
     def suspend(self):
         """\
         Suspend this X2go session.
@@ -971,6 +1104,7 @@ class X2goSession(object):
                     self.running = False
                     self.suspended = True
                     self.terminated = False
+                    self.faults = False
                     self.session_cleanup()
                     return True
 
@@ -980,6 +1114,7 @@ class X2goSession(object):
                     self.running = False
                     self.suspended = True
                     self.terminated = False
+                    self.faulty = False
                     self.session_cleanup()
                     return True
 
@@ -1007,6 +1142,7 @@ class X2goSession(object):
                     self.running = False
                     self.suspended = False
                     self.terminated = True
+                    self.faulty = False
                     self.session_cleanup()
                     return True
 
@@ -1016,6 +1152,7 @@ class X2goSession(object):
                     self.running = False
                     self.suspended = False
                     self.terminated = True
+                    self.faulty = False
                     self.session_cleanup()
                     return True
             else:
@@ -1062,11 +1199,34 @@ class X2goSession(object):
         @rtype: C{bool}
 
         """
-        if self.terminal_session is not None:
+        if self.has_terminal_session():
             return self.terminal_session.ok()
         return False
     __session_ok = session_ok
 
+    def color_depth_from_session_name(self):
+        """\
+        Extract color depth from session name.
+
+        @return: the session's color depth (as found in the session name)
+        @rtype: C{str}
+
+        """
+        return int(self.get_session_name().split('_')[2][2:])
+    __color_depth_from_session_name = color_depth_from_session_name
+
+    def is_color_depth_ok(self):
+        """\
+        Check if this session will display properly with the local screen's color depth.
+
+        @return: C{True} if the session will display on this client screen, False otherwise.
+            If no terminal session is yet registered with this session, C{None} is returned.
+        @rtype C{bool}
+
+        """
+        return utils.is_color_depth_ok(depth_session=self.color_depth_from_session_name(), depth_local=utils.local_color_depth())
+        __is_color_depth_ok = is_color_depth_ok
+
     def is_connected(self):
         """\
         Test if the L{X2goSession}'s control session is connected to the 
@@ -1081,6 +1241,7 @@ class X2goSession(object):
             self.running = None
             self.suspended = None
             self.terminated = None
+            self.faulty = None
         return self.connected
     __is_connected = is_connected
 
@@ -1097,6 +1258,7 @@ class X2goSession(object):
             if self.running:
                 self.suspended = False
                 self.terminated = False
+                self.faulty = False
             if self.virgin and not self.running:
                 self.running = None
         return self.running
@@ -1115,6 +1277,7 @@ class X2goSession(object):
             if self.suspended:
                 self.running = False
                 self.terminated = False
+                self.faulty = False
             if self.virgin and not self.suspended:
                 self.suspended = None
         return self.suspended
@@ -1133,6 +1296,7 @@ class X2goSession(object):
             if self.terminated:
                 self.running = False
                 self.suspended = False
+                self.faulty = False
             if self.virgin and not self.terminated:
                 self.terminated = None
         return self.has_terminated
@@ -1151,12 +1315,28 @@ class X2goSession(object):
         @rtype: C{bool}
 
         """
-        if self.allow_share_local_folders:
-            return self.terminal_session.share_local_folder(folder_name=folder_name)
+        if self.has_terminal_session():
+            if self.allow_share_local_folders:
+                return self.terminal_session.share_local_folder(folder_name=folder_name)
+            else:
+                self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
         else:
-            self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
+            raise X2goSessionException('this X2goSession object does not have any associated terminal')
     __share_local_folder = share_local_folder
 
+    def is_locked(self):
+        """\
+        Query session if it is locked by some command being processed.
+
+        @return: return C{True} is the session is locked, C{False} if not; returns None, if there is no
+            control session yet.
+        @rtype: C{bool}
+
+        """
+        if self.control_session is not None:
+            return self.control_session.locked or self.locked
+        return None
+
     def session_cleanup(self):
         """\
         Clean up X2go session.
@@ -1164,4 +1344,6 @@ class X2goSession(object):
         """
         if self.has_terminal_session():
             self.terminal_session.release_proxy()
-            self.terminal_session = None
+        if self.has_terminal_session():
+            self.terminal_session.__del__()
+        self.terminal_session = None
diff --git a/x2go/sshproxy.py b/x2go/sshproxy.py
index ddcd380..e59d430 100644
--- a/x2go/sshproxy.py
+++ b/x2go/sshproxy.py
@@ -175,6 +175,9 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
                 except AuthenticationException, e:
                     self.close()
                     raise X2goSSHProxyAuthenticationException('pubkey auth mechanisms both failed')
+                except:
+                    self.close()
+                    raise
 
             # if there is not private key, we will use the given password, if any
             else:
@@ -191,10 +194,16 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
                 except AuthenticationException:
                     self.close()
                     raise X2goSSHProxyAuthenticationException('interactive auth mechanisms failed')
+                except:
+                    self.close()
+                    raise
 
         except paramiko.SSHException, e:
             self.close()
             raise X2goSSHProxyException(str(e))
+        except:
+            self.close()
+            raise
 
         self.set_missing_host_key_policy(paramiko.RejectPolicy())
         threading.Thread.__init__(self)
@@ -249,12 +258,14 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
         """
         if self.fw_tunnel is not None and self.fw_tunnel.is_active:
             self.logger('taking down SSH proxy tunnel via [%s]:%s' % (self.hostname, self.port), loglevel=log.loglevel_NOTICE)
-        forward.stop_forward_tunnel(self.fw_tunnel)
+        try: forward.stop_forward_tunnel(self.fw_tunnel)
+        except: pass
         self.fw_tunnel = None
         self._keepalive = False
         if self.get_transport() is not None:
             self.logger('closing SSH proxy connection to [%s]:%s' % (self.hostname, self.port), loglevel=log.loglevel_NOTICE)
         self.close()
+        self.password = self.sshproxy_password = None
 
     def __del__(self):
         """\
diff --git a/x2go/utils.py b/x2go/utils.py
index dffca68..089706a 100644
--- a/x2go/utils.py
+++ b/x2go/utils.py
@@ -42,6 +42,9 @@ from defaults import X2GO_SESSIONPROFILE_DEFAULTS as _X2GO_SESSIONPROFILE_DEFAUL
 from defaults import X2GO_MIMEBOX_ACTIONS as _X2GO_MIMEBOX_ACTIONS
 from defaults import _pack_methods_nx3
 
+if _X2GOCLIENT_OS == 'Windows':
+    import win32api
+
 def is_in_nx3packmethods(method):
 
     """\
@@ -396,4 +399,37 @@ def xkb_rules_names():
     }
     return _rn_dict
 
+def local_color_depth():
+    """\
+    Detect the current local screen's color depth.
+
+    """
+    if _X2GOCLIENT_OS != 'Windows':
+        p = subprocess.Popen(['xwininfo', '-root',], stdout=subprocess.PIPE, )
+        _depth_line = [ _info.strip() for _info in p.stdout.read().split('\n') if 'Depth:' in _info ][0]
+        _depth = _depth_line.split(' ')[1]
+        return int(_depth)
+    else:
+        return win32api.GetSystemMetrics(2)
 
+def is_color_depth_ok(depth_session, depth_local):
+    """\
+    Test if color depth of this session is compatible with the
+    local screen's color depth.
+
+    @param depth_session: color depth of the session
+    @type depth_session: C{int}
+    @param depth_local: color depth of local screen
+    @type depth_local: C{int}
+
+    @return: Does the session color depth work with the local display?
+    @rtype: C{bool}
+
+    """
+    if depth_session == 0:
+        return True
+    if depth_session == depth_local:
+        return True
+    if ( ( depth_session == 24 or depth_session == 32 ) and ( depth_local == 24 or depth_local == 32 ) ):
+        return true;
+    return False
diff --git a/x2go/x2go_exceptions.py b/x2go/x2go_exceptions.py
index a2d7968..9b7b3eb 100644
--- a/x2go/x2go_exceptions.py
+++ b/x2go/x2go_exceptions.py
@@ -60,6 +60,8 @@ class X2goPrintActionException(_X2goException): pass
 class X2goSSHProxyException(_X2goException): pass
 class X2goSSHProxyAuthenticationException(_X2goException): pass
 class X2goNotImplementedYetException(_X2goException): pass
+class X2goDesktopSharingException(_X2goException): pass
+class X2goTimeOutException(_X2goException): pass
 if _X2GOCLIENT_OS != 'Windows':
     # faking Windows errors on non-Windows systems...
     class WindowsError(_X2goException): pass


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