Exemplo n.º 1
0
def update_repo_listing(ui, for_identity, fcphost=None, fcpport=None):
    """
    Insert list of repositories published by the given identity.

    :type for_identity: Local_WoT_ID
    """
    # TODO: Somehow store the edition, perhaps in ~/.infocalypse. WoT
    # properties are apparently not appropriate.

    cfg = Config.from_ui(ui)

    # TODO: WoT property containing repo list edition. Used when requesting.
    # Version number to support possible format changes.
    root = ET.Element('vcs', {'version': '0'})

    ui.status("Updating repo listing for '%s'\n" % for_identity)

    for request_uri in build_repo_list(ui, for_identity):
        repo = ET.SubElement(root, 'repository', {
            'vcs': VCS_NAME,
        })
        repo.text = request_uri

    # TODO: Nonstandard IP and port from cfg
    node = fcp.FCPNode(**get_fcpopts(fcphost=fcphost, fcpport=fcpport))
    atexit.register(node.shutdown)

    insert_uri = for_identity.insert_uri.clone()

    insert_uri.name = 'vcs'
    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',
                   data=ET.tostring(root),
                   priority=1)

    if uri is None:
        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)
Exemplo n.º 2
0
def _request_matching_identities_lcwot(truster,
                                       context="vcs",
                                       prefix=None,
                                       fcpopts={}):
    """
    Return a response for a partial nickname request.
    """
    nickname_prefix, key_prefix = _parse_name(prefix)
    # TODO: Support different FCP IP / port.
    node = fcp.FCPNode(**fcpopts)
    atexit.register(node.shutdown)

    # Test for GetIdentitiesByPartialNickname support. currently LCWoT-only.
    # src/main/java/plugins/WebOfTrust/fcp/GetIdentitiesByPartialNickname
    # TODO: LCWoT allows limiting by context; should we make sure otherwise?
    # Feature request for WoT: https://bugs.freenetproject.org/view.php?id=6184

    # GetIdentitiesByPartialNickname does not support empty nicknames.
    if not nickname_prefix:
        raise util.Abort(
            "Partial matching in LCWoT does not support empty nicknames. Got {}"
            .format(prefix))

    params = {
        'Message': 'GetIdentitiesByPartialNickname',
        'Truster': truster.identity_id,
        'PartialNickname': nickname_prefix + '*',
        'PartialID': key_prefix,
        'MaxIdentities': 2,
        'Context': 'vcs'
    }

    response = \
        node.fcpPluginMessage(plugin_name="plugins.WebOfTrust.WebOfTrust",
                              plugin_params=params)[0]

    if response['header'] != 'FCPPluginReply' or \
            'Replies.Message' not in response:
        raise util.Abort('Unexpected reply. Got {0}\n'.format(response))

    return response
Exemplo n.º 3
0
def _request_matching_identities(truster,
                                 context="vcs",
                                 prefix=None,
                                 fcpopts={}):
    """
    Return a list of responses for all matching identities.
    """
    node = fcp.FCPNode(**fcpopts)
    atexit.register(node.shutdown)
    params = {
        'Message': 'GetIdentities',  # GetIdentitiesByScore is much slower
        'Truster': truster.identity_id
    }

    if context:
        params['Context'] = context

    response = node.fcpPluginMessage(
        plugin_name="plugins.WebOfTrust.WebOfTrust", plugin_params=params)[0]

    if response['header'] != 'FCPPluginReply' or \
       'Replies.Message' not in response:
        raise util.Abort('Unexpected reply. Got {0}\n'.format(response))
    nIDs = int(response["Replies.Identities.Amount"])

    def get_attribute(attribute, i, message):
        return message['Replies.Identities.{0}.{1}'.format(i, attribute)]

    responses = []
    for i in range(nIDs):
        identifier = "@".join(
            (get_attribute("Nickname", i,
                           response), get_attribute("Identity", i, response)))
        if not prefix or identifier.startswith(prefix):
            responses.append(
                _get_identity(identifier, truster, exact=True,
                              fcpopts=fcpopts))
    return responses
Exemplo n.º 4
0
def fetch_edition(uri, fcphost=None, fcpport=None):
    """
    Fetch a USK uri, following redirects. Change the uri edition to the one
    fetched.
    :type uri: USK
    """
    node = fcp.FCPNode(**get_fcpopts(fcphost=fcphost, fcpport=fcpport))
    atexit.register(node.shutdown)
    # 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)
Exemplo n.º 5
0
import fcp

# ------------------------------------------
# state where our FCP port is

fcpHost = "127.0.0.1"


# ------------------------------------------
# create a node connection object
# 
# we're setting a relatively high verbosity so you
# can see the traffic

node = fcp.FCPNode(host=fcpHost, verbosity=fcp.DETAIL)


# -----------------------------------------------
# now, perform a simple direct insert of a string

# val = raw_input("Please enter a string to insert: ")
# ksk = raw_input("Please enter a short KSK key name: ")
val = "testinsert"
ksk = "testinsertkey" + uuid.uuid4().hex

uri = "KSK@" + ksk
print "Inserting %s, containing '%s'" % (uri, val)

# do the put - note that 'data=' inserts a string directly
# note too that mimetype is optional, defaulting to text/plain
Exemplo n.º 6
0
put/get ksk ssk usk dir freesite → data file async

wrong node ip
different IP Port
"""

import sys, os, tempfile, random, uuid
import fcp
fcpHost = "127.0.0.1"
workdir = tempfile.mkdtemp()
os.chdir(workdir)
myid = str(uuid.uuid4().hex)
with open("index.html", "w") as f:
    f.write("<html><head><title>Test</title></head><body>Test</body></html>\n")

node = fcp.FCPNode(host=fcpHost, verbosity=fcp.FATAL)


def genkey(*args, **kwds):
    '''

    >>> public, private = genkey()
    >>> fcp.node.uriIsPrivate(public)
    False
    >>> fcp.node.uriIsPrivate(private)
    True
    '''
    return node.genkey(*args, **kwds)


def getUniqueId(*args, **kwds):
Exemplo n.º 7
0
def _get_local_identity(wot_identifier, fcpopts={}):
    """
    Internal.

    Return (id_number, FCP reply) from WoT for a local identity matching the
    identifier. Abort if anything but exactly one match is found.

    :type wot_identifier: str
    """
    nickname_prefix, key_prefix = _parse_name(wot_identifier)

    node = fcp.FCPNode(**fcpopts)
    atexit.register(node.shutdown)
    response = \
        node.fcpPluginMessage(plugin_name="plugins.WebOfTrust.WebOfTrust",
                              plugin_params={'Message':
                                             'GetOwnIdentities'})[0]

    if response['header'] != 'FCPPluginReply' or \
            'Replies.Message' not in response or \
            response['Replies.Message'] != 'OwnIdentities':
        raise util.Abort("Unexpected reply. Got {0}\n.".format(response))

    # Find nicknames starting with the supplied nickname prefix.
    prefix = 'Replies.Nickname'
    # Key: nickname, value (id_num, public key hash).
    matches = {}
    for key in response.iterkeys():
        if key.startswith(prefix) and \
                response[key].startswith(nickname_prefix):

            # Key is Replies.Nickname<number>, where number is used in
            # the other attributes returned for that identity.
            id_num = key[len(prefix):]

            nickname = response[key]
            pubkey_hash = response['Replies.Identity{0}'.format(id_num)]

            matches[nickname] = (id_num, pubkey_hash)

    # Remove matching nicknames not also matching the (possibly partial)
    # public key hash.
    for key in matches.keys():
        # public key hash is second member of value tuple.
        if not matches[key][1].startswith(key_prefix):
            del matches[key]

    if len(matches) > 1:
        raise util.Abort("'{0}' matches more than one local identity.".format(
            wot_identifier))

    if len(matches) == 0:
        raise util.Abort(
            "No local identities match '{0}'.".format(wot_identifier))

    assert len(matches) == 1

    # id_num is first member of value tuple.
    only_key = matches.keys()[0]
    id_num = matches[only_key][0]

    return id_num, response
Exemplo n.º 8
0
def _get_identity(wot_identifier, truster, exact=False, fcpopts={}):
    """
    Internal.

    Return an FCP reply from WoT for an identity on the truster's trust list
    matching the identifier. Abort if anything but exactly one match is found.

    :type wot_identifier: str
    :type truster: Local_WoT_ID
    :param exact: Whether to match the wot_identifier exactly or use it as prefix.
    """
    nickname_prefix, key_prefix = _parse_name(wot_identifier)
    # TODO: Support different FCP IP / port.
    node = fcp.FCPNode(**fcpopts)
    atexit.register(node.shutdown)

    if not exact:
        # Test for GetIdentitiesByPartialNickname support. currently LCWoT-only.
        # src/main/java/plugins/WebOfTrust/fcp/GetIdentitiesByPartialNickname
        # TODO: LCWoT allows limiting by context; should we make sure otherwise?
        # Feature request for WoT: https://bugs.freenetproject.org/view.php?id=6184

        # GetIdentitiesByPartialNickname does not support empty nicknames.
        try:
            response = _request_matching_identities_lcwot(
                truster, context="vcs", prefix=wot_identifier, fcpopts=fcpopts)
            if response['Replies.Message'] == 'Identities':
                matches = response['Replies.IdentitiesMatched']
            else:
                raise util.Abort("WoT does not support partial matching.")
        except util.Abort:
            all_responses = _request_matching_identities(truster,
                                                         prefix=wot_identifier,
                                                         fcpopts=fcpopts)
            matches = len(all_responses)
            if matches:
                response = all_responses[0]

        if matches == 0:
            raise util.Abort(
                "No identities match '{0}'.".format(wot_identifier))
        elif matches == 1:
            return response
        else:
            # TODO: Ask the user to choose interactively (select 1, 2, 3, ...)
            raise util.Abort(
                "'{0}' matches more than one identity.".format(wot_identifier))

    # exact matching requested. The key_prefix must be the complete key.
    # key_prefix must be a complete key for the lookup to succeed.
    params = {
        'Message': 'GetIdentity',
        'Truster': truster.identity_id,
        'Identity': key_prefix
    }
    response = \
        node.fcpPluginMessage(plugin_name="plugins.WebOfTrust.WebOfTrust",
                              plugin_params=params)[0]

    if response['Replies.Message'] == 'Error':
        # Searching by exact public key hash, not matching.
        raise util.Abort("No identity has the complete public key hash '{0}'. "
                         "({1}). Error: {2}".format(
                             key_prefix, wot_identifier,
                             response.get('Replies.Message', "")))

    # There should be only one result.
    # Depends on https://bugs.freenetproject.org/view.php?id=5729
    return response
Exemplo n.º 9
0
def infocalypse_create(ui_, repo, local_identity=None, **opts):
    """ Create a new Infocalypse repository in Freenet.
    :type local_identity: Local_WoT_ID
    :param local_identity: If specified the new repository is associated with
                           that identity.
    """
    params, stored_cfg = get_config_info(ui_, opts)

    if opts['uri'] and opts['wot']:
        ui_.warn("Please specify only one of --uri or --wot.\n")
        return
    elif opts['uri']:
        insert_uri = parse_repo_path(opts['uri'])
    elif opts['wot']:
        opts['wot'] = parse_repo_path(opts['wot'])
        nick_prefix, repo_name, repo_edition = opts['wot'].split('/', 2)

        if not repo_name.endswith('.R1') and not repo_name.endswith('.R0'):
            ui_.warn("Warning: Creating repository without redundancy. (R0 or"
                     " R1)\n")

        from wot_id import Local_WoT_ID

        local_identity = Local_WoT_ID(nick_prefix)

        insert_uri = local_identity.insert_uri.clone()

        insert_uri.name = repo_name
        insert_uri.edition = repo_edition
        # Before passing along into execute_create().
        insert_uri = str(insert_uri)
    else:
        ui_.warn("Please set the insert key with either --uri or --wot.\n")
        return

    # This is a WoT repository.
    if local_identity:
        # Prompt whether to replace in the case of conflicting names.
        from wot import build_repo_list

        request_usks = build_repo_list(ui_, local_identity)
        names = map(lambda x: USK(x).get_repo_name(), request_usks)
        new_name = USK(insert_uri).get_repo_name()

        if new_name in names:
            replace = ui_.prompt("A repository with the name '{0}' is already"
                                 " published by {1}. Replace it? [y/N]".format(
                                     new_name, local_identity),
                                 default='n')

            if replace.lower() != 'y':
                raise util.Abort("A repository with this name already exists.")

            # Remove the existing repository from each configuration section.
            existing_usk = request_usks[names.index(new_name)]

            existing_dir = None
            for directory, request_usk in stored_cfg.request_usks.iteritems():
                if request_usk == existing_usk:
                    if existing_dir:
                        raise util.Abort("Configuration lists the same "
                                         "request USK multiple times.")
                    existing_dir = directory

            assert existing_dir

            existing_hash = normalize(existing_usk)

            # Config file changes will not be written until a successful insert
            # below.
            del stored_cfg.version_table[existing_hash]
            del stored_cfg.request_usks[existing_dir]
            del stored_cfg.insert_usks[existing_hash]
            del stored_cfg.wot_identities[existing_hash]

        # Add "vcs" context. No-op if the identity already has it.
        msg_params = {
            'Message': 'AddContext',
            'Identity': local_identity.identity_id,
            'Context': 'vcs'
        }

        import fcp
        import wot
        node = fcp.FCPNode(**wot.get_fcpopts(fcphost=opts["fcphost"],
                                             fcpport=opts["fcpport"]))
        atexit.register(node.shutdown)
        vcs_response =\
            node.fcpPluginMessage(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':
            raise util.Abort(
                "Failed to add context. Got {0}\n.".format(vcs_response))

    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)

    if inserted_to and local_identity:
        # creation returns a list of request URIs; use the first.
        stored_cfg.set_wot_identity(inserted_to[0], local_identity)
        Config.to_file(stored_cfg)

        import wot
        wot.update_repo_listing(ui_,
                                local_identity,
                                fcphost=opts["fcphost"],
                                fcpport=opts["fcpport"])
Exemplo n.º 10
0
def connect(ui, repo):
    """
    Connect to the WebUI plugin to provide local support.
    
    TODO: Add command option handling (fcphost and fcpport).
    """
    node = fcp.FCPNode()
    atexit.register(node.shutdown)

    ui.status("Connecting.\n")

    # TODO: Would it be worthwhile to have a wrapper that includes PLUGIN_NAME?
    # TODO: Where to document the spec? devnotes.txt? How to format?
    hi_there = node.fcpPluginMessage(plugin_name=PLUGIN_NAME,
                                     plugin_params={
                                         'Message': 'Hello',
                                         'VoidQuery': 'true'
                                     })[0]

    if hi_there['header'] == 'Error':
        raise util.Abort("The DVCS web UI plugin is not loaded.")

    if hi_there['Replies.Message'] == 'Error':
        # TODO: Debugging
        print hi_there
        raise util.Abort("Another VCS instance is already connected.")

    session_token = hi_there['Replies.SessionToken']

    ui.status("Connected.\n")

    def disconnect(signum, frame):
        ui.status("Disconnecting.\n")
        node.fcpPluginMessage(plugin_name=PLUGIN_NAME,
                              plugin_params={
                                  'Message': 'Disconnect',
                                  'SessionToken': session_token
                              })
        sys.exit()

    # Send Disconnect on interrupt instead of waiting on timeout.
    signal(SIGINT, disconnect)

    def ping():
        # Loop with delay.
        while True:
            pong = node.fcpPluginMessage(plugin_name=PLUGIN_NAME,
                                         plugin_params={
                                             'Message': 'Ping',
                                             'SessionToken': session_token
                                         })[0]
            if pong['Replies.Message'] == 'Error':
                raise util.Abort(pong['Replies.Description'])
            elif pong['Replies.Message'] != 'Pong':
                ui.warn("Got unrecognized Ping reply '{0}'.\n".format(
                    pong['Replies.Message']))

            # Wait for less than timeout threshold. In testing responses take
            # a little over a second.
            sleep(3.5)

    # Start self-perpetuating pinging in the background.
    t = threading.Timer(0.0, ping)
    # Daemon threads do not hold up the process exiting. Allows prompt
    # response to - for instance - SIGTERM.
    t.daemon = True
    t.start()

    while True:
        # Load the config each time - it could change.
        # TODO: Monitor config file for change events instead.
        cfg = Config.from_ui(ui)

        query_identifier = node._getUniqueId()
        # The event-querying is single-threaded, which makes things slow as
        # everything waits on the completion of the current operation.
        # Asynchronous code would require changes on the plugin side but
        # potentially have much lower latency.
        # TODO: Can wrap away PLUGIN_NAME, SessionToken, and QueryIdentifier?
        command = node.fcpPluginMessage(plugin_name=PLUGIN_NAME,
                                        plugin_params={
                                            'Message': 'Ready',
                                            'SessionToken': session_token,
                                            'QueryIdentifier': query_identifier
                                        })[0]

        response = command['Replies.Message']
        if response == 'Error':
            raise util.Abort(command['Replies.Description'])

        if response not in handlers:
            raise util.Abort("Unsupported query '{0}'\n".format(response))

        ui.status("Got query: {0}\n".format(response))

        # Handlers are indexed by the query message name, take the query
        # message, and return (result_name, plugin_params).
        result_name, plugin_params = handlers[response](command,
                                                        cfg=cfg,
                                                        ui=ui)

        plugin_params['Message'] = result_name
        plugin_params['QueryIdentifier'] = query_identifier
        plugin_params['SessionToken'] = session_token

        ack = node.fcpPluginMessage(plugin_name=PLUGIN_NAME,
                                    plugin_params=plugin_params)[0]

        if ack['Replies.Message'] != "Ack":
            raise util.Abort("Received unexpected message instead of result "
                             "acknowledgement:\n{0}\n".format(ack))

        ui.status("Query complete.\n")
Exemplo n.º 11
0
                continue
            stat_fields.append(argfield)
        continue
    i = arg.find(".")
    if (-1 != i):
        host = arg
        continue
    stat_fields.append(arg)

if (2 != len(stat_fields) and not list_fields_flag):
    print "Must specify two stat_fields when not using --list-fields"
    print
    usage()
    sys.exit(1)

f = fcp.FCPNode(host=host, port=port)
entry = f.refstats(WithVolatile=True)
f.shutdown()
if (list_fields_flag):
    keys = entry.keys()
    keys.sort()
    print "Volatile fields:"
    for key in keys:
        if (not key.startswith("volatile.")):
            continue
        print key[9:]
    print
    print "non-volatile fields:"
    for key in keys:
        if (key.startswith("volatile.")):
            continue
Exemplo n.º 12
0
 def __init__(self):
     self.fcpNode = fcp.FCPNode(host="localhost")