Changeset 13056


Ignore:
Timestamp:
11/27/10 11:03:34 (11 years ago)
Author:
Mark Evenson
Message:

Fix problems with #\Space characters in JAR pathnames.

We now require that inputs to the PATHNAME routines that have the URI
scheme "jar:file" or "file" properly encode themselves as URIs
according to RFC2396. Mainly this means that #\Space and #\?
characters in such strings should be percent encoded
(i.e. "jar:file:/path%20with%20/space/and%3fquestion-mark"). The
corresponding namestring routines have been adjusted to output such
URI encoded representations, although the underlying PATHNAME objects
contain unescaped values. The routines for loading FASLs have been
adjusted to URI encode their inputs as well.

The #\+ character is no longer an escape for #\Space (this was a bug).

Location:
trunk/abcl/src/org/armedbear/lisp
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/src/org/armedbear/lisp/Load.java

    r12760 r13056  
    154154        if (Utilities.checkZipFile(truename)) {
    155155            String n = truename.getNamestring();
     156            n = Pathname.uriEncode(n);
    156157            if (n.startsWith("jar:")) {
    157158                n = "jar:" + n + "!/" + truename.name.getStringValue() + "."
  • trunk/abcl/src/org/armedbear/lisp/Pathname.java

    r13026 r13056  
    3939import java.io.InputStream;
    4040import java.io.FileInputStream;
     41import java.io.UnsupportedEncodingException;
    4142import java.net.MalformedURLException;
    4243import java.net.URI;
     
    4546import java.net.URLDecoder;
    4647import java.net.URLConnection;
     48import java.net.URLEncoder;
    4749import java.util.Enumeration;
    4850import java.util.StringTokenizer;
     
    196198
    197199    public Pathname(URL url) {
    198         if ("file".equals(url.getProtocol())) {
    199             String s = url.getPath();
    200             if (s != null) {
    201                 if (Utilities.isPlatformWindows) {
    202                     //  Workaround for Java's idea of URLs
    203                     //  new (URL"file:///c:/a/b").getPath() --> "/c:/a/b"
    204                     //  whereas we need "c" to be the DEVICE.
    205                     if (s.length() > 2
    206                         && s.charAt(0) == '/'
    207                         && s.charAt(2) == ':') {
    208                         s = s.substring(1);
    209                     }
    210                 }
    211                 init(s);
    212                 return;
    213             }
    214         } else {
    215             init(url.toString());
    216             return;
    217         }
    218         error(new LispError("Failed to construct Pathname from URL: "
    219                             + "'" + url.toString() + "'"));
     200         // URL handling is now buried in init(String), as the URI
     201         // escaping mechanism didn't interact well with '+' and other
     202         // characters.
     203        init(url.toString());
    220204    }
    221205
     
    280264            }
    281265            if (jar.startsWith("jar:file:")) {
    282                 String jarString
    283                     = jar.substring("jar:".length(),
     266                String file
     267                    = jar.substring("jar:file:".length(),
    284268                                    jar.length() - jarSeparator.length());
    285                 // Use URL constructor to normalize Windows' use of device
    286                 URL url = null;
    287                 try {
    288                     url = new URL(jarString);
    289                 } catch (MalformedURLException e) {
    290                     error(new LispError("Failed to parse '" + jarString + "'"
    291                             + " as URL:"
    292                             + e.getMessage()));
    293                 }
    294                 Pathname jarPathname = new Pathname(url);
     269                Pathname jarPathname;
     270                if (file.length() > 0) {
     271                    // Instead of "use URL constructor to normalize Windows' use of device"
     272                    // attempt to shorten the URL to pass through the normal constructor.
     273                    if (Utilities.isPlatformWindows
     274                        && file.charAt(0) == '/'
     275                        && file.charAt(2) == ':'
     276                        && Character.isLetter(file.charAt(1)))
     277                        {
     278                            file = file.substring(1);
     279                        }
     280                    URL url = null;
     281                    URI uri = null;
     282                    try {
     283                        url = new URL("file:" + file);
     284                        uri = url.toURI();
     285                    } catch (MalformedURLException e1) {
     286                        error(new FileError("Failed to create URI from "
     287                                            + "'" + file + "'"
     288                                            + ": " + e1.getMessage()));
     289                    } catch (URISyntaxException e2) {
     290                        error(new FileError("Failed to create URI from "
     291                                            + "'" + file + "'"
     292                                            + ": " + e2.getMessage()));
     293                    }
     294                    String path = uri.getPath();
     295                    if (path == null) {
     296                        // We allow "jar:file:baz.jar!/" to construct a relative
     297                        // path for jar files, so MERGE-PATHNAMES means something.
     298                        jarPathname = new Pathname(uri.getSchemeSpecificPart());
     299                    } else {
     300                        jarPathname = new Pathname(path);
     301                    }
     302                } else {
     303                    jarPathname = new Pathname("");
     304                }
    295305                jars = jars.push(jarPathname);
    296306            } else {
     
    316326        if (separatorIndex > 0 && s.startsWith("jar:")) {
    317327            final String jarURL = s.substring(0, separatorIndex + jarSeparator.length());
    318             Pathname d = new Pathname(jarURL);
     328            URL url = null;
     329            try {
     330                url = new URL(jarURL);
     331            } catch (MalformedURLException ex) {
     332                error(new FileError("Failed to parse URL "
     333                                    + "'" + jarURL + "'"
     334                                    + ex.getMessage()));
     335            }
     336            Pathname d = new Pathname(url);
    319337            if (device instanceof Cons) {
    320338                LispObject[] jars = d.copyToArray();
     
    343361            String scheme = url.getProtocol();
    344362            if (scheme.equals("file")) {
    345                 Pathname p = new Pathname(url.getFile());
     363                URI uri = null;
     364                try {
     365                    uri = url.toURI();
     366                } catch (URISyntaxException ex) {
     367                    error(new FileError("Improper URI syntax for "
     368                                    + "'" + url.toString() + "'"
     369                                    + ": " + ex.toString()));
     370                }
     371                Pathname p = new Pathname(uri.getPath());
    346372                this.host = p.host;
    347373                this.device = p.device;
     
    597623            }
    598624        }
     625        boolean uriEncoded = false;
    599626        if (device == NIL) {
    600627        } else if (device == Keyword.UNSPECIFIC) {
     
    606633                if (!((Pathname)jars[i]).isURL() && i == 0) {
    607634                    sb.append("file:");
    608                 }
    609                 sb.append(((Pathname) jars[i]).getNamestring());
     635                    uriEncoded = true;
     636                }
     637                Pathname jar = (Pathname) jars[i];
     638                String encodedNamestring;
     639                if (uriEncoded) {
     640                    encodedNamestring = uriEncode(jar.getNamestring());
     641                } else {
     642                    encodedNamestring = jar.getNamestring();
     643                }
     644                sb.append(encodedNamestring);
    610645                sb.append("!/");
    611646            }
     
    621656        }
    622657        String directoryNamestring = getDirectoryNamestring();
     658        if (uriEncoded) {
     659            directoryNamestring = uriEncode(directoryNamestring);
     660        }
    623661        if (isJar()) {
    624662            if (directoryNamestring.startsWith("/")) {
     
    636674                return null;
    637675            }
    638             sb.append(n);
     676            if (uriEncoded) {
     677                sb.append(uriEncode(n));
     678            } else {
     679                sb.append(n);
     680            }
    639681        } else if (name == Keyword.WILD) {
    640682            sb.append('*');
     
    651693                    }
    652694                }
    653                 sb.append(t);
     695                if (uriEncoded) {
     696                    sb.append(uriEncode(t));
     697                } else {
     698                    sb.append(t);
     699                }
    654700            } else if (type == Keyword.WILD) {
    655701                sb.append('*');
     
    19822028                if (truename != null
    19832029                    && truename instanceof Pathname) {
    1984                     jars.car = (Pathname)truename;
     2030                    Pathname truePathname = (Pathname)truename;
     2031                    // A jar that is a directory makes no sense, so exit
     2032                    if (truePathname.getNamestring().endsWith("/")) {
     2033                        break jarfile;
     2034                    }
     2035                    jars.car = truePathname;
    19852036                } else {
    19862037                    break jarfile;
     
    19952046            // 3.  JAR with Entry
    19962047            // 4.  JAR in JAR with Entry
     2048
    19972049            ZipFile jarFile = ZipCache.get((Pathname)jars.car());
    19982050            String entryPath = pathname.asEntryPath();
     
    23512403    }
    23522404
     2405    static String uriDecode(String s) {
     2406        try {
     2407            URI uri = new URI(null, null, null, s, null);
     2408            return uri.toASCIIString().substring(1);
     2409        } catch (URISyntaxException e) {}
     2410        return null;  // Error
     2411    }
     2412
     2413    static String uriEncode(String s) {
     2414        // The constructor we use here only allows absolute paths, so
     2415        // we manipulate the input and output correspondingly.
     2416        String u;
     2417        if (!s.startsWith("/")) {
     2418            u = "/" + s;
     2419        } else {
     2420            u = new String(s);
     2421        }
     2422        try {
     2423            URI uri = new URI("file", "", u, "");
     2424            String result = uri.getRawPath();
     2425            if (!s.startsWith("/")) {
     2426                return result.substring(1);
     2427            }
     2428            return result;
     2429        } catch (URISyntaxException e) {
     2430            Debug.assertTrue(false);
     2431        }
     2432        return null; // Error
     2433    }
    23532434}
    23542435
  • trunk/abcl/src/org/armedbear/lisp/Utilities.java

    r12612 r13056  
    255255    }
    256256
    257     static String uriEncode(String s) {
    258         try {
    259             URI uri = new URI("?" + s);
    260             return uri.getQuery();
    261         } catch (URISyntaxException e) {}
    262         return null;
    263     }
    264 
    265     static String uriDecode(String s) {
    266         try {
    267             URI uri = new URI(null, null, null, s, null);
    268             return uri.toASCIIString().substring(1);
    269         } catch (URISyntaxException e) {}
    270         return null;  // Error
    271     }
    272    
    273257    static String escapeFormat(String s) {
    274258        return s.replace("~", "~~");
Note: See TracChangeset for help on using the changeset viewer.