Package x2go :: Module session
[frames] | no frames]

Source Code for Module x2go.session

   1  # -*- coding: utf-8 -*- 
   2   
   3  # Copyright (C) 2010-2011 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de> 
   4  # 
   5  # Python X2go is free software; you can redistribute it and/or modify 
   6  # it under the terms of the GNU General Public License as published by 
   7  # the Free Software Foundation; either version 3 of the License, or 
   8  # (at your option) any later version. 
   9  # 
  10  # Python X2go is distributed in the hope that it will be useful, 
  11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  13  # GNU General Public License for more details. 
  14  # 
  15  # You should have received a copy of the GNU General Public License 
  16  # along with this program; if not, write to the 
  17  # Free Software Foundation, Inc., 
  18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 
  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  # Python X2go modules 
  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  # options of the paramiko.SSHClient().connect() 
  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   
83 -class X2goSession(object):
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
276 - def HOOK_rforward_request_denied(self, server_port=0):
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
289 - def HOOK_forwarding_tunnel_setup_failed(self, chain_host='UNKNOWN', chain_port=0):
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 # mark session as faulty 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 # get rid of the faulty session... 308 self.terminate()
309
310 - def HOOK_check_host_dialog(self, host, port, fingerprint='no fingerprint', fingerprint_type='RSA'):
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
332 - def init_control_session(self):
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
351 - def set_server(self, server):
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
361 - def set_profile_name(self, profile_name):
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
372 - def __str__(self):
373 return self.__get_uuid()
374
375 - def __repr__(self):
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
382 - def __call__(self):
383 return self.__get_uuid()
384
385 - def __del__(self):
386 """\ 387 Class destructor. 388 389 """ 390 if self.has_control_session() and self.has_terminal_session(): 391 self.get_control_session().dissociate(self.get_terminal_session()) 392 393 if self.has_control_session(): 394 if self.keep_controlsession_alive: 395 # regenerate this session instance for re-usage if this is the last session for a certain session profile 396 # and keep_controlsession_alive is set to True... 397 self.virgin = True 398 self.connected = self.is_connected() 399 self.running = None 400 self.suspended = None 401 self.terminated = None 402 self._current_status = { 403 'timestamp': time.time(), 404 'server': self.server, 405 'virgin': self.virgin, 406 'connected': self.connected, 407 'running': self.running, 408 'suspended': self.suspended, 409 'terminated': self.terminated, 410 'faulty': self.faulty, 411 } 412 self._last_status = None 413 self.session_name = None 414 415 else: 416 self.get_control_session().__del__() 417 self.control_session = None 418 419 if self.has_terminal_session(): 420 self.get_terminal_session().__del__() 421 self.terminal_session = None
422
423 - def update_params(self, params):
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
485 - def get_uuid(self):
486 """\ 487 Retrieve session UUID hash for this L{X2goSession}. 488 489 """ 490 return str(self.uuid)
491 __get_uuid = get_uuid 492
493 - def get_username(self):
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 # try to retrieve the username from the control session, if already connected 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
510 - def user_is_x2gouser(self, username=None):
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
525 - def get_password(self):
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
537 - def get_server_peername(self):
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
551 - def get_server_hostname(self):
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
566 - def get_server_port(self):
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
579 - def get_session_name(self):
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
590 - def get_session_cmd(self):
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
604 - def get_control_session(self):
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
614 - def has_control_session(self):
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
625 - def get_terminal_session(self):
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
638 - def has_terminal_session(self):
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
650 - def check_host(self):
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
668 - def can_auto_connect(self):
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 # do we have a key file passed as control parameter? 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 # or a private key? 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 # remove credentials immediately 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 # remove credentials immediately 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 # then tidy up... 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
784 - def disconnect(self):
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
805 - def set_print_action(self, print_action, **kwargs):
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
826 - def is_alive(self):
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
840 - def clean_sessions(self):
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
851 - def list_sessions(self, raw=False):
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
870 - def list_desktops(self, raw=False):
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
894 - def update_status(self, session_list=None, force_update=False):
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 # skip this session status update if not longer than a second ago... 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 # FIXME: normally this part gets called if you suspend a session that is associated to another client 993 # we do not have a possibility to really check if SSH has released port forwarding channels or 994 # sockets, thus we plainly have to wait a while 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 # only run the session startup command if we do not resume... 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
1059 - def start(self):
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 # x2gostartagent output parsing will result in a ValueError. This one we will catch 1118 # here and change it into an X2goSessionException 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 # shared desktop sessions get their startup command set by the control 1125 # session, run this pre-set command now... 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
1144 - def suspend(self):
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
1182 - def terminate(self):
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
1219 - def get_profile_name(self):
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
1230 - def get_profile_id(self):
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 ### QUERYING INFORMATION 1243 ### 1244
1245 - def session_ok(self):
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
1259 - def color_depth_from_session_name(self):
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
1270 - def is_color_depth_ok(self):
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
1281 - def is_connected(self):
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
1299 - def is_running(self):
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
1318 - def is_suspended(self):
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
1337 - def has_terminated(self):
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
1356 - def share_local_folder(self, folder_name):
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
1378 - def is_locked(self):
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
1391 - def session_cleanup(self):
1392 """\ 1393 Clean up X2go session. 1394 1395 """ 1396 if self.has_terminal_session(): 1397 self.terminal_session.release_proxy() 1398 if self.has_terminal_session(): 1399 self.terminal_session.__del__() 1400 self.terminal_session = None
1401