[X2Go-Commits] python-x2go.git - brokerclient (branch) updated: b76266a559f5a8aad7470cd5abc7c38b94bdbf70

X2Go dev team git-admin at x2go.org
Tue Jan 7 16:19:44 CET 2014


The branch, brokerclient has been updated
       via  b76266a559f5a8aad7470cd5abc7c38b94bdbf70 (commit)
      from  e3fa36a46adeb8516579f9e38c6d0d5aa00d1415 (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:
 x2go/backends/terminal/_stdout.py |   49 ++++++-
 x2go/client.py                    |   11 +-
 x2go/defaults.py                  |   20 ++-
 x2go/dropbox.py                   |  258 +++++++++++++++++++++++++++++++++++
 x2go/dropboxactions.py            |  266 +++++++++++++++++++++++++++++++++++++
 x2go/printactions.py              |   44 +++---
 x2go/printqueue.py                |    7 +-
 x2go/registry.py                  |   23 ----
 x2go/session.py                   |   40 +++++-
 x2go/utils.py                     |   17 +--
 10 files changed, 669 insertions(+), 66 deletions(-)
 create mode 100644 x2go/dropbox.py
 create mode 100644 x2go/dropboxactions.py

The diff of changes is:
diff --git a/x2go/backends/terminal/_stdout.py b/x2go/backends/terminal/_stdout.py
index 03d83d1..000980e 100644
--- a/x2go/backends/terminal/_stdout.py
+++ b/x2go/backends/terminal/_stdout.py
@@ -40,6 +40,7 @@ import copy
 import x2go.rforward as rforward
 import x2go.sftpserver as sftpserver
 import x2go.printqueue as printqueue
+import x2go.dropbox as dropbox
 import x2go.log as log
 import x2go.defaults as defaults
 import x2go.utils as utils
@@ -487,7 +488,6 @@ class X2goTerminalSessionSTDOUT(object):
         """
         self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
 
-
     def stop_printing(self):
         """\
         Shutdown (pause) the X2go Print Queue thread.
@@ -496,6 +496,41 @@ class X2goTerminalSessionSTDOUT(object):
         if self.print_queue is not None:
             self.print_queue.pause()
 
+    def start_dropbox(self, dropbox_extensions=[], dropbox_action=None):
+        """\
+        Initialize X2go dropbox handling.
+
+        """
+        dropbox_dir = os.path.join(self.session_info.local_container, 'dropbox')
+        if not os.path.exists(dropbox_dir):
+            os.mkdir(dropbox_dir)
+        self.share_local_folder(folder_name=dropbox_dir, folder_type='dropbox')
+        self.dropbox_queue = dropbox.X2goDropboxQueue(profile_name=self.profile_name,
+                                                      session_name=self.session_info.name,
+                                                      dropbox_dir=dropbox_dir,
+                                                      dropbox_extensions=dropbox_extensions,
+                                                      dropbox_action=dropbox_action,
+                                                      client_instance=self.client_instance,
+                                                      logger=self.logger,
+                                                     )
+        self.dropbox_queue.start()
+        self.active_threads.append(self.dropbox_queue)
+
+    def set_dropbox_action(self, dropbox_action, **kwargs):
+        """\
+        STILL UNDOCUMENTED
+
+        """
+        self.dropbox_queue.set_dropbox_action(dropbox_action, logger=self.logger, **kwargs)
+
+    def stop_dropbox(self):
+        """\
+        Shutdown (pause) the X2go Dropbox Queue thread.
+
+        """
+        if self.dropbox_queue is not None:
+            self.dropbox_queue.pause()
+
     def share_local_folder(self, folder_name=None, folder_type='disk'):
         """\
         Share a local folder with the X2go session.
@@ -583,6 +618,18 @@ class X2goTerminalSessionSTDOUT(object):
                          'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
                        ]
 
+        elif folder_type is 'dropbox':
+
+            cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
+                         'x2gomountdirs', 
+                         'dir',
+                         str(self.session_info.name), 
+                         _CURRENT_LOCAL_USER,
+                         _x2go_key_fname,
+                         '%s__REVERSESSH_PORT__%s; ' % (folder_name, self.session_info.sshfs_port),
+                         'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 
+                       ]
+
         (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
         self.logger('x2gomountdirs output is : %s' % stdout.read().split('\n'), log.loglevel_NOTICE)
 
diff --git a/x2go/client.py b/x2go/client.py
index a9fb1b3..f1db57a 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -277,6 +277,8 @@ class X2goClient(object):
         self.logger('the Python X2go module could not find any usable XServer application, you will not be able to start X2go sessions without XServer', loglevel=log.loglevel_WARN)
     def HOOK_open_print_dialog(self, filename, profile_name='UNKNOWN', session_name='UNKNOWN'):
         self.logger('HOOK_open_print_dialog: incoming print job ,, %s'' detected by X2goClient hook method' % filename, loglevel=log.loglevel_WARN)
+    def HOOK_open_dropbox_saveas_dialog(self, filename, profile_name='UNKNOWN', session_name='UNKNOWN'):
+        self.logger('HOOK_open_dropbox_saveas_dialog: incoming dropbox job ,, %s'' detected by X2goClient hook method' % filename, loglevel=log.loglevel_WARN)
     def HOOK_printaction_error(self, filename, profile_name='UNKNOWN', session_name='UNKNOWN', err_msg='GENERIC_ERROR'):
         self.logger('HOOK_printaction_error: incoming print job ,, %s'' caused error: %s' % (filename, err_msg), loglevel=log.loglevel_ERROR)
     def HOOK_on_control_session_death(self, profile_name):
@@ -437,9 +439,11 @@ class X2goClient(object):
         return sessions
 
     def register_session(self, server=None, profile_id=None, profile_name=None, session_name=None,
-                         printing=False, allow_share_local_folders=False, share_local_folders=[], return_object=False,
+                         allow_printing=False, 
+                         allow_share_local_folders=False, share_local_folders=[], 
+                         allow_dropbox=False, dropbox_extensions=[], dropbox_action='OPEN',
                          add_to_known_hosts=False, known_hosts=None, 
-                         force=False, **kwargs):
+                         return_object=False, **kwargs):
         """\
         Register a new X2go client session. Within one X2goClient 
         instance you can manage several sessions on serveral
@@ -529,6 +533,9 @@ class X2goClient(object):
             _params['printing'] = printing
             _params['allow_share_local_folders'] = allow_share_local_folders
             _params['share_local_folders'] = share_local_folders
+            _params['allow_dropbox'] = allow_dropbox
+            _params['dropbox_extensions'] = dropbox_extensions
+            _params['dropbox_action'] = dropbox_action
             _params['client_instance'] = self
 
         session_uuid = self.session_registry.register(server=server,
diff --git a/x2go/defaults.py b/x2go/defaults.py
index dd567fe..959d7c0 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -48,10 +48,11 @@ if X2GOCLIENT_OS == "Windows":
     import win32api
     CURRENT_LOCAL_USER = win32api.GetUserName()
     X2GO_SSH_ROOTDIR = '.ssh'
-    SUPPORTED_SOUND = False
+    SUPPORTED_SOUND = True
     SUPPORTED_PRINTING = True
     SUPPORTED_FOLDERSHARING = True
-    
+    SUPPORTED_DROPBOX = True
+
 elif X2GOCLIENT_OS == "Linux":
     ROOT_DIR = '/'
     ETC_DIR = os.path.join(ROOT_DIR, 'etc', 'x2goclient')
@@ -61,6 +62,7 @@ elif X2GOCLIENT_OS == "Linux":
     SUPPORTED_SOUND = True
     SUPPORTED_PRINTING = True
     SUPPORTED_FOLDERSHARING = True
+    SUPPORTED_DROPBOX = True
 
 elif X2GOCLIENT_OS == "Mac":
     ROOT_DIR = '/'
@@ -71,6 +73,7 @@ elif X2GOCLIENT_OS == "Mac":
     SUPPORTED_SOUND = True
     SUPPORTED_PRINTING = True
     SUPPORTED_FOLDERSHARING = True
+    SUPPORTED_DROPBOX = True
 
 else:
     import exceptions
@@ -235,9 +238,8 @@ X2GO_SESSIONPROFILE_DEFAULTS = {
     'speed': 2, 'pack': '16m-jpeg', 'quality': 9, 'link':'ADSL',
     'iconvto': 'UTF-8', 'iconvfrom': 'UTF-8', 'useiconv': False,
     'usesshproxy': False, 'sshproxyhost': '', 'sshproxyuser': '', 'sshproxytunnel': '', 'sshproxykeyfile': '',
-    'useexports': True,
-    'fstunnel': True,
-    'export': '',
+    'useexports': True, 'fstunnel': True, 'export': '',
+    'usedropbox': False, 'dropboxextensions': '', 'dropboxaction': 'OPEN',
     'fullscreen': False,
     'width': 800,'height': 600,'dpi': 96,'setdpi': False,
     'usekbd':True, 'layout': 'us', 'type': 'pc105/us',
@@ -340,3 +342,11 @@ DEFAULT_PDFSAVE_LOCATION = '~/PDF'
 """Default location for saving PDF files (PDFSAVE print action)."""
 DEFAULT_PRINTCMD_CMD = 'lpr'
 """Default command for the PRINTCMD print action."""
+
+X2GO_DROPBOX_ACTIONS = {
+    'OPEN': 'X2goDropboxActionOPEN',
+    'OPENWITH': 'X2goDropboxActionOPENWITH',
+    'SAVEAS': 'X2goDropboxActionSAVEAS',
+}
+"""Relating dropbox action names and classes."""
+
diff --git a/x2go/dropbox.py b/x2go/dropbox.py
new file mode 100644
index 0000000..858fcaa
--- /dev/null
+++ b/x2go/dropbox.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010-2011 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
+#
+# Python X2go is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Python X2go is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""\
+L{X2goDropboxQueue} sets up a thread that listens for incoming files that
+shall be opened locally on the client.
+
+For each file that gets dropped in the drop-box an individual 
+thread is started (L{X2goDropboxJob}) that handles the processing 
+of the incoming file.
+
+"""
+__NAME__ = 'x2godropboxqueue-pylib'
+
+# modules
+import os
+import copy
+import types
+import threading
+import gevent
+
+# Python X2go modules
+import defaults
+import utils
+import log
+import dropboxactions
+
+if defaults.X2GOCLIENT_OS != 'Windows':
+    from x2go_exceptions import WindowsError
+
+
+class X2goDropboxQueue(threading.Thread):
+    """\
+    If the X2go drop-box is supported in a particaluar L{X2goSession} instance
+    this class provides a sub-thread for handling incoming files in the drop-box
+    directory. The actual handling of a dropped file is handled by the classes
+    L{X2goDropboxActionOPEN}, L{X2goDropboxActionOPENWITH} and L{X2goDropboxActionSAVEAS}.
+
+    """
+    dropbox_action = None
+
+    dropbox = None
+    active_jobs = {}
+    dropbox_history = []
+
+    def __init__(self, profile_name='UNKNOWN', session_name='UNKNOWN', 
+                       dropbox_dir=None, dropbox_action=None, dropbox_extensions=[],
+                       client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
+        """\
+        @param profile_name: name of the session profile this print queue belongs to
+        @type profile_name: C{str}
+        @param dropbox_dir: local directory for incoming dropbox files
+        @type dropbox_dir: C{str}
+        @param dropbox_action: name or instance of either of the possible X2go print action classes
+        @type dropbox_action: C{str} or instance
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
+        @param logger: you can pass an L{X2goLogger} object to the
+            L{X2goPrintQueue} constructor
+        @type logger: C{instance}
+        @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+            constructed with the given loglevel
+        @type loglevel: C{int}
+
+        """
+        if logger is None:
+            self.logger = log.X2goLogger(loglevel=loglevel)
+        else:
+            self.logger = copy.deepcopy(logger)
+        self.logger.tag = __NAME__
+
+        self.profile_name = profile_name
+        self.session_name = session_name
+        self.dropbox_dir = dropbox_dir
+        self.dropbox_extensions = dropbox_extensions
+        self.client_instance = client_instance
+        self.client_rootdir = client_instance.get_client_rootdir()
+
+        if dropbox_action is None:
+            dropbox_action = dropbox_actions.X2goDropboxActionOPEN(client_instance=self.client_instance, logger=self.logger, **dropbox_action_args)
+        elif type(dropbox_action) is types.StringType:
+            dropbox_action = self.set_dropbox_action(dropbox_action)
+        else:
+            # hope it's already an instance...
+            self.dropbox_action = dropbox_action
+
+        threading.Thread.__init__(self)
+        self.daemon = True
+        self._accept_jobs = True
+
+    def __del__(self):
+        self.stop_thread()
+
+    def pause(self):
+        """\
+        Prevent acceptance of new incoming files. The processing of dropbox jobs that 
+        are currently still active will be completed, though.
+
+        """
+        if self._accept_jobs == True:
+            self._accept_jobs = False
+            self.logger('paused thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
+
+    def resume(self):
+        """\
+        Resume operation of the X2go dropbox queue and continue accepting new incoming 
+        files.
+
+        """
+        if self._accept_jobs == False:
+            self._accept_jobs = True
+            self.logger('resumed thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
+
+    def stop_thread(self):
+        """\
+        Stops this L{X2goDropboxQueue} thread completely.
+
+        """
+        self.pause()
+        self._keepalive = False
+        self.logger('stopping thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
+
+    @property
+    def _incoming_dropbox_jobs(self):
+        l = os.listdir(self.dropbox_dir)
+        dropbox_jobs = []
+        for _ext in self.dropbox_extensions:
+            dropbox_jobs.extend([ dj for dj in l if dj.upper().endswith(_ext.upper()) ])
+        else:
+            dropbox_jobs = l
+        return [ dj for dj in dropbox_jobs if dj not in self.active_jobs.keys() ]
+
+    def set_dropbox_action(self, dropbox_action, **kwargs):
+        """\
+        Modify the dropbox action of this L{X2goDropboxQueue} thread during runtime. The 
+        change of the dropbox action will be valid for the next incoming file in the dropbox
+        directory.
+
+        """
+        if dropbox_action in defaults.X2GO_DROPBOX_ACTIONS.keys():
+            dropbox_action = defaults.X2GO_DROPBOX_ACTIONS[dropbox_action]
+
+        if dropbox_action in defaults.X2GO_DROPBOX_ACTIONS.values():
+            self.dropbox_action = eval ('dropboxactions.%s(**kwargs)' % dropbox_action)
+
+    def run(self):
+        """\
+        Start this L{X2goDropboxQueue} thread...
+
+        """
+        self.logger('starting dropbox queue thread: %s' % repr(self), loglevel=log.loglevel_DEBUG)
+
+        self._keepalive = True
+        while self._keepalive:
+
+            while self._accept_jobs:
+
+                if self._incoming_dropbox_jobs:
+
+                    for _job in self._incoming_dropbox_jobs:
+                        self.logger('processing incoming X2go dropbox job: %s' % _job, loglevel=log.loglevel_NOTICE)
+                        _new_dropboxjob_thread = X2goDropboxJob(target=x2go_dropboxjob_handler,
+                                                                kwargs={ 
+                                                                  'dropbox_file': _job,
+                                                                  'dropbox_extensions': self.dropbox_extensions,
+                                                                  'dropbox_action': self.dropbox_action,
+                                                                  'parent_thread': self, 
+                                                                  'logger': self.logger, 
+                                                                }
+                                                               )
+                        self.active_jobs['%s' % _job] = _new_dropboxjob_thread
+                        _new_dropboxjob_thread.start()
+
+                gevent.sleep(3)
+
+            gevent.sleep(1)
+
+
+def x2go_dropboxjob_handler(dropbox_file=None, 
+                            dropbox_extensions=[],
+                            dropbox_action=None,
+                            parent_thread=None, logger=None, ):
+    """\
+    This function is called as a handler function for each incoming X2go print job 
+    represented by the class L{X2goPrintJob}.
+
+    @param dropbox_file: PDF file name as placed in to the X2go spool directory
+    @type dropbox_file: C{str}
+    @param dropbox_action: an instance of either of the possible C{X2goPrintActionXXX} classes
+    @type dropbox_action: C{X2goPrintActionXXX} nstance
+    @param parent_thread: the L{X2goPrintQueue} thread that actually created this handler's L{X2goPrintJob} instance
+    @type parent_thread: C{instance}
+    @param logger: the L{X2goPrintQueue}'s logging instance
+    @type logger: C{instance}
+
+    """
+    dropbox_action.profile_name = parent_thread.profile_name
+    dropbox_action.session_name = parent_thread.session_name
+
+    logger('action for printing is: %s' % dropbox_action, loglevel=log.loglevel_DEBUG)
+
+    _really_process = bool((not dropbox_extensions) or [ ext for ext in dropbox_extensions if dropbox_file.upper().endswith('.%s' % ext.upper()) ])
+    if _really_process: 
+        dropbox_action.do_process(dropbox_file=dropbox_file,
+                                  dropbox_dir=parent_thread.dropbox_dir,
+                                 )
+    else:
+        logger('file extension of dropbox file %s is prohibited by session profile configuration' % dropbox_file, loglevel=log.loglevel_WARN)
+
+    logger('removing dropbox file %s' % dropbox_file, loglevel=log.loglevel_DEBUG)
+
+    utils.patiently_remove_file(parent_thread.dropbox_dir, dropbox_file)
+    logger('removed print job file %s' % dropbox_file, loglevel=log.loglevel_DEBUG)
+
+    del parent_thread.active_jobs['%s' % dropbox_file]
+    parent_thread.dropbox_history.append(dropbox_file)
+    # in case we do a lot of dropbox file exports we do not want to risk an
+    # endlessly growing dropbox job history
+    if len(parent_thread.dropbox_history) > 100:
+        parent_thread.dropbox_history = parent_thread.dropbox_history[-100:]
+
+
+class X2goDropboxJob(threading.Thread):
+    """\
+    For each X2go dropbox job we create a sub-thread that let's 
+    the dropbox job be processed in the background.
+
+    As a handler for this class the function L{x2go_dropboxjob_handler()} 
+    is used.
+
+    """
+    def __init__(self, **kwargs):
+        """\
+        Construct the X2go dropbox job thread...
+
+        All parameters (**kwargs) are passed through to the constructor
+        of C{threading.Thread()}.
+
+        """
+        threading.Thread.__init__(self, **kwargs)
+        self.daemon = True
diff --git a/x2go/dropboxactions.py b/x2go/dropboxactions.py
new file mode 100644
index 0000000..2fb149b
--- /dev/null
+++ b/x2go/dropboxactions.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010-2011 by Mike Gabriel <m.gabriel at das-netzwerkteam.de>
+#
+# Python X2go is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Python X2go is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""\
+For dropbox jobs there are currently two handling actions available:
+L{X2goDropboxActionOPEN}, L{X2goDropboxActionOPENWITH} and L{X2goDropboxActionSAVEAS}.
+
+"""
+__NAME__ = 'x2godropboxactions-pylib'
+
+# modules
+import os
+import sys
+import shutil
+import copy
+import types
+import threading
+import time
+
+from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
+if _X2GOCLIENT_OS in ("Windows"):
+    import subprocess
+    import win32api
+else:
+    import gevent_subprocess as subprocess
+
+# Python X2go modules
+import log
+import defaults
+# we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables)
+import utils
+import x2go_exceptions
+
+_DROPBOX_ENV = os.environ.copy()
+
+
+class X2goDropboxAction(object):
+
+    __name__ = 'NAME'
+    __description__ = 'DESCRIPTION'
+
+    def __init__(self, client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
+        """\
+        This is a meta class and has no functionality as such. It is used as parent 
+        class by »real« X2go dropbox actions.
+
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
+        @param logger: you can pass an L{X2goLogger} object to the
+            L{X2goDropboxAction} constructor
+        @type logger: C{instance}
+        @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+            constructed with the given loglevel
+        @type loglevel: C{int}
+
+        """
+        if logger is None:
+            self.logger = log.X2goLogger(loglevel=loglevel)
+        else:
+            self.logger = copy.deepcopy(logger)
+        self.logger.tag = __NAME__
+
+        # these get set from within the X2goDropboxQueue class
+        self.profile_name = 'UNKNOWN'
+        self.session_name = 'UNKNOWN'
+
+        self.client_instance = client_instance
+
+    @property
+    def name():
+        """\
+        Return the X2go dropbox action's name.
+
+        """
+        return self.__name__
+
+    @property
+    def description():
+        """\
+        Return the X2go dropbox action's description text.
+
+        """
+        return self.__description__
+
+    def do_process(self, dropbox_file, dropbox_dir, ):
+        """\
+        Perform the defined dropbox action (doing nothing in L{X2goDropboxAction} parent class).
+
+        @param dropbox_file: file name as placed in to the X2go dropbox directory
+        @type dropbox_file: C{str}
+        @param dropbox_dir: location of the X2go sessions's dropbox directory
+        @type dropbox_dir: C{str}
+
+        """
+        pass
+
+
+class X2goDropboxActionOPEN(X2goDropboxAction):
+    """\
+    Dropbox action that opens incoming files in the default application.
+
+    """
+    __name__= 'OPEN'
+    __decription__= 'Open incoming file with local system\'s default application.'
+
+    def __init__(self, client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
+        """\
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
+        @param logger: you can pass an L{X2goLogger} object to the
+            L{X2goDropboxActionOPEN} constructor
+        @type logger: C{instance}
+        @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+            constructed with the given loglevel
+        @type loglevel: C{int}
+
+        """
+        self.client_instance = client_instance
+        X2goDropboxAction.__init__(self, logger=logger, loglevel=loglevel)
+
+    def do_process(self, dropbox_file, dropbox_dir, ):
+        """\
+        Open an incoming dropbox file in the system's default application.
+
+        @param dropbox_file: file name as placed in to the dropbox directory
+        @type dropbox_file: C{str}
+        @param dropbox_dir: location of the X2go session's dropbox directory
+        @type dropbox_dir: C{str}
+
+        """
+        if _X2GOCLIENT_OS == "Windows":
+            self.logger('opening incoming dropbox file with Python\'s os.startfile() command: %s' % dropbox_file, loglevel=log.loglevel_DEBUG)
+            try:
+                os.startfile(os.path.join(dropbox_dir, dropbox_file))
+            except WindowsError, win_err:
+                if self.client_instance:
+                    self.client_instance.HOOK_dropboxaction_error(dropbox_file,
+                                                                  profile_name=self.profile_name,
+                                                                  session_name=self.session_name,
+                                                                  err_msg=str(win_err)
+                                                                 )
+                else:
+                    self.logger('Encountered WindowsError: %s' % str(win_err), loglevel=log.loglevel_ERROR)
+            time.sleep(20)
+        else:
+            cmd_line = [ 'xdg-open', os.path.join(dropbox_dir, dropbox_file), ]
+            self.logger('opening dropbox file with command: %s' % ' '.join(cmd_line), loglevel=log.loglevel_DEBUG)
+            p = subprocess.Popen(cmd_line, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=_DROPBOX_ENV)
+            time.sleep(20)
+
+
+class X2goDropboxActionOPENWITH(X2goDropboxAction):
+    """\
+    Dropbox action that calls the system's ,,Open with...'' dialog on incoming files. Currently only
+    properly implementable on Windows platforms.
+
+    """
+    __name__= 'OPENWITH'
+    __decription__= 'Evoke ,,Open with...\'\' dialog on incoming dropbox files.'
+
+    def __init__(self, client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
+        """\
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
+        @param logger: you can pass an L{X2goLogger} object to the
+            L{X2goDropboxActionOPENWITH} constructor
+        @type logger: C{instance}
+        @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+            constructed with the given loglevel
+        @type loglevel: C{int}
+
+        """
+        self.client_instance = client_instance
+        X2goDropboxAction.__init__(self, logger=logger, loglevel=loglevel)
+
+    def do_process(self, dropbox_file, dropbox_dir, ):
+        """\
+        Open an incoming dropbox file in the system's default application.
+
+        @param dropbox_file: file name as placed in to the dropbox directory
+        @type dropbox_file: C{str}
+        @param dropbox_dir: location of the X2go session's dropbox directory
+        @type dropbox_dir: C{str}
+
+        """
+        if _X2GOCLIENT_OS == "Windows":
+            self.logger('evoking Open-with dialog on incoming dropbox file: %s' % dropbox_file, loglevel=log.loglevel_DEBUG)
+            win32api.ShellExecute (
+                  0,
+                  "open",
+                  "rundll32.exe",
+                  "shell32.dll,OpenAs_RunDLL %s" % os.path.join(dropbox_dir, dropbox_file),
+                  None,
+                  0,
+            )
+            time.sleep(20)
+        else:
+            self.logger('the evocation of the Open-with dialog box is currently not available on Linux, falling back to dropbox action OPEN', loglevel=log.loglevel_WARN)
+            cmd_line = [ 'xdg-open', os.path.join(dropbox_dir, dropbox_file), ]
+            self.logger('opening dropbox file with command: %s' % ' '.join(cmd_line), loglevel=log.loglevel_DEBUG)
+            p = subprocess.Popen(cmd_line, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=_DROPBOX_ENV)
+            time.sleep(20)
+
+
+class X2goDropboxActionSAVEAS(X2goDropboxAction):
+    """\
+    Dropbox action that allows saving incoming dropbox files to a local folder. What this 
+    dropbox actually does is calling a hook method in the L{X2goClient} instance that
+    can be hi-jacked by one of your application's methods which then can handle the ,,Save as...''
+    request.
+
+    """
+    __name__ = 'SAVEAS'
+    __decription__= 'Save incoming file as...'
+
+    def __init__(self, client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
+        """\
+        @param client_instance: an L{X2goClient} instance, within your customized L{X2goClient} make sure 
+            you have a C{HOOK_open_dropbox_saveas_dialog(filename=<str>)} method defined that will actually
+            handle the incoming dropbox file.
+        @type client_instance: C{instance}
+        @param logger: you can pass an L{X2goLogger} object to the
+            L{X2goDropboxActionSAVEAS} constructor
+        @type logger: C{instance}
+        @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
+            constructed with the given loglevel
+        @type loglevel: C{int}
+
+        """
+        if client_instance is None:
+            raise x2go_exceptions.X2goDropboxActionException('the SAVEAS dropbox action needs to know the X2goClient instance (client=<instance>)')
+        X2goDropboxAction.__init__(self, client_instance=client_instance, logger=logger, loglevel=loglevel)
+
+    def do_process(self, dropbox_file, dropbox_dir):
+        """\
+        Call an L{X2goClient} hook method (C{HOOK_open_dropbox_saveas_dialog}) that
+        can handle the dropbox's SAVEAS action.
+
+        @param dropbox_file: file name as placed in to the dropbox directory
+        @type dropbox_file: C{str}
+        @param dropbox_dir: location of the X2go session's dropbox directory
+        @type dropbox_dir: C{str}
+        @param dropbox_file: PDF file name as placed in to the X2go spool directory
+
+        """
+        self.logger('Session %s (%s) is calling X2goClient class hook method <client_instance>.HOOK_open_dropbox_saveas_dialog(%s)' % (self.session_name, self.profile_name, self.dropbox_file), loglevel=log.loglevel_NOTICE)
+        self.client_instance.HOOK_open_dropbox_saveas_dialog(os.path.join(dropbox_dir, dropbox_file), profile_name=self.profile_name, session_name=self.session_name)
+        time.sleep(60)
+
diff --git a/x2go/printactions.py b/x2go/printactions.py
index 4b8c66b..3f084e5 100644
--- a/x2go/printactions.py
+++ b/x2go/printactions.py
@@ -59,11 +59,13 @@ class X2goPrintAction(object):
     __name__ = 'NAME'
     __description__ = 'DESCRIPTION'
 
-    def __init__(self, logger=None, loglevel=log.loglevel_DEFAULT):
+    def __init__(self, client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
         """\
-        This is a meta class has no functionality. It is used as parent class by »real«
-        X2go print actions.
+        This is a meta class and has no functionality as such. It is used as parent 
+        class by »real« X2go print actions.
 
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
         @param logger: you can pass an L{X2goLogger} object to the
             L{X2goPrintAction} constructor
         @type logger: C{instance}
@@ -82,6 +84,8 @@ class X2goPrintAction(object):
         self.profile_name = 'UNKNOWN'
         self.session_name = 'UNKNOWN'
 
+        self.client_instance = client_instance
+
     @property
     def name():
         """\
@@ -139,6 +143,8 @@ class X2goPrintActionPDFVIEW(X2goPrintAction):
 
     def __init__(self, client_instance=None, pdfview_cmd=None, logger=None, loglevel=log.loglevel_DEFAULT):
         """\
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
         @param pdfview_cmd: command that starts the external PDF viewer application
         @type pdfview_cmd: C{str}
         @param logger: you can pass an L{X2goLogger} object to the
@@ -152,8 +158,7 @@ class X2goPrintActionPDFVIEW(X2goPrintAction):
         if pdfview_cmd is None:
             pdfview_cmd = defaults.DEFAULT_PDFVIEW_CMD
         self.pdfview_cmd = pdfview_cmd
-        self.client_instance = client_instance
-        X2goPrintAction.__init__(self, logger=logger, loglevel=loglevel)
+        X2goPrintAction.__init__(self, client_instance=client_instance, logger=logger, loglevel=loglevel)
 
     def do_print(self, pdf_file, job_title, spool_dir, ):
         """\
@@ -204,6 +209,8 @@ class X2goPrintActionPDFSAVE(X2goPrintAction):
 
     def __init__(self, client_instance=None, save_to_folder=None, logger=None, loglevel=log.loglevel_DEFAULT):
         """\
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
         @param save_to_folder: saving location for incoming print jobs (PDF files)
         @type save_to_folder: C{str}
         @param logger: you can pass an L{X2goLogger} object to the
@@ -217,8 +224,7 @@ class X2goPrintActionPDFSAVE(X2goPrintAction):
         if save_to_folder is None:
             save_to_folder = os.path.expanduser(defaults.DEFAULT_PDFSAVE_LOCATION)
         self.save_to_folder = save_to_folder
-        self.client_instance = client_instance
-        X2goPrintAction.__init__(self, logger=None, loglevel=loglevel)
+        X2goPrintAction.__init__(self, client_instance=client_instance, logger=None, loglevel=loglevel)
 
     def do_print(self, pdf_file, job_title, spool_dir):
         """\
@@ -252,10 +258,12 @@ class X2goPrintActionPRINT(X2goPrintAction):
 
     def __init__(self, client_instance=None, printer=None, logger=None, loglevel=log.loglevel_DEFAULT):
         """\
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
         @param printer: name of the preferred printer, if C{None} the system's/user's default printer will be used
         @type printer: C{str}
         @param logger: you can pass an L{X2goLogger} object to the
-            L{X2goPrintAction} constructor
+            L{X2goPrintActionPRINT} constructor
         @type logger: C{instance}
         @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
             constructed with the given loglevel
@@ -263,8 +271,7 @@ class X2goPrintActionPRINT(X2goPrintAction):
 
         """
         self.printer = printer
-        self.client_instance = client_instance
-        X2goPrintAction.__init__(self, logger=logger, loglevel=loglevel)
+        X2goPrintAction.__init__(self, client_instance=client_instance, logger=logger, loglevel=loglevel)
 
     def do_print(self, pdf_file, job_title, spool_dir, ):
         """\
@@ -331,10 +338,12 @@ class X2goPrintActionPRINTCMD(X2goPrintAction):
 
     def __init__(self, client_instance=None, print_cmd=None, logger=None, loglevel=log.loglevel_DEFAULT):
         """\
+        @param client_instance: the underlying L{X2goClient} instance
+        @type client_instance: C{instance}
         @param print_cmd: external command to be called on incoming print jobs
         @type print_cmd: C{str}
         @param logger: you can pass an L{X2goLogger} object to the
-            L{X2goPrintAction} constructor
+            L{X2goPrintActionPRINTCMD} constructor
         @type logger: C{instance}
         @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
             constructed with the given loglevel
@@ -343,9 +352,8 @@ class X2goPrintActionPRINTCMD(X2goPrintAction):
         """
         if print_cmd is None:
             print_cmd = defaults.DEFAULT_PRINTCMD_CMD
-        self.client_instance = client_instance
         self.print_cmd = print_cmd
-        X2goPrintAction.__init__(self, logger=logger, loglevel=loglevel)
+        X2goPrintAction.__init__(self, client_instance=client_instance, logger=logger, loglevel=loglevel)
 
     def do_print(self, pdf_file, job_title, spool_dir):
         """\
@@ -392,18 +400,16 @@ class X2goPrintActionDIALOG(X2goPrintAction):
             open the print dialog.
         @type client_instance: C{instance}
         @param logger: you can pass an L{X2goLogger} object to the
-            L{X2goPrintAction} constructor
+            L{X2goPrintActionDIALOG} constructor
         @type logger: C{instance}
         @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
             constructed with the given loglevel
         @type loglevel: C{int}
 
         """
-        if client_instance is not None:
-            self.client_instance = client_instance
-        else:
-            raise x2go_exceptions.X2goPrintActionException('the DIALOG print actions needs to know the X2goClient instance (client=<instance>)')
-        X2goPrintAction.__init__(self, logger=logger, loglevel=loglevel)
+        if client_instance is None:
+            raise x2go_exceptions.X2goPrintActionException('the DIALOG print action needs to know the X2goClient instance (client=<instance>)')
+        X2goPrintAction.__init__(self, client_instance=client_instance, logger=logger, loglevel=loglevel)
 
     def do_print(self, pdf_file, job_title, spool_dir):
         """\
diff --git a/x2go/printqueue.py b/x2go/printqueue.py
index 647f879..f479760 100644
--- a/x2go/printqueue.py
+++ b/x2go/printqueue.py
@@ -29,7 +29,8 @@ of the incoming print job.
 __NAME__ = 'x2goprintqueue-pylib'
 
 # modules
-import os, copy
+import os
+import copy
 import threading
 import gevent
 
@@ -37,6 +38,8 @@ import gevent
 import defaults
 import utils
 import log
+import printactions
+
 # we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables)
 from backends.printing import X2goClientPrinting as _X2goClientPrinting
 
@@ -159,7 +162,7 @@ class X2goPrintQueue(threading.Thread):
             print_action = defaults.X2GO_PRINT_ACTIONS[print_action]
 
         if print_action in defaults.X2GO_PRINT_ACTIONS.values():
-            self.print_action = eval ('%s(**kwargs)' % print_action)
+            self.print_action = eval ('printactions.%s(**kwargs)' % print_action)
 
     def run(self):
         """\
diff --git a/x2go/registry.py b/x2go/registry.py
index 3f0a423..6ba4e6b 100644
--- a/x2go/registry.py
+++ b/x2go/registry.py
@@ -283,19 +283,7 @@ class X2goSessionRegistry(object):
             session_uuid = _virgin_sessions[0].get_uuid()
             _params = self.client_instance.session_profiles.to_session_params(profile_id)
 
-            try: del _params['server'] 
-            except: pass
-            try: del _params['printing'] 
-            except: pass
-            try: del _params['share_local_folders'] 
-            except: pass
-            try: del _params['profile_name']
-            except: pass
-            try: del _params['profile_id'] 
-            except: pass
-
             self(session_uuid).update_params(_params)
-
             self.logger('using already initially-registered yet-unused session %s' % session_uuid, log.loglevel_NOTICE)
             return session_uuid
 
@@ -303,17 +291,6 @@ class X2goSessionRegistry(object):
             session_uuid = self.get_session_of_session_name(session_name)
             _params = self.client_instance.session_profiles.to_session_params(profile_id)
 
-            try: del _params['server'] 
-            except: pass
-            try: del _params['printing'] 
-            except: pass
-            try: del _params['share_local_folders'] 
-            except: pass
-            try: del _params['profile_name']
-            except: pass
-            try: del _params['profile_id'] 
-            except: pass
-
             self(session_uuid).update_params(_params)
             self.logger('using already registered-by-session-name session %s' % session_uuid, log.loglevel_NOTICE)
             return session_uuid
diff --git a/x2go/session.py b/x2go/session.py
index 5356acb..64937a2 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -49,7 +49,7 @@ from defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
 from defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR
 from defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR
 
-from defaults import SUPPORTED_SOUND, SUPPORTED_PRINTING, SUPPORTED_FOLDERSHARING
+from defaults import SUPPORTED_SOUND, SUPPORTED_PRINTING, SUPPORTED_FOLDERSHARING, SUPPORTED_DROPBOX
 
 # options of the paramiko.SSHClient().connect()
 _X2GO_SESSION_PARAMS = ('geometry', 'depth', 'link', 'pack',
@@ -77,6 +77,9 @@ class X2goSession(object):
                  profile_id=None, profile_name='UNKNOWN',
                  session_name=None,
                  printing=False,
+                 allow_dropbox=False,
+                 dropbox_extensions=[],
+                 dropbox_action='OPEN',
                  allow_share_local_folders=False,
                  share_local_folders=[],
                  control_backend=_X2goControlSession,
@@ -129,6 +132,9 @@ class X2goSession(object):
         self.printing = printing
         self.allow_share_local_folders = allow_share_local_folders
         self.share_local_folders = share_local_folders
+        self.allow_dropbox = allow_dropbox
+        self.dropbox_extensions = dropbox_extensions
+        self.dropbox_action = dropbox_action
         self._control_backend = control_backend
         self._terminal_backend = terminal_backend
         self._info_backend = info_backend
@@ -237,6 +243,27 @@ class X2goSession(object):
         STILL UNDOCUMENTED
 
         """
+        try: del params['server'] 
+        except KeyError: pass
+        try: del params['profile_name']
+        except KeyError: pass
+        try: del params['profile_id'] 
+        except KeyError: pass
+        try: del params['printing'] 
+        except KeyError: pass
+        try: del params['allow_share_local_folders']
+        except KeyError: pass
+        try: del params['share_local_folders'] 
+        except KeyError: pass
+        try: del params['allow_dropbox']
+        except KeyError: pass
+        try: del params['dropbox_extensions']
+        except KeyError: pass
+        try: del params['dropbox_action']
+        except KeyError: pass
+        try: del params['use_sshproxy']
+        except KeyError: pass
+
         _terminal_params = copy.deepcopy(params)
         _control_params = copy.deepcopy(params)
         _sshproxy_params = copy.deepcopy(params)
@@ -250,10 +277,6 @@ class X2goSession(object):
             else:
                 del _sshproxy_params[p]
                 del _terminal_params[p]
-        try: del _sshproxy_params['use_sshproxy']
-        except KeyError: pass
-        try: del _control_params['allow_share_local_folders']
-        except KeyError: pass
 
         self.control_params.update(_control_params)
         self.terminal_params.update(_terminal_params)
@@ -563,7 +586,9 @@ class X2goSession(object):
                     _terminal.start_sound()
 
 
-                if (SUPPORTED_PRINTING and self.printing) or (SUPPORTED_FOLDERSHARING and self.allow_share_local_folders and self.share_local_folders):
+                if (SUPPORTED_PRINTING and self.printing) or \
+                   (SUPPORTED_DROPBOX and self.allow_dropbox) or \
+                   (SUPPORTED_FOLDERSHARING and self.allow_share_local_folders and self.share_local_folders):
                     _terminal.start_sshfs()
 
                 try:
@@ -572,6 +597,9 @@ class X2goSession(object):
                 except X2goUserException:
                     pass
 
+                if SUPPORTED_DROPBOX and self.allow_dropbox:
+                        _terminal.start_dropbox(dropbox_extensions=self.dropbox_extensions, dropbox_action=self.dropbox_action)
+
                 if SUPPORTED_FOLDERSHARING and self.share_local_folders:
                     if _control.get_transport().reverse_tunnels[_terminal.get_session_name()]['sshfs'][1] is not None:
                         for _folder in self.share_local_folders:
diff --git a/x2go/utils.py b/x2go/utils.py
index 37eca39..389127c 100644
--- a/x2go/utils.py
+++ b/x2go/utils.py
@@ -33,6 +33,7 @@ import paramiko
 # Python X2go modules
 from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
 from defaults import X2GO_SESSIONPROFILE_DEFAULTS as _X2GO_SESSIONPROFILE_DEFAULTS
+from defaults import X2GO_DROPBOX_ACTIONS as _X2GO_DROPBOX_ACTIONS
 from defaults import _pack_methods_nx3
 
 def is_in_nx3packmethods(method):
@@ -126,6 +127,9 @@ def _convert_SessionProfileOptions_2_SessionParams(_options):
             'sshport': 'port',
             'useexports': 'allow_share_local_folders',
             'export': 'share_local_folders',
+            'usedropbox': 'allow_dropbox',
+            'dropboxextensions': 'dropbox_extensions',
+            'dropboxaction': 'dropbox_action',
             'print': 'printing',
             'name': 'profile_name',
             'key': 'key_filename',
@@ -168,18 +172,12 @@ def _convert_SessionProfileOptions_2_SessionParams(_options):
                 _params['link'] = val
 
             # share_local_folders is a list
-            if opt == 'share_local_folders':
+            if opt in ('share_local_folders', 'dropbox_extensions'):
                 if type(val) is types.StringType:
                     if val:
-                        _params[opt] = _params[opt].split(',')
+                        _params[opt] = val.split(',')
                     else:
                         _params[opt] = []
-                    #del _params['export']
-                    if not _options['fstunnel']:
-                        _params[opt] = None
-
-            if not val:
-                val = None
 
         # append value for quality to value for pack method
         if _params['quality']:
@@ -206,6 +204,9 @@ def _convert_SessionProfileOptions_2_SessionParams(_options):
             _params['session_type'] = 'desktop'
         del _params['rootless']
 
+        if _params['dropbox_action'] not in _X2GO_DROPBOX_ACTIONS.keys():
+            _params['dropbox_action'] = 'OPEN'
+
         # currently known but ignored in Python X2go
         _ignored_options = [
             'dpi',


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