[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:28:32 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