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 """
29 __NAME__ = 'x2gosession-pylib'
30
31 import os
32 import copy
33 import types
34 import uuid
35 import time
36 import threading
37 import gevent
38
39
40 import log
41 import utils
42 import session
43 from x2go_exceptions import *
44
45 from x2go.backends.control import X2goControlSession as _X2goControlSession
46 from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession
47 from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
48 from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
49 from x2go.backends.proxy import X2goProxy as _X2goProxy
50 from x2go.backends.profiles import X2goSessionProfiles as _X2goSessionProfiles
51 from x2go.backends.settings import X2goClientSettings as _X2goClientSettings
52 from x2go.backends.printing import X2goClientPrinting as _X2goClientPrinting
53
54 from defaults import LOCAL_HOME as _LOCAL_HOME
55 from defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
56 from defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR
57 from defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR
58
59 from defaults import SUPPORTED_SOUND, SUPPORTED_PRINTING, SUPPORTED_FOLDERSHARING, SUPPORTED_MIMEBOX
60
61
62 _X2GO_SESSION_PARAMS = ('geometry', 'depth', 'link', 'pack',
63 'cache_type', 'kblayout', 'kbtype',
64 'session_type', 'snd_system', 'snd_port',
65 'cmd',
66 'rdp_server', 'rdp_options',
67 'xdmcp_server',
68 'rootdir', 'loglevel', 'profile_name', 'profile_id',
69 'print_action', 'print_action_args',
70 'convert_encoding', 'client_encoding', 'server_encoding',
71 'proxy_options',
72 'logger',
73 'control_backend', 'terminal_backend', 'proxy_backend',
74 'profiles_backend', 'settings_backend', 'printing_backend',
75 )
76 """A list of allowed X2go session parameters."""
77 _X2GO_SSHPROXY_PARAMS = ('sshproxy_host', 'sshproxy_user', 'sshproxy_password',
78 'sshproxy_key_filename', 'sshproxy_pkey', 'sshproxy_tunnel',
79 )
80 """A list of allowed X2go SSH proxy parameters."""
81
82
84 """\
85 Public API class for launching X2go sessions. Recommended is to manage X2go sessions from
86 within an L{X2goClient} instance. However, Python X2go is designed in a way that it also
87 allows the management of singel L{X2goSession} instance.
88
89 Thus, you can use the L{X2goSession} class to manually set up X2go sessions without
90 L{X2goClient} context (session registry, session list cache, auto-registration of new
91 sessions etc.).
92
93 """
94 - def __init__(self, server=None, control_session=None,
95 use_sshproxy=False,
96 profile_id=None, profile_name='UNKNOWN',
97 session_name=None,
98 printing=False,
99 allow_mimebox=False,
100 mimebox_extensions=[],
101 mimebox_action='OPEN',
102 allow_share_local_folders=False,
103 share_local_folders=[],
104 control_backend=_X2goControlSession,
105 terminal_backend=_X2goTerminalSession,
106 info_backend=_X2goServerSessionInfo,
107 list_backend=_X2goServerSessionList,
108 proxy_backend=_X2goProxy,
109 settings_backend=_X2goClientSettings,
110 printing_backend=_X2goClientPrinting,
111 client_rootdir=os.path.join(_LOCAL_HOME, _X2GO_CLIENT_ROOTDIR),
112 sessions_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SESSIONS_ROOTDIR),
113 ssh_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SSH_ROOTDIR),
114 keep_controlsession_alive=False,
115 add_to_known_hosts=False,
116 known_hosts=None,
117 logger=None, loglevel=log.loglevel_DEFAULT,
118 connected=False, virgin=True, running=None, suspended=None, terminated=None, faulty=None,
119 client_instance=None,
120 **params):
121 """\
122 @param server: hostname of X2go server
123 @type server: C{str}
124 @param control_session: an already initialized C{X2goControlSession*} instance
125 @type control_session: C{X2goControlSession*} instance
126 @param use_sshproxy: for communication with X2go server use an SSH proxy host
127 @type use_sshproxy: C{bool}
128 @param profile_id: profile ID
129 @type profile_id: C{str}
130 @param profile_name: profile name
131 @type profile_name: C{str}
132 @param session_name: session name (if available)
133 @type session_name: C{str}
134 @param printing: enable X2go printing
135 @type printing: C{bool}
136 @param allow_mimebox: enable X2go MIME box support
137 @type allow_mimebox: C{bool}
138 @param mimebox_extensions: whitelist of allowed X2go MIME box extensions
139 @type mimebox_extensions: C{list}
140 @param mimebox_action: action for incoming X2go MIME box files
141 @type mimebox_action: C{X2goMimeBoxAction*} or C{str}
142 @param allow_share_local_folders: enable local folder sharing support
143 @type allow_share_local_folders: C{bool}
144 @param share_local_folders: list of local folders to share with the remote X2go session
145 @type share_local_folders: C{list}
146 @param control_backend: X2go control session backend to use
147 @type control_backend: C{class}
148 @param terminal_backend: X2go terminal session backend to use
149 @type terminal_backend: C{class}
150 @param info_backend: X2go session info backend to use
151 @type info_backend: C{class}
152 @param list_backend: X2go session list backend to use
153 @type list_backend: C{class}
154 @param proxy_backend: X2go proxy backend to use
155 @type proxy_backend: C{class}
156 @param settings_backend: X2go client settings backend to use
157 @type settings_backend: C{class}
158 @param printing_backend: X2go client printing backend to use
159 @type printing_backend: C{class}
160 @param client_rootdir: client base dir (default: ~/.x2goclient)
161 @type client_rootdir: C{str}
162 @param sessions_rootdir: sessions base dir (default: ~/.x2go)
163 @type sessions_rootdir: C{str}
164 @param ssh_rootdir: ssh base dir (default: ~/.ssh)
165 @type ssh_rootdir: C{str}
166 @param keep_controlsession_alive: On last L{X2goSession.disconnect()} keep the associated C{X2goControlSession*} instance alive?
167 @ŧype keep_controlsession_alive: C{bool}
168 @param add_to_known_hosts: Auto-accept server host validity?
169 @type add_to_known_hosts: C{bool}
170 @param known_hosts: the underlying Paramiko/SSH systems C{known_hosts} file
171 @type known_hosts: C{str}
172 @param connected: manipulate session state »connected« by giving a pre-set value
173 @type connected: C{bool}
174 @param virgin: manipulate session state »virgin« by giving a pre-set value
175 @type virgin: C{bool}
176 @param running: manipulate session state »running« by giving a pre-set value
177 @type running: C{bool}
178 @param suspended: manipulate session state »suspended« by giving a pre-set value
179 @type suspended: C{bool}
180 @param terminated: manipulate session state »terminated« by giving a pre-set value
181 @type terminated: C{bool}
182 @param faulty: manipulate session state »faulty« by giving a pre-set value
183 @type faulty: C{bool}
184 @param client_instance: if available, the underlying L{X2goClient} instance
185 @type client_instance: C{X2goClient} instance
186 @param params: further control session, terminal session and SSH proxy class options
187 @type params: C{dict}
188
189 """
190 if logger is None:
191 self.logger = log.X2goLogger(loglevel=loglevel)
192 else:
193 self.logger = copy.deepcopy(logger)
194 self.logger.tag = __NAME__
195
196 self._keep = None
197
198 self.uuid = uuid.uuid1()
199 self.connected = connected
200
201 self.virgin = virgin
202 self.running = running
203 self.suspended = suspended
204 self.terminated = terminated
205 self.faulty = faulty
206 self.keep_controlsession_alive = keep_controlsession_alive
207
208 self.profile_id = profile_id
209 self.profile_name = profile_name
210 self.session_name = session_name
211 self.server = server
212
213 self._last_status = None
214
215 self.locked = False
216
217 self.printing = printing
218 self.allow_share_local_folders = allow_share_local_folders
219 self.share_local_folders = share_local_folders
220 self.allow_mimebox = allow_mimebox
221 self.mimebox_extensions = mimebox_extensions
222 self.mimebox_action = mimebox_action
223 self.control_backend = control_backend
224 self.terminal_backend = terminal_backend
225 self.info_backend = info_backend
226 self.list_backend = list_backend
227 self.proxy_backend = proxy_backend
228 self.settings_backend = settings_backend
229 self.printing_backend = printing_backend
230 self.client_rootdir = client_rootdir
231 self.sessions_rootdir = sessions_rootdir
232 self.ssh_rootdir = ssh_rootdir
233 self.control_session = control_session
234
235 self.control_params = {}
236 self.terminal_params = {}
237 self.sshproxy_params = {}
238 self.update_params(params)
239
240 self.session_environment = {}
241
242 try: del self.control_params['server']
243 except: pass
244
245 self.client_instance = client_instance
246
247 if self.logger.get_loglevel() & log.loglevel_DEBUG:
248 self.logger('X2go control session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
249 for p in self.control_params:
250 self.logger(' %s: %s' % (p, self.control_params[p]), log.loglevel_DEBUG)
251 self.logger('X2go terminal session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
252 for p in self.terminal_params:
253 self.logger(' %s: %s' % (p,self.terminal_params[p]), log.loglevel_DEBUG)
254 self.logger('X2go sshproxy parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
255 for p in self.sshproxy_params:
256 self.logger(' %s: %s' % (p,self.sshproxy_params[p]), loglevel=log.loglevel_DEBUG)
257
258 self.add_to_known_hosts = add_to_known_hosts
259 self.known_hosts = known_hosts
260 self.use_sshproxy = use_sshproxy
261
262 self._current_status = {
263 'timestamp': time.time(),
264 'server': self.server,
265 'virgin': self.virgin,
266 'connected': self.connected,
267 'running': self.running,
268 'suspended': self.suspended,
269 'terminated': self.terminated,
270 'faulty': self.faulty,
271 }
272
273 self.init_control_session()
274 self.terminal_session = None
275
277 """\
278 HOOK method: called if a reverse port forwarding request has been denied.
279
280 @param server_port: remote server port (starting point of reverse forwarding tunnel)
281 @type server_port: C{str}
282
283 """
284 if self.client_instance:
285 self.client_instance.HOOK_rforward_request_denied(profile_name=self.profile_name, session_name=self.session_name, server_port=server_port)
286 else:
287 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)
288
290 """\
291 HOOK method: called if a port forwarding tunnel setup failed.
292
293 @param chain_host: hostname of chain host (forwarding tunnel end point)
294 @type chain_host: C{str}
295 @param chain_port: port of chain host (forwarding tunnel end point)
296 @type chain_port: C{str}
297
298 """
299
300 self.faulty = True
301
302 if self.client_instance:
303 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)
304 else:
305 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)
306
307
308 self.terminate()
309
311 """\
312 HOOK method: called if a host check is requested. This hook has to either return C{True} (default) or C{False}.
313
314 @param host: SSH server name to validate
315 @type host: C{str}
316 @param port: SSH server port to validate
317 @type port: C{int}
318 @param fingerprint: the server's fingerprint
319 @type fingerprint: C{str}
320 @param fingerprint_type: finger print type (like RSA, DSA, ...)
321 @type fingerprint_type: C{str}
322 @return: if host validity is verified, this hook method should return C{True}
323 @rtype: C{bool}
324
325 """
326 if self.client_instance:
327 return self.client_instance.HOOK_check_host_dialog(profile_name=self.profile_name, host=host, port=port, fingerprint=fingerprint, fingerprint_type=fingerprint_type)
328 else:
329 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)
330 return True
331
333 """\
334 Initialize a new control session (C{X2goControlSession*}).
335
336 """
337 if self.control_session is None:
338 self.logger('initializing X2goControlSession', loglevel=log.loglevel_DEBUG)
339 self.control_session = self.control_backend(profile_name=self.profile_name,
340 add_to_known_hosts=self.add_to_known_hosts,
341 known_hosts=self.known_hosts,
342 terminal_backend=self.terminal_backend,
343 info_backend=self.info_backend,
344 list_backend=self.list_backend,
345 proxy_backend=self.proxy_backend,
346 client_rootdir=self.client_rootdir,
347 sessions_rootdir=self.sessions_rootdir,
348 ssh_rootdir=self.ssh_rootdir,
349 logger=self.logger)
350
352 """\
353 Modify server name after L{X2goSession} has already been initialized.
354
355 @param server: new server name
356 @type server: C{str}
357
358 """
359 self.server = server
360
362 """\
363 Modify session profile name after L{X2goSession} has already been initialized.
364
365 @param profile_name: new session profile name
366 @type profile_name: C{str}
367
368 """
369 self.profile_name = profile_name
370 self.control_session.set_profile_name(profile_name)
371
373 return self.__get_uuid()
374
376 result = 'X2goSession('
377 for p in dir(self):
378 if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
379 result += p + '=' + str(self.__dict__[p]) + ', '
380 return result + ')'
381
383 return self.__get_uuid()
384
422
424 """\
425 This method can be used to modify L{X2goSession} parameters after the
426 L{X2goSession} instance has already been initialized.
427
428 @param params: a Python dictionary with L{X2goSession} parameters
429 @type params: C{dict}
430
431 """
432 try: del params['server']
433 except KeyError: pass
434 try: del params['profile_name']
435 except KeyError: pass
436 try: del params['profile_id']
437 except KeyError: pass
438 try:
439 self.printing = params['printing']
440 del params['printing']
441 except KeyError: pass
442 try:
443 self.allow_share_local_folders = params['allow_share_local_folders']
444 del params['allow_share_local_folders']
445 except KeyError: pass
446 try:
447 self.share_local_folders = params['share_local_folders']
448 del params['share_local_folders']
449 except KeyError: pass
450 try:
451 self.allow_mimebox = params['allow_mimebox']
452 del params['allow_mimebox']
453 except KeyError: pass
454 try:
455 self.mimebox_extensions = params['mimebox_extensions']
456 del params['mimebox_extensions']
457 except KeyError: pass
458 try:
459 self.mimebox_action = params['mimebox_action']
460 del params['mimebox_action']
461 except KeyError: pass
462 try:
463 self.use_sshproxy = params['use_sshproxy']
464 del params['use_sshproxy']
465 except KeyError: pass
466
467 _terminal_params = copy.deepcopy(params)
468 _control_params = copy.deepcopy(params)
469 _sshproxy_params = copy.deepcopy(params)
470 for p in params.keys():
471 if p in session._X2GO_SESSION_PARAMS:
472 del _control_params[p]
473 del _sshproxy_params[p]
474 elif p in session._X2GO_SSHPROXY_PARAMS:
475 del _control_params[p]
476 del _terminal_params[p]
477 else:
478 del _sshproxy_params[p]
479 del _terminal_params[p]
480
481 self.control_params.update(_control_params)
482 self.terminal_params.update(_terminal_params)
483 self.sshproxy_params.update(_sshproxy_params)
484
486 """\
487 Retrieve session UUID hash for this L{X2goSession}.
488
489 """
490 return str(self.uuid)
491 __get_uuid = get_uuid
492
494 """\
495 After a session has been set up you can query the
496 username the sessions runs as.
497
498 @return: the remote username the X2go session runs as
499 @rtype: C{str}
500
501 """
502
503 try:
504 return self.control_session.get_transport().get_username()
505 except AttributeError:
506 return self.control_params['username']
507 __get_username = get_username
508
509
511 """\
512 Check if a given user is valid server-side X2go user.
513
514 @param username: username to check validity for
515 @type username: C{str}
516 @return: return C{True} if the username is allowed to launch X2go sessions
517 @rtype: C{bool}
518
519 """
520 if username is None:
521 username = self.__get_username()
522 return self.control_session.is_x2gouser(username)
523 __user_is_x2gouser = user_is_x2gouser
524
526 """\
527 After a session has been setup up you can query the
528 username's password from the session.
529
530 @return: the username's password
531 @rtype: C{str}
532
533 """
534 return self.control_session._session_password
535 __get_password = get_password
536
538 """\
539 After a session has been setup up you can query the
540 peername of the host this session is connected to (or
541 about to connect to).
542
543 @return: the address of the server the X2go session is
544 connected to (as an C{(addr,port)} tuple)
545 @rtype: C{tuple}
546
547 """
548 return self.control_session.get_transport().getpeername()
549 __get_server_peername = get_server_peername
550
552 """\
553 After a session has been setup up you can query the
554 hostname of the host this session is connected to (or
555 about to connect to).
556
557 @return: the hostname of the server the X2go session is
558 connected to / about to connect to
559 @rtype: C{str}
560
561 """
562 self.server = self.control_session.hostname
563 return self.server
564 __get_server_hostname = get_server_hostname
565
567 """\
568 After a session has been setup up you can query the
569 IP socket port used for connecting the remote X2go server.
570
571 @return: the server-side IP socket port that is used by the X2go session to
572 connect to the server
573 @rtype: C{str}
574
575 """
576 return self.control_session.port
577 __get_server_port = get_server_port
578
580 """\
581 Retrieve the server-side X2go session name for this session.
582
583 @return: X2go session name
584 @rtype: C{str}
585
586 """
587 return self.session_name
588 __get_session_name = get_session_name
589
591 """\
592 Retrieve the server-side command that is used to start a session
593 on the remote X2go server.
594
595 @return: server-side session command
596 @rtype: C{str}
597
598 """
599 if self.terminal_params.has_key('cmd'):
600 return self.terminal_params['cmd']
601 return None
602 __get_session_cmd = get_session_cmd
603
605 """\
606 Retrieve the control session (C{X2goControlSession*} backend) of this L{X2goSession}.
607
608 @return: the L{X2goSession}'s control session
609 @rtype: C{X2goControlSession*} instance
610 """
611 return self.control_session
612 __get_control_session = get_control_session
613
615 """\
616 Check if this L{X2goSession} instance has an associated control session.
617
618 @return: returns C{True} if this L{X2goSession} has a control session associated to itself
619 @rtype: C{bool}
620
621 """
622 return self.control_session is not None
623 __has_control_session = has_control_session
624
626 """\
627 Retrieve the terminal session (C{X2goTerminalSession*} backend) of this L{X2goSession}.
628
629 @return: the L{X2goSession}'s terminal session
630 @rtype: C{X2goControlTerminal*} instance
631
632 """
633 if self.terminal_session == 'PENDING':
634 return None
635 return self.terminal_session
636 __get_terminal_session = get_terminal_session
637
639 """\
640 Check if this L{X2goSession} instance has an associated terminal session.
641
642 @return: returns C{True} if this L{X2goSession} has a terminal session associated to itself
643 @rtype: C{bool}
644
645
646 """
647 return self.terminal_session not in (None, 'PENDING')
648 __has_terminal_session = has_terminal_session
649
651 """\
652 Provide a host check mechanism. This method basically calls the L{HOOK_check_host_dialog()} method
653 which by itself calls the L{X2goClient.HOOK_check_host_dialog()} method. Make sure you
654 override any of these to enable user interaction on X2go server validity checks.
655
656 @return: returns C{True} if an X2go server host is valid for authentication
657 @rtype: C{bool}
658
659 """
660 if self.connected:
661 return True
662
663 _port = self.control_params['port']
664 (_valid, _host, _port, _fingerprint, _fingerprint_type) = self.control_session.check_host(self.server, port=_port)
665 return _valid or self.HOOK_check_host_dialog(host=_host, port=_port, fingerprint=_fingerprint, fingerprint_type=_fingerprint_type)
666 __check_host = check_host
667
669 """\
670 Check if a session is configured adequately to be able to auto-connect to the X2go
671 server (e.g. public key authentication).
672
673 @return: returns C{True} if the session can auto-connect, C{False} otherwise, C{None}
674 if no control session has been set up yet.
675 @rtype: C{bool}
676
677 """
678
679 def _can_sshproxy_autoconnect():
680
681 if self.use_sshproxy:
682 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'])):
683 return True
684 elif self.sshproxy_params.has_key('sshproxy_pkey') and self.sshproxy_params['sshproxy_pkey']:
685 return True
686 else:
687 return False
688 else:
689 return True
690
691
692 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'])):
693 return _can_sshproxy_autoconnect()
694
695
696 elif self.control_params.has_key('pkey') and self.control_params['pkey']:
697 return _can_sshproxy_autoconnect()
698
699 else:
700 return False
701 __can_auto_connect = can_auto_connect
702
703 - def connect(self, username='', password='', add_to_known_hosts=False, force_password_auth=False,
704 use_sshproxy=False, sshproxy_user='', sshproxy_password=''):
705 """\
706 Connects to the L{X2goSession}'s server host. This method basically wraps around
707 the C{X2goControlSession*.connect()} method.
708
709 @param username: the username for the X2go server that is going to be
710 connected to (as a last minute way of changing the session username)
711 @type username: C{str}
712 @param password: the user's password for the X2go server that is going to be
713 connected to
714 @type password: C{str}
715 @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
716 is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
717 is used
718 @type add_to_known_hosts: C{bool}
719 @param force_password_auth: disable SSH pub/priv key authentication mechanisms
720 completely
721 @type force_password_auth: C{bool}
722 @param use_sshproxy: use an SSH proxy host for connecting the target X2go server
723 @type use_sshproxy: C{bool}
724 @param sshproxy_user: username for authentication against the SSH proxy host
725 @type sshproxy_user: C{str}
726 @param sshproxy_password: password for authentication against the SSH proxy host
727 @type sshproxy_password: C{str}
728
729 @return: returns C{True} is the connection to the X2go server has been successful
730 @rtype C{bool}
731
732 """
733 if self.control_session and self.control_session.is_connected():
734 self.logger('control session is already connected, skipping authentication', loglevel=log.loglevel_DEBUG)
735 self.connected = True
736 else:
737 if username:
738 self.control_params['username'] = username
739 if add_to_known_hosts is not None:
740 self.control_params['add_to_known_hosts'] = add_to_known_hosts
741 if force_password_auth is not None:
742 self.control_params['force_password_auth'] = force_password_auth
743 if sshproxy_user:
744 self.sshproxy_params['sshproxy_user'] = sshproxy_user
745 if sshproxy_password:
746 self.sshproxy_params['sshproxy_password'] = sshproxy_password
747 self.control_params['password'] = password
748
749 _params = {}
750 _params.update(self.control_params)
751 _params.update(self.sshproxy_params)
752
753 try:
754 self.connected = self.control_session.connect(self.server,
755 use_sshproxy=self.use_sshproxy,
756 session_instance=self,
757 **_params)
758 except X2goControlSessionException, e:
759 raise X2goSessionException(str(e))
760 except:
761
762 self.control_params['password'] = ''
763 if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
764 del self.sshproxy_params['sshproxy_password']
765 raise
766 finally:
767
768 self.control_params['password'] = ''
769 if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
770 del self.sshproxy_params['sshproxy_password']
771
772 if not self.connected:
773
774 self.disconnect()
775
776 _dummy = self.get_server_hostname()
777
778 if self.connected:
779 self.update_status()
780
781 return self.connected
782 __connect = connect
783
785 """\
786 Disconnect this L{X2goSession} instance.
787
788 @return: returns C{True} if the disconnect operation has been successful
789 @rtype: C{bool}
790
791 """
792 self.connected = False
793 self.running = None
794 self.suspended = None
795 self.terminated = None
796 self.faults = None
797 try:
798 self.update_status(force_update=True)
799 except X2goControlSessionException:
800 pass
801 retval = self.control_session.disconnect()
802 return retval
803 __disconnect = disconnect
804
806 """\
807 If X2go client-side printing is enable within this X2go session you can use
808 this method to alter the way how incoming print spool jobs are handled/processed.
809
810 For further information, please refer to the documentation of the L{X2goClient.set_session_print_action()}
811 method.
812
813 @param print_action: one of the named above print actions, either as string or class instance
814 @type print_action: C{str} or C{instance}
815 @param kwargs: additional information for the given print action (print
816 action arguments), for possible print action arguments and their values see each individual
817 print action class
818 @type kwargs: C{dict}
819
820 """
821 if type(print_action) is not types.StringType:
822 return False
823 self.terminal_session.set_print_action(print_action, **kwargs)
824 __set_print_action = set_print_action
825
827 """\
828 Find out if this X2go session is still alive (that is: connected to the server).
829
830 @return: returns C{True} if the server connection is still alive
831 @rtype: C{bool}
832
833 """
834 self.connected = self.control_session.is_alive()
835 if not self.connected:
836 self._X2goSession__disconnect()
837 return self.connected
838 __is_alive = is_alive
839
841 """\
842 Clean all running sessions for the authenticated user on the remote X2go server.
843
844 """
845 if self.is_alive():
846 self.control_session.clean_sessions()
847 else:
848 self._X2goSession__disconnect()
849 __clean_sessions = clean_sessions
850
852 """\
853 List all sessions on the remote X2go server that are owned by the authenticated user
854
855 @param raw: if C{True} the output of this method equals
856 the output of the server-side C{x2golistsessions} command
857 @type raw: C{bool}
858
859 @return: a session list (as data object or list of strings when called with C{raw=True} option)
860 @rtype: C{X2goServerSessionList*} instance or C{list}
861
862 """
863 try:
864 return self.control_session.list_sessions(raw=raw)
865 except X2goControlSessionException:
866 self._X2goSession_disconnect()
867 return None
868 __list_sessions = list_sessions
869
871 """\
872 List X2go desktops sessions available for desktop sharing on the remote X2go server.
873
874 @param raw: if C{True} the output of this method equals
875 the output of the server-side C{x2golistdesktops} command
876 @type raw: C{bool}
877
878 @return: a list of strings representing available desktop sessions
879 @rtype: C{list}
880
881 """
882 try:
883 return self.control_session.list_desktops(raw=raw)
884 except X2goDesktopSharingException:
885 if raw:
886 return ('','')
887 else:
888 return []
889 except X2goControlSessionException:
890 self._X2goSession_disconnect()
891 return None
892 __list_desktops = list_desktops
893
895 """\
896 Update the current session status. The L{X2goSession} instance uses an internal
897 session status cache that allows to query the session status without the need
898 of retrieving data from the remote X2go server for each query.
899
900 The session status (if initialized properly with the L{X2goClient} constructor gets
901 updated in regularly intervals.
902
903 In case you use the L{X2goSession} class in standalone instances (that is: without
904 being embedded into an L{X2goSession} context) then run this method in regular
905 intervals to make sure the L{X2goSession}'s internal status cache information
906 is always up-to-date.
907
908 @param session_list: provide an C{X2goServerSessionList*} that refers to X2go sessions we want to update.
909 This option is mainly for reducing server/client traffic.
910 @type session_list: C{X2goServerSessionList*} instance
911 @param force_update: force a session status update, if if the last update is less then 1 second ago
912 @type force_update: C{bool}
913
914 """
915 if not force_update and self._last_status is not None:
916 _status_update_timedelta = time.time() - self._last_status['timestamp']
917
918
919 if _status_update_timedelta < 1:
920 self.logger('status update interval too short (%s), skipping status about this time...' % _status_update_timedelta, loglevel=log.loglevel_DEBUG)
921 return False
922
923 e = None
924 self._last_status = copy.deepcopy(self._current_status)
925 if session_list is None:
926 try:
927 session_list = self.control_session.list_sessions()
928 self.connected = True
929 except X2goControlSessionException, e:
930 self.connected = False
931 self.running = None
932 self.suspended = None
933 self.terminated = None
934 self.faulty = None
935
936 if self.connected:
937 try:
938 _session_name = self.get_session_name()
939 _session_info = session_list[_session_name]
940 self.running = _session_info.is_running()
941 self.suspended = _session_info.is_suspended()
942 if not self.virgin:
943 self.terminated = not (self.running or self.suspended)
944 else:
945 self.terminated = None
946 except KeyError:
947 self.running = False
948 self.suspended = False
949 if not self.virgin:
950 self.terminated = True
951 self.faulty = not (self.running or self.suspended or self.terminated or self.virgin)
952
953
954 self._current_status = {
955 'timestamp': time.time(),
956 'server': self.server,
957 'virgin': self.virgin,
958 'connected': self.connected,
959 'running': self.running,
960 'suspended': self.suspended,
961 'terminated': self.terminated,
962 'faulty': self.faulty,
963 }
964
965 if (not self.connected or self.faulty) and e:
966 raise e
967
968 return True
969
970 __update_status = update_status
971
972 - def resume(self, session_name=None):
973 """\
974 Resume or continue a suspended / running X2go session on the
975 remote X2go server.
976
977 @param session_name: the server-side name of an X2go session
978 @type session_name: C{str}
979
980 @return: returns C{True} if resuming the session has been successful, C{False} otherwise
981 @rtype: C{bool}
982
983 """
984 self.terminal_session = 'PENDING'
985 _new_session = False
986 if self.session_name is None:
987 self.session_name = session_name
988
989 if self.is_alive():
990 _control = self.control_session
991
992
993
994
995 if self.is_running():
996 self.suspend()
997 gevent.sleep(10)
998
999 self.terminal_session = _control.resume(session_name=self.session_name,
1000 session_instance=self,
1001 logger=self.logger, **self.terminal_params)
1002
1003 if self.session_name is None:
1004 _new_session = True
1005 try:
1006 self.session_name = self.terminal_session.session_info.name
1007 except AttributeError:
1008 raise X2goSessionException('start of new X2go session failed')
1009
1010 if self.has_terminal_session() and not self.faulty:
1011
1012 if SUPPORTED_SOUND and self.terminal_session.params.snd_system is not 'none':
1013 self.terminal_session and not self.faulty and self.terminal_session.start_sound()
1014
1015 if (SUPPORTED_PRINTING and self.printing) or \
1016 (SUPPORTED_MIMEBOX and self.allow_mimebox) or \
1017 (SUPPORTED_FOLDERSHARING and self.allow_share_local_folders):
1018 self.terminal_session and not self.faulty and self.terminal_session.start_sshfs()
1019
1020 try:
1021 if SUPPORTED_PRINTING and self.printing:
1022 self.terminal_session and not self.faulty and self.terminal_session.start_printing()
1023 self.terminal_session and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
1024 except X2goUserException:
1025 pass
1026
1027 if SUPPORTED_MIMEBOX and self.allow_mimebox:
1028 self.terminal_session and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
1029 self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
1030
1031 if SUPPORTED_FOLDERSHARING and self.share_local_folders:
1032 if _control.get_transport().reverse_tunnels[self.terminal_session.get_session_name()]['sshfs'][1] is not None:
1033 for _folder in self.share_local_folders:
1034 self.share_local_folder(_folder)
1035
1036
1037 if _new_session:
1038 self.terminal_session.run_command(env=self.session_environment)
1039
1040 self.virgin = False
1041 self.suspended = False
1042 self.running = True
1043 self.terminated = False
1044 self.faulty = False
1045
1046 return True
1047
1048 else:
1049 self.terminal_session = None
1050 return False
1051
1052 return self.running
1053 else:
1054 self._X2goSession__disconnect()
1055 return False
1056
1057 __resume = resume
1058
1060 """\
1061 Start a new X2go session on the remote X2go server.
1062
1063 @return: returns C{True} if starting the session has been successful, C{False} otherwise
1064 @rtype: C{bool}
1065
1066 """
1067 self.session_name = None
1068 return self.resume()
1069 __start = start
1070
1071 - def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True):
1072 """\
1073 Share an already running X2go session on the remote X2go server locally. The shared session may be either
1074 owned by the same user or by a user that grants access to his/her desktop session by the local user.
1075
1076 @param desktop: desktop ID of a sharable desktop in format <user>@<display>
1077 @type desktop: C{str}
1078 @param user: user name and display number can be given separately, here give the
1079 name of the user who wants to share a session with you.
1080 @type user: C{str}
1081 @param display: user name and display number can be given separately, here give the
1082 number of the display that a user allows you to be shared with.
1083 @type display: C{str}
1084 @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
1085 @type share_mode: C{int}
1086 @param check_desktop_list: check if the given desktop is available on the X2go server; handle with care as
1087 the server-side C{x2golistdesktops} command might block client I/O.
1088 @type check_desktop_list: C{bool}
1089
1090 @return: returns C{True} if starting the session has been successful, C{False} otherwise
1091 @rtype: C{bool}
1092
1093 """
1094 self.terminal_session = 'PENDING'
1095
1096 _desktop = desktop or '%s@%s' % (user, display)
1097 if check_desktop_list:
1098 if not _desktop in self._X2goSession__list_desktops():
1099 _orig_desktop = _desktop
1100 _desktop = '%s.0' % _desktop
1101 if not _desktop in self._X2GoSession__list_desktops():
1102 raise X2goDesktopSharingException('No such desktop ID: %s' % _orig_desktop)
1103
1104 _session_owner = _desktop.split('@')[0]
1105 _display = _desktop.split('@')[1]
1106
1107 if self.is_alive():
1108 if self.get_username() != _session_owner:
1109 self.logger('waiting for user ,,%s\'\' to interactively grant you access to his/her desktop session...' % _session_owner, loglevel=log.loglevel_NOTICE)
1110 self.logger('THIS MAY TAKE A WHILE!', loglevel=log.loglevel_NOTICE)
1111
1112 _control = self.control_session
1113 try:
1114 self.terminal_session = _control.share_desktop(desktop=_desktop, share_mode=share_mode,
1115 logger=self.logger, **self.terminal_params)
1116 except ValueError:
1117
1118
1119 raise X2goSessionException('the session on desktop %s is seemingly dead' % _desktop)
1120
1121 if self.has_terminal_session():
1122 self.session_name = self.terminal_session.session_info.name
1123
1124
1125
1126 self.terminal_session.run_command(env=self.session_environment)
1127
1128 self.virgin = False
1129 self.suspended = False
1130 self.running = True
1131 self.terminated = False
1132 self.faulty = False
1133
1134 return self.running
1135 else:
1136 self.terminal_session = None
1137 return False
1138
1139 else:
1140 self._X2goSession__disconnect()
1141 return False
1142 __share_desktop = share_desktop
1143
1145 """\
1146 Suspend this X2go session.
1147
1148 @return: returns C{True} if suspending the session has been successful, C{False} otherwise
1149 @rtype: C{bool}
1150
1151 """
1152 if self.is_alive():
1153 if self.has_terminal_session():
1154 if self.terminal_session.suspend():
1155
1156 self.running = False
1157 self.suspended = True
1158 self.terminated = False
1159 self.faults = False
1160 self.session_cleanup()
1161 return True
1162
1163 elif self.has_control_session() and self.session_name:
1164 if self.control_session.suspend(session_name=self.session_name):
1165
1166 self.running = False
1167 self.suspended = True
1168 self.terminated = False
1169 self.faulty = False
1170 self.session_cleanup()
1171 return True
1172
1173 else:
1174 raise X2goClientException('cannot suspend session')
1175
1176 else:
1177 self._X2goSession__disconnect()
1178
1179 return False
1180 __suspend = suspend
1181
1183 """\
1184 Terminate this X2go session.
1185
1186 @return: returns C{True} if terminating the session has been successful, C{False} otherwise
1187 @rtype: C{bool}
1188
1189 """
1190 if self.is_alive():
1191 if self.has_terminal_session():
1192 if self.terminal_session.terminate():
1193
1194 self.running = False
1195 self.suspended = False
1196 self.terminated = True
1197 self.faulty = False
1198 self.session_cleanup()
1199 return True
1200
1201 elif self.has_control_session() and self.session_name:
1202 if self.control_session.terminate(session_name=self.session_name):
1203
1204 self.running = False
1205 self.suspended = False
1206 self.terminated = True
1207 self.faulty = False
1208 self.session_cleanup()
1209 return True
1210 else:
1211 raise X2goClientException('cannot terminate session')
1212
1213 else:
1214 self._X2goSession__disconnect()
1215
1216 return False
1217 __terminate = terminate
1218
1220 """\
1221 Retrieve the profile name of this L{X2goSession} instance.
1222
1223 @return: X2go client profile name of the session
1224 @rtype: C{str}
1225
1226 """
1227 return self.profile_name
1228 __get_profile_name = get_profile_name
1229
1231 """\
1232 Retrieve the profile ID of this L{X2goSession} instance.
1233
1234 @return: the session profile's id
1235 @rtype: C{str}
1236
1237 """
1238 return self.profile_id
1239 __get_profile_id = get_profile_id
1240
1241
1242
1243
1244
1246 """\
1247 Test if this C{X2goSession} is
1248 in a healthy state.
1249
1250 @return: C{BTrue} if session is ok, C{False} otherwise
1251 @rtype: C{bool}
1252
1253 """
1254 if self.has_terminal_session():
1255 return self.terminal_session.ok()
1256 return False
1257 __session_ok = session_ok
1258
1260 """\
1261 Extract color depth from session name.
1262
1263 @return: the session's color depth (as found in the session name)
1264 @rtype: C{str}
1265
1266 """
1267 return int(self.get_session_name().split('_')[2][2:])
1268 __color_depth_from_session_name = color_depth_from_session_name
1269
1271 """\
1272 Check if this session will display properly with the local screen's color depth.
1273
1274 @return: C{True} if the session will display on this client screen, False otherwise. If no terminal session is yet registered with this session, C{None} is returned.
1275 @rtype C{bool}
1276
1277 """
1278 return utils.is_color_depth_ok(depth_session=self.color_depth_from_session_name(), depth_local=utils.local_color_depth())
1279 __is_color_depth_ok = is_color_depth_ok
1280
1282 """\
1283 Test if the L{X2goSession}'s control session is connected to the
1284 remote X2go server.
1285
1286 @return: C{True} if session is connected, C{False} otherwise
1287 @rtype: C{bool}
1288
1289 """
1290 self.connected = bool(self.control_session and self.control_session.is_connected())
1291 if not self.connected:
1292 self.running = None
1293 self.suspended = None
1294 self.terminated = None
1295 self.faulty = None
1296 return self.connected
1297 __is_connected = is_connected
1298
1300 """\
1301 Test if the L{X2goSession}'s terminal session is up and running.
1302
1303 @return: C{True} if session is running, C{False} otherwise
1304 @rtype: C{bool}
1305
1306 """
1307 if self.is_connected():
1308 self.running = self.control_session.is_running(self.get_session_name())
1309 if self.running:
1310 self.suspended = False
1311 self.terminated = False
1312 self.faulty = False
1313 if self.virgin and not self.running:
1314 self.running = None
1315 return self.running
1316 __is_running = is_running
1317
1319 """\
1320 Test if the L{X2goSession}'s terminal session is in suspended state.
1321
1322 @return: C{True} if session is suspended, C{False} otherwise
1323 @rtype: C{bool}
1324
1325 """
1326 if self.is_connected():
1327 self.suspended = self.control_session.is_suspended(self.get_session_name())
1328 if self.suspended:
1329 self.running = False
1330 self.terminated = False
1331 self.faulty = False
1332 if self.virgin and not self.suspended:
1333 self.suspended = None
1334 return self.suspended
1335 __is_suspended = is_suspended
1336
1338 """\
1339 Test if the L{X2goSession}'s terminal session has terminated.
1340
1341 @return: C{True} if session has terminated, C{False} otherwise
1342 @rtype: C{bool}
1343
1344 """
1345 if self.is_connected():
1346 self.terminated = not self.virgin and self.control_session.has_terminated(self.get_session_name())
1347 if self.terminated:
1348 self.running = False
1349 self.suspended = False
1350 self.faulty = False
1351 if self.virgin and not self.terminated:
1352 self.terminated = None
1353 return self.has_terminated
1354 __has_terminated = has_terminated
1355
1357 """\
1358 Share a local folder with this registered X2go session.
1359
1360 @param folder_name: the full path to an existing folder on the local
1361 file system
1362 @type folder_name: C{str}
1363
1364 @return: returns C{True} if the local folder has been successfully mounted within
1365 this X2go session
1366 @rtype: C{bool}
1367
1368 """
1369 if self.has_terminal_session():
1370 if self.allow_share_local_folders:
1371 return self.terminal_session.share_local_folder(folder_name=folder_name)
1372 else:
1373 self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
1374 else:
1375 raise X2goSessionException('this X2goSession object does not have any associated terminal')
1376 __share_local_folder = share_local_folder
1377
1379 """\
1380 Query session if it is locked by some command being processed.
1381
1382 @return: return C{True} is the session is locked, C{False} if not; returns None, if there is no
1383 control session yet.
1384 @rtype: C{bool}
1385
1386 """
1387 if self.control_session is not None:
1388 return self.control_session.locked or self.locked
1389 return None
1390
1401