Package x2go :: Module session

[frames] | no frames]

# Source Code for Module x2go.session

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