[X2Go-Commits] python-x2go.git - build-baikal (branch) updated: 0.1.0.3-1-g5af424a

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


The branch, build-baikal has been updated
       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                  |    7 +++
 x2go/backends/control/_stdout.py  |  106 ++++++++++++++++++++++++++++++++-----
 x2go/backends/terminal/_stdout.py |   31 ++++++++---
 x2go/cache.py                     |   75 +++++++++++++++++++++-----
 x2go/client.py                    |   80 ++++++++++++++++++++++++++++
 x2go/defaults.py                  |    4 ++
 x2go/session.py                   |   80 ++++++++++++++++++++++++++--
 x2go/x2go_exceptions.py           |    1 +
 8 files changed, 345 insertions(+), 39 deletions(-)

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index 29a2aec..8005b43 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+python-x2go (0.1.0.3-0~x2go2) UNRELEASED; urgency=low
+
+  * New upstream version (0.1.1.0):
+    -Add X2go desktop sharing support.
+
+ -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Mon, 13 Jun 2011 23:24:02 +0200
+
 python-x2go (0.1.0.3-0~x2go1) unstable; urgency=low
 
   * New upstream version (0.1.0.3):
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index 1daa82f..76938b8 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -545,32 +545,110 @@ 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):
+        """\
+        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
+            _success = False
+            while not _success:
+
+                (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
 
-        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),
-            }
+                _stdout_read = stdout.read()
+
+                try:
+                    _listdesktops = _stdout_read.split('\n')
+                    _success = True
+                except KeyError:
+                    gevent.sleep(1)
+                except IndexError:
+                    gevent.sleep(1)
+                except ValueError:
+                    gevent.sleep(1)
 
-        return _terminal or None
+            return _listdesktops
 
     def list_sessions(self, raw=False):
         """\
diff --git a/x2go/backends/terminal/_stdout.py b/x2go/backends/terminal/_stdout.py
index 91a8059..09a4147 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
@@ -112,10 +112,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
@@ -138,7 +141,7 @@ class X2goSessionParams(object):
         Update all properties in the object L{X2goSessionParams} object from
         the passed on dictionary.
 
-        @param properties_to_be_updated: a dictionary with L{X2goSessionParams}
+        @param propertBies_to_be_updated: a dictionary with L{X2goSessionParams}
             property names as keys und their values to be update in 
             L{X2goSessionParams} object.
         @type properties_to_be_updated: dict
@@ -216,7 +219,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})
@@ -679,6 +682,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 +725,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", 
@@ -804,8 +813,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 +830,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(),
                                     )
diff --git a/x2go/cache.py b/x2go/cache.py
index e91e54b..7bd8b88 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 = {}
@@ -105,7 +106,39 @@ class X2goListSessionsCache(object):
 
     def update(self, profile_name):
         """\
-        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}
+
+        """
+        if not self.x2go_listsessions_cache.has_key(profile_name):
+            self.x2go_listsessions_cache[profile_name] = {}
+        self._update_sessions(profile_name)
+        self._update_desktops(profile_name)
+
+    def _update_desktops(self, profile_name):
+        """\
+        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}
+
+        """
+        self.last_listsessions_cache = copy.deepcopy(self.x2go_listsessions_cache)
+        control_session = self.client_instance.client_control_session_of_profile_name(profile_name)
+        try:
+            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):
+        """\
+        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}
@@ -114,7 +147,7 @@ class X2goListSessionsCache(object):
         self.last_listsessions_cache = copy.deepcopy(self.x2go_listsessions_cache)
         control_session = self.client_instance.client_control_session_of_profile_name(profile_name)
         try:
-            self.x2go_listsessions_cache[profile_name] = control_session.list_sessions()
+            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 +157,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,7 +168,25 @@ 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
 
diff --git a/x2go/client.py b/x2go/client.py
index c6b41ff..ae4dec1 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -1120,6 +1120,31 @@ class X2goClient(object):
         return self.session_registry(session_uuid).start()
     __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}
+
+        """
+        return self.session_registry(session_uuid).share_desktop(desktop=desktop, user=user, display=display, share_mode=share_mode)
+    __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
@@ -1866,6 +1891,61 @@ 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 no_cache:
+            _desktop_list = self.session_registry(session_uuid).list_desktops()
+        else:
+            _desktop_list = self.listsessions_cache.list_desktops(session_uuid)
+
+        return _desktop_list
+    __list_desktops = list_desktops
+
     ###
     ### Provide access to config file class objects
     ### 
diff --git a/x2go/defaults.py b/x2go/defaults.py
index 3ad6fa8..bfe8899 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -393,3 +393,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/session.py b/x2go/session.py
index 3857d72..687a010 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -792,16 +792,35 @@ 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 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
@@ -895,11 +914,10 @@ class X2goSession(object):
             _terminal = _control.resume(session_name=self.session_name, 
                                         session_instance=self,
                                         logger=self.logger, **self.terminal_params)
-            self.terminal_session = _terminal
 
             if self.session_name is None:
                 _new_session = True
-                self.session_name = self.terminal_session.session_info.name
+                self.session_name = _terminal.session_info.name
 
             if _terminal is not None:
 
@@ -929,7 +947,7 @@ class X2goSession(object):
 
                 # only run the session startup command if we do not resume...
                 if _new_session:
-                    self.terminal_session.run_command(env=self.session_environment)
+                    _terminal.run_command(env=self.session_environment)
 
                 self.virgin = False
                 self.suspended = False
@@ -937,11 +955,16 @@ class X2goSession(object):
                 self.terminated = False
 
                 self.terminal_session = _terminal
+                return True
+
+            else:
+                return False
 
             return self.running
         else:
             self._X2goSession__disconnect()
             return False
+
     __resume = resume
 
     def start(self):
@@ -956,6 +979,53 @@ class X2goSession(object):
         return self.resume()
     __start = start
 
+    def share_desktop(self, desktop=None, user=None, display=None, share_mode=0):
+        """\
+        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}
+
+        @return: returns C{True} if starting the session has been successful, C{False} otherwise
+        @rtype: C{bool}
+
+        """
+        if self.is_alive():
+            _control = self.control_session
+            _terminal = _control.share_desktop(desktop=desktop, user=user, display=display, share_mode=share_mode,
+                                               logger=self.logger, **self.terminal_params)
+
+            if _terminal:
+                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
+
+                return self.running
+            else:
+                return False
+
+        else:
+            self._X2goSession__disconnect()
+            return False
+    __share_desktop = share_desktop
+
     def suspend(self):
         """\
         Suspend this X2go session.
diff --git a/x2go/x2go_exceptions.py b/x2go/x2go_exceptions.py
index a2d7968..e211836 100644
--- a/x2go/x2go_exceptions.py
+++ b/x2go/x2go_exceptions.py
@@ -60,6 +60,7 @@ class X2goPrintActionException(_X2goException): pass
 class X2goSSHProxyException(_X2goException): pass
 class X2goSSHProxyAuthenticationException(_X2goException): pass
 class X2goNotImplementedYetException(_X2goException): pass
+class X2goDesktopSharingException(_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