Пример #1
0
class DBusInterface(object):
    """The DBus Interface to Ubuntu One's SyncDaemon."""

    def __init__(self, msd):
        # magicicada's syncdaemon
        self.msd = msd
        logger.info("DBus interface starting")
        self._public_files_deferred = None

        # set up dbus and related stuff
        loop = DBusGMainLoop(set_as_default=True)
        self._bus = bus = SessionBus(mainloop=loop)
        self.sync_daemon_tool = SyncDaemonTool(bus)

        # hook up for signals and store info for the shutdown
        _signals = [
            (self._on_status_changed, 'Status', 'StatusChanged'),
            (self._on_queue_added, 'Status', 'RequestQueueAdded'),
            (self._on_queue_removed, 'Status', 'RequestQueueRemoved'),
            (self._on_upload_progress, 'Status', 'UploadFileProgress'),
            (self._on_download_progress, 'Status', 'DownloadFileProgress'),
            (self._on_folder_created, 'Folders', 'FolderCreated'),
            (self._on_folder_deleted, 'Folders', 'FolderDeleted'),
            (self._on_folder_subscribed, 'Folders', 'FolderSubscribed'),
            (self._on_folder_unsubscribed, 'Folders', 'FolderUnSubscribed'),
            (self._on_share_created, 'Shares', 'ShareCreated'),
            (self._on_share_deleted, 'Shares', 'ShareDeleted'),
            (self._on_share_changed, 'Shares', 'ShareChanged'),
            (self._on_public_files_changed, 'PublicFiles',
                                            'PublicAccessChanged'),
        ]
        self._dbus_matches = []
        for method, dbus_lastname, signal_name in _signals:
            if dbus_lastname is None:
                dbus_interface = None
            else:
                dbus_interface = 'com.ubuntuone.SyncDaemon.' + dbus_lastname
            match = bus.add_signal_receiver(method,
                                            dbus_interface=dbus_interface,
                                            signal_name=signal_name)
            self._dbus_matches.append((match, dbus_interface, signal_name))

    def shutdown(self):
        """Shut down the SyncDaemon."""
        logger.info("DBus interface going down")

        # remove the signals from DBus
        remove = self._bus.remove_signal_receiver
        for match, dbus_interface, signal in self._dbus_matches:
            remove(match, dbus_interface=dbus_interface, signal_name=signal)

    def _process_status(self, state):
        """Transform status information."""
        name = state['name']
        description = state['description']
        is_error = bool(state['is_error'])
        is_connected = bool(state['is_connected'])
        is_online = bool(state['is_online'])
        queues = state['queues']
        connection = state['connection']
        return (name, description, is_error, is_connected,
                is_online, queues, connection)

    @retryable
    def get_status(self):
        """Get SD status."""
        logger.info("Getting status")
        d = self.sync_daemon_tool.get_status()
        d.addCallback(self._process_status)
        return d

    @retryable
    @defer.inlineCallbacks
    def get_free_space(self, volume_id):
        """Get the free space for a volume."""
        result = yield self.sync_daemon_tool.free_space(volume_id)
        logger.info("Free space for volume %r is %r", volume_id, result)
        defer.returnValue(result)

    @retryable
    @defer.inlineCallbacks
    def get_real_shares_dir(self):
        """Get the real directory for the shares."""
        result = yield self.sync_daemon_tool.get_shares_dir()
        logger.info("Real shares dir: %r", result)
        defer.returnValue(result)

    @retryable
    @defer.inlineCallbacks
    def get_link_shares_dir(self):
        """Get the link directory for the shares."""
        result = yield self.sync_daemon_tool.get_shares_dir_link()
        logger.info("Link shares dir: %r", result)
        defer.returnValue(result)

    def _process_transfers(self, transfers, progress):
        """Process downloads or uploads to keep useful info only."""
        r = [Transfer(t['path'], int(t[progress]), int(t['deflated_size']))
             for t in transfers if 'deflated_size' in t]
        return r

    @retryable
    @defer.inlineCallbacks
    def get_current_downloads(self):
        """Get the current_downloads."""
        result = yield self.sync_daemon_tool.get_current_downloads()
        processed = self._process_transfers(result, 'n_bytes_read')
        logger.info("Get current downloads: %d items", len(processed))
        defer.returnValue(processed)

    @retryable
    @defer.inlineCallbacks
    def get_current_uploads(self):
        """Get the current_uploads."""
        result = yield self.sync_daemon_tool.get_current_uploads()
        processed = self._process_transfers(result, 'n_bytes_written')
        logger.info("Get current uploads: %d items", len(processed))
        defer.returnValue(processed)

    def _on_status_changed(self, state):
        """Call the SD callback."""
        logger.info("Received Status changed")
        logger.debug("Status changed data: %r", state)
        data = self._process_status(state)
        self.msd.on_sd_status_changed(*data)

    def _on_queue_added(self, op_name, op_id, op_data):
        """Call the SD callback."""
        logger.debug("Received Queue added: %r [%s] %s",
                     op_name, op_id, op_data)
        self.msd.on_sd_queue_added(op_name, op_id, op_data)

    def _on_queue_removed(self, op_name, op_id, op_data):
        """Call the SD callback."""
        logger.debug("Received Queue removed: %r [%s] %s",
                     op_name, op_id, op_data)
        self.msd.on_sd_queue_removed(op_name, op_id, op_data)

    def _on_upload_progress(self, path, op_data):
        """Call the SD callback."""
        logger.debug("Received Upload progress: %r %s", path, op_data)
        transf = Transfer(path, int(op_data['n_bytes_written']),
                          int(op_data['deflated_size']))
        self.msd.on_sd_upload_progress(transf)

    def _on_download_progress(self, path, op_data):
        """Call the SD callback."""
        logger.debug("Received Download progress: %r %s", path, op_data)
        transf = Transfer(path, int(op_data['n_bytes_read']),
                          int(op_data['deflated_size']))
        self.msd.on_sd_download_progress(transf)

    def _on_folder_created(self, _):
        """Call the SD callback."""
        logger.info("Received Folder created")
        self.msd.on_sd_folders_changed()

    def _on_folder_deleted(self, _):
        """Call the SD callback."""
        logger.info("Received Folder deleted")
        self.msd.on_sd_folders_changed()

    def _on_folder_subscribed(self, _):
        """Call the SD callback."""
        logger.info("Received Folder subscribed")
        self.msd.on_sd_folders_changed()

    def _on_folder_unsubscribed(self, _):
        """Call the SD callback."""
        logger.info("Received Folder unsubscribed")
        self.msd.on_sd_folders_changed()

    def _on_share_created(self, _):
        """Call the SD callback."""
        logger.info("Received Share created")
        self.msd.on_sd_shares_changed()

    def _on_share_deleted(self, _):
        """Call the SD callback."""
        logger.info("Received Share deleted")
        self.msd.on_sd_shares_changed()

    def _on_share_changed(self, _):
        """Call the SD callback."""
        logger.info("Received Share changed")
        self.msd.on_sd_shares_changed()

    def _on_public_files_changed(self, data):
        """Call the SD callback."""
        logger.debug("Received Public Files changed: %s", data)
        pf = PublicFilesData(volume=data['share_id'], node=data['node_id'],
                             path=data['path'], public_url=data['public_url'])
        is_public = bool(data['is_public'])
        self.msd.on_sd_public_files_changed(pf, is_public)

    def _on_public_files_list(self, data):
        """Call the SD callback."""
        logger.info("Received Public Files list (%d)", len(data))
        processed = []
        for d in data:
            logger.debug("    Public Files data: %s", d)
            p = PublicFilesData(volume=d['volume_id'], node=d['node_id'],
                                path=d['path'], public_url=d['public_url'])
            processed.append(p)

        return processed

    @defer.inlineCallbacks
    def get_public_files(self):
        """Ask the Public Files info to syncdaemon."""
        try:
            result = yield self.sync_daemon_tool.get_public_files()
            logger.debug("Public files asked ok.")
        except AttributeError:
            logger.warning('Method sdtool.get_public_files is not available, '
                           'trying old one directly from dbus.')
            result = yield self.get_public_files_old()
        except:
            logger.exception("Public files finished with error:")
            result = []

        defer.returnValue(self._on_public_files_list(result))

    def get_public_files_old(self):
        """Ask the Public Files info to syncdaemon (old approach)."""
        # yes, they can be imported! pylint: disable=F0401,E0611,W0404
        from ubuntuone.platform.tools import DBusClient, ErrorSignal
        from ubuntuone.platform.dbus_interface import \
            DBUS_IFACE_PUBLIC_FILES_NAME
        client = DBusClient(self._bus, '/publicfiles',
                            DBUS_IFACE_PUBLIC_FILES_NAME)

        # note that these callbacks do not come with the requested info, the
        # method just will return None, and the real info will come later
        # in a signal

        def call_done(result):
            """Call was succesful."""
            logger.debug("Public files asked ok.")

        def call_error(error):
            """Call was not succesful."""
            logger.error("Public files asked with error: %s", error)

        d = self.sync_daemon_tool.wait_for_signal('PublicFilesList',
                                                  filter=lambda _: True)

        client.call_method('get_public_files',
                           reply_handler=call_done,
                           error_handler=call_error)
        return d

    @retryable
    def get_queue_content(self):
        """Get the queue content from SDT."""
        logger.info("Getting queue content")
        return self.sync_daemon_tool.waiting()

    @retryable
    def get_folders(self):
        """Get the folders info from SDT."""

        def process(data):
            """Enhance data format."""
            logger.info("Processing Folders items (%d)", len(data))
            all_items = []
            for d in data:
                logger.debug("    Folders data: %r", d)
                f = self._get_folder_data(d)
                all_items.append(f)
            return all_items

        logger.info("Getting folders")
        d = self.sync_daemon_tool.get_folders()
        d.addCallback(process)
        return d

    def start(self):
        """Start SDT."""
        logger.info("Calling start")
        return self.sync_daemon_tool.start()

    def quit(self):
        """Stop SDT."""
        logger.info("Calling quit")
        return self.sync_daemon_tool.quit()

    def connect(self):
        """Connect SDT."""
        logger.info("Calling connect")
        return self.sync_daemon_tool.connect()

    def disconnect(self):
        """Disconnect SDT."""
        logger.info("Calling disconnect")
        return self.sync_daemon_tool.disconnect()

    @defer.inlineCallbacks
    def is_sd_started(self):
        """Find out if SD is active in the system."""
        started = yield is_already_running()
        logger.info("Checking if SD is started: %s", started)
        defer.returnValue(started)

    def _process_share_info(self, data):
        """Process share data."""
        all_items = []
        for d in data:
            logger.debug("    Share data: %r", d)

            # some processing
            dfb = d['free_bytes']
            free_bytes = None if dfb == '' else int(dfb)

            s = ShareData(
                accepted=bool(d['accepted']),
                access_level=d['access_level'],
                free_bytes=free_bytes,
                name=d['name'],
                node_id=d['node_id'],
                other_username=d['other_username'],
                other_visible_name=d['other_visible_name'],
                path=d['path'],
                volume_id=d['volume_id'],
                subscribed=bool(d['subscribed']),
            )
            all_items.append(s)
        return all_items

    @retryable
    def get_shares_to_me(self):
        """Get the shares to me ('shares') info from SDT."""

        def process(data):
            """Enhance data format."""
            logger.info("Processing Shares To Me items (%d)", len(data))
            return self._process_share_info(data)

        logger.info("Getting shares to me")
        d = self.sync_daemon_tool.get_shares()
        d.addCallback(process)
        return d

    @retryable
    def get_shares_to_others(self):
        """Get the shares to others ('shared') info from SDT."""

        def process(data):
            """Enhance data format."""
            logger.info("Processing Shares To Others items (%d)", len(data))
            return self._process_share_info(data)

        logger.info("Getting shares to others")
        d = self.sync_daemon_tool.list_shared()
        d.addCallback(process)
        return d

    @retryable
    def get_metadata(self, path):
        """Return the raw metadata."""
        logger.info("Getting metadata for %r", path)

        def fix_failure(failure):
            """Get the failure and return a nice message."""
            if failure.check(dbus.exceptions.DBusException):
                if failure.value.get_dbus_name() == DBUSERR_PYKEYERROR:
                    return NOT_SYNCHED_PATH
            return failure

        def process(metadata):
            """Process the metadata."""
            logger.debug("Got metadata for path %r: %r", path, metadata)
            return dict(metadata)

        d = self.sync_daemon_tool.get_metadata(path)
        d.addCallbacks(process, fix_failure)
        return d

    @retryable
    @defer.inlineCallbacks
    def _answer_share(self, share_id, method, action_name):
        """Effectively accept or reject a share."""
        logger.debug("%s share %s started", action_name, share_id)
        try:
            result = yield method(share_id)
        except Exception, e:
            if len(e.args) == 2 and len(e.args[1]) > 1:
                error = "%s (%s)" % (e.args[0], e.args[1][1])
            else:
                error = str(e.args[0])
            logger.debug("%s share %s crashed: %s",
                         action_name, share_id, error)
            raise ShareOperationError(share_id=share_id, error=error)

        logger.debug("%s share %s finished: %s", action_name, share_id, result)
        if 'error' in result:
            raise ShareOperationError(share_id=share_id, error=result['error'])
Пример #2
0
class U1FinderLib(NSObject):

    ##
    # Default constructor.
    @objc.typedSelector('@@:@')
    def initWithDelegate_(self, delegate):
        self = super(U1FinderLib, self).init()
        self.sync_daemon_tool = SyncDaemonTool(None)
        self.delegate = delegate
        cfreactor.install()
        return self

    ##
    # Returns the list of the shared volumes in a NSArray<NSString>. Example:
    #
    # [
    #    @"/Users/jose/Ubuntu One",
    #    @"/Users/jose/Pictures"
    # ]
    @objc.typedSelector('v@:')
    def volumeList(self):
        d = self.sync_daemon_tool.get_folders()
        d.addCallback(lambda r: volume_list(r, self.delegate))
        return None

    ##
    # Returns a NSArray<NSString> whit all the files that are being uploaded. Example:
    # [
    #     @"/Users/jose/Ubuntu One/Document.pdf",
    #     @"/Users/jose/Pictures/Image.png"
    # }
    @objc.typedSelector('v@:')
    def currentUploads(self):
        d = self.sync_daemon_tool.get_current_uploads()
        d.addCallback(lambda r: get_uploads(r, self.delegate))
        return None

    ##
    # Like currentUploads() but with the downloads.
    @objc.typedSelector('v@:')
    def currentDownloads(self):
        d = self.sync_daemon_tool.get_current_downloads()
        d.addCallback(lambda r: get_downloads(r, self.delegate))
        return None

    ##
    # Indicates if the specified file is public or not.
    @objc.typedSelector('@@:@')
    def isFilePublic_(self, filePath):
        d = self.sync_daemon_tool.get_public_files()
        d.addCallback(lambda r: file_is_public(r, filePath, self.delegate))
        return None

    ##
    # Publish or unpublish the specified file.
    @objc.typedSelector('v@:@B')
    def changeFile_visibillity_(self, filePath, isPublic):
        self.sync_daemon_tool.change_public_access(os.path.abspath(filePath),
                                                   isPublic)
        return None

    ##
    # Returns the link (NSString) of a public file or nil if the file is not public.
    @objc.typedSelector('@@:@')
    def getPublicLinkOfFile_(self, filePath):
        d = self.sync_daemon_tool.get_public_files()
        d.addCallback(lambda r: get_public_files(r, filePath, self.delegate))
        return None

    ##
    # Synchronizes the specified folder
    @objc.typedSelector('@@:@')
    def synchronizeFolderAtPath_(self, folderPath):
        d = self.sync_daemon_tool.create_folder(os.path.abspath(folderPath))
        d.addCallback(lambda r: folder_synchronized(r, self.delegate))
        return None

    ##
    # Unsuscribes the specified folder.
    @objc.typedSelector('@@:@')
    def unsuscribeFolderAtPath_(self, folderPath):
        d = self.sync_daemon_tool.get_folders()
        d.addCallback(lambda r: unsuscribe_volume_list(
            r, folderPath, self.sync_daemon_tool, self.delegate))
        return None
Пример #3
0
class U1FinderLib(NSObject):
    
    ##
    # Default constructor.
    @objc.typedSelector('@@:@')
    def initWithDelegate_(self, delegate):
        self = super(U1FinderLib, self).init()
        self.sync_daemon_tool = SyncDaemonTool(None)
        self.delegate = delegate
        cfreactor.install()
        return self
    
    ##
    # Returns the list of the shared volumes in a NSArray<NSString>. Example:
    #
    # [
    #    @"/Users/jose/Ubuntu One",
    #    @"/Users/jose/Pictures"
    # ]
    @objc.typedSelector('v@:')
    def volumeList(self):
        d = self.sync_daemon_tool.get_folders()
        d.addCallback(lambda r: volume_list(r, self.delegate))
        return None

    ##
    # Returns a NSArray<NSString> whit all the files that are being uploaded. Example:
    # [
    #     @"/Users/jose/Ubuntu One/Document.pdf",
    #     @"/Users/jose/Pictures/Image.png"
    # }
    @objc.typedSelector('v@:')
    def currentUploads(self):
        d = self.sync_daemon_tool.get_current_uploads()
        d.addCallback(lambda r: get_uploads(r, self.delegate))
        return None

    ##
    # Like currentUploads() but with the downloads.
    @objc.typedSelector('v@:')
    def currentDownloads(self):
        d = self.sync_daemon_tool.get_current_downloads()
        d.addCallback(lambda r: get_downloads(r, self.delegate))
        return None
    
    ##
    # Indicates if the specified file is public or not.
    @objc.typedSelector('@@:@')
    def isFilePublic_(self, filePath):
        d = self.sync_daemon_tool.get_public_files()
        d.addCallback(lambda r: file_is_public(r, filePath, self.delegate))
        return None
    
    ##
    # Publish or unpublish the specified file.
    @objc.typedSelector('v@:@B')
    def changeFile_visibillity_(self, filePath, isPublic):
        self.sync_daemon_tool.change_public_access(os.path.abspath(filePath), isPublic)
        return None

    ##
    # Returns the link (NSString) of a public file or nil if the file is not public.
    @objc.typedSelector('@@:@')
    def getPublicLinkOfFile_(self, filePath):
        d = self.sync_daemon_tool.get_public_files()
        d.addCallback(lambda r: get_public_files(r, filePath, self.delegate))
        return None

    ##
    # Synchronizes the specified folder
    @objc.typedSelector('@@:@')
    def synchronizeFolderAtPath_(self, folderPath):
        d = self.sync_daemon_tool.create_folder(os.path.abspath(folderPath))
        d.addCallback(lambda r: folder_synchronized(r, self.delegate))
        return None

    ##
    # Unsuscribes the specified folder.
    @objc.typedSelector('@@:@')
    def unsuscribeFolderAtPath_(self, folderPath):
        d = self.sync_daemon_tool.get_folders()
        d.addCallback(lambda r: unsuscribe_volume_list(r, folderPath, self.sync_daemon_tool, self.delegate))
        return None