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 self.shared_folders = []
240
241 self.session_environment = {}
242
243 try: del self.control_params['server']
244 except: pass
245
246 self.client_instance = client_instance
247
248 if self.logger.get_loglevel() & log.loglevel_DEBUG:
249 self.logger('X2go control session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
250 for p in self.control_params:
251 self.logger(' %s: %s' % (p, self.control_params[p]), log.loglevel_DEBUG)
252 self.logger('X2go terminal session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
253 for p in self.terminal_params:
254 self.logger(' %s: %s' % (p,self.terminal_params[p]), log.loglevel_DEBUG)
255 self.logger('X2go sshproxy parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG)
256 for p in self.sshproxy_params:
257 self.logger(' %s: %s' % (p,self.sshproxy_params[p]), loglevel=log.loglevel_DEBUG)
258
259 self.add_to_known_hosts = add_to_known_hosts
260 self.known_hosts = known_hosts
261 self.use_sshproxy = use_sshproxy
262
263 self._current_status = {
264 'timestamp': time.time(),
265 'server': self.server,
266 'virgin': self.virgin,
267 'connected': self.connected,
268 'running': self.running,
269 'suspended': self.suspended,
270 'terminated': self.terminated,
271 'faulty': self.faulty,
272 }
273
274 self._SUPPORTED_SOUND = SUPPORTED_SOUND
275 self._SUPPORTED_PRINTING = SUPPORTED_PRINTING
276 self._SUPPORTED_MIMEBOX = SUPPORTED_MIMEBOX
277 self._SUPPORTED_FOLDERSHARING = SUPPORTED_FOLDERSHARING
278
279 self.init_control_session()
280 self.terminal_session = None
281
283 """\
284 HOOK method: called if the startup of a session failed.
285
286 """
287 if self.client_instance:
288 self.client_instance.HOOK_session_startup_failed(profile_name=self.profile_name)
289 else:
290 self.logger('HOOK_session_startup_failed: session startup for session profile ,,%s'' failed.' % self.profile_name, loglevel=log.loglevel_WARN)
291
293 """\
294 HOOK method: called if a reverse port forwarding request has been denied.
295
296 @param server_port: remote server port (starting point of reverse forwarding tunnel)
297 @type server_port: C{str}
298
299 """
300 if self.client_instance:
301 self.client_instance.HOOK_rforward_request_denied(profile_name=self.profile_name, session_name=self.session_name, server_port=server_port)
302 else:
303 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)
304
306 """\
307 HOOK method: called if a port forwarding tunnel setup failed.
308
309 @param chain_host: hostname of chain host (forwarding tunnel end point)
310 @type chain_host: C{str}
311 @param chain_port: port of chain host (forwarding tunnel end point)
312 @type chain_port: C{str}
313
314 """
315
316 self.faulty = True
317
318 if self.client_instance:
319 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)
320 else:
321 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)
322
323
324 self.terminate()
325
327 """\
328 HOOK method: called if a host check is requested. This hook has to either return C{True} (default) or C{False}.
329
330 @param host: SSH server name to validate
331 @type host: C{str}
332 @param port: SSH server port to validate
333 @type port: C{int}
334 @param fingerprint: the server's fingerprint
335 @type fingerprint: C{str}
336 @param fingerprint_type: finger print type (like RSA, DSA, ...)
337 @type fingerprint_type: C{str}
338 @return: if host validity is verified, this hook method should return C{True}
339 @rtype: C{bool}
340
341 """
342 if self.client_instance:
343 return self.client_instance.HOOK_check_host_dialog(profile_name=self.profile_name, host=host, port=port, fingerprint=fingerprint, fingerprint_type=fingerprint_type)
344 else:
345 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)
346 return True
347
349 """\
350 Initialize a new control session (C{X2goControlSession*}).
351
352 """
353 if self.control_session is None:
354 self.logger('initializing X2goControlSession', loglevel=log.loglevel_DEBUG)
355 self.control_session = self.control_backend(profile_name=self.profile_name,
356 add_to_known_hosts=self.add_to_known_hosts,
357 known_hosts=self.known_hosts,
358 terminal_backend=self.terminal_backend,
359 info_backend=self.info_backend,
360 list_backend=self.list_backend,
361 proxy_backend=self.proxy_backend,
362 client_rootdir=self.client_rootdir,
363 sessions_rootdir=self.sessions_rootdir,
364 ssh_rootdir=self.ssh_rootdir,
365 logger=self.logger)
366
368 """\
369 Modify server name after L{X2goSession} has already been initialized.
370
371 @param server: new server name
372 @type server: C{str}
373
374 """
375 self.server = server
376
378 """\
379 Modify session profile name after L{X2goSession} has already been initialized.
380
381 @param profile_name: new session profile name
382 @type profile_name: C{str}
383
384 """
385 self.profile_name = profile_name
386 self.control_session.set_profile_name(profile_name)
387
389 return self.__get_uuid()
390
392 result = 'X2goSession('
393 for p in dir(self):
394 if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
395 result += p + '=' + str(self.__dict__[p]) + ', '
396 return result + ')'
397
399 return self.__get_uuid()
400
438
440 """\
441 This method can be used to modify L{X2goSession} parameters after the
442 L{X2goSession} instance has already been initialized.
443
444 @param params: a Python dictionary with L{X2goSession} parameters
445 @type params: C{dict}
446
447 """
448 try: del params['server']
449 except KeyError: pass
450 try: del params['profile_name']
451 except KeyError: pass
452 try: del params['profile_id']
453 except KeyError: pass
454 try:
455 self.printing = params['printing']
456 del params['printing']
457 except KeyError: pass
458 try:
459 self.allow_share_local_folders = params['allow_share_local_folders']
460 del params['allow_share_local_folders']
461 except KeyError: pass
462 try:
463 self.share_local_folders = params['share_local_folders']
464 del params['share_local_folders']
465 except KeyError: pass
466 try:
467 self.allow_mimebox = params['allow_mimebox']
468 del params['allow_mimebox']
469 except KeyError: pass
470 try:
471 self.mimebox_extensions = params['mimebox_extensions']
472 del params['mimebox_extensions']
473 except KeyError: pass
474 try:
475 self.mimebox_action = params['mimebox_action']
476 del params['mimebox_action']
477 except KeyError: pass
478 try:
479 self.use_sshproxy = params['use_sshproxy']
480 del params['use_sshproxy']
481 except KeyError: pass
482
483 _terminal_params = copy.deepcopy(params)
484 _control_params = copy.deepcopy(params)
485 _sshproxy_params = copy.deepcopy(params)
486 for p in params.keys():
487 if p in session._X2GO_SESSION_PARAMS:
488 del _control_params[p]
489 del _sshproxy_params[p]
490 elif p in session._X2GO_SSHPROXY_PARAMS:
491 del _control_params[p]
492 del _terminal_params[p]
493 else:
494 del _sshproxy_params[p]
495 del _terminal_params[p]
496
497 self.control_params.update(_control_params)
498 self.terminal_params.update(_terminal_params)
499 self.sshproxy_params.update(_sshproxy_params)
500
502 """\
503 Retrieve session UUID hash for this L{X2goSession}.
504
505 """
506 return str(self.uuid)
507 __get_uuid = get_uuid
508
510 """\
511 After a session has been set up you can query the
512 username the sessions runs as.
513
514 @return: the remote username the X2go session runs as
515 @rtype: C{str}
516
517 """
518
519 try:
520 return self.control_session.get_transport().get_username()
521 except AttributeError:
522 return self.control_params['username']
523 __get_username = get_username
524
525
527 """\
528 Check if a given user is valid server-side X2go user.
529
530 @param username: username to check validity for
531 @type username: C{str}
532 @return: return C{True} if the username is allowed to launch X2go sessions
533 @rtype: C{bool}
534
535 """
536 if username is None:
537 username = self.__get_username()
538 return self.control_session.is_x2gouser(username)
539 __user_is_x2gouser = user_is_x2gouser
540
542 """\
543 After a session has been setup up you can query the
544 username's password from the session.
545
546 @return: the username's password
547 @rtype: C{str}
548
549 """
550 return self.control_session._session_password
551 __get_password = get_password
552
554 """\
555 After a session has been setup up you can query the
556 peername of the host this session is connected to (or
557 about to connect to).
558
559 @return: the address of the server the X2go session is
560 connected to (as an C{(addr,port)} tuple)
561 @rtype: C{tuple}
562
563 """
564 return self.control_session.get_transport().getpeername()
565 __get_server_peername = get_server_peername
566
568 """\
569 After a session has been setup up you can query the
570 hostname of the host this session is connected to (or
571 about to connect to).
572
573 @return: the hostname of the server the X2go session is
574 connected to / about to connect to
575 @rtype: C{str}
576
577 """
578 self.server = self.control_session.hostname
579 return self.server
580 __get_server_hostname = get_server_hostname
581
583 """\
584 After a session has been setup up you can query the
585 IP socket port used for connecting the remote X2go server.
586
587 @return: the server-side IP socket port that is used by the X2go session to
588 connect to the server
589 @rtype: C{str}
590
591 """
592 return self.control_session.port
593 __get_server_port = get_server_port
594
596 """\
597 Retrieve the server-side X2go session name for this session.
598
599 @return: X2go session name
600 @rtype: C{str}
601
602 """
603 return self.session_name
604 __get_session_name = get_session_name
605
607 """\
608 Retrieve the server-side command that is used to start a session
609 on the remote X2go server.
610
611 @return: server-side session command
612 @rtype: C{str}
613
614 """
615 if self.terminal_params.has_key('cmd'):
616 return self.terminal_params['cmd']
617 return None
618 __get_session_cmd = get_session_cmd
619
621 """\
622 Retrieve the control session (C{X2goControlSession*} backend) of this L{X2goSession}.
623
624 @return: the L{X2goSession}'s control session
625 @rtype: C{X2goControlSession*} instance
626 """
627 return self.control_session
628 __get_control_session = get_control_session
629
631 """\
632 Check if this L{X2goSession} instance has an associated control session.
633
634 @return: returns C{True} if this L{X2goSession} has a control session associated to itself
635 @rtype: C{bool}
636
637 """
638 return self.control_session is not None
639 __has_control_session = has_control_session
640
642 """\
643 Retrieve the terminal session (C{X2goTerminalSession*} backend) of this L{X2goSession}.
644
645 @return: the L{X2goSession}'s terminal session
646 @rtype: C{X2goControlTerminal*} instance
647
648 """
649 if self.terminal_session == 'PENDING':
650 return None
651 return self.terminal_session
652 __get_terminal_session = get_terminal_session
653
655 """\
656 Check if this L{X2goSession} instance has an associated terminal session.
657
658 @return: returns C{True} if this L{X2goSession} has a terminal session associated to itself
659 @rtype: C{bool}
660
661
662 """
663 return self.terminal_session not in (None, 'PENDING')
664 __has_terminal_session = has_terminal_session
665
667 """\
668 Provide a host check mechanism. This method basically calls the L{HOOK_check_host_dialog()} method
669 which by itself calls the L{X2goClient.HOOK_check_host_dialog()} method. Make sure you
670 override any of these to enable user interaction on X2go server validity checks.
671
672 @return: returns C{True} if an X2go server host is valid for authentication
673 @rtype: C{bool}
674
675 """
676 if self.connected:
677 return True
678
679 _port = self.control_params['port']
680 (_valid, _host, _port, _fingerprint, _fingerprint_type) = self.control_session.check_host(self.server, port=_port)
681 return _valid or self.HOOK_check_host_dialog(host=_host, port=_port, fingerprint=_fingerprint, fingerprint_type=_fingerprint_type)
682 __check_host = check_host
683
685 """\
686 Check if a session is configured to use an intermediate SSH proxy server.
687
688 @return: returns C{True} if the session is configured to use an SSH proxy, C{False} otherwise.
689 @rtype: C{bool}
690
691 """
692 return self.use_sshproxy
693
695 """\
696 Check if a session's SSH proxy (if used) is configured adequately to be able to auto-connect
697 to the SSH proxy server (e.g. by public key authentication).
698
699 @return: returns C{True} if the session's SSH proxy can auto-connect, C{False} otherwise, C{None}
700 if no SSH proxy is used for this session, C{None} is returned.
701 @rtype: C{bool}
702
703 """
704 if self.use_sshproxy:
705 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'])):
706 return True
707 elif self.sshproxy_params.has_key('sshproxy_pkey') and self.sshproxy_params['sshproxy_pkey']:
708 return True
709 else:
710 return False
711 else:
712 return None
713 __can_sshproxy_auto_connect = can_sshproxy_auto_connect
714
716 """\
717 Check if a session is configured adequately to be able to auto-connect to the X2go
718 server (e.g. public key authentication).
719
720 @return: returns C{True} if the session can auto-connect, C{False} otherwise, C{None}
721 if no control session has been set up yet.
722 @rtype: C{bool}
723
724 """
725 if self.control_session is None:
726 return None
727
728
729 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'])):
730 _can_sshproxy_auto_connect = self.can_sshproxy_auto_connect()
731 if _can_sshproxy_auto_connect is not None:
732 return _can_sshproxy_auto_connect
733 else:
734 return True
735
736
737 elif self.control_params.has_key('pkey') and self.control_params['pkey']:
738 _can_sshproxy_auto_connect = self.can_sshproxy_auto_connect()
739 if _can_sshproxy_auto_connect is not None:
740 return _can_sshproxy_auto_connect
741 else:
742 return True
743
744 else:
745 return False
746 __can_auto_connect = can_auto_connect
747
748 - def connect(self, username='', password='', add_to_known_hosts=False, force_password_auth=False,
749 use_sshproxy=False, sshproxy_user='', sshproxy_password=''):
750 """\
751 Connects to the L{X2goSession}'s server host. This method basically wraps around
752 the C{X2goControlSession*.connect()} method.
753
754 @param username: the username for the X2go server that is going to be
755 connected to (as a last minute way of changing the session username)
756 @type username: C{str}
757 @param password: the user's password for the X2go server that is going to be
758 connected to
759 @type password: C{str}
760 @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
761 is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
762 is used
763 @type add_to_known_hosts: C{bool}
764 @param force_password_auth: disable SSH pub/priv key authentication mechanisms
765 completely
766 @type force_password_auth: C{bool}
767 @param use_sshproxy: use an SSH proxy host for connecting the target X2go server
768 @type use_sshproxy: C{bool}
769 @param sshproxy_user: username for authentication against the SSH proxy host
770 @type sshproxy_user: C{str}
771 @param sshproxy_password: password for authentication against the SSH proxy host
772 @type sshproxy_password: C{str}
773
774 @return: returns C{True} is the connection to the X2go server has been successful
775 @rtype C{bool}
776
777 """
778 if self.control_session and self.control_session.is_connected():
779 self.logger('control session is already connected, skipping authentication', loglevel=log.loglevel_DEBUG)
780 self.connected = True
781 else:
782 if username:
783 self.control_params['username'] = username
784 if add_to_known_hosts is not None:
785 self.control_params['add_to_known_hosts'] = add_to_known_hosts
786 if force_password_auth is not None:
787 self.control_params['force_password_auth'] = force_password_auth
788 if sshproxy_user:
789 self.sshproxy_params['sshproxy_user'] = sshproxy_user
790 if sshproxy_password:
791 self.sshproxy_params['sshproxy_password'] = sshproxy_password
792 self.control_params['password'] = password
793
794 _params = {}
795 _params.update(self.control_params)
796 _params.update(self.sshproxy_params)
797
798 try:
799 self.connected = self.control_session.connect(self.server,
800 use_sshproxy=self.use_sshproxy,
801 session_instance=self,
802 **_params)
803 except X2goControlSessionException, e:
804 raise X2goSessionException(str(e))
805 except X2goRemoteHomeException, e:
806 self.disconnect()
807 raise e
808 except:
809
810 self.control_params['password'] = ''
811 if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
812 del self.sshproxy_params['sshproxy_password']
813 raise
814 finally:
815
816 self.control_params['password'] = ''
817 if self.sshproxy_params and self.sshproxy_params.has_key('sshproxy_password'):
818 del self.sshproxy_params['sshproxy_password']
819
820 if not self.connected:
821
822 self.disconnect()
823
824 _dummy = self.get_server_hostname()
825
826 if self.connected:
827 self.update_status()
828
829 return self.connected
830 __connect = connect
831
833 """\
834 Disconnect this L{X2goSession} instance.
835
836 @return: returns C{True} if the disconnect operation has been successful
837 @rtype: C{bool}
838
839 """
840 self.connected = False
841 self.running = None
842 self.suspended = None
843 self.terminated = None
844 self.faults = None
845 try:
846 self.update_status(force_update=True)
847 except X2goControlSessionException:
848 pass
849 retval = self.control_session.disconnect()
850 return retval
851 __disconnect = disconnect
852
854 """\
855 If X2go client-side printing is enable within this X2go session you can use
856 this method to alter the way how incoming print spool jobs are handled/processed.
857
858 For further information, please refer to the documentation of the L{X2goClient.set_session_print_action()}
859 method.
860
861 @param print_action: one of the named above print actions, either as string or class instance
862 @type print_action: C{str} or C{instance}
863 @param kwargs: additional information for the given print action (print
864 action arguments), for possible print action arguments and their values see each individual
865 print action class
866 @type kwargs: C{dict}
867
868 """
869 if type(print_action) is not types.StringType:
870 return False
871 self.terminal_session.set_print_action(print_action, **kwargs)
872 __set_print_action = set_print_action
873
875 """\
876 Find out if this X2go session is still alive (that is: connected to the server).
877
878 @return: returns C{True} if the server connection is still alive
879 @rtype: C{bool}
880
881 """
882 self.connected = self.control_session.is_alive()
883 if not self.connected:
884 self._X2goSession__disconnect()
885 return self.connected
886 __is_alive = is_alive
887
889 """\
890 Clean all running sessions for the authenticated user on the remote X2go server.
891
892 """
893 if self.is_alive():
894 self.control_session.clean_sessions(destroy_terminals=destroy_terminals)
895 else:
896 self._X2goSession__disconnect()
897 __clean_sessions = clean_sessions
898
900 """\
901 List all sessions on the remote X2go server that are owned by the authenticated user
902
903 @param raw: if C{True} the output of this method equals
904 the output of the server-side C{x2golistsessions} command
905 @type raw: C{bool}
906
907 @return: a session list (as data object or list of strings when called with C{raw=True} option)
908 @rtype: C{X2goServerSessionList*} instance or C{list}
909
910 """
911 try:
912 return self.control_session.list_sessions(raw=raw)
913 except X2goControlSessionException:
914 self._X2goSession__disconnect()
915 return None
916 __list_sessions = list_sessions
917
919 """\
920 List X2go desktops sessions available for desktop sharing on the remote X2go server.
921
922 @param raw: if C{True} the output of this method equals
923 the output of the server-side C{x2golistdesktops} command
924 @type raw: C{bool}
925
926 @return: a list of strings representing available desktop sessions
927 @rtype: C{list}
928
929 """
930 try:
931 return self.control_session.list_desktops(raw=raw)
932 except X2goDesktopSharingException:
933 if raw:
934 return ('','')
935 else:
936 return []
937 except X2goControlSessionException:
938 self._X2goSession__disconnect()
939 return None
940 __list_desktops = list_desktops
941
943 """\
944 Update the current session status. The L{X2goSession} instance uses an internal
945 session status cache that allows to query the session status without the need
946 of retrieving data from the remote X2go server for each query.
947
948 The session status (if initialized properly with the L{X2goClient} constructor gets
949 updated in regularly intervals.
950
951 In case you use the L{X2goSession} class in standalone instances (that is: without
952 being embedded into an L{X2goSession} context) then run this method in regular
953 intervals to make sure the L{X2goSession}'s internal status cache information
954 is always up-to-date.
955
956 @param session_list: provide an C{X2goServerSessionList*} that refers to X2go sessions we want to update.
957 This option is mainly for reducing server/client traffic.
958 @type session_list: C{X2goServerSessionList*} instance
959 @param force_update: force a session status update, if if the last update is less then 1 second ago
960 @type force_update: C{bool}
961
962 """
963 if not force_update and self._last_status is not None:
964 _status_update_timedelta = time.time() - self._last_status['timestamp']
965
966
967 if _status_update_timedelta < 1:
968 self.logger('status update interval too short (%s), skipping status update this time...' % _status_update_timedelta, loglevel=log.loglevel_DEBUG)
969 return False
970
971 e = None
972 self._last_status = copy.deepcopy(self._current_status)
973 if session_list is None:
974 try:
975 session_list = self.control_session.list_sessions()
976 self.connected = True
977 except X2goControlSessionException, e:
978 self.connected = False
979 self.running = None
980 self.suspended = None
981 self.terminated = None
982 self.faulty = None
983
984 if self.connected:
985 try:
986 _session_name = self.get_session_name()
987 _session_info = session_list[_session_name]
988 self.running = _session_info.is_running()
989 self.suspended = _session_info.is_suspended()
990 if not self.virgin:
991 self.terminated = not (self.running or self.suspended)
992 else:
993 self.terminated = None
994 except KeyError:
995 self.running = False
996 self.suspended = False
997 if not self.virgin:
998 self.terminated = True
999 self.faulty = not (self.running or self.suspended or self.terminated or self.virgin)
1000
1001
1002 self._current_status = {
1003 'timestamp': time.time(),
1004 'server': self.server,
1005 'virgin': self.virgin,
1006 'connected': self.connected,
1007 'running': self.running,
1008 'suspended': self.suspended,
1009 'terminated': self.terminated,
1010 'faulty': self.faulty,
1011 }
1012
1013 if (not self.connected or self.faulty) and e:
1014 raise e
1015
1016 return True
1017
1018 __update_status = update_status
1019
1020 - def resume(self, session_name=None):
1021 """\
1022 Resume or continue a suspended / running X2go session on the
1023 remote X2go server.
1024
1025 @param session_name: the server-side name of an X2go session
1026 @type session_name: C{str}
1027
1028 @return: returns C{True} if resuming the session has been successful, C{False} otherwise
1029 @rtype: C{bool}
1030
1031 """
1032 self.terminal_session = 'PENDING'
1033 _new_session = False
1034 if self.session_name is None:
1035 self.session_name = session_name
1036
1037 if self.is_alive():
1038 _control = self.control_session
1039
1040
1041
1042
1043 if self.is_running():
1044 self.suspend()
1045 gevent.sleep(10)
1046
1047 self.terminal_session = _control.resume(session_name=self.session_name,
1048 session_instance=self,
1049 logger=self.logger, **self.terminal_params)
1050
1051 if self.session_name is None:
1052 _new_session = True
1053 try:
1054 self.session_name = self.terminal_session.session_info.name
1055 except AttributeError:
1056 self.HOOK_session_startup_failed()
1057 return False
1058
1059 if self.has_terminal_session() and not self.faulty:
1060
1061
1062 if _new_session:
1063 self.terminal_session.run_command(env=self.session_environment)
1064
1065 if self._SUPPORTED_SOUND and self.terminal_session.params.snd_system is not 'none':
1066 self.terminal_session and not self.faulty and self.terminal_session.start_sound()
1067 else:
1068 self._SUPPORTED_SOUND = False
1069
1070 try:
1071 if (self._SUPPORTED_PRINTING and self.printing) or \
1072 (self._SUPPORTED_MIMEBOX and self.allow_mimebox) or \
1073 (self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders):
1074 self.terminal_session and not self.faulty and self.terminal_session.start_sshfs()
1075 except X2goUserException, e:
1076 self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
1077
1078 self._SUPPORTED_PRINTING = False
1079 self._SUPPORTED_MIMEBOX = False
1080 self._SUPPORTED_FOLDERSHARING = False
1081
1082 try:
1083 if SUPPORTED_PRINTING and self.printing:
1084 self.terminal_session and not self.faulty and self.terminal_session.start_printing()
1085 self.terminal_session and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
1086 except X2goUserException, e:
1087 self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
1088
1089 self._SUPPORTED_PRINTING = False
1090
1091 if self._SUPPORTED_MIMEBOX and self.allow_mimebox:
1092 self.terminal_session and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
1093 self.terminal_session and self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
1094
1095 if self.share_local_folders and self.terminal_session and not self.faulty and self.is_folder_sharing_available():
1096 if _control.get_transport().reverse_tunnels[self.terminal_session.get_session_name()]['sshfs'][1] is not None:
1097 for _folder in self.share_local_folders:
1098 self.share_local_folder(_folder)
1099
1100 self.virgin = False
1101 self.suspended = False
1102 self.running = True
1103 self.terminated = False
1104 self.faulty = False
1105
1106 return True
1107
1108 else:
1109 self.terminal_session = None
1110 return False
1111
1112 return self.running
1113 else:
1114 self._X2goSession__disconnect()
1115 return False
1116
1117 __resume = resume
1118
1120 """\
1121 Start a new X2go session on the remote X2go server.
1122
1123 @return: returns C{True} if starting the session has been successful, C{False} otherwise
1124 @rtype: C{bool}
1125
1126 """
1127 self.session_name = None
1128 return self.resume()
1129 __start = start
1130
1131 - def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True):
1132 """\
1133 Share an already running X2go session on the remote X2go server locally. The shared session may be either
1134 owned by the same user or by a user that grants access to his/her desktop session by the local user.
1135
1136 @param desktop: desktop ID of a sharable desktop in format <user>@<display>
1137 @type desktop: C{str}
1138 @param user: user name and display number can be given separately, here give the
1139 name of the user who wants to share a session with you.
1140 @type user: C{str}
1141 @param display: user name and display number can be given separately, here give the
1142 number of the display that a user allows you to be shared with.
1143 @type display: C{str}
1144 @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
1145 @type share_mode: C{int}
1146 @param check_desktop_list: check if the given desktop is available on the X2go server; handle with care as
1147 the server-side C{x2golistdesktops} command might block client I/O.
1148 @type check_desktop_list: C{bool}
1149
1150 @return: returns C{True} if starting the session has been successful, C{False} otherwise
1151 @rtype: C{bool}
1152
1153 """
1154 self.terminal_session = 'PENDING'
1155
1156 _desktop = desktop or '%s@%s' % (user, display)
1157 if check_desktop_list:
1158 if not _desktop in self._X2goSession__list_desktops():
1159 _orig_desktop = _desktop
1160 _desktop = '%s.0' % _desktop
1161 if not _desktop in self._X2GoSession__list_desktops():
1162 raise X2goDesktopSharingException('No such desktop ID: %s' % _orig_desktop)
1163
1164 _session_owner = _desktop.split('@')[0]
1165 _display = _desktop.split('@')[1]
1166
1167 if self.is_alive():
1168 if self.get_username() != _session_owner:
1169 self.logger('waiting for user ,,%s\'\' to interactively grant you access to his/her desktop session...' % _session_owner, loglevel=log.loglevel_NOTICE)
1170 self.logger('THIS MAY TAKE A WHILE!', loglevel=log.loglevel_NOTICE)
1171
1172 _control = self.control_session
1173 try:
1174 self.terminal_session = _control.share_desktop(desktop=_desktop, share_mode=share_mode,
1175 logger=self.logger, **self.terminal_params)
1176 except ValueError:
1177
1178
1179 raise X2goSessionException('the session on desktop %s is seemingly dead' % _desktop)
1180
1181 if self.has_terminal_session():
1182 self.session_name = self.terminal_session.session_info.name
1183
1184
1185
1186 self.terminal_session.run_command(env=self.session_environment)
1187
1188 self.virgin = False
1189 self.suspended = False
1190 self.running = True
1191 self.terminated = False
1192 self.faulty = False
1193
1194 return self.running
1195 else:
1196 self.terminal_session = None
1197
1198 else:
1199 self._X2goSession__disconnect()
1200
1201 return False
1202 __share_desktop = share_desktop
1203
1205 """\
1206 Suspend this X2go session.
1207
1208 @return: returns C{True} if suspending the session has been successful, C{False} otherwise
1209 @rtype: C{bool}
1210
1211 """
1212 if self.is_alive():
1213 if self.has_terminal_session():
1214
1215 if self.terminal_session.suspend():
1216
1217 self.running = False
1218 self.suspended = True
1219 self.terminated = False
1220 self.faults = False
1221 self.session_cleanup()
1222 return True
1223
1224 elif self.has_control_session() and self.session_name:
1225 if self.control_session.suspend(session_name=self.session_name):
1226
1227 self.running = False
1228 self.suspended = True
1229 self.terminated = False
1230 self.faulty = False
1231 self.session_cleanup()
1232 return True
1233
1234 else:
1235 raise X2goClientException('cannot suspend session')
1236
1237 else:
1238 self._X2goSession__disconnect()
1239
1240 return False
1241 __suspend = suspend
1242
1244 """\
1245 Terminate this X2go session.
1246
1247 @return: returns C{True} if terminating the session has been successful, C{False} otherwise
1248 @rtype: C{bool}
1249
1250 """
1251 if self.is_alive():
1252 if self.has_terminal_session():
1253
1254 if self.terminal_session.terminate():
1255 self.running = False
1256 self.suspended = False
1257 self.terminated = True
1258 self.faulty = False
1259 self.session_cleanup()
1260 return True
1261
1262 elif self.has_control_session() and self.session_name:
1263 if self.control_session.terminate(session_name=self.session_name):
1264
1265 self.running = False
1266 self.suspended = False
1267 self.terminated = True
1268 self.faulty = False
1269 self.session_cleanup()
1270 return True
1271 else:
1272 raise X2goClientException('cannot terminate session')
1273
1274 else:
1275 self._X2goSession__disconnect()
1276
1277 return False
1278 __terminate = terminate
1279
1281 """\
1282 Retrieve the profile name of this L{X2goSession} instance.
1283
1284 @return: X2go client profile name of the session
1285 @rtype: C{str}
1286
1287 """
1288 return self.profile_name
1289 __get_profile_name = get_profile_name
1290
1292 """\
1293 Retrieve the profile ID of this L{X2goSession} instance.
1294
1295 @return: the session profile's id
1296 @rtype: C{str}
1297
1298 """
1299 return self.profile_id
1300 __get_profile_id = get_profile_id
1301
1302
1303
1304
1305
1307 """\
1308 Test if this C{X2goSession} is
1309 in a healthy state.
1310
1311 @return: C{BTrue} if session is ok, C{False} otherwise
1312 @rtype: C{bool}
1313
1314 """
1315 if self.has_terminal_session():
1316 return self.terminal_session.ok()
1317 return False
1318 __session_ok = session_ok
1319
1321 """\
1322 Extract color depth from session name.
1323
1324 @return: the session's color depth (as found in the session name)
1325 @rtype: C{str}
1326
1327 """
1328 return int(self.get_session_name().split('_')[2][2:])
1329 __color_depth_from_session_name = color_depth_from_session_name
1330
1332 """\
1333 Check if this session will display properly with the local screen's color depth.
1334
1335 @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.
1336 @rtype C{bool}
1337
1338 """
1339 return utils.is_color_depth_ok(depth_session=self.color_depth_from_session_name(), depth_local=utils.local_color_depth())
1340 __is_color_depth_ok = is_color_depth_ok
1341
1343 """\
1344 Test if the L{X2goSession}'s control session is connected to the
1345 remote X2go server.
1346
1347 @return: C{True} if session is connected, C{False} otherwise
1348 @rtype: C{bool}
1349
1350 """
1351 self.connected = bool(self.control_session and self.control_session.is_connected())
1352 if not self.connected:
1353 self.running = None
1354 self.suspended = None
1355 self.terminated = None
1356 self.faulty = None
1357 return self.connected
1358 __is_connected = is_connected
1359
1361 """\
1362 Test if the L{X2goSession}'s terminal session is up and running.
1363
1364 @return: C{True} if session is running, C{False} otherwise
1365 @rtype: C{bool}
1366
1367 """
1368 if self.is_connected():
1369 self.running = self.control_session.is_running(self.get_session_name())
1370 if self.running:
1371 self.suspended = False
1372 self.terminated = False
1373 self.faulty = False
1374 if self.virgin and not self.running:
1375 self.running = None
1376 return self.running
1377 __is_running = is_running
1378
1380 """\
1381 Test if the L{X2goSession}'s terminal session is in suspended state.
1382
1383 @return: C{True} if session is suspended, C{False} otherwise
1384 @rtype: C{bool}
1385
1386 """
1387 if self.is_connected():
1388 self.suspended = self.control_session.is_suspended(self.get_session_name())
1389 if self.suspended:
1390 self.running = False
1391 self.terminated = False
1392 self.faulty = False
1393 if self.virgin and not self.suspended:
1394 self.suspended = None
1395 return self.suspended
1396 __is_suspended = is_suspended
1397
1399 """\
1400 Test if the L{X2goSession}'s terminal session has terminated.
1401
1402 @return: C{True} if session has terminated, C{False} otherwise
1403 @rtype: C{bool}
1404
1405 """
1406 if self.is_connected():
1407 self.terminated = not self.virgin and self.control_session.has_terminated(self.get_session_name())
1408 if self.terminated:
1409 self.running = False
1410 self.suspended = False
1411 self.faulty = False
1412 if self.virgin and not self.terminated:
1413 self.terminated = None
1414 return self.terminated
1415 __has_terminated = has_terminated
1416
1418 """\
1419 Test if the remote session allows sharing of local folders with the session.
1420
1421 @return: returns C{True} if local folder sharing is available in the remote session
1422 @rtype: C{bool}
1423
1424 """
1425 if self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders:
1426 if self.is_connected():
1427 return self.control_session.is_folder_sharing_available()
1428 else:
1429 self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
1430
1432 """\
1433 Share a local folder with this registered X2go session.
1434
1435 @param local_path: the full path to an existing folder on the local
1436 file system
1437 @type local_path: C{str}
1438 @param folder_name: synonymous to C{local_path}
1439 @type folder_name: C{str}
1440
1441 @return: returns C{True} if the local folder has been successfully mounted within
1442 this X2go session
1443 @rtype: C{bool}
1444
1445 """
1446
1447 if folder_name: local_path=folder_name
1448
1449 if self.has_terminal_session():
1450 if self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders:
1451 if self.terminal_session.share_local_folder(local_path=local_path):
1452 self.shared_folders.append(local_path)
1453 return True
1454 return False
1455 else:
1456 self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
1457 else:
1458 raise X2goSessionException('this X2goSession object does not have any associated terminal')
1459 __share_local_folder = share_local_folder
1460
1462 """\
1463 Unshare all local folders mounted within this X2go session.
1464
1465 @param force_all: Really unmount _all_ shared folders, including the print spool folder and
1466 the MIME box spool dir (not recommended).
1467 @type force_all: C{bool}
1468
1469 @return: returns C{True} if all local folders could be successfully unmounted
1470 inside this X2go session
1471 @rtype: C{bool}
1472
1473 """
1474 if self.has_terminal_session():
1475 if self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders:
1476 if force_all:
1477 self.shared_folders = []
1478 return self.terminal_session.unshare_all_local_folders()
1479 else:
1480 retval = 0
1481 for _shared_folder in self.shared_folders:
1482 retval = retval | self.terminal_session.unshare_local_folder(_shared_folder)
1483 self.shared_folders = []
1484 return retval
1485 else:
1486 self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
1487 else:
1488 raise X2goSessionException('this X2goSession object does not have any associated terminal')
1489 return False
1490 __unshare_all_local_folders = unshare_all_local_folders
1491
1493 """\
1494 Unshare a local folder that is mounted within this X2go session.
1495
1496 @param local_path: the full path to an existing folder on the local
1497 file system that is mounted in this X2go session and shall be
1498 unmounted
1499 @type local_path: C{str}
1500 @return: returns C{True} if all local folders could be successfully unmounted
1501 inside this X2go session
1502 @rtype: C{bool}
1503
1504 """
1505 if self.has_terminal_session():
1506 if self._SUPPORTED_FOLDERSHARING and self.allow_share_local_folders and local_path in self.shared_folders:
1507 self.shared_folders.remove(local_path)
1508 return self.terminal_session.unshare_local_folder(local_path=local_path)
1509 else:
1510 self.logger('local folder sharing is disabled for this session profile', loglevel=log.loglevel_WARN)
1511 else:
1512 raise X2goSessionException('this X2goSession object does not have any associated terminal')
1513 __unshare_local_folder = unshare_local_folder
1514
1516 """\
1517 Get a list of local folders mounted within this X2go session from this client.
1518
1519 @return: returns a C{list} of those folder names that are mounted with this X2go session.
1520 @rtype: C{list}
1521
1522 """
1523 return self.shared_folders
1524 __get_shared_folders = get_shared_folders
1525
1527 """\
1528 Query session if it is locked by some command being processed.
1529
1530 @return: return C{True} is the session is locked, C{False} if not; returns None, if there is no
1531 control session yet.
1532 @rtype: C{bool}
1533
1534 """
1535 if self.control_session is not None:
1536 return self.control_session.locked or self.locked
1537 return None
1538
1563