示例#1
0
    def _create_empty_file_or_folder(self):
        self._fullname = FilePath(self._get_full_name())
        if not self._fullname:
            return

        data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
        self._in_data_dir = self._fullname in FilePath(data_dir)
        logger.debug("Adding special file %s", self._fullname)
        if self._in_data_dir:
            self._sync.add_special_file(self._fullname,
                                        self._on_special_file_event)

        special_file_created = False
        try:
            if self._is_folder:
                make_dirs(self._fullname, is_folder=True)
            else:
                create_empty_file(self._fullname)
            special_file_created = True
            self._update_spec_files(self._fullname, self._is_folder)
        except Exception as e:
            logger.warning("Can't create file or folder %s. Reason %s",
                           self._fullname, e)

        if not self._in_data_dir and special_file_created:
            self._sync.add_special_file(self._fullname,
                                        self._on_special_file_event)
        elif self._in_data_dir and not special_file_created:
            self._sync.remove_special_file(self._fullname)
示例#2
0
 def default_config():
     return dict(
         autologin=not is_portable(),
         upload_limit=0,
         download_limit=0,
         fs_events_processing_delay=1,
         fs_events_processing_period=1,
         fs_folder_timeout=2,
         sync_directory=FilePath(get_data_dir()),
         conflict_file_suffix='Conflicted copy from {}'.format(
             get_device_name()),
         node_hash=sha512(uuid4().bytes).hexdigest(),
         user_hash=None,
         send_statistics=True,
         license_type=UNKNOWN_LICENSE,  # unknown licence by default
         lang=None,  # System language
         user_email=None,
         last_user_email=None,
         user_password_hash=None,
         http_downloader_timeout=3,
         excluded_dirs=(),  # List of directories excluded from sync
         max_log_size=100,  # Max log file size Mb
         download_backups=False,
         remote_events_max_total=5000,
         max_remote_events_per_request=100,
         max_relpath_len=3096,
         sync_dir_size=0,
         copies_logging=True,
         excluded_dirs_applied=(),  # List of excluded dirs, applied in DB
         host=REGULAR_URI,
         tracking_address='https://tracking.pvtbox.net:443/1/',
         smart_sync=True,
     )
示例#3
0
    def _init_connectivity(self):
        self._connectivity_service = ConnectivityService(
            self._ss_client, self._network_speed_calculator)
        self._connectivity_service_thread = QThread()
        self._connectivity_service.moveToThread(
            self._connectivity_service_thread)
        self._connectivity_service_thread.started.connect(
            self._connectivity_service.init.emit)
        self._connectivity_service.connected_nodes_outgoing_changed.connect(
            self._on_connected_nodes_changed, Qt.QueuedConnection)
        self._download_manager = DownloadManager(
            connectivity_service=self._connectivity_service,
            ss_client=self._ss_client,
            upload_enabled=False,
            tracker=self._tracker)
        self._download_manager.moveToThread(self._connectivity_service_thread)
        data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
        downloads_dir = get_downloads_dir(data_dir=data_dir, create=True)
        self._connectivity_service_thread.started.connect(
            lambda: self._download_manager.prepare_cleanup([downloads_dir]))
        self._connectivity_service_thread.start()

        self._download_manager.idle.connect(self._sync.on_share_idle)
        self._download_manager.working.connect(self._sync.on_share_downloading)
        self._download_manager.error.connect(
            self._sync.on_share_downloading_error)
        self._download_manager.progress.connect(
            self._sync.send_download_progress)
        self._download_manager.downloads_status.connect(
            self._sync.send_downloads_status)
        self._download_manager.signal_info_tx.connect(self._on_info_tx)
        self._download_manager.signal_info_rx.connect(self._on_info_rx)
示例#4
0
 def _clear_share_download(self):
     data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
     downloads_dir = get_downloads_dir(data_dir=data_dir, create=True)
     download_name = op.join(downloads_dir, self._current_share_hash)
     if self._is_folder:
         remove_dir(download_name)
     else:
         remove_file(download_name)
示例#5
0
def get_url():
    filename = config.get_main_option("filename")
    if filename is None:
        filename = ensure_unicode(
            join(get_patches_dir(get_data_dir(), create=True), 'patches.db'))
    url = config.get_main_option("sqlalchemy.url")
    url = ensure_unicode(url)
    url = url.format(filename=FilePath(filename))
    return url
示例#6
0
 def _get_full_name(self, cancel=True, existing_file=False):
     data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
     dest_dir = self._dest_dirs.get(self._current_share_hash, data_dir)
     if FilePath(dest_dir) in FilePath(data_dir):
         if not self._renew_dest_dir(cancel, wait=False):
             return ""
         dest_dir = self._dest_dirs.get(self._current_share_hash, data_dir)
     fullname = op.join(dest_dir, self._current_share_name + '.download')
     fullname = FilePath(fullname).longpath
     if not existing_file:
         fullname = get_next_name(fullname)
     return fullname
示例#7
0
 def _relpath(self, path, data_dir=None):
     rel_path = None
     if not data_dir:
         data_dir = FilePath(
             self._cfg.sync_directory if self._cfg else get_data_dir())
     if not path:
         path = FilePath(data_dir)
     if path in data_dir:
         rel_path = op.relpath(path, data_dir)
         if rel_path == os.curdir:
             rel_path = ""
         rel_path = ensure_unicode(rel_path)
     return rel_path
示例#8
0
    def _get_path_relative_to_share(self, file_info):
        data_path = self._cfg.sync_directory if self._cfg else get_data_dir()
        share_dir = get_downloads_dir(data_path)
        rel_path = FilePath(op.relpath(file_info.fullname, share_dir))
        rel_path_list = rel_path.split('/')

        assert rel_path_list, "Must have relative path for share"

        # replace share hash with share name in rel path
        rel_path_list[0] = self._current_share_name
        rel_path = '/'.join(rel_path_list)
        logger.debug("Relative path for shared file %s is %s", file_info,
                     rel_path)
        return rel_path
示例#9
0
    def start_file_download(self, file_info):
        data_path = self._cfg.sync_directory if self._cfg else get_data_dir()
        download_path = op.join(get_copies_dir(data_path), file_info.file_hash)

        logger.info("Initiating downloading of file '%s' to '%s'...",
                    file_info.fullname, download_path)

        def on_success(task):
            self.signals.download_success.emit(task.id, file_info,
                                               download_path)

        def on_failure(task):
            self.signals.download_failure.emit(task.id, file_info)

        if not file_info.size:
            create_empty_file(file_info.fullname)
            self._tasks[self._current_share_hash].remove(file_info.event_uuid)
            self._finish_task_download()
            return

        elif not self._cfg.download_backups:
            self._sync.make_copy_from_existing_files(file_info.file_hash)

        files_info = [{
            "target_file_path":
            self._get_path_relative_to_share(file_info),
            "mtime":
            0,  # mtime == 0 => shared file
            "is_created":
            None,
            "is_deleted":
            None
        }]

        self._download_manager.add_file_download(
            DOWNLOAD_PRIORITY_FILE,
            file_info.event_uuid,
            file_info.size,
            file_info.file_hash,
            download_path,
            'Downloading shared file {}'.format(file_info.name),
            on_success,
            on_failure,
            files_info=files_info,
        )
示例#10
0
 def read_config_file(self):
     need_sync = False
     with open(self.config_file_name, 'rb') as f:
         data = f.read()
     try:
         decrypted_data = xor_with_key(data)
         config = loads(decrypted_data)
         secret = UUID(int=158790876260364472748646807733425668096 +
                       getnode()).bytes
         if is_portable():
             config['user_hash'] = None
             config['user_password_hash'] = None
             config['sync_directory'] = FilePath(get_data_dir())
             config['conflict_file_suffix'] = 'Conflicted copy from {}'\
                 .format(get_device_name())
         else:
             try:
                 if config.get('user_hash', None):
                     config['user_hash'] = xor_with_key(
                         bytes.fromhex(config['user_hash']),
                         secret).decode()
             except Exception as e:
                 logger.debug("Error decoding user hash: %s", e)
                 config["user_hash"] = None
             try:
                 if config.get('user_password_hash', None):
                     config['user_password_hash'] = xor_with_key(
                         bytes.fromhex(config['user_password_hash']),
                         secret).decode()
             except Exception as e:
                 logger.debug("Error decoding user password hash: %s", e)
                 config["user_password_hash"] = None
     except ValueError as e:
         logger.warning("Error: %s", e)
         config = loads(data)
         config["user_hash"] = None
         config["user_password_hash"] = None
         need_sync = True
     return config, need_sync
示例#11
0
    def _move(self):
        data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
        downloads_dir = get_downloads_dir(data_dir=data_dir, create=True)
        download_name = op.join(downloads_dir, self._current_share_hash)
        if not self._renew_dest_dir():
            return

        dest_dir = self._dest_dirs.get(self._current_share_hash, data_dir)
        dest_name = op.join(dest_dir, self._current_share_name)
        dest_name = FilePath(dest_name).longpath
        dest_name = get_next_name(dest_name)
        logger.debug("Move '%s' to '%s'", download_name, dest_name)
        try:
            if FilePath(dest_dir) not in FilePath(data_dir):
                make_dirs(dest_name)
            shutil.move(download_name, dest_name)
        except IOError as e:
            logger.warning(
                "Can't move downloaded shared file to %s. "
                "Reason: %s", dest_name, e)
            self.cancel_share_download(self._current_share_name,
                                       folder_deleted=True)
示例#12
0
    def _renew_dest_dir(self, cancel=True, wait=True):
        logger.debug("Waiting for folder uuid")
        if wait:
            self._folder_uuid_ready.wait()
        folder_uuid = self._dest_uuids[self._current_share_hash]
        logger.debug("Folder uuid got %s", folder_uuid)
        if not folder_uuid:
            return True

        try:
            with self._db.soft_lock():
                path, deleted, excluded = self._db \
                    .get_folder_path_deleted_excluded_by_uuid(folder_uuid)
        except EventsDbBusy:
            logger.debug("Events db busy")
            path = deleted = excluded = None
        if path is None or deleted or excluded:
            reason_str = 'not synced' if path is None \
                else 'deleted' if deleted else 'excluded'
            logger.warning(
                "Can't download shared file '%s' because "
                "dir %s is %s", self._current_share_name,
                self._dest_dirs[self._current_share_hash], reason_str)
            if cancel:
                if deleted:
                    self.cancel_share_download(self._current_share_name,
                                               folder_deleted=True)
                else:
                    self.cancel_share_download(self._current_share_name,
                                               folder_excluded=True)

            return False

        data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
        self._dest_dirs[self._current_share_hash] = op.join(data_dir, path)
        return True
示例#13
0
 def _get_folder_uuid(self, dest_dir):
     data_dir = FilePath(
         self._cfg.sync_directory if self._cfg else get_data_dir())
     if not dest_dir:
         dest_dir = data_dir
     dest_dir = FilePath(dest_dir)
     if dest_dir in data_dir:
         rel_path = self._relpath(dest_dir, data_dir)
         if not rel_path:
             folder_uuid = None
         else:
             try:
                 folder_uuid = self._sync.get_file_uuid(rel_path)
                 if folder_uuid is None:
                     raise FileNotFound("")
                 logger.debug("Folder uuid for path %s is %s", rel_path,
                              folder_uuid)
             except (FileNotFound, EventsDbBusy):
                 logger.warning("No folder uuid for folder %s", dest_dir)
                 raise WebshareHandlerPathNotFoundError(
                     "No folder uuid for folder")
     else:
         folder_uuid = ""
     return folder_uuid
示例#14
0
    def _on_sync_folder_location_button_clicked(self):
        selected_folder = QFileDialog.getExistingDirectory(
            self._dialog, tr('Choose Pvtbox folder location'),
            get_parent_dir(FilePath(self._cfg.sync_directory)))
        selected_folder = ensure_unicode(selected_folder)

        try:
            if not selected_folder:
                raise self._MigrationFailed("Folder is not selected")

            if len(selected_folder + "/Pvtbox") > self._max_root_len:
                if not self._migrate:
                    msgbox(tr("Destination path too long. "
                              "Please select shorter path."),
                           tr("Path too long"),
                           parent=self._dialog)
                raise self._MigrationFailed("Destination path too long")

            free_space = get_free_space(selected_folder)
            selected_folder = get_data_dir(dir_parent=selected_folder,
                                           create=False)
            if FilePath(selected_folder) == FilePath(self._cfg.sync_directory):
                raise self._MigrationFailed("Same path selected")

            if FilePath(selected_folder) in FilePath(self._cfg.sync_directory):
                msgbox(tr("Can't migrate into existing Pvtbox folder.\n"
                          "Please choose other location"),
                       tr("Invalid Pvtbox folder location"),
                       parent=self._dialog)
                raise self._MigrationFailed(
                    "Can't migrate into existing Pvtbox folder")

            if self._size and free_space < self._size:
                logger.debug(
                    "No disk space in %s. Free space: %s. Needed: %s.",
                    selected_folder, free_space, self._size)
                msgbox(tr(
                    "Insufficient disk space for migration to\n{}.\n"
                    "Please clean disk", selected_folder),
                       tr("No disk space"),
                       parent=self._dialog)
                raise self._MigrationFailed(
                    "Insufficient disk space for migration")

            self._migration_cancelled = False
            dialog = QProgressDialog(self._dialog)
            dialog.setWindowTitle(tr('Migrating to new Pvtbox folder'))
            dialog.setWindowIcon(QIcon(':/images/icon.svg'))
            dialog.setModal(True)
            dialog.setMinimum(0)
            dialog.setMaximum(100)
            dialog.setMinimumSize(400, 80)
            dialog.setAutoClose(False)

            def progress(value):
                logger.debug("Migration dialog progress received: %s", value)
                dialog.setValue(value)

            def migration_failed(error):
                logger.warning("Migration failed with error: %s", error)
                msgbox(error,
                       tr('Migration to new Pvtbox folder error'),
                       parent=dialog)
                dialog.cancel()
                self._migration_cancelled = True
                done()

            def cancel():
                logger.debug("Migration dialog cancelled")
                self._migration_cancelled = True
                self._migration.cancel()

            def done():
                logger.debug("Migration done")
                try:
                    self._migration.progress.disconnect(progress)
                    self._migration.failed.disconnect(migration_failed)
                    self._migration.done.disconnect(done)
                    dialog.canceled.disconnect(cancel)
                except Exception as e:
                    logger.warning("Can't disconnect signal %s", e)
                dialog.hide()
                dialog.done(QDialog.Accepted)
                dialog.close()

            self._migration = SyncDirMigration(self._cfg, parent=self._dialog)
            self._migration.progress.connect(progress, Qt.QueuedConnection)
            self._migration.failed.connect(migration_failed,
                                           Qt.QueuedConnection)
            self._migration.done.connect(done, Qt.QueuedConnection)
            dialog.canceled.connect(cancel)
            self._exit_service()
            old_dir = self._cfg.sync_directory
            self._migration.migrate(old_dir, selected_folder)

            def on_finished():
                logger.info("Migration dialog closed")
                if not self._migration_cancelled:
                    logger.debug("Setting new location")
                    self._ui.location_edit.setText(FilePath(selected_folder))

                    disable_file_logging(logger)
                    shutil.rmtree(op.join(old_dir, '.pvtbox'),
                                  ignore_errors=True)
                    set_root_directory(FilePath(selected_folder))
                    enable_file_logging(logger)

                    make_dir_hidden(get_patches_dir(selected_folder))

                self._start_service()

            dialog.finished.connect(on_finished)
            dialog.show()

        except self._MigrationFailed as e:
            logger.warning("Sync dir migration failed. Reason: %s", e)
        finally:
            if self._migrate:
                self._dialog.accept()
示例#15
0
        args = vars(parser.parse_args(new_argv))
    else:
        args = vars(namespace)

    return args


if __name__ == "__main__":
    # for multiprocessing under build pyinstaller
    multiprocessing.freeze_support()

    from common import utils

    utils.is_daemon = True
    utils.get_cfg_dir(create=True)
    utils.get_patches_dir(utils.get_data_dir(create=True), create=True)

    from common.application import Application
    from daemon.application_impl import ApplicationImpl

    args = sys.argv[1:]
    # Parse command line arguments
    args = parseArgs(args)

    # To terminate from console with Ctrl+C
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    Application.set_instance_class(ApplicationImpl)
    Application.start(args)

    print('Exited')