(Steve Dougherty)
2013-08-08: Track the latest known repo list edition. Track the latest known repo list edition. This takes some responsibility off the node for fetching an up-to-date edition, because it is no longer always given edition 0 to start from. This does introduce some clumsiness as if the node follows redirects it does not return the used edition. This should probably be replaced with a WoT property once WoT properties can be inserted without causing an insert, as it will avoid starting the repo list at edition 0.
diff --git a/infocalypse/config.py b/infocalypse/config.py --- a/infocalypse/config.py +++ b/infocalypse/config.py @@ -128,8 +128,13 @@ class Config: # repo id -> publisher WoT identity self.wot_identities = {} # TODO: Should this be keyed by str(WoT_ID) ? - # WoT public key hash -> Freemail password + # WoT identity ID -> Freemail password self.freemail_passwords = {} + # WoT identity ID -> last known repo list edition. + # TODO: Once WoT allows setting a property without triggering an + # immediate insert, this can move to a WoT property. (Can then query + # remote identities! Easier bootstrapping than from edition 0.) + self.repo_list_editions = {} # fms_id -> (usk_hash, ...) map self.fmsread_trust_map = DEFAULT_TRUST.copy() self.fmsread_groups = DEFAULT_GROUPS @@ -269,6 +274,23 @@ class Config: "Run hg fn-setupfreemail --truster {0}\n" .format(wot_identity)) + def set_repo_list_edition(self, wot_identity, edition): + """ + Set the repository list edition for the given WoT identity. + :type wot_identity: WoT_ID + """ + self.repo_list_editions[wot_identity.identity_id] = edition + + def get_repo_list_edition(self, wot_identity): + """ + Return the repository list edition associated with the given WoT + identity. Return 0 if one is not set. + """ + if wot_identity.identity_id in self.repo_list_editions: + return self.repo_list_editions[wot_identity.identity_id] + else: + return 0 + # Hmmm... really nescessary? def get_dir_insert_uri(self, repo_dir): """ Return the insert USK for repo_dir or None. """ @@ -383,6 +405,11 @@ class Config: cfg.freemail_passwords[wot_id] = parser.get( 'freemail_passwords', wot_id) + if parser.has_section('repo_list_editions'): + for wot_id in parser.options('repo_list_editions'): + cfg.repo_list_editions[wot_id] = int(parser.get( + 'repo_list_editions', wot_id)) + # ignored = fms_id|usk_hash|usk_hash|... if parser.has_section('fmsread_trust_map'): cfg.fmsread_trust_map.clear() # Wipe defaults. @@ -467,6 +494,10 @@ class Config: for wot_id in cfg.freemail_passwords: parser.set('freemail_passwords', wot_id, cfg.freemail_passwords[ wot_id]) + parser.add_section('repo_list_editions') + for wot_id in cfg.repo_list_editions: + parser.set('repo_list_editions', wot_id, cfg.repo_list_editions[ + wot_id]) parser.add_section('fmsread_trust_map') for index, fms_id in enumerate(cfg.fmsread_trust_map): entry = cfg.fmsread_trust_map[fms_id] diff --git a/infocalypse/wot.py b/infocalypse/wot.py --- a/infocalypse/wot.py +++ b/infocalypse/wot.py @@ -311,8 +311,10 @@ def update_repo_listing(ui, for_identity # TODO: Somehow store the edition, perhaps in ~/.infocalypse. WoT # properties are apparently not appropriate. + cfg = Config.from_ui(ui) + insert_uri.name = 'vcs' - insert_uri.edition = '0' + insert_uri.edition = cfg.get_repo_list_edition(for_identity) ui.status("Inserting with URI:\n{0}\n".format(insert_uri)) uri = node.put(uri=str(insert_uri), mimetype='application/xml', @@ -322,6 +324,8 @@ def update_repo_listing(ui, for_identity ui.warn("Failed to update repository listing.") else: ui.status("Updated repository listing:\n{0}\n".format(uri)) + cfg.set_repo_list_edition(for_identity, USK(uri).edition) + Config.to_file(cfg) def build_repo_list(ui, for_identity): @@ -369,19 +373,19 @@ def read_repo_listing(ui, identity): :type identity: WoT_ID """ + cfg = Config.from_ui(ui) uri = identity.request_uri.clone() uri.name = 'vcs' - uri.edition = 0 + uri.edition = cfg.get_repo_list_edition(identity) # TODO: Set and read vcs edition property. - node = fcp.FCPNode() - ui.status("Fetching {0}\n".format(uri)) - # TODO: What exception can this throw on failure? Catch it, - # print its description, and return None. - mime_type, repo_xml, msg = node.get(str(uri), priority=1, - followRedirect=True) + ui.status("Fetching.\n") + mime_type, repo_xml, msg = fetch_edition(uri) + ui.status("Fetched {0}.\n".format(uri)) - ui.status("Parsing.\n") + cfg.set_repo_list_edition(identity, uri.edition) + Config.to_file(cfg) + repositories = {} root = fromstring(repo_xml) for repository in root.iterfind('repository'): @@ -395,6 +399,30 @@ def read_repo_listing(ui, identity): return repositories +def fetch_edition(uri): + """ + Fetch a USK uri, following redirects. Change the uri edition to the one + fetched. + :type uri: USK + """ + node = fcp.FCPNode() + # Following a redirect automatically does not provide the edition used, + # so manually following redirects is required. + # TODO: Is there ever legitimately more than one redirect? + try: + return node.get(str(uri), priority=1) + except fcp.FCPGetFailed, e: + # Error code 27 is permanent redirect: there's a newer edition of + # the USK. + # https://wiki.freenetproject.org/FCPv2/GetFailed#Fetch_Error_Codes + if not e.info['Code'] == 27: + raise + + uri.edition = USK(e.info['RedirectURI']).edition + + return node.get(str(uri), priority=1) + + def resolve_pull_uri(ui, path, truster): """ Return a pull URI for the given path.