[X2go-Commits] python-x2go.git - build-main (branch) updated: 0.2.1.0

X2Go dev team git-admin at x2go.org
Mon Dec 10 13:05:31 CET 2012


The branch, build-main has been updated
       via  86a4bfdb9c720bc4dd24a59dc9bf7b2403f52005 (commit)
       via  3bb8542118d12a81d4017908337d1ce39e9e2eb8 (commit)
       via  a853ca8373782108de71c4a24189e0d60703ac98 (commit)
       via  901b38885619f6f4a16a42b17633de61d531427a (commit)
       via  64dbd1b29eeefb88427ee92a5ba0ccfef2cb309f (commit)
       via  8b51f4435d5042e8f8a3d38767d8cf70d38a67b6 (commit)
       via  54e4dbcf1b1b740eb5fff25d8f308a4331ba616f (commit)
       via  384130ba3e22a2ea7c3c4d021f1e7e22a072727b (commit)
       via  78a8ce92518502a16c909b84a7987e1a3f858523 (commit)
       via  1d46f7b6adb1bf292e1e97b9ccb4ceeb46d6e1c7 (commit)
       via  f7bffc7d1e9e8c64a3e01e3c79ec8a94ed7b4970 (commit)
       via  fc277361b4e764ef8f5a9b9c66fbd61b0c7cad40 (commit)
       via  9818ac0840cc6df753e4c354a8461b31f89c6af9 (commit)
       via  2e69094ec995acd4f217f0af6e9f2b24e673f555 (commit)
       via  8d835dcdec07d7f620a25cbcd4aafd88a1a044c7 (commit)
       via  19142c8cf8165b553780cfc33048df5ae11890ef (commit)
       via  b4b82d69db5599877036a014a7494bffb39e527f (commit)
       via  dd46f32662d19394bd2a495a1817068a04e11253 (commit)
       via  5035773988304b7af3f3b28b2e736eb832fc7501 (commit)
       via  5ba7271ccdfd828b3c045b4b6878a56df6336c2e (commit)
       via  564bc7cc97eccfc1ddca356a2bf52b093b9004a3 (commit)
       via  14bdff2bb22efd16db1561ee229e8ae004396a21 (commit)
       via  20679137ce0388c27829cd4948afd33414abe494 (commit)
       via  da000f3c040a8217ebdfc10e6d25065a1b887810 (commit)
       via  bda44ea56d0e45b4b5ced3b25aed52aa70baa5ff (commit)
       via  c0080475df80c728c9b5d1a44e60a2105327c73b (commit)
       via  5b35eeeed3c98f3d2d0972b58b7f5e905ae5872a (commit)
       via  b37028bb97e1c1dc628d28caa2c2318d4da409d1 (commit)
       via  dcf2f7a347ceb98d7d9713fa4815b8183a13d5b9 (commit)
       via  6445a869c0d6c857450ebbae46186b810e59911a (commit)
       via  5b5d71d08a7fbeb2a052a9ddf7a1c8a4faa0da7f (commit)
       via  92939b01b77adec243a5d9beba84936a9ca3947b (commit)
       via  c5362882e6f50d6672f084b9852f998b87a01fcc (commit)
       via  e5a1796b9bb9c31634dfa35832686766b4683028 (commit)
       via  539c961f6f63bc4cc6a4f07ea608b08f060e96c8 (commit)
       via  84bca5bbc2d00462ad237d375c6555dc4c16d3be (commit)
       via  b3a01ec628bebb147c88ca41a52d699d70f77887 (commit)
       via  b94f81857a38691d156a39338bbe3e139c95433e (commit)
       via  66b5505d45f44e1b9a76c33015fdd7543c12310f (commit)
       via  fdfc7942b08d52562801713f25a1cf988225be65 (commit)
       via  f60285590b5485e4105071e1ce4020bf9026e1ef (commit)
       via  8439aa2b9b670c6c3b35fb9cf6908b5ce19e03de (commit)
       via  6365a4cb5d05e7eb2630b0a26c6e91dbdb2a2123 (commit)
       via  8eafb118d1654a534ab836d2f7275c8153e3b941 (commit)
       via  9006f4a59cfe2149b93e4c726b42969b567f7d69 (commit)
       via  e6d43d971febfb231c2cd4fbbc1a3045fbde6937 (commit)
       via  d89340ec8c50cc600be22be8c1335ca8f8e1de76 (commit)
       via  8ba5b2566d646c596e3a89ffaaf3a52771e9ea4a (commit)
       via  6c1c0d22eb6c6693754f6fc845ab715b20fe3dc0 (commit)
       via  861e37bd3dae69c7e9b6a8cd5a687763379ca58a (commit)
       via  75cba9e266f86bb406c7b86aab45c71767ac90d8 (commit)
       via  f7b6768fd794368b50dd6405b0068b2e2b72d28c (commit)
       via  e96a2aa11681e12b5120f28d6756aafe1400412b (commit)
       via  3499e74793409357bcf82fa3d69017341b94f59c (commit)
       via  04ab4fe08e55f4f4a1bf6be9885de31c298c8e66 (commit)
       via  c1c3fdcc41c591e50755f0ec985928939b63c36c (commit)
       via  3b3c145b31f048dbfe80296d91275ce94e86e45e (commit)
       via  559b9c89255acd600c10f6f0a50456c0efe14109 (commit)
       via  4d14558d12ba6b319527dedf1fa36bb95945d6dc (commit)
       via  d4c2a7c746c1b1dd9095d533d12ea484b2839769 (commit)
       via  265398a755330a15787fc7aa69924648a9b528c5 (commit)
       via  c005bbc4e71fb40b5766e4a26d63cf30dec25bd1 (commit)
       via  6e31c25958c932247293307f015082d13df2b31e (commit)
       via  b75628bac35ba95c3c70d3a9dccba9c116b263a7 (commit)
       via  b1b4c6bc2a503e903dcf2c1e19fd7e386a8213ca (commit)
       via  c8d8cf7147febea9ade38e28f9b828eda7360402 (commit)
       via  5da7378d77a8bd36de9c78305a785f046163c920 (commit)
       via  b72afbf25c794c48d142d0cd665e210c559b57fb (commit)
       via  092b64a698c248ce93ce1881014c6a41e21a61c3 (commit)
       via  c94349b46c7749705dce1047fd048437baac8518 (commit)
       via  4f9151805b10d30f2d5f6c241a20c8748337fdec (commit)
       via  2c2e237a244d60c893b607249cf3bce65ca8ff7b (commit)
       via  cac70a5c83b14573d816108094bea2b9700b70f9 (commit)
       via  54ba077c24f31891f5c466e5a0ba3a39897c8a53 (commit)
       via  b898e68df808df4f2237b4130fa23bab5200a2cf (commit)
       via  5a2665a0073fda2ad80ec670f4c83de11d389342 (commit)
       via  64324badc16a2a920198600f746c1e9b23afa5c9 (commit)
       via  c226acf269b3df217a5ec1117b9ed1525c111ef4 (commit)
       via  cf9e6e3eaf59710e86d896a5cd80d122a1afa3e8 (commit)
       via  a737395d986559888c214c894cce07d614bab78a (commit)
       via  d71817b8ec2e482fa41d2bd5fb5a6e5c5c7f66f5 (commit)
       via  cb51eaa57a9f7fa5a7e44c16de8669a66efbbf32 (commit)
       via  1b4e8047d89d66cac2d2f3cce52132186de0da6d (commit)
       via  b5f0772583dda128584dd65b76f525577745c137 (commit)
       via  53ef90396c4951ab6f29ab4fe255cf24bd93879d (commit)
       via  0a195f200987ef888ee52a2906829e458f9fd7ad (commit)
       via  ef0cd78fb0629ded896a4295b2c9add7a6608560 (commit)
       via  f7d6e9545fd25b055a80d5442f94916d676f2e3f (commit)
       via  feac4d0816d71ffe3fb3bf46c5dd0b9b919917ea (commit)
       via  b08887f153ce00d3501b0992ba4e37a332d8af72 (commit)
       via  80bec3b0f51d6abcfee150bf932701dfdd368586 (commit)
       via  302db0f93eadd030c49eea9ceed308c779dfbb46 (commit)
       via  f0c1eb56cb7a54b762f1d9f651399b48e0ba04aa (commit)
       via  a1805270345b3eebe19a2d99cd78d1a24b351d55 (commit)
      from  38373f3f05f4b34858f236f48c23e56b370112b8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 debian/changelog                                   |   70 ++
 debian/control                                     |    2 +-
 debian/pyversions                                  |    1 -
 debian/rules                                       |    4 +-
 ... => x2go_start_session_with_progress_status.py} |   26 +-
 setup.py                                           |    2 +-
 x2go/__init__.py                                   |    2 +-
 x2go/{monkey_patch_paramiko.py => _paramiko.py}    |   22 +-
 x2go/backends/control/_stdout.py                   |  148 ++--
 x2go/backends/profiles/_file.py                    |   45 +-
 x2go/backends/proxy/base.py                        |    2 +
 x2go/backends/terminal/_stdout.py                  |   48 +-
 x2go/cache.py                                      |    1 +
 x2go/client.py                                     |  152 +++-
 x2go/defaults.py                                   |   47 +-
 x2go/forward.py                                    |   17 +-
 x2go/guardian.py                                   |    6 +-
 x2go/inifiles.py                                   |    3 +-
 x2go/mimeboxactions.py                             |    4 +-
 x2go/registry.py                                   |   90 ++-
 x2go/session.py                                    |  791 +++++++++++++++-----
 x2go/sshproxy.py                                   |   69 +-
 x2go/utils.py                                      |  158 +++-
 23 files changed, 1326 insertions(+), 384 deletions(-)
 delete mode 100644 debian/pyversions
 copy examples/{x2go_start_session.py => x2go_start_session_with_progress_status.py} (69%)
 rename x2go/{monkey_patch_paramiko.py => _paramiko.py} (80%)

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index f82a5fd..9baeb62 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,73 @@
+python-x2go (0.2.1.0-0~x2go1) unstable; urgency=low
+
+  * New upstream version (0.2.1.0):
+    - Prepare for staying compatible with new SSH proxy feature
+      in X2Go Client.
+    - Add sshproxy_port option to session (SSH proxy) options.
+    - Allow smooth session profile migration, prefer <host>:<port> (set
+      via sshproxy_host) for SSH proxy port detection.
+    - Implementation of session profile parameters ,,sshproxysameuser''
+      and ,,sshproxysameauth''.
+    - Fixing typos in __doc__ strings.
+    - Add support for starting maximized session windows. Closes upstream
+      issue #12.
+    - Fix category ,,Utility'' when parsing .desktop files.
+    - For ,,maxdim'' session property fallback to _NET_DESKTOP_GEOMETRY if
+      _NET_WORKAREA is not available from the window manager.
+    - Mention ,,maximize'' as possible value for session option geometry
+      in __doc__string of class X2goTerminalSessionSTDOUT.
+    - Implement SSH agent authentication forwarding.
+    - Implement X2Go session profile features ,,autologin'' and
+      ,,sshproxyautologin'' (meaning: look_for_keys and allow_agent in Python
+      Paramiko terms).
+    - Make X2goClient instance available in initial X2goSession instances.
+    - Allow post-initialization updating of forward_sshagent class property.
+    - Enable autologin and sshproxyautologin for new session profiles.
+    - Only monkey patch Python Paramiko based on the currently used Paramiko
+      version (our monkey patches have been sent upstream, so we might not
+      need the monkey patching for paramiko >= 1.8.0 anymore).
+    - Rename session type XFCE4 to XFCE (using an unversioned name).
+    - Avoid false positive notifications about started-by-other sessions.
+    - Introduce locks for session registrations.
+    - Wait for mounting of print and mimebox spooling share.
+    - Allow mixing key file, key object, key discovery and agent
+      authentication.
+    - Add progress bar support for session startup / resuming. Closes upstream
+      issue #14.
+    - Set the session name in case a session start failed due to lack of
+      forwarding tunneling support in the server's SSH daemon.
+    - Fall back to password auth if agent auth and key discovery fail.
+    - Give the session window more time to appear.
+    - Catch rare condition in utils.find_session_window in case where
+      the list of window IDs is not available.
+    - Disable SSH agent forwarding on MS Windows platform for current
+      versions of Python Paramiko.
+    - Allow usernames containing space characters in user names (common
+      on MS Windows).
+    - Use threading.Lock to prohibit simultaneous calls of
+      get_published_applications() of control sessions.
+    - Implement some internal locking for X2goSession objects.
+    - Add option to disable auto-registration of pubapp sessions.
+    - Implement functionality for restoring mounted shares on session
+      resumption / re-start. Sponsored by Dick Kniep, LinDix NL.
+    - Catch exceptions where a user tries to resume a session that has
+      just been removed from the session list on the server (race
+      condition).
+    - Consolidating management of shared and unshared client-side
+      folders.
+    - Before suspending/terminating a session, make sure that the list of
+      shared folders is up-to-date.
+    - Fix password authentication in case no private RSA/DSA key for a
+      client user exists.
+  * /debian/rules:
+    + Allow package build on systems with missing dh_python2.
+  * /debian/control:
+    + Versioned depend on python-paramiko (>= 1.8.0-0~).
+  * /debian/pyversions:
+    + Drop file as it is deprecated.
+
+ -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Mon, 10 Dec 2012 13:01:51 +0100
+
 python-x2go (0.2.0.10-0~x2go2) unstable; urgency=low
 
   * /debian/control:
diff --git a/debian/control b/debian/control
index 36f017e..9c85958 100644
--- a/debian/control
+++ b/debian/control
@@ -26,7 +26,7 @@ Depends:
  ${python:Depends},
  ${misc:Depends},
  python-gevent (>= 0.13.6-0~),
- python-paramiko,
+ python-paramiko (>= 1.8.0-0~),
  python-cups,
  python-xlib,
  nxproxy
diff --git a/debian/pyversions b/debian/pyversions
deleted file mode 100644
index 44dd54d..0000000
--- a/debian/pyversions
+++ /dev/null
@@ -1 +0,0 @@
-2.5-
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
index d0a0f1d..214d87b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -3,8 +3,10 @@
 # Based on sample debian/rules file - for GNU Hello (1.3).
 # Copyright 2010-2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
 
+WITH_PYTHON2 = $(shell test -f /usr/bin/dh_python2 && echo "--with python2")
+
 %:
-	dh ${@} --with python2
+	dh ${@} ${WITH_PYTHON2}
 
 override_dh_auto_build:
 	mkdir -p .epydoc/html
diff --git a/examples/x2go_start_session.py b/examples/x2go_start_session_with_progress_status.py
similarity index 69%
copy from examples/x2go_start_session.py
copy to examples/x2go_start_session_with_progress_status.py
index d260f64..8e7b0e6 100644
--- a/examples/x2go_start_session.py
+++ b/examples/x2go_start_session_with_progress_status.py
@@ -28,13 +28,21 @@ import x2go
 import sys
 import gevent
 import getpass
+import threading
 
 # modify to your needs...
 server   = "server.mydomain.tld"
-port     = 22
+port     = 222
 username = "foo"
 command  = "GNOME"
 
+def my_progress_bar(ps):
+
+    for status in ps:
+        print '---------------'
+        print 'SESSION STATUS: ' + '#' * status + "(" + str(status) + "%%)"
+        print '---------------'
+
 password = getpass.getpass()
 
 cli = x2go.X2goClient(use_cache=False, loglevel=x2go.log.loglevel_DEBUG)
@@ -47,8 +55,20 @@ cli.connect_session(s_uuid, password=password)
 
 # clean sessions and check the result
 cli.clean_sessions(s_uuid)
-# start the session and run a command
-cli.start_session(s_uuid)
+
+# initialize a ProgressStatus event and iterator
+progress_event = threading.Event()
+progress_status = x2go.utils.ProgressStatus(progress_event, cli.get_session(s_uuid).get_progress_status)
+
+# start the status bar
+gevent.spawn(my_progress_bar, progress_status)
+
+# start the session
+gevent.spawn(cli.start_session, s_uuid, progress_event=progress_event)
+
+# wait long enough for session to come up completely
+while (cli.get_session(s_uuid).get_progress_status() < 100) and (cli.get_session(s_uuid).get_progress_status() != -1):
+    gevent.sleep(1)
 
 try:
     while cli.session_ok(s_uuid):
diff --git a/setup.py b/setup.py
index 1aeb494..396dd13 100755
--- a/setup.py
+++ b/setup.py
@@ -25,7 +25,7 @@ setup(
     name = "x2go",
     version = x2go.__VERSION__,
     description = "Python X2Go implements an X2Go client/session library in Python based on the Python Paramiko SSH module.",
-    license = 'GPLv3+',
+    license = 'AGPLv3+',
     author = 'Mike Gabriel',
     url = 'http://www.x2go.org',
     packages = find_packages('.'),
diff --git a/x2go/__init__.py b/x2go/__init__.py
index 706d488..b0567cd 100644
--- a/x2go/__init__.py
+++ b/x2go/__init__.py
@@ -178,7 +178,7 @@ Contact
 """
 
 __NAME__    = 'python-x2go'
-__VERSION__ = '0.2.0.10'
+__VERSION__ = '0.2.1.0'
 
 from gevent import monkey
 monkey.patch_all()
diff --git a/x2go/monkey_patch_paramiko.py b/x2go/_paramiko.py
similarity index 80%
rename from x2go/monkey_patch_paramiko.py
rename to x2go/_paramiko.py
index f9b373b..b1e57c5 100644
--- a/x2go/monkey_patch_paramiko.py
+++ b/x2go/_paramiko.py
@@ -18,11 +18,22 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """\
-Monkey Patch for Python Paramiko
+Monkey Patch and feature map for Python Paramiko
 
 """
 
 import paramiko
+import platform
+from utils import compare_versions
+
+PARAMIKO_VERSION = paramiko.__version__.split()[0]
+PARAMIKO_FEATURE = {
+    'forward-ssh-agent': compare_versions(PARAMIKO_VERSION, ">=", '1.8.0') and (platform.system() != "Windows"),
+    'use-compression': compare_versions(PARAMIKO_VERSION, ">=", '1.7.7.1'),
+    'hash-host-entries': compare_versions(PARAMIKO_VERSION, ">=", '99'),
+    'host-entries-reloadable': compare_versions(PARAMIKO_VERSION, ">=", '99'),
+    'preserve-known-hosts': compare_versions(PARAMIKO_VERSION, ">=", '99'),
+}
 
 def _SSHClient_save_host_keys(self, filename):
     """\
@@ -114,6 +125,9 @@ def _HostKeys_add(self, hostname, keytype, key, hash_hostname=True):
 
 
 def monkey_patch_paramiko():
-    paramiko.SSHClient.save_host_keys = _SSHClient_save_host_keys
-    paramiko.hostkeys.HostKeys.load = _HostKeys_load
-    paramiko.hostkeys.HostKeys.add = _HostKeys_add
+    if not PARAMIKO_FEATURE['preserve-known-hosts']:
+        paramiko.SSHClient.save_host_keys = _SSHClient_save_host_keys
+    if not PARAMIKO_FEATURE['host-entries-reloadable']:
+        paramiko.hostkeys.HostKeys.load = _HostKeys_load
+    if not PARAMIKO_FEATURE['hash-host-entries']:
+        paramiko.hostkeys.HostKeys.add = _HostKeys_add
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index 108d6ef..7e29166 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -53,8 +53,8 @@ from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
 from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
 from x2go.backends.proxy import X2goProxy as _X2goProxy
 
-from x2go.monkey_patch_paramiko import monkey_patch_paramiko
-monkey_patch_paramiko()
+import x2go._paramiko
+x2go._paramiko.monkey_patch_paramiko()
 
 def _rerewrite_blanks(cmd):
     """\
@@ -117,6 +117,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                  profile_name='UNKNOWN',
                  add_to_known_hosts=False,
                  known_hosts=None,
+                 forward_sshagent=False,
                  terminal_backend=_X2goTerminalSession,
                  info_backend=_X2goServerSessionInfo,
                  list_backend=_X2goServerSessionList,
@@ -140,6 +141,8 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @type add_to_known_hosts: C{bool}
         @param known_hosts: the underlying Paramiko/SSH systems C{known_hosts} file
         @type known_hosts: C{str}
+        @param forward_sshagent: forward SSH agent authentication requests to the X2Go client-side
+        @type forward_sshagent: C{bool}
         @param terminal_backend: X2Go terminal session backend to use
         @type terminal_backend: C{class}
         @param info_backend: backend for handling storage of server session information
@@ -163,7 +166,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
             constructed with the given loglevel
         @type loglevel: C{int}
-        @param kwargs: parameters passed through to C{SSHClient.__init__()}
+        @param kwargs: catch any non-defined parameters in C{kwargs}
         @type kwargs: C{dict}
 
         """
@@ -173,6 +176,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         self.profile_name = profile_name
         self.add_to_known_hosts = add_to_known_hosts
         self.known_hosts = known_hosts
+        self.forward_sshagent = forward_sshagent
 
         self.hostname = None
         self.port = None
@@ -204,14 +208,17 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
 
         self._published_applications_menu = {}
 
-        paramiko.SSHClient.__init__(self, **kwargs)
+        self.agent_chan = None
+        self.agent_handler = None
+
+        paramiko.SSHClient.__init__(self)
         if self.add_to_known_hosts:
             self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
         self.session_died = False
 
         self.published_applications_no_submenus = published_applications_no_submenus
-        self._already_querying_published_applications = False
+        self._already_querying_published_applications = threading.Lock()
 
         self._transport_lock = threading.Lock()
 
@@ -605,9 +612,11 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         return checkhosts.check_ssh_host_key(self, hostname, port=port)
 
     def connect(self, hostname, port=22, username='', password='', pkey=None,
-                use_sshproxy=False, sshproxy_host='', sshproxy_user='', sshproxy_password='',
-                sshproxy_key_filename='', sshproxy_tunnel='',
                 key_filename=None, timeout=None, allow_agent=False, look_for_keys=False,
+                use_sshproxy=False, sshproxy_host='', sshproxy_port=22, sshproxy_user='', sshproxy_password='', sshproxy_force_password_auth=False,
+                sshproxy_key_filename='', sshproxy_pkey=None, sshproxy_look_for_keys=False, sshproxy_allow_agent=False,
+                sshproxy_tunnel='',
+                forward_sshagent=None,
                 session_instance=None,
                 add_to_known_hosts=False, force_password_auth=False):
         """\
@@ -643,18 +652,22 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @param password: a password to use for authentication or for unlocking
             a private key
         @type password: C{str}
-        @param pkey: an optional private key to use for authentication
-        @type pkey: C{PKey}
         @param key_filename: the filename, or list of filenames, of optional
             private key(s) to try for authentication
         @type key_filename: C{str} or list(str)
+        @param pkey: an optional private key to use for authentication
+        @type pkey: C{PKey}
+        @param forward_sshagent: forward SSH agent authentication requests to the X2Go client-side
+            (will update the class property of the same name)
+        @type forward_sshagent: C{bool}
         @param timeout: an optional timeout (in seconds) for the TCP connect
         @type timeout: float
-        @param allow_agent: set to False to disable connecting to the SSH agent
-        @type allow_agent: C{bool}
-        @param look_for_keys: set to False to disable searching for discoverable
+        @param look_for_keys: set to C{True} to enable searching for discoverable
             private key files in C{~/.ssh/}
         @type look_for_keys: C{bool}
+        @param allow_agent: set to C{True} to enable connecting to a local SSH agent
+            for acquiring authentication information
+        @type allow_agent: C{bool}
         @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy() 
             is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy() 
             is used
@@ -667,16 +680,27 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @type session_instance: C{obj}
         @param use_sshproxy: connect through an SSH proxy
         @type use_sshproxy: C{True} if an SSH proxy is to be used for tunneling the connection
-        @param sshproxy_host: hostname of the SSH proxy server, use <hostname>:<port> to name a
-            non-standard SSH port
+        @param sshproxy_host: hostname of the SSH proxy server
         @type sshproxy_host: C{str}
+        @param sshproxy_port: port of the SSH proxy server
+        @type sshproxy_port: C{int}
         @param sshproxy_user: username that we use for authenticating against C{<sshproxy_host>}
         @type sshproxy_user: C{str}
         @param sshproxy_password: a password to use for SSH proxy authentication or for unlocking
             a private key
         @type sshproxy_password: C{str}
+        @param sshproxy_force_password_auth: enforce using a given C{sshproxy_password} even if a key(file) is given
+        @type sshproxy_force_password_auth: C{bool}
         @param sshproxy_key_filename: local file location of the private key file
         @type sshproxy_key_filename: C{str}
+        @param sshproxy_pkey: an optional private key to use for SSH proxy authentication
+        @type sshproxy_pkey: C{PKey}
+        @param sshproxy_look_for_keys: set to C{True} to enable connecting to a local SSH agent
+            for acquiring authentication information (for SSH proxy authentication)
+        @type sshproxy_look_for_keys: C{bool}
+        @param sshproxy_allow_agent: set to C{True} to enable connecting to a local SSH agent
+            for acquiring authentication information (for SSH proxy authentication)
+        @type sshproxy_allow_agent: C{bool}
         @param sshproxy_tunnel: the SSH proxy tunneling parameters, format is: <local-address>:<local-port>:<remote-address>:<remote-port>
         @type sshproxy_tunnel: C{str}
 
@@ -698,9 +722,14 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             try:
                 self.sshproxy_session = sshproxy.X2goSSHProxy(known_hosts=self.known_hosts,
                                                               sshproxy_host=sshproxy_host,
+                                                              sshproxy_port=sshproxy_port,
                                                               sshproxy_user=sshproxy_user,
                                                               sshproxy_password=sshproxy_password,
+                                                              sshproxy_force_password_auth=sshproxy_force_password_auth,
                                                               sshproxy_key_filename=sshproxy_key_filename,
+                                                              sshproxy_pkey=sshproxy_pkey,
+                                                              sshproxy_look_for_keys=sshproxy_look_for_keys,
+                                                              sshproxy_allow_agent=sshproxy_allow_agent,
                                                               sshproxy_tunnel=sshproxy_tunnel,
                                                               session_instance=session_instance,
                                                               logger=self.logger,
@@ -743,21 +772,37 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         if _hostname in ('localhost', 'localhost.localdomain'):
             _hostname = '127.0.0.1'
 
-        if (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
+        # update self.forward_sshagent via connect method parameter
+        if forward_sshagent is not None:
+            self.forward_sshagent = forward_sshagent
+
+        if key_filename or pkey or look_for_keys or allow_agent or (password and force_password_auth):
             try:
-                self.logger('trying SSH pub/priv key authentication with server', loglevel=log.loglevel_DEBUG)
-                if password:
-                    paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=pkey, password=password,
-                                               key_filename=key_filename, timeout=timeout, allow_agent=allow_agent, 
-                                               look_for_keys=look_for_keys)
-                else:
+                if password and force_password_auth:
+                    self.logger('trying keyboard-interactive SSH authentication with server', loglevel=log.loglevel_DEBUG)
+                    paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=None, password=password,
+                                               key_filename=None, timeout=timeout, allow_agent=False,
+                                               look_for_keys=False)
+                elif (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
+                    self.logger('trying SSH pub/priv key authentication with server', loglevel=log.loglevel_DEBUG)
                     paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=pkey,
-                                               key_filename=key_filename, timeout=timeout, allow_agent=allow_agent, 
+                                               key_filename=key_filename, timeout=timeout, allow_agent=allow_agent,
                                                look_for_keys=look_for_keys)
+                else:
+                    self.logger('trying SSH key discovery or agent authentication with server', loglevel=log.loglevel_DEBUG)
+                    try:
+                        paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=None,
+                                                      key_filename=None, timeout=timeout, allow_agent=allow_agent,
+                                                      look_for_keys=look_for_keys)
+                    except paramiko.SSHException, e:
+                        if str(e) == 'No authentication methods available':
+                            raise paramiko.AuthenticationException('Interactive password authentication required!')
+                        else:
+                            raise(e)
 
                 # since Paramiko 1.7.7.1 there is compression available, let's use it if present...
                 t = self.get_transport()
-                if hasattr(t, 'use_compression'):
+                if x2go._paramiko.PARAMIKO_FEATURE['use-compression']:
                     t.use_compression(compress=True)
 
             except paramiko.AuthenticationException, e:
@@ -766,13 +811,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                     self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', loglevel=log.loglevel_DEBUG)
                     try:
                         paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password,
-                                                   timeout=timeout, allow_agent=allow_agent, 
-                                                   look_for_keys=look_for_keys)
-                    except paramiko.AuthenticationException, e:
-                        self.close()
-                        if self.sshproxy_session:
-                            self.sshproxy_session.stop_thread()
-                        raise e
+                                                   key_filename=None, pkey=None, timeout=timeout, allow_agent=False, look_for_keys=False)
                     except:
                         self.close()
                         if self.sshproxy_session:
@@ -790,7 +829,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                     self.sshproxy_session.stop_thread()
                 raise
 
-        # if there is not private key, we will use the given password, if any
+        # if there is no private key (and no agent auth), we will use the given password, if any
         else:
             # create a random password if password is empty to trigger host key validity check
             if not password:
@@ -798,7 +837,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             self.logger('performing SSH keyboard-interactive authentication with server', loglevel=log.loglevel_DEBUG)
             try:
                 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password,
-                                           timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys)
+                                           timeout=timeout, allow_agent=False, look_for_keys=False)
             except paramiko.AuthenticationException, e:
                 self.close()
                 if self.sshproxy_session:
@@ -823,9 +862,16 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         ssh_transport._x2go_session_marker = True
         self._session_password = password
 
-        if self.get_transport():
+        if ssh_transport is not None:
             self.session_died = False
             self.query_server_features(force=True)
+            if self.forward_sshagent:
+                if x2go._paramiko.PARAMIKO_FEATURE['forward-ssh-agent']:
+                    self.agent_chan = ssh_transport.open_session()
+                    self.agent_handler = paramiko.agent.AgentRequestHandler(self.agent_chan)
+                    self.logger('Requesting SSH agent forwarding for control session of connected session profile %s' % self.profile_name, loglevel=log.loglevel_INFO)
+                else:
+                    self.logger('SSH agent forwarding is not available in the Paramiko version used with this instance of Python X2Go', loglevel=log.loglevel_WARN)
 
         self._remote_home = None
         if not self.home_exists():
@@ -880,6 +926,13 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         # in any case, release out internal transport lock
         self._transport_lock.release()
 
+        # close SSH agent auth forwarding objects
+        if self.agent_handler is not None:
+            self.agent_handler.close()
+
+        if self.agent_chan is not None:
+            self.agent_chan.close()
+
         retval = False
         try:
             if self.get_transport() is not None:
@@ -961,12 +1014,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @rtype: C{list}
 
         """
-
-        if self._already_querying_published_applications:
-            self.logger('This control session instance is currently already querying the published applications menu tree for session profile %s. Whenever this warning pops up in your log file it means that you should fix your client implementation code. Only call this method once per session profile!!!' % self.profile_name, loglevel=log.loglevel_WARN)
-            return None
-        else:
-            self._already_querying_published_applications = True
+        self._already_querying_published_applications.acquire()
 
         if defaults.X2GOCLIENT_OS != 'Windows' and lang is None:
             lang = locale.getdefaultlocale()[0]
@@ -989,6 +1037,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
 
                 if very_raw:
                     self.logger('published applications query for %s finished, return very raw output' % self.profile_name, loglevel=log.loglevel_NOTICE)
+                    self._already_querying_published_applications.release()
                     return _raw_output
 
                 ### STAGE 2: dissect the text file retrieved from server, cut into single menu elements
@@ -1010,6 +1059,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
 
                 if raw:
                     self.logger('published applications query for %s finished, returning raw output' % self.profile_name, loglevel=log.loglevel_NOTICE)
+                    self._already_querying_published_applications.release()
                     return _menu
 
                 if len(_menu) > max_no_submenus >= 0:
@@ -1083,7 +1133,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                                 continue
                             elif 'System' in line:
                                 _menu_entry_cat = 'System'
-                            elif 'Utilities' in line:
+                            elif 'Utility' in line:
                                 _menu_entry_cat = 'Utilities'
                             else:
                                 _menu_entry_cat = 'Other Applications'
@@ -1134,10 +1184,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             # FIXME: ignoring the absence of the published applications feature for now, handle it appropriately later
             pass
 
-        self._already_querying_published_applications = False
+        self._already_querying_published_applications.release()
         return self._published_applications_menu
 
-
     def start(self, **kwargs):
         """\
         Start a new X2Go session.
@@ -1176,13 +1225,16 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         if not self.is_x2gouser(self.get_transport().get_username()):
             raise x2go_exceptions.X2goUserException('remote user %s is not allowed to run X2Go commands' % self.get_transport().get_username())
 
-        if session_name is not None:
-            if session_list:
-                session_info = session_list[session_name]
+        try:
+            if session_name is not None:
+                if session_list:
+                    session_info = session_list[session_name]
+                else:
+                    session_info = self.list_sessions()[session_name]
             else:
-                session_info = self.list_sessions()[session_name]
-        else:
-            session_info = None
+                session_info = None
+        except KeyError:
+            _success = False
 
         _terminal = self._terminal_backend(self,
                                            profile_name=self.profile_name,
diff --git a/x2go/backends/profiles/_file.py b/x2go/backends/profiles/_file.py
index 586f262..eb21f61 100644
--- a/x2go/backends/profiles/_file.py
+++ b/x2go/backends/profiles/_file.py
@@ -28,6 +28,7 @@ __NAME__ = 'x2gosessionprofiles-pylib'
 
 import copy
 import types
+import re
 
 # Python X2Go modules
 from x2go.defaults import X2GO_SESSIONPROFILES_CONFIGFILES as _X2GO_SESSIONPROFILES_CONFIGFILES
@@ -177,12 +178,14 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         # we have to handle the get_type method separately...
         return self.get_profile_option_type(key)
 
-    def get_profile_config(self, profile_id_or_name=None, profile_id=None):
+    def get_profile_config(self, profile_id_or_name=None, parameter=None, profile_id=None):
         """\
         The configuration options for a single session profile.
 
         @param profile_id_or_name: either profile ID or profile name is accepted
         @type profile_id_or_name: C{str}
+        @param parameter: if specified, only the value for the given parameter is returned
+        @type parameter: C{str}
         @param profile_id: profile ID (fast than specifying C{profile_id_or_name})
         @type profile_id: C{str}
 
@@ -194,6 +197,27 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         _profile_config = {}
         for option in self.iniConfig.options(_profile_id):
             _profile_config[option] = self.get(_profile_id, option, key_type=self.get_profile_option_type(option))
+
+        if parameter is not None:
+            if parameter in _profile_config.keys():
+
+                value = _profile_config[parameter]
+
+                if parameter == 'export':
+                    _strvalue = value.replace(',', ';').strip().strip('"').strip().strip(';').strip()
+                    value = {}
+                    if _strvalue:
+                        _export_paths = _strvalue.split(';')
+                        for _path in _export_paths:
+                            if not re.match('.*:(0|1)$', _path): _path = '%s:1' % _path
+                            _auto_export_path = re.match('.*:1$', _path) and True or False
+                            _export_path = ':'.join(_path.split(':')[:-1])
+                            value[_export_path] = _auto_export_path
+
+                return value
+
+            else:
+                raise X2goProfileException('no such session profile parameter: %s' % parameter)
         return _profile_config
 
     def default_profile_config(self):
@@ -362,17 +386,30 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         @type value: any type, depends on the session profile option
 
         """
-        profile_id = section
+        try:
+            profile_id = self.check_profile_id_or_name(section)
+        except X2goProfileException:
+            profile_id = section
         if key == 'name':
             profile_name = value
-            current_profile_name = self.get_value(section, key)
+            current_profile_name = self.get_value(profile_id, key)
             if not profile_name:
                 raise X2goProfileException('profile name for profile id %s may not be empty' % profile_id)
             else:
                 if profile_name != current_profile_name and profile_name in self.profile_names:
                     raise X2goProfileException('a profile of name ,,%s'' already exists' % profile_name)
             self._cached_profile_names = []
-        inifiles.X2goIniFile.update_value(self, section, key, value)
+
+        if key == 'export' and type(value) == types.DictType:
+
+            _strvalue = '"'
+            for folder in value.keys():
+                _strvalue += "%s:%s;" % (folder, int(value[folder]))
+            _strvalue += '"'
+            _strvalue = _strvalue.replace('""', '')
+            value = _strvalue
+
+        inifiles.X2goIniFile.update_value(self, profile_id, key, value)
 
     def check_profile_id_or_name(self, profile_id_or_name):
         """\
diff --git a/x2go/backends/proxy/base.py b/x2go/backends/proxy/base.py
index 1c62313..38e80da 100644
--- a/x2go/backends/proxy/base.py
+++ b/x2go/backends/proxy/base.py
@@ -105,6 +105,7 @@ class X2goProxyBASE(threading.Thread):
 
         self.sessions_rootdir = sessions_rootdir
         self.session_info = session_info
+        self.session_name = self.session_info.name
         self.ssh_transport = ssh_transport
         self.session_log = session_log
         self.proxy_options = proxy_options
@@ -186,6 +187,7 @@ class X2goProxyBASE(threading.Thread):
                                                       remote_port=self.session_info.graphics_port,
                                                       ssh_transport=self.ssh_transport,
                                                       session_instance=self.session_instance,
+                                                      session_name=self.session_name,
                                                       logger=self.logger,
                                                      )
 
diff --git a/x2go/backends/terminal/_stdout.py b/x2go/backends/terminal/_stdout.py
index 3df1491..1ce197f 100644
--- a/x2go/backends/terminal/_stdout.py
+++ b/x2go/backends/terminal/_stdout.py
@@ -227,8 +227,8 @@ class X2goTerminalSessionSTDOUT(object):
         new X2Go sessions, resume suspended sessions or suspend resp. terminate
         currently running sessions on a connected X2Go server.
 
-        @param geometry: screen geometry of the X2Go session. Can be either C{<width>x<height>}
-            or C{fullscreen}
+        @param geometry: screen geometry of the X2Go session. Can be either C{<width>x<height>},
+            C{maximize} or C{fullscreen}
         @type geometry: C{str}
         @param depth: color depth in bits (common values: C{16}, C{24})
         @type depth: C{int}
@@ -844,17 +844,17 @@ class X2goTerminalSessionSTDOUT(object):
                 _client_encoding = 'WINDOWS-1252'
 
             if _convert_encoding:
-                export_iconv_settings = 'export X2GO_ICONV=modules=iconv,from_code=%s,to_code=%s &&' % (_client_encoding, _server_encoding)
+                export_iconv_settings = 'export X2GO_ICONV=modules=iconv,from_code=%s,to_code=%s && ' % (_client_encoding, _server_encoding)
             else:
                 export_iconv_settings = ''
 
             if folder_type == 'disk':
 
-                cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
+                cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
                              'x2gomountdirs',
                              'dir',
                              str(self.session_info.name),
-                             '"%s"' % _CURRENT_LOCAL_USER,
+                             '\'%s\'' % _CURRENT_LOCAL_USER,
                              _x2go_key_fname,
                              '%s__REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
                              'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
@@ -862,11 +862,11 @@ class X2goTerminalSessionSTDOUT(object):
 
             elif folder_type == 'spool':
 
-                cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
+                cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
                              'x2gomountdirs',
                              'dir',
                              str(self.session_info.name),
-                             '"%s"' % _CURRENT_LOCAL_USER,
+                             '\'%s\'' % _CURRENT_LOCAL_USER,
                              _x2go_key_fname,
                              '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
                              'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
@@ -874,11 +874,11 @@ class X2goTerminalSessionSTDOUT(object):
 
             elif folder_type == 'mimebox':
 
-                cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
+                cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
                              'x2gomountdirs',
                              'dir',
                              str(self.session_info.name),
-                             '"%s"' % _CURRENT_LOCAL_USER,
+                             '\'%s\'' % _CURRENT_LOCAL_USER,
                              _x2go_key_fname,
                              '%s__MIMEBOX_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
                              'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
@@ -987,7 +987,7 @@ class X2goTerminalSessionSTDOUT(object):
         if self.session_title != _generic_title and not dont_set:
             self.set_session_window_title(title=self.session_title)
 
-    def find_session_window(self, timeout=30):
+    def find_session_window(self, timeout=60):
         """\
         Try for <timeout> seconds to find the X2Go session window of this
         terminal session.
@@ -1026,7 +1026,7 @@ class X2goTerminalSessionSTDOUT(object):
 
             gevent.sleep(1)
 
-    def set_session_window_title(self, title, timeout=30):
+    def set_session_window_title(self, title, timeout=60):
         """\
         Modify the session window title.
 
@@ -1066,7 +1066,7 @@ class X2goTerminalSessionSTDOUT(object):
 
             gevent.sleep(1)
 
-    def raise_session_window(self, timeout=30):
+    def raise_session_window(self, timeout=60):
         """\
         Try for <timeout> seconds to raise the X2Go session window of this
         terminal session to the top and bring it to focus.
@@ -1159,7 +1159,7 @@ class X2goTerminalSessionSTDOUT(object):
         @rtype: C{tuple} of C{str}
 
         """
-        if not self.has_command(_rewrite_cmd(self.params.cmd, params=self.params)):
+        if not self.has_command(_rewrite_cmd(str(self.params.cmd), params=self.params)):
             if self.client_instance:
                 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
             return False
@@ -1374,6 +1374,16 @@ class X2goTerminalSessionSTDOUT(object):
 
         self.params.rewrite_session_type()
 
+        if self.params.geometry == 'maximize':
+            _geometry = utils.get_workarea_geometry()
+            if _geometry is None or len(_geometry) != 2:
+                _geometry = utils.get_desktop_geometry()
+            if _geometry and len(_geometry) == 2:
+                self.params.geometry = "%sx%s" % _geometry
+            else:
+                self.logger('failed to detect best maxmimized geometry of your client-side desktop', loglevel=log.loglevel_WARN)
+                self.params.geometry = "1024x768"
+
         cmd_line = [ "x2gostartagent",
                      str(self.params.geometry),
                      str(self.params.link),
@@ -1383,7 +1393,7 @@ class X2goTerminalSessionSTDOUT(object):
                      str(self.params.kbtype),
                      str(setkbd),
                      str(self.params.session_type),
-                     self.params.cmd,
+                     str(self.params.cmd),
                    ]
 
         if self.params.cmd == 'XDMCP' and self.params.xdmcp_server:
@@ -1458,6 +1468,16 @@ class X2goTerminalSessionSTDOUT(object):
         if self.params.kbtype != "null/null":
             setkbd = "1"
 
+        if self.params.geometry == 'maximize':
+            _geometry = utils.get_workarea_geometry()
+            if _geometry is None or len(_geometry) != 2:
+                _geometry = utils.get_desktop_geometry()
+            if _geometry and len(_geometry) == 2:
+                self.params.geometry = "%sx%s" % _geometry
+            else:
+                self.logger('failed to detect best maxmimized geometry of your client-side desktop, using 1024x768 instead', loglevel=log.loglevel_WARN)
+                self.params.geometry = "1024x768"
+
         cmd_line = [ "x2goresume-session", self.session_info.name,
                      self.params.geometry,
                      self.params.link,
diff --git a/x2go/cache.py b/x2go/cache.py
index 5d90191..7c73692 100644
--- a/x2go/cache.py
+++ b/x2go/cache.py
@@ -149,6 +149,7 @@ class X2goListSessionsCache(object):
         @raise X2goControlSessionException: if the control session's C{list_mounts} method fails
         """
         try:
+            self.x2go_listsessions_cache[profile_name]['mounts'] = {}
             if self.x2go_listsessions_cache[profile_name]['sessions']:
                 for session_name in self.x2go_listsessions_cache[profile_name]['sessions']:
                     self.x2go_listsessions_cache[profile_name]['mounts'].update(control_session.list_mounts(session_name))
diff --git a/x2go/client.py b/x2go/client.py
index e52b535..c1e5358 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -179,7 +179,7 @@ class X2goClient(object):
 
     lang = 'en'
 
-    def __init__(self, 
+    def __init__(self,
                  control_backend=control.X2goControlSession,
                  terminal_backend=terminal.X2goTerminalSession,
                  info_backend=info.X2goServerSessionInfo,
@@ -194,12 +194,13 @@ class X2goClient(object):
                  start_xserver=False,
                  start_pulseaudio=False,
                  use_cache=False,
-                 use_listsessions_cache=False, 
+                 use_listsessions_cache=False,
                  auto_update_listsessions_cache=False,
                  auto_update_listdesktops_cache=False,
                  auto_update_listmounts_cache=False,
                  auto_update_sessionregistry=False,
                  auto_register_sessions=False,
+                 no_auto_reg_pubapp_sessions=False,
                  refresh_interval=5,
                  pulseaudio_installdir=os.path.join(os.getcwd(), 'pulseaudio'),
                  logger=None, loglevel=log.loglevel_DEFAULT):
@@ -244,6 +245,8 @@ class X2goClient(object):
         @type auto_update_sessionregistry: C{bool}
         @param auto_register_sessions: activate automatic X2Go session registration
         @type auto_register_sessions: C{bool}
+        @param no_auto_reg_pubapp_sessions: skip automatic X2Go session registration for suspended/running published applications sessions
+        @type no_auto_reg_pubapp_sessions: C{bool}
         @param refresh_interval: refresh session list cache and session status every C{refresh_interval} seconds
         @type refresh_interval: C{int}
         @param pulseaudio_installdir: install path of Pulseaudio binary
@@ -346,12 +349,14 @@ class X2goClient(object):
             self.pulseaudio = X2goPulseAudio(path=self.pulseaudio_installdir, client_instance=self, logger=self.logger)
 
         self.auto_register_sessions = auto_register_sessions
+        self.no_auto_reg_pubapp_sessions = no_auto_reg_pubapp_sessions
         self.session_registry = X2goSessionRegistry(self, logger=self.logger)
         self.session_guardian = X2goSessionGuardian(self, auto_update_listsessions_cache=auto_update_listsessions_cache & (use_listsessions_cache|use_cache),
                                                     auto_update_listdesktops_cache=auto_update_listdesktops_cache & use_listsessions_cache,
                                                     auto_update_listmounts_cache=auto_update_listmounts_cache & use_listsessions_cache,
                                                     auto_update_sessionregistry=auto_update_sessionregistry,
                                                     auto_register_sessions=auto_register_sessions, 
+                                                    no_auto_reg_pubapp_sessions=no_auto_reg_pubapp_sessions,
                                                     refresh_interval=refresh_interval,
                                                     logger=self.logger
                                                    )
@@ -856,7 +861,7 @@ class X2goClient(object):
                          allow_printing=False, 
                          allow_share_local_folders=False, share_local_folders=[], 
                          allow_mimebox=False, mimebox_extensions=[], mimebox_action='OPEN',
-                         add_to_known_hosts=False, known_hosts=None,
+                         add_to_known_hosts=False, known_hosts=None, forward_sshagent=False,
                          proxy_options={},
                          return_object=False, **kwargs):
         """\
@@ -913,6 +918,8 @@ class X2goClient(object):
         @type add_to_known_hosts: C{bool}
         @param known_hosts: full path to C{known_hosts} file
         @type known_hosts: C{str}
+        @param forward_sshagent: forward SSH agent authentication requests to the X2Go client-side
+        @type forward_sshagent: C{bool}
         @param proxy_options: a set of very C{X2goProxy*} backend specific options; any option that is not known
             to the C{X2goProxy*} backend will simply be ignored
         @type proxy_options: C{dict}
@@ -967,7 +974,7 @@ class X2goClient(object):
             _profile_id = utils._genSessionProfileId()
             _profile_name = profile_name or sys.argv[0]
             _params = kwargs
-            _params['printing'] = printing
+            _params['printing'] = allow_printing
             _params['allow_share_local_folders'] = allow_share_local_folders
             _params['share_local_folders'] = share_local_folders
             _params['allow_mimebox'] = allow_mimebox
@@ -975,6 +982,7 @@ class X2goClient(object):
             _params['mimebox_action'] = mimebox_action
             _params['client_instance'] = self
             _params['proxy_options'] = proxy_options
+            _params['forward_sshagent'] = forward_sshagent
 
         session_uuid = self.session_registry.register(server=server,
                                                       profile_id=_profile_id, profile_name=_profile_name,
@@ -1041,7 +1049,7 @@ class X2goClient(object):
         hostname of the host the session is connected to (or
         about to connect to).
 
-        @param session_uuid: the X2Go sessions UUID registry hash
+        @param session_uuid: the X2Go session's UUID registry hash
         @type session_uuid: C{str}
 
         @return: the host an X2Go session is connected to 
@@ -1058,7 +1066,7 @@ class X2goClient(object):
         application (e.g. like it has been specified in the session 
         profile).
 
-        @param session_uuid: the X2Go sessions UUID registry hash
+        @param session_uuid: the X2Go session's UUID registry hash
         @type session_uuid: C{str}
 
         @return: the hostname for the queried X2Go session as specified 
@@ -1197,6 +1205,17 @@ class X2goClient(object):
         return self.session_registry(session_uuid).check_host()
     __check_session_host = check_session_host
 
+    def session_reuses_sshproxy_authinfo(self, session_uuid):
+        """\
+        Check if session with unique identifier <session_uuid> is configured to re-use the X2Go session's
+        password / key for proxy authentication, as well.
+
+        @return: returns C{True} if the session is configured to re-use session password / key for proxy authentication
+        @rtype: C{bool}
+        """
+        return self.session_registry(session_uuid).reuses_sshproxy_authinfo()
+    __session_reuses_sshproxy_authinfo = session_reuses_sshproxy_authinfo
+
     def session_uses_sshproxy(self, session_uuid):
         """\
         Check if session with unique identifier <session_uuid> is configured to use an
@@ -1264,7 +1283,9 @@ class X2goClient(object):
                         sshproxy_user='',
                         sshproxy_password='',
                         add_to_known_hosts=False,
-                        force_password_auth=False):
+                        force_password_auth=False,
+                        sshproxy_force_password_auth=False,
+                       ):
         """\
         Connect to a registered X2Go session with registry hash C{session_uuid}
         This method basically wraps around paramiko.SSHClient.connect() for the
@@ -1288,6 +1309,9 @@ class X2goClient(object):
         @param force_password_auth: disable SSH pub/priv key authentication mechanisms
             completely
         @type force_password_auth: C{bool}
+        @param sshproxy_force_password_auth: disable SSH pub/priv key authentication mechanisms
+            completely for SSH proxy connection
+        @type sshproxy_force_password_auth: C{bool}
 
         @return: returns True if this method has been successful
         @rtype: C{bool}
@@ -1297,6 +1321,7 @@ class X2goClient(object):
                                                                sshproxy_user=sshproxy_user, sshproxy_password=sshproxy_password,
                                                                add_to_known_hosts=add_to_known_hosts,
                                                                force_password_auth=force_password_auth,
+                                                               sshproxy_force_password_auth=sshproxy_force_password_auth,
                                                               )
         if self.auto_register_sessions:
             self.session_registry.register_available_server_sessions(profile_name=self.get_session_profile_name(session_uuid),
@@ -1401,7 +1426,7 @@ class X2goClient(object):
         self.session_registry(session_uuid).do_auto_start_or_resume(newest=newest, oldest=oldest, all_suspended=all_suspended, start=start, redirect_to_client=False)
     __session_auto_start_or_resume = session_auto_start_or_resume
 
-    def start_session(self, session_uuid):
+    def start_session(self, session_uuid, **sessionopts):
         """\
         Start a new X2Go session on the remote X2Go server. This method
         will open---if everything has been successful till here---the X2Go 
@@ -1411,8 +1436,10 @@ class X2goClient(object):
         with L{register_session} (initialization of session parameters) and 
         connect to it with L{connect_session} (authentication).
 
-        @param session_uuid: the X2Go sessions UUID registry hash
+        @param session_uuid: the X2Go session's UUID registry hash
         @type session_uuid: C{str}
+        @param sessionopts: pass-through of options directly to the session instance's L{X2goSession.start()} method
+        @type sessionopts: C{dict}
 
         @return: returns True if this method has been successful
         @rtype: C{bool}
@@ -1423,7 +1450,7 @@ class X2goClient(object):
             self.session_registry.disable_session_auto_registration()
 
         # start the actual session
-        _retval = self.session_registry(session_uuid).start()
+        _retval = self.session_registry(session_uuid).start(**sessionopts)
 
         # re-enable session auto-registration...
         if self.auto_register_sessions:
@@ -1432,7 +1459,7 @@ class X2goClient(object):
         return _retval
     __start_session = start_session
 
-    def share_desktop_session(self, session_uuid, desktop=None, user=None, display=None, share_mode=0):
+    def share_desktop_session(self, session_uuid, desktop=None, user=None, display=None, share_mode=0, **sessionopts):
         """\
         Share another already running desktop session. Desktop sharing can be run
         in two different modes: view-only and full-access mode. Like new sessions
@@ -1449,6 +1476,8 @@ class X2goClient(object):
         @type display: C{str}
         @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
         @type share_mode: C{int}
+        @param sessionopts: pass-through of options directly to the session instance's L{X2goSession.share_desktop()} method
+        @type sessionopts: C{dict}
 
         @return: True if the session could be successfully shared.
         @rtype: C{bool}
@@ -1471,10 +1500,10 @@ class X2goClient(object):
             if not _desktop in self._X2goClient__list_desktops(session_uuid):
                 raise x2go_exceptions.X2goDesktopSharingException('No such desktop ID: %s' % _orig_desktop)
 
-        return self.session_registry(session_uuid).share_desktop(desktop=_desktop, share_mode=share_mode, check_desktop_list=False)
+        return self.session_registry(session_uuid).share_desktop(desktop=_desktop, share_mode=share_mode, check_desktop_list=False, **sessionopts)
     __share_desktop_session = share_desktop_session
 
-    def resume_session(self, session_uuid=None, session_name=None):
+    def resume_session(self, session_uuid=None, session_name=None, **sessionopts):
         """\
         Resume or continue a suspended / running X2Go session on a
         remote X2Go server (as specified when L{register_session} was
@@ -1484,6 +1513,8 @@ class X2goClient(object):
         @type session_uuid: C{str}
         @param session_name: the server-side name of an X2Go session
         @type session_name: C{str}
+        @param sessionopts: pass-through of options directly to the session instance's L{X2goSession.resume()} method
+        @type sessionopts: C{dict}
 
         @return: returns True if this method has been successful
         @rtype: C{bool}
@@ -1498,23 +1529,23 @@ class X2goClient(object):
                 raise x2go_exceptions.X2goClientException('don\'t know which session to resume')
             if session_uuid is None:
                 session_uuid = self.session_registry.get_session_of_session_name(session_name=session_name, return_object=False)
-                return self.session_registry(session_uuid).resume(session_list=self.list_sessions(session_uuid=session_uuid))
+                return self.session_registry(session_uuid).resume(session_list=self.list_sessions(session_uuid=session_uuid), **sessionopts)
             else:
-                return self.session_registry(session_uuid).resume(session_name=session_name, session_list=self.list_sessions(session_uuid=session_uuid))
+                return self.session_registry(session_uuid).resume(session_name=session_name, session_list=self.list_sessions(session_uuid=session_uuid), **sessionopts)
         except x2go_exceptions.X2goControlSessionException:
             profile_name = self.get_session_profile_name(session_uuid)
             self.HOOK_on_control_session_death(profile_name)
             self.disconnect_profile(profile_name)
     __resume_session = resume_session
 
-    def suspend_session(self, session_uuid, session_name=None):
+    def suspend_session(self, session_uuid, session_name=None, **sessionopts):
         """\
         Suspend an X2Go session.
 
         Normally, you will use this method to suspend a registered session that you
         have formerly started/resumed from within your recent
-        L{X2goClient} instance. For this you simply call this method 
-        using the sessions C{session_uuid}, leave the C{session_name}
+        L{X2goClient} instance. For this you simply call this method
+        using the session's C{session_uuid}, leave the C{session_name}
         empty.
 
         Alternatively, you can suspend a non-associated X2Go session:
@@ -1529,6 +1560,8 @@ class X2goClient(object):
         @param session_name: the server-side name of an X2Go session (for 
             non-associated session suspend)
         @type session_name: C{str}
+        @param sessionopts: pass-through of options directly to the session instance's L{X2goSession.suspend()} method
+        @type sessionopts: C{dict}
 
         @return: returns True if this method has been successful
         @rtype: C{bool}
@@ -1536,26 +1569,30 @@ class X2goClient(object):
         """
         try:
             if session_name is None:
-                return self.session_registry(session_uuid).suspend()
+
+                # make sure that the current list of shared folders is up-to-date before the session suspends
+                self.get_shared_folders(session_uuid, check_list_mounts=True)
+
+                return self.session_registry(session_uuid).suspend(**sessionopts)
             else:
                 for session in self.session_registry.running_sessions():
                     if session_name == session.get_session_name():
                         return session.suspend()
-            return self.session_registry(session_uuid).control_session.suspend(session_name=session_name)
+            return self.session_registry(session_uuid).control_session.suspend(session_name=session_name, **sessionopts)
         except x2go_exceptions.X2goControlSessionException:
             profile_name = self.get_session_profile_name(session_uuid)
             self.HOOK_on_control_session_death(profile_name)
             self.disconnect_profile(profile_name)
     __suspend_session = suspend_session
 
-    def terminate_session(self, session_uuid, session_name=None):
+    def terminate_session(self, session_uuid, session_name=None, **sessionopts):
         """\
         Terminate an X2Go session.
 
         Normally you will use this method to terminate a registered session that you 
         have formerly started/resumed from within your recent
-        L{X2goClient} instance. For this you simply call this method 
-        using the sessions C{session_uuid}, leave the C{session_name}
+        L{X2goClient} instance. For this you simply call this method
+        using the session's C{session_uuid}, leave the C{session_name}
         empty.
 
         Alternatively, you can terminate a non-associated X2Go session:
@@ -1569,6 +1606,8 @@ class X2goClient(object):
         @type session_uuid: C{str}
         @param session_name: the server-side name of an X2Go session
         @type session_name: C{str}
+        @param sessionopts: pass-through of options directly to the session instance's L{X2goSession.terminate()} method
+        @type sessionopts: C{dict}
 
         @return: returns True if this method has been successful
         @rtype: C{bool}
@@ -1576,12 +1615,16 @@ class X2goClient(object):
         """
         try:
             if session_name is None:
-                return self.session_registry(session_uuid).terminate()
+
+                # make sure that the current list of shared folders is up-to-date before the session terminates
+                self.get_shared_folders(session_uuid, check_list_mounts=True)
+
+                return self.session_registry(session_uuid).terminate(**sessionopts)
             else:
                 for session in self.session_registry.running_sessions() + self.session_registry.suspended_sessions():
                     if session_name == session.get_session_name():
                         return session.terminate()
-            return self.session_registry(session_uuid).control_session.terminate(session_name=session_name)
+            return self.session_registry(session_uuid).control_session.terminate(session_name=session_name, **sessionopts)
         except x2go_exceptions.X2goControlSessionException:
             profile_name = self.get_session_profile_name(session_uuid)
             self.HOOK_on_control_session_death(profile_name)
@@ -1890,6 +1933,13 @@ class X2goClient(object):
         Get a list of local folders mounted within X2Go session with session hash <session_uuid>
         from this client.
 
+        @param session_uuid: the X2Go session's UUID registry hash
+        @type session_uuid: C{str}
+        @param profile_name: alternatively, the profile name can be used to get mounted folders of a session connected profile
+        @type profile_name: C{str}
+        @param check_list_mounts: query the server-side mount list for up-to-date information
+        @type check_list_mounts: C{bool}
+
         @return: returns a C{list} of those local folder names that are mounted within X2Go session <session_uuid>.
         @rtype: C{list}
 
@@ -2406,7 +2456,6 @@ class X2goClient(object):
         """
         _destroy_terminals = not ( self.auto_update_sessionregistry == True)
         session = self.session_registry(session_uuid)
-        profile_name = session.get_profile_name()
         session.clean_sessions(destroy_terminals=_destroy_terminals, published_applications=published_applications)
     __clean_sessions = clean_sessions
 
@@ -2682,7 +2731,7 @@ class X2goClient(object):
     ### Session profile oriented methods
     ### 
 
-    def get_profile_config(self, profile_id_or_name):
+    def get_profile_config(self, profile_id_or_name, parameter=None):
         """\
         Returns a dictionary with session options and values that represent
         the session profile for C{profile_id_or_name}.
@@ -2690,15 +2739,38 @@ class X2goClient(object):
         @param profile_id_or_name: name or id of an X2Go session profile as found
             in the sessions configuration file
         @type profile_id_or_name: C{str}
+        @param parameter: if specified, only the value for the given parameter is returned
+        @type parameter: C{str}
 
         @return: a Python dictionary with session profile options
-        @rtype: C{dict}
+        @rtype: C{dict} or C{bool}, C{int}, C{str}
 
         """
-        return self.session_profiles.get_profile_config(profile_id_or_name)
+        return self.session_profiles.get_profile_config(profile_id_or_name, parameter=parameter)
     __get_profile_config = get_profile_config
     with_profile_config = get_profile_config
 
+    def set_profile_config(self, profile_id_or_name, parameter, value):
+        """\
+        Set individual session profile parameters for session profile C{profile_id_or_name}.
+
+        @param profile_id_or_name: name or id of an X2Go session profile as found
+            in the sessions configuration file
+        @type profile_id_or_name: C{str}
+        @param parameter: set this parameter with the given C{value}
+        @type parameter: C{str}
+        @param value: set this value for the given C{parameter}
+        @type value: C{bool}, C{int}, C{str}, C{list} or C{dict}
+
+        @return: returns C{True} if this operation has been successful
+        @rtype: C{dict}
+
+        """
+        self.session_profiles.update_value(profile_id_or_name, parameter, value)
+        self.session_profiles.write_user_config = True
+        self.session_profiles.write()
+    __set_profile_config = set_profile_config
+
     def to_profile_id(self, profile_name):
         """\
         Retrieve the session profile ID of the session whose profile name
@@ -2926,7 +2998,7 @@ class X2goClient(object):
 
     __update_cache_all_profiles = update_cache_all_profiles
 
-    def register_available_server_sessions_by_profile_name(self, profile_name, re_register=False):
+    def register_available_server_sessions_by_profile_name(self, profile_name, re_register=False, skip_pubapp_sessions=False):
         """\
         Register available sessions that are found on the X2Go server the profile
         of name C{profile_name} is connected to.
@@ -2935,6 +3007,8 @@ class X2goClient(object):
         @type profile_name: C{str}
         @param re_register: re-register available sessions, needs to be done after session profile changes
         @type re_register: C{bool}
+        @param skip_pubapp_sessions: Do not auto-register published applications sessions.
+        @type skip_pubapp_sessions: C{bool}
 
         """
         if profile_name not in self.client_connected_profiles(return_profile_names=True):
@@ -2944,33 +3018,39 @@ class X2goClient(object):
                                           register_sessions=False,
                                          )
         try:
-            self.session_registry.register_available_server_sessions(profile_name, session_list=session_list, re_register=re_register)
-        except x2go_exceptions.X2goControlSessionException:
+            self.session_registry.register_available_server_sessions(profile_name, session_list=session_list, re_register=re_register, skip_pubapp_sessions=skip_pubapp_sessions)
+        except x2go_exceptions.X2goControlSessionException, e:
             self.HOOK_on_control_session_death(profile_name)
             self.disconnect_profile(profile_name)
+            raise e
     __register_available_server_sessions_by_profile_name = register_available_server_sessions_by_profile_name
 
-    def register_available_server_sessions_by_session_uuid(self, session_uuid):
+    def register_available_server_sessions_by_session_uuid(self, session_uuid, skip_pubapp_sessions=False):
         """\
         Register available sessions that are found on the X2Go server that the L{X2goSession} instance 
         with session identifier <session_uuid> is connected to.
 
         @param session_uuid: the X2Go session's UUID registry hash
         @type session_uuid: C{str}
+        @param skip_pubapp_sessions: Do not auto-register published applications sessions.
+        @type skip_pubapp_sessions: C{bool}
 
         """
         profile_name = self.get_session_profile_name(session_uuid)
-        self.__register_available_server_sessions_by_profile_name(profile_name)
+        self.__register_available_server_sessions_by_profile_name(profile_name, skip_pubapp_sessions=skip_pubapp_sessions)
     __register_available_server_sessions_by_session_uuid = register_available_server_sessions_by_session_uuid
 
-    def register_available_server_sessions_all_profiles(self):
+    def register_available_server_sessions_all_profiles(self, skip_pubapp_sessions=False):
         """\
         Register all available sessions found on an X2Go server for each session profile.
 
+        @param skip_pubapp_sessions: Do not auto-register published applications sessions.
+        @type skip_pubapp_sessions: C{bool}
+
         """
         for profile_name in self.client_connected_profiles(return_profile_names=True):
             try:
-                self.__register_available_server_sessions_by_profile_name(profile_name)
+                self.__register_available_server_sessions_by_profile_name(profile_name, skip_pubapp_sessions=skip_pubapp_sessions)
             except x2go_exceptions.X2goSessionRegistryException:
                 pass
     __register_available_server_sessions_all_profiles = register_available_server_sessions_all_profiles
diff --git a/x2go/defaults.py b/x2go/defaults.py
index a9562bf..15ea901 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -179,33 +179,33 @@ X2GO_CLIENTSETTINGS_DEFAULTS = {
         'server': 'localhost',
         'port1': 0,
         'port2': 0,
-        },
+    },
     'General': {
         # clientport is not needed for Python X2go
         'clientport': 22, 
         'autoresume': True,
-        },
+    },
     'Authorization': {
         'newprofile': True,
         'suspend': True,
         'editprofile': True,
         'resume': True
-        },
+    },
     'trayicon': {
         'enabled': True,
         'mintotray': True,
         'noclose': True,
         'mincon': True,
         'maxdiscon': True,
-        },
-    }
+    },
+}
 X2GO_CLIENTPRINTING_DEFAULTS = {
     'General': {
         # showdialog will result in a print action that allows opening a print dialog box
         'showdialog': False,
         # if true, open a PDF viewer (or save as PDF file). If false, print via CUPS or print command
         'pdfview': True,
-        },
+    },
     'print': {
         # If false, print via CUPS. If true, run "command" to process the print job
         'startcmd': False,
@@ -215,7 +215,7 @@ X2GO_CLIENTPRINTING_DEFAULTS = {
         'stdin': False,
         # ignored in Python X2go
         'ps': False,
-        },
+    },
     'save': {
         # a path relative to the user's home directory
         'folder': 'PDF',
@@ -227,13 +227,13 @@ X2GO_CLIENTPRINTING_DEFAULTS = {
         'open': True,
         # command to execute as PDF viewer
         'command': 'xpdf',
-        },
+    },
     'CUPS': {
         # default print queue for CUPS, if print queue does not exist, the default 
         # CUPS queue is detected
         'defaultprinter': 'PDF',
-        },
-    }
+    },
+}
 if X2GOCLIENT_OS == 'Windows':
     X2GO_CLIENTPRINTING_DEFAULTS['print'].update({'gsprint': os.path.join(os.environ['ProgramFiles'], 'GhostGum', 'gsview', 'gsprint.exe'), })
 
@@ -250,7 +250,7 @@ if X2GOCLIENT_OS == 'Windows':
             'test_installed': os.path.join(os.environ['SystemDrive'], '\\', 'cygwin', 'bin', 'XWin.exe'),
             'run_command': os.path.join(os.environ['SystemDrive'], '\\', 'cygwin', 'bin', 'XWin.exe'),
             'parameters': [':40', '-clipboard', '-multiwindow', '-notrayicon', '-nowinkill', '-nounixkill', '-swcursor', ],
-            },
+        },
         'VcXsrv': {
             'display': 'localhost:40',
             'last_display': 'localhost:40',
@@ -258,7 +258,7 @@ if X2GOCLIENT_OS == 'Windows':
             'test_installed': os.path.join(os.environ['ProgramFiles'], 'VcXsrv', 'vcxsrv.exe'), 
             'run_command': os.path.join(os.environ['ProgramFiles'], 'VcXsrv', 'vcxsrv.exe'),
             'parameters': [':40', '-clipboard', '-multiwindow', '-notrayicon', '-nowinkill', '-nounixkill', '-swcursor', ],
-            },
+        },
         'VcXsrv_shipped': {
             'display': 'localhost:40',
             'last_display': 'localhost:40',
@@ -266,7 +266,7 @@ if X2GOCLIENT_OS == 'Windows':
             'test_installed': os.path.join(os.getcwd(), 'VcXsrv', 'vcxsrv.exe'), 
             'run_command': os.path.join(os.getcwd(), 'VcXsrv', 'vcxsrv.exe'),
             'parameters': [':40', '-clipboard', '-multiwindow', '-notrayicon', '-nowinkill', '-nounixkill', '-swcursor', ],
-            },
+        },
         'VcXsrv_development': {
             'display': 'localhost:40',
             'last_display': 'localhost:40',
@@ -274,7 +274,7 @@ if X2GOCLIENT_OS == 'Windows':
             'test_installed': os.path.join(os.getcwd(), '..', 'pyhoca-contrib', 'mswin', 'vcxsrv-mswin', 'VcXsrv-1.12.0.1', 'vcxsrv.exe'), 
             'run_command': os.path.join(os.getcwd(), '..', 'pyhoca-contrib', 'mswin', 'vcxsrv-mswin', 'VcXsrv-1.12.0.1', 'vcxsrv.exe'), 
             'parameters': [':40', '-clipboard', '-multiwindow', '-notrayicon', '-nowinkill', '-nounixkill', '-swcursor', ],
-            },
+        },
         'Xming': {
             'display': 'localhost:40',
             'last_display': 'localhost:40',
@@ -282,8 +282,8 @@ if X2GOCLIENT_OS == 'Windows':
             'test_installed': os.path.join(os.environ['ProgramFiles'], 'Xming', 'Xming.exe'), 
             'run_command': os.path.join(os.environ['ProgramFiles'], 'Xming', 'Xming.exe'),
             'parameters': [':40', '-clipboard', '-multiwindow', '-notrayicon', '-nowinkill', '-nounixkill', '-swcursor', ],
-            },
-        }
+        },
+    }
 else:
     # make the variable available when building API documentation with epydoc
     X2GO_CLIENTXCONFIG_DEFAULTS = {}
@@ -292,24 +292,25 @@ X2GO_GENERIC_APPLICATIONS = [ 'WWWBROWSER', 'MAILCLIENT', 'OFFICE', 'TERMINAL',
 """X2go's generic applications."""
 
 X2GO_SESSIONPROFILE_DEFAULTS = {
-    'autologin': False, 'autostart': False, 'setsessiontitle': False, 'sessiontitle': "",
+    'autologin': True, 'autoconnect': False, 'autostart': False, 'setsessiontitle': False, 'sessiontitle': "",
     'speed': 2, 'pack': '16m-jpeg', 'quality': 9,
     'iconvto': 'UTF-8', 'iconvfrom': 'UTF-8', 'useiconv': False,
-    'usesshproxy': False, 'sshproxyhost': '', 'sshproxyuser': '', 'sshproxytunnel': '', 'sshproxykeyfile': '',
-    'useexports': True, 'fstunnel': True, 'export': '',
+    'usesshproxy': False, 'sshproxyhost': 'proxyhost.mydomain', 'sshproxyport': 22, 'sshproxyuser': '', 'sshproxytunnel': 'localhost:44444:server.mydomain.private:22', 'sshproxykeyfile': '',
+    'sshproxytype': 'SSH', 'sshproxysameuser': False, 'sshproxysamepass': False, 'sshproxyautologin': True,
+    'useexports': True, 'restoreexports': False, 'fstunnel': True, 'export': '',
     'usemimebox': False, 'mimeboxextensions': '', 'mimeboxaction': 'OPEN',
     'fullscreen': False,
-    'width': 800,'height': 600,'dpi': 96,'setdpi': False, 'xinerama': False, 'multidisp': False,
+    'width': 800,'height': 600, 'maxdim': False, 'dpi': 96, 'setdpi': False, 'xinerama': False, 'multidisp': False,
     'usekbd': True, 'layout': 'us', 'type': 'pc105/us', 'variant': '',
     'sound': False, 'soundsystem': 'pulse', 'startsoundsystem': False, 'soundtunnel':True, 'defsndport':True, 'sndport':4713,
     'name': 'NEW_PROFILE', 'icon': ':icons/128x128/x2gosession.png',
-    'host': '', 'user': CURRENT_LOCAL_USER, 'key': '', 'sshport': 22, 'krblogin': False,
+    'host': 'server.mydomain', 'user': CURRENT_LOCAL_USER, 'key': '', 'sshport': 22, 'krblogin': False, 'forwardsshagent': False,
     'rootless': True, 'applications': X2GO_GENERIC_APPLICATIONS, 'command':'TERMINAL', 'published': False,
     'directrdp': False, 'directrdpsettings': '', 'rdpclient': 'rdesktop', 'rdpport': 3389,
     'rdpoptions': '-u X2GO_USER -p X2GO_PASSWORD', 'rdpserver': '',
     'print': False,
     'xdmcpserver': 'localhost',
-    }
+}
 """L{X2goSessionProfiles} default values to fill a new session profile with."""
 ##
 ## X2Go Proxy defaults
@@ -375,7 +376,7 @@ pack_methods_nx3.sort()
 X2GO_DESKTOPSESSIONS={
     'KDE': 'startkde',
     'GNOME': 'gnome-session',
-    'XFCE4': 'xfce4-session',
+    'XFCE': 'xfce4-session',
     'LXDE': 'startlxde',
     'TRINITY': 'starttrinity',
     'UNITY': 'unity',
diff --git a/x2go/forward.py b/x2go/forward.py
index 5cb2c00..f626efc 100644
--- a/x2go/forward.py
+++ b/x2go/forward.py
@@ -45,7 +45,7 @@ class X2goFwServer(StreamServer):
     through an external proxy command launched by a C{X2goProxy*} backend.
 
     """
-    def __init__ (self, listener, remote_host, remote_port, ssh_transport, session_instance=None, logger=None, loglevel=log.loglevel_DEFAULT,):
+    def __init__ (self, listener, remote_host, remote_port, ssh_transport, session_instance=None, session_name=None, logger=None, loglevel=log.loglevel_DEFAULT,):
         """\
         @param listener: listen on TCP/IP socket C{(<IP>, <Port>)}
         @type listener: C{tuple}
@@ -55,6 +55,11 @@ class X2goFwServer(StreamServer):
         @type remote_port: C{int}
         @param ssh_transport: a valid Paramiko/SSH transport object
         @type ssh_transport: C{obj}
+        @param session_instance: the complete L{X2goSession} instance of the X2Go session this port forwarding server belongs to.
+            Note: for new L{X2goSession} instances the object has the session name not yet set(!!!)
+        @type session_instance: C{obj}
+        @param session_name: the session name of the X2Go session this port forwarding server belongs to
+        @type session_name: C{str}
         @param logger: you can pass an L{X2goLogger} object to the
             L{X2goFwServer} constructor
         @type logger: C{obj}
@@ -76,6 +81,7 @@ class X2goFwServer(StreamServer):
         self.chain_host = remote_host
         self.chain_port = remote_port
         self.ssh_transport = ssh_transport
+        self.session_name = session_name
         self.session_instance = session_instance
 
         self.fw_socket = None
@@ -130,6 +136,7 @@ class X2goFwServer(StreamServer):
                                                                                 _count),
                                                                                 loglevel=log.loglevel_ERROR)
             if self.session_instance:
+                self.session_instance.set_session_name(self.session_name)
                 self.session_instance.HOOK_forwarding_tunnel_setup_failed(chain_host=self.chain_host, chain_port=self.chain_port)
             self.failed = True
 
@@ -216,6 +223,7 @@ def start_forward_tunnel(local_host='127.0.0.1', local_port=22022,
                          remote_host='127.0.0.1', remote_port=22,
                          ssh_transport=None, 
                          session_instance=None,
+                         session_name=None,
                          logger=None, ):
     """\
     Setup up a Paramiko/SSH port forwarding tunnel (like openssh -L option).
@@ -230,10 +238,12 @@ def start_forward_tunnel(local_host='127.0.0.1', local_port=22022,
     @type remote_host: C{str}
     @param remote_port: ... on port C{<remote_port>}
     @type remote_port: C{int}
-    @param ssh_transport: the Paramiko/SSH transport (i.e. the X2Go sessions Paramiko/SSH transport object)
+    @param ssh_transport: the Paramiko/SSH transport (i.e. the X2Go session's Paramiko/SSH transport object)
     @type ssh_transport: C{obj}
     @param session_instance: the L{X2goSession} instance that initiates this tunnel
     @type session_instance: C{obj}
+    @param session_name: the session name of the X2Go session this port forwarding server belongs to
+    @type session_name: C{str}
     @param logger: an X2goLogger object
     @type logger: C{obj}
 
@@ -243,7 +253,8 @@ def start_forward_tunnel(local_host='127.0.0.1', local_port=22022,
     """
     fw_server = X2goFwServer(listener=(local_host, local_port),
                              remote_host=remote_host, remote_port=remote_port,
-                             ssh_transport=ssh_transport, session_instance=session_instance,
+                             ssh_transport=ssh_transport,
+                             session_instance=session_instance, session_name=session_name,
                              logger=logger,
                             )
     try:
diff --git a/x2go/guardian.py b/x2go/guardian.py
index ba5d5b2..5e035ff 100644
--- a/x2go/guardian.py
+++ b/x2go/guardian.py
@@ -50,6 +50,7 @@ class X2goSessionGuardian(threading.Thread):
                  auto_update_listmounts_cache=False, 
                  auto_update_sessionregistry=False,
                  auto_register_sessions=False,
+                 no_auto_reg_pubapp_sessions=False,
                  refresh_interval=5,
                  logger=None, loglevel=log.loglevel_DEFAULT):
         """\
@@ -64,6 +65,8 @@ class X2goSessionGuardian(threading.Thread):
         @param auto_register_sessions: register new sessions automatically once they appear in the X2Go session (e.g. 
             instantiated by another client that is connected to the same X2Go server under same user ID)
         @type auto_register_sessions: C{bool}
+        @param no_auto_reg_pubapp_sessions: do not auto-register published applications sessions
+        @type no_auto_reg_pubapp_sessions: C{bool}
         @param refresh_interval: refresh cache and session registry every <refresh_interval> seconds
         @type refresh_interval: C{int}
         @param logger: you can pass an L{X2goLogger} object to the L{X2goSessionGuardian} constructor
@@ -85,6 +88,7 @@ class X2goSessionGuardian(threading.Thread):
         self.auto_update_listmounts_cache = auto_update_listmounts_cache
         self.auto_update_sessionregistry = auto_update_sessionregistry
         self.auto_register_sessions = auto_register_sessions
+        self.no_auto_reg_pubapp_sessions = no_auto_reg_pubapp_sessions
         self.refresh_interval = refresh_interval
 
         threading.Thread.__init__(self, target=self.guardian)
@@ -117,7 +121,7 @@ class X2goSessionGuardian(threading.Thread):
 
                 # session auto-registration will automatically trigger an update of the session registry status
                 if self.auto_register_sessions:
-                    self.client_instance.register_available_server_sessions_all_profiles()
+                    self.client_instance.register_available_server_sessions_all_profiles(skip_pubapp_sessions=self.no_auto_reg_pubapp_sessions)
 
         self.logger('X2Go session guardian thread waking up after %s seconds' % seconds, loglevel=log.loglevel_DEBUG)
 
diff --git a/x2go/inifiles.py b/x2go/inifiles.py
index 0b843b2..996cf83 100644
--- a/x2go/inifiles.py
+++ b/x2go/inifiles.py
@@ -134,7 +134,8 @@ class X2goIniFile(object):
         result = 'X2goIniFile('
         for p in dir(self):
             if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
-            result += p + '=' + str(self.__dict__[p])
+            result += p + '=' + str(self.__dict__[p]) + ','
+        result = result.strip(',')
         return result + ')'
 
     def _storeValue(self, section, key, value):
diff --git a/x2go/mimeboxactions.py b/x2go/mimeboxactions.py
index 5a2f0f4..a2749f4 100644
--- a/x2go/mimeboxactions.py
+++ b/x2go/mimeboxactions.py
@@ -100,7 +100,7 @@ class X2goMIMEboxAction(object):
 
         @param mimebox_file: file name as placed in to the X2Go MIME box directory
         @type mimebox_file: C{str}
-        @param mimebox_dir: location of the X2Go sessions's MIME box directory
+        @param mimebox_dir: location of the X2Go session's MIME box directory
         @type mimebox_dir: C{str}
 
         """
@@ -113,7 +113,7 @@ class X2goMIMEboxAction(object):
 
         @param mimebox_file: file name as placed in to the X2Go MIME box directory
         @type mimebox_file: C{str}
-        @param mimebox_dir: location of the X2Go sessions's MIME box directory
+        @param mimebox_dir: location of the X2Go session's MIME box directory
         @type mimebox_dir: C{str}
 
         """
diff --git a/x2go/registry.py b/x2go/registry.py
index 85ddf3f..5b4d67f 100644
--- a/x2go/registry.py
+++ b/x2go/registry.py
@@ -27,9 +27,12 @@ import os
 import copy
 import types
 import time
+import threading
+import re
 
 # Python X2Go modules
 import log
+import utils
 import session
 import x2go_exceptions
 
@@ -45,6 +48,7 @@ from x2go.backends.printing import X2goClientPrinting as _X2goClientPrinting
 from defaults import LOCAL_HOME as _LOCAL_HOME
 from defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
 from defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR
+from defaults import X2GO_SESSIONPROFILE_DEFAULTS as _X2GO_SESSIONPROFILE_DEFAULTS
 from defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR
 
 class X2goSessionRegistry(object):
@@ -79,6 +83,7 @@ class X2goSessionRegistry(object):
 
         self._last_available_session_registration = None
         self._skip_auto_registration = False
+        self._profile_locks = {}
 
     def keys(self):
         """\
@@ -94,7 +99,8 @@ class X2goSessionRegistry(object):
         result = 'X2goSessionRegistry('
         for p in dir(self):
             if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
-            result += p + '=' + str(self.__dict__[p])
+            result += p + '=' + str(self.__dict__[p]) + ','
+        result = result.strip(',')
         return result + ')'
 
     def __call__(self, session_uuid):
@@ -267,7 +273,8 @@ class X2goSessionRegistry(object):
 
             if not self(_session_uuid).update_status(session_list=session_list, force_update=force_update):
                 # skip this run, as nothing has changed since the last time...
-                return False
+                continue
+
             _last_status = copy.deepcopy(self(_session_uuid)._last_status)
             _current_status = copy.deepcopy(self(_session_uuid)._current_status)
 
@@ -316,8 +323,8 @@ class X2goSessionRegistry(object):
                         if _last_status['suspended']:
                             # from a suspended state
                             self.client_instance.HOOK_on_session_has_resumed_by_other(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
-                        elif _last_status['virgin']:
-                            # as a new session
+                        elif _last_status['connected'] and _last_status['virgin']:
+                            # as a new session, do not report directly after connect due to many false positives then...
                             self.client_instance.HOOK_on_session_has_started_by_other(session_uuid=_session_uuid, profile_name=_profile_name, session_name=_session_name)
 
             elif _last_status['connected'] and (not _last_status['suspended'] and _current_status['suspended']) and not _current_status['faulty'] and _session_name:
@@ -325,6 +332,11 @@ class X2goSessionRegistry(object):
                 # unregister as master session
                 if _profile_name in self.master_sessions.keys():
                     if self.master_sessions[_profile_name] == self(_session_uuid):
+
+                        # save exported folders to session profile config if requested by session profile parameter ,,restoreexports''...
+                        if self.client_instance and self(_session_uuid).restore_shared_local_folders:
+                            self.client_instance.set_profile_config(_profile_name, 'export', self(_session_uuid)._restore_exported_folders)
+
                         self(_session_uuid).unset_master_session()
                         del self.master_sessions[_profile_name]
 
@@ -337,6 +349,11 @@ class X2goSessionRegistry(object):
                 # unregister as master session
                 if _profile_name in self.master_sessions.keys():
                     if self.master_sessions[_profile_name] == self(_session_uuid):
+
+                        # save exported folders to session profile config if requested by session profile parameter ,,restoreexports''...
+                        if self.client_instance and self(_session_uuid).restore_shared_local_folders:
+                            self.client_instance.set_profile_config(_profile_name, 'export', self(_session_uuid)._restore_exported_folders)
+
                         self(_session_uuid).unset_master_session()
                         del self.master_sessions[_profile_name]
 
@@ -369,7 +386,7 @@ class X2goSessionRegistry(object):
 
         return True
 
-    def register_available_server_sessions(self, profile_name, session_list=None, newly_connected=False, re_register=False):
+    def register_available_server_sessions(self, profile_name, session_list=None, newly_connected=False, re_register=False, skip_pubapp_sessions=False):
         """\
         Register server-side available X2Go sessions with this L{X2goSessionRegistry} instance for a given profile name.
 
@@ -382,6 +399,8 @@ class X2goSessionRegistry(object):
         @type newly_connected: C{bool}
         @param re_register: re-register available sessions, needs to be done after changes to the session profile
         @type re_register: C{bool}
+        @param skip_pubapp_sessions: Do not register published applications sessions
+        @type skip_pubapp_sessions: C{bool}
 
         """
         if self._last_available_session_registration is not None:
@@ -438,12 +457,13 @@ class X2goSessionRegistry(object):
 
                     # this if clause catches problems when x2golistsessions commands give weird results
                     if not self.has_session_of_session_name(session_name) or re_register:
-                        session_uuid = self.register(server, profile_id, profile_name,
-                                                     session_name=session_name, virgin=False,
-                                                     **kwargs
-                                                    )
-                        self(session_uuid).connected = True
-                        self.update_status(session_uuid=session_uuid, force_update=True, newly_connected=newly_connected)
+                        if not (skip_pubapp_sessions and re.match('.*_stRPUBLISHED_.*', session_name)):
+                            session_uuid = self.register(server, profile_id, profile_name,
+                                                         session_name=session_name, virgin=False,
+                                                         **kwargs
+                                                        )
+                            self(session_uuid).connected = True
+                            self.update_status(session_uuid=session_uuid, force_update=True, newly_connected=newly_connected)
 
     def register(self, server, profile_id, profile_name,
                  session_name=None,
@@ -505,40 +525,52 @@ class X2goSessionRegistry(object):
         @rtype: C{str}
 
         """
+        if profile_id not in self._profile_locks.keys():
+            self._profile_locks[profile_id] = threading.Lock()
+
+        self._profile_locks[profile_id].acquire()
+
         control_session = None
         if profile_id in self.control_sessions.keys():
             control_session = self.control_sessions[profile_id]
 
+        try:
+            _params = self.client_instance.session_profiles.to_session_params(profile_id)
+
+        except x2go_exceptions.X2goProfileException:
+            _params = utils._convert_SessionProfileOptions_2_SessionParams(_X2GO_SESSIONPROFILE_DEFAULTS)
+
+        for _k in _params.keys():
+            if _k in kwargs.keys():
+                _params[_k] = kwargs[_k]
+
         # when starting a new session, we will try to use unused registered virgin sessions
         # depending on your application layout, there should either be one or no such virgin session at all
         _virgin_sessions = [ s for s in self.virgin_sessions_of_profile_name(profile_name, return_objects=True) if not s.activated ]
         if _virgin_sessions and not session_name:
-
-            _virgin_sessions[0].activated = True
             session_uuid = _virgin_sessions[0].get_uuid()
-            _params = self.client_instance.session_profiles.to_session_params(profile_id)
-            for _k in _params.keys():
-                if _k in kwargs.keys():
-                    _params[_k] = kwargs[_k]
-            self(session_uuid).update_params(_params)
-            self(session_uuid).set_server(server)
-            self(session_uuid).set_profile_name(profile_name)
+            self(session_uuid).activated = True
             self.logger('using already initially-registered yet-unused session %s' % session_uuid, loglevel=log.loglevel_NOTICE)
-            return session_uuid
 
-        session_uuid = self.get_session_of_session_name(session_name)
+        else:
+            session_uuid = self.get_session_of_session_name(session_name)
+            if session_uuid is not None: self.logger('using already registered-by-session-name session %s' % session_uuid, loglevel=log.loglevel_NOTICE)
+
         if session_uuid is not None:
             self(session_uuid).activated = True
-            _params = self.client_instance.session_profiles.to_session_params(profile_id)
-            for _k in _params.keys():
-                if _k in kwargs.keys():
-                    _params[_k] = kwargs[_k]
             self(session_uuid).update_params(_params)
             self(session_uuid).set_server(server)
             self(session_uuid).set_profile_name(profile_name)
-            self.logger('using already registered-by-session-name session %s' % session_uuid, loglevel=log.loglevel_NOTICE)
+            self._profile_locks[profile_id].release()
             return session_uuid
 
+        try: del _params['server'] 
+        except: pass
+        try: del _params['profile_name']
+        except: pass
+        try: del _params['profile_id'] 
+        except: pass
+
         s = session.X2goSession(server=server, control_session=control_session,
                                 profile_id=profile_id, profile_name=profile_name,
                                 session_name=session_name,
@@ -555,7 +587,8 @@ class X2goSessionRegistry(object):
                                 keep_controlsession_alive=keep_controlsession_alive,
                                 add_to_known_hosts=add_to_known_hosts,
                                 known_hosts=known_hosts,
-                                logger=self.logger, **kwargs)
+                                client_instance=self.client_instance,
+                                logger=self.logger, **_params)
 
         session_uuid = s._X2goSession__get_uuid()
         self.logger('registering X2Go session %s...' % profile_name, log.loglevel_NOTICE)
@@ -570,6 +603,7 @@ class X2goSessionRegistry(object):
         if control_session is None:
             self(session_uuid).do_auto_connect()
 
+        self._profile_locks[profile_id].release()
         return session_uuid
 
     def has_session_of_session_name(self, session_name):
diff --git a/x2go/session.py b/x2go/session.py
index c04a391..bca32ec 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -58,6 +58,10 @@ import uuid
 import time
 import gevent
 import re
+import threading
+
+# 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
+import paramiko
 
 # Python X2Go modules
 import defaults
@@ -66,7 +70,6 @@ import utils
 import session
 import x2go_exceptions
 
-
 from x2go.backends.control import X2goControlSession as _X2goControlSession
 from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession
 from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
@@ -83,36 +86,39 @@ from defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR
 
 from defaults import SUPPORTED_SOUND, SUPPORTED_PRINTING, SUPPORTED_FOLDERSHARING, SUPPORTED_MIMEBOX
 
-_X2GO_SESSION_PARAMS = ('use_sshproxy', 'profile_id', 'session_name',
+_X2GO_SESSION_PARAMS = ('use_sshproxy', 'sshproxy_reuse_authinfo',
+                        'profile_id', 'session_name',
                         'auto_start_or_resume', 'auto_connect',
                         'printing', 'allow_mimebox',
                         'mimebox_extensions', 'mimebox_action',
-                        'allow_share_local_folders', 'share_local_folders',
+                        'allow_share_local_folders', 'share_local_folders', 'restore_shared_local_folders',
                         'control_backend', 'terminal_backend', 'info_backend', 'list_backend', 'proxy_backend', 'settings_backend', 'printing_backend',
                         'client_rootdir', 'sessions_rootdir', 'ssh_rootdir',
-                        'keep_controlsession_alive', 'add_to_known_hosts', 'known_hosts'
+                        'keep_controlsession_alive', 'add_to_known_hosts', 'known_hosts', 'forward_sshagent',
                         'connected', 'virgin', 'running', 'suspended', 'terminated', 'faulty'
                         'client_instance',
                        )
 """A list of allowed X2Go pure session parameters (i.e. parameters that are passed on neither to an X2goControlSession, X2goSSHProxy nor an X2goControlSession object."""
 # options of the paramiko.SSHClient().connect() method, any option that is allowed for a terminal session instance
 _X2GO_TERMINAL_PARAMS = ('geometry', 'depth', 'link', 'pack', 'dpi',
-                        'cache_type', 'kbtype', 'kblayout', 'kbvariant',
-                        'session_type', 'snd_system', 'snd_port',
-                        'cmd', 'set_session_title', 'session_title',
-                        'rdp_server', 'rdp_options', 'applications',
-                        'xdmcp_server',
-                        'rootdir', 'loglevel', 'profile_name', 'profile_id',
-                        'print_action', 'print_action_args',
-                        'convert_encoding', 'client_encoding', 'server_encoding',
-                        'proxy_options', 'published_applications', 'published_applications_no_submenus',
-                        'logger',
-                        'control_backend', 'terminal_backend', 'proxy_backend',
-                        'profiles_backend', 'settings_backend', 'printing_backend',
-                       )
+                         'cache_type', 'kbtype', 'kblayout', 'kbvariant',
+                         'session_type', 'snd_system', 'snd_port',
+                         'cmd', 'set_session_title', 'session_title',
+                         'rdp_server', 'rdp_options', 'applications',
+                         'xdmcp_server',
+                         'rootdir', 'loglevel', 'profile_name', 'profile_id',
+                         'print_action', 'print_action_args',
+                         'convert_encoding', 'client_encoding', 'server_encoding',
+                         'proxy_options', 'published_applications', 'published_applications_no_submenus',
+                         'logger',
+                         'control_backend', 'terminal_backend', 'proxy_backend',
+                         'profiles_backend', 'settings_backend', 'printing_backend',
+                        )
 """A list of allowed X2Go terminal session parameters."""
-_X2GO_SSHPROXY_PARAMS = ('sshproxy_host', 'sshproxy_user', 'sshproxy_password',
-                         'sshproxy_key_filename', 'sshproxy_pkey', 'sshproxy_tunnel',
+_X2GO_SSHPROXY_PARAMS = ('sshproxy_host', 'sshproxy_port', 'sshproxy_user', 'sshproxy_password',
+                         'sshproxy_key_filename', 'sshproxy_pkey',
+                         'sshproxy_look_for_keys', 'sshproxy_allow_agent',
+                         'sshproxy_tunnel',
                         )
 """A list of allowed X2Go SSH proxy parameters."""
 
@@ -130,6 +136,7 @@ class X2goSession(object):
     """
     def __init__(self, server=None, port=22, control_session=None,
                  use_sshproxy=False,
+                 sshproxy_reuse_authinfo=False,
                  profile_id=None, profile_name='UNKNOWN',
                  session_name=None,
                  auto_start_or_resume=False,
@@ -140,6 +147,7 @@ class X2goSession(object):
                  mimebox_action='OPEN',
                  allow_share_local_folders=False,
                  share_local_folders=[],
+                 restore_shared_local_folders=False,
                  control_backend=_X2goControlSession,
                  terminal_backend=_X2goTerminalSession,
                  info_backend=_X2goServerSessionInfo,
@@ -153,6 +161,7 @@ class X2goSession(object):
                  keep_controlsession_alive=False,
                  add_to_known_hosts=False,
                  known_hosts=None,
+                 forward_sshagent=False,
                  logger=None, loglevel=log.loglevel_DEFAULT,
                  connected=False, activated=False, virgin=True, running=None, suspended=None, terminated=None, faulty=None,
                  client_instance=None,
@@ -164,6 +173,8 @@ class X2goSession(object):
         @type control_session: C{X2goControlSession*} instance
         @param use_sshproxy: for communication with X2Go server use an SSH proxy host
         @type use_sshproxy: C{bool}
+        @param sshproxy_reuse_authinfo: for proxy authentication re-use the X2Go session's password / key file
+        @type sshproxy_reuse_authinfo: C{bool}
         @param profile_id: profile ID
         @type profile_id: C{str}
         @param profile_name: profile name
@@ -186,6 +197,8 @@ class X2goSession(object):
         @type allow_share_local_folders: C{bool}
         @param share_local_folders: list of local folders to share with the remote X2Go session
         @type share_local_folders: C{list}
+        @param restore_shared_local_folders: store actual list of shared local folders after session has been suspended or terminated
+        @type restore_shared_local_folders: C{bool}
         @param control_backend: X2Go control session backend to use
         @type control_backend: C{class}
         @param terminal_backend: X2Go terminal session backend to use
@@ -212,6 +225,8 @@ class X2goSession(object):
         @type add_to_known_hosts: C{bool}
         @param known_hosts: the underlying Paramiko/SSH systems C{known_hosts} file
         @type known_hosts: C{str}
+        @param forward_sshagent: forward SSH agent authentication requests to the SSH agent on the X2Go client-side
+        @type forward_sshagent: C{bool}
         @param connected: manipulate session state »connected« by giving a pre-set value
         @type connected: C{bool}
         @param activated: normal leave this untouched, an activated session is a session that is about to be used
@@ -266,6 +281,7 @@ class X2goSession(object):
         self.printing = printing
         self.allow_share_local_folders = allow_share_local_folders
         self.share_local_folders = share_local_folders
+        self.restore_shared_local_folders = restore_shared_local_folders
         self.allow_mimebox = allow_mimebox
         self.mimebox_extensions = mimebox_extensions
         self.mimebox_action = mimebox_action
@@ -296,11 +312,14 @@ class X2goSession(object):
             if not re.match('.*_stRPUBLISHED_.*',self.session_name):
                 self.published_applications = params['published_applications'] = False
 
+        self.use_sshproxy = use_sshproxy
+        self.sshproxy_reuse_authinfo = sshproxy_reuse_authinfo
+
         self.control_params = {}
         self.terminal_params = {}
         self.sshproxy_params = {}
         self.update_params(params)
-        self.shared_folders = []
+        self.shared_folders = {}
 
         self.session_environment = {}
         self.server_features = []
@@ -323,7 +342,7 @@ class X2goSession(object):
 
         self.add_to_known_hosts = add_to_known_hosts
         self.known_hosts = known_hosts
-        self.use_sshproxy = use_sshproxy
+        self.forward_sshagent = forward_sshagent
 
         self._current_status = {
             'timestamp': time.time(),
@@ -348,6 +367,65 @@ class X2goSession(object):
         if self.is_connected():
             self.retrieve_server_features()
 
+        self._progress_status = 0
+        self._lock = threading.Lock()
+
+        if self.client_instance and self.restore_shared_local_folders:
+            self._restore_exported_folders = self.client_instance.get_profile_config(self.profile_name, 'export')
+
+    def __str__(self):
+        return self.__get_uuid()
+
+    def __repr__(self):
+        result = 'X2goSession('
+        for p in dir(self):
+            if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
+            result += p + '=' + str(self.__dict__[p]) + ','
+        result = result.strip(',')
+        return result + ')'
+
+    def __call__(self):
+        return self.__get_uuid()
+
+    def __del__(self):
+        """\
+        Class destructor.
+
+        """
+        if self.has_control_session() and self.has_terminal_session():
+            self.get_control_session().dissociate(self.get_terminal_session())
+
+        if self.has_control_session():
+            if self.keep_controlsession_alive:
+                # regenerate this session instance for re-usage if this is the last session for a certain session profile
+                # and keep_controlsession_alive is set to True...
+                self.virgin = True
+                self.activated = False
+                self.connected = self.is_connected()
+                self.running = None
+                self.suspended = None
+                self.terminated = None
+                self._current_status = {
+                    'timestamp': time.time(),
+                    'server': self.server,
+                    'virgin': self.virgin,
+                    'connected': self.connected,
+                    'running': self.running,
+                    'suspended': self.suspended,
+                    'terminated': self.terminated,
+                    'faulty': self.faulty,
+                }
+                self._last_status = None
+                self.session_name = None
+
+            else:
+                self.get_control_session().__del__()
+                self.control_session = None
+
+        if self.has_terminal_session():
+            self.get_terminal_session().__del__()
+            self.terminal_session = None
+
     def get_client_instance(self):
         """\
         Return parent L{X2goClient} instance if avaiable.
@@ -422,7 +500,7 @@ class X2goSession(object):
 
         # get rid of the faulty session...
         try:
-            self.terminate()
+            self._terminate()
         except x2go_exceptions.X2goSessionException:
             pass
 
@@ -498,6 +576,7 @@ class X2goSession(object):
             self.control_session = self.control_backend(profile_name=self.profile_name,
                                                         add_to_known_hosts=self.add_to_known_hosts,
                                                         known_hosts=self.known_hosts,
+                                                        forward_sshagent=self.forward_sshagent,
                                                         terminal_backend=self.terminal_backend,
                                                         info_backend=self.info_backend,
                                                         list_backend=self.list_backend,
@@ -544,6 +623,11 @@ class X2goSession(object):
         self.logger('Using session %s as master session for profile %s.' % (self.get_session_name(), self.get_profile_name()), loglevel=log.loglevel_NOTICE)
         self.master_session = True
 
+        # retrieve an up-to-date list of sharable local folders from the client instance
+        if self.client_instance:
+            _exports = self.client_instance.get_profile_config(self.profile_name, 'export')
+            self.share_local_folders = [ sf for sf in _exports.keys() if _exports[sf] ]
+
         i = 0
         while i < max_wait:
             i += 1
@@ -552,9 +636,9 @@ class X2goSession(object):
             gevent.sleep(1)
 
         if wait:
-            gevent.spawn_later(wait, self.share_all_local_folders)
+            gevent.spawn_later(wait, self.share_all_local_folders, update_exported_folders=False)
         else:
-            gevent.spawn(self.share_all_local_folders)
+            gevent.spawn(self.share_all_local_folders, update_exported_folders=False)
     __set_master_session = set_master_session
 
     def unset_master_session(self):
@@ -564,7 +648,7 @@ class X2goSession(object):
         """
         # unmount shared folders
         if self.has_terminal_session():
-            self.unshare_all_local_folders()
+            self.unshare_all_local_folders(update_exported_folders=False)
         self.master_session = False
     __unset_master_session = unset_master_session
 
@@ -621,58 +705,6 @@ class X2goSession(object):
             raise x2go_exceptions.X2goProfileException('Unknown session profile option: %s.' % option)
     __get_session_profile_option = get_session_profile_option
 
-    def __str__(self):
-        return self.__get_uuid()
-
-    def __repr__(self):
-        result = 'X2goSession('
-        for p in dir(self):
-            if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
-            result += p + '=' + str(self.__dict__[p]) + ', '
-        return result + ')'
-
-    def __call__(self):
-        return self.__get_uuid()
-
-    def __del__(self):
-        """\
-        Class destructor.
-
-        """
-        if self.has_control_session() and self.has_terminal_session():
-            self.get_control_session().dissociate(self.get_terminal_session())
-
-        if self.has_control_session():
-            if self.keep_controlsession_alive:
-                # regenerate this session instance for re-usage if this is the last session for a certain session profile
-                # and keep_controlsession_alive is set to True...
-                self.virgin = True
-                self.is_activated = False
-                self.connected = self.is_connected()
-                self.running = None
-                self.suspended = None
-                self.terminated = None
-                self._current_status = {
-                    'timestamp': time.time(),
-                    'server': self.server,
-                    'virgin': self.virgin,
-                    'connected': self.connected,
-                    'running': self.running,
-                    'suspended': self.suspended,
-                    'terminated': self.terminated,
-                    'faulty': self.faulty,
-                }
-                self._last_status = None
-                self.session_name = None
-
-            else:
-                self.get_control_session().__del__()
-                self.control_session = None
-
-        if self.has_terminal_session():
-            self.get_terminal_session().__del__()
-            self.terminal_session = None
-
     def update_params(self, params):
         """\
         This method can be used to modify L{X2goSession} parameters after the
@@ -701,6 +733,10 @@ class X2goSession(object):
             del params['share_local_folders'] 
         except KeyError: pass
         try:
+            self.restore_shared_local_folders = params['restore_shared_local_folders']
+            del params['restore_shared_local_folders']
+        except KeyError: pass
+        try:
             self.allow_mimebox = params['allow_mimebox']
             del params['allow_mimebox']
         except KeyError: pass
@@ -717,14 +753,30 @@ class X2goSession(object):
             del params['use_sshproxy']
         except KeyError: pass
         try:
+            self.sshproxy_reuse_authinfo = params['sshproxy_reuse_authinfo']
+            del params['sshproxy_reuse_authinfo']
+        except KeyError: pass
+        try:
             self.auto_connect = params['auto_connect']
             del params['auto_connect']
         except KeyError: pass
         try:
+            self.forward_sshagent = params['forward_sshagent']
+            del params['forward_sshagent']
+        except KeyError: pass
+        try:
             self.auto_start_or_resume = params['auto_start_or_resume']
             del params['auto_start_or_resume']
         except KeyError: pass
 
+        if self.sshproxy_reuse_authinfo:
+            if params.has_key('key_filename'):
+                params['sshproxy_key_filename'] = params['key_filename']
+            if params.has_key('pkey'):
+                params['sshproxy_pkey'] = params['pkey']
+            if params.has_key('password'):
+                params['sshproxy_password'] = params['password']
+
         _terminal_params = copy.deepcopy(params)
         _control_params = copy.deepcopy(params)
         _sshproxy_params = copy.deepcopy(params)
@@ -757,7 +809,7 @@ class X2goSession(object):
     def get_username(self):
         """\
         After a session has been set up you can query the
-        username the sessions runs as.
+        username the session runs as.
 
         @return: the remote username the X2Go session runs as
         @rtype: C{str}
@@ -853,6 +905,17 @@ class X2goSession(object):
         return self.session_name
     __get_session_name = get_session_name
 
+    def set_session_name(self, session_name):
+        """\
+        Manipulate the L{X2GoSession}'s session name.
+
+        @param session_name: the new session name to be set
+        @type session_name: C{str}
+
+        """
+        self.session_name = session_name
+    __set_session_name = set_session_name
+
     def get_session_info(self):
         """\
         Retrieve the server-side X2Go session info object for this session.
@@ -992,6 +1055,18 @@ class X2goSession(object):
         return self.use_sshproxy
     __uses_sshproxy = uses_sshproxy
 
+    def reuses_sshproxy_authinfo(self):
+        """\
+        Check if a session is configured to re-use the X2Go session's password / key for
+        proxy authentication, as well.
+
+        @return: returns C{True} if the session is configured to re-use session password / key for proxy authentication
+        @rtype: C{bool}
+
+        """
+        return self.sshproxy_reuse_authinfo
+    __reuses_sshproxy_authinfo = reuses_sshproxy_authinfo
+
     def can_sshproxy_auto_connect(self):
         """\
         Check if a session's SSH proxy (if used) is configured adequately to be able to auto-connect
@@ -1005,8 +1080,16 @@ class X2goSession(object):
         if self.use_sshproxy:
             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'])):
                 return True
+            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'])):
+                return True
             elif self.sshproxy_params.has_key('sshproxy_pkey') and self.sshproxy_params['sshproxy_pkey']:
                 return True
+            elif self.sshproxy_reuse_authinfo and self.control_params.has_key('pkey') and self.control_params['pkey']:
+                return True
+            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'))):
+                return True
+            elif self.sshproxy_params.has_key('sshproxy_allow_agent') and self.sshproxy_params['sshproxy_allow_agent'] and paramiko.Agent().get_keys():
+                return True
             else:
                 return False
         else:
@@ -1026,21 +1109,23 @@ class X2goSession(object):
         if self.control_session is None:
             return None
 
+        _can_sshproxy_auto_connect = self.can_sshproxy_auto_connect()
+
         # do we have a key file passed as control parameter?
         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'])):
-            _can_sshproxy_auto_connect = self.can_sshproxy_auto_connect()
-            if _can_sshproxy_auto_connect is not None:
-                return _can_sshproxy_auto_connect
-            else:
-                return True
+            return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
 
         # or a private key?
         elif self.control_params.has_key('pkey') and self.control_params['pkey']:
-            _can_sshproxy_auto_connect = self.can_sshproxy_auto_connect()
-            if _can_sshproxy_auto_connect is not None:
-                return _can_sshproxy_auto_connect
-            else:
-                return True
+            return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
+
+        # or a key auto discovery?
+        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'))):
+            return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
+
+        # or an SSH agent usage?
+        elif self.control_params.has_key('allow_agent') and self.control_params['allow_agent'] and paramiko.Agent().get_keys():
+            return (_can_sshproxy_auto_connect is None) or _can_sshproxy_auto_connect
 
         else:
             return False
@@ -1065,7 +1150,8 @@ class X2goSession(object):
     __do_auto_connect = do_auto_connect
 
     def connect(self, username='', password='', add_to_known_hosts=False, force_password_auth=False,
-                use_sshproxy=False, sshproxy_user='', sshproxy_password=''):
+                look_for_keys=None, allow_agent=None,
+                use_sshproxy=None, sshproxy_reuse_authinfo=False, sshproxy_user='', sshproxy_password='', sshproxy_force_password_auth=False):
         """\
         Connects to the L{X2goSession}'s server host. This method basically wraps around 
         the C{X2goControlSession*.connect()} method.
@@ -1083,12 +1169,22 @@ class X2goSession(object):
         @param force_password_auth: disable SSH pub/priv key authentication mechanisms
             completely
         @type force_password_auth: C{bool}
+        @param look_for_keys: set to C{True} to enable searching for discoverable
+            private key files in C{~/.ssh/}
+        @type look_for_keys: C{bool}
+        @param allow_agent: set to C{True} to enable connecting to a local SSH agent
+            for acquiring authentication information
+        @type allow_agent: C{bool}
         @param use_sshproxy: use an SSH proxy host for connecting the target X2Go server
         @type use_sshproxy: C{bool}
+        @param sshproxy_reuse_authinfo: for proxy authentication re-use the X2Go session's password / key file
+        @type sshproxy_reuse_authinfo: C{bool}
         @param sshproxy_user: username for authentication against the SSH proxy host
         @type sshproxy_user: C{str}
         @param sshproxy_password: password for authentication against the SSH proxy host
         @type sshproxy_password: C{str}
+        @param sshproxy_force_password_auth: enforce password authentication even is a key(file) is present
+        @type sshproxy_force_password_auth: C{bool}
 
         @return: returns C{True} is the connection to the X2Go server has been successful
         @rtype C{bool}
@@ -1102,18 +1198,41 @@ class X2goSession(object):
             self.logger('control session is already connected, skipping authentication', loglevel=log.loglevel_DEBUG)
             self.connected = True
         else:
+
+            if use_sshproxy is not None:
+                self.use_sshproxy = use_sshproxy
+
+            if sshproxy_reuse_authinfo is not None:
+                self.sshproxy_reuse_authinfo = sshproxy_reuse_authinfo
+
             if username:
                 self.control_params['username'] = username
             if add_to_known_hosts is not None:
                 self.control_params['add_to_known_hosts'] = add_to_known_hosts
             if force_password_auth is not None:
                 self.control_params['force_password_auth'] = force_password_auth
+            if look_for_keys is not None:
+                self.control_params['look_for_keys'] = look_for_keys
+            if allow_agent is not None:
+                self.control_params['allow_agent'] = allow_agent
+
             if sshproxy_user:
                 self.sshproxy_params['sshproxy_user'] = sshproxy_user
             if sshproxy_password:
                 self.sshproxy_params['sshproxy_password'] = sshproxy_password
+            if sshproxy_force_password_auth:
+                self.sshproxy_params['sshproxy_force_password_auth'] = sshproxy_force_password_auth
+
             self.control_params['password'] = password
 
+            if self.sshproxy_reuse_authinfo:
+                if self.control_params.has_key('key_filename'):
+                    self.sshproxy_params['sshproxy_key_filename'] = self.control_params['key_filename']
+                if self.control_params.has_key('pkey'):
+                    self.sshproxy_params['sshproxy_pkey'] = self.control_params['pkey']
+                if self.control_params.has_key('password'):
+                    self.sshproxy_params['sshproxy_password'] = self.control_params['password']
+
             _params = {}
             _params.update(self.control_params)
             _params.update(self.sshproxy_params)
@@ -1123,8 +1242,9 @@ class X2goSession(object):
 
             try:
                 self.connected = self.control_session.connect(self.server,
-                                                              use_sshproxy=self.use_sshproxy, 
-                                                              session_instance=self, 
+                                                              use_sshproxy=self.use_sshproxy,
+                                                              session_instance=self,
+                                                              forward_sshagent=self.forward_sshagent,
                                                               **_params)
             except x2go_exceptions.X2goControlSessionException, e:
                 raise x2go_exceptions.X2goSessionException(str(e))
@@ -1172,6 +1292,7 @@ class X2goSession(object):
         self.terminated = None
         self.faults = None
         self.active = False
+        self._lock.release()
         self.unset_master_session()
         try:
             self.update_status(force_update=True)
@@ -1549,7 +1670,55 @@ class X2goSession(object):
                         return self.start()
     __do_auto_start_or_resume = do_auto_start_or_resume
 
-    def resume(self, session_name=None, session_list=None, cmd=None):
+    def reset_progress_status(self):
+        """\
+        Reset session startup/resumption progress status.
+
+        """
+        self._progress_status = 0
+
+    def get_progress_status(self):
+        """\
+        Retrieve session startup/resumption progress status.
+
+        @return: returns an C{int} value between 0 and 100 reflecting the session startup/resumption status
+        @rtype: C{int}
+
+        """
+        return self._progress_status
+
+    def resume(self, session_name=None, session_list=None, cmd=None, progress_event=None):
+        """\
+        Resume or continue a suspended / running X2Go session on the
+        remote X2Go server.
+
+        @param session_name: the server-side name of an X2Go session
+        @type session_name: C{str}
+        @param session_list: a session list to avoid a server-side session list query
+        @type session_list: C{dict}
+        @param cmd: if starting a new session, manually hand over the command to be launched in
+            the new session
+        @type cmd: C{str}
+        @param progress_event: a C{thread.Event} object that notifies a status object like the one in
+            L{utils.ProgressStatus}.
+        @type progress_event: C{obj}
+
+        @return: returns C{True} if resuming the session has been successful, C{False} otherwise
+        @rtype: C{bool}
+
+        @raise Exception: any exception that occurs during published application menu retrieval is passed through
+
+        """
+        self._lock.acquire()
+        try:
+            _retval = self._resume(session_name=session_name, session_list=session_list, cmd=cmd, progress_event=progress_event)
+        except:
+            raise
+        finally:
+            self._lock.release()
+        return _retval
+
+    def _resume(self, session_name=None, session_list=None, cmd=None, progress_event=None):
         """\
         Resume or continue a suspended / running X2Go session on the
         remote X2Go server.
@@ -1561,6 +1730,9 @@ class X2goSession(object):
         @param cmd: if starting a new session, manually hand over the command to be launched in
             the new session
         @type cmd: C{str}
+        @param progress_event: a C{thread.Event} object that notifies a status object like the one in
+            L{utils.ProgressStatus}.
+        @type progress_event: C{obj}
 
         @return: returns C{True} if resuming the session has been successful, C{False} otherwise
         @rtype: C{bool}
@@ -1569,30 +1741,64 @@ class X2goSession(object):
 
         """
         self.terminal_session = 'PENDING'
+
+        # initialize a dummy event to avoid many if clauses further down in the code
+        self.reset_progress_status()
+        _dummy_event = threading.Event()
+        if type(progress_event) != type(_dummy_event):
+            progress_event = _dummy_event
+
+        self._progress_status = 1
+        progress_event.set()
+
         _new_session = False
         if self.session_name is None:
             self.session_name = session_name
 
+        self._progress_status = 2
+        progress_event.set()
+
         if self.is_alive():
 
+            self._progress_status = 5
+            progress_event.set()
+
             _control = self.control_session
 
+            self._progress_status = 7
+            progress_event.set()
+
             # FIXME: normally this part gets called if you suspend a session that is associated to another client
             # we do not have a possibility to really check if SSH has released port forwarding channels or
             # sockets, thus  we plainly have to wait a while
 
             if self.is_running():
                 try:
-                    self.suspend()
+
+                    self._suspend()
+
+                    self._progress_status = 10
+                    progress_event.set()
+
                     gevent.sleep(5)
+
+                    self._progress_status = 15
+                    progress_event.set()
+
                 except x2go_exceptions.X2goSessionException:
                     pass
 
+            self._progress_status = 20
+            progress_event.set()
+
             try:
                 if self.published_applications:
                     self.published_applications_menu = gevent.spawn(self.get_published_applications)
             except:
                 # FIXME: test the code to see what exceptions may occur here...
+
+                self._progress_status = -1
+                progress_event.set()
                 raise
 
             if cmd is not None:
@@ -1603,6 +1809,9 @@ class X2goSession(object):
                                                     session_list=session_list,
                                                     logger=self.logger, **self.terminal_params)
 
+            self._progress_status = 25
+            progress_event.set()
+
             if self.session_name is None:
                 _new_session = True
                 try:
@@ -1610,8 +1819,15 @@ class X2goSession(object):
                 except AttributeError:
                     # if self.terminal_session is None, we end up with a session failure...
                     self.HOOK_session_startup_failed()
+
+                    self._progress_status = -1
+                    progress_event.set()
+
                     return False
 
+            self._progress_status = 30
+            progress_event.set()
+
             if self.has_terminal_session() and not self.faulty:
 
                 self.terminal_session.session_info_protect()
@@ -1619,11 +1835,17 @@ class X2goSession(object):
                 if self.get_session_cmd() != 'PUBLISHED':
                     self.published_applications = False
 
+                self._progress_status = 40
+                progress_event.set()
+
                 if self._SUPPORTED_SOUND and self.terminal_session.params.snd_system is not 'none':
                     self.has_terminal_session() and not self.faulty and self.terminal_session.start_sound()
                 else:
                     self._SUPPORTED_SOUND = False
 
+                self._progress_status = 50
+                progress_event.set()
+
                 try:
                     if (self._SUPPORTED_PRINTING and self.printing) or \
                        (self._SUPPORTED_MIMEBOX and self.allow_mimebox) or \
@@ -1636,24 +1858,33 @@ class X2goSession(object):
                     self._SUPPORTED_MIMEBOX = False
                     self._SUPPORTED_FOLDERSHARING = False
 
+                self._progress_status = 60
+                progress_event.set()
+
                 if self._SUPPORTED_PRINTING and self.printing:
                     try:
-                        self.has_terminal_session() and not self.faulty and gevent.spawn(self.terminal_session.start_printing)
+                        self.has_terminal_session() and not self.faulty and self.terminal_session.start_printing()
                         self.has_terminal_session() and not self.faulty and self.session_environment.update({'X2GO_SPOOLDIR': self.terminal_session.get_printing_spooldir(), })
                     except x2go_exceptions.X2goUserException, e:
                         self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
                         self.HOOK_printing_not_available()
                         self._SUPPORTED_PRINTING = False
 
+                self._progress_status = 70
+                progress_event.set()
+
                 if self._SUPPORTED_MIMEBOX and self.allow_mimebox:
                     try:
-                        self.has_terminal_session() and not self.faulty and gevent.spawn(self.terminal_session.start_mimebox, mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
+                        self.has_terminal_session() and not self.faulty and self.terminal_session.start_mimebox(mimebox_extensions=self.mimebox_extensions, mimebox_action=self.mimebox_action)
                         self.has_terminal_session() and self.session_environment.update({'X2GO_MIMEBOX': self.terminal_session.get_mimebox_spooldir(), })
                     except x2go_exceptions.X2goUserException, e:
                         self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
                         self.HOOK_mimebox_not_available()
                         self._SUPPORTED_MIMEBOX = False
 
+                self._progress_status = 80
+                progress_event.set()
+
                 # only run the session startup command if we do not resume...
                 if _new_session:
                     self.has_terminal_session() and self.terminal_session.run_command(env=self.session_environment)
@@ -1664,42 +1895,96 @@ class X2goSession(object):
                 self.terminated = False
                 self.faulty = False
 
-                # if there is a client instance for X2Go sessions that the client instance will handle the mounting of shared folders
+                self._progress_status = 90
+                progress_event.set()
+
+                # if self.client_instance exists than the folder sharing is handled via the self.set_master_session() evoked by the session registry
                 if (not self.client_instance) and \
                    self._SUPPORTED_FOLDERSHARING and \
                    self.allow_share_local_folders:
                     gevent.spawn(self.share_all_local_folders)
 
+                self._progress_status = 100
+                progress_event.set()
+
                 self.has_terminal_session() and self.terminal_session.session_info_unprotect()
                 return True
 
             else:
                 self.terminal_session = None
+
+                self._progress_status = -1
+                progress_event.set()
+
                 return False
 
-            return self.running
         else:
+
+            self._progress_status = -1
+            progress_event.set()
+
             self._X2goSession__disconnect()
             return False
 
     __resume = resume
 
-    def start(self, cmd=None):
+    def start(self, cmd=None, progress_event=None):
         """\
         Start a new X2Go session on the remote X2Go server.
 
         @param cmd: manually hand over the command that is to be launched in the new session
         @type cmd: C{str}
+        @param progress_event: a C{thread.Event} object that notifies a status object like the one in
+            L{utils.ProgressStatus}.
+        @type progress_event: C{obj}
 
         @return: returns C{True} if starting the session has been successful, C{False} otherwise
         @rtype: C{bool}
 
         """
         self.session_name = None
-        return self.resume(cmd=cmd)
+        return self.resume(cmd=cmd, progress_event=progress_event)
     __start = start
 
-    def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True):
+    def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True, progress_event=None):
+        """\
+        Share an already running X2Go session on the remote X2Go server locally. The shared session may be either
+        owned by the same user or by a user that grants access to his/her desktop session by the local user.
+
+        @param desktop: desktop ID of a sharable desktop in format <user>@<display>
+        @type desktop: C{str}
+        @param user: user name and display number can be given separately, here give the
+            name of the user who wants to share a session with you.
+        @type user: C{str}
+        @param display: user name and display number can be given separately, here give the
+            number of the display that a user allows you to be shared with.
+        @type display: C{str}
+        @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
+        @type share_mode: C{int}
+        @param check_desktop_list: check if the given desktop is available on the X2Go server; handle with care as
+            the server-side C{x2golistdesktops} command might block client I/O.
+        @type check_desktop_list: C{bool}
+        @param progress_event: a C{thread.Event} object that notifies a status object like the one in
+            L{utils.ProgressStatus}.
+        @type progress_event: C{obj}
+
+        @return: returns C{True} if starting the session has been successful, C{False} otherwise
+        @rtype: C{bool}
+
+        @raise X2goDesktopSharingException: if the given desktop ID is not an available desktop session on the remote server
+        @raise X2goSessionException: if the available desktop session appears to be dead, in fact
+
+        """
+        self._lock.acquire()
+        try:
+            _retval = self._share_desktop(desktop=desktop, user=user, display=display, share_mode=share_mode, check_desktop_list=check_desktop_list, progress_event=progress_event)
+        except:
+            raise
+        finally:
+            self._lock.release()
+        return _retval
+
+    def _share_desktop(self, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=True, progress_event=None):
         """\
         Share an already running X2Go session on the remote X2Go server locally. The shared session may be either
         owned by the same user or by a user that grants access to his/her desktop session by the local user.
@@ -1717,6 +2002,9 @@ class X2goSession(object):
         @param check_desktop_list: check if the given desktop is available on the X2Go server; handle with care as
             the server-side C{x2golistdesktops} command might block client I/O.
         @type check_desktop_list: C{bool}
+        @param progress_event: a C{thread.Event} object that notifies a status object like the one in
+            L{utils.ProgressStatus}.
+        @type progress_event: C{obj}
 
         @return: returns C{True} if starting the session has been successful, C{False} otherwise
         @rtype: C{bool}
@@ -1727,6 +2015,15 @@ class X2goSession(object):
         """
         self.terminal_session = 'PENDING'
 
+        # initialize a dummy event to avoid many if clauses further down in the code
+        self.reset_progress_status()
+        _dummy_event = threading.Event()
+        if type(progress_event) != type(_dummy_event):
+            progress_event = _dummy_event
+
+        self._progress_status = 5
+        progress_event.set()
+
         _desktop = desktop or '%s@%s' % (user, display)
         if check_desktop_list:
             if not _desktop in self._X2goSession__list_desktops():
@@ -1735,23 +2032,39 @@ class X2goSession(object):
                 if not _desktop in self._X2goSession__list_desktops():
                     raise x2go_exceptions.X2goDesktopSharingException('No such desktop ID: %s' % _orig_desktop)
 
+        self._progress_status = 33
+        progress_event.set()
+
         _session_owner = _desktop.split('@')[0]
-        #_display = _desktop.split('@')[1]
 
         if self.is_alive():
             if self.get_username() != _session_owner:
                 self.logger('waiting for user ,,%s\'\' to interactively grant you access to his/her desktop session...' % _session_owner, loglevel=log.loglevel_NOTICE)
                 self.logger('THIS MAY TAKE A WHILE!', loglevel=log.loglevel_NOTICE)
 
+                self._progress_status = 50
+                progress_event.set()
+
             _control = self.control_session
             try:
                 self.terminal_session = _control.share_desktop(desktop=_desktop, share_mode=share_mode,
                                                                logger=self.logger, **self.terminal_params)
+
+                self._progress_status = 80
+                progress_event.set()
+
             except ValueError:
                 # x2gostartagent output parsing will result in a ValueError. This one we will catch
                 # here and change it into an X2goSessionException
+
+                self._progress_status = -1
+                progress_event.set()
+
                 raise x2go_exceptions.X2goSessionException('the session on desktop %s is seemingly dead' % _desktop)
 
+            self._progress_status = 90
+            progress_event.set()
+
             if self.has_terminal_session():
                 self.session_name = self.terminal_session.session_info.name
 
@@ -1765,11 +2078,21 @@ class X2goSession(object):
                 self.terminated = False
                 self.faulty = False
 
+                self._progress_status = 100
+                progress_event.set()
+
                 return self.running
             else:
                 self.terminal_session = None
 
+                self._progress_status = -1
+                progress_event.set()
+
         else:
+
+            self._progress_status = -1
+            progress_event.set()
+
             self._X2goSession__disconnect()
 
         return False
@@ -1785,6 +2108,26 @@ class X2goSession(object):
         @raise X2goSessionException: if the session could not be suspended
 
         """
+        self._lock.acquire()
+        try:
+            _retval = self._suspend()
+        except:
+            raise
+        finally:
+            self._lock.release()
+
+        return _retval
+
+    def _suspend(self):
+        """\
+        Suspend this X2Go session.
+
+        @return: returns C{True} if suspending the session has been successful, C{False} otherwise
+        @rtype: C{bool}
+
+        @raise X2goSessionException: if the session could not be suspended
+
+        """
         if self.is_alive():
             if self.has_terminal_session():
 
@@ -1799,12 +2142,12 @@ class X2goSession(object):
 
                 self.unset_master_session()
 
-                if self.terminal_session.suspend():
-
-                    self.session_cleanup()
-                    del self.terminal_session
-                    self.terminal_session = None
-                    return True
+                if self.has_terminal_session():
+                    if self.terminal_session.suspend():
+                        self.session_cleanup()
+                        del self.terminal_session
+                        self.terminal_session = None
+                        return True
 
             elif self.has_control_session() and self.session_name:
                 if self.control_session.suspend(session_name=self.session_name):
@@ -1836,6 +2179,26 @@ class X2goSession(object):
         @raise X2goSessionException: if the session could not be terminated
 
         """
+        self._lock.acquire()
+        try:
+            _retval = self._terminate()
+        except:
+            raise
+        finally:
+            self._lock.release()
+
+        return _retval
+
+    def _terminate(self):
+        """\
+        Terminate this X2Go session.
+
+        @return: returns C{True} if terminating the session has been successful, C{False} otherwise
+        @rtype: C{bool}
+
+        @raise X2goSessionException: if the session could not be terminated
+
+        """
         if self.is_alive():
             if self.has_terminal_session():
 
@@ -1850,11 +2213,12 @@ class X2goSession(object):
 
                 self.unset_master_session()
 
-                if self.terminal_session.terminate():
-                    self.session_cleanup()
-                    del self.terminal_session
-                    self.terminal_session = None
-                    return True
+                if self.has_terminal_session():
+                    if self.terminal_session.terminate():
+                        self.session_cleanup()
+                        del self.terminal_session
+                        self.terminal_session = None
+                        return True
 
             elif self.has_control_session() and self.session_name:
                 if self.control_session.terminate(session_name=self.session_name):
@@ -2040,7 +2404,19 @@ class X2goSession(object):
         return False
     __is_folder_sharing_available = is_folder_sharing_available
 
-    def share_local_folder(self, local_path=None, folder_name=None):
+    def _update_restore_exported_folders(self):
+
+        # remember exported folders for restoring them on session suspension/termination
+        if self.client_instance and self.restore_shared_local_folders:
+            _exported_folders = copy.deepcopy(self._restore_exported_folders)
+            for folder in [ sf for sf in self.shared_folders.keys() if self.shared_folders[sf]['status'] in ('new', 'mounted') ]:
+                _exported_folders.update({ unicode(folder): True })
+            for folder in _exported_folders.keys():
+                if folder in [ sf for sf in self.shared_folders.keys() if self.shared_folders[sf]['status'] == 'unmounted' ]:
+                    del _exported_folders[unicode(folder)]
+            self._restore_exported_folders = _exported_folders
+
+    def share_local_folder(self, local_path=None, folder_name=None, update_exported_folders=True):
         """\
         Share a local folder with this registered X2Go session.
 
@@ -2049,6 +2425,8 @@ class X2goSession(object):
         @type local_path: C{str}
         @param folder_name: synonymous to C{local_path}
         @type folder_name: C{str}
+        @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+        @type update_exported_folders: C{bool}
 
         @return: returns C{True} if the local folder has been successfully mounted within
             this X2Go session
@@ -2060,48 +2438,79 @@ class X2goSession(object):
         # compat for Python-X2Go (<=0.1.1.6)
         if folder_name: local_path=folder_name
 
+        local_path = unicode(local_path)
+
+        retval = False
         if self.has_terminal_session():
             if self.is_folder_sharing_available() and self.is_master_session():
+
+                # for the sake of non-blocking I/O: let's pretend the action has already been successful
+                if self.shared_folders.has_key(local_path):
+                    self.shared_folders[local_path]['status'] = 'mounted'
+                else:
+                    self.shared_folders.update({ local_path: { 'status': 'new', 'mountpoint': '', }, })
                 if self.terminal_session.share_local_folder(local_path=local_path):
-                    self.shared_folders.append(local_path)
-                    return True
-                return False
+                    if update_exported_folders:
+                        self._update_restore_exported_folders()
+                    retval = True
+                else:
+                    # remove local_path from folder again if the mounting process failed
+                    if self.shared_folders[local_path]['status'] == 'new':
+                        del self.shared_folders[local_path]
+                    else:
+                        self.shared_folders[local_path]['status'] = 'unmounted'
+
+                    # disable this local folder in session profile if restoring shared folders for following sessions is activated
+                    if self.client_instance and self.restore_shared_local_folders:
+                        if local_path in self._restore_exported_folders.keys():
+                            self._restore_exported_folders[local_path] = False
+
         else:
             raise x2go_exceptions.X2goSessionException('this X2goSession object does not have any associated terminal')
+        return retval
+
     __share_local_folder = share_local_folder
 
-    def share_all_local_folders(self):
+    def share_all_local_folders(self, update_exported_folders=True):
         """\
         Share all local folders configured to be mounted within this X2Go session.
 
+        @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+        @type update_exported_folders: C{bool}
+
         @return: returns C{True} if all local folders could be successfully mounted
             inside this X2Go session
         @rtype: C{bool}
 
         """
-        _retval = False
+        retval = False
         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():
             if self.is_master_session():
                 if self.is_folder_sharing_available():
-                    if self.control_session.get_transport().reverse_tunnels[self.terminal_session.get_session_name()]['sshfs'][1] is not None:
-                        _retval = True
-                        for _folder in self.share_local_folders:
-                            try:
-                                _retval = self.share_local_folder(_folder) and _retval
-                            except x2go_exceptions.X2goUserException, e:
-                                self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
+                    retval = True
+                    for _folder in self.share_local_folders:
+                        try:
+                            retval = self.share_local_folder(_folder, update_exported_folders=False) and retval
+                        except x2go_exceptions.X2goUserException, e:
+                            retval = False
+                            self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
+                    if update_exported_folders:
+                        self._update_restore_exported_folders()
                 else:
                     self.HOOK_foldersharing_not_available()
-        return _retval
+        return retval
     __share_all_local_folders = share_all_local_folders
 
-    def unshare_all_local_folders(self, force_all=False):
+    def unshare_local_folder(self, local_path=None, update_exported_folders=True):
         """\
-        Unshare all local folders mounted within this X2Go session.
+        Unshare a local folder that is mounted within this X2Go session.
 
-        @param force_all: Really unmount _all_ shared folders, including the print spool folder and
-            the MIME box spool dir (not recommended).
-        @type force_all: C{bool}
+        @param local_path: the full path to an existing folder on the local
+            file system that is mounted in this X2Go session and shall be
+            unmounted
+        @type local_path: C{str}
+        @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+        @type update_exported_folders: C{bool}
 
         @return: returns C{True} if all local folders could be successfully unmounted
             inside this X2Go session
@@ -2110,30 +2519,37 @@ class X2goSession(object):
         @raise X2goSessionException: if this L{X2goSession} does not have an associated terminal session
 
         """
+        retval = False
+
+        local_path = unicode(local_path)
+
         if self.has_terminal_session():
-            if self.is_folder_sharing_available() and self.is_master_session():
-                if force_all:
-                    self.shared_folders = []
-                    return self.terminal_session.unshare_all_local_folders()
+            if self.is_folder_sharing_available() and self.is_master_session() and local_path in self.shared_folders.keys():
+
+                # for the sake of non-blocking I/O: let's pretend the action has already been successful
+                self.shared_folders[local_path]['status'] = 'unmounted'
+                if self.terminal_session.unshare_local_folder(local_path=local_path):
+                    retval = True
                 else:
-                    retval = 0
-                    for _shared_folder in self.shared_folders:
-                        retval = retval | self.terminal_session.unshare_local_folder(_shared_folder)
-                    self.shared_folders = []
-                    return retval
+                    # if unmounting failed restore the status with ,,mounted'', not sure if that works ok...
+                    self.shared_folders[local_path]['status'] = 'mounted'
+
         else:
             raise x2go_exceptions.X2goSessionException('this X2goSession object does not have any associated terminal')
-        return False
-    __unshare_all_local_folders = unshare_all_local_folders
 
-    def unshare_local_folder(self, local_path=None):
+        return retval
+    __unshare_local_folder = unshare_local_folder
+
+    def unshare_all_local_folders(self, force_all=False, update_exported_folders=True):
         """\
-        Unshare a local folder that is mounted within this X2Go session.
+        Unshare all local folders mounted within this X2Go session.
+
+        @param force_all: Really unmount _all_ shared folders, including the print spool folder and
+            the MIME box spool dir (not recommended).
+        @type force_all: C{bool}
+        @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+        @type update_exported_folders: C{bool}
 
-        @param local_path: the full path to an existing folder on the local
-            file system that is mounted in this X2Go session and shall be
-            unmounted
-        @type local_path: C{str}
         @return: returns C{True} if all local folders could be successfully unmounted
             inside this X2Go session
         @rtype: C{bool}
@@ -2142,12 +2558,25 @@ class X2goSession(object):
 
         """
         if self.has_terminal_session():
-            if self.is_folder_sharing_available() and self.is_master_session() and local_path in self.shared_folders:
-                self.shared_folders.remove(local_path)
-                return self.terminal_session.unshare_local_folder(local_path=local_path)
+            if self.is_folder_sharing_available() and self.is_master_session():
+
+                if force_all:
+                    retval = self.terminal_session.unshare_all_local_folders()
+                    if retval:
+                        self.shared_folders = {}
+                    return retval
+                else:
+                    retval = True
+                    _shared_folders = copy.deepcopy(self.shared_folders)
+                    for _folder in _shared_folders.keys():
+                        retval = self.unshare_local_folder(_folder, update_exported_folders=False) and retval
+                    if update_exported_folders:
+                        self._update_restore_exported_folders()
+                    return retval
         else:
             raise x2go_exceptions.X2goSessionException('this X2goSession object does not have any associated terminal')
-    __unshare_local_folder = unshare_local_folder
+        return False
+    __unshare_all_local_folders = unshare_all_local_folders
 
     def get_shared_folders(self, check_list_mounts=False, mounts=None):
         """\
@@ -2168,39 +2597,45 @@ class X2goSession(object):
             unshared_folders = []
             if mounts is None:
                 mounts = self.list_mounts()
+            _defacto_mounts = [ unicode(m.split('|')[1].split('/')[-1]) for m in mounts ]
 
-            for shared_folder in self.shared_folders:
+            for shared_folder in self.shared_folders.keys():
 
-                _found = False
+                if _X2GOCLIENT_OS == 'Windows':
+                    _driveletter, _path = os.path.splitdrive(shared_folder)
+                    _mount_point = '_windrive_%s%s' % (_driveletter[0], _path.replace('\\', '_'))
+                    _mount_point = _mount_point.replace(' ', '_')
 
-                for mount in mounts:
-                    mount = mount.split('|')[1]
-                    if _X2GOCLIENT_OS == 'Windows':
-                        _driveletter, _path = os.path.splitdrive(shared_folder)
-                        _mount_point = '_windrive_%s%s' % (_driveletter[0], _path.replace('\\', '_'))
-                        _mount_point = _mount_point.replace(' ', '_')
+                else:
+                    _mount_point = shared_folder.replace('/', '_')
+                    _mount_point = _mount_point.replace(' ', '_')
 
-                        if mount.lower().endswith(_mount_point.lower()):
-                            _found = True
-                            break
-                    else:
-                        _mount_point = shared_folder.replace('/', '_')
-                        _mount_point = _mount_point.replace(' ', '_')
-                        if mount.endswith(_mount_point):
-                            _found = True
-                            break
+                self.shared_folders[shared_folder]['status'] = 'mounted'
+                self.shared_folders[shared_folder]['mountpoint'] = unicode(_mount_point)
 
-                if not _found:
-                    unshared_folders.append(shared_folder)
+            for m in _defacto_mounts:
+                for sf in self.shared_folders.keys():
+                    if self.shared_folders[sf]['mountpoint'] == m:
+                        self.shared_folders[sf]['status'] = 'mounted'
+                        break
 
-            for unshared_folder in unshared_folders:
-                try:
-                    self.shared_folders.remove(unshared_folder)
-                    self.logger('Detected server-side unsharing of client-side folder for profile %s: %s:' % (self.get_profile_name(), shared_folder), loglevel=log.loglevel_INFO)
-                except IndexError:
-                    pass
+            unshared_folders = False
+
+            for sf in self.shared_folders.keys():
+                m = self.shared_folders[sf]['mountpoint']
+                if m and m not in _defacto_mounts:
+                    try:
+                        if self.shared_folders[sf]['status'] == 'mounted':
+                            self.shared_folders[sf]['status'] = 'unmounted'
+                            self.logger('Detected server-side unsharing of client-side folder for profile %s: %s:' % (self.get_profile_name(), sf), loglevel=log.loglevel_INFO)
+                            unshared_folders = True
+                    except IndexError:
+                        pass
+
+            if unshared_folders:
+                self._update_restore_exported_folders()
 
-        return self.shared_folders
+        return [ unicode(sf) for sf in self.shared_folders if self.shared_folders[sf]['status'] in ('new', 'mounted') ]
     __get_shared_folders = get_shared_folders
 
     def is_locked(self):
diff --git a/x2go/sshproxy.py b/x2go/sshproxy.py
index 5cecf17..df50f2b 100644
--- a/x2go/sshproxy.py
+++ b/x2go/sshproxy.py
@@ -44,8 +44,8 @@ from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
 from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
 from x2go.defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR
 
-from monkey_patch_paramiko import monkey_patch_paramiko
-monkey_patch_paramiko()
+import x2go._paramiko
+x2go._paramiko.monkey_patch_paramiko()
 
 class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
     """\
@@ -54,11 +54,12 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
     """
     fw_tunnel = None
 
-    def __init__(self, hostname=None, port=22, username=None, password=None, key_filename=None,
+    def __init__(self, hostname=None, port=22, username=None, password=None, force_password_auth=False, key_filename=None,
                  local_host='localhost', local_port=22022, remote_host='localhost', remote_port=22,
-                 known_hosts=None, add_to_known_hosts=False, pkey=None,
-                 sshproxy_host=None, sshproxy_port=22, sshproxy_user=None, 
-                 sshproxy_password=None, sshproxy_key_filename=None, sshproxy_pkey=None,
+                 known_hosts=None, add_to_known_hosts=False, pkey=None, look_for_keys=False, allow_agent=False,
+                 sshproxy_host=None, sshproxy_port=22, sshproxy_user=None,
+                 sshproxy_password=None, sshproxy_force_password_auth=False, sshproxy_key_filename=None, sshproxy_pkey=None,
+                 sshproxy_look_for_keys=False, sshproxy_allow_agent=False,
                  sshproxy_tunnel=None,
                  ssh_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SSH_ROOTDIR), 
                  session_instance=None,
@@ -77,6 +78,12 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
         @type key_filename: C{str}
         @param pkey: a private DSA/RSA key object (as provided by Paramiko/SSH)
         @type pkey: C{RSA/DSA key instance}
+        @param force_password_auth: enforce password authentication even if a key(file) is present
+        @type force_password_auth: C{bool}
+        @param look_for_keys: look for key files with standard names and try those if any can be found
+        @type look_for_keys: C{bool}
+        @param allow_agent: try authentication via a locally available SSH agent
+        @type allow_agent: C{bool}
         @param local_host: bind SSH tunnel to the C{local_host} IP socket address (default: localhost)
         @type local_host: C{str}
         @param local_port: IP socket port to bind the SSH tunnel to (default; 22022)
@@ -93,9 +100,9 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
         @type hostname: C{str}
         @param port: alias for C{local_port}
         @type port: C{int}
-        @param sshproxy_host: alias for C{remote_host}
+        @param sshproxy_host: alias for C{hostname}
         @type sshproxy_host: C{str}
-        @param sshproxy_port: alias for C{remote_port}
+        @param sshproxy_port: alias for C{post}
         @type sshproxy_port: C{int}
         @param sshproxy_user: alias for C{username}
         @type sshproxy_user: C{str}
@@ -105,6 +112,12 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
         @type sshproxy_key_filename: C{str}
         @param sshproxy_pkey: alias for C{pkey}
         @type sshproxy_pkey: C{RSA/DSA key instance} (Paramiko)
+        @param sshproxy_force_password_auth: alias for C{force_password_auth}
+        @type sshproxy_force_password_auth: C{bool}
+        @param sshproxy_look_for_keys: alias for C{look_for_keys}
+        @type sshproxy_look_for_keys: C{bool}
+        @param sshproxy_allow_agent: alias for C{allow_agent}
+        @type sshproxy_allow_agent: C{bool}
 
         @param sshproxy_tunnel: a string of the format <local_host>:<local_port>:<remote_host>:<remote_port> 
             which will override---if used---the options: C{local_host}, C{local_port}, C{remote_host} and C{remote_port}
@@ -132,7 +145,10 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
 
         self.hostname, self.port, self.username = hostname, port, username
 
+        if sshproxy_port: self.port = sshproxy_port
+
         # translate between X2goSession options and paramiko.SSHCLient.connect() options
+        # if <hostname>:<port> is used for sshproxy_host, then this <port> is used
         if sshproxy_host:
             if sshproxy_host.find(':'):
                 self.hostname = sshproxy_host.split(':')[0]
@@ -143,8 +159,11 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
 
         if sshproxy_user: self.username = sshproxy_user
         if sshproxy_password: password = sshproxy_password
+        if sshproxy_force_password_auth: force_password_auth = sshproxy_force_password_auth
         if sshproxy_key_filename: key_filename = sshproxy_key_filename
         if sshproxy_pkey: pkey = sshproxy_pkey
+        if sshproxy_look_for_keys: look_for_keys = sshproxy_look_for_keys
+        if sshproxy_allow_agent: allow_agent = sshproxy_allow_agent
         if sshproxy_tunnel:
             self.local_host, self.local_port, self.remote_host, self.remote_port = sshproxy_tunnel.split(':')
             self.local_port = int(self.local_port)
@@ -194,39 +213,47 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
             self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
         try:
-            if (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
+            if key_filename or pkey or look_for_keys or allow_agent or (password and force_password_auth):
                 try:
-                    if password:
-                        self.connect(_hostname, port=self.port, 
+                    if password and force_password_auth:
+                        self.connect(_hostname, port=self.port,
                                      username=self.username,
                                      password=password,
-                                     key_filename=key_filename,
-                                     pkey=pkey,
+                                     key_filename=None,
+                                     pkey=None,
                                      look_for_keys=False,
                                      allow_agent=False,
                                     )
-                    else:
-                        self.connect(_hostname, port=self.port, 
+                    elif (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
+                        self.connect(_hostname, port=self.port,
                                      username=self.username, 
                                      key_filename=key_filename,
                                      pkey=pkey,
-                                     look_for_keys=False,
-                                     allow_agent=False,
+                                     look_for_keys=look_for_keys,
+                                     allow_agent=allow_agent,
+                                    )
+                    else:
+                        self.connect(_hostname, port=self.port,
+                                     username=self.username, 
+                                     look_for_keys=look_for_keys,
+                                     allow_agent=allow_agent,
                                     )
 
                 except x2go_exceptions.AuthenticationException, e:
                     self.close()
                     raise x2go_exceptions.X2goSSHProxyAuthenticationException('pubkey auth mechanisms both failed')
-                except:
+                except x2go_exceptions.SSHException, e:
                     self.close()
+                    raise x2go_exceptions.X2goSSHProxyAuthenticationException('interactive authentication required')
+                except:
                     raise
 
                 # since Paramiko 1.7.7.1 there is compression available, let's use it if present...
                 t = self.get_transport()
-                if hasattr(t, 'use_compression'):
+                if x2go._paramiko.PARAMIKO_FEATURE['use-compression']:
                     t.use_compression(compress=True)
 
-            # if there is not private key, we will use the given password, if any
+            # if there is no private key, we will use the given password, if any
             else:
                 # create a random password if password is empty to trigger host key validity check
                 if not password:
@@ -245,7 +272,7 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
                     self.close()
                     raise
 
-        except x2go_exceptions.SSHException, e:
+        except (x2go_exceptions.SSHException, IOError), e:
             self.close()
             raise x2go_exceptions.X2goSSHProxyException(str(e))
         except:
diff --git a/x2go/utils.py b/x2go/utils.py
index 9dc38c4..ab98120 100644
--- a/x2go/utils.py
+++ b/x2go/utils.py
@@ -33,6 +33,7 @@ import socket
 import gevent
 import string
 import subprocess
+import distutils.version
 
 # Python X2Go modules
 from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
@@ -185,6 +186,7 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
             'speed': 'link',
             'sshport': 'port',
             'useexports': 'allow_share_local_folders',
+            'restoreexports': 'restore_shared_local_folders',
             'usemimebox': 'allow_mimebox',
             'mimeboxextensions': 'mimebox_extensions',
             'mimeboxaction': 'mimebox_action',
@@ -200,6 +202,7 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
             'iconvfrom': 'client_encoding',
             'usesshproxy': 'use_sshproxy',
             'sshproxyhost': 'sshproxy_host',
+            'sshproxyport': 'sshproxy_port',
             'sshproxyuser': 'sshproxy_user',
             'sshproxykeyfile': 'sshproxy_key_filename',
             'sshproxytunnel': 'sshproxy_tunnel',
@@ -207,8 +210,10 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
             'setsessiontitle': 'set_session_title',
             'published': 'published_applications',
             'autostart': 'auto_start_or_resume',
-            'autologin': 'auto_connect',
-
+            'autoconnect': 'auto_connect',
+            'forwardsshagent': 'forward_sshagent',
+            'autologin': 'look_for_keys',
+            'sshproxyautologin': 'sshproxy_look_for_keys',
     }
     _speed_dict = {
             '0': 'modem',
@@ -246,6 +251,12 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
                 else:
                     _params[opt] = []
 
+    if _params['cmd'] == "XFCE4": _params['cmd'] = "XFCE"
+    if _params['look_for_keys']:
+        _params['allow_agent'] = True
+    if _params['sshproxy_look_for_keys']:
+        _params['sshproxy_allow_agent'] = True
+
     # append value for quality to value for pack method
     if _params['quality']:
         _params['pack'] = '%s-%s' % (_params['pack'], _params['quality'])
@@ -271,13 +282,16 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
             if _shared_folder.split(":")[-1] == "1":
                 _params['share_local_folders'].append(":".join(_shared_folder.split(":")[:-1]))
 
-    if not options['fullscreen']:
-        _params['geometry'] = '%sx%s' % (options['width'], options['height'])
-    else:
+    if options['fullscreen']:
         _params['geometry'] = 'fullscreen'
+    elif options['maxdim']:
+        _params['geometry'] = 'maximize'
+    else:
+        _params['geometry'] = '%sx%s' % (options['width'], options['height'])
     del _params['width']
     del _params['height']
     del _params['fullscreen']
+    del _params['maxdim']
 
     if not options['sound']:
         _params['snd_system'] = 'none'
@@ -306,6 +320,14 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
         del _params['dpi']
     del _params['setdpi']
 
+    if options['sshproxysameuser']:
+        _params['sshproxy_user'] = _params['username']
+    del _params['sshproxysameuser']
+    if options['sshproxysamepass']:
+        _params['sshproxy_reuse_authinfo'] = True
+        _params['sshproxy_key_filename'] = _params['key_filename']
+    del _params['sshproxysamepass']
+
     # currently known but ignored in Python X2go
     _ignored_options = [
             'startsoundsystem',
@@ -319,6 +341,7 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
             'directrdpsettings',
             'rdpclient',
             'rdpport',
+            'sshproxytype',
     ]
     for i in _ignored_options:
         del _params[i]
@@ -496,7 +519,6 @@ def xkb_rules_names():
     }
     return _rn_dict
 
-
 def local_color_depth():
     """\
     Detect the current local screen's color depth.
@@ -564,13 +586,17 @@ def find_session_window(session_name):
         root = display.screen().root
 
         success = False
-        windowIDs = root.get_full_property(display.intern_atom('_NET_CLIENT_LIST'), Xlib.X.AnyPropertyType).value
-        for windowID in windowIDs:
-            window = display.create_resource_object('window', windowID)
-            name = window.get_wm_name()
-            if name is not None and session_name in name:
-                success = True
-                break
+        windowIDs_obj = root.get_full_property(display.intern_atom('_NET_CLIENT_LIST'), Xlib.X.AnyPropertyType)
+
+        if windowIDs_obj is not None:
+            windowIDs = windowIDs_obj.value
+
+            for windowID in windowIDs:
+                window = display.create_resource_object('window', windowID)
+                name = window.get_wm_name()
+                if name is not None and session_name in name:
+                    success = True
+                    break
 
         if success:
             return window
@@ -590,6 +616,50 @@ def find_session_window(session_name):
         return window
 
 
+def get_desktop_geometry():
+    """\
+    Get the geometry of the current screen's desktop by
+    wrapping around::
+
+        xprop -root '_NET_DESKTOP_GEOMETRY'
+
+    @return: a (<width>, <height>) tuple will be returned
+    @rtype: C{tuple}
+
+    """
+    if _X2GOCLIENT_OS != 'Windows':
+        p = subprocess.Popen(['xprop', '-root', '_NET_DESKTOP_GEOMETRY',], stdout=subprocess.PIPE, )
+        _paramval = p.stdout.read().split("=")
+        if len(_paramval) == 2:
+            _list = _paramval[1].rstrip('\n').split(',')
+            if len(_list) == 2:
+                return (_list[0].strip(), _list[1].strip())
+
+    return None
+
+def get_workarea_geometry():
+    """\
+    Get the geometry of the current screen's work area by
+    wrapping around::
+
+        xprop -root '_NET_WORKAREA'
+
+    @return: a (<width>, <height>) tuple will be returned
+    @rtype: C{tuple}
+
+    """
+    if _X2GOCLIENT_OS != 'Windows':
+        p = subprocess.Popen(['xprop', '-root', '_NET_WORKAREA',], stdout=subprocess.PIPE, )
+        _list = p.stdout.read().rstrip('\n').split(',')
+        if len(_list) == 4:
+            return (_list[2].strip(), _list[3].strip())
+        else:
+            return None
+    else:
+
+        return None
+
+
 def set_session_window_title(session_window, session_title):
     """\
     Set title of session window.
@@ -663,3 +733,65 @@ def merge_ordered_lists(l1, l2):
 
     return ordered_list
 
+def compare_versions(version_a, op, version_b):
+    """\
+    Compare <version_a> with <version_b> using operator <op>.
+    In the background C{distutils.version.LooseVersion} is
+    used for the comparison operation.
+
+    @param version_a: a version string
+    @type version_a: C{str}
+    @param op: an operator provide as string (e.g. '<', '>', '==', '>=' etc.)
+    @type op: C{str}
+    @param version_b: another version string that is to be compared with <version_a>
+    @type version_b: C{str}
+
+    """
+
+    ### FIXME: this comparison is not reliable with beta et al. version strings
+
+    ver_a = distutils.version.LooseVersion(version_a)
+    ver_b = distutils.version.LooseVersion(version_b)
+
+    return eval("ver_a %s ver_b" % op)
+
+class ProgressStatus(object):
+    """\
+    A simple progress status iterator class.
+
+    """
+    def __init__(self, progress_event, progress_func=range(0, 100, 10)):
+        """\
+        @param progress_event: a threading.Event() object that gets notified on progress
+        @type progress_event: C{obj}
+        @param progress_func: a function that delivers a value between 0 and 100 (progress percentage value)
+        @type progress_func: C{func}
+
+        """
+        self.ev = progress_event
+        self.progress_func = progress_func
+
+    def __iter__(self):
+        """\
+        Intialize the L{ProgresStatus} iterator object.
+
+        """
+        self.status = self.progress_func()
+        return self
+
+    def next(self):
+        """\
+        On each iteration wait for the progress event to get triggered from an outside
+        part of the application.
+
+        Once the event fires read the progress status from the progress retrieval function
+        and clear the event afterwards (so we wait for the next firing of the event).
+
+        """
+        if self.status < 100 and self.status != -1:
+            self.ev.wait()
+            self.status = self.progress_func()
+            self.ev.clear()
+            return self.status
+        else:
+            raise StopIteration


hooks/post-receive
-- 
python-x2go.git (Python X2Go Client API)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-x2go.git" (Python X2Go Client API).




More information about the x2go-commits mailing list