(Steve Dougherty)
2013-06-20: Merge previous head with fixed merge. Merge previous head with fixed merge.
diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py --- a/infocalypse/__init__.py +++ b/infocalypse/__init__.py @@ -339,555 +339,15 @@ d kar bott at com cast dot net import os from commands import * + from mercurial import commands, extensions, util, hg, dispatch, discovery from mercurial.i18n import _ + import freenetrepo -from infcmds import get_config_info, execute_create, execute_pull, \ - execute_push, execute_setup, execute_copy, execute_reinsert, \ - execute_info - -from fmscmds import execute_fmsread, execute_fmsnotify, get_uri_from_hash, \ - execute_setupfms - -from sitecmds import execute_putsite, execute_genkey -from wikicmds import execute_wiki, execute_wiki_apply -from arccmds import execute_arc_create, execute_arc_pull, execute_arc_push, \ - execute_arc_reinsert - -from config import read_freesite_cfg -from validate import is_hex_string, is_fms_id - -def set_target_version(ui_, repo, opts, params, msg_fmt): - """ INTERNAL: Update TARGET_VERSION in params. """ - - revs = opts.get('rev') or None - if not revs is None: - for rev in revs: - repo.changectx(rev) # Fail if we don't have the rev. - - params['TO_VERSIONS'] = tuple(revs) - ui_.status(msg_fmt % ' '.join([ver[:12] for ver in revs])) - else: - # REDFLAG: get rid of default versions arguments? - params['TO_VERSIONS'] = tuple([hexlify(head) for head in repo.heads()]) - #print "set_target_version -- using all head" - #print params['TO_VERSIONS'] - -def infocalypse_create(ui_, repo, **opts): - """ Create a new Infocalypse repository in Freenet. """ - params, stored_cfg = get_config_info(ui_, opts) - - insert_uri = '' - attributes = None - if opts['uri'] != '' and opts['wot'] != '': - ui_.warn("Please specify only one of --uri or --wot.\n") - return - elif opts['uri'] != '': - insert_uri = opts['uri'] - elif opts['wot'] != '': - # Expecting wot_id/repo_name.R<redundancy num>/edition/ - wot_id, repo_desc = opts['wot'].split('/', 1) - - import wot - - ui_.status("Querying WoT for local identities.\n") - - attributes = wot.resolve_local_identity(ui_, wot_id) - if attributes is None: - # Something went wrong; the function already printed an error. - return - - ui_.status('Found {0}@{1}\n'.format(attributes['Nickname'], - attributes['Identity'])) - - insert_uri = attributes['InsertURI'] - - # LCWoT returns URIs with a "freenet:" prefix, and WoT does not. The - # rest of Infocalypse does not support the prefix. The local way to fix - # this is to remove it here, but the more flexible way that is also - # more work is to expand support to the rest of Infocalypse. - # TODO: More widespread support for "freenet:" URI prefix. - prefix = "freenet:" - if insert_uri.startswith(prefix): - insert_uri = insert_uri[len(prefix):] - - # URI is USK@key/WebOfTrust/<edition>, but we only want USK@key - insert_uri = insert_uri.split('/', 1)[0] - insert_uri += '/' + repo_desc - - # Add "vcs" context. No-op if the identity already has it. - msg_params = {'Message':'AddContext', - 'Identity': attributes['Identity'], - 'Context': 'vcs'} - - import fcp - node = fcp.FCPNode() - vcs_response =\ - node.fcpPluginMessage(async=False, - plugin_name="plugins.WebOfTrust.WebOfTrust", - plugin_params=msg_params)[0] - - if vcs_response['header'] != 'FCPPluginReply' or\ - 'Replies.Message' not in vcs_response or\ - vcs_response['Replies.Message'] != 'ContextAdded': - ui_.warn("Failed to add context. Got {0}\n.".format(vcs_response)) - return - - # TODO: Would it be friendlier to include the nickname as well? - stored_cfg.set_wot_identity(stored_cfg.get_request_uri(repo.root), - attributes['Identity']) - else: - ui_.warn("Please set the insert key with either --uri or --wot.\n") - - set_target_version(ui_, repo, opts, params, - "Only inserting to version(s): %s\n") - params['INSERT_URI'] = insert_uri - inserted_to = execute_create(ui_, repo, params, stored_cfg) - - # TODO: Move into some function. How to separate local success context? - if inserted_to is not None and attributes is not None and \ - stored_cfg.has_wot_identity(stored_cfg.get_request_uri(repo.root)): - import wot - wot.update_repo_listing(ui_, attributes['Identity']) - -def infocalypse_copy(ui_, repo, **opts): - """ Copy an Infocalypse repository to a new URI. """ - params, stored_cfg = get_config_info(ui_, opts) - - insert_uri = opts['inserturi'] - if insert_uri == '': - # REDFLAG: fix parameter definition so that it is required? - ui_.warn("Please set the insert URI with --inserturi.\n") - return - - request_uri = opts['requesturi'] - if request_uri == '': - request_uri = stored_cfg.get_request_uri(repo.root) - if not request_uri: - ui_.warn("There is no stored request URI for this repo.\n" - "Please set one with the --requesturi option.\n") - return - - params['INSERT_URI'] = insert_uri - params['REQUEST_URI'] = request_uri - execute_copy(ui_, repo, params, stored_cfg) - -def infocalypse_reinsert(ui_, repo, **opts): - """ Reinsert the current version of an Infocalypse repository. """ - params, stored_cfg = get_config_info(ui_, opts) - - request_uri = opts['uri'] - if request_uri == '': - request_uri = stored_cfg.get_request_uri(repo.root) - if not request_uri: - ui_.warn("There is no stored request URI for this repo.\n" - "Do a fn-pull from a repository USK and try again.\n") - return - - level = opts['level'] - if level < 1 or level > 5: - ui_.warn("level must be 1,2,3,4 or 5.\n") - return - - insert_uri = stored_cfg.get_dir_insert_uri(repo.root) - if not insert_uri: - if level == 1 or level == 4: - ui_.warn(("You can't re-insert at level %i without the " - + "insert URI.\n") % level) - return - - ui_.status("No insert URI. Will skip re-insert " - +"of top key.\n") - insert_uri = None - - params['INSERT_URI'] = insert_uri - params['REQUEST_URI'] = request_uri - params['REINSERT_LEVEL'] = level - execute_reinsert(ui_, repo, params, stored_cfg) - -def infocalypse_pull(ui_, repo, **opts): - """ Pull from an Infocalypse repository in Freenet. - """ - params, stored_cfg = get_config_info(ui_, opts) - - if opts['hash']: - # Use FMS to lookup the uri from the repo hash. - if opts['uri'] != '': - ui_.warn("Ignoring --uri because --hash is set!\n") - if len(opts['hash']) != 1: - raise util.Abort("Only one --hash value is allowed.") - params['FMSREAD_HASH'] = opts['hash'][0] - params['FMSREAD_ONLYTRUSTED'] = bool(opts['onlytrusted']) - request_uri = get_uri_from_hash(ui_, repo, params, stored_cfg) - elif opts['wot']: - import wot - if opts['truster']: - truster = opts['truster'] - else : - truster = stored_cfg.get_wot_identity( - stored_cfg.get_dir_insert_uri(repo.root)) - # TODO: Require repo name, not full path as part of the --wot. Look - # it up from the XML. - # TODO: Insert XML. - - # Expecting <id stuff>/reponame - wot_id, repo_name = opts['wot'].split('/', 1) - - # TODO: How to handle redundancy? Does Infocalypse automatically try - # an R0 if an R1 fails? - - repositories = wot.read_repo_listing(ui_, truster, wot_id) - if repo_name not in repositories: - ui_.warn("Could not find repository named \"{0}\".\n".format(repo_name)) - return - - request_uri = repositories[repo_name] - else: - request_uri = opts['uri'] - - if request_uri == '': - request_uri = stored_cfg.get_request_uri(repo.root) - if not request_uri: - ui_.warn("There is no stored request URI for this repo.\n" - "Please set one with the --uri option.\n") - return - - params['REQUEST_URI'] = request_uri - # Hmmmm... can't really implement rev. - execute_pull(ui_, repo, params, stored_cfg) - -def infocalypse_push(ui_, repo, **opts): - """ Push to an Infocalypse repository in Freenet. """ - params, stored_cfg = get_config_info(ui_, opts) - insert_uri = opts['uri'] - if insert_uri == '': - insert_uri = stored_cfg.get_dir_insert_uri(repo.root) - if not insert_uri: - ui_.warn("There is no stored insert URI for this repo.\n" - "Please set one with the --uri option.\n") - return - - set_target_version(ui_, repo, opts, params, - "Only pushing to version(s): %s\n") - params['INSERT_URI'] = insert_uri - #if opts['requesturi'] != '': - # # DOESN'T search the insert uri index. - # ui_.status(("Copying from:\n%s\nTo:\n%s\n\nThis is an " - # + "advanced feature. " - # + "I hope you know what you're doing.\n") % - # (opts['requesturi'], insert_uri)) - # params['REQUEST_URI'] = opts['requesturi'] - - inserted_to = execute_push(ui_, repo, params, stored_cfg) - # TODO: Messy. - if inserted_to is not None and stored_cfg.has_wot_identity(stored_cfg - .get_request_uri(repo.root)): - import wot - wot.update_repo_listing(ui_, stored_cfg.get_wot_identity(stored_cfg - .get_request_uri(repo.root))) - -def infocalypse_info(ui_, repo, **opts): - """ Display information about an Infocalypse repository. - """ - # FCP not required. Hmmm... Hack - opts['fcphost'] = '' - opts['fcpport'] = 0 - params, stored_cfg = get_config_info(ui_, opts) - request_uri = opts['uri'] - if request_uri == '': - request_uri = stored_cfg.get_request_uri(repo.root) - if not request_uri: - ui_.warn("There is no stored request URI for this repo.\n" - "Please set one with the --uri option.\n") - return - - params['REQUEST_URI'] = request_uri - execute_info(ui_, repo, params, stored_cfg) - -def parse_trust_args(params, opts): - """ INTERNAL: Helper function to parse --hash and --fmsid. """ - if opts.get('hash', []) == []: - raise util.Abort("Use --hash to set the USK hash.") - if len(opts['hash']) != 1: - raise util.Abort("Only one --hash value is allowed.") - if not is_hex_string(opts['hash'][0]): - raise util.Abort("[%s] doesn't look like a USK hash." % - opts['hash'][0]) - - if opts.get('fmsid', []) == []: - raise util.Abort("Use --fmsid to set the FMS id.") - if len(opts['fmsid']) != 1: - raise util.Abort("Only one --fmsid value is allowed.") - if not is_fms_id(opts['fmsid'][0]): - raise util.Abort("[%s] doesn't look like an FMS id." - % opts['fmsid'][0]) - - params['FMSREAD_HASH'] = opts['hash'][0] - params['FMSREAD_FMSID'] = opts['fmsid'][0] - -def parse_fmsread_subcmd(params, opts): - """ INTERNAL: Parse subcommand for fmsread.""" - if opts['listall']: - params['FMSREAD'] = 'listall' - elif opts['list']: - params['FMSREAD'] = 'list' - elif opts['showtrust']: - params['FMSREAD'] = 'showtrust' - elif opts['trust']: - params['FMSREAD'] = 'trust' - parse_trust_args(params, opts) - elif opts['untrust']: - params['FMSREAD'] = 'untrust' - parse_trust_args(params, opts) - else: - params['FMSREAD'] = 'update' - -def infocalypse_fmsread(ui_, repo, **opts): - """ Read repository update information from fms. - """ - # FCP not required. Hmmm... Hack - opts['fcphost'] = '' - opts['fcpport'] = 0 - params, stored_cfg = get_config_info(ui_, opts) - request_uri = opts['uri'] - if request_uri == '': - request_uri = stored_cfg.get_request_uri(repo.root) - if not request_uri: - ui_.status("There is no stored request URI for this repo.\n") - request_uri = None - parse_fmsread_subcmd(params, opts) - params['DRYRUN'] = opts['dryrun'] - params['REQUEST_URI'] = request_uri - execute_fmsread(ui_, params, stored_cfg) - -def infocalypse_fmsnotify(ui_, repo, **opts): - """ Post a msg with the current repository USK index to fms. - """ - params, stored_cfg = get_config_info(ui_, opts) - insert_uri = stored_cfg.get_dir_insert_uri(repo.root) - if not insert_uri and not (opts['submitbundle'] or - opts['submitwiki']): - ui_.warn("You can't notify because there's no stored " - + "insert URI for this repo.\n" - + "Run from the directory you inserted from.\n") - return - - params['ANNOUNCE'] = opts['announce'] - params['SUBMIT_BUNDLE'] = opts['submitbundle'] - params['SUBMIT_WIKI'] = opts['submitwiki'] - if params['SUBMIT_WIKI'] or params['SUBMIT_BUNDLE']: - request_uri = stored_cfg.get_request_uri(repo.root) - if not request_uri: - ui_.warn("There is no stored request URI for this repo.\n") - raise util.Abort("No request URI.") - params['REQUEST_URI'] = request_uri - - params['DRYRUN'] = opts['dryrun'] - params['INSERT_URI'] = insert_uri - execute_fmsnotify(ui_, repo, params, stored_cfg) - -MSG_BAD_INDEX = 'You must set --index to a value >= 0.' -def infocalypse_putsite(ui_, repo, **opts): - """ Insert an update to a freesite. - """ - - if opts['createconfig']: - if opts['wiki']: - raise util.Abort("Use fn-wiki --createconfig.") - params = {'SITE_CREATE_CONFIG':True} - execute_putsite(ui_, repo, params) - return - - params, stored_cfg = get_config_info(ui_, opts) - if opts['key'] != '': # order important - params['SITE_KEY'] = opts['key'] - if not (params['SITE_KEY'].startswith('SSK') or - params['SITE_KEY'] == 'CHK@'): - raise util.Abort("--key must be a valid SSK " - + "insert key or CHK@.") - - params['ISWIKI'] = opts['wiki'] - read_freesite_cfg(ui_, repo, params, stored_cfg) - - try: - # --index not required for CHK@ - if not params['SITE_KEY'].startswith('CHK'): - params['SITE_INDEX'] = int(opts['index']) - if params['SITE_INDEX'] < 0: - raise ValueError() - else: - params['SITE_INDEX'] = -1 - except ValueError: - raise util.Abort(MSG_BAD_INDEX) - except TypeError: - raise util.Abort(MSG_BAD_INDEX) - - params['DRYRUN'] = opts['dryrun'] - - if not params.get('SITE_KEY', None): - insert_uri = stored_cfg.get_dir_insert_uri(repo.root) - if not insert_uri: - ui_.warn("You don't have the insert URI for this repo.\n" - + "Supply a private key with --key or fn-push " - + "the repo.\n") - return # REDFLAG: hmmm... abort? - params['SITE_KEY'] = 'SSK' + insert_uri.split('/')[0][3:] - - execute_putsite(ui_, repo, params) - -def infocalypse_wiki(ui_, repo, **opts): - """ View and edit the current repository as a wiki. """ - if os.getcwd() != repo.root: - raise util.Abort("You must be in the repository root directory.") - - subcmds = ('run', 'createconfig', 'apply') - required = sum([bool(opts[cmd]) for cmd in subcmds]) - if required == 0: - raise util.Abort("You must specify either --run, " + - "--createconfig, --apply") - if required > 1: - raise util.Abort("Use either --run, --createconfig, or --apply") - - if opts['apply'] != '': - params, stored_cfg = get_config_info(ui_, opts) - params['REQUEST_URI'] = opts['apply'] - execute_wiki_apply(ui_, repo, params, stored_cfg) - return - - if opts['fcphost'] != '' or opts['fcpport'] != 0: - raise util.Abort("--fcphost, --fcpport only for --apply") - - # hmmmm.... useless copy? - params = {'WIKI' : [cmd for cmd in subcmds if opts[cmd]][0], - 'HTTP_PORT': opts['http_port'], - 'HTTP_BIND': opts['http_bind']} - execute_wiki(ui_, repo, params) - -def infocalypse_genkey(ui_, **opts): - """ Print a new SSK key pair. """ - params, dummy = get_config_info(ui_, opts) - execute_genkey(ui_, params) - -def infocalypse_setup(ui_, **opts): - """ Setup the extension for use for the first time. """ - - execute_setup(ui_, - opts['fcphost'], - opts['fcpport'], - opts['tmpdir']) - - if not opts['nofms']: - execute_setupfms(ui_, opts) - else: - ui_.status("Skipped FMS configuration because --nofms was set.\n") - - if not opts['nowot']: - import wot - wot.execute_setup_wot(ui_, opts) - else: - ui_.status("Skipped WoT configuration because --nowot was set.\n") - -def infocalypse_setupfms(ui_, **opts): - """ Setup or modify the fms configuration. """ - # REQUIRES config file. - execute_setupfms(ui_, opts) - - -# TODO: Why ui with trailing underscore? Is there a global "ui" somewhere? -def infocalypse_setupwot(ui_, **opts): - import wot - wot.execute_setup_wot(ui_, opts) - - -#----------------------------------------------------------" -def do_archive_create(ui_, opts, params, stored_cfg): - """ fn-archive --create.""" - insert_uri = opts['uri'] - if insert_uri == '': - raise util.Abort("Please set the insert URI with --uri.") - - params['INSERT_URI'] = insert_uri - params['FROM_DIR'] = os.getcwd() - execute_arc_create(ui_, params, stored_cfg) - -def do_archive_push(ui_, opts, params, stored_cfg): - """ fn-archive --push.""" - insert_uri = opts['uri'] - if insert_uri == '': - insert_uri = ( - stored_cfg.get_dir_insert_uri(params['ARCHIVE_CACHE_DIR'])) - if not insert_uri: - ui_.warn("There is no stored insert URI for this archive.\n" - "Please set one with the --uri option.\n") - raise util.Abort("No Insert URI.") - - params['INSERT_URI'] = insert_uri - params['FROM_DIR'] = os.getcwd() - - execute_arc_push(ui_, params, stored_cfg) - -def do_archive_pull(ui_, opts, params, stored_cfg): - """ fn-archive --pull.""" - request_uri = opts['uri'] - - if request_uri == '': - request_uri = ( - stored_cfg.get_request_uri(params['ARCHIVE_CACHE_DIR'])) - if not request_uri: - ui_.warn("There is no stored request URI for this archive.\n" - "Please set one with the --uri option.\n") - raise util.Abort("No request URI.") - - params['REQUEST_URI'] = request_uri - params['TO_DIR'] = os.getcwd() - execute_arc_pull(ui_, params, stored_cfg) - -ILLEGAL_FOR_REINSERT = ('uri', 'aggressive', 'nosearch') -def do_archive_reinsert(ui_, opts, params, stored_cfg): - """ fn-archive --reinsert.""" - illegal = [value for value in ILLEGAL_FOR_REINSERT - if value in opts and opts[value]] - if illegal: - raise util.Abort("--uri, --aggressive, --nosearch illegal " + - "for reinsert.") - request_uri = stored_cfg.get_request_uri(params['ARCHIVE_CACHE_DIR']) - if request_uri is None: - ui_.warn("There is no stored request URI for this archive.\n" + - "Run fn-archive --pull first!.\n") - raise util.Abort(" No request URI, can't re-insert") - - insert_uri = stored_cfg.get_dir_insert_uri(params['ARCHIVE_CACHE_DIR']) - params['REQUEST_URI'] = request_uri - params['INSERT_URI'] = insert_uri - params['FROM_DIR'] = os.getcwd() # hmmm not used. - params['REINSERT_LEVEL'] = 3 - execute_arc_reinsert(ui_, params, stored_cfg) - -ARCHIVE_SUBCMDS = {'create':do_archive_create, - 'push':do_archive_push, - 'pull':do_archive_pull, - 'reinsert':do_archive_reinsert} -ARCHIVE_CACHE_DIR = '.ARCHIVE_CACHE' -def infocalypse_archive(ui_, **opts): - """ Commands to maintain a non-hg incremental archive.""" - subcmd = [value for value in ARCHIVE_SUBCMDS if opts[value]] - if len(subcmd) > 1: - raise util.Abort("--create, --pull, --push are mutally exclusive. " + - "Only specify one.") - if len(subcmd) > 0: - subcmd = subcmd[0] - else: - subcmd = "pull" - - params, stored_cfg = get_config_info(ui_, opts) - params['ARCHIVE_CACHE_DIR'] = os.path.join(os.getcwd(), ARCHIVE_CACHE_DIR) - - if not subcmd in ARCHIVE_SUBCMDS: - raise util.Abort("Unhandled subcommand: " + subcmd) - - # 2 qt? - ARCHIVE_SUBCMDS[subcmd](ui_, opts, params, stored_cfg) +_freenetschemes = ('freenet', ) +for _scheme in _freenetschemes: + hg.schemes[_scheme] = freenetrepo #----------------------------------------------------------" @@ -1019,6 +479,7 @@ cmdtable = { "fn-setup": (infocalypse_setup, [('', 'tmpdir', '~/infocalypse_tmp', 'temp directory'), ('', 'nofms', None, 'skip FMS configuration'), + ('', 'nowot', None, 'skip WoT configuration'), ('', 'fmsid', '', "fmsid (only part before '@'!)"), ('', 'timeout', 30, "fms socket timeout in seconds")] + WOT_OPTS diff --git a/infocalypse/infcmds.py b/infocalypse/infcmds.py --- a/infocalypse/infcmds.py +++ b/infocalypse/infcmds.py @@ -495,12 +495,8 @@ def is_redundant(uri): ############################################################ # User feedback? success, failure? def execute_create(ui_, repo, params, stored_cfg): - """ - Run the create command. - Return the URIs the repo was inserted to, or None in the case of an error. - """ + """ Run the create command. """ update_sm = None - inserted_to = None try: update_sm = setup(ui_, repo, params, stored_cfg) # REDFLAG: Do better. @@ -520,9 +516,9 @@ def execute_create(ui_, repo, params, st run_until_quiescent(update_sm, params['POLL_SECS']) if update_sm.get_state(QUIESCENT).arrived_from(((FINISHING,))): - inserted_to = update_sm.get_state(INSERTING_URI).get_request_uris() ui_.status("Inserted to:\n%s\n" % - '\n'.join(inserted_to)) + '\n'.join(update_sm.get_state(INSERTING_URI). + get_request_uris())) else: ui_.status("Create failed.\n") @@ -530,8 +526,6 @@ def execute_create(ui_, repo, params, st finally: cleanup(update_sm) - return inserted_to - # REDFLAG: LATER: make this work without a repo? def execute_copy(ui_, repo, params, stored_cfg): """ Run the copy command. """ @@ -612,14 +606,10 @@ def execute_reinsert(ui_, repo, params, cleanup(update_sm) def execute_push(ui_, repo, params, stored_cfg): - """ - Run the push command. - Return the URIs the repo was inserted to if it changed, or None otherwise. - """ + """ Run the push command. """ assert params.get('REQUEST_URI', None) is None update_sm = None - inserted_to = None try: update_sm = setup(ui_, repo, params, stored_cfg) request_uri, is_keypair = do_key_setup(ui_, update_sm, params, @@ -636,9 +626,9 @@ def execute_push(ui_, repo, params, stor run_until_quiescent(update_sm, params['POLL_SECS']) if update_sm.get_state(QUIESCENT).arrived_from(((FINISHING,))): - inserted_to = update_sm.get_state(INSERTING_URI).get_request_uris() ui_.status("Inserted to:\n%s\n" % - '\n'.join(inserted_to)) + '\n'.join(update_sm.get_state(INSERTING_URI). + get_request_uris())) else: extra = '' if update_sm.ctx.get('UP_TO_DATE', False): @@ -649,8 +639,6 @@ def execute_push(ui_, repo, params, stor finally: cleanup(update_sm) - return inserted_to - def execute_pull(ui_, repo, params, stored_cfg): """ Run the pull command. """ update_sm = None diff --git a/infocalypse/wot.py b/infocalypse/wot.py --- a/infocalypse/wot.py +++ b/infocalypse/wot.py @@ -1,3 +1,4 @@ +import string import fcp from config import Config import xml.etree.ElementTree as ET @@ -320,9 +321,14 @@ def to_freemail_address(identity): # addresses are not case sensitive, so some clients may mangle case. # See https://github.com/zidel/Freemail/blob/v0.2.2.1/docs/spec/spec.tex#L32 - for item in identity.iteritem(): + for item in identity.iteritems(): if item[1] == 'Freemail' and item[0].startswith('Context'): - return identity['Nickname'] + '@' + b32encode(base64decode( - identity['Identity'])) + 'freemail' + re_encode = b32encode(base64decode(identity['Identity'])) + # Remove trailing '=' padding. + re_encode = re_encode.rstrip('=') + + # Freemail addresses are lower case. + return string.lower(identity['Nickname'] + '@' + re_encode + + '.freemail') return None