1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 X2GoSession class - a public API of Python X2Go, handling standalone X2Go
22 sessions.
23
24 This class is normally embedded into the context of an L{X2GoClient}
25 instance, but it is also possible to address L{X2GoSession}s directly via this
26 class.
27
28 To launch a session manually from the Python interactive shell, perform these
29 simple steps::
30
31 $ python
32 Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
33 [GCC 4.4.5] on linux2
34 Type "help", "copyright", "credits" or "license" for more information.
35 >>> import x2go
36 >>> import gevent
37 Xlib.protocol.request.QueryExtension
38 >>> s = x2go.session.X2GoSession()
39 >>> s.set_server('<my.x2go.server>')
40 >>> s.set_port(<ssh-port>)
41 >>> s.connect('<my-login>', '<my-password>')
42 [<pidno>] (x2gocontrolsession-pylib) NOTICE: connecting to [<my.x2go.server>]:<ssh-port>
43 [<pidno>] (x2gosession-pylib) NOTICE: SSH host key verification for host [<my.x2go.server>]:<ssh-port> with SSH-RSA fingerprint ,,<ssh-fingerprint>'' initiated. We are seeing this X2Go server for the first time.
44 [<pidno>] (x2gosession-pylib) WARN: HOOK_check_host_dialog: host check requested for [<my.x2go.server>]:<ssh-port> with SSH-RSA fingerprint: ,,<ssh-fingerprint>''. Automatically adding host as known host.
45 True
46 >>> s.start(cmd="LXDE")
47 True
48 >>> while True: gevent.sleep(1)
49
50 """
51
52 __NAME__ = 'x2gosession-pylib'
53
54 import os
55 import copy
56 import types
57 import uuid
58 import time
59 import gevent
60 import re
61 import threading
62
63
64 import paramiko
65
66
67 import defaults
68 import log
69 import utils
70 import session
71 import x2go_exceptions
72
73 from x2go.backends.control import X2GoControlSession as _X2GoControlSession
74 from x2go.backends.terminal import X2GoTerminalSession as _X2GoTerminalSession
75 from x2go.backends.info import X2GoServerSessionInfo as _X2GoServerSessionInfo
76 from x2go.backends.info import X2GoServerSessionList as _X2GoServerSessionList
77 from x2go.backends.proxy import X2GoProxy as _X2GoProxy
78 from x2go.backends.settings import X2GoClientSettings as _X2GoClientSettings
79 from x2go.backends.printing import X2GoClientPrinting as _X2GoClientPrinting
80
81 from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
82 from defaults import LOCAL_HOME as _LOCAL_HOME
83 from defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
84 from defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR
85 from defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR
86
87 from defaults import SUPPORTED_SOUND, SUPPORTED_PRINTING, SUPPORTED_FOLDERSHARING, SUPPORTED_MIMEBOX
88
89 _X2GO_SESSION_PARAMS = ('use_sshproxy', 'sshproxy_reuse_authinfo',
90 'profile_id', 'session_name',
91 'auto_start_or_resume', 'auto_connect',
92 'printing', 'allow_mimebox',
93 'mimebox_extensions', 'mimebox_action',
94 'allow_share_local_folders', 'share_local_folders', 'restore_shared_local_folders',
95 'control_backend', 'terminal_backend', 'info_backend', 'list_backend', 'proxy_backend', 'settings_backend', 'printing_backend',
96 'client_rootdir', 'sessions_rootdir', 'ssh_rootdir',
97 'keep_controlsession_alive', 'add_to_known_hosts', 'known_hosts', 'forward_sshagent',
98 'connected', 'virgin', 'running', 'suspended', 'terminated', 'faulty'
99 'client_instance',
100 )
101 """A list of allowed X2Go pure session parameters (i.e. parameters that are passed on neither to an X2GoControlSession, X2GoSSHProxy nor an X2GoControlSession object."""
102
103 _X2GO_TERMINAL_PARAMS = ('geometry', 'depth', 'link', 'pack', 'dpi',
104 'cache_type', 'kbtype', 'kblayout', 'kbvariant',
105 'session_type', 'snd_system', 'snd_port',
106 'cmd', 'set_session_title', 'session_title',
107 'rdp_server', 'rdp_options', 'applications',
108 'xdmcp_server',
109 'rootdir', 'loglevel', 'profile_name', 'profile_id',
110 'print_action', 'print_action_args',
111 'convert_encoding', 'client_encoding', 'server_encoding',
112 'proxy_options', 'published_applications', 'published_applications_no_submenus',
113 'logger',
114 'control_backend', 'terminal_backend', 'proxy_backend',
115 'profiles_backend', 'settings_backend', 'printing_backend',
116 )
117 """A list of allowed X2Go terminal session parameters."""
118 _X2GO_SSHPROXY_PARAMS = ('sshproxy_host', 'sshproxy_port', 'sshproxy_user', 'sshproxy_password',
119 'sshproxy_key_filename', 'sshproxy_pkey',
120 'sshproxy_look_for_keys', 'sshproxy_allow_agent',
121 'sshproxy_tunnel',
122 )
123 """A list of allowed X2Go SSH proxy parameters."""
124
125
127 """\
128 Public API class for launching X2Go sessions. Recommended is to manage X2Go sessions from
129 within an L{X2GoClient} instance. However, Python X2Go is designed in a way that it also
130 allows the management of singel L{X2GoSession} instance.
131
132 Thus, you can use the L{X2GoSession} class to manually set up X2Go sessions without
133 L{X2GoClient} context (session registry, session list cache, auto-registration of new
134 sessions etc.).
135
136 """
137 - def __init__(self, server=None, port=22, control_session=None,
138 use_sshproxy=False,
139 sshproxy_reuse_authinfo=False,
140 profile_id=None, profile_name='UNKNOWN',
141 session_name=None,
142 auto_start_or_resume=False,
143 auto_connect=False,
144 printing=False,
145 allow_mimebox=False,
146 mimebox_extensions=[],
147 mimebox_action='OPEN',
148 allow_share_local_folders=False,
149 share_local_folders=[],
150 restore_shared_local_folders=False,
151 control_backend=_X2GoControlSession,
152 terminal_backend=_X2GoTerminalSession,
153 info_backend=_X2GoServerSessionInfo,
154 list_backend=_X2GoServerSessionList,
155 proxy_backend=_X2GoProxy,
156 settings_backend=_X2GoClientSettings,
157 printing_backend=_X2GoClientPrinting,
158 client_rootdir=os.path.join(_LOCAL_HOME, _X2GO_CLIENT_ROOTDIR),
159 sessions_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SESSIONS_ROOTDIR),
160 ssh_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SSH_ROOTDIR),
161 keep_controlsession_alive=False,
162 add_to_known_hosts=False,
163 known_hosts=None,
164 forward_sshagent=False,
165 logger=None, loglevel=log.loglevel_DEFAULT,
166 connected=False, activated=False, virgin=True, running=None, suspended=None, terminated=None, faulty=None,
167 client_instance=None,
168 **params):
169 """\
170 @param server: hostname of X2Go server
171 @type server: C{str}
172 @param control_session: an already initialized C{X2GoControlSession*} instance
173 @type control_session: C{X2GoControlSession*} instance
174 @param use_sshproxy: for communication with X2Go server use an SSH proxy host
175 @type use_sshproxy: C{bool}
176 @param sshproxy_reuse_authinfo: for proxy authentication re-use the X2Go session's password / key file
177 @type sshproxy_reuse_authinfo: C{bool}
178 @param profile_id: profile ID
179 @type profile_id: C{str}
180 @param profile_name: profile name
181 @type profile_name: C{str}
182 @param session_name: session name (if available)
183 @type session_name: C{str}
184 @param auto_start_or_resume: automatically start a new or resume latest session after connect
185 @type auto_start_or_resume: C{bool}
186 @param auto_connect: call a hook method that handles connecting the session profile automatically after a session for this profile has been registered
187 @type auto_connect: C{bool}
188 @param printing: enable X2Go printing
189 @type printing: C{bool}
190 @param allow_mimebox: enable X2Go MIME box support
191 @type allow_mimebox: C{bool}
192 @param mimebox_extensions: whitelist of allowed X2Go MIME box extensions
193 @type mimebox_extensions: C{list}
194 @param mimebox_action: action for incoming X2Go MIME box files
195 @type mimebox_action: C{X2GoMimeBoxAction*} or C{str}
196 @param allow_share_local_folders: enable local folder sharing support
197 @type allow_share_local_folders: C{bool}
198 @param share_local_folders: list of local folders to share with the remote X2Go session
199 @type share_local_folders: C{list}
200 @param restore_shared_local_folders: store actual list of shared local folders after session has been suspended or terminated
201 @type restore_shared_local_folders: C{bool}
202 @param control_backend: X2Go control session backend to use
203 @type control_backend: C{class}
204 @param terminal_backend: X2Go terminal session backend to use
205 @type terminal_backend: C{class}
206 @param info_backend: X2Go session info backend to use
207 @type info_backend: C{class}
208 @param list_backend: X2Go session list backend to use
209 @type list_backend: C{class}
210 @param proxy_backend: X2Go proxy backend to use
211 @type proxy_backend: C{class}
212 @param settings_backend: X2Go client settings backend to use
213 @type settings_backend: C{class}
214 @param printing_backend: X2Go client printing backend to use
215 @type printing_backend: C{class}
216 @param client_rootdir: client base dir (default: ~/.x2goclient)
217 @type client_rootdir: C{str}
218 @param sessions_rootdir: sessions base dir (default: ~/.x2go)
219 @type sessions_rootdir: C{str}
220 @param ssh_rootdir: ssh base dir (default: ~/.ssh)
221 @type ssh_rootdir: C{str}
222 @param keep_controlsession_alive: On last L{X2GoSession.disconnect()} keep the associated C{X2GoControlSession*} instance alive?
223 @ŧype keep_controlsession_alive: C{bool}
224 @param add_to_known_hosts: Auto-accept server host validity?
225 @type add_to_known_hosts: C{bool}
226 @param known_hosts: the underlying Paramiko/SSH systems C{known_hosts} file
227 @type known_hosts: C{str}
228 @param forward_sshagent: forward SSH agent authentication requests to the SSH agent on the X2Go client-side
229 @type forward_sshagent: C{bool}
230 @param connected: manipulate session state »connected« by giving a pre-set value
231 @type connected: C{bool}
232 @param activated: normal leave this untouched, an activated session is a session that is about to be used
233 @type activated: C{bool}
234 @param virgin: manipulate session state »virgin« by giving a pre-set value
235 @type virgin: C{bool}
236 @param running: manipulate session state »running« by giving a pre-set value
237 @type running: C{bool}
238 @param suspended: manipulate session state »suspended« by giving a pre-set value
239 @type suspended: C{bool}
240 @param terminated: manipulate session state »terminated« by giving a pre-set value
241 @type terminated: C{bool}
242 @param faulty: manipulate session state »faulty« by giving a pre-set value
243 @type faulty: C{bool}
244 @param client_instance: if available, the underlying L{X2GoClient} instance
245 @type client_instance: C{X2GoClient} instance
246 @param params: further control session, terminal session and SSH proxy class options
247 @type params: C{dict}
248
249 """
250 if logger is None:
251 self.logger = log.X2GoLogger(loglevel=loglevel)
252 else:
253 self.logger = copy.deepcopy(logger)
254 self.logger.tag = __NAME__
255
256 self._keep = None
257
258 self.uuid = uuid.uuid1()
259 self.connected = connected
260
261 self.activated = activated
262 self.virgin = virgin
263 self.running = running
264 self.suspended = suspended
265 self.terminated = terminated
266 self.faulty = faulty
267 self.keep_controlsession_alive = keep_controlsession_alive
268
269 self.profile_id = profile_id
270 self.profile_name = profile_name
271 self.session_name = session_name
272 self.server = server
273 self.port = port
274
275 self._last_status = None
276
277 self.locked = False
278
279 self.auto_start_or_resume = auto_start_or_resume
280 self.auto_connect = auto_connect
281 self.printing = printing
282 self.allow_share_local_folders = allow_share_local_folders
283 self.share_local_folders = share_local_folders
284 self.restore_shared_local_folders = restore_shared_local_folders
285 self.allow_mimebox = allow_mimebox
286 self.mimebox_extensions = mimebox_extensions
287 self.mimebox_action = mimebox_action
288 self.control_backend = control_backend
289 self.terminal_backend = terminal_backend
290 self.info_backend = info_backend
291 self.list_backend = list_backend
292 self.proxy_backend = proxy_backend
293 self.settings_backend = settings_backend
294 self.printing_backend = printing_backend
295 self.client_rootdir = client_rootdir
296 self.sessions_rootdir = sessions_rootdir
297 self.ssh_rootdir = ssh_rootdir
298 self.control_session = control_session
299
300 if params.has_key('published_applications'):
301 self.published_applications = params['published_applications']
302 if self.published_applications:
303 params['cmd'] = 'PUBLISHED'
304 else:
305 self.published_applications = params['published_applications'] = False
306
307 if params.has_key('cmd') and params['cmd'] != 'PUBLISHED':
308 self.published_applications = params['published_applications'] = False
309 self.published_applications_menu = None
310
311 if self.session_name:
312 if not re.match('.*_stRPUBLISHED_.*',self.session_name):
313 self.published_applications = params['published_applications'] = False
314
315 self.use_sshproxy = use_sshproxy
316 self.sshproxy_reuse_authinfo = sshproxy_reuse_authinfo
317
318 self.control_params = {}
319 self.terminal_params = {}
320 self.sshproxy_params = {}
321 self.update_params(params)
322 self.shared_folders = {}
323
324 self.session_environment = {}
325 self.server_features = []
326
327 try: del self.control_params['server']
328 except: pass
329
330 self.client_instance = client_instance
331
332 if self.logger.get_loglevel() & log.loglevel_DEBUG:
333 self.logger('X2Go control session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
334 for p in self.control_params:
335 self.logger(' %s: %s' % (p, self.control_params[p]), log.loglevel_DEBUG)
336 self.logger('X2Go terminal session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
337 for p in self.terminal_params:
338 self.logger(' %s: %s' % (p,self.terminal_params[p]), log.loglevel_DEBUG)
339 self.logger('X2Go sshproxy parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
340 for p in self.sshproxy_params:
341 self.logger(' %s: %s' % (p,self.sshproxy_params[p]), loglevel=log.loglevel_DEBUG)
342
343 self.add_to_known_hosts = add_to_known_hosts
344 self.known_hosts = known_hosts
345 self.forward_sshagent = forward_sshagent
346
347 self._current_status = {
348 'timestamp': time.time(),
349 'server': self.server,
350 'virgin': self.virgin,
351 'connected': self.connected,
352 'running': self.running,
353 'suspended': self.suspended,
354 'terminated': self.terminated,
355 'faulty': self.faulty,
356 }
357
358 self._SUPPORTED_SOUND = SUPPORTED_SOUND
359 self._SUPPORTED_PRINTING = SUPPORTED_PRINTING
360 self._SUPPORTED_MIMEBOX = SUPPORTED_MIMEBOX
361 self._SUPPORTED_FOLDERSHARING = SUPPORTED_FOLDERSHARING
362
363 self.master_session = None
364 self.init_control_session()
365 self.terminal_session = None
366
367 if self.is_connected():
368 self.retrieve_server_features()
369
370 self._progress_status = 0
371 self._lock = threading.Lock()
372
373 self._restore_exported_folders = {}
374 if self.client_instance and self.restore_shared_local_folders:
375 self._restore_exported_folders = self.client_instance.get_profile_config(self.profile_name, 'export')
376
378 return self.__get_uuid()
379
381 result = 'X2GoSession('
382 for p in dir(self):
383 if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
384 result += p + '=' + str(self.__dict__[p]) + ','
385 result = result.strip(',')
386 return result + ')'
387
389 return self.__get_uuid()
390
392 """\
393 Class destructor.
394
395 """
396 if self.has_control_session() and self.has_terminal_session():
397 self.get_control_session().dissociate(self.get_terminal_session())
398
399 if self.has_control_session():
400 if self.keep_controlsession_alive:
401
402
403 self.virgin = True
404 self.activated = False
405 self.connected = self.is_connected()
406 self.running = None
407 self.suspended = None
408 self.terminated = None
409 self._current_status = {
410 'timestamp': time.time(),
411 'server': self.server,
412 'virgin': self.virgin,
413 'connected': self.connected,
414 'running': self.running,
415 'suspended': self.suspended,
416 'terminated': self.terminated,
417 'faulty': self.faulty,
418 }
419 self._last_status = None
420 self.session_name = None
421
422 else:
423 self.get_control_session().__del__()
424 self.control_session = None
425
426 if self.has_terminal_session():
427 self.get_terminal_session().__del__()
428 self.terminal_session = None
429
431 """\
432 Return parent L{X2GoClient} instance if avaiable.
433
434 return: L{X2GoClient} instance this session is associated with
435 rtype: C{obj}
436
437 """
438 return self.client_instance
439 __get_client_instance = get_client_instance
440
442 """\
443 HOOK method: called if a control session (server connection) has unexpectedly encountered a failure.
444
445 """
446 if self.client_instance:
447 self.client_instance.HOOK_on_control_session_death(profile_name=self.profile_name)
448 else:
449 self.logger('HOOK_on_control_session_death: the control session of profile %s has died unexpectedly' % self.profile_name, loglevel=log.loglevel_WARN)
450
452 """\
453 HOOK method: called if the session demands to auto connect.
454
455 """
456 if self.client_instance:
457 self.client_instance.HOOK_profile_auto_connect(profile_name=self.profile_name)
458 else:
459 self.logger('HOOK_auto_connect: profile ,,%s\'\' wants to auto-connect to the X2Go server.' % self.profile_name, loglevel=log.loglevel_WARN)
460
462 """\
463 HOOK method: called if the startup of a session failed.
464
465 """
466 if self.client_instance:
467 self.client_instance.HOOK_session_startup_failed(profile_name=self.profile_name)
468 else:
469 self.logger('HOOK_session_startup_failed: session startup for session profile ,,%s\'\' failed.' % self.profile_name, loglevel=log.loglevel_WARN)
470
472 """\
473 HOOK method: called if the x2golistdesktops command generates a timeout due to long execution time.
474
475 """
476 if self.client_instance:
477 self.client_instance.HOOK_list_desktops_timeout(profile_name=self.profile_name)
478 else:
479 self.logger('HOOK_list_desktops_timeout: the server-side x2golistdesktops command for session profile %s took too long to return results. This can happen from time to time, please try again.' % self.profile_name, loglevel=log.loglevel_WARN)
480
482 """\
483 HOOK method: called if it is tried to connect to a shared desktop that's not available (anymore).
484
485 """
486 if self.client_instance:
487 self.client_instance.HOOK_no_such_desktop(profile_name=self.profile_name, desktop=desktop)
488 else:
489 self.logger('HOOK_no_such_desktop: the desktop %s (via session profile %s) is not available for sharing (anymore).' % (desktop, self.profile_name), loglevel=log.loglevel_WARN)
490
492 """\
493 HOOK method: called if a reverse port forwarding request has been denied.
494
495 @param server_port: remote server port (starting point of reverse forwarding tunnel)
496 @type server_port: C{str}
497
498 """
499 if self.client_instance:
500 self.client_instance.HOOK_rforward_request_denied(profile_name=self.profile_name, session_name=self.session_name, server_port=server_port)
501 else:
502 self.logger('HOOK_rforward_request_denied: TCP port (reverse) forwarding request for session %s to server port %s has been denied by server %s. This is a common issue with SSH, it might help to restart the server\'s SSH daemon.' % (self.session_name, server_port, self.profile_name), loglevel=log.loglevel_WARN)
503
505 """\
506 HOOK method: called if a port forwarding tunnel setup failed.
507
508 @param chain_host: hostname of chain host (forwarding tunnel end point)
509 @type chain_host: C{str}
510 @param chain_port: port of chain host (forwarding tunnel end point)
511 @type chain_port: C{str}
512
513 """
514
515 self.faulty = True
516
517 if self.client_instance:
518 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)
519 else:
520 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)
521
522
523 try:
524 self._terminate()
525 except x2go_exceptions.X2GoSessionException:
526 pass
527
529 """\
530 HOOK method: called if X2Go client-side printing is not available.
531
532 """
533 if self.client_instance:
534 self.client_instance.HOOK_printing_not_available(profile_name=self.profile_name, session_name=self.session_name)
535 else:
536 self.logger('HOOK_printing_not_available: X2Go\'s client-side printing feature is not available with this session (%s) of profile %s.' % (self.session_name, self.profile_name), loglevel=log.loglevel_WARN)
537
539 """\
540 HOOK method: called if the X2Go MIME box is not available.
541
542 """
543 if self.client_instance:
544 self.client_instance.HOOK_mimebox_not_available(profile_name=self.profile_name, session_name=self.session_name)
545 else:
546 self.logger('HOOK_mimebox_not_available: X2Go\'s MIME box feature is not available with this session (%s) of profile %s.' % (self.session_name, self.profile_name), loglevel=log.loglevel_WARN)
547
549 """\
550 HOOK method: called if X2Go client-side folder-sharing is not available.
551
552 """
553 if self.client_instance:
554 self.client_instance.HOOK_foldersharing_not_available(profile_name=self.profile_name, session_name=self.session_name)
555 else:
556 self.logger('HOOK_foldersharing_not_available: X2Go\'s client-side folder sharing feature is not available with this session (%s) of profile %s.' % (self.session_name, self.profile_name), loglevel=log.loglevel_WARN)
557
559 """\
560 HOOK method: called if the X2Go server denies SSHFS access.
561
562 """
563 if self.client_instance:
564 self.client_instance.HOOK_sshfs_not_available(profile_name=self.profile_name, session_name=self.session_name)
565 else:
566 self.logger('HOOK_sshfs_not_available: the remote X2Go server (%s) denies SSHFS access for session %s. This will result in client-side folder sharing, printing and the MIME box feature being unavailable' % (self.profile_name, self.session_name), loglevel=log.loglevel_WARN)
567
569 """\
570 HOOK method: called if a host check is requested. This hook has to either return C{True} (default) or C{False}.
571
572 @param host: SSH server name to validate
573 @type host: C{str}
574 @param port: SSH server port to validate
575 @type port: C{int}
576 @param fingerprint: the server's fingerprint
577 @type fingerprint: C{str}
578 @param fingerprint_type: finger print type (like RSA, DSA, ...)
579 @type fingerprint_type: C{str}
580 @return: if host validity is verified, this hook method should return C{True}
581 @rtype: C{bool}
582
583 """
584 if self.client_instance:
585 return self.client_instance.HOOK_check_host_dialog(profile_name=self.profile_name, host=host, port=port, fingerprint=fingerprint, fingerprint_type=fingerprint_type)
586 else:
587 self.logger('HOOK_check_host_dialog: host check requested for [%s]:%s with %s fingerprint: ,,%s\'\'. Automatically adding host as known host.' % (host, port, fingerprint_type, fingerprint), loglevel=log.loglevel_WARN)
588 return True
589
591 """\
592 Initialize a new control session (C{X2GoControlSession*}).
593
594 """
595 low_latency = self.terminal_params.has_key('link') and self.terminal_params['link'].lower() in ('modem', 'isdn')
596
597 if self.control_session is None:
598 self.logger('initializing X2GoControlSession', loglevel=log.loglevel_DEBUG)
599 self.control_session = self.control_backend(profile_name=self.profile_name,
600 add_to_known_hosts=self.add_to_known_hosts,
601 known_hosts=self.known_hosts,
602 forward_sshagent=self.forward_sshagent,
603 terminal_backend=self.terminal_backend,
604 info_backend=self.info_backend,
605 list_backend=self.list_backend,
606 proxy_backend=self.proxy_backend,
607 client_rootdir=self.client_rootdir,
608 sessions_rootdir=self.sessions_rootdir,
609 ssh_rootdir=self.ssh_rootdir,
610 low_latency=low_latency,
611 logger=self.logger)
612 else:
613 self.control_session.low_latency = low_latency
614 __init_control_session = init_control_session
615
617 """\
618 Is this session a/the master session of sessions.
619
620 The master session is the session has been launched first for a specific connection,
621 it also is _the_ session that controls the client-side shared folders.
622
623 If this L{X2GoSession} instance is a standalone instance (without parent L{X2GoClient})
624 this method will always return C{True}.
625
626 @return: returns C{True} if this session is a master session
627 @rtype: C{bool}
628
629 """
630 if self.master_session is None and self.client_instance is None:
631 return True
632 return bool(self.master_session)
633 __is_master_session = is_master_session
634
636 """\
637 Declare this as a master session of a connection channel.
638
639 This method gets called by the L{X2GoSessionRegistry} while sessions are starting or resuming and it relies on
640 an already set-up terminal session.
641
642 @param wait: wait for <wait> seconds before sharing local folders via the new master session
643 of the corresponding session profile.
644 @type wait: C{int}
645 @param max_wait: wait for <max_wait> seconds for the terminal session to appear
646 @type max_wait: C{int}
647
648 """
649 self.logger('Using session %s as master session for profile %s.' % (self.get_session_name(), self.get_profile_name()), loglevel=log.loglevel_NOTICE)
650 self.master_session = True
651
652
653 if self.client_instance:
654 _exports = self.client_instance.get_profile_config(self.profile_name, 'export')
655 self.share_local_folders = [ sf for sf in _exports.keys() if _exports[sf] ]
656
657 i = 0
658 while i < max_wait:
659 i += 1
660 if self.has_terminal_session():
661 break
662 gevent.sleep(1)
663
664 if wait:
665 gevent.spawn_later(wait, self.share_all_local_folders, update_exported_folders=False)
666 else:
667 gevent.spawn(self.share_all_local_folders, update_exported_folders=False)
668 __set_master_session = set_master_session
669
679 __unset_master_session = unset_master_session
680
682 """\
683 Modify server name after L{X2GoSession} has already been initialized.
684
685 @param server: new server name
686 @type server: C{str}
687
688 """
689 self.server = server
690 __set_server = set_server
691
693 """\
694 Modify server port after L{X2GoSession} has already been initialized.
695
696 @param port: socket port of server to connect to
697 @type port: C{int}
698
699 """
700 self.port = port
701 __set_port = set_port
702
704 """\
705 Modify session profile name after L{X2GoSession} has already been initialized.
706
707 @param profile_name: new session profile name
708 @type profile_name: C{str}
709
710 """
711 self.profile_name = profile_name
712 self.control_session.set_profile_name(profile_name)
713 __set_profile_name = set_profile_name
714
716 """\
717 Retrieve a specific profile parameter for this session.
718
719 @param option: name of a specific profile option to be queried.
720 @type option: C{str}
721
722 @return: value for profile option C{<option>}
723 @rtype: C{bool,str,int}
724
725 @raise X2GoProfileException: if the session profile option is unknown
726
727 """
728 if option in _X2GO_SESSION_PARAMS + _X2GO_TERMINAL_PARAMS + _X2GO_SSHPROXY_PARAMS and hasattr(self, option):
729 return eval("self.%s" % option)
730 else:
731 raise x2go_exceptions.X2GoProfileException('Unknown session profile option: %s.' % option)
732 __get_session_profile_option = get_session_profile_option
733
735 """\
736 This method can be used to modify L{X2GoSession} parameters after the
737 L{X2GoSession} instance has already been initialized.
738
739 @param params: a Python dictionary with L{X2GoSession} parameters
740 @type params: C{dict}
741
742 """
743 try: del params['server']
744 except KeyError: pass
745 try: del params['profile_name']
746 except KeyError: pass
747 try: del params['profile_id']
748 except KeyError: pass
749 try:
750 self.printing = params['printing']
751 del params['printing']
752 except KeyError: pass
753 try:
754 self.allow_share_local_folders = params['allow_share_local_folders']
755 del params['allow_share_local_folders']
756 except KeyError: pass
757 try:
758 self.share_local_folders = params['share_local_folders']
759 del params['share_local_folders']
760 except KeyError: pass
761 try:
762 self.restore_shared_local_folders = params['restore_shared_local_folders']
763 del params['restore_shared_local_folders']
764 except KeyError: pass
765 try:
766 self.allow_mimebox = params['allow_mimebox']
767 del params['allow_mimebox']
768 except KeyError: pass
769 try:
770 self.mimebox_extensions = params['mimebox_extensions']
771 del params['mimebox_extensions']
772 except KeyError: pass
773 try:
774 self.mimebox_action = params['mimebox_action']
775 del params['mimebox_action']
776 except KeyError: pass
777 try:
778 self.use_sshproxy = params['use_sshproxy']
779 del params['use_sshproxy']
780 except KeyError: pass
781 try:
782 self.sshproxy_reuse_authinfo = params['sshproxy_reuse_authinfo']
783 del params['sshproxy_reuse_authinfo']
784 except KeyError: pass
785 try:
786 self.auto_connect = params['auto_connect']
787 del params['auto_connect']
788 except KeyError: pass
789 try:
790 self.forward_sshagent = params['forward_sshagent']
791 del params['forward_sshagent']
792 except KeyError: pass
793 try:
794 self.auto_start_or_resume = params['auto_start_or_resume']
795 del params['auto_start_or_resume']
796 except KeyError: pass
797
798 if self.sshproxy_reuse_authinfo:
799 if params.has_key('key_filename'):
800 params['sshproxy_key_filename'] = params['key_filename']
801 if params.has_key('pkey'):
802 params['sshproxy_pkey'] = params['pkey']
803 if params.has_key('password'):
804 params['sshproxy_password'] = params['password']
805
806 _terminal_params = copy.deepcopy(params)
807 _control_params = copy.deepcopy(params)
808 _sshproxy_params = copy.deepcopy(params)
809 for p in params.keys():
810 if p in session._X2GO_TERMINAL_PARAMS:
811 del _control_params[p]
812 del _sshproxy_params[p]
813 elif p in session._X2GO_SSHPROXY_PARAMS:
814 del _control_params[p]
815 del _terminal_params[p]
816 else:
817 del _sshproxy_params[p]
818 del _terminal_params[p]
819
820 self.control_params.update(_control_params)
821 self.terminal_params.update(_terminal_params)
822 self.sshproxy_params.update(_sshproxy_params)
823
825 """\
826 Retrieve session UUID hash for this L{X2GoSession}.
827
828 @return: the session's UUID hash
829 @rtype: C{str}
830
831 """
832 return str(self.uuid)
833 __get_uuid = get_uuid
834
836 """\
837 After a session has been set up you can query the
838 username the session runs as.
839
840 @return: the remote username the X2Go session runs as
841 @rtype: C{str}
842
843 """
844
845 try:
846 return self.control_session.get_transport().get_username()
847 except AttributeError:
848 return self.control_params['username']
849 __get_username = get_username
850
852 """\
853 Check if a given user is valid server-side X2Go user.
854
855 @param username: username to check validity for
856 @type username: C{str}
857
858 @return: C{True} if the username is allowed to launch X2Go sessions
859 @rtype: C{bool}
860
861 """
862 if username is None:
863 username = self.__get_username()
864 return self.control_session.is_x2gouser(username)
865 __user_is_x2gouser = user_is_x2gouser
866
868 """\
869 After a session has been setup up you can query the
870 username's password from the session.
871
872 @return: the username's password
873 @rtype: C{str}
874
875 """
876 return self.control_session._session_password
877 __get_password = get_password
878
880 """\
881 After a session has been setup up you can query the
882 peername of the host this session is connected to (or
883 about to connect to).
884
885 @return: the address of the server the X2Go session is
886 connected to (as an C{(addr,port)} tuple)
887 @rtype: C{tuple}
888
889 """
890 return self.control_session.remote_peername()
891 __get_server_peername = get_server_peername
892 remote_peername = get_server_peername
893 __remote_peername = get_server_peername
894
896 """\
897 After a session has been setup up you can query the
898 hostname of the host this session is connected to (or
899 about to connect to).
900
901 @return: the hostname of the server the X2Go session is
902 connected to / about to connect to
903 @rtype: C{str}
904
905 """
906 self.server = self.control_session.get_hostname()
907 return self.server
908 __get_server_hostname = get_server_hostname
909
911 """\
912 After a session has been setup up you can query the
913 IP socket port used for connecting the remote X2Go server.
914
915 @return: the server-side IP socket port that is used by the X2Go session to
916 connect to the server
917 @rtype: C{str}
918
919 """
920 return self.control_session.get_port()
921 __get_server_port = get_server_port
922
924 """\
925 Retrieve the server-side X2Go session name for this session.
926
927 @return: X2Go session name
928 @rtype: C{str}
929
930 """
931 return self.session_name
932 __get_session_name = get_session_name
933
935 """\
936 Manipulate the L{X2GoSession}'s session name.
937
938 @param session_name: the new session name to be set
939 @type session_name: C{str}
940
941 """
942 self.session_name = session_name
943 __set_session_name = set_session_name
944
946 """\
947 Retrieve the server-side X2Go session info object for this session.
948
949 @return: X2Go session info
950 @rtype: C{obj}
951
952 """
953 if self.has_terminal_session():
954 self.terminal_session.get_session_info()
955 __get_session_info = get_session_info
956
958 """\
959 Retrieve the server-side command that is used to start a session
960 on the remote X2Go server.
961
962 @return: server-side session command
963 @rtype: C{str}
964
965 """
966 if self.has_terminal_session():
967 return self.terminal_session.get_session_cmd()
968 if self.terminal_params.has_key('cmd'):
969 return self.terminal_params['cmd']
970 return None
971 __get_session_cmd = get_session_cmd
972
974 """\
975 Retrieve the session type of a session (R, D, S or P).
976
977 - R: rootless session
978 - D: desktop session
979 - S: shadow session
980 - P: session in published applications mode
981
982 @return: session type
983 @rtype: C{str}
984
985 """
986 if self.has_terminal_session():
987 return self.terminal_session.get_session_type()
988 else:
989 return None
990 __get_session_type = get_session_type
991
993 """\
994 Retrieve the session window title of this
995 session.
996
997 @return: session window title
998 @rtype: C{str}
999
1000 """
1001 if self.has_terminal_session():
1002 return self.terminal_session.session_title
1003 else:
1004 return 'X2GO-%s' % self.get_session_name()
1005 __get_session_title = get_session_title
1006
1008 """\
1009 Retrieve the control session (C{X2GoControlSession*} backend) of this L{X2GoSession}.
1010
1011 @return: the L{X2GoSession}'s control session
1012 @rtype: C{X2GoControlSession*} instance
1013
1014 """
1015 return self.control_session
1016 __get_control_session = get_control_session
1017
1019 """\
1020 Check if this L{X2GoSession} instance has an associated control session.
1021
1022 @return: returns C{True} if this L{X2GoSession} has a control session associated to itself
1023 @rtype: C{bool}
1024
1025 """
1026 return self.control_session is not None
1027 __has_control_session = has_control_session
1028
1030 """\
1031 Retrieve the terminal session (C{X2GoTerminalSession*} backend) of this L{X2GoSession}.
1032
1033 @return: the L{X2GoSession}'s terminal session
1034 @rtype: C{X2GoControlTerminal*} instance
1035
1036 """
1037 if self.terminal_session == 'PENDING':
1038 return None
1039 return self.terminal_session
1040 __get_terminal_session = get_terminal_session
1041
1043 """\
1044 Check if this L{X2GoSession} instance has an associated terminal session.
1045
1046 @return: returns C{True} if this L{X2GoSession} has a terminal session associated to itself
1047 @rtype: C{bool}
1048
1049 """
1050 return self.terminal_session not in (None, 'PENDING')
1051 __has_terminal_session = has_terminal_session
1052 is_associated = has_terminal_session
1053 __is_associated = has_terminal_session
1054
1056 """\
1057 Provide a host check mechanism. This method basically calls the L{HOOK_check_host_dialog()} method
1058 which by itself calls the L{X2GoClient.HOOK_check_host_dialog()} method. Make sure you
1059 override any of these to enable user interaction on X2Go server validity checks.
1060
1061 @return: returns C{True} if an X2Go server host is valid for authentication
1062 @rtype: C{bool}
1063
1064 """
1065 if self.connected:
1066 return True
1067
1068 _port = self.control_params['port']
1069 (_valid, _host, _port, _fingerprint, _fingerprint_type) = self.control_session.check_host(self.server, port=_port)
1070 return _valid or self.HOOK_check_host_dialog(host=_host, port=_port, fingerprint=_fingerprint, fingerprint_type=_fingerprint_type)
1071 __check_host = check_host
1072
1074 """\
1075 Check if a session is configured to use an intermediate SSH proxy server.
1076
1077 @return: returns C{True} if the session is configured to use an SSH proxy, C{False} otherwise.
1078 @rtype: C{bool}
1079
1080 """
1081 return self.use_sshproxy
1082 __uses_sshproxy = uses_sshproxy
1083
1085 """\
1086 Check if a session is configured to re-use the X2Go session's password / key for
1087 proxy authentication, as well.
1088
1089 @return: returns C{True} if the session is configured to re-use session password / key for proxy authentication
1090 @rtype: C{bool}
1091
1092 """
1093 return self.sshproxy_reuse_authinfo
1094 __reuses_sshproxy_authinfo = reuses_sshproxy_authinfo
1095
1097 """\
1098 Check if a session's SSH proxy (if used) is configured adequately to be able to auto-connect
1099 to the SSH proxy server (e.g. by public key authentication).
1100
1101 @return: returns C{True} if the session's SSH proxy can auto-connect, C{False} otherwise, C{None}
1102 if no SSH proxy is used for this session, C{None} is returned.
1103 @rtype: C{bool}
1104
1105 """
1106 if self.use_sshproxy:
1107 if self.sshproxy_params.has_key('sshproxy_key_filename') and self.sshproxy_params['sshproxy_key_filename'] and os.path.exists(os.path.normpath(self.sshproxy_params['sshproxy_key_filename'])):
1108 return True
1109 elif self.sshproxy_reuse_authinfo and self.control_params.has_key('key_filename') and self.control_params['key_filename'] and os.path.exists(os.path.normpath(self.control_params['key_filename'])):
1110 return True
1111 elif self.sshproxy_params.has_key('sshproxy_pkey') and self.sshproxy_params['sshproxy_pkey']:
1112 return True
1113 elif self.sshproxy_reuse_authinfo and self.control_params.has_key('pkey') and self.control_params['pkey']:
1114 return True
1115 elif self.sshproxy_params.has_key('sshproxy_look_for_keys') and self.sshproxy_params['sshproxy_look_for_keys'] and (os.path.exists(os.path.expanduser('~/.ssh/id_rsa')) or os.path.exists(os.path.expanduser('~/.ssh/id_dsa'))):
1116 return True
1117 elif self.sshproxy_params.has_key('sshproxy_allow_agent') and self.sshproxy_params['sshproxy_allow_agent'] and paramiko.Agent().get_keys():
1118 return True
1119 else:
1120 return False
1121 else:
1122 return None
1123 __can_sshproxy_auto_connect = can_sshproxy_auto_connect
1124
1126 """\
1127 Check if a session is configured adequately to be able to auto-connect to the X2Go
1128 server (e.g. public key authentication).
1129
1130 @return: returns C{True} if the session can auto-connect, C{False} otherwise, C{None}
1131 if no control session has been set up yet.
1132 @rtype: C{bool}
1133
1134 """
1135 if self.control_session is None:
1136 return None
1137
1138 _can_sshproxy_auto_connect = self.can_sshproxy_auto_connect()
1139
1140
1141 if self.control_params.has_key('key_filename') and self.control_params['key_filename'] and os.path.exists(os.path.normpath(self.control_params['key_filename'])):
1142 return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
1143
1144
1145 elif self.control_params.has_key('pkey') and self.control_params['pkey']:
1146 return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
1147
1148
1149 elif self.control_params.has_key('look_for_keys') and self.control_params['look_for_keys'] and (os.path.exists(os.path.expanduser('~/.ssh/id_rsa')) or os.path.exists(os.path.expanduser('~/.ssh/id_dsa'))):
1150 return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
1151
1152
1153 elif self.control_params.has_key('allow_agent') and self.control_params['allow_agent'] and paramiko.Agent().get_keys():
1154 return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
1155
1156 else:
1157 return False
1158 __can_auto_connect = can_auto_connect
1159
1161 """\
1162 Automatically connect this session.
1163
1164 @return: Return success (or failure) of connecting this sessions
1165 @rtype: C{bool}
1166
1167 """
1168 if not self.is_connected():
1169 if self.client_instance and redirect_to_client:
1170 return self.client_instance.session_auto_connect(self())
1171 else:
1172 if self.can_auto_connect() and self.auto_connect:
1173 gevent.spawn(self.connect)
1174 elif self.auto_connect:
1175 gevent.spawn(self.HOOK_auto_connect)
1176 __do_auto_connect = do_auto_connect
1177
1178 - def connect(self, username='', password='', add_to_known_hosts=False, force_password_auth=False,
1179 look_for_keys=None, allow_agent=None,
1180 use_sshproxy=None, sshproxy_reuse_authinfo=False, sshproxy_user='', sshproxy_password='', sshproxy_force_password_auth=False):
1181 """\
1182 Connects to the L{X2GoSession}'s server host. This method basically wraps around
1183 the C{X2GoControlSession*.connect()} method.
1184
1185 @param username: the username for the X2Go server that is going to be
1186 connected to (as a last minute way of changing the session username)
1187 @type username: C{str}
1188 @param password: the user's password for the X2Go server that is going to be
1189 connected to
1190 @type password: C{str}
1191 @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
1192 is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
1193 is used
1194 @type add_to_known_hosts: C{bool}
1195 @param force_password_auth: disable SSH pub/priv key authentication mechanisms
1196 completely
1197 @type force_password_auth: C{bool}
1198 @param look_for_keys: set to C{True} to enable searching for discoverable
1199 private key files in C{~/.ssh/}
1200 @type look_for_keys: C{bool}
1201 @param allow_agent: set to C{True} to enable connecting to a local SSH agent
1202 for acquiring authentication information
1203 @type allow_agent: C{bool}
1204 @param use_sshproxy: use an SSH proxy host for connecting the target X2Go server
1205 @type use_sshproxy: C{bool}
1206 @param sshproxy_reuse_authinfo: for proxy authentication re-use the X2Go session's password / key file
1207 @type sshproxy_reuse_authinfo: C{bool}
1208 @param sshproxy_user: username for authentication against the SSH proxy host
1209 @type sshproxy_user: C{str}
1210 @param sshproxy_password: password for authentication against the SSH proxy host
1211 @type sshproxy_password: C{str}
1212 @param sshproxy_force_password_auth: enforce password authentication even is a key(file) is present
1213 @type sshproxy_force_password_auth: C{bool}
1214
1215 @return: returns C{True} is the connection to the X2Go server has been successful
1216 @rtype C{bool}
1217
1218 @raise X2GoSessionException: on control session exceptions
1219 @raise X2GoRemoteHomeException: if the remote home directory does not exist
1220 @raise Exception: any other exception during connecting is passed through
1221
1222 """
1223 if self.control_session and self.control_session.is_connected():
1224 self.logger('control session is already connected, skipping authentication', loglevel=log.loglevel_DEBUG)
1225 self.connected = True
1226 else:
1227
1228 if use_sshproxy is not None:
1229 self.use_sshproxy = use_sshproxy
1230
1231 if sshproxy_reuse_authinfo is not None:
1232 self.sshproxy_reuse_authinfo = sshproxy_reuse_authinfo
1233
1234 if username:
1235 self.control_params['username'] = username
1236 if add_to_known_hosts is not None:
1237 self.control_params['add_to_known_hosts'] = add_to_known_hosts
1238 if force_password_auth is not None:
1239 self.control_params['force_password_auth'] = force_password_auth
1240 if look_for_keys is not None:
1241 self.control_params['look_for_keys'] = look_for_keys
1242 if allow_agent is not None:
1243 self.control_params['allow_agent'] = allow_agent
1244
1245 if sshproxy_user:
1246 self.sshproxy_params['sshproxy_user'] = sshproxy_user
1247 if sshproxy_password:
1248 self.sshproxy_params['sshproxy_password'] = sshproxy_password
1249 if sshproxy_force_password_auth:
1250 self.sshproxy_params['sshproxy_force_password_auth'] = sshproxy_force_password_auth
1251
1252 self.control_params['password'] = password
1253
1254 if self.sshproxy_reuse_authinfo:
1255 if self.control_params.has_key('key_filename'):
1256 self.sshproxy_params['sshproxy_key_filename'] = self.control_params['key_filename']
1257 if self.control_params.has_key('pkey'):
1258 self.sshproxy_params['sshproxy_pkey'] = self.control_params['pkey']
1259 if self.control_params.has_key('password'):
1260 self.sshproxy_params['sshproxy_password'] = self.control_params['password']
1261
1262 _params = {}
1263 _params.update(self.control_params)
1264 _params.update(self.sshproxy_params)
1265
1266 if 'port' not in _params:
1267 _params['port'] = self.port
1268
1269 try:
1270 self.connected = self.control_session.connect(self.server,
1271 use_sshproxy=self.use_sshproxy,
1272 session_instance=self,
1273 forward_sshagent=self.forward_sshagent,
1274 **_params)
1275 except x2go_exceptions.X2GoControlSessionException, e:
1276 raise x2go_exceptions.X2GoSessionException(str(e))
1277 except x2go_exceptions.X2GoRemoteHomeException, e:
1278 self.disconnect()
1279 raise e
1280 except:
1281
1282 self.control_params['password'] = ''
1283 if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
1284 del self.sshproxy_params['sshproxy_password']
1285 raise
1286 finally:
1287
1288 self.control_params['password'] = ''
1289 if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
1290 del self.sshproxy_params['sshproxy_password']
1291
1292 if not self.connected:
1293
1294 self.disconnect()
1295
1296 self.get_server_hostname()
1297
1298 if self.connected:
1299 self.update_status()
1300 self.retrieve_server_features()
1301 if self.auto_start_or_resume:
1302 gevent.spawn(self.do_auto_start_or_resume)
1303
1304 return self.connected
1305 __connect = connect
1306
1308 """\
1309 Disconnect this L{X2GoSession} instance.
1310
1311 @return: returns C{True} if the disconnect operation has been successful
1312 @rtype: C{bool}
1313
1314 """
1315 self.connected = False
1316 self.running = None
1317 self.suspended = None
1318 self.terminated = None
1319 self.faults = None
1320 self.active = False
1321 self._lock.release()
1322 self.unset_master_session()
1323 try:
1324 self.update_status(force_update=True)
1325 except x2go_exceptions.X2GoControlSessionException:
1326 pass
1327 retval = self.control_session.disconnect()
1328 return retval
1329 __disconnect = disconnect
1330
1332 """\
1333 Query the X2Go server for a list of supported features.
1334
1335 """
1336 self.server_features = self.control_session.query_server_features()
1337 __retrieve_server_features = retrieve_server_features
1338
1340 """\
1341 Return a list of X2Go server-sides features (supported functionalities).
1342
1343 @return: a C{list} of X2Go feature names
1344 @rtype: C{list}
1345
1346 """
1347 return self.server_features
1348 __get_server_features = get_server_features
1349
1351 """\
1352 Check if C{feature} is a present feature of the connected X2Go server.
1353
1354 @param feature: an X2Go server feature as found in C{$SHAREDIR/x2go/feature.d/*}
1355 @type feature: C{str}
1356
1357 @return: returns C{True} if the feature is present
1358 @rtype: C{bool}
1359
1360 """
1361 return feature in self.get_server_features()
1362 __has_server_feature = has_server_feature
1363
1365 """\
1366 Modify session window title. If the session ID does not occur in the
1367 given title, it will be prepended, so that every X2Go session window
1368 always contains the X2Go session ID of that window.
1369
1370 @param title: new title for session window
1371 @type title: C{str}
1372
1373 """
1374 if self.terminal_session is not None:
1375 self.terminal_session.set_session_window_title(title=title)
1376 __set_session_window_title = set_session_window_title
1377
1379 """\
1380 Try to lift the session window above all other windows and bring
1381 it to focus.
1382
1383 """
1384 if self.terminal_session is not None:
1385 self.terminal_session.raise_session_window()
1386 __raise_session_window = raise_session_window
1387
1389 """\
1390 If X2Go client-side printing is enable within this X2Go session you can use
1391 this method to alter the way how incoming print spool jobs are handled/processed.
1392
1393 For further information, please refer to the documentation of the L{X2GoClient.set_session_print_action()}
1394 method.
1395
1396 @param print_action: one of the named above print actions, either as string or class instance
1397 @type print_action: C{str} or C{instance}
1398 @param kwargs: additional information for the given print action (print
1399 action arguments), for possible print action arguments and their values see each individual
1400 print action class
1401 @type kwargs: C{dict}
1402
1403 """
1404 if type(print_action) is not types.StringType:
1405 return False
1406 self.terminal_session.set_print_action(print_action, **kwargs)
1407 __set_print_action = set_print_action
1408
1410 """\
1411 Find out if this X2Go session is still alive (that is: connected to the server).
1412
1413 @return: returns C{True} if the server connection is still alive
1414 @rtype: C{bool}
1415
1416 """
1417 self.connected = self.control_session.is_alive()
1418 if self.control_session.has_session_died():
1419 self.HOOK_on_control_session_death()
1420 if not self.connected:
1421 self._X2GoSession__disconnect()
1422 return self.connected
1423 __is_alive = is_alive
1424
1425 - def clean_sessions(self, destroy_terminals=True, published_applications=False):
1426 """\
1427 Clean all running sessions for the authenticated user on the remote X2Go server.
1428
1429 @param destroy_terminals: destroy associated terminal sessions
1430 @type destroy_terminals: C{bool}
1431 @param published_applications: clean sessions that are published applications providers, too
1432 @type published_applications: C{bool}
1433
1434 """
1435 if self.is_alive():
1436
1437
1438 if self.has_terminal_session():
1439 self.unshare_all_local_folders(force_all=True)
1440
1441 self.control_session.clean_sessions(destroy_terminals=destroy_terminals, published_applications=published_applications)
1442 else:
1443 self._X2GoSession__disconnect()
1444 __clean_sessions = clean_sessions
1445
1447 """\
1448 List all sessions on the remote X2Go server that are owned by the authenticated user
1449
1450 @param raw: if C{True} the output of this method equals
1451 the output of the server-side C{x2golistsessions} command
1452 @type raw: C{bool}
1453
1454 @return: a session list (as data object or list of strings when called with C{raw=True} option)
1455 @rtype: C{X2GoServerSessionList*} instance or C{list}
1456
1457 """
1458 try:
1459 return self.control_session.list_sessions(raw=raw)
1460 except x2go_exceptions.X2GoControlSessionException:
1461 if self.connected: self.HOOK_on_control_session_death()
1462 self._X2GoSession__disconnect()
1463 return None
1464 __list_sessions = list_sessions
1465
1467 """\
1468 List X2Go desktops sessions available for desktop sharing on the remote X2Go server.
1469
1470 @param raw: if C{True} the output of this method equals
1471 the output of the server-side C{x2golistdesktops} command
1472 @type raw: C{bool}
1473
1474 @return: a list of strings representing available desktop sessions
1475 @rtype: C{list}
1476
1477 """
1478 try:
1479 return self.control_session.list_desktops(raw=raw)
1480 except x2go_exceptions.X2GoTimeoutException:
1481 if self.is_alive(): self.HOOK_list_desktop_timeout()
1482 return []
1483 except x2go_exceptions.X2GoControlSessionException:
1484 if self.connected: self.HOOK_on_control_session_death()
1485 self._X2GoSession__disconnect()
1486 return None
1487 __list_desktops = list_desktops
1488
1490 """\
1491 Use the X2Go session registered under C{session_uuid} to
1492 retrieve its list of mounted client shares for that session.
1493
1494 @param raw: output the list of mounted client shares in X2Go's
1495 raw C{x2golistmounts} format
1496 @type raw: C{bool}
1497
1498 @return: a list of strings representing mounted client shares for this session
1499 @rtype: C{list}
1500
1501 """
1502 try:
1503 return self.control_session.list_mounts(self.session_name, raw=raw)
1504 except x2go_exceptions.X2GoControlSessionException:
1505 if self.connected: self.HOOK_on_control_session_death()
1506 self._X2GoSession__disconnect()
1507 return None
1508 __list_mounts = list_mounts
1509
1510 - def update_status(self, session_list=None, force_update=False):
1511 """\
1512 Update the current session status. The L{X2GoSession} instance uses an internal
1513 session status cache that allows to query the session status without the need
1514 of retrieving data from the remote X2Go server for each query.
1515
1516 The session status (if initialized properly with the L{X2GoClient} constructor gets
1517 updated in regularly intervals.
1518
1519 In case you use the L{X2GoSession} class in standalone instances (that is: without
1520 being embedded into an L{X2GoSession} context) then run this method in regular
1521 intervals to make sure the L{X2GoSession}'s internal status cache information
1522 is always up-to-date.
1523
1524 @param session_list: provide an C{X2GoServerSessionList*} that refers to X2Go sessions we want to update.
1525 This option is mainly for reducing server/client traffic.
1526 @type session_list: C{X2GoServerSessionList*} instance
1527 @param force_update: force a session status update, if if the last update is less then 1 second ago
1528 @type force_update: C{bool}
1529
1530 @raise Exception: any exception is passed through in case the session disconnected surprisingly
1531 or has been marked as faulty
1532
1533 """
1534 if not force_update and self._last_status is not None:
1535 _status_update_timedelta = time.time() - self._last_status['timestamp']
1536
1537
1538 if _status_update_timedelta < 1:
1539 self.logger('status update interval too short (%s), skipping status update this time...' % _status_update_timedelta, loglevel=log.loglevel_DEBUG)
1540 return False
1541
1542 e = None
1543 self._last_status = copy.deepcopy(self._current_status)
1544 if session_list is None:
1545 try:
1546 session_list = self.control_session.list_sessions()
1547 self.connected = True
1548 except x2go_exceptions.X2GoControlSessionException, e:
1549 self.connected = False
1550 self.running = None
1551 self.suspended = None
1552 self.terminated = None
1553 self.faulty = None
1554
1555 if self.connected:
1556 try:
1557 _session_name = self.get_session_name()
1558 _session_info = session_list[_session_name]
1559 self.running = _session_info.is_running()
1560 self.suspended = _session_info.is_suspended()
1561 if not self.virgin:
1562 self.terminated = not (self.running or self.suspended)
1563 else:
1564 self.terminated = None
1565 except KeyError, e:
1566 self.running = False
1567 self.suspended = False
1568 if not self.virgin:
1569 self.terminated = True
1570 self.faulty = not (self.running or self.suspended or self.terminated or self.virgin)
1571
1572 self._current_status = {
1573 'timestamp': time.time(),
1574 'server': self.server,
1575 'virgin': self.virgin,
1576 'connected': self.connected,
1577 'running': self.running,
1578 'suspended': self.suspended,
1579 'terminated': self.terminated,
1580 'faulty': self.faulty,
1581 }
1582
1583 if (not self.connected or self.faulty) and e:
1584 raise e
1585
1586 return True
1587 __update_status = update_status
1588
1590 """\
1591 Returns true if this session runs in published applications mode.
1592
1593 @return: returns C{True} if this session is a provider session for published applications.
1594 @rtype: C{bool}
1595
1596 """
1597 if self.has_terminal_session() and self.is_running() :
1598 return self.terminal_session.is_published_applications_provider()
1599 return False
1600 __is_published_applications_provider = is_published_applications_provider
1601
1603 """\
1604 Return a list of published menu items from the X2Go server
1605 for session type published applications.
1606
1607 @param lang: locale/language identifier
1608 @type lang: C{str}
1609 @param refresh: force reload of the menu tree from X2Go server
1610 @type refresh: C{bool}
1611 @param raw: retrieve a raw output of the server list of published applications
1612 @type raw: C{bool}
1613 @param very_raw: retrieve a very raw output of the server list of published applications (as-is output of x2gogetapps script)
1614 @type very_raw: C{bool}
1615
1616 @return: A C{list} of C{dict} elements. Each C{dict} elements has a
1617 C{desktop} key containing the text output of a .desktop file and
1618 an C{icon} key which contains the desktop icon data base64 encoded
1619 @rtype: C{list}
1620
1621 """
1622 if self.client_instance and hasattr(self.client_instance, 'lang'):
1623 lang = self.client_instance.lang
1624 return self.control_session.get_published_applications(lang=lang, refresh=refresh, raw=raw, very_raw=very_raw, max_no_submenus=max_no_submenus)
1625 __get_published_applications = get_published_applications
1626
1628 """\
1629 Execute an application while in published application mode.
1630
1631 @param exec_name: command to execute on server
1632 @type exec_name: C{str}
1633
1634 """
1635 if self.terminal_session is not None:
1636 self.logger('for %s executing published application: %s' % (self.profile_name, exec_name), loglevel=log.loglevel_NOTICE)
1637 self.terminal_session.exec_published_application(exec_name, timeout=timeout, env=self.session_environment)
1638 __exec_published_application = exec_published_application
1639
1640 - def do_auto_start_or_resume(self, newest=True, oldest=False, all_suspended=False, start=True, redirect_to_client=True):
1641 """\
1642 Automatically start or resume this session, if already associated with a server session. Otherwise
1643 resume a server-side available/suspended session (see options to declare which session to resume).
1644 If no session is available for resuming a new session will be launched.
1645
1646 Sessions in published applications mode are not resumed/started by this method.
1647
1648 @param newest: if resuming, only resume newest/youngest session
1649 @type newest: C{bool}
1650 @param oldest: if resuming, only resume oldest session
1651 @type oldest: C{bool}
1652 @param all_suspended: if resuming, resume all suspended sessions
1653 @type all_suspended: C{bool}
1654 @param start: is no session is to be resumed, start a new session
1655 @type start: C{bool}
1656 @param redirect_to_client: redirect this call to the L{X2GoClient} instance (if available) to allow frontend interaction
1657 @type redirect_to_client: C{bool}
1658
1659 @return: returns success (or failure) of starting/resuming this sessions
1660 @rtype: C{bool}
1661
1662 """
1663 if self.client_instance and redirect_to_client:
1664 return self.client_instance.session_auto_start_or_resume(self())
1665 else:
1666 if self.session_name is not None and 'PUBLISHED' not in self.session_name:
1667 return self.resume()
1668 else:
1669 session_infos = self.list_sessions()
1670
1671
1672 for session_name in session_infos.keys():
1673 if session_infos[session_name].is_published_applications_provider():
1674 del session_infos[session_name]
1675
1676 if session_infos:
1677 sorted_session_names = utils.session_names_by_timestamp(session_infos)
1678 if newest:
1679 if sorted_session_names[0].find('RDP') == -1:
1680 return self.resume(session_name=sorted_session_names[-1])
1681 elif oldest:
1682 if sorted_session_names[-1].find('RDP') == -1:
1683 return self.resume(session_name=sorted_session_names[0])
1684 elif all_suspended:
1685 for session_name in [ _sn for _sn in session_infos.keys() if session_infos[_sn].is_suspended() ]:
1686 return self.resume(session_name=session_name)
1687 else:
1688 if not self.published_applications:
1689 return self.start()
1690 __do_auto_start_or_resume = do_auto_start_or_resume
1691
1693 """\
1694 Reset session startup/resumption progress status.
1695
1696 """
1697 self._progress_status = 0
1698
1700 """\
1701 Retrieve session startup/resumption progress status.
1702
1703 @return: returns an C{int} value between 0 and 100 reflecting the session startup/resumption status
1704 @rtype: C{int}
1705
1706 """
1707 return self._progress_status
1708
1709 - def resume(self, session_name=None, session_list=None, cmd=None, progress_event=None):
1710 """\
1711 Resume or continue a suspended / running X2Go session on the
1712 remote X2Go server.
1713
1714 @param session_name: the server-side name of an X2Go session
1715 @type session_name: C{str}
1716 @param session_list: a session list to avoid a server-side session list query
1717 @type session_list: C{dict}
1718 @param cmd: if starting a new session, manually hand over the command to be launched in
1719 the new session
1720 @type cmd: C{str}
1721 @param progress_event: a C{thread.Event} object that notifies a status object like the one in
1722 L{utils.ProgressStatus}.
1723 @type progress_event: C{obj}
1724
1725 @return: returns C{True} if resuming the session has been successful, C{False} otherwise
1726 @rtype: C{bool}
1727
1728 @raise Exception: any exception that occurs during published application menu retrieval is passed through
1729
1730 """
1731 self._lock.acquire()
1732 try:
1733 _retval = self._resume(session_name=session_name, session_list=session_list, cmd=cmd, progress_event=progress_event)
1734 except:
1735 raise
1736 finally:
1737 self._lock.release()
1738 return _retval
1739
1740 - def _resume(self, session_name=None, session_list=None, cmd=None, progress_event=None):
1741 """\
1742 Resume or continue a suspended / running X2Go session on the
1743 remote X2Go server.
1744
1745 @param session_name: the server-side name of an X2Go session
1746 @type session_name: C{str}
1747 @param session_list: a session list to avoid a server-side session list query
1748 @type session_list: C{dict}
1749 @param cmd: if starting a new session, manually hand over the command to be launched in
1750 the new session
1751 @type cmd: C{str}
1752 @param progress_event: a C{thread.Event} object that notifies a status object like the one in
1753 L{utils.ProgressStatus}.
1754 @type progress_event: C{obj}
1755
1756 @return: returns C{True} if resuming the session has been successful, C{False} otherwise
1757 @rtype: C{bool}
1758
1759 @raise Exception: any exception that occurs during published application menu retrieval is passed through
1760
1761 """
1762 self.terminal_session = 'PENDING'
1763
1764
1765 self.reset_progress_status()
1766 _dummy_event = threading.Event()
1767 if type(progress_event) != type(_dummy_event):
1768 progress_event = _dummy_event
1769
1770 self._progress_status = 1
1771 progress_event.set()
1772
1773 _new_session = False
1774 if self.session_name is None:
1775 self.session_name = session_name
1776
1777 self._progress_status = 2
1778 progress_event.set()
1779
1780 if self.is_alive():
1781
1782 self._progress_status = 5
1783 progress_event.set()
1784
1785 _control = self.control_session
1786
1787 self._progress_status = 7
1788 progress_event.set()
1789
1790
1791
1792
1793
1794 if self.is_running():
1795 try:
1796
1797 self._suspend()
1798
1799 self._progress_status = 10
1800 progress_event.set()
1801
1802 gevent.sleep(5)
1803
1804 self._progress_status = 15
1805 progress_event.set()
1806
1807 except x2go_exceptions.X2GoSessionException:
1808 pass
1809
1810 self._progress_status = 20
1811 progress_event.set()
1812
1813 try:
1814 if self.published_applications:
1815 self.published_applications_menu = gevent.spawn(self.get_published_applications)
1816 except:
1817
1818
1819 self._progress_status = -1
1820 progress_event.set()
1821 raise
1822
1823 if cmd is not None:
1824 self.terminal_params['cmd'] = cmd
1825
1826 self.terminal_session = _control.resume(session_name=self.session_name,
1827 session_instance=self,
1828 session_list=session_list,
1829 logger=self.logger, **self.terminal_params)
1830
1831 self._progress_status = 25
1832 progress_event.set()
1833
1834 if self.session_name is None:
1835 _new_session = True
1836 try:
1837 self.session_name = self.terminal_session.session_info.name
1838 except AttributeError:
1839
1840 self.HOOK_session_startup_failed()
1841
1842 self._progress_status = -1
1843 progress_event.set()
1844
1845 return False
1846
1847 self._progress_status = 30
1848 progress_event.set()
1849
1850 if self.has_terminal_session() and not self.faulty:
1851
1852 self.terminal_session.session_info_protect()
1853
1854 if self.get_session_cmd() != 'PUBLISHED':
1855 self.published_applications = False
1856
1857 self._progress_status = 40
1858 progress_event.set()
1859
1860 if self._SUPPORTED_SOUND and self.terminal_session.params.snd_system is not 'none':
1861 self.has_terminal_session() and not self.faulty and self.terminal_session.start_sound()
1862 else:
1863 self._SUPPORTED_SOUND = False
1864
1865 self._progress_status = 50
1866 progress_event.set()
1867
1868 try:
1869 if (self._SUPPORTED_PRINTING and self.printing) or \
1870 (self._SUPPORTED_MIMEBOX and self.allow_mimebox) or \
1871 (self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders):
1872 self.has_terminal_session() and not self.faulty and self.terminal_session.start_sshfs()
1873 except x2go_exceptions.X2GoUserException, e:
1874 self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
1875 self.HOOK_sshfs_not_available()
1876 self._SUPPORTED_PRINTING = False
1877 self._SUPPORTED_MIMEBOX = False
1878 self._SUPPORTED_FOLDERSHARING = False
1879
1880 self._progress_status = 60
1881 progress_event.set()
1882
1883 if self._SUPPORTED_PRINTING and self.printing:
1884 try:
1885 self.has_terminal_session() and not self.faulty and self.terminal_session.start_printing()
1886 self.has_terminal_session() and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
1887 except x2go_exceptions.X2GoUserException, e:
1888 self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
1889 self.HOOK_printing_not_available()
1890 self._SUPPORTED_PRINTING = False
1891
1892 self._progress_status = 70
1893 progress_event.set()
1894
1895 if self._SUPPORTED_MIMEBOX and self.allow_mimebox:
1896 try:
1897 self.has_terminal_session() and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
1898 self.has_terminal_session() and self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
1899 except x2go_exceptions.X2GoUserException, e:
1900 self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
1901 self.HOOK_mimebox_not_available()
1902 self._SUPPORTED_MIMEBOX = False
1903
1904 self._progress_status = 80
1905 progress_event.set()
1906
1907
1908 if _new_session:
1909 self.has_terminal_session() and self.terminal_session.run_command(env=self.session_environment)
1910
1911 self.virgin = False
1912 self.suspended = False
1913 self.running = True
1914 self.terminated = False
1915 self.faulty = False
1916
1917 self._progress_status = 90
1918 progress_event.set()
1919
1920
1921 if (not self.client_instance) and \
1922 self._SUPPORTED_FOLDERSHARING and \
1923 self.allow_share_local_folders:
1924 gevent.spawn(self.share_all_local_folders)
1925
1926 self._progress_status = 100
1927 progress_event.set()
1928
1929 self.has_terminal_session() and self.terminal_session.session_info_unprotect()
1930 return True
1931
1932 else:
1933 self.terminal_session = None
1934
1935 self._progress_status = -1
1936 progress_event.set()
1937
1938 return False
1939
1940 else:
1941
1942 self._progress_status = -1
1943 progress_event.set()
1944
1945 self._X2GoSession__disconnect()
1946 return False
1947 __resume = resume
1948
1949 - def start(self, cmd=None, progress_event=None):
1950 """\
1951 Start a new X2Go session on the remote X2Go server.
1952
1953 @param cmd: manually hand over the command that is to be launched in the new session
1954 @type cmd: C{str}
1955 @param progress_event: a C{thread.Event} object that notifies a status object like the one in
1956 L{utils.ProgressStatus}.
1957 @type progress_event: C{obj}
1958
1959 @return: returns C{True} if starting the session has been successful, C{False} otherwise
1960 @rtype: C{bool}
1961
1962 """
1963 self.session_name = None
1964 return self.resume(cmd=cmd, progress_event=progress_event)
1965 __start = start
1966
1967 - def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True, progress_event=None):
1968 """\
1969 Share an already running X2Go session on the remote X2Go server locally. The shared session may be either
1970 owned by the same user or by a user that grants access to his/her desktop session by the local user.
1971
1972 @param desktop: desktop ID of a sharable desktop in format <user>@<display>
1973 @type desktop: C{str}
1974 @param user: user name and display number can be given separately, here give the
1975 name of the user who wants to share a session with you.
1976 @type user: C{str}
1977 @param display: user name and display number can be given separately, here give the
1978 number of the display that a user allows you to be shared with.
1979 @type display: C{str}
1980 @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
1981 @type share_mode: C{int}
1982 @param check_desktop_list: check if the given desktop is available on the X2Go server; handle with care as
1983 the server-side C{x2golistdesktops} command might block client I/O.
1984 @type check_desktop_list: C{bool}
1985 @param progress_event: a C{thread.Event} object that notifies a status object like the one in
1986 L{utils.ProgressStatus}.
1987 @type progress_event: C{obj}
1988
1989 @return: returns C{True} if starting the session has been successful, C{False} otherwise
1990 @rtype: C{bool}
1991
1992 @raise X2GoDesktopSharingException: if the given desktop ID is not an available desktop session on the remote server
1993 @raise X2GoSessionException: if the available desktop session appears to be dead, in fact
1994
1995 """
1996 self._lock.acquire()
1997 try:
1998 _retval = self._share_desktop(desktop=desktop, user=user, display=display, share_mode=share_mode, check_desktop_list=check_desktop_list, progress_event=progress_event)
1999 except:
2000 raise
2001 finally:
2002 self._lock.release()
2003 return _retval
2004
2005 - def _share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True, progress_event=None):
2006 """\
2007 Share an already running X2Go session on the remote X2Go server locally. The shared session may be either
2008 owned by the same user or by a user that grants access to his/her desktop session by the local user.
2009
2010 @param desktop: desktop ID of a sharable desktop in format <user>@<display>
2011 @type desktop: C{str}
2012 @param user: user name and display number can be given separately, here give the
2013 name of the user who wants to share a session with you.
2014 @type user: C{str}
2015 @param display: user name and display number can be given separately, here give the
2016 number of the display that a user allows you to be shared with.
2017 @type display: C{str}
2018 @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
2019 @type share_mode: C{int}
2020 @param check_desktop_list: check if the given desktop is available on the X2Go server; handle with care as
2021 the server-side C{x2golistdesktops} command might block client I/O.
2022 @type check_desktop_list: C{bool}
2023 @param progress_event: a C{thread.Event} object that notifies a status object like the one in
2024 L{utils.ProgressStatus}.
2025 @type progress_event: C{obj}
2026
2027 @return: returns C{True} if starting the session has been successful, C{False} otherwise
2028 @rtype: C{bool}
2029
2030 @raise X2GoDesktopSharingException: if the given desktop ID is not an available desktop session on the remote server
2031 @raise X2GoSessionException: if the available desktop session appears to be dead, in fact
2032
2033 """
2034 self.terminal_session = 'PENDING'
2035
2036
2037 self.reset_progress_status()
2038 _dummy_event = threading.Event()
2039 if type(progress_event) != type(_dummy_event):
2040 progress_event = _dummy_event
2041
2042 self._progress_status = 5
2043 progress_event.set()
2044
2045 _desktop = desktop or '%s@%s' % (user, display)
2046 if check_desktop_list:
2047 desktop_list = self._X2GoSession__list_desktops()
2048 if not _desktop in desktop_list:
2049 _orig_desktop = _desktop
2050 _desktop = '%s.0' % _desktop
2051 if not _desktop in desktop_list:
2052 self.HOOK_no_such_desktop(desktop=_orig_desktop)
2053 self._progress_status = -1
2054 progress_event.set()
2055 return False
2056
2057 self._progress_status = 33
2058 progress_event.set()
2059
2060 _session_owner = _desktop.split('@')[0]
2061
2062 if self.is_alive():
2063 if self.get_username() != _session_owner:
2064 self.logger('waiting for user ,,%s\'\' to interactively grant you access to his/her desktop session...' % _session_owner, loglevel=log.loglevel_NOTICE)
2065 self.logger('THIS MAY TAKE A WHILE!', loglevel=log.loglevel_NOTICE)
2066
2067 self._progress_status = 50
2068 progress_event.set()
2069
2070 _control = self.control_session
2071 try:
2072 self.terminal_session = _control.share_desktop(desktop=_desktop, share_mode=share_mode,
2073 logger=self.logger, **self.terminal_params)
2074
2075 self._progress_status = 80
2076 progress_event.set()
2077
2078 except ValueError:
2079
2080
2081
2082 self._progress_status = -1
2083 progress_event.set()
2084
2085 raise x2go_exceptions.X2GoSessionException('the session on desktop %s is seemingly dead' % _desktop)
2086
2087 self._progress_status = 90
2088 progress_event.set()
2089
2090 if self.has_terminal_session():
2091 self.session_name = self.terminal_session.session_info.name
2092
2093
2094
2095 self.terminal_session.run_command(env=self.session_environment)
2096
2097 self.virgin = False
2098 self.suspended = False
2099 self.running = True
2100 self.terminated = False
2101 self.faulty = False
2102
2103 self._progress_status = 100
2104 progress_event.set()
2105
2106 return self.running
2107 else:
2108 self.terminal_session = None
2109
2110 self._progress_status = -1
2111 progress_event.set()
2112
2113 else:
2114
2115 self._progress_status = -1
2116 progress_event.set()
2117
2118 self._X2GoSession__disconnect()
2119
2120 return False
2121 __share_desktop = share_desktop
2122
2124 """\
2125 Test if this X2Go session is a desktop session.
2126
2127 @return: C{True} if this session is of session type desktop ('D').
2128 @rtype: C{bool}
2129
2130 """
2131 if self.has_terminal_session():
2132 return self.terminal_session.is_desktop_session()
2133 __is_desktop_session = is_desktop_session
2134
2136 """\
2137 Test if this X2Go session is a rootless session.
2138
2139 @return: C{True} if this session is of session type rootless ('R').
2140 @rtype: C{bool}
2141
2142 """
2143 if self.has_terminal_session():
2144 return self.terminal_session.is_rootless_session()
2145 __is_rootless_session = is_rootless_session
2146
2148 """\
2149 Test if this X2Go session is a desktop sharing (aka shadow) session.
2150
2151 @return: C{True} if this session is of session type shadow ('S').
2152 @rtype: C{bool}
2153
2154 """
2155 if self.has_terminal_session():
2156 return self.terminal_session.is_shadow_session()
2157 __is_shadow_session = is_shadow_session
2158
2160 """\
2161 Test if this X2Go session is a published applications session.
2162
2163 @return: C{True} if this session is of session type published applications ('P').
2164 @rtype: C{bool}
2165
2166 """
2167 if self.has_terminal_session():
2168 return self.terminal_session.is_pubapp_session()
2169 __is_pubapp_session = is_pubapp_session
2170
2172 """\
2173 Suspend this X2Go session.
2174
2175 @return: returns C{True} if suspending the session has been successful, C{False} otherwise
2176 @rtype: C{bool}
2177
2178 @raise X2GoSessionException: if the session could not be suspended
2179
2180 """
2181 self._lock.acquire()
2182 try:
2183 _retval = self._suspend()
2184 except:
2185 raise
2186 finally:
2187 self._lock.release()
2188
2189 return _retval
2190
2192 """\
2193 Suspend this X2Go session.
2194
2195 @return: returns C{True} if suspending the session has been successful, C{False} otherwise
2196 @rtype: C{bool}
2197
2198 @raise X2GoSessionException: if the session could not be suspended
2199
2200 """
2201 if self.is_alive():
2202 if self.has_terminal_session():
2203
2204 self.running = False
2205 self.suspended = True
2206 self.terminated = False
2207 self.faulty = False
2208 self.active = False
2209
2210
2211 self.unshare_all_local_folders(force_all=True, update_exported_folders=False)
2212
2213 self.unset_master_session()
2214
2215 if self.has_terminal_session():
2216 if self.terminal_session.suspend():
2217 self.session_cleanup()
2218 del self.terminal_session
2219 self.terminal_session = None
2220 return True
2221
2222 elif self.has_control_session() and self.session_name:
2223 if self.control_session.suspend(session_name=self.session_name):
2224
2225 self.running = False
2226 self.suspended = True
2227 self.terminated = False
2228 self.faulty = False
2229 self.active = False
2230 self.session_cleanup()
2231 return True
2232
2233 else:
2234 raise x2go_exceptions.X2GoSessionException('cannot suspend session')
2235
2236 else:
2237 self._X2GoSession__disconnect()
2238
2239 return False
2240 __suspend = suspend
2241
2243 """\
2244 Terminate this X2Go session.
2245
2246 @return: returns C{True} if terminating the session has been successful, C{False} otherwise
2247 @rtype: C{bool}
2248
2249 @raise X2GoSessionException: if the session could not be terminated
2250
2251 """
2252 self._lock.acquire()
2253 try:
2254 _retval = self._terminate()
2255 except:
2256 raise
2257 finally:
2258 self._lock.release()
2259
2260 return _retval
2261
2263 """\
2264 Terminate this X2Go session.
2265
2266 @return: returns C{True} if terminating the session has been successful, C{False} otherwise
2267 @rtype: C{bool}
2268
2269 @raise X2GoSessionException: if the session could not be terminated
2270
2271 """
2272 if self.is_alive():
2273 if self.has_terminal_session():
2274
2275 self.running = False
2276 self.suspended = False
2277 self.terminated = True
2278 self.faulty = False
2279 self.active = False
2280
2281
2282 self.unshare_all_local_folders(force_all=True, update_exported_folders=False)
2283
2284 self.unset_master_session()
2285
2286 if self.has_terminal_session():
2287 if self.terminal_session.terminate():
2288 self.session_cleanup()
2289 del self.terminal_session
2290 self.terminal_session = None
2291 return True
2292
2293 elif self.has_control_session() and self.session_name:
2294 if self.control_session.terminate(session_name=self.session_name):
2295
2296 self.running = False
2297 self.suspended = False
2298 self.terminated = True
2299 self.faulty = False
2300 self.active = False
2301 self.session_cleanup()
2302 return True
2303 else:
2304 raise x2go_exceptions.X2GoSessionException('cannot terminate session')
2305
2306 else:
2307 self._X2GoSession__disconnect()
2308
2309 return False
2310 __terminate = terminate
2311
2313 """\
2314 Retrieve the profile name of this L{X2GoSession} instance.
2315
2316 @return: X2Go client profile name of the session
2317 @rtype: C{str}
2318
2319 """
2320 return self.profile_name
2321 __get_profile_name = get_profile_name
2322
2324 """\
2325 Retrieve the profile ID of this L{X2GoSession} instance.
2326
2327 @return: the session profile's id
2328 @rtype: C{str}
2329
2330 """
2331 return self.profile_id
2332 __get_profile_id = get_profile_id
2333
2334
2335
2336
2337
2339 """\
2340 Test if this C{X2GoSession} is
2341 in a healthy state.
2342
2343 @return: C{True} if session is ok, C{False} otherwise
2344 @rtype: C{bool}
2345
2346 """
2347 if self.has_terminal_session():
2348 return self.terminal_session.ok()
2349 return False
2350 __session_ok = session_ok
2351
2353 """\
2354 Extract color depth from session name.
2355
2356 @return: the session's color depth (as found in the session name)
2357 @rtype: C{str}
2358
2359 """
2360 return int(self.get_session_name().split('_')[2][2:])
2361 __color_depth_from_session_name = color_depth_from_session_name
2362
2364 """\
2365 Check if this session will display properly with the local screen's color depth.
2366
2367 @return: C{True} if the session will display on this client screen,
2368 C{False} otherwise. If no terminal session is yet registered with this session, C{None} is returned.
2369 @rtype: C{bool}
2370
2371 """
2372 return utils.is_color_depth_ok(depth_session=self.color_depth_from_session_name(), depth_local=utils.local_color_depth())
2373 __is_color_depth_ok = is_color_depth_ok
2374
2376 """\
2377 Test if the L{X2GoSession}'s control session is connected to the
2378 remote X2Go server.
2379
2380 @return: C{True} if session is connected, C{False} otherwise
2381 @rtype: C{bool}
2382
2383 """
2384 self.connected = bool(self.control_session and self.control_session.is_connected())
2385 if not self.connected:
2386 self.running = None
2387 self.suspended = None
2388 self.terminated = None
2389 self.faulty = None
2390 return self.connected
2391 __is_connected = is_connected
2392
2394 """\
2395 Test if the L{X2GoSession}'s terminal session is up and running.
2396
2397 @return: C{True} if session is running, C{False} otherwise
2398 @rtype: C{bool}
2399
2400 """
2401 if not update_status:
2402 return self.running
2403
2404 if self.is_connected():
2405 self.running = self.control_session.is_running(self.get_session_name())
2406 if self.running:
2407 self.suspended = False
2408 self.terminated = False
2409 self.faulty = False
2410 if self.virgin and not self.running:
2411 self.running = None
2412 return self.running
2413 __is_running = is_running
2414
2416 """\
2417 Test if the L{X2GoSession}'s terminal session is in suspended state.
2418
2419 @return: C{True} if session is suspended, C{False} otherwise
2420 @rtype: C{bool}
2421
2422 """
2423 if not update_status:
2424 return self.suspended
2425
2426 if self.is_connected():
2427 self.suspended = self.control_session.is_suspended(self.get_session_name())
2428 if self.suspended:
2429 self.running = False
2430 self.terminated = False
2431 self.faulty = False
2432 if self.virgin and not self.suspended:
2433 self.suspended = None
2434 return self.suspended
2435 __is_suspended = is_suspended
2436
2438 """\
2439 Test if the L{X2GoSession}'s terminal session has terminated.
2440
2441 @return: C{True} if session has terminated, C{False} otherwise
2442 @rtype: C{bool}
2443
2444 """
2445 if not update_status:
2446 return self.terminated
2447
2448 if self.is_connected():
2449 self.terminated = not self.virgin and self.control_session.has_terminated(self.get_session_name())
2450 if self.terminated:
2451 self.running = False
2452 self.suspended = False
2453 self.faulty = False
2454 if self.virgin and not self.terminated:
2455 self.terminated = None
2456 return self.terminated
2457 __has_terminated = has_terminated
2458
2460 """\
2461 Test if the remote session allows sharing of local folders with the session.
2462
2463 @return: returns C{True} if local folder sharing is available in the remote session
2464 @rtype: C{bool}
2465
2466 """
2467 if self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders:
2468 if self.is_connected():
2469 return self.control_session.is_sshfs_available()
2470 else:
2471 self.logger('session is not connected, cannot share local folders now', loglevel=log.loglevel_WARN)
2472 else:
2473 self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
2474 return False
2475 __is_folder_sharing_available = is_folder_sharing_available
2476
2478
2479
2480 if self.client_instance and self.restore_shared_local_folders:
2481 _exported_folders = copy.deepcopy(self._restore_exported_folders)
2482 for folder in [ sf for sf in self.shared_folders.keys() if self.shared_folders[sf]['status'] in ('new', 'mounted') ]:
2483 _exported_folders.update({ unicode(folder): True })
2484 for folder in _exported_folders.keys():
2485 if folder in [ sf for sf in self.shared_folders.keys() if self.shared_folders[sf]['status'] == 'unmounted' ]:
2486 _exported_folders.update({ unicode(folder): False })
2487 self._restore_exported_folders = _exported_folders
2488
2489 - def share_local_folder(self, local_path=None, folder_name=None, update_exported_folders=True):
2490 """\
2491 Share a local folder with this registered X2Go session.
2492
2493 @param local_path: the full path to an existing folder on the local
2494 file system
2495 @type local_path: C{str}
2496 @param folder_name: synonymous to C{local_path}
2497 @type folder_name: C{str}
2498 @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
2499 @type update_exported_folders: C{bool}
2500
2501 @return: returns C{True} if the local folder has been successfully mounted within
2502 this X2Go session
2503 @rtype: C{bool}
2504
2505 @raise X2GoSessionException: if this L{X2GoSession} does not have an associated terminal session
2506
2507 """
2508
2509 if folder_name: local_path=folder_name
2510
2511 local_path = unicode(local_path)
2512
2513 retval = False
2514 if self.has_terminal_session():
2515 if self.is_folder_sharing_available() and self.is_master_session():
2516
2517
2518 if self.shared_folders.has_key(local_path):
2519 self.shared_folders[local_path]['status'] = 'mounted'
2520 else:
2521 self.shared_folders.update({ local_path: { 'status': 'new', 'mountpoint': '', }, })
2522 if self.terminal_session.share_local_folder(local_path=local_path):
2523 if update_exported_folders:
2524 self._update_restore_exported_folders()
2525 retval = True
2526 else:
2527
2528 if self.shared_folders[local_path]['status'] == 'new':
2529 del self.shared_folders[local_path]
2530 else:
2531 self.shared_folders[local_path]['status'] = 'unmounted'
2532
2533
2534 if self.client_instance and self.restore_shared_local_folders:
2535 if local_path in self._restore_exported_folders.keys():
2536 self._restore_exported_folders[local_path] = False
2537
2538
2539 if update_exported_folders and self.client_instance and self.restore_shared_local_folders:
2540 self._update_restore_exported_folders()
2541 self.client_instance.set_profile_config(self.profile_name, 'export', self._restore_exported_folders)
2542
2543 else:
2544 raise x2go_exceptions.X2GoSessionException('this X2GoSession object does not have any associated terminal')
2545 return retval
2546
2547 __share_local_folder = share_local_folder
2548
2550 """\
2551 Share all local folders configured to be mounted within this X2Go session.
2552
2553 @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
2554 @type update_exported_folders: C{bool}
2555
2556 @return: returns C{True} if all local folders could be successfully mounted
2557 inside this X2Go session
2558 @rtype: C{bool}
2559
2560 """
2561 retval = False
2562 if self.is_running() and not self.faulty and self._SUPPORTED_FOLDERSHARING and self.share_local_folders and self.allow_share_local_folders and self.has_terminal_session():
2563 if self.is_master_session():
2564 if self.is_folder_sharing_available():
2565 retval = True
2566 for _folder in self.share_local_folders:
2567 try:
2568 retval = self.share_local_folder(_folder, update_exported_folders=False) and retval
2569 except x2go_exceptions.X2GoUserException, e:
2570 retval = False
2571 self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
2572 if update_exported_folders:
2573 self._update_restore_exported_folders()
2574 self.client_instance.set_profile_config(self.profile_name, 'export', self._restore_exported_folders)
2575 else:
2576 self.HOOK_foldersharing_not_available()
2577 return retval
2578 __share_all_local_folders = share_all_local_folders
2579
2581 """\
2582 Unshare a local folder that is mounted within this X2Go session.
2583
2584 @param local_path: the full path to an existing folder on the local
2585 file system that is mounted in this X2Go session and shall be
2586 unmounted
2587 @type local_path: C{str}
2588 @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
2589 @type update_exported_folders: C{bool}
2590
2591 @return: returns C{True} if all local folders could be successfully unmounted
2592 inside this X2Go session
2593 @rtype: C{bool}
2594
2595 @raise X2GoSessionException: if this L{X2GoSession} does not have an associated terminal session
2596
2597 """
2598 retval = False
2599
2600 local_path = unicode(local_path)
2601
2602 if self.has_terminal_session():
2603 if self.is_folder_sharing_available() and self.is_master_session() and local_path in self.shared_folders.keys():
2604
2605
2606 self.shared_folders[local_path]['status'] = 'unmounted'
2607 if self.terminal_session.unshare_local_folder(local_path=local_path):
2608 retval = True
2609 else:
2610
2611 self.shared_folders[local_path]['status'] = 'mounted'
2612
2613
2614 if update_exported_folders and self.client_instance and self.restore_shared_local_folders:
2615 self._update_restore_exported_folders()
2616 self.client_instance.set_profile_config(self.profile_name, 'export', self._restore_exported_folders)
2617
2618 else:
2619 raise x2go_exceptions.X2GoSessionException('this X2GoSession object does not have any associated terminal')
2620
2621 return retval
2622 __unshare_local_folder = unshare_local_folder
2623
2625 """\
2626 Unshare all local folders mounted within this X2Go session.
2627
2628 @param force_all: Really unmount _all_ shared folders, including the print spool folder and
2629 the MIME box spool dir (not recommended).
2630 @type force_all: C{bool}
2631 @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
2632 @type update_exported_folders: C{bool}
2633
2634 @return: returns C{True} if all local folders could be successfully unmounted
2635 inside this X2Go session
2636 @rtype: C{bool}
2637
2638 @raise X2GoSessionException: if this L{X2GoSession} does not have an associated terminal session
2639
2640 """
2641 if self.has_terminal_session():
2642 if self.is_folder_sharing_available() and self.is_master_session():
2643
2644 if force_all:
2645 retval = self.terminal_session.unshare_all_local_folders()
2646 if retval:
2647 self.shared_folders = {}
2648 return retval
2649 else:
2650 retval = True
2651 _shared_folders = copy.deepcopy(self.shared_folders)
2652 for _folder in _shared_folders.keys():
2653 retval = self.unshare_local_folder(_folder, update_exported_folders=False) and retval
2654 if update_exported_folders:
2655 self._update_restore_exported_folders()
2656 self.client_instance.set_profile_config(self.profile_name, 'export', self._restore_exported_folders)
2657 return retval
2658 else:
2659 raise x2go_exceptions.X2GoSessionException('this X2GoSession object does not have any associated terminal')
2660 return False
2661 __unshare_all_local_folders = unshare_all_local_folders
2662
2664 """\
2665 Get a list of local folders mounted within this X2Go session from this client.
2666
2667 @param check_list_mounts: if set to C{True} the list of shared folders is referenced against
2668 the latest status of the server-side mount list.
2669 @type check_list_mounts: C{bool}
2670 @param mounts: a server-side dictionary of session name keys and lists of mounted shares (server-side mount points)
2671 @type mounts: C{dict}
2672
2673 @return: returns a C{list} of those local folder names that are mounted with this X2Go session.
2674 @rtype: C{list}
2675
2676 """
2677 if self.is_folder_sharing_available and self.is_master_session() and self.shared_folders and check_list_mounts:
2678
2679 unshared_folders = []
2680 if mounts is None:
2681 mounts = self.list_mounts()
2682 _defacto_mounts = [ unicode(m.split('|')[1].split('/')[-1]) for m in mounts ]
2683
2684 for shared_folder in self.shared_folders.keys():
2685
2686 if _X2GOCLIENT_OS == 'Windows':
2687 _driveletter, _path = os.path.splitdrive(shared_folder)
2688 _mount_point = '_windrive_%s%s' % (_driveletter[0], _path.replace('\\', '_'))
2689 _mount_point = _mount_point.replace(' ', '_')
2690
2691 else:
2692 _mount_point = shared_folder.replace('/', '_')
2693 _mount_point = _mount_point.replace(' ', '_')
2694
2695 self.shared_folders[shared_folder]['status'] = 'mounted'
2696 self.shared_folders[shared_folder]['mountpoint'] = unicode(_mount_point)
2697
2698 for m in _defacto_mounts:
2699 for sf in self.shared_folders.keys():
2700 if self.shared_folders[sf]['mountpoint'] == m:
2701 self.shared_folders[sf]['status'] = 'mounted'
2702 break
2703
2704 unshared_folders = False
2705
2706 for sf in self.shared_folders.keys():
2707 m = self.shared_folders[sf]['mountpoint']
2708 if m and m not in _defacto_mounts:
2709 try:
2710 if self.shared_folders[sf]['status'] == 'mounted':
2711 self.shared_folders[sf]['status'] = 'unmounted'
2712 self.logger('Detected server-side unsharing of client-side folder for profile %s: %s:' % (self.get_profile_name(), sf), loglevel=log.loglevel_INFO)
2713 unshared_folders = True
2714 except IndexError:
2715 pass
2716
2717 if unshared_folders:
2718 self._update_restore_exported_folders()
2719
2720 return [ unicode(sf) for sf in self.shared_folders if self.shared_folders[sf]['status'] in ('new', 'mounted') ]
2721 __get_shared_folders = get_shared_folders
2722
2724 """\
2725 Query session if it is locked by some command being processed.
2726
2727 @return: returns C{True} is the session is locked, C{False} if not; returns C{None}, if there is no
2728 control session yet.
2729 @rtype: C{bool}
2730
2731 """
2732 if self.control_session is not None:
2733 return self.control_session.locked or self.locked
2734 return None
2735 __is_locked = is_locked
2736
2755 __session_cleanup = session_cleanup
2756