/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.webkit.network; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.PushbackInputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.sun.webkit.network.URLs.newURL; /* * Interposing class that will transform the raw stream from a FtpURLConnection * or FileURLConnection into an HTML page listing the content of the directory */ final class DirectoryURLConnection extends URLConnection { // Patterns used to parse the output from a FTP directory list. private static final String[] patStrings = { // drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog // drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog "([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*((?:\\d\\d:\\d\\d)|(?:\\d{4}))\\s*(\\p{Print}*)", // 04/28/2006 09:12a 3,563 genBuffer.sh "(\\d{2}/\\d{2}/\\d{4})\\s*(\\d{2}:\\d{2}[ap])\\s*((?:[0-9,]+)|(?:))\\s*(\\p{Graph}*)", // 01-29-97 11:32PM prog "(\\d{2}-\\d{2}-\\d{2})\\s*(\\d{2}:\\d{2}[AP]M)\\s*((?:[0-9,]+)|(?:))\\s*(\\p{Graph}*)" }; private static final int[][] patternGroups = { // file, size, date1, date2, permissions {7, 4, 5, 6, 1}, {4, 3, 1, 2, 0}, {4, 3, 1, 2, 0} }; private static final Pattern[] patterns; private static final Pattern linkp = Pattern.compile("(\\p{Print}+) \\-\\> (\\p{Print}+)$"); // Style sheet to make the TABLE better looking private static final String styleSheet = ""; static { patterns = new Pattern[patStrings.length]; for (int i = 0; i < patStrings.length; i++) { patterns[i] = Pattern.compile(patStrings[i]); } } private final URLConnection inner; private final boolean sure; private String dirUrl = null; // Set toHTML to false when it is not a directory and we don't want to // change the stream. private boolean toHTML = true; private final boolean ftp; private InputStream ins = null; /* * We need an interposing InputStream as well. We subclass PushbackInputStream * so that we can do some safe read-ahead if we have to guess whether the * URL points to a directory or not (the read-ahead is for FTP only). */ private final class DirectoryInputStream extends PushbackInputStream { private final byte[] buffer; private boolean endOfStream = false; private ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); private PrintStream out = new PrintStream(bytesOut); private ByteArrayInputStream bytesIn = null; private final StringBuffer tmpString = new StringBuffer(); private int lineCount = 0; private DirectoryInputStream(InputStream ins, boolean guess) { super(ins, 512); buffer = new byte[512]; /* * If 'guess' is true, it means it's an FTP link and we're not sure * this is a directory */ if (guess) { StringBuffer line = new StringBuffer(); int l = 0; int c; try { l = super.read(buffer, 0, buffer.length); } catch (IOException e) { } if (l <= 0) { toHTML = false; } else { for (int i = 0; i < l; i++) { line.append((char) buffer[i]); } String line2 = line.toString(); toHTML = false; for (Pattern p : patterns) { Matcher m = p.matcher(line2); if (m.find()) { // One of the patterns matched // Means it's a directory listing toHTML = true; break; } } try { super.unread(buffer, 0, l); } catch (IOException ioe) { // Shouldn't happen } } } if (toHTML) { /* * We're good to go. Let's generate the header for the table */ String parent = null; String path; URL prevUrl = null; if (!dirUrl.endsWith("/")) { dirUrl = dirUrl + "/"; } try { prevUrl = newURL(dirUrl); } catch (Exception e) { // can't happen } path = prevUrl.getPath(); if (path != null && !path.isEmpty()) { int index = path.lastIndexOf("/", path.length() - 2); if (index >= 0) { int removed = path.length() - index - 1; index = dirUrl.indexOf(path); parent = dirUrl.substring(0, index + path.length() - removed) + dirUrl.substring(index + path.length()); } } out.print("index of "); out.print(dirUrl); out.print(""); out.print(styleSheet); out.print("

Index of "); out.print(dirUrl); out.print("


"); out.print(""); out.print(""); if (parent != null) { lineCount++; out.print(""); } out.close(); bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); out = null; bytesOut = null; } } private void parseFile(String s) { tmpString.append(s); int i; while ((i = tmpString.indexOf("\n")) >= 0) { String sb = tmpString.substring(0, i); tmpString.delete(0, i + 1); String filename = sb; String size = null; String date = null; boolean dir = false; boolean noaccess = false; URL furl = null; if (filename != null) { lineCount++; try { furl = newURL(dirUrl + URLEncoder.encode(filename, "UTF-8")); URLConnection fconn = furl.openConnection(); fconn.connect(); date = fconn.getHeaderField("last-modified"); size = fconn.getHeaderField("content-length"); if (size == null) { dir = true; } fconn.getInputStream().close(); } catch (IOException e) { // No access right noaccess = true; } if (bytesOut == null) { bytesOut = new ByteArrayOutputStream(); out = new PrintStream(bytesOut); } out.print(""); } else { out.print(""); } out.print(""); } } if (bytesOut != null) { out.close(); bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); out = null; bytesOut = null; } } private void parseFTP(String s) { tmpString.append(s); int i; while ((i = tmpString.indexOf("\n")) >= 0) { String sb = tmpString.substring(0, i); tmpString.delete(0, i + 1); String filename = null; String link = null; String size = null; String date = null; boolean dir = false; Matcher m = null; for (int j = 0; j < patterns.length; j++) { m = patterns[j].matcher(sb); if (m.find()) { filename = m.group(patternGroups[j][0]); size = m.group(patternGroups[j][1]); date = m.group(patternGroups[j][2]); if (patternGroups[j][3] > 0) { date += (" " + m.group(patternGroups[j][3])); } if (patternGroups[j][4] > 0) { String perms = m.group(patternGroups[j][4]); dir = perms.startsWith("d"); } if ("".equals(size)) { dir = true; size = null; } } } if (filename != null) { m = linkp.matcher(filename); if (m.find()) { // There is a symbolic link filename = m.group(1); link = m.group(2); } if (bytesOut == null) { bytesOut = new ByteArrayOutputStream(); out = new PrintStream(bytesOut); } lineCount++; out.print(""); } else if (dir) { out.print(""); } else { out.print(""); } out.print(""); } } if (bytesOut != null) { out.close(); bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); out = null; bytesOut = null; } } private void endOfList() { // Let's make sure we don't miss the last line because a new-line // is missing if (ftp) { parseFTP("\n"); } else { parseFile("\n"); } if (bytesOut == null) { bytesOut = new ByteArrayOutputStream(); out = new PrintStream(bytesOut); } out.print("
FileSizeLast Modified
Up to parent directory
"); if (noaccess) { out.print(filename); } else { out.print(""); out.print(filename); out.print(""); } if (dir) { out.print("<Directory>" + (size == null ? " " : size) + "" + (date == null ? " " : date) + "
"); out.print(filename); out.print(""); if (link != null) { out.print(" → " + link + "<Link><Directory>" + size + "" + date + "


"); out.close(); bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); out = null; bytesOut = null; } @Override public int read(byte[] buf) throws IOException { return read(buf, 0, buf.length); } @Override public int read(byte[] buf, int offset, int length) throws IOException { int l = 0; if (!toHTML) { return super.read(buf, offset, length); } if (bytesIn != null) { l = bytesIn.read(buf, offset, length); if (l == -1) { bytesIn.close(); bytesIn = null; if (endOfStream) { return -1; } } else { return l; } } if (!endOfStream) { l = super.read(buffer, 0, buffer.length); if (l == -1) { endOfStream = true; endOfList(); return read(buf, offset, length); } else { if (ftp) { parseFTP(new String(buffer, 0, l)); } else { parseFile(new String(buffer, 0, l)); } if (bytesIn != null) { return read(buf, offset, length); } } } return 0; } } /* * Constructor to use for an FTP (or FTPS) URLConnection. * Set 'notsure' to true if it is necessary to look ahead to make sure * it is, indeed, a directory. */ DirectoryURLConnection(URLConnection con, boolean notsure) { super(con.getURL()); dirUrl = con.getURL().toExternalForm(); inner = con; sure = !notsure; ftp = true; } /* * Constructor to use for a File URLConnection. */ DirectoryURLConnection(URLConnection con) { super(con.getURL()); dirUrl = con.getURL().toExternalForm(); ftp = false; sure = true; inner = con; } @Override public void connect() throws IOException { inner.connect(); } @Override public InputStream getInputStream() throws IOException { if (ins == null) { if (ftp) { ins = new DirectoryInputStream(inner.getInputStream(), !sure); } else { ins = new DirectoryInputStream(inner.getInputStream(), false); } } return ins; } @Override public String getContentType() { try { if (!sure) { getInputStream(); } } catch (IOException e) { } if (toHTML) { return "text/html"; } return inner.getContentType(); } @Override public String getContentEncoding() { return inner.getContentEncoding(); } @Override public int getContentLength() { return inner.getContentLength(); } @Override public Map> getHeaderFields() { return inner.getHeaderFields(); } @Override public String getHeaderField(String key) { return inner.getHeaderField(key); } }