def get_local_client(self, path):
     if AbstractOSIntegration.is_mac() and (
                 self._testMethodName == 'test_local_delete_readonly_folder' or
                 self._testMethodName == 'test_local_rename_readonly_folder'):
         return LocalClient(path)
     # Old mac dont handle case rename
     if AbstractOSIntegration.is_mac() and AbstractOSIntegration.os_version_below("10.10") and (
                 self._testMethodName == 'test_local_rename_file_uppercase_stopped' or
                 self._testMethodName == 'test_local_rename_file_uppercase'):
         return LocalClient(path)
     return super(TestLocalMoveAndRename, self).get_local_client(path)
 def get_local_client(self, path):
     if AbstractOSIntegration.is_mac() and (
             self._testMethodName == 'test_local_delete_readonly_folder' or
             self._testMethodName == 'test_local_rename_readonly_folder'):
         return LocalClient(path)
     # Old mac dont handle case rename
     if AbstractOSIntegration.is_mac(
     ) and AbstractOSIntegration.os_version_below("10.10") and (
             self._testMethodName
             == 'test_local_rename_file_uppercase_stopped'
             or self._testMethodName == 'test_local_rename_file_uppercase'):
         return LocalClient(path)
     return super(TestLocalMoveAndRename, self).get_local_client(path)
    def test_finder_in_use(self):
        if not AbstractOSIntegration.is_mac():
            raise SkipTest('Not relevant on Linux or Windows')
        self.engine_1.start()
        self.wait_sync(wait_for_async=True)
        self.local_client_1.make_file('/', u'File.txt',
                                      content=u'Some Content 1'.encode('utf-8'))

        # Emulate the Finder in use flag
        OSX_FINDER_INFO_ENTRY_SIZE = 32
        key = (OSX_FINDER_INFO_ENTRY_SIZE)*[0]
        key[0] = 0x62
        key[1] = 0x72
        key[2] = 0x6F
        key[3] = 0x6B
        key[4] = 0x4D
        key[5] = 0x41
        key[6] = 0x43
        key[7] = 0x53
        import xattr
        xattr.setxattr(self.local_client_1._abspath(u'/File.txt'), xattr.XATTR_FINDERINFO_NAME, bytes(bytearray(key)))

        # The file should not be synced and there have no remote id
        self.wait_sync(wait_for_async=True, fail_if_timeout=False, timeout=10)
        info = self.local_client_1.get_remote_id(u'/File.txt')
        self.assertIsNone(info)

        # Remove the Finder flag
        self.local_client_1.remove_remote_id(u'/File.txt', xattr.XATTR_FINDERINFO_NAME)

        # The sync process should now handle the file and sync it
        self.wait_sync(wait_for_async=True, fail_if_timeout=False, timeout=10)
        info = self.local_client_1.get_remote_id(u'/File.txt')
        self.assertIsNotNone(info)
Ejemplo n.º 4
0
 def remove_remote_id(self, ref, name='ndrive'):
     # Can be move to another class
     path = self._abspath(ref)
     log.trace('Removing xattr %s from %s', name, path)
     locker = self.unlock_path(path, False)
     if AbstractOSIntegration.is_windows():
         pathAlt = path + ":" + name
         try:
             if os.path.exists(pathAlt):
                 os.remove(pathAlt)
         except WindowsError as e:
             if e.errno == os.errno.EACCES:
                 self.unset_path_readonly(path)
                 os.remove(pathAlt)
                 self.set_path_readonly(path)
             else:
                 raise e
         finally:
             self.lock_path(path, locker)
     else:
         try:
             import xattr
             if AbstractOSIntegration.is_mac():
                 xattr.removexattr(path, name)
             else:
                 xattr.removexattr(path, 'user.' + name)
         except IOError as e:
             # Ignore IOError: [Errno 93] Attribute not found ( Mac )
             # IOError: [Errno 61] No data available ( Linux )
             if e.errno == 93 or e.errno == 61:
                 pass
             else:
                 raise
         finally:
             self.lock_path(path, locker)
Ejemplo n.º 5
0
    def __init__(self, root, path, folderish, last_modification_time, size=0,
                 digest_func='md5', check_suspended=None, remote_ref=None):

        # Function to check during long-running processing like digest
        # computation if the synchronization thread needs to be suspended
        self.check_suspended = check_suspended
        self.size = size
        filepath = os.path.join(root, path[1:].replace(u'/', os.path.sep))
        root = unicodedata.normalize('NFC', root)
        path = unicodedata.normalize('NFC', path)
        normalized_filepath = os.path.join(root, path[1:].replace(u'/', os.path.sep))
        self.filepath = normalized_filepath

        # Normalize name on the file system if not normalized
        # See https://jira.nuxeo.com/browse/NXDRIVE-188
        if os.path.exists(filepath) and normalized_filepath != filepath and not AbstractOSIntegration.is_mac():
            log.debug('Forcing normalization of %r to %r', filepath, normalized_filepath)
            os.rename(filepath, normalized_filepath)

        self.root = root  # the sync root folder local path
        self.path = path  # the truncated path (under the root)
        self.folderish = folderish  # True if a Folder
        self.remote_ref = remote_ref

        # Last OS modification date of the file
        self.last_modification_time = last_modification_time

        # Function to use
        self._digest_func = digest_func.lower()

        # Precompute base name once and for all are it's often useful in
        # practice
        self.name = os.path.basename(path)
Ejemplo n.º 6
0
 def remove_remote_id(self, ref, name='ndrive'):
     # Can be move to another class
     path = self._abspath(ref)
     log.trace('Removing xattr %s from %s', name, path)
     locker = self.unlock_path(path, False)
     if AbstractOSIntegration.is_windows():
         pathAlt = path + ":" + name
         try:
             os.remove(pathAlt)
         except WindowsError as e:
             if e.errno == os.errno.EACCES:
                 self.unset_path_readonly(path)
                 os.remove(pathAlt)
                 self.set_path_readonly(path)
             else:
                 raise e
         finally:
             self.lock_path(path, locker)
     else:
         try:
             import xattr
             if AbstractOSIntegration.is_mac():
                 xattr.removexattr(path, name)
             else:
                 xattr.removexattr(path, 'user.' + name)
         finally:
             self.lock_path(path, locker)
Ejemplo n.º 7
0
class DarwinTest(unittest.TestCase):
    if AbstractOSIntegration.is_mac() and not AbstractOSIntegration.os_version_below("10.10"):
        def test_folder_registration(self):

            name = "TestCazz"

            # Unregister first; to ensure favorite bar is cleaned.
            os = AbstractOSIntegration.get(None)
            os.unregister_folder_link(name)
            self.assertFalse(self._is_folder_registered(name))

            os.register_folder_link(".", name)
            self.assertTrue(self._is_folder_registered(name))

            os.unregister_folder_link(name)
            self.assertFalse(self._is_folder_registered(name))

            assert 1

        # Others test can be written here.

        # Utils methods
        def _is_folder_registered(self, name):

            os = AbstractOSIntegration.get(None)
            lst = os._get_favorite_list()
            return os._find_item_in_list(lst, name) is not None

    else:
        @unittest.skip('Skipped test suite on another system than OSX.')
        def test_darwin(self):
            assert 0
Ejemplo n.º 8
0
 def _handle_watchdog_event_on_known_acquired_pair(self, doc_pair, evt, rel_path):
     if evt.event_type == 'deleted':
         # Delay on Windows the delete event
         if self._windows:
             self._win_lock.acquire()
             log.debug('Add pair to delete events: %r', doc_pair)
             try:
                 self._delete_events[doc_pair.remote_ref] = (current_milli_time(), doc_pair)
             finally:
                 self._win_lock.release()
         else:
             # In case of case sensitive can be an issue
             if self.client.exists(doc_pair.local_path):
                 remote_id = self.client.get_remote_id(doc_pair.local_path)
                 if remote_id == doc_pair.remote_ref or remote_id is None:
                     # This happens on update don't do anything
                     return
             self._handle_watchdog_delete(doc_pair)
         return
     if evt.event_type == 'created':
         # NXDRIVE-471 case maybe
         remote_ref = self.client.get_remote_id(rel_path)
         if remote_ref is None:
             log.debug("Created event on a known pair with no remote_ref,"
                       " this should only happen in case of a quick move and copy-paste: %r", doc_pair)
             return
         else:
             # NXDRIVE-509
             log.debug("Created event on a known pair with a remote_ref: %r", doc_pair)
     local_info = self.client.get_info(rel_path, raise_if_missing=False)
     if local_info is not None:
         # Unchanged folder
         if doc_pair.folderish:
             log.debug('Unchanged folder %s (watchdog event [%s]), only update last_local_updated',
                       rel_path, evt.event_type)
             self._dao.update_local_modification_time(doc_pair, local_info)
             return
         if doc_pair.local_state == 'synchronized':
             digest = local_info.get_digest()
             # Unchanged digest, can be the case if only the last modification time or file permissions
             # have been updated
             if doc_pair.local_digest == digest:
                 log.debug('Digest has not changed for %s (watchdog event [%s]), only update last_local_updated',
                           rel_path, evt.event_type)
                 if local_info.remote_ref is None:
                     self.client.set_remote_id(rel_path, doc_pair.remote_ref)
                 self._dao.update_local_modification_time(doc_pair, local_info)
                 return
             doc_pair.local_digest = digest
             doc_pair.local_state = 'modified'
         if AbstractOSIntegration.is_mac() and evt.event_type == 'modified' and doc_pair.remote_ref is not None and doc_pair.remote_ref != local_info.remote_ref:
             original_pair = self._dao.get_normal_state_from_remote(local_info.remote_ref)
             original_info = None
             if original_pair is not None:
                 original_info = self.client.get_info(original_pair.local_path, raise_if_missing=False)
             if original_info is not None and original_info.remote_ref == local_info.remote_ref:
                 log.debug("MacOSX has postponed overwriting of xattr, need to reset remote_ref for %r", doc_pair)
                 # We are in a copy/paste situation with OS overriding the xattribute
                 self.client.set_remote_id(doc_pair.local_path, doc_pair.remote_ref)
         self._dao.update_local_state(doc_pair, local_info)
Ejemplo n.º 9
0
 def _new_notification(self, notification):
     if not notification.is_bubble():
         return
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_above("10.8"):
             from nxdrive.osi.darwin.pyNotificationCenter import notify, NotificationDelegator
             if self._delegator is None:
                 self._delegator = NotificationDelegator.alloc().init()
                 self._delegator._manager = self.manager
             # Use notification center
             userInfo = dict()
             userInfo["uuid"] = notification.get_uid()
             return notify(notification.get_title(),
                           None,
                           notification.get_description(),
                           userInfo=userInfo)
     self.current_notification = notification
     icon = QtGui.QSystemTrayIcon.Information
     if (notification.get_level() == Notification.LEVEL_WARNING):
         icon = QtGui.QSystemTrayIcon.Warning
     elif (notification.get_level() == Notification.LEVEL_ERROR):
         icon = QtGui.QSystemTrayIcon.Critical
     self.show_message(notification.get_title(),
                       notification.get_description(),
                       icon=icon)
Ejemplo n.º 10
0
 def set_folder_icon(self, ref, icon):
     if icon is None:
         return
     if AbstractOSIntegration.is_windows():
         self.set_folder_icon_win32(ref, icon)
     elif AbstractOSIntegration.is_mac():
         self.set_folder_icon_darwin(ref, icon)
Ejemplo n.º 11
0
 def remove_remote_id(self, ref, name='ndrive'):
     # Can be move to another class
     path = self._abspath(ref)
     log.trace('Removing xattr %s from %s', name, path)
     locker = self.unlock_path(path, False)
     if AbstractOSIntegration.is_windows():
         pathAlt = path + ":" + name
         try:
             if os.path.exists(pathAlt):
                 os.remove(pathAlt)
         except WindowsError as e:
             if e.errno == os.errno.EACCES:
                 self.unset_path_readonly(path)
                 os.remove(pathAlt)
                 self.set_path_readonly(path)
             else:
                 raise e
         finally:
             self.lock_path(path, locker)
     else:
         try:
             import xattr
             if AbstractOSIntegration.is_mac():
                 xattr.removexattr(path, name)
             else:
                 xattr.removexattr(path, 'user.' + name)
         except IOError as e:
             # Ignore IOError: [Errno 93] Attribute not found ( Mac )
             # IOError: [Errno 61] No data available ( Linux )
             if e.errno == 93 or e.errno == 61:
                 pass
             else:
                 raise
         finally:
             self.lock_path(path, locker)
Ejemplo n.º 12
0
 def showMessage(self, title, message, icon=QtGui.QSystemTrayIcon.Information, timeout = 10000):
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_above("10.8"):
             from nxdrive.osi.darwin.pyNotificationCenter import notify
             # Use notification center
             return notify(title, None, message)
     return QtGui.QSystemTrayIcon.showMessage(self, title, message, icon, timeout)
Ejemplo n.º 13
0
 def set_folder_icon(self, ref, icon):
     if icon is None:
         return
     if AbstractOSIntegration.is_windows():
         self.set_folder_icon_win32(ref, icon)
     elif AbstractOSIntegration.is_mac():
         self.set_folder_icon_darwin(ref, icon)
Ejemplo n.º 14
0
 def get_local_client(self, path):
     if AbstractOSIntegration.is_windows():
         from tests.win_local_client import WindowsLocalClient
         return WindowsLocalClient(path)
     if AbstractOSIntegration.is_mac():
         from tests.mac_local_client import MacLocalClient
         return MacLocalClient(path)
     return LocalClient(path)
Ejemplo n.º 15
0
 def get_local_client(self, path):
     if AbstractOSIntegration.is_windows():
         from nxdrive.tests.win_local_client import WindowsLocalClient
         return WindowsLocalClient(path)
     if AbstractOSIntegration.is_mac():
         from nxdrive.tests.mac_local_client import MacLocalClient
         return MacLocalClient(path)
     return LocalClient(path)
Ejemplo n.º 16
0
 def showMessage(self, title, message, icon=QtGui.QSystemTrayIcon.Information, timeout = 10000):
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_above("10.8"):
             from nxdrive.osi.darwin.pyNotificationCenter import notify
             # Use notification center
             log.trace("Display systray message: %s | %s | %s", QtCore.QCoreApplication.applicationName(), title, message)
             return notify(title, None, message)
     return QtGui.QSystemTrayIcon.showMessage(self, title, message, icon, timeout)
Ejemplo n.º 17
0
 def has_folder_icon(self, ref):
     target_folder = self._abspath(ref)
     if AbstractOSIntegration.is_mac():
         meta_file = os.path.join(target_folder, "Icon\r")
         return os.path.exists(meta_file)
     if AbstractOSIntegration.is_windows():
         meta_file = os.path.join(target_folder, "desktop.ini")
         return os.path.exists(meta_file)
     return False
Ejemplo n.º 18
0
 def has_folder_icon(self, ref):
     target_folder = self._abspath(ref)
     if AbstractOSIntegration.is_mac():
         meta_file = os.path.join(target_folder, "Icon\r")
         return os.path.exists(meta_file)
     if AbstractOSIntegration.is_windows():
         meta_file = os.path.join(target_folder, "desktop.ini")
         return os.path.exists(meta_file)
     return False
Ejemplo n.º 19
0
 def unset_folder_icon(self, ref):
     '''
         Unset the red icon
     '''
     if AbstractOSIntegration.is_windows():
         # TODO Clean version
         desktop_ini_file_path = os.path.join(self._abspath(ref), "desktop.ini")
     if AbstractOSIntegration.is_mac():
         desktop_ini_file_path = os.path.join(self._abspath(ref), "Icon\r")
     if os.path.exists(desktop_ini_file_path):
         os.remove(desktop_ini_file_path)
Ejemplo n.º 20
0
    def __init__(self, manager, options, argv=()):
        super(Application, self).__init__(list(argv))
        self.setApplicationName(manager.get_appname())
        self.setQuitOnLastWindowClosed(False)
        self._delegator = None
        self.manager = manager
        from nxdrive.scripting import DriveUiScript
        self.manager.set_script_object(DriveUiScript(manager, self))
        self.options = options
        self.mainEngine = None
        self.filters_dlg = None
        self._conflicts_modals = dict()
        self.current_notification = None
        # Make dialog unique
        self.uniqueDialogs = dict()
        # Init translator
        self._init_translator()

        for _, engine in self.manager.get_engines().iteritems():
            self.mainEngine = engine
            break
        if self.mainEngine is not None and options.debug:
            from nxdrive.engine.engine import EngineLogger
            self.engineLogger = EngineLogger(self.mainEngine)
        self.engineWidget = None

        self.aboutToQuit.connect(self.manager.stop)
        self.manager.dropEngine.connect(self.dropped_engine)

        # Timer to spin the transferring icon
        self.icon_spin_timer = QtCore.QTimer()
        self.icon_spin_timer.timeout.connect(self.spin_transferring_icon)
        self.icon_spin_count = 0

        # Application update
        self.manager.get_updater().appUpdated.connect(self.app_updated)
        self.updated_version = None

        # This is a windowless application mostly using the system tray
        self.setQuitOnLastWindowClosed(False)

        self.setup_systray()

        # Direct Edit conflict
        self.manager.get_drive_edit().driveEditConflict.connect(self._direct_edit_conflict)

        # Check if actions is required, separate method so it can be override
        self.init_checks()
        self.engineWidget = None

        # Setup notification center for Mac
        if AbstractOSIntegration.is_mac():
            if AbstractOSIntegration.os_version_above("10.8"):
                self._setup_notification_center()
Ejemplo n.º 21
0
 def get_creation_time(self, child_full_path):
     if self._windows:
         return os.path.getctime(child_full_path)
     else:
         stat = os.stat(child_full_path)
         # Try inode number as on HFS seems to be increasing
         if AbstractOSIntegration.is_mac() and hasattr(stat, "st_ino"):
             return stat.st_ino
         if hasattr(stat, "st_birthtime"):
             return stat.st_birthtime
         return 0
Ejemplo n.º 22
0
 def unset_folder_icon(self, ref):
     '''
         Unset the red icon
     '''
     if AbstractOSIntegration.is_windows():
         # TODO Clean version
         desktop_ini_file_path = os.path.join(self._abspath(ref),
                                              "desktop.ini")
     if AbstractOSIntegration.is_mac():
         desktop_ini_file_path = os.path.join(self._abspath(ref), "Icon\r")
     if os.path.exists(desktop_ini_file_path):
         os.remove(desktop_ini_file_path)
Ejemplo n.º 23
0
 def showMessage(self,
                 title,
                 message,
                 icon=QtGui.QSystemTrayIcon.Information,
                 timeout=10000):
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_above("10.8"):
             from nxdrive.osi.darwin.pyNotificationCenter import notify
             # Use notification center
             return notify(title, None, message)
     return QtGui.QSystemTrayIcon.showMessage(self, title, message, icon,
                                              timeout)
Ejemplo n.º 24
0
    def __init__(self, manager, options, argv=()):
        super(Application, self).__init__(manager, options, list(argv))
        self.setQuitOnLastWindowClosed(False)
        self._delegator = None
        from nxdrive.scripting import DriveUiScript
        self.manager.set_script_object(DriveUiScript(manager, self))
        self.mainEngine = None
        self.filters_dlg = None
        self._conflicts_modals = dict()
        self.current_notification = None
        # Make dialog unique
        self.uniqueDialogs = dict()

        for _, engine in self.manager.get_engines().iteritems():
            self.mainEngine = engine
            break
        if self.mainEngine is not None and options.debug:
            from nxdrive.engine.engine import EngineLogger
            self.engineLogger = EngineLogger(self.mainEngine)
        self.engineWidget = None

        self.aboutToQuit.connect(self.manager.stop)
        self.manager.dropEngine.connect(self.dropped_engine)

        # Timer to spin the transferring icon
        self.icon_spin_timer = QtCore.QTimer()
        self.icon_spin_timer.timeout.connect(self.spin_transferring_icon)
        self.icon_spin_count = 0

        # Application update
        self.manager.get_updater().appUpdated.connect(self.app_updated)
        self.updated_version = None

        # This is a windowless application mostly using the system tray
        self.setQuitOnLastWindowClosed(False)

        self.setup_systray()

        # Direct Edit conflict
        self.manager.get_drive_edit().driveEditConflict.connect(
            self._direct_edit_conflict)

        # Check if actions is required, separate method so it can be override
        self.init_checks()
        self.engineWidget = None

        # Setup notification center for Mac
        if AbstractOSIntegration.is_mac():
            if AbstractOSIntegration.os_version_above("10.8"):
                self._setup_notification_center()
Ejemplo n.º 25
0
 def test_fileinfo_normalization(self):
     import os
     from nxdrive.client.local_client import FileInfo
     from nose.plugins.skip import SkipTest
     from nxdrive.osi import AbstractOSIntegration
     if AbstractOSIntegration.is_mac():
         raise SkipTest("Normalization dont work on Mac")
     self.engine_1.stop()
     name = u'Teste\u0301'
     self.local_client.make_file('/', name, 'Test')
     info = FileInfo(self.local_client.base_folder, '/' + name, False, 0)
     # The encoding should be different - cannot trust the get_children as they use FileInfo
     children = os.listdir(self.local_client._abspath('/'))
     children.sort()
     self.assertNotEqual(children[0], name)
Ejemplo n.º 26
0
 def is_osxbundle(self, ref):
     '''
     This is not reliable yet
     '''
     if not AbstractOSIntegration.is_mac():
         return False
     if (os.path.isfile(self._abspath(ref))):
         return False
     # Dont want to synchornize app - when copy paste this file might not has been created yet
     if os.path.isfile(os.path.join(ref, "Contents", "Info.plist")):
         return True
     attrs = self.get_remote_id(ref, "com.apple.FinderInfo")
     if attrs is None:
         return False
     return bool(ord(attrs[8]) & 0x20)
Ejemplo n.º 27
0
 def is_osxbundle(self, ref):
     '''
     This is not reliable yet
     '''
     if not AbstractOSIntegration.is_mac():
         return False
     if (os.path.isfile(self._abspath(ref))):
         return False
     # Dont want to synchornize app - when copy paste this file might not has been created yet
     if os.path.isfile(os.path.join(ref, "Contents", "Info.plist")):
         return True
     attrs = self.get_remote_id(ref, "com.apple.FinderInfo")
     if attrs is None:
         return False
     return bool(ord(attrs[8]) & 0x20)
Ejemplo n.º 28
0
    def current_locale(self):
        """ Detect the OS default language. """

        encoding = locale.getdefaultlocale()[1]
        if AbstractOSIntegration.is_windows():
            l10n_code = ctypes.windll.kernel32.GetUserDefaultUILanguage()
            l10n = locale.windows_locale[l10n_code]
        elif AbstractOSIntegration.is_mac():
            l10n_code = NSLocale.currentLocale()
            l10n = NSLocale.localeIdentifier(l10n_code)
            encoding = 'UTF-8'
        else:
            l10n = locale.getdefaultlocale()[0]

        return '.'.join([l10n, encoding])
Ejemplo n.º 29
0
 def test_fileinfo_normalization(self):
     import os
     from nxdrive.client.local_client import FileInfo
     from nose.plugins.skip import SkipTest
     from nxdrive.osi import AbstractOSIntegration
     if AbstractOSIntegration.is_mac():
         raise SkipTest("Normalization dont work on Mac")
     self.engine_1.stop()
     name = u'Teste\u0301'
     self.local_client.make_file('/', name, 'Test')
     info = FileInfo(self.local_client.base_folder, '/' + name, False, 0)
     # The encoding should be different - cannot trust the get_children as they use FileInfo
     children = os.listdir(self.local_client._abspath('/'))
     children.sort()
     self.assertNotEqual(children[0], name)
Ejemplo n.º 30
0
    def __init__(self, manager, *args):
        super(Application, self).__init__(manager, *args)
        self.setQuitOnLastWindowClosed(False)
        self._delegator = None
        from nxdrive.scripting import DriveUiScript
        self.manager.set_script_object(DriveUiScript(manager, self))
        self.mainEngine = None
        self.filters_dlg = None
        self._conflicts_modals = dict()
        self.current_notification = None
        self.default_tooltip = self.manager.app_name

        for _, engine in self.manager.get_engines().iteritems():
            self.mainEngine = engine
            break
        if self.mainEngine is not None and Options.debug:
            from nxdrive.engine.engine import EngineLogger
            self.engineLogger = EngineLogger(self.mainEngine)
        self.engineWidget = None

        self.aboutToQuit.connect(self.manager.stop)
        self.manager.dropEngine.connect(self.dropped_engine)

        # Timer to spin the transferring icon
        self.icon_spin_timer = QtCore.QTimer()
        self.icon_spin_timer.timeout.connect(self.spin_transferring_icon)
        self.icon_spin_count = 0

        # Application update
        self.manager.get_updater().appUpdated.connect(self.app_updated)
        self.updated_version = None

        # This is a windowless application mostly using the system tray
        self.setQuitOnLastWindowClosed(False)

        self.setup_systray()

        # Direct Edit conflict
        self.manager.direct_edit.directEditConflict.connect(self._direct_edit_conflict)

        # Check if actions is required, separate method so it can be override
        self.init_checks()
        self.engineWidget = None

        # Setup notification center for macOS
        if (AbstractOSIntegration.is_mac()
                and AbstractOSIntegration.os_version_above('10.8')):
            self._setup_notification_center()
Ejemplo n.º 31
0
    def copy_with_xattr(src, dst):
        """
        Custom copy tree method that also copies xattr along with
        shutil.copytree functionality.
        """

        if AbstractOSIntegration.is_windows():
            # Make a copy of file1 using shell (to copy including xattr)
            shell.SHFileOperation((0, shellcon.FO_COPY, src, dst,
                                   shellcon.FOF_NOCONFIRMATION, None, None))
        elif AbstractOSIntegration.is_mac():
            fm = Cocoa.NSFileManager.defaultManager()
            fm.copyItemAtPath_toPath_error_(src, dst, None)
        else:
            shutil.copy2(src, dst)
            remote_id = xattr.getxattr(src, 'user.ndrive')
            xattr.setxattr(dst, 'user.ndrive', remote_id)
    def copy_with_xattr(src, dst):
        """
        Custom copy tree method that also copies xattr along with
        shutil.copytree functionality.
        """

        if AbstractOSIntegration.is_windows():
            # Make a copy of file1 using shell (to copy including xattr)
            shell.SHFileOperation((0, shellcon.FO_COPY, src, dst,
                                   shellcon.FOF_NOCONFIRMATION, None, None))
        elif AbstractOSIntegration.is_mac():
            fm = Cocoa.NSFileManager.defaultManager()
            fm.copyItemAtPath_toPath_error_(src, dst, None)
        else:
            shutil.copy2(src, dst)
            remote_id = xattr.getxattr(src, 'user.ndrive')
            xattr.setxattr(dst, 'user.ndrive', remote_id)
Ejemplo n.º 33
0
    def __init__(self,
                 root,
                 path,
                 folderish,
                 last_modification_time,
                 size=0,
                 digest_func='md5',
                 check_suspended=None,
                 remote_ref=None):

        # Function to check during long-running processing like digest
        # computation if the synchronization thread needs to be suspended
        self.check_suspended = check_suspended
        self.size = size
        filepath = os.path.join(root, path[1:].replace(u'/', os.path.sep))
        root = unicodedata.normalize('NFC', root)
        path = unicodedata.normalize('NFC', path)
        normalized_filepath = os.path.join(root,
                                           path[1:].replace(u'/', os.path.sep))
        self.filepath = normalized_filepath

        # Normalize name on the file system if not normalized
        # See https://jira.nuxeo.com/browse/NXDRIVE-188
        if os.path.exists(
                filepath
        ) and normalized_filepath != filepath and not AbstractOSIntegration.is_mac(
        ):
            log.debug('Forcing normalization of %r to %r', filepath,
                      normalized_filepath)
            os.rename(filepath, normalized_filepath)

        self.root = root  # the sync root folder local path
        self.path = path  # the truncated path (under the root)
        self.folderish = folderish  # True if a Folder
        self.remote_ref = remote_ref

        # Last OS modification date of the file
        self.last_modification_time = last_modification_time

        # Function to use
        self._digest_func = digest_func.lower()

        # Precompute base name once and for all are it's often useful in
        # practice
        self.name = os.path.basename(path)
Ejemplo n.º 34
0
 def get_path_remote_id(path, name="ndrive"):
     if AbstractOSIntegration.is_windows():
         path = path + ":" + name
         try:
             with open(path, "r") as f:
                 return unicode(f.read(), 'utf-8')
         except:
             return None
     else:
         import xattr
         try:
             if AbstractOSIntegration.is_mac():
                 value = xattr.getxattr(path, name)
             else:
                 value = xattr.getxattr(path, 'user.' + name)
             return unicode(value, 'utf-8')
         except:
             return None
Ejemplo n.º 35
0
 def get_path_remote_id(path, name="ndrive"):
     if AbstractOSIntegration.is_windows():
         path = path + ":" + name
         try:
             with open(path, "r") as f:
                 return unicode(f.read(), 'utf-8')
         except:
             return None
     else:
         import xattr
         try:
             if AbstractOSIntegration.is_mac():
                 value = xattr.getxattr(path, name)
             else:
                 value = xattr.getxattr(path, 'user.' + name)
             return unicode(value, 'utf-8')
         except:
             return None
Ejemplo n.º 36
0
 def _new_notification(self, notification):
     if not notification.is_bubble():
         return
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_above("10.8"):
             from nxdrive.osi.darwin.pyNotificationCenter import notify, NotificationDelegator
             if self._delegator is None:
                 self._delegator = NotificationDelegator.alloc().init()
                 self._delegator._manager = self.manager
             # Use notification center
             userInfo = dict()
             userInfo["uuid"] = notification.get_uid()
             return notify(notification.get_title(), None, notification.get_description(), userInfo=userInfo)
     self.current_notification = notification
     icon = QtGui.QSystemTrayIcon.Information
     if (notification.get_level() == Notification.LEVEL_WARNING):
         icon = QtGui.QSystemTrayIcon.Warning
     elif (notification.get_level() == Notification.LEVEL_ERROR):
         icon =  QtGui.QSystemTrayIcon.Critical
     self.show_message(notification.get_title(), notification.get_description(), icon=icon)
Ejemplo n.º 37
0
 def _set_root_icon(self):
     local_client = self.get_local_client()
     if not local_client.exists('/') or local_client.has_folder_icon('/'):
         return
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_below("10.10"):
             icon = find_icon("NuxeoDrive_Mac_Folder.dat")
         else:
             icon = find_icon("NuxeoDrive_Mac_Yosemite_Folder.dat")
     elif AbstractOSIntegration.is_windows():
         icon = find_icon("NuxeoDrive_Windows_Folder.ico")
     else:
         # No implementation on Linux
         return
     if icon is None:
         return
     locker = local_client.unlock_ref('/', unlock_parent=False)
     try:
         local_client.set_folder_icon('/', icon)
     finally:
         local_client.lock_ref('/', locker)
Ejemplo n.º 38
0
    def get_path_remote_id(path, name="ndrive"):
        if AbstractOSIntegration.is_windows():
            path = path + ":" + name
            try:
                with open(path, "r") as f:
                    return f.read()
            except:
                return None
        else:
            import xattr

            try:
                if AbstractOSIntegration.is_mac():
                    value = xattr.getxattr(path, name)
                else:
                    value = xattr.getxattr(path, "user." + name)
                if type(value).__name__ == "unicode":
                    value = unicode(value)
                return value
            except:
                return None
Ejemplo n.º 39
0
 def set_remote_id(self, ref, remote_id, name='ndrive'):
     if type(remote_id).__name__ == "unicode":
         remote_id = unicodedata.normalize('NFC', remote_id).encode('utf-8')
     # Can be move to another class
     path = self._abspath(ref)
     log.trace('Setting xattr %s with value %r on %r', name, remote_id,
               path)
     locker = self.unlock_path(path, False)
     if AbstractOSIntegration.is_windows():
         pathAlt = path + ":" + name
         try:
             if not os.path.exists(path):
                 raise NotFound()
             stat = os.stat(path)
             with open(pathAlt, "w") as f:
                 f.write(remote_id)
             # Avoid time modified change
             os.utime(path, (stat.st_atime, stat.st_mtime))
         except IOError as e:
             # Should not happen
             if e.errno == os.errno.EACCES:
                 self.unset_path_readonly(path)
                 with open(pathAlt, "w") as f:
                     f.write(remote_id)
                 self.set_path_readonly(path)
             else:
                 raise e
         finally:
             self.lock_path(path, locker)
     else:
         try:
             import xattr
             stat = os.stat(path)
             if AbstractOSIntegration.is_mac():
                 xattr.setxattr(path, name, remote_id)
             else:
                 xattr.setxattr(path, 'user.' + name, remote_id)
             os.utime(path, (stat.st_atime, stat.st_mtime))
         finally:
             self.lock_path(path, locker)
Ejemplo n.º 40
0
    def set_remote_id(self, ref, remote_id, name="ndrive"):
        if type(remote_id).__name__ == "unicode":
            remote_id = unicodedata.normalize("NFC", remote_id).encode("utf-8")
        # Can be move to another class
        path = self._abspath(ref)
        log.trace("Setting xattr %s with value %r on %r", name, remote_id, path)
        locker = self.unlock_path(path, False)
        if AbstractOSIntegration.is_windows():
            pathAlt = path + ":" + name
            try:
                if not os.path.exists(path):
                    raise NotFound()
                stat = os.stat(path)
                with open(pathAlt, "w") as f:
                    f.write(remote_id)
                # Avoid time modified change
                os.utime(path, (stat.st_atime, stat.st_mtime))
            except IOError as e:
                # Should not happen
                if e.errno == os.errno.EACCES:
                    self.unset_path_readonly(path)
                    with open(pathAlt, "w") as f:
                        f.write(remote_id)
                    self.set_path_readonly(path)
                else:
                    raise e
            finally:
                self.lock_path(path, locker)
        else:
            try:
                import xattr

                stat = os.stat(path)
                if AbstractOSIntegration.is_mac():
                    xattr.setxattr(path, name, remote_id)
                else:
                    xattr.setxattr(path, "user." + name, remote_id)
                os.utime(path, (stat.st_atime, stat.st_mtime))
            finally:
                self.lock_path(path, locker)
Ejemplo n.º 41
0
 def _set_root_icon(self):
     local_client = self.get_local_client()
     if local_client.has_folder_icon('/'):
         return
     if AbstractOSIntegration.is_mac():
         if AbstractOSIntegration.os_version_below("10.10"):
             icon = find_icon("NuxeoDrive_Mac_Folder.dat")
         else:
             icon = find_icon("NuxeoDrive_Mac_Yosemite_Folder.dat")
     elif AbstractOSIntegration.is_windows():
         if AbstractOSIntegration.os_version_below("5.2"):
             icon = find_icon("NuxeoDrive_Windows_Xp_Folder.ico")
         else:
             icon = find_icon("NuxeoDrive_Windows_Folder.ico")
     else:
         # No implementation on Linux
         return
     locker = local_client.unlock_ref('/', unlock_parent=False)
     try:
         local_client.set_folder_icon('/', icon)
     finally:
         local_client.lock_ref('/', locker)
Ejemplo n.º 42
0
    def test_finder_in_use(self):
        if not AbstractOSIntegration.is_mac():
            raise SkipTest('Not relevant on Linux or Windows')
        self.engine_1.start()
        self.wait_sync(wait_for_async=True)
        self.local_client_1.make_file(
            '/', u'File.txt', content=u'Some Content 1'.encode('utf-8'))

        # Emulate the Finder in use flag
        OSX_FINDER_INFO_ENTRY_SIZE = 32
        key = (OSX_FINDER_INFO_ENTRY_SIZE) * [0]
        key[0] = 0x62
        key[1] = 0x72
        key[2] = 0x6F
        key[3] = 0x6B
        key[4] = 0x4D
        key[5] = 0x41
        key[6] = 0x43
        key[7] = 0x53
        import xattr
        xattr.setxattr(self.local_client_1._abspath(u'/File.txt'),
                       xattr.XATTR_FINDERINFO_NAME, bytes(bytearray(key)))

        # The file should not be synced and there have no remote id
        self.wait_sync(wait_for_async=True, fail_if_timeout=False, timeout=10)
        info = self.local_client_1.get_remote_id(u'/File.txt')
        self.assertIsNone(info)

        # Remove the Finder flag
        self.local_client_1.remove_remote_id(u'/File.txt',
                                             xattr.XATTR_FINDERINFO_NAME)

        # The sync process should now handle the file and sync it
        self.wait_sync(wait_for_async=True, fail_if_timeout=False, timeout=10)
        info = self.local_client_1.get_remote_id(u'/File.txt')
        self.assertIsNotNone(info)
Ejemplo n.º 43
0
class TestEncoding(UnitTestCase):
    def setUp(self):
        super(TestEncoding, self).setUp()
        self.engine_1.start()
        self.wait_sync()
        # Bind the server and root workspace
        self.remote_client = self.remote_document_client_1
        self.local_client = self.local_client_1

    def test_filename_with_accents_from_server(self):
        self.remote_client.make_file(self.workspace, u'Nom sans accents.doc',
                                     u"Contenu sans accents.")
        self.remote_client.make_file(self.workspace,
                                     u'Nom avec accents \xe9 \xe8.doc',
                                     u"Contenu sans accents.")

        self.wait_sync(wait_for_async=True)

        self.assertEqual(
            self.local_client.get_content(u'/Nom sans accents.doc'),
            u"Contenu sans accents.")
        self.assertEqual(
            self.local_client.get_content(u'/Nom avec accents \xe9 \xe8.doc'),
            u"Contenu sans accents.")

    def test_filename_with_katakana(self):
        self.remote_client.make_file(self.workspace,
                                     u'Nom sans \u30bc\u30ec accents.doc',
                                     u"Contenu")
        self.local_client.make_file('/', u'Avec accents \u30d7 \u793e.doc',
                                    u"Contenu")

        self.wait_sync(wait_for_async=True)

        self.assertEqual(
            self.local_client.get_content(
                u'/Nom sans \u30bc\u30ec accents.doc'), u"Contenu")
        self.assertEqual(
            self.remote_client.get_content(u'/Avec accents \u30d7 \u793e.doc'),
            u"Contenu")

    def test_content_with_accents_from_server(self):
        self.remote_client.make_file(
            self.workspace, u'Nom sans accents.txt',
            u"Contenu avec caract\xe8res accentu\xe9s.".encode('utf-8'))
        self.wait_sync(wait_for_async=True)
        self.assertEqual(
            self.local_client.get_content(u'/Nom sans accents.txt'),
            u"Contenu avec caract\xe8res accentu\xe9s.".encode('utf-8'))

    def test_filename_with_accents_from_client(self):
        self.local_client.make_file('/', u'Avec accents \xe9 \xe8.doc',
                                    u"Contenu sans accents.")
        self.local_client.make_file('/', u'Sans accents.doc',
                                    u"Contenu sans accents.")
        self.wait_sync(wait_for_async=True)
        self.assertEqual(
            self.remote_client.get_content(u'/Avec accents \xe9 \xe8.doc'),
            u"Contenu sans accents.")
        self.assertEqual(self.remote_client.get_content(u'/Sans accents.doc'),
                         u"Contenu sans accents.")

    def test_content_with_accents_from_client(self):
        self.local_client.make_file(
            '/', u'Nom sans accents',
            u"Contenu avec caract\xe8res accentu\xe9s.".encode('utf-8'))
        self.wait_sync(wait_for_async=True)
        self.assertEqual(
            self.remote_client.get_content(u'/Nom sans accents'),
            u"Contenu avec caract\xe8res accentu\xe9s.".encode('utf-8'))

    def test_name_normalization(self):
        self.local_client.make_file('/', u'espace\xa0 et TM\u2122.doc')
        self.wait_sync(wait_for_async=True)
        self.assertEqual(
            self.remote_client.get_info(u'/espace\xa0 et TM\u2122.doc').name,
            u'espace\xa0 et TM\u2122.doc')

    @skipIf(AbstractOSIntegration.is_mac(), 'Normalization dont work on Mac')
    def test_fileinfo_normalization(self):
        self.engine_1.stop()
        name = u'Teste\u0301'
        self.local_client.make_file('/', name, 'Test')
        info = FileInfo(self.local_client.base_folder, '/' + name, False, 0)
        # The encoding should be different - cannot trust the get_children as they use FileInfo
        children = os.listdir(self.local_client.abspath('/'))
        children.sort()
        self.assertNotEqual(children[0], name)
Ejemplo n.º 44
0
    def _execute(self):
        self._current_metrics = dict()
        self._current_item = self._get_item()
        soft_lock = None
        while (self._current_item != None):
            # Take client every time as it is cached in engine
            local_client = self._engine.get_local_client()
            remote_client = self._engine.get_remote_client()
            doc_pair = None
            try:
                doc_pair = self._dao.acquire_state(self._thread_id, self._current_item.id)
            except:
                log.trace("Cannot acquire state for: %r", self._current_item)
                self._postpone_pair(self._current_item, 'Pair in use', interval=3)
                self._current_item = self._get_item()
                continue
            try:
                if doc_pair is None:
                    log.trace("Didn't acquire state, dropping %r", self._current_item)
                    self._current_item = self._get_item()
                    continue
                log.debug('Executing processor on %r(%d)', doc_pair, doc_pair.version)
                self._current_doc_pair = doc_pair
                self._current_temp_file = None
                if (doc_pair.pair_state == 'synchronized'
                    or doc_pair.pair_state == 'unsynchronized'
                    or doc_pair.pair_state is None
                    or doc_pair.pair_state.startswith('parent_')):
                    log.trace("Skip as pair is in non-processable state: %r", doc_pair)
                    self._current_item = self._get_item()
                    continue
                if AbstractOSIntegration.is_mac() and local_client.exists(doc_pair.local_path):
                    try:
                        finder_info = local_client.get_remote_id(doc_pair.local_path, "com.apple.FinderInfo")
                        if finder_info is not None and 'brokMACS' in finder_info:
                            log.trace("Skip as pair is in use by Finder: %r", doc_pair)
                            self._postpone_pair(doc_pair, 'Finder using file', interval=3)
                            self._current_item = self._get_item()
                            continue
                    except IOError:
                        pass
                # TODO Update as the server dont take hash to avoid conflict yet
                if (doc_pair.pair_state.startswith("locally")
                        and doc_pair.remote_ref is not None):
                    try:
                        remote_info = remote_client.get_info(doc_pair.remote_ref)
                        if remote_info.digest != doc_pair.remote_digest and doc_pair.remote_digest is not None:
                            doc_pair.remote_state = 'modified'
                        self._refresh_remote(doc_pair, remote_client, remote_info)
                        # Can run into conflict
                        if doc_pair.pair_state == 'conflicted':
                            self._current_item = self._get_item()
                            continue
                        doc_pair = self._dao.get_state_from_id(doc_pair.id)
                        if doc_pair is None:
                            self._current_item = self._get_item()
                            continue
                    except NotFound:
                        doc_pair.remote_ref = None
                parent_path = doc_pair.local_parent_path
                if (parent_path == ''):
                    parent_path = "/"
                if not local_client.exists(parent_path):
                    if doc_pair.remote_state == "deleted":
                        self._dao.remove_state(doc_pair)
                        continue
                    self._handle_no_parent(doc_pair, local_client, remote_client)
                    self._current_item = self._get_item()
                    continue

                self._current_metrics = dict()
                handler_name = '_synchronize_' + doc_pair.pair_state
                self._action = Action(handler_name)
                sync_handler = getattr(self, handler_name, None)
                if sync_handler is None:
                    log.debug("Unhandled pair_state: %r for %r",
                                       doc_pair.pair_state, doc_pair)
                    self.increase_error(doc_pair, "ILLEGAL_STATE")
                    self._current_item = self._get_item()
                    continue
                else:
                    self._current_metrics = dict()
                    self._current_metrics["handler"] = doc_pair.pair_state
                    self._current_metrics["start_time"] = current_milli_time()
                    log.trace("Calling %s on doc pair %r", sync_handler, doc_pair)
                    try:
                        soft_lock = self._lock_soft_path(doc_pair.local_path)
                        sync_handler(doc_pair, local_client, remote_client)
                        self._current_metrics["end_time"] = current_milli_time()
                        self.pairSync.emit(doc_pair, self._current_metrics)
                        # TO_REVIEW May have a call to reset_error
                        log.trace("Finish %s on doc pair %r", sync_handler, doc_pair)
                    except ThreadInterrupt:
                        raise
                    except PairInterrupt:
                        from time import sleep
                        # Wait one second to avoid retrying to quickly
                        self._current_doc_pair = None
                        log.debug("PairInterrupt wait 1s and requeue on %r", doc_pair)
                        sleep(1)
                        self._engine.get_queue_manager().push(doc_pair)
                        continue
                    except Exception as e:
                        if isinstance(e, IOError) and e.errno == 28:
                            self._engine.noSpaceLeftOnDevice.emit()
                        log.exception(e)
                        self.increase_error(doc_pair, "SYNC_HANDLER" % handler_name, exception=e)
                        self._current_item = self._get_item()
                        continue
            except ThreadInterrupt:
                self._engine.get_queue_manager().push(doc_pair)
                raise
            except Exception as e:
                log.exception(e)
                self.increase_error(doc_pair, "EXCEPTION", exception=e)
                raise e
            finally:
                if soft_lock is not None:
                    self._unlock_soft_path(soft_lock)
                self._dao.release_state(self._thread_id)
            self._interact()
            self._current_item = self._get_item()
Ejemplo n.º 45
0
 def _handle_watchdog_event_on_known_pair(self, doc_pair, evt, rel_path):
     log.trace("watchdog event %r on known pair: %r", evt, doc_pair)
     if (evt.event_type == 'moved'):
         # Ignore move to Office tmp file
         src_filename = os.path.basename(evt.src_path)
         dest_filename = os.path.basename(evt.dest_path)
         if dest_filename.endswith('.tmp'):
             if dest_filename.startswith('~') or len(dest_filename) == 12:
                 # 12 is for Office 813DEFA7.tmp
                 log.debug('Ignoring Office tmp file: %r', evt.dest_path)
                 return
         # Ignore normalization of the filename on the file system
         # See https://jira.nuxeo.com/browse/NXDRIVE-188
         if evt.dest_path == normalize_event_filename(evt.src_path):
             log.debug('Ignoring move from %r to normalized name: %r', evt.src_path, evt.dest_path)
             return
         src_path = normalize_event_filename(evt.dest_path)
         rel_path = self.client.get_path(src_path)
         # Office weird replacement handling
         if is_office_file(dest_filename):
             pair = self._dao.get_state_from_local(rel_path)
             remote_ref = self.client.get_remote_id(rel_path)
             if pair is not None and pair.remote_ref == remote_ref:
                 local_info = self.client.get_info(rel_path, raise_if_missing=False)
                 if local_info is not None:
                     digest = local_info.get_digest()
                     # Drop event if digest hasn't changed, can be the case if only file permissions have been updated
                     if not doc_pair.folderish and pair.local_digest == digest:
                         log.trace('Dropping watchdog event [%s] as digest has not changed for %s',
                           evt.event_type, rel_path)
                         self._dao.remove_state(doc_pair)
                         return
                     pair.local_digest = digest
                     pair.local_state = 'modified'
                     self._dao.update_local_state(pair, local_info)
                     self._dao.remove_state(doc_pair)
                     log.debug("Office substitution file: remove pair(%r) mark(%r) as modified", doc_pair, pair)
                     return
         local_info = self.client.get_info(rel_path, raise_if_missing=False)
         if local_info is not None:
             old_local_path = None
             rel_parent_path = self.client.get_path(os.path.dirname(src_path))
             if rel_parent_path == '':
                 rel_parent_path = '/'
             # Ignore inner movement
             remote_parent_ref = self.client.get_remote_id(rel_parent_path)
             if (doc_pair.remote_name == local_info.name and
                     doc_pair.remote_parent_ref == remote_parent_ref):
                     # The pair was moved but it has been canceled manually
                     doc_pair.local_state = 'synchronized'
             elif not (local_info.name == doc_pair.local_name and
                     doc_pair.remote_parent_ref == remote_parent_ref):
                 log.debug("Detect move for %r (%r)", local_info.name, doc_pair)
                 if doc_pair.local_state != 'created':
                     doc_pair.local_state = 'moved'
                     old_local_path = doc_pair.local_path
                     self._dao.update_local_state(doc_pair, local_info, versionned=True)
             self._dao.update_local_state(doc_pair, local_info, versionned=False)
             if self._windows and old_local_path is not None and self._windows_folder_scan_delay > 0:
                 self._win_lock.acquire()
                 try:
                     if old_local_path in self._folder_scan_events:
                         log.debug('Update queue of folders to scan: move from %r to %r', old_local_path, rel_path)
                         del self._folder_scan_events[old_local_path]
                         self._folder_scan_events[rel_path] = (
                                         mktime(local_info.last_modification_time.timetuple()), doc_pair)
                 finally:
                     self._win_lock.release()
         return
     if doc_pair.processor > 0:
         log.trace("Don't update as in process %r", doc_pair)
         return
     if evt.event_type == 'deleted':
         # Delay on Windows the delete event
         if self._windows:
             self._win_lock.acquire()
             log.debug('Add pair to delete events: %r', doc_pair)
             try:
                 self._delete_events[doc_pair.remote_ref] = (current_milli_time(), doc_pair)
             finally:
                 self._win_lock.release()
         else:
             # In case of case sensitive can be an issue
             if self.client.exists(doc_pair.local_path):
                 remote_id = self.client.get_remote_id(doc_pair.local_path)
                 if remote_id == doc_pair.remote_ref or remote_id is None:
                     # This happens on update don't do anything
                     return
             self._handle_watchdog_delete(doc_pair)
         return
     if evt.event_type == 'created':
         # NXDRIVE-471 case maybe
         log.trace("This should only happen in case of a quick move and copy-paste")
         return
     local_info = self.client.get_info(rel_path, raise_if_missing=False)
     if local_info is not None:
         if doc_pair.local_state == 'synchronized':
             digest = local_info.get_digest()
             # Drop event if digest hasn't changed, can be the case if only file permissions have been updated
             if not doc_pair.folderish and doc_pair.local_digest == digest:
                 log.debug('Dropping watchdog event [%s] as digest has not changed for %s',
                           evt.event_type, rel_path)
                 return
             doc_pair.local_digest = digest
             doc_pair.local_state = 'modified'
         queue = not (evt.event_type == 'modified' and doc_pair.folderish and doc_pair.local_state == 'modified')
         if AbstractOSIntegration.is_mac() and evt.event_type == 'modified' and doc_pair.remote_ref is not None and doc_pair.remote_ref != local_info.remote_ref:
             original_pair = self._dao.get_normal_state_from_remote(local_info.remote_ref)
             original_info = None
             if original_pair is not None:
                 original_info = self.client.get_info(original_pair.local_path, raise_if_missing=False)
             if original_info is not None and original_info.remote_ref == local_info.remote_ref:
                 log.debug("MacOSX has postponed overwriting of xattr, need to reset remote_ref for %r", doc_pair)
                 # We are in a copy/paste situation with OS overriding the xattribute
                 self.client.set_remote_id(doc_pair.local_path, doc_pair.remote_ref)
         # No need to change anything on sync folder
         if (evt.event_type == 'modified' and doc_pair.folderish and doc_pair.local_state == 'modified'):
             doc_pair.local_state = 'synchronized'
         self._dao.update_local_state(doc_pair, local_info, queue=queue)
Ejemplo n.º 46
0
# coding: utf-8
from unittest import skipIf

from nxdrive.osi import AbstractOSIntegration
from tests.common_unit_test import UnitTestCase


@skipIf(not AbstractOSIntegration.is_mac(),
        'Not relevant on GNU/Linux nor Windows')
class TestMacSpecific(UnitTestCase):
    def test_finder_in_use(self):
        """ Test that if Finder is using the file we postpone the sync. """

        self.engine_1.start()
        self.wait_sync(wait_for_async=True)
        self.local_client_1.make_file(
            '/', u'File.txt', content=u'Some Content 1'.encode('utf-8'))

        # Emulate the Finder in use flag
        OSX_FINDER_INFO_ENTRY_SIZE = 32
        key = (OSX_FINDER_INFO_ENTRY_SIZE) * [0]
        key[0] = 0x62
        key[1] = 0x72
        key[2] = 0x6F
        key[3] = 0x6B
        key[4] = 0x4D
        key[5] = 0x41
        key[6] = 0x43
        key[7] = 0x53
        import xattr
        xattr.setxattr(self.local_client_1.abspath(u'/File.txt'),
Ejemplo n.º 47
0
# coding: utf-8
from unittest import skipIf

from nxdrive.osi import AbstractOSIntegration
from tests.common_unit_test import UnitTestCase


@skipIf(not AbstractOSIntegration.is_mac(),
        'Not relevant on GNU/Linux nor Windows')
class TestMacSpecific(UnitTestCase):

    def test_finder_in_use(self):
        """ Test that if Finder is using the file we postpone the sync. """

        self.engine_1.start()
        self.wait_sync(wait_for_async=True)
        self.local_client_1.make_file('/', u'File.txt',
                                      content=u'Some Content 1'.encode('utf-8'))

        # Emulate the Finder in use flag
        OSX_FINDER_INFO_ENTRY_SIZE = 32
        key = (OSX_FINDER_INFO_ENTRY_SIZE)*[0]
        key[0] = 0x62
        key[1] = 0x72
        key[2] = 0x6F
        key[3] = 0x6B
        key[4] = 0x4D
        key[5] = 0x41
        key[6] = 0x43
        key[7] = 0x53
        import xattr
# coding: utf-8
""""
Test if changes made to local file system when Drive is offline sync's back
later when Drive becomes online.
"""

import shutil

from nxdrive.osi import AbstractOSIntegration
from tests.common_unit_test import FILE_CONTENT, UnitTestCase

if AbstractOSIntegration.is_windows():
    from win32com.shell import shell, shellcon
else:
    import xattr
    if AbstractOSIntegration.is_mac():
        import Cocoa


class TestOfflineChangesSync(UnitTestCase):
    """
    All changes made in local PC while drive is offline should sync later when
    drive comes back online.
    offline can be one of: suspended, exited or disconnect
    See NXDRIVE-686
    """

    def setUp(self):
        self.engine_1.start()
        self.wait_sync(wait_for_async=True)
Ejemplo n.º 49
0
# coding: utf-8
""""
Test if changes made to local file system when Drive is offline sync's back
later when Drive becomes online.
"""

import shutil

from nxdrive.osi import AbstractOSIntegration
from tests.common_unit_test import FILE_CONTENT, UnitTestCase

if AbstractOSIntegration.is_windows():
    from win32com.shell import shell, shellcon
else:
    import xattr
    if AbstractOSIntegration.is_mac():
        import Cocoa


class TestOfflineChangesSync(UnitTestCase):
    """
    All changes made in local PC while drive is offline should sync later when
    drive comes back online.
    offline can be one of: suspended, exited or disconnect
    See NXDRIVE-686
    """
    def setUp(self):
        self.engine_1.start()
        self.wait_sync(wait_for_async=True)

        self.local = self.local_client_1