As is checkin of fn-fmsread, fn-fmsnotify. Needs cleanup.
diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py
--- a/infocalypse/__init__.py
+++ b/infocalypse/__init__.py
@@ -155,7 +155,7 @@ from mercurial import commands, util
from infcmds import get_config_info, execute_create, execute_pull, \
execute_push, execute_setup, execute_copy, execute_reinsert, \
- execute_info
+ execute_info, execute_fmsread, execute_fmsnotify
def set_target_version(ui_, repo, opts, params, msg_fmt):
""" INTERNAL: Update TARGET_VERSION in params. """
@@ -301,6 +301,47 @@ def infocalypse_info(ui_, repo, **opts):
params['REQUEST_URI'] = request_uri
execute_info(ui_, repo, params, stored_cfg)
+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
+
+ if opts['listall']:
+ params['FMSREAD'] = 'listall'
+ elif opts['list']:
+ params['FMSREAD'] = 'list'
+ else:
+ params['FMSREAD'] = 'update'
+ params['DRYRUN'] = opts['dryrun']
+ params['REQUEST_URI'] = request_uri
+ execute_fmsread(ui_, repo, params, stored_cfg)
+
+def infocalypse_fmsnotify(ui_, repo, **opts):
+ """ Post an update 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:
+ 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['DRYRUN'] = opts['dryrun']
+ params['INSERT_URI'] = insert_uri
+ execute_fmsnotify(ui_, repo, params, stored_cfg)
+
def infocalypse_setup(ui_, **opts):
""" Setup the extension for use for the first time. """
@@ -359,7 +400,21 @@ cmdtable = {
"fn-info": (infocalypse_info,
[('', 'uri', '', 'request URI'),],
"[options]"),
-
+
+
+ "fn-fmsread": (infocalypse_fmsread,
+ [('', 'uri', '', 'request URI'),
+ ('', 'list', None, 'show repo USKs from trusted fms identities'),
+ ('', 'listall', None, 'show all repo USKs'),
+ ('', 'dryrun', None, "don't update the index cache"),],
+ "[options]"),
+
+ "fn-fmsnotify": (infocalypse_fmsnotify,
+ [('', 'dryrun', None, "don't send fms message"),
+ ('', 'announce', None, "include full URI update"), ]
+ + FCP_OPTS, # Needs to invert the insert uri
+ "[options]"),
+
"fn-setup": (infocalypse_setup,
[('', 'tmpdir', '~/infocalypse_tmp', 'temp directory'),]
+ FCP_OPTS,
diff --git a/infocalypse/config.py b/infocalypse/config.py
--- a/infocalypse/config.py
+++ b/infocalypse/config.py
@@ -26,6 +26,7 @@ import sys
from fcpclient import get_usk_hash, is_usk_file, get_version, \
get_usk_for_usk_version
+from knownrepos import DEFAULT_TRUST, DEFAULT_GROUPS
from mercurial import util
from ConfigParser import ConfigParser
@@ -62,6 +63,10 @@ class Config:
self.request_usks = {}
# repo_id -> insert uri map
self.insert_usks = {}
+ # fms_id -> (usk_hash, ...) map
+ self.fmsread_trust_map = {}
+ self.fmsread_groups = ()
+
self.file_name = None
# Use a dict instead of members to avoid pylint R0902.
@@ -71,6 +76,11 @@ class Config:
self.defaults['TMP_DIR'] = None
self.defaults['DEFAULT_PRIVATE_KEY'] = None
+ self.defaults['FMS_HOST'] = '127.0.0.1'
+ self.defaults['FMS_PORT'] = 1119
+ self.defaults['FMS_ID'] = None # REDFLAG?
+ self.defaults['FMSNOTIFY_GROUP'] = None # REDFLAG?
+
def get_index(self, usk_or_id):
""" Returns the highest known USK version for a USK or None. """
return self.version_table.get(normalize(usk_or_id))
@@ -150,6 +160,23 @@ class Config:
if parser.has_section('insert_usks'):
for repo_id in parser.options('insert_usks'):
cfg.insert_usks[repo_id] = parser.get('insert_usks', repo_id)
+
+ # ignored = fms_id|usk_hash|usk_hash|...
+ if parser.has_section('fmsread_trust_map'):
+ for ordinal in parser.options('fmsread_trust_map'):
+ fields = parser.get('fmsread_trust_map',
+ ordinal).strip().split('|')
+ # REDFLAG: better validation for fms_id, hashes?
+ if fields[0].find('@') == -1:
+ raise ValueError("%s doesn't look like an fms id." %
+ fields[0])
+ if len(fields) < 2:
+ raise ValueError("No USK hashes for fms id: %s?" %
+ fields[0])
+ cfg.fmsread_trust_map[fields[0]] = tuple(fields[1:])
+ else:
+ cfg.fmsread_trust_map = DEFAULT_TRUST
+
if parser.has_section('default'):
if parser.has_option('default','host'):
cfg.defaults['HOST'] = parser.get('default','host')
@@ -161,6 +188,21 @@ class Config:
cfg.defaults['DEFAULT_PRIVATE_KEY'] = (
parser.get('default','default_private_key'))
+ if parser.has_option('default','fms_host'):
+ cfg.defaults['FMS_HOST'] = parser.get('default','fms_host')
+ if parser.has_option('default','fms_port'):
+ cfg.defaults['FMS_PORT'] = parser.getint('default','fms_port')
+ if parser.has_option('default','fms_id'):
+ cfg.defaults['FMS_ID'] = parser.get('default','fms_id')
+ if parser.has_option('default','fmsnotify_group'):
+ cfg.defaults['FMSNOTIFY_GROUP'] = parser.get('default',
+ 'fmsnotify_group')
+ if parser.has_option('default','fmsread_groups'):
+ cfg.fmsread_groups = (parser.get('default','fmsread_groups').
+ strip().split('|'))
+ else:
+ cfg.fmsread_groups = DEFAULT_GROUPS
+
cfg.file_name = file_name
return cfg
@@ -199,6 +241,14 @@ class Config:
parser.set('default', 'tmp_dir', cfg.defaults['TMP_DIR'])
parser.set('default', 'default_private_key',
cfg.defaults['DEFAULT_PRIVATE_KEY'])
+
+ parser.set('default', 'fms_host', cfg.defaults['FMS_HOST'])
+ parser.set('default', 'fms_port', cfg.defaults['FMS_PORT'])
+ parser.set('default', 'fms_id', cfg.defaults['FMS_ID'])
+ parser.set('default', 'fmsnotify_group',
+ cfg.defaults['FMSNOTIFY_GROUP'])
+ parser.set('default', 'fmsread_groups', '|'.join(cfg.fmsread_groups))
+
parser.add_section('index_values')
for repo_id in cfg.version_table:
parser.set('index_values', repo_id, cfg.version_table[repo_id])
@@ -208,6 +258,12 @@ class Config:
parser.add_section('insert_usks')
for repo_id in cfg.insert_usks:
parser.set('insert_usks', repo_id, cfg.insert_usks[repo_id])
+ parser.add_section('fmsread_trust_map')
+ for index, fms_id in enumerate(cfg.fmsread_trust_map):
+ entry = cfg.fmsread_trust_map[fms_id]
+ assert len(entry) > 0
+ parser.set('fmsread_trust_map', str(index),
+ fms_id + '|' + '|'.join(entry))
out_file = open(file_name, 'wb')
try:
diff --git a/infocalypse/fms.py b/infocalypse/fms.py
new file mode 100644
--- /dev/null
+++ b/infocalypse/fms.py
@@ -0,0 +1,381 @@
+""" Code to support sending and receiving update notifications via fms.
+
+ Copyright (C) 2009 Darrell Karbott
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2.0 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks
+"""
+
+import nntplib
+import StringIO
+
+from fcpclient import get_usk_hash, get_version, is_usk_file, \
+ get_usk_for_usk_version
+
+
+MSG_TEMPLATE = """From: %s
+Newsgroups: %s
+Subject: %s
+
+%s"""
+
+# msg_tuple = (sender, group, subject, text)
+def send_msgs(fms_host, fms_port, msg_tuples):
+ """ Send messages via fms.
+ msg_tuple format is: (sender, group, subject, text)
+ """
+
+ server = nntplib.NNTP(fms_host, fms_port)
+
+ try:
+ for msg_tuple in msg_tuples:
+ raw_msg = MSG_TEMPLATE % (msg_tuple[0],
+ msg_tuple[1],
+ msg_tuple[2],
+ msg_tuple[3])
+ in_file = StringIO.StringIO(raw_msg)
+ print raw_msg
+ try:
+ #server.post(in_file)
+ pass
+ finally:
+ in_file.close()
+ finally:
+ server.quit()
+
+
+class IFmsMessageSink:
+ def __init__(self):
+ pass
+
+ def wants_msg(self, group, item):
+ return True
+
+ def recv_fms_msg(self, group, item, lines):
+ pass
+
+def recv_msgs(fms_host, fms_port, msg_sink, groups):
+ """ Read messages from fms. """
+ server = nntplib.NNTP(fms_host, fms_port)
+ try:
+ for group in groups:
+ result = server.group(group)
+ if result[1] == '0':
+ continue
+ # Doesn't return msg lines as shown in python doc?
+ # http://docs.python.org/library/nntplib.html
+ # Is this an fms bug?
+ result, items = server.xover(result[2], result[3])
+ if result.split(' ')[0] != '224':
+ # REDFLAG: untested code path
+ raise Exception(result)
+ for item in items:
+ if not msg_sink.wants_msg(group, item):
+ continue
+ result = server.article(item[0])
+ if result[0].split(' ')[0] != '220':
+ # REDFLAG: untested code path
+ raise Exception(result[0])
+ pos = result[3].index('')
+ lines = []
+ if pos != -1:
+ lines = result[3][pos + 1:]
+ msg_sink.recv_fms_msg(group, item, lines)
+ finally:
+ server.quit()
+
+############################################################
+# Infocalypse specific stuff.
+############################################################
+def clean_nym(fms_id):
+ pos = fms_id.index('@')
+ if pos == -1:
+ return fms_id
+
+ return fms_id[pos + 1:]
+
+def to_msg_string(updates, announcements=None):
+ """ Dump updates and announcements in a format which can
+ be read by parse. """
+ if updates is None:
+ updates = []
+
+ if announcements is None:
+ announcements = []
+
+ # Make sure we always get the same string rep.
+ updates = list(updates)
+ updates.sort()
+ announcements = list(announcements)
+ announcements.sort()
+
+ text = ''
+ for value in announcements:
+ assert is_usk_file(value)
+ text += "A:%s\n" % value
+
+ for update in updates:
+ assert is_hex_string(update[0], 12)
+ assert update[1] >= 0
+ text += "U:%s:%i\n" % (update[0], update[1])
+
+ return text
+
+# A grepper, not a parser...
+def parse(text, is_lines=False):
+ """ Parse updates and announcements from raw text. """
+ if is_lines:
+ lines = text
+ else:
+ lines = text.split('\n')
+
+ announcements = set([])
+ updates = set([])
+
+ for line in lines:
+ line = line.strip() # Handle crlf bs on Windoze.
+ fields = line.split(':')
+ if fields[0] == 'U' and len(fields) >= 3:
+ try:
+ if is_hex_string(fields[1]):
+ updates.add((fields[1], int(fields[2])))
+ except ValueError:
+ continue
+ elif fields[0] == 'A' and len(fields) >= 2:
+ try:
+ if is_usk_file(fields[1]):
+ announcements.add(fields[1])
+ # Implicit update.
+ updates.add((get_usk_hash(fields[1]), get_version(fields[1])))
+ except ValueError:
+ continue
+ # else, silently fail... hmmmm
+
+ # Perhaps a bit too metrosexual...
+ # Make sure you always get the same tuple for a given text.
+ updates = list(updates)
+ updates.sort()
+ announcements = list(announcements)
+ announcements.sort()
+ return (tuple(updates), tuple(announcements))
+
+
+def strip_names(trust_map):
+ clean = {}
+ for nym in trust_map:
+ cleaned = clean_nym(nym)
+ if nym in clean:
+ print "strip_name -- nym appears multiple times w/ different " \
+ + "name part: " + nym
+ clean[cleaned] = list(set(list(trust_map[nym])
+ + clean.get(cleaned, [])))
+ return clean
+
+# REDFLAG: Trust map ids are w/o names
+# 'isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks', not
+# 'djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks'
+class USKIndexUpdateParser(IFmsMessageSink):
+ """ Class which accumulates USK index update notifications
+ from fms messages. """
+ def __init__(self, trust_map):
+ self.trust_map = strip_names(trust_map)
+ self.updates = {}
+
+ def wants_msg(self, group, items):
+ if len(items[5]) != 0:
+ # Skip replies
+ return False
+
+ if clean_nym(items[2]) not in self.trust_map:
+ #print "Not trusted: ", items[2]
+ # Sender not authoritative on any USK.
+ return False
+
+ return True
+
+ def recv_fms_msg(self, group, items, lines):
+ """ recv_messages message callback implementation. """
+ allowed_hashes = self.trust_map[clean_nym(items[2])]
+
+ #print "---\nSender: %s\nSubject: %s\n" % (items[2], items[1])
+ for update in parse(lines, True)[0]:
+ if update[0] in allowed_hashes:
+ # Only update if the nym is trusted *for the specific USK*.
+ #print "UPDATING ---\nSender: %s\nSubject: %s\n" % (items[2], items[1])
+ self.handle_update(update)
+
+ def handle_update(self, update):
+ """ INTERNAL: Handle a single update. """
+ index = update[1]
+ value = self.updates.get(update[0], index)
+ if index >= value:
+ self.updates[update[0]] = index
+
+ def updated(self, previous=None):
+ """ Returns a USK hash -> index map for USKs which
+ have been updated. """
+ if previous is None:
+ previous = {}
+ ret = {}
+ for usk_hash in self.updates:
+ if not usk_hash in previous:
+ ret[usk_hash] = self.updates[usk_hash]
+ continue
+ if self.updates[usk_hash] > previous[usk_hash]:
+ ret[usk_hash] = self.updates[usk_hash]
+
+ return ret
+
+class USKAnnouncementParser(IFmsMessageSink):
+ """ Class which accumulates USK announcement notifications
+ from fms messages. """
+ # None means accept all announcements.
+ def __init__(self, trust_map = None):
+ if not trust_map is None:
+ trust_map = strip_names(trust_map)
+ self.trust_map = trust_map
+ self.usks = {}
+
+ def wants_msg(self, group, items):
+ if len(items[5]) != 0:
+ # Skip replies
+ return False
+
+ if self.trust_map is None:
+ return True
+
+ if clean_nym(items[2]) not in self.trust_map:
+ #print "Not trusted: ", items[2]
+ # Sender not authoritative on any USK.
+ return False
+
+ return True
+
+ def recv_fms_msg(self, group, items, lines):
+ #print "---\nSender: %s\nSubject: %s\n" % (items[2], items[1])
+ for usk in parse(lines, True)[1]:
+ self.handle_announcement(items[2], usk)
+
+ def handle_announcement(self, sender, usk):
+ """ INTERNAL: Handle a single announcement """
+ usk = get_usk_for_usk_version(usk, 0)
+ entry = self.usks.get(usk, [])
+ if not sender in entry:
+ entry.append(sender)
+ self.usks[usk] = entry
+
+HEX_CHARS = frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f'])
+# REQUIRES: Lowercase!
+def is_hex_string(value, length=12):
+ if not length is None:
+ if len(value) != length:
+ raise ValueError("Expected hex string of length: %i" % length)
+ for char in value:
+ if not char in HEX_CHARS:
+ return False
+ return True
+
+############################################################
+
+DEFAULT_SUBJECT = 'Ignore'
+def make_update_msg(fms_id, group, updates, announcements=None,
+ subject=DEFAULT_SUBJECT):
+ print "updates: ", updates
+ print "announcements: ", announcements
+
+ # fms doesn't want to see the full id?
+ fms_id = fms_id.split('@')[0]
+ text = to_msg_string(updates, announcements)
+ return (fms_id, group, subject, text)
+
+############################################################
+
+MSG_FMT = """---
+Sender : %s
+Subject: %s
+Date : %s
+Group : %s
+%s
+---
+"""
+class MsgSink(IFmsMessageSink):
+ def __init__(self):
+ IFmsMessageSink.__init__(self)
+
+ def recv_fms_msg(self, group, item, lines):
+ print MSG_FMT % (item[2], item[1], item[3], group, '\n'.join(lines))
+
+def smoke_test():
+ # trust_map = {'djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks':
+ # ('be68e8feccdd', ),}
+
+
+ trust_map = {'falafel@IxVqeqM0LyYdTmYAf5z49SJZUxr7NtQkOqVYG0hvITw':
+ ('1' * 12, ),
+ 'SDiZ@17fy9sQtAvZI~nwDt5xXJkTZ9KlXon1ucEakK0vOFTc':
+ ('2' * 12, ),
+ }
+
+ parser = USKIndexUpdateParser(trust_map)
+ recv_msgs('127.0.0.1', 11119, parser, ('test',))
+ print
+ print "fms updates:"
+ print parser.updated()
+ print
+ print
+ parser = USKAnnouncementParser(trust_map)
+ recv_msgs('127.0.0.1', 11119, parser, ('test',))
+ print
+ print "fms announcements:"
+ print parser.usks
+ print
+ print
+
+ values0 = ((('be68e8feccdd', 23), ('e246cc31bc42', 3)),
+ ('USK@kRM~jJVREwnN2qnA8R0Vt8HmpfRzBZ0j4rHC2cQ-0hw,'
+ + '2xcoQVdQLyqfTpF2DpkdUIbHFCeL4W~2X1phUYymnhM,AQACAAE/'
+ + 'infocalypse.hgext.R1/12', ))
+
+ # Includes implicit update from announcement.
+ values2 = ((('be68e8feccdd', 12), ('be68e8feccdd', 23), ('e246cc31bc42', 3)),
+ ('USK@kRM~jJVREwnN2qnA8R0Vt8HmpfRzBZ0j4rHC2cQ-0hw,'
+ + '2xcoQVdQLyqfTpF2DpkdUIbHFCeL4W~2X1phUYymnhM,AQACAAE/'
+ + 'infocalypse.hgext.R1/12',))
+
+ # From tuple to string
+ print "---"
+ print values0
+
+ text = to_msg_string(values0[0], values0[1])
+ print "---"
+ # And back
+ print text
+ values1 = parse(text)
+ print "---"
+ print values1
+ # Not values0 because of implicit update.
+ assert values1 == values2
+
+ msg = make_update_msg('djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks'
+ 'test',
+ 'test',
+ values0[0],
+ values0[1])
+ send_msgs('127.0.0.1', 11119, (msg, ))
+
+if __name__ == "__main__":
+ smoke_test()
diff --git a/infocalypse/infcmds.py b/infocalypse/infcmds.py
--- a/infocalypse/infcmds.py
+++ b/infocalypse/infcmds.py
@@ -31,7 +31,8 @@ import time
from mercurial import util
from fcpclient import parse_progress, is_usk, is_ssk, get_version, \
- get_usk_for_usk_version, FCPClient, is_usk_file, is_negative_usk
+ get_usk_for_usk_version, FCPClient, is_usk_file, is_negative_usk, \
+ get_usk_hash
from fcpconnection import FCPConnection, PolledSocket, CONNECTION_STATES, \
get_code, FCPError
@@ -46,6 +47,9 @@ from updatesm import UpdateStateMachine,
from config import Config, DEFAULT_CFG_PATH, normalize
+from fms import USKAnnouncementParser, USKIndexUpdateParser, recv_msgs, \
+ to_msg_string, MSG_TEMPLATE, send_msgs
+
DEFAULT_PARAMS = {
# FCP params
'MaxRetries':3,
@@ -317,7 +321,7 @@ def setup(ui_, repo, params, stored_cfg)
# REDFLAG: Hack to work around 1208 cancel bug. Remove.
if update_sm.params['FREENET_BUILD'] == '1208':
ui_.warn("DISABLING request canceling to work around 1208 FCP bug.\n"
- "This may cause requests to hang. :-(\n")
+ "This may cause requests to hang. :-(\n\n")
disable_cancel(update_sm)
# Patch state machine to re-enable canceling on shutdown.
@@ -410,8 +414,8 @@ def do_key_setup(ui_, update_sm, params,
# Update the inverted insert URI to the latest known version.
params['INVERTED_INSERT_URI'] = get_usk_for_usk_version(
- inverted_uri,
- max_index)
+ inverted_uri,
+ max_index)
# Update the index of the request uri using the stored config.
request_uri = params.get('REQUEST_URI')
@@ -675,7 +679,7 @@ NO_INFO_FMT = """There's no stored infor
USK hash: %s
"""
-INFO_FMT ="""USK hash: %s
+INFO_FMT = """USK hash: %s
index : %i
Request URI:
@@ -704,6 +708,152 @@ def execute_info(ui_, repo, params, stor
ui_.status(INFO_FMT %
(usk_hash, max_index or -1, request_uri, insert_uri))
+def execute_fmsread(ui_, repo, params, stored_cfg):
+ action = params['FMSREAD']
+ if params['VERBOSITY'] >= 2:
+ ui_.status(('Connecting to fms on %s:%i\n'
+ + 'Searching groups: %s\n') %
+ (stored_cfg.defaults['FMS_HOST'],
+ stored_cfg.defaults['FMS_PORT'],
+ ' '.join(stored_cfg.fmsread_groups)))
+
+ if action == 'list' or action == 'listall':
+ if action == 'listall':
+ parser = USKAnnouncementParser()
+ if params['VERBOSITY'] >= 2:
+ ui_.status('Listing all repo USKs.\n')
+ else:
+ trust_map = stored_cfg.fmsread_trust_map.copy() # paranoid copy
+ if params['VERBOSITY'] >= 2:
+ fms_ids = trust_map.keys()
+ fms_ids.sort()
+ ui_.status(("Only listing repo USKs from trusted "
+ + "fms IDs:\n%s\n\n") % '\n'.join(fms_ids))
+ parser = USKAnnouncementParser(trust_map)
+ recv_msgs(stored_cfg.defaults['FMS_HOST'],
+ stored_cfg.defaults['FMS_PORT'],
+ parser,
+ stored_cfg.fmsread_groups)
+ if len(parser.usks) == 0:
+ ui_.status("No USKs found.\n")
+ return
+ ui_.status("\n")
+ for usk in parser.usks:
+ usk_entry = parser.usks[usk]
+ ui_.status("USK Hash: %s\n%s\n%s\n\n" %
+ (get_usk_hash(usk), usk,
+ '\n'.join(usk_entry)))
+ else:
+ trust_map = stored_cfg.fmsread_trust_map.copy() # paranoid copy
+ if params['VERBOSITY'] >= 2:
+ fms_ids = trust_map.keys()
+ fms_ids.sort()
+ ui_.status("Update Trust Map:\n")
+ for fms_id in fms_ids:
+ ui_.status(" %s: %s\n" % (fms_id,
+ ' '.join(trust_map[fms_id])))
+ ui_.status("\n")
+ parser = USKIndexUpdateParser(trust_map)
+ recv_msgs(stored_cfg.defaults['FMS_HOST'],
+ stored_cfg.defaults['FMS_PORT'],
+ parser,
+ stored_cfg.fmsread_groups)
+ changed = parser.updated(stored_cfg.version_table)
+ if len(changed) == 0:
+ ui_.status('No updates found.\n')
+ return
+
+ for usk_hash in changed:
+ ui_.status('%s:%i\n' % (usk_hash, changed[usk_hash]))
+
+ if params['DRYRUN']:
+ ui_.status('Exiting without saving because --dryrun was set.\n')
+ return
+
+ for usk_hash in changed:
+ stored_cfg.update_index(usk_hash, changed[usk_hash])
+
+ Config.to_file(stored_cfg)
+ ui_.status('Saved updated indices.\n')
+ # Back map to uris and print
+ # show message if current repo was updated
+ # support dry run
+
+# REDFLAG: Catch this in config when depersisting?
+def is_none(value):
+ return value is None or value == 'None'
+
+def execute_fmsnotify(ui_, repo, params, stored_cfg):
+ update_sm = None
+ try:
+ # REDFLAG: dci, test non uri keys
+ update_sm = setup(ui_, repo, params, stored_cfg)
+ request_uri, dummy = do_key_setup(ui_, update_sm,
+ params, stored_cfg)
+ if request_uri is None:
+ ui_.warn("Only works for USK file URIs.\n")
+ return
+
+ usk_hash = get_usk_hash(request_uri)
+ index = stored_cfg.get_index(usk_hash)
+ # REDFLAG: DCI. Needed?
+ request_uri = get_usk_for_usk_version(request_uri, index)
+ if index is None:
+ ui_.warn("Can't notify because there's no stored index "
+ + "for %s.\n" % usk_hash)
+ return
+
+ if is_none(stored_cfg.defaults['FMS_ID']):
+ ui_.warn("Can't notify because the fms ID isn't set in the "
+ + "config file.\n")
+ ui_.status("Update the fms_id = line and try again.\n")
+ return
+
+ if is_none(stored_cfg.defaults['FMSNOTIFY_GROUP']):
+ ui_.warn("Can't notify because fms group isn't set in the "
+ + "config file.\n")
+ ui_.status("Update the fmsnotify_group = line and try again.\n")
+ return
+
+ if params['ANNOUNCE']:
+ text = to_msg_string(None, (request_uri, ))
+ else:
+ text = to_msg_string(((usk_hash, index), ))
+
+ subject = 'Update:' + '/'.join(request_uri.split('/')[1:])
+ msg_tuple = (stored_cfg.defaults['FMS_ID'],
+ stored_cfg.defaults['FMSNOTIFY_GROUP'],
+ subject,
+ text)
+
+ if params['VERBOSITY'] >= 2:
+ ui_.status('Connecting to fms on %s:%i\n' %
+ (stored_cfg.defaults['FMS_HOST'],
+ stored_cfg.defaults['FMS_PORT']))
+
+ ui_.status('Group: %s\nSubject: %s\n%s\n' %
+ (stored_cfg.defaults['FMSNOTIFY_GROUP'],
+ subject, text))
+
+ if params['VERBOSITY'] >= 5:
+ raw_msg = MSG_TEMPLATE % (msg_tuple[0],
+ msg_tuple[1],
+ msg_tuple[2],
+ msg_tuple[3])
+ ui_.status('--- Raw Message ---\n%s\n---\n' % raw_msg)
+
+ if params['DRYRUN']:
+ ui_.status('Exiting without sending because --dryrun was set.\n')
+ return
+
+ send_msgs(stored_cfg.defaults['FMS_HOST'],
+ stored_cfg.defaults['FMS_PORT'],
+ (msg_tuple, ))
+
+ ui_.status('Notification message sent.\n')
+ finally:
+ cleanup(update_sm)
+
def setup_tmp_dir(ui_, tmp):
""" INTERNAL: Setup the temp directory. """
tmp = os.path.expanduser(tmp)
diff --git a/infocalypse/knownrepos.py b/infocalypse/knownrepos.py
new file mode 100644
--- /dev/null
+++ b/infocalypse/knownrepos.py
@@ -0,0 +1,15 @@
+
+# If you maintain a repository that doesn't contain illicit content
+# let me know and I'll add it here.
+KNOW_REPOS = (
+ 'USK@kRM~jJVREwnN2qnA8R0Vt8HmpfRzBZ0j4rHC2cQ-0hw,'
+ + '2xcoQVdQLyqfTpF2DpkdUIbHFCeL4W~2X1phUYymnhM,AQACAAE/'
+ + 'infocalypse.hgext.R1/23',
+ )
+
+DEFAULT_TRUST = {
+ 'djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks':
+ ('be68e8feccdd', ),
+ }
+
+DEFAULT_GROUPS = ('test', )