Package x2go :: Module session

[frames] | no frames]

# Source Code for Module x2go.session

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