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

X2Go dev team git-admin at x2go.org
Wed Jan 8 15:25:36 CET 2014


The branch, build-baikal 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