From a88edb8ea88df4d940ac2a6b711d68429956542b Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Thu, 30 Jun 2011 16:59:44 +0000
Subject: [PATCH] - Top-level uri fixup part 2: Remember base uri in
 CacheProvider;   do the rectification in CacheProvider too

---
 .../router/activity/I2PWebViewClient.java     | 49 ++++------
 .../android/router/activity/WebActivity.java  |  2 +
 .../router/provider/CacheProvider.java        | 95 +++++++++++++++++--
 src/net/i2p/android/router/util/AppCache.java | 10 +-
 4 files changed, 115 insertions(+), 41 deletions(-)

diff --git a/src/net/i2p/android/router/activity/I2PWebViewClient.java b/src/net/i2p/android/router/activity/I2PWebViewClient.java
index 5ca891c38..cf28ca8ae 100644
--- a/src/net/i2p/android/router/activity/I2PWebViewClient.java
+++ b/src/net/i2p/android/router/activity/I2PWebViewClient.java
@@ -46,37 +46,23 @@ class I2PWebViewClient extends WebViewClient {
         Util.e("Should override? " + url);
         view.stopLoading();
 
-            Uri uri = Uri.parse(url);
+        Uri uri = Uri.parse(url);
+
+        if (CONTENT.equals(uri.getScheme())) {
+            // Fix up top-level links like <a href="/foo">
+            // take the host from the current uri and the path from the new uri
+            String currentUrl = view.getUrl();
+            Uri currentUri = Uri.parse(currentUrl);
+            uri = CacheProvider.rectifyContentUri(currentUri, uri);
+
+            //reverse back to a i2p URI so we can load it here and not in ContentProvider
+            try {
+                uri = CacheProvider.getI2PUri(uri);
+                Util.e("Reversed content uri back to " + uri);
+            } catch (FileNotFoundException fnfe) {}
+            url = uri.toString();
+        }
 
-            if (CONTENT.equals(uri.getScheme())) {
-                if (CacheProvider.AUTHORITY.equals(uri.getAuthority())) {
-                    if (!url.startsWith(CacheProvider.CONTENT_URI.toString())) {
-                        // Fix up top-level links like <a href="/foo">
-                        // take the host from the current uri and the path from the new uri
-                        Util.e("Content URI bad nonce, FIXME: " + url);
-                        String currentUrl = view.getUrl();
-                        Uri currentUri = Uri.parse(currentUrl);
-                        try {
-                            //reverse back to a i2p URI
-                            Uri iUri = CacheProvider.getI2PUri(currentUri);
-                            String q = uri.getQuery();
-                            url = iUri.getScheme() + "://" + iUri.getHost() + '/' + uri.getPath() +
-                                  (q != null ? ('?' + q) : "");
-                            uri = Uri.parse(url);
-                            Util.e("Fixed up top-level url back to " + url);
-                        } catch (FileNotFoundException fnfe) {}
-                    } else {
-                        try {
-                            //reverse back to a i2p URI so we can load it here and not in ContentProvider
-                            uri = CacheProvider.getI2PUri(uri);
-                            url = uri.toString();
-                            Util.e("Reversed content uri back to " + url);
-                        } catch (FileNotFoundException fnfe) {}
-                    }
-                } else {
-                    Util.e("Content URI but not for us?? " + url);
-                }
-            }
 
             String s = uri.getScheme();
             if (s == null) {
@@ -314,7 +300,8 @@ class I2PWebViewClient extends WebViewClient {
                 try { out.close(); } catch (IOException ioe) {}
                 if (success) {
                     // store in cache, get content URL, and load that way
-                    Uri content = AppCache.getInstance(_view.getContext()).addCacheFile(uri);
+                    // Set as current base
+                    Uri content = AppCache.getInstance(_view.getContext()).addCacheFile(uri, true);
                     if (content != null) {
                         Util.e("Stored cache in " + content);
                     } else {
diff --git a/src/net/i2p/android/router/activity/WebActivity.java b/src/net/i2p/android/router/activity/WebActivity.java
index 43305e1f7..14655ac3a 100644
--- a/src/net/i2p/android/router/activity/WebActivity.java
+++ b/src/net/i2p/android/router/activity/WebActivity.java
@@ -86,6 +86,8 @@ public class WebActivity extends I2PActivityBase {
             _wvClient.cancelAll();
             wv.stopLoading();
             if (wv.canGoBack()) {
+                // TODO go into history, get url and call shouldOverrideUrlLoading()
+                // so we have control ??? But then back won't work right
                 wv.goBack();
                 return true;
             }
diff --git a/src/net/i2p/android/router/provider/CacheProvider.java b/src/net/i2p/android/router/provider/CacheProvider.java
index 92cbe6c97..5d7a9404e 100644
--- a/src/net/i2p/android/router/provider/CacheProvider.java
+++ b/src/net/i2p/android/router/provider/CacheProvider.java
@@ -50,8 +50,11 @@ public class CacheProvider extends ContentProvider {
     public static final String AUTHORITY = "net.i2p.android.router";
     /** includes the nonce */
     public static final Uri CONTENT_URI = Uri.parse(SCHEME + "://" + AUTHORITY + '/' + NONCE);
-    /** the database key */
+
+    /** the database keys */
     public static final String DATA = "_data";
+    public static final String CURRENT_BASE = "currentBase";
+
     private static final String QUERY_MARKER = "!!QUERY!!";
 
     private static final String ERROR_HEADER = "<html><head><title>Not Found</title></head><body>";
@@ -61,7 +64,7 @@ public class CacheProvider extends ContentProvider {
 
     /**
      *  Generate a cache content (resource) URI for a given URI key
-     *  If the key is already a resource URI, canonicalize it
+     *  If the key is already a content URI, canonicalize it
      *  by twizzling the query if necessary
      *
      *  @param key must contain a scheme, authority and path
@@ -110,6 +113,10 @@ public class CacheProvider extends ContentProvider {
     @Override
     public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
         Util.e("CacheProvider open " + uri);
+
+        // if uri is malformed and we have a current base, rectify it
+        uri = rectifyContentUri(getCurrentBase(), uri);
+
         // map the resource URI to a local file URI and return it if it exists
         String filePath = get(uri);
         if (filePath != null) {
@@ -131,7 +138,7 @@ public class CacheProvider extends ContentProvider {
     }
 
     /**
-     *  Generate an i2p URI for a resource URI
+     *  Generate an i2p URI for a content URI
      *
      *  @param uri must contain a scheme, authority and path with nonce etc. as defined above
      *  @return non-null
@@ -168,6 +175,59 @@ public class CacheProvider extends ContentProvider {
         return Uri.parse(i2pUri);
     }
 
+    /**
+     *  Rectify a malformed content uri using the current base content uri.
+     *  Any query in uri is also canonicalized.
+     *
+     *  @param base a valid content base uri e.g. content://net.i2p.android.router/0/http/bar.i2p/baz/baf.html
+     *             if null, uri is returned.
+     *  @param uri a malformed content uri e.g. content://net.i2p.android.router/foo.html
+     *  @return a valid content uri e.g. content://net.i2p.android.router/0/http/bar.i2p/foo.html,
+     *          or the original uri on error, or if no rectification needed
+     */
+    public static Uri rectifyContentUri(Uri base, Uri uri) {
+        Util.e("rectifyContentUri  base: " + base + " and uri: " + uri);
+        if (base == null)
+            return uri;
+        if (!SCHEME.equals(base.getScheme()))
+            return uri;
+        if (!AUTHORITY.equals(base.getEncodedAuthority()))
+            return uri;
+        String basePath = base.getEncodedPath();
+        if (basePath == null)
+            return uri;
+        String[] segs = basePath.split("/", 5);
+        if (segs.length < 3)
+            return uri;
+        // first seg is empty since string starts with /
+        if (!segs[1].equals(NONCE))
+            return uri;
+        if (!segs[2].equals("http"))
+            return uri;
+        String host = segs[3];
+        if (!SCHEME.equals(uri.getScheme()))
+            return uri;
+        if (!AUTHORITY.equals(uri.getEncodedAuthority()))
+            return uri;
+        String path = uri.getEncodedPath();
+        if (path != null && (path.startsWith(NONCE + '/') || path.startsWith('/' + NONCE + '/')))
+            return uri;
+        String query = uri.getEncodedQuery();
+        StringBuilder buf = new StringBuilder(128);
+        buf.append(SCHEME).append("://")
+           .append(AUTHORITY).append('/')
+           .append(NONCE).append("/http/")
+           .append(host);
+        if (path == null || !path.startsWith("/"))
+            buf.append('/');
+        if (path != null)
+            buf.append(path);
+        if (query != null)
+            buf.append(QUERY_MARKER).append(query);
+        Util.e("rectified from base: " + base + " and uri: " + uri + " to: " + buf);
+        return Uri.parse(buf.toString());
+    }
+
     private ParcelFileDescriptor eepFetch(Uri uri) throws FileNotFoundException {
         AppCache cache = AppCache.getInstance(getContext());
         OutputStream out;
@@ -182,8 +242,8 @@ public class CacheProvider extends ContentProvider {
         if (success) {
             File file = cache.getCacheFile(uri);
             if (file.length() > 0) {
-                // this call will insert it back to us
-                Uri content = cache.addCacheFile(uri);
+                // this call will insert it back to us (don't set as current base)
+                Uri content = cache.addCacheFile(uri, false);
                 ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
                 return parcel;
             } else {
@@ -211,10 +271,16 @@ public class CacheProvider extends ContentProvider {
      *  _data -> String absolute path of the file (NOT a file:// URI)
      */
     public Uri insert(Uri uri, ContentValues values) {
-        Util.e("CacheProvider insert " + uri);
         String fileURI = values.getAsString(DATA);
-        if (fileURI != null)
+        if (fileURI != null) {
+            Util.e("CacheProvider insert " + uri);
             put(uri, fileURI);
+        }
+        Boolean setAsCurrentBase = values.getAsBoolean(CURRENT_BASE);
+        if (setAsCurrentBase != null && setAsCurrentBase.booleanValue()) {
+            Util.e("CacheProvider set current base " + uri);
+            setCurrentBase(uri);
+        }
         return uri;
     }
 
@@ -237,10 +303,13 @@ public class CacheProvider extends ContentProvider {
     ///// Map stuff
 
     private void cleanup() {
+        String pfx = CONTENT_URI.toString();
         List<String> toDelete = new ArrayList();
         Map<String, ?> map = _sharedPrefs.getAll();
         for (Map.Entry<String, ?> e : map.entrySet()) {
             String path = (String) e.getValue();
+            if (!path.startsWith(pfx))
+                continue;
             File f = new File(path);
             if (!f.exists())
                 toDelete.add(e.getKey());
@@ -262,6 +331,18 @@ public class CacheProvider extends ContentProvider {
         setPref(uri.toString(), fileURI);
     }
 
+    /** @return may be null */
+    private Uri getCurrentBase() {
+        String url = getPref(CURRENT_BASE);
+        if (url != null)
+           return Uri.parse(url);
+        return null;
+    }
+
+    private void setCurrentBase(Uri contentURI) {
+        setPref(CURRENT_BASE, contentURI.toString());
+    }
+
     /** @return true if it was removed */
     private boolean remove(Uri uri) {
         String old = getPref(uri.toString());
diff --git a/src/net/i2p/android/router/util/AppCache.java b/src/net/i2p/android/router/util/AppCache.java
index c34f1790a..a9cafdcb1 100644
--- a/src/net/i2p/android/router/util/AppCache.java
+++ b/src/net/i2p/android/router/util/AppCache.java
@@ -85,9 +85,11 @@ public class AppCache {
      *  Add a previously written file to the cache index.
      *  Return a content:// uri for the cached content in question,
      *  or null on error
+     *
      *  @param key no fragment allowed
+     *  @param setAsCurrentBase tell CacheProvider
      */
-    public Uri addCacheFile(Uri key) {
+    public Uri addCacheFile(Uri key, boolean setAsCurrentBase) {
         int hash = toHash(key);
         synchronized(_cache) {
             _cache.put(Integer.valueOf(hash), DUMMY);
@@ -95,7 +97,7 @@ public class AppCache {
         // file:/// uri
         //return Uri.fromFile(toFile(hash)).toString();
         // content:// uri
-        return insertContent(key);
+        return insertContent(key, setAsCurrentBase);
     }
 
     /**
@@ -237,10 +239,12 @@ public class AppCache {
     /**
      *  @return the uri inserted or null on failure
      */
-    private static Uri insertContent(Uri key) {
+    private static Uri insertContent(Uri key, boolean setAsCurrentBase) {
         String path = toFile(key).getAbsolutePath();
         ContentValues cv = new ContentValues();
         cv.put(CacheProvider.DATA, path);
+        if (setAsCurrentBase)
+            cv.put(CacheProvider.CURRENT_BASE, Boolean.TRUE);
         Uri uri = CacheProvider.getContentUri(key);
         if (uri != null) {
            _resolver.insert(uri, cv);
-- 
GitLab