Package x2go :: Module session

[frames] | no frames]

# Source Code for Module x2go.session

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