package com.onionnetworks.util; import java.util.BitSet; import java.util.StringTokenizer; import java.net.URL; import java.io.*; public class FileUtil { // safe characters for file name sanitization static BitSet safeChars = new BitSet(256); static { // a-z for (int i='a';i<='z';i++) { safeChars.set(i); } // A-Z for (int i='A';i<='Z';i++) { safeChars.set(i); } // 0-9 for (int i='0';i<='9';i++) { safeChars.set(i); } safeChars.set('-'); safeChars.set('_'); safeChars.set(' '); safeChars.set('.'); } public static final String sanitizeFileName(String name) { StringBuffer result = new StringBuffer(); for (int i=0;i<name.length();i++) { char c = name.charAt(i); // squish multiple '.'s into a single one. if (c == '.' && i < name.length()-1 && name.charAt(i+1) == '.') { continue; } if (safeChars.get(c)) { result.append(c); } } return result.toString(); } public static final String pickSafeFileName(URL url) { String name = sanitizeFileName(new File(url.getFile()).getName()); if (name == null || name.equals("")) { name = "index.html"; } return name; } /** * Creates a file in the .onion directory after scrubbing every directory * and filename for unsafe chracters. * @param rel the relative path to clean and put in .onion * @return File object that's name safe * @throws IllegalArgumentException if path isn't relative or if any path * element (including the filename) is "" after sanitization. */ public static File safeOnionFile(String rel) { if ((new File(rel)).isAbsolute()) { throw new IllegalArgumentException(rel + " isn't relative"); } StringBuffer safe = new StringBuffer(); for (StringTokenizer st = new StringTokenizer(rel, File.separator); st.hasMoreTokens(); ) { String tok = sanitizeFileName(st.nextToken()); if (tok == null || "".equals(tok)) { throw new IllegalArgumentException("collapsed elemnt"); } if (safe == null) { safe = new StringBuffer(tok); } else { safe.append(File.separator).append(tok); } } File result = new File(getOnionDir(), safe.toString()); try { ensureExists(result); } catch (IOException e) { throw new IllegalStateException ("Failed to ensure that file exists: "+e.getMessage()); } return result; } public static void ensureExists(File f) throws IOException { if (!f.getParentFile().exists()) { if (!f.getParentFile().mkdirs()) { throw new IOException("Couldn't create parent dirs: "+f); } } if (!f.exists()) { f.createNewFile(); } } /** * Get the .onion directory off of the user's home directory. * Created if it doesn't exist * @return File to onion networks directory, null on failure to create */ public static File getOnionDir() { String s = System.getProperty("user.home"); // If they don't have a home directory, then null ("java.io.tmpdir") // will be used. if (s == null) { //System.out.println("user.home was null"); return null; } File f = new File(s, ".onion"); // FIX hardcoded name. //System.out.println(f); // check if exists if (!f.exists()) { //System.out.println("doesn't exist"); // make dirs if (!f.mkdirs()) { //System.out.println("mkdirs failed"); // fall back to system temp dir. return null; } } return f; } /** * Create a temp directory off of the onion directory, we do this * instead of the system temp directory because the user probably has more * disk space in their home directory. */ public static final File getUserTempDir() { File f = new File(getOnionDir(), "tmp"); // FIX hardcoded name //System.out.println(f); // check if exists if (!f.exists()) { //System.out.println("doesn't exist"); // make dirs if (!f.mkdirs()) { //System.out.println("mkdirs failed"); // fall back to system temp dir. return null; } } return f; } /** * Create a temporary file in the same directory as f and with a similar * name. If f is null, then a randomly named file will be created in * either the user temp directory or the system temp directory. */ public static final File createTempFile(File f) throws IOException { //FIX unit test for relative file //FIX the file names are hardcoded File parent; String name; if (f == null) { // null parent causes it to use the system temp dir. parent = getUserTempDir(); name = "onion"; } else { parent = f.getAbsoluteFile().getParentFile(); name = f.getName(); // createTempFile requires a suffix length of at least 3 if (name.length() < 3) { name += "onion"; } } try { return File.createTempFile(name,null,parent); } catch (IOException e) { if (f != null) { // try the user's temp dir return createTempFile(null); } else if (parent != null) { // try the system temp dir return File.createTempFile(name,null,null); } else { throw new IOException("Unable to create temp file"); } } } public static void skipFully(InputStream is, long count) throws IOException { byte[] b = null; long left = count; while (left > 0) { long skipped = is.skip(left); if (skipped == 0) { // We couldn't skip any bytes, lets try reading some. if (b == null) { b = new byte[1024]; } // (int) cast is safe due to min(int,long) int c = is.read(b,0,(int)Math.min(b.length,left)); if (c == -1) { throw new EOFException(); } else { skipped = c; } } left -= skipped; } } }