diff --git a/itemmanager.py b/itemmanager.py index a9fc204..e0168fc 100644 --- a/itemmanager.py +++ b/itemmanager.py @@ -33,7 +33,7 @@ class ItemManager: item.pop('dirty', None) item.pop('updated_at', None) - response = self.standard_notes.sync(dirty_items) + response = self.sn_api.sync(dirty_items) self.mapResponseItemsToLocalItems(response['response_items']) self.mapResponseItemsToLocalItems(response['saved_items'], metadata_only=True) @@ -85,6 +85,6 @@ class ItemManager: item['dirty'] = True self.syncItems() - def __init__(self, username, password): - self.standard_notes = StandardNotesAPI(username, password) + def __init__(self, sn_api): + self.sn_api = sn_api self.syncItems() diff --git a/sn-fuse.py b/sn_fuse.py similarity index 93% rename from sn-fuse.py rename to sn_fuse.py index 2a15b94..eec0b8e 100644 --- a/sn-fuse.py +++ b/sn_fuse.py @@ -12,9 +12,9 @@ from datetime import datetime from fuse import FUSE, FuseOSError, Operations, LoggingMixIn from itemmanager import ItemManager -class StandardNotesFS(LoggingMixIn, Operations): - def __init__(self, path='.'): - self.item_manager = ItemManager('tanner@domain.com', 'complexpass') +class StandardNotesFUSE(LoggingMixIn, Operations): + def __init__(self, sn_api, path='.'): + self.item_manager = ItemManager(sn_api) self.uid = os.getuid() self.gid = os.getgid() @@ -135,4 +135,4 @@ if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) - fuse = FUSE(StandardNotesFS(), argv[1], foreground=True, nothreads=True) + fuse = FUSE(StandardNotesFUSE(), argv[1], foreground=True, nothreads=True) diff --git a/standardnotes-fs.py b/standardnotes-fs.py deleted file mode 100644 index ed773a1..0000000 --- a/standardnotes-fs.py +++ /dev/null @@ -1,86 +0,0 @@ -import appdirs -import argparse -import logging -import os -import pathlib -import sys - -from configparser import ConfigParser -from getpass import getpass - -from api import StandardNotesAPI - -OFFICIAL_SERVER_URL = 'https://sync.standardnotes.org' -APP_NAME = 'standardnotes-fs' -logging.basicConfig(level=logging.DEBUG) - -# path settings -cfg_env = os.environ.get('SN_FS_CONFIG_PATH') -CONFIG_PATH = cfg_env if cfg_env else appdirs.user_config_dir(APP_NAME) -CONFIG_FILE = os.path.join(CONFIG_PATH, APP_NAME + '.conf') -CONFIG_FILE = pathlib.PurePath(CONFIG_PATH, APP_NAME + '.conf') - -def parse_options(): - parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('--username', - help='Standard Notes username to log in with') - parser.add_argument('--password', - help='Standard Notes password to log in with\n' - 'NOTE: It is NOT recommended to use this! The\n' - ' password may be stored in history, so\n' - ' use the password prompt instead.') - parser.add_argument('--sync-url', - help='URL of Standard File sync server. Defaults to:\n' - ''+OFFICIAL_SERVER_URL) - parser.add_argument('--no-config-file', action='store_true', - help='Don\'t load or create a config file') - parser.add_argument('--config', - help='Specify a config file location. Defaults to:\n' - ''+str(CONFIG_FILE)) - return parser.parse_args() - -def main(): - args = parse_options() - config = ConfigParser() - config['DEFAULT'] = {} - keys = {} - - if not args.no_config_file: - config_file = args.config if args.config else CONFIG_FILE - config_file = pathlib.Path(config_file) - - try: - config_file.parent.mkdir(mode=0o0700, parents=True, exist_ok=True) - except OSError: - err_msg = 'Error creating directory "%s".' - logging.critical(err_msg % str(config_file.parent)) - sys.exit(1) - - try: - with config_file.open() as f: - config.read_file(f) - except OSError: - err_msg = 'Config file "%s" not found.' - logging.debug(err_msg % str(config_file)) - - if config.has_option('user', 'sync_url'): - sync_url = config.get('user', 'sync_url') - else: - sync_url = args.sync_url if args.sync_url else OFFICIAL_SERVER_URL - - if config.has_option('user', 'username') and config.has_section('keys'): - username = config.get('user', 'username') - keys = dict(config.items('keys')) - else: - username = args.username if args.username else \ - input('Please enter your Standard Notes username: ') - password = args.password if args.password else \ - getpass('Please enter your password (hidden): ') - - sn_api = StandardNotesAPI(sync_url, username) - if not keys: - keys = sn_api.genKeys(password) - sn_api.signIn(keys) - -if __name__ == '__main__': - main() diff --git a/standardnotes_fs.py b/standardnotes_fs.py new file mode 100644 index 0000000..8b7f582 --- /dev/null +++ b/standardnotes_fs.py @@ -0,0 +1,132 @@ +import appdirs +import argparse +import logging +import os +import pathlib +import sys + +from configparser import ConfigParser +from getpass import getpass + +from api import StandardNotesAPI +from sn_fuse import StandardNotesFUSE +from fuse import FUSE + +OFFICIAL_SERVER_URL = 'https://sync.standardnotes.org' +APP_NAME = 'standardnotes-fs' +logging.basicConfig(level=logging.DEBUG) + +# path settings +cfg_env = os.environ.get('SN_FS_CONFIG_PATH') +CONFIG_PATH = cfg_env if cfg_env else appdirs.user_config_dir(APP_NAME) +CONFIG_FILE = pathlib.PurePath(CONFIG_PATH, APP_NAME + '.conf') + +def parse_options(): + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('mountpoint', nargs='?', help='local mountpoint folder') + parser.add_argument('--username', + help='Standard Notes username to log in with') + parser.add_argument('--password', + help='Standard Notes password to log in with\n' + 'NOTE: It is NOT recommended to use this! The\n' + ' password may be stored in history, so\n' + ' use the password prompt instead.') + parser.add_argument('--sync-url', + help='URL of Standard File sync server. Defaults to:\n' + ''+OFFICIAL_SERVER_URL) + parser.add_argument('--no-config-file', action='store_true', + help='don\'t load or create a config file') + parser.add_argument('--config', + help='specify a config file location. Defaults to:\n' + ''+str(CONFIG_FILE)) + parser.add_argument('--logout', action='store_true', + help='delete login credentials saved in config and quit') + return parser.parse_args() + +def main(): + args = parse_options() + config = ConfigParser() + keys = {} + + config_file = args.config if args.config else CONFIG_FILE + config_file = pathlib.Path(config_file) + + # logout and quit if wanted + if args.logout: + try: + config_file.unlink() + print('Config file deleted.') + except OSError: + print('Already logged out.') + sys.exit(0) + + # make sure mountpoint is specified + if not args.mountpoint: + logging.critical('No mountpoint specified.') + sys.exit(1) + + # load config file settings + if not args.no_config_file: + try: + config_file.parent.mkdir(mode=0o0700, parents=True, exist_ok=True) + except OSError: + err_msg = 'Error creating directory "%s".' + logging.critical(err_msg % str(config_file.parent)) + sys.exit(1) + + try: + with config_file.open() as f: + config.read_file(f) + except OSError: + err_msg = 'Unable to read config file "%s".' + logging.info(err_msg % str(config_file)) + + # figure out all login params + if args.sync_url: + sync_url = args.sync_url + elif config.has_option('user', 'sync_url'): + sync_url = config.get('user', 'sync_url') + else: + sync_url = OFFICIAL_SERVER_URL + + if config.has_option('user', 'username') \ + and config.has_section('keys') \ + and not args.username \ + and not args.password: + username = config.get('user', 'username') + keys = dict(config.items('keys')) + else: + username = args.username if args.username else \ + input('Please enter your Standard Notes username: ') + password = args.password if args.password else \ + getpass('Please enter your password (hidden): ') + + # log the user in + try: + sn_api = StandardNotesAPI(sync_url, username) + if not keys: + keys = sn_api.genKeys(password) + sn_api.signIn(keys) + login_success = True + except: + err_msg = 'Failed to log into account "%s".' + logging.critical(err_msg % username) + login_success = False + + if login_success: + fuse = FUSE(StandardNotesFUSE(sn_api), args.mountpoint, foreground=True, nothreads=True) + + # write settings back if good, clear if not + if not args.no_config_file: + config.read_dict(dict(user=dict(sync_url=sync_url, username=username), + keys=keys)) + try: + with config_file.open(mode='w+') as f: + if login_success: + config.write(f) + except OSError: + err_msg = 'Unable to write config file "%s".' + logging.error(err_msg % str(config_file)) + +if __name__ == '__main__': + main()