def link(config_name: str, running): """Links Maestral with your Dropbox account.""" if not is_maestral_linked(config_name): if running == "gui": click.echo( "Maestral GUI is already running. Please link through the GUI." ) return if running == "daemon": # stop daemon stop_maestral_daemon(config_name) from maestral.main import Maestral Maestral(run=False) if running == "daemon": # start daemon start_daemon_subprocess(config_name) else: click.echo("Maestral is already linked.")
def get_maestral_daemon_proxy(config_name="maestral", fallback=False): """ Returns a proxy of the running Maestral daemon. If fallback == True, a new instance of Maestral will be returned when the daemon cannot be reached. This can be dangerous if the GUI is running at the same time. """ pid, location, p_type = get_maestral_process_info(config_name) if p_type == "daemon": maestral_daemon = Pyro4.Proxy(URI.format(config_name, location)) try: maestral_daemon._pyroBind() return maestral_daemon except Pyro4.errors.CommunicationError: maestral_daemon._pyroRelease() if fallback: from maestral.main import Maestral m = Maestral(run=False) return m else: raise Pyro4.errors.CommunicationError
def start_daemon_subprocess(config_name): """Starts the Maestral daemon as a subprocess (by calling `start_maestral_daemon`). This command will create a new daemon on each run. Take care not to sync the same directory with multiple instances of Meastral! You can use `get_maestral_process_info` to check if either a Meastral gui or daemon is already running for the given `config_name`. :param str config_name: The name of maestral configuration to use. :returns: Popen object instance. """ import subprocess from maestral.main import Maestral if Maestral.pending_link() or Maestral.pending_dropbox_folder(): # run onboarding m = Maestral(run=False) m.create_dropbox_directory() m.select_excluded_folders() click.echo("Starting Maestral...", nl=False) proc = subprocess.Popen("maestral sync -c {}".format(config_name), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # check if the subprocess is still running after 1 sec try: proc.wait(timeout=1) click.echo("\rStarting Maestral... " + FAILED) except subprocess.TimeoutExpired: click.echo("\rStarting Maestral... " + OK) return proc
def m(): config_name = "test-config" m = Maestral(config_name) m.log_level = logging.DEBUG # link with given token access_token = os.environ.get("DROPBOX_ACCESS_TOKEN") refresh_token = os.environ.get("DROPBOX_REFRESH_TOKEN") if access_token: m.client._init_sdk_with_token(access_token=access_token) m.client.auth._access_token = access_token m.client.auth._token_access_type = "legacy" elif refresh_token: m.client._init_sdk_with_token(refresh_token=refresh_token) m.client.auth._refresh_token = refresh_token m.client.auth._token_access_type = "offline" else: raise RuntimeError( "Either access token or refresh token must be given as environment " "variable DROPBOX_ACCESS_TOKEN or DROPBOX_REFRESH_TOKEN." ) # get corresponding Dropbox ID and store in keyring for other processes res = m.client.get_account_info() m.client.auth._account_id = res.account_id m.client.auth.loaded = True m.client.auth.save_creds() # set local Dropbox directory home = get_home_dir() local_dropbox_dir = generate_cc_name(home + "/Dropbox", suffix="test runner") m.create_dropbox_directory(local_dropbox_dir) # acquire test lock and perform initial sync lock = DropboxTestLock(m) if not lock.acquire(timeout=60 * 60): raise TimeoutError("Could not acquire test lock") # create / clean our temporary test folder m.test_folder_dbx = "/sync_tests" m.test_folder_local = m.to_local_path(m.test_folder_dbx) try: m.client.remove(m.test_folder_dbx) except NotFoundError: pass m.client.make_dir(m.test_folder_dbx) # start syncing m.start_sync() wait_for_idle(m) # return synced and running instance yield m # stop syncing and clean up remote folder m.stop_sync() try: m.client.remove(m.test_folder_dbx) except NotFoundError: pass try: m.client.remove("/.mignore") except NotFoundError: pass # remove all shared links res = m.client.list_shared_links() for link in res.links: m.revoke_shared_link(link.url) # remove creds from system keyring m.client.auth.delete_creds() # remove local files and folders delete(m.dropbox_path) remove_configuration(m.config_name) # release lock lock.release()
def start(foreground: bool, verbose: bool, config_name: str) -> None: # ---- run setup if necessary ------------------------------------------------------ # We run the setup in the current process. This avoids starting a subprocess despite # running with the --foreground flag, prevents leaving a zombie process if the setup # fails with an exception and does not confuse systemd. from maestral.main import Maestral m = Maestral(config_name, log_to_stdout=verbose) if m.pending_link: # this may raise KeyringAccessError link_dialog(m) if m.pending_dropbox_folder: path = select_dbx_path_dialog(config_name, allow_merge=True) while True: try: m.create_dropbox_directory(path) break except OSError: click.echo( "Could not create folder. Please make sure that you have " "permissions to write to the selected location or choose a " "different location." ) exclude_folders_q = click.confirm( "Would you like to exclude any folders from syncing?", ) if exclude_folders_q: click.echo( "Please choose which top-level folders to exclude. You can exclude\n" 'individual files or subfolders later with "maestral excluded add".\n' ) click.echo("Loading...", nl=False) # get all top-level Dropbox folders entries = m.list_folder("/", recursive=False) excluded_items: List[str] = [] click.echo("\rLoading... Done") # paginate through top-level folders, ask to exclude for e in entries: if e["type"] == "FolderMetadata": yes = click.confirm( 'Exclude "{path_display}" from sync?'.format(**e) ) if yes: path_lower = cast(str, e["path_lower"]) excluded_items.append(path_lower) m.set_excluded_items(excluded_items) # free resources del m if foreground: # stop daemon process after setup and restart in our current process stop_maestral_daemon_process(config_name) start_maestral_daemon(config_name, log_to_stdout=verbose, start_sync=True) else: # start daemon process click.echo("Starting Maestral...", nl=False) res = start_maestral_daemon_process( config_name, log_to_stdout=verbose, start_sync=True ) if res == Start.Ok: click.echo("\rStarting Maestral... " + OK) elif res == Start.AlreadyRunning: click.echo("\rStarting Maestral... Already running.") else: click.echo("\rStarting Maestral... " + FAILED) click.echo("Please check logs for more information.")
def m(): m = Maestral("test-config") m._conf.save() yield m remove_configuration(m.config_name)
def __init__(self, pending_link=True, parent=None): super(self.__class__, self).__init__(parent=parent) # load user interface layout from .ui file uic.loadUi(SETUP_DIALOG_PATH, self) self.app_icon = QtGui.QIcon(APP_ICON_PATH) self.labelIcon_0.setPixmap(icon_to_pixmap(self.app_icon, 170)) self.labelIcon_1.setPixmap(icon_to_pixmap(self.app_icon, 70)) self.labelIcon_2.setPixmap(icon_to_pixmap(self.app_icon, 70)) self.labelIcon_3.setPixmap(icon_to_pixmap(self.app_icon, 100)) self.mdbx = None self.folder_items = [] # resize dialog buttons width = self.pushButtonAuthPageCancel.width() * 1.1 for b in (self.pushButtonAuthPageLink, self.pussButtonDropboxPathUnlink, self.pussButtonDropboxPathSelect, self.pushButtonFolderSelectionBack, self.pushButtonFolderSelectionSelect, self.pushButtonAuthPageCancel, self.pussButtonDropboxPathCalcel, self.pushButtonClose): b.setMinimumWidth(width) b.setMaximumWidth(width) # set up combobox self.dropbox_location = osp.dirname(CONF.get( "main", "path")) or get_home_dir() relative_path = self.rel_path(self.dropbox_location) folder_icon = get_native_item_icon(self.dropbox_location) self.comboBoxDropboxPath.addItem(folder_icon, relative_path) self.comboBoxDropboxPath.insertSeparator(1) self.comboBoxDropboxPath.addItem(QtGui.QIcon(), "Other...") self.comboBoxDropboxPath.currentIndexChanged.connect(self.on_combobox) self.dropbox_folder_dialog = QtWidgets.QFileDialog(self) self.dropbox_folder_dialog.setAcceptMode( QtWidgets.QFileDialog.AcceptOpen) self.dropbox_folder_dialog.setFileMode(QtWidgets.QFileDialog.Directory) self.dropbox_folder_dialog.setOption( QtWidgets.QFileDialog.ShowDirsOnly, True) self.dropbox_folder_dialog.fileSelected.connect(self.on_new_dbx_folder) self.dropbox_folder_dialog.rejected.connect( lambda: self.comboBoxDropboxPath.setCurrentIndex(0)) # connect buttons to callbacks self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.pushButtonLink.clicked.connect(self.on_link) self.pushButtonAuthPageCancel.clicked.connect(self.abort) self.pushButtonAuthPageLink.clicked.connect(self.on_auth_clicked) self.pussButtonDropboxPathCalcel.clicked.connect(self.abort) self.pussButtonDropboxPathSelect.clicked.connect( self.on_dropbox_location_selected) self.pussButtonDropboxPathUnlink.clicked.connect( self.unlink_and_go_to_start) self.pushButtonFolderSelectionBack.clicked.connect( self.stackedWidget.slideInPrev) self.pushButtonFolderSelectionSelect.clicked.connect( self.on_folders_selected) self.pushButtonClose.clicked.connect(self.accept) self.listWidgetFolders.itemChanged.connect( self.update_select_all_checkbox) self.selectAllCheckBox.clicked.connect(self.on_select_all_clicked) self.labelDropboxPath.setText(self.labelDropboxPath.text().format( CONF.get("main", "default_dir_name"))) # check if we are already authenticated, skip authentication if yes if not pending_link: self.labelDropboxPath.setText(""" <html><head/><body> <p align="left"> Your Dropbox folder has been moved or deleted from its original location. Maestral will not work properly until you move it back. It used to be located at: </p><p align="left">{0}</p> <p align="left"> To move it back, click "Quit" below, move the Dropbox folder back to its original location, and launch Maestral again. </p> <p align="left"> To re-download your Dropbox, please select a location for your Dropbox folder below. Maestral will create a new folder named "{1}" in the selected location.</p> <p align="left"> To unlink your Dropbox account from Maestral, click "Unlink" below.</p> </body></html> """.format(CONF.get("main", "path"), CONF.get("main", "default_dir_name"))) self.pussButtonDropboxPathCalcel.setText("Quit") self.stackedWidget.setCurrentIndex(2) self.stackedWidgetButtons.setCurrentIndex(2) self.mdbx = Maestral(run=False) self.mdbx.client.get_account_info() else: self.stackedWidget.setCurrentIndex(0) self.stackedWidgetButtons.setCurrentIndex(0)
def m(): m = Maestral("test-config") m.log_level = logging.DEBUG m._conf.save() yield m remove_configuration(m.config_name)