/** depth-first search */ private static NamingService searchNamingService(NamingService ns, String srch) { String name = ns.getName(); if (name.equals(srch) || basename(name).equals(srch) || name.equals(DEFAULT_NS)) return ns; List<NamingService> list = ns.getNamingServices(); if (list != null) { for (NamingService nss : list) { NamingService rv = searchNamingService(nss, srch); if (rv != null) return rv; } } return null; }
/** * Very simple web server. * * Serve local files in the docs/ directory, for CSS and images in * error pages, using the reserved address proxy.i2p * (similar to p.p in privoxy). * This solves the problems with including links to the router console, * as assuming the router console is at 127.0.0.1 leads to broken * links if it isn't. * * Ignore all request headers (If-Modified-Since, etc.) * * There is basic protection here - * FileUtil.readFile() prevents traversal above the base directory - * but inproxy/gateway ops would be wise to block proxy.i2p to prevent * exposing the docs/ directory or perhaps other issues through * uncaught vulnerabilities. * Restrict to the /themes/ directory for now. * * @param targetRequest decoded path only, non-null * @param query raw (encoded), may be null */ public static void serveLocalFile(OutputStream out, String method, String targetRequest, String query, String proxyNonce) { //System.err.println("targetRequest: \"" + targetRequest + "\""); // a home page message for the curious... if (targetRequest.equals("/")) { try { out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes()); out.flush(); } catch (IOException ioe) {} return; } if ((method.equals("GET") || method.equals("HEAD")) && targetRequest.startsWith("/themes/") && !targetRequest.contains("..")) { String filename = null; try { filename = targetRequest.substring(8); // "/themes/".length } catch (IndexOutOfBoundsException ioobe) { return; } // theme hack if (filename.startsWith("console/default/")) filename = filename.replaceFirst("default", I2PAppContext.getGlobalContext().getProperty("routerconsole.theme", "light")); File themesDir = new File(I2PAppContext.getGlobalContext().getBaseDir(), "docs/themes"); File file = new File(themesDir, filename); if (file.exists() && !file.isDirectory()) { String type; if (filename.endsWith(".css")) type = "text/css"; else if (filename.endsWith(".ico")) type = "image/x-icon"; else if (filename.endsWith(".png")) type = "image/png"; else if (filename.endsWith(".jpg")) type = "image/jpeg"; else type = "text/html"; try { out.write("HTTP/1.1 200 OK\r\nContent-Type: ".getBytes()); out.write(type.getBytes()); out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes()); FileUtil.readFile(filename, themesDir.getAbsolutePath(), out); } catch (IOException ioe) {} return; } } // Add to addressbook (form submit) // Parameters are url, host, dest, nonce, and master | router | private. // Do the add and redirect. if (targetRequest.equals("/add")) { Map<String, String> opts = new HashMap(8); // this only works if all keys are followed by =value StringTokenizer tok = new StringTokenizer(query, "=&;"); while (tok.hasMoreTokens()) { String k = tok.nextToken(); if (!tok.hasMoreTokens()) break; String v = tok.nextToken(); opts.put(decode(k), decode(v)); } String url = opts.get("url"); String host = opts.get("host"); String b64Dest = opts.get("dest"); String nonce = opts.get("nonce"); String referer = opts.get("referer"); String book = "privatehosts.txt"; if (opts.get("master") != null) book = "userhosts.txt"; else if (opts.get("router") != null) book = "hosts.txt"; Destination dest = null; if (b64Dest != null) { try { dest = new Destination(b64Dest); } catch (DataFormatException dfe) { System.err.println("Bad dest to save?" + b64Dest); } } //System.err.println("url : \"" + url + "\""); //System.err.println("host : \"" + host + "\""); //System.err.println("b64dest : \"" + b64Dest + "\""); //System.err.println("book : \"" + book + "\""); //System.err.println("nonce : \"" + nonce + "\""); if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) { try { NamingService ns = I2PAppContext.getGlobalContext().namingService(); Properties nsOptions = new Properties(); nsOptions.setProperty("list", book); if (referer != null && referer.startsWith("http")) { String from = "<a href=\"" + referer + "\">" + referer + "</a>"; nsOptions.setProperty("s", _("Added via address helper from {0}", from)); } else { nsOptions.setProperty("s", _("Added via address helper")); } boolean success = ns.put(host, dest, nsOptions); writeRedirectPage(out, success, host, book, url); return; } catch (IOException ioe) {} } try { out.write(ERR_ADD); out.flush(); } catch (IOException ioe) {} return; } try { out.write(ERR_404); out.flush(); } catch (IOException ioe) {} }
/** * Load addressbook and apply filter, returning messages about this. * To control memory, don't load the whole addressbook if we can help it... * only load what is searched for. */ @Override public String getLoadBookMessages() { if (isDirect()) return super.getLoadBookMessages(); NamingService service = getNamingService(); Debug.debug("Searching within " + service + " with filename=" + getFileName() + " and with filter=" + filter + " and with search=" + search); String message = ""; try { LinkedList<AddressBean> list = new LinkedList<AddressBean>(); Map<String, Destination> results; Properties searchProps = new Properties(); // only blockfile needs this searchProps.setProperty("list", getFileName()); if (filter != null) { String startsAt = filter.equals("0-9") ? "[0-9]" : filter; searchProps.setProperty("startsWith", startsAt); } if (isPrefiltered()) { // Only limit if we not searching or filtering, so we will // know the total number of results if (beginIndex > 0) searchProps.setProperty("skip", Integer.toString(beginIndex)); int limit = 1 + endIndex - beginIndex; if (limit > 0) searchProps.setProperty("limit", Integer.toString(limit)); } if (search != null && search.length() > 0) searchProps.setProperty("search", search.toLowerCase(Locale.US)); results = service.getEntries(searchProps); Debug.debug("Result count: " + results.size()); for (Map.Entry<String, Destination> entry : results.entrySet()) { String name = entry.getKey(); if( filter != null && filter.length() > 0 ) { if (filter.equals("0-9")) { char first = name.charAt(0); if( first < '0' || first > '9' ) continue; } else if( ! name.toLowerCase(Locale.US).startsWith( filter.toLowerCase(Locale.US) ) ) { continue; } } if( search != null && search.length() > 0 ) { if( name.indexOf( search ) == -1 ) { continue; } } String destination = entry.getValue().toBase64(); if (destination != null) { list.addLast( new AddressBean( name, destination ) ); } else { // delete it too? System.err.println("Bad entry " + name + " in database " + service.getName()); } } AddressBean array[] = list.toArray(new AddressBean[list.size()]); Arrays.sort( array, sorter ); entries = array; message = generateLoadMessage(); } catch (Exception e) { Debug.debug( e.getClass().getName() + ": " + e.getMessage() ); } if( message.length() > 0 ) message = "<p>" + message + "</p>"; return message; }