infocalypse
 
(Steve Dougherty)
2013-06-25: Add more work on freenet:// path resolution for WoT.

Add more work on freenet:// path resolution for WoT.

diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py
--- a/infocalypse/__init__.py
+++ b/infocalypse/__init__.py
@@ -554,6 +554,13 @@ extensions.wrapfunction(discovery, 'find
 
 
 def freenetpathtouri(ui, path, pull=True):
+    """
+    Return a usable request or insert URI. Expects a freenet:// or freenet:
+    protocol to be specified.
+
+    If the key is not a USK it will be resolved as a WoT identity. In this
+    case if the resolution fails, print an error message and return None.
+    """
     # TODO: Is this the only URL encoding that may happen? Why not use a more
     # semantically meaningful function?
     path = path.replace("%7E", "~").replace("%2C", ",")
@@ -632,6 +639,8 @@ def freenetpush(orig, *args, **opts):
     if not isfreenetpath(path):
         return orig(*args, **opts)
     uri = freenetpathtouri(ui, path, pull=False)
+    if uri is None:
+        return
     # if the uri is the short form (USK@/name/#), generate the key and preprocess the uri.
     if uri.startswith("USK@/"):
         ui.status("creating a new key for the repo. For a new repo with an existing key, use clone.\n")
diff --git a/infocalypse/keys.py b/infocalypse/keys.py
new file mode 100644
--- /dev/null
+++ b/infocalypse/keys.py
@@ -0,0 +1,15 @@
+from string import split
+
+
+class USK:
+    def __init__(self, path):
+        components = split(path, '/')
+        # Expecting USK@key/name/edition
+        assert len(components) == 3
+
+        self.key = components[0]
+        self.name = components[1]
+        self.edition = components[2]
+
+    def __str__(self):
+        return self.key + '/' + self.name + '/' + self.edition
\ No newline at end of file
diff --git a/infocalypse/wot.py b/infocalypse/wot.py
--- a/infocalypse/wot.py
+++ b/infocalypse/wot.py
@@ -6,6 +6,7 @@ from defusedxml.ElementTree import froms
 import smtplib
 from base64 import b32encode
 from fcp.node import base64decode
+from keys import USK
 
 
 def send_pull_request(ui, from_identity, to_identity):
@@ -67,10 +68,31 @@ def update_repo_listing(ui, for_identity
         ui.status("Updated repository listing:\n{0}\n".format(uri))
 
 
+def find_repo(ui, truster, wot_identifier, repo_name):
+    """
+    Return a request URI for a repo of the given name published by an
+    identity matching the given identifier.
+    Print an error message and return None on failure.
+    """
+    listing = read_repo_listing(ui, truster, wot_identifier)
+
+    if listing is None:
+        return
+
+    if repo_name not in listing:
+        # TODO: Perhaps resolve again; print full nick / key?
+        # TODO: Maybe print key found in the resolve_*identity?
+        ui.warn("{0} does not publish a repo named '{1}'\n".format(
+            wot_identifier, repo_name))
+        return
+
+    return listing[repo_name]
+
+
 def read_repo_listing(ui, truster, wot_identifier):
     """
     Read a repo listing for a given identity.
-    Return a dictionary of repository URIs keyed by name.
+    Return a dictionary of repository request URIs keyed by name.
     """
     identity = resolve_identity(ui, truster, wot_identifier)
     if identity is None:
@@ -85,6 +107,8 @@ def read_repo_listing(ui, truster, wot_i
     # 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(uri, priority=1)
 
     ui.status("Parsing.\n")
@@ -121,17 +145,7 @@ def resolve_pull_uri(ui, path, truster):
         # TODO: How to handle redundancy? Does Infocalypse automatically try
         # an R0 if an R1 fails?
 
-        repositories = read_repo_listing(ui, truster, wot_id)
-
-        if repositories is None:
-            return
-
-        if repo_name not in repositories:
-            ui.warn("Could not find repository named \"{0}\".\n"
-                    .format(repo_name))
-            return
-
-        return repositories[repo_name]
+        return find_repo(ui, truster, wot_id, repo_name)
 
 
 def resolve_push_uri(ui, path):
@@ -140,17 +154,34 @@ def resolve_push_uri(ui, path):
     Print an error message and return None on failure.
 
     :param ui: For feedback.
-    :param path: path describing a repo: nick@key/reponame,
+    :param path: path describing a repo - nick@key/repo_name,
     where the identity is a local one. (Such that the insert URI is known.)
     """
-    # Expecting <id stuff>/reponame
-    # TODO: Duplcate with resolve_pull
+    # Expecting <id stuff>/repo_name
+    # TODO: Duplicate with resolve_pull
     wot_id, repo_name = path.split('/', 1)
 
-    local_id = resolve_local_identity(ui, )
+    local_id = resolve_local_identity(ui, wot_id)
 
-    # Get edition by checking one's own repo list.
-    repositories = read_repo_listing(ui, )
+    if local_id is None:
+        return
+
+    insert_uri = USK(local_id['InsertURI'])
+
+    identifier = local_id['Nickname'] + '@' + local_id['Identity']
+
+    repo = find_repo(ui, local_id['Identity'], identifier, repo_name)
+
+    if repo is None:
+        return
+
+    # Request URI
+    repo_uri = USK(repo)
+
+    # Maintains path, edition.
+    repo_uri.key = insert_uri.key
+
+    return str(repo_uri)
 
 # Support for querying WoT for own identities and identities meeting various
 # criteria.