Spiffed up the Shortcut servlet a bit, and started on some work to make the AddressPublisher have some web configuration, subscriptions and such. Not sure what to do about how the configuration servlet needs to be in the router webapps whereas the actual hosts publishing servlet has to be in the eepsite webapps directory. Just making two .war files, for now.
This commit is contained in:
@ -12,7 +12,7 @@
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
|
||||
<!-- Create the War File -->
|
||||
<target name="-post-jar">
|
||||
<target name="war">
|
||||
<property name="warDir" value="${build.dir}/war"/>
|
||||
|
||||
<fail unless="build.dir">Must set build.dir</fail>
|
||||
@ -26,5 +26,22 @@
|
||||
<!-- Create war file and place in dist directory -->
|
||||
<jar jarfile="${dist.dir}/addresspublisher.war" basedir="${warDir}" />
|
||||
</target>
|
||||
<target name="configWar">
|
||||
<property name="war2Dir" value="${build.dir}/war-config"/>
|
||||
|
||||
<fail unless="build.dir">Must set build.dir</fail>
|
||||
<copy todir="${war2Dir}/WEB-INF/classes">
|
||||
<fileset dir="${build.dir}/classes" includes="**/*.class" />
|
||||
</copy>
|
||||
<copy todir="${war2Dir}/WEB-INF">
|
||||
<fileset dir="${basedir}" includes="web-config.xml" />
|
||||
</copy>
|
||||
|
||||
<!-- Create war file and place in dist directory -->
|
||||
<jar jarfile="${dist.dir}/addresspublisher-config.war" basedir="${war2Dir}" />
|
||||
</target>
|
||||
|
||||
<target name="-post-jar" depends="war,configWar" />
|
||||
|
||||
<target name="-javadoc-build" />
|
||||
</project>
|
||||
|
@ -316,15 +316,18 @@ is divided into following sections:
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<sequential>
|
||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
||||
<pathconvert pathsep="," property="javac.includes.binary">
|
||||
<pathconvert pathsep="${line.separator}" property="javac.includes.binary">
|
||||
<path>
|
||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
||||
</path>
|
||||
<globmapper from="*.java" to="*.class"/>
|
||||
</pathconvert>
|
||||
<tempfile deleteonexit="true" property="javac.includesfile.binary"/>
|
||||
<echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
|
||||
<delete>
|
||||
<files includes="${javac.includes.binary}"/>
|
||||
<files includesfile="${javac.includesfile.binary}"/>
|
||||
</delete>
|
||||
<delete file="${javac.includesfile.binary}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
|
@ -4,5 +4,5 @@ build.xml.stylesheet.CRC32=28e38971@1.38.2.45
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
nbproject/build-impl.xml.data.CRC32=00fdd0af
|
||||
nbproject/build-impl.xml.script.CRC32=967d9836
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=f33e10ff@1.38.2.45
|
||||
nbproject/build-impl.xml.script.CRC32=b5dc77d5
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=229523de@1.38.3.45
|
||||
|
@ -1,11 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1">
|
||||
<file>file:/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/src/i2p/dream/addresspublisher/Servlet.java</file>
|
||||
<file>file:/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/src/i2p/dream/addresspublisher/RecordIndex.java</file>
|
||||
<file>file:/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/src/i2p/dream/addresspublisher/PersistentLong.java</file>
|
||||
<file>file:/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/src/i2p/dream/addresspublisher/Configuration.java</file>
|
||||
<file>file:/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/src/i2p/dream/addresspublisher/Record.java</file>
|
||||
</open-files>
|
||||
</project-private>
|
||||
|
123
addresspublisher/src/i2p/dream/addresspublisher/BlackList.java
Normal file
123
addresspublisher/src/i2p/dream/addresspublisher/BlackList.java
Normal file
@ -0,0 +1,123 @@
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
// This just implements a persistent blacklist of addresses that were deleted
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
class BlackList implements Iterable<Hash> {
|
||||
public Iterator<Hash> iterator() {
|
||||
return blacklist.iterator();
|
||||
}
|
||||
private static class HashReader implements Iterator<Hash>, Iterable<Hash>{
|
||||
private final FileInputStream in;
|
||||
boolean done = false;
|
||||
Hash queued = null;
|
||||
public HashReader(FileInputStream in) {
|
||||
this.in = in;
|
||||
queueNext();
|
||||
}
|
||||
|
||||
final void queueNext() {
|
||||
try {
|
||||
byte[] buf = new byte[Hash.HASH_LENGTH];
|
||||
int amount = in.read(buf);
|
||||
if(amount < Hash.HASH_LENGTH)
|
||||
done = true;
|
||||
queued = new Hash(buf);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(BlackList.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return !done;
|
||||
}
|
||||
|
||||
public Hash next() {
|
||||
Hash next = queued;
|
||||
queueNext();
|
||||
return next;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
public Iterator<Hash> iterator() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Hash> blacklist = new HashSet<Hash>();
|
||||
|
||||
final Timer timer;
|
||||
|
||||
File theList() {
|
||||
return new File(Configuration.getConfDir(),"blacklist.dat");
|
||||
}
|
||||
|
||||
BlackList(Context context) {
|
||||
timer = context.timer;
|
||||
load();
|
||||
}
|
||||
|
||||
public final void load() {
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(theList());
|
||||
in.getChannel().lock();
|
||||
for(Hash hash : new HashReader(in)) {
|
||||
blacklist.add(hash);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
Logger.getLogger(BlackList.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(theList());
|
||||
out.getChannel().lock();
|
||||
for(Hash hash : blacklist) {
|
||||
out.write(hash.getData());
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
Logger.getLogger(BlackList.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
boolean contains(Destination dest) {
|
||||
return blacklist.contains(dest.calculateHash());
|
||||
}
|
||||
|
||||
void add(Destination dest) {
|
||||
blacklist.add(dest.calculateHash());
|
||||
final BlackList that = this;
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
that.save();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
105
addresspublisher/src/i2p/dream/addresspublisher/BookServlet.java
Normal file
105
addresspublisher/src/i2p/dream/addresspublisher/BookServlet.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.util.TimeZone;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
|
||||
public class BookServlet extends HttpServlet {
|
||||
static final Context context = new Context("Book Servlet");
|
||||
|
||||
static final BlackList blacklist = new BlackList(context);
|
||||
|
||||
/** just check if console password option is set, jetty will do auth */
|
||||
private boolean validPassphrase() {
|
||||
String pass = I2PAppContext.getGlobalContext().getProperty("consolePassword");
|
||||
return pass != null && pass.trim().length() > 0;
|
||||
}
|
||||
|
||||
static final DateFormat formatter = DateFormat.getDateTimeInstance();
|
||||
static {
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
if(!validPassphrase()) return;
|
||||
|
||||
response.setContentType("text/html");
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = response.getWriter();
|
||||
out.println("<html><head><title>Addresses</title></head>");
|
||||
out.println("<form method=POST>");
|
||||
out.println("<table>");
|
||||
for(Record record : RecordIndex.getInstance()) {
|
||||
out.println("<tr>");
|
||||
out.println("<td><input name=\"checked\" type=\"checkbox\" value=\""+
|
||||
Long.toHexString(record.id)+
|
||||
"\" /></td>");
|
||||
out.println(" <td>"+record.getName()+"</td>");
|
||||
out.println(" <td>"+
|
||||
formatter.format(record.getModified()) + "</td>");
|
||||
out.println(" <td>" + record.getAddress().toBase64() + "</td>");
|
||||
out.println("</tr>");
|
||||
}
|
||||
out.println("</table>");
|
||||
|
||||
out.println("<input name=\"delete\" type=\"submit\" value=\"Delete Selected\" />");
|
||||
out.println("</form>");
|
||||
out.println("<h3>Deleted addresses:</h3>");
|
||||
out.println("<form method=POST>");
|
||||
out.println("<table>");
|
||||
for(Hash hash : blacklist) {
|
||||
String shash = Base32.encode(hash.getData());
|
||||
out.println("<tr>");
|
||||
out.println(" <td><input name=\"checked\" type=\"checkbox\" value=\"" +
|
||||
shash +"\" /></td>");
|
||||
out.println(" <td>"+shash+"</td>");
|
||||
out.println("</tr>");
|
||||
}
|
||||
out.println("</table>");
|
||||
|
||||
out.println("<input type=\"submit\" name=\"submit\" value=\"Undelete Selected\" />");
|
||||
|
||||
out.println("</form></body></html>");
|
||||
} finally {
|
||||
if(out!=null)
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
response.setContentType("text/html");
|
||||
PrintWriter out = response.getWriter();
|
||||
try {
|
||||
out.println("<html><head><title>Wheeee</title></head><body><p>Under construction whee</p></body></html>");
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
139
addresspublisher/src/i2p/dream/addresspublisher/CheckRSS.java
Normal file
139
addresspublisher/src/i2p/dream/addresspublisher/CheckRSS.java
Normal file
@ -0,0 +1,139 @@
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/* AUghhhhhh
|
||||
*
|
||||
* RSS is a terrible format!
|
||||
*
|
||||
* Oh sure we need well structured XML so that we can
|
||||
* OH HELL LET'S JUST THROW AN XML ESCAPED STRING OF
|
||||
* UNSTANDARD HTML IN THERE SO THAT PEOPLE CAN BE ON
|
||||
* THE WEB ON THEIR RSS!
|
||||
*
|
||||
* But more importantly, stats.i2p doesn't provide the address in RSS
|
||||
* easier just to request newHosts.txt from it -_-
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
public class CheckRSS extends TimerTask {
|
||||
|
||||
final URL url;
|
||||
|
||||
String lastModified = null;
|
||||
String lastEtag = null;
|
||||
private final KnownHosts knownHosts;
|
||||
private DocumentBuilder parser;
|
||||
|
||||
CheckRSS(Context context, URL url) {
|
||||
this.url = url;
|
||||
this.knownHosts = context.knownHosts;
|
||||
context.timer.schedule(this, 1000, 60*60*1000);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
URLConnection request = (HttpURLConnection) url.openConnection();
|
||||
if (lastModified != null) {
|
||||
request.setRequestProperty("If-Modified-since", lastModified);
|
||||
}
|
||||
if (lastEtag != null) {
|
||||
request.setRequestProperty("ETag", lastEtag);
|
||||
}
|
||||
lastModified = request.getHeaderField("Last-Modified");
|
||||
lastEtag = request.getHeaderField("ETag");
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = request.getInputStream();
|
||||
parse(in);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CheckRSS.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
Logger.getLogger(CheckRSS.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} }
|
||||
|
||||
void foundName(String name, String address, Date added) {
|
||||
try {
|
||||
if (knownHosts.contains(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Destination dest = new Destination();
|
||||
try {
|
||||
dest.fromBase64(address);
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
Record rec = RecordIndex.getInstance().newRecord();
|
||||
rec.setName(name);
|
||||
|
||||
rec.setAddress(dest);
|
||||
rec.setModified(new Date());
|
||||
try {
|
||||
rec.maybeSave();
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return;
|
||||
}
|
||||
knownHosts.add(name);
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.INFO, "Created new address record for {0}({1})", new Object[]{name, rec.id});
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CheckRSS.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
static DateFormat parseDate = new SimpleDateFormat("yyyy-MM-ddTHH:mm:ss+00:00");
|
||||
private void parse(InputStream in) {
|
||||
try {
|
||||
CheckRSS that = this;
|
||||
Document doc = parser.parse(in);
|
||||
for(Node e : new StupidW3C(doc.getElementsByTagName("link"))) {
|
||||
String href = e.getAttributes().getNamedItem("href").getTextContent();
|
||||
do {
|
||||
e = e.getNextSibling();
|
||||
} while(! e.getNodeName().equals("updated"));
|
||||
Date updated = parseDate.parse(e.getTextContent());
|
||||
String name = href.substring(7);
|
||||
String address = "This is BULLTHIT";
|
||||
}
|
||||
} catch (ParseException ex) {
|
||||
Logger.getLogger(CheckRSS.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (SAXException ex) {
|
||||
Logger.getLogger(CheckRSS.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CheckRSS.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,4 +24,12 @@ public class Configuration {
|
||||
}
|
||||
|
||||
static public final Charset charset = Charset.forName("UTF-8");
|
||||
|
||||
static File getTempDir() {
|
||||
File conf = getConfDir();
|
||||
File temp = new File(conf,"temp");
|
||||
if(!temp.isDirectory())
|
||||
temp.mkdir();
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
21
addresspublisher/src/i2p/dream/addresspublisher/Context.java
Normal file
21
addresspublisher/src/i2p/dream/addresspublisher/Context.java
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.util.Timer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
public class Context {
|
||||
final public Timer timer;
|
||||
final public KnownHosts knownHosts;
|
||||
Context(String name) {
|
||||
timer = new Timer(name+" Timer",true);
|
||||
knownHosts = new KnownHosts(timer);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Date;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
public class DownloadInspectHosts extends InspectHosts {
|
||||
private final URL source;
|
||||
|
||||
|
||||
/* Use this if they just have a hosts.txt file. Note you will
|
||||
* MISS SOME NEW NAMES from this source if they delete anything
|
||||
* in the file. Probably should try not to use this.
|
||||
*/
|
||||
|
||||
DownloadInspectHosts(Context context, String id, URL source) {
|
||||
super(context,id);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
File temp = File.createTempFile("hosts", "txt", Configuration.getTempDir());
|
||||
URLConnection c = source.openConnection(new Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
new InetSocketAddress("localhost",4444)));
|
||||
c.setRequestProperty("Range",Long.toString(lastPos.get())+"-");
|
||||
inspect(c.getInputStream());
|
||||
} catch (EOFException ex) {
|
||||
// done reading hosts
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DownloadInspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class DownloadRecentHosts extends InspectHosts {
|
||||
private final URL source;
|
||||
|
||||
String lastModified = null;
|
||||
String etag = null;
|
||||
|
||||
/* Use this if they list only the recent hosts and respect last-modified headers
|
||||
* i.e. if this source is another addresspublisher servlet.
|
||||
*/
|
||||
|
||||
DownloadRecentHosts(Context context, String id, URL source) {
|
||||
super(context,id);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
File temp = File.createTempFile("hosts", "txt", Configuration.getTempDir());
|
||||
URLConnection c = source.openConnection(new Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
new InetSocketAddress("localhost",4444)));
|
||||
if(lastModified!=null) {
|
||||
c.setRequestProperty("Last-Modified",lastModified);
|
||||
}
|
||||
if(etag!=null) {
|
||||
c.setRequestProperty("ETag", etag);
|
||||
}
|
||||
|
||||
lastModified = c.getRequestProperty("Last-Modified");
|
||||
etag = c.getRequestProperty("ETag");
|
||||
inspect(c.getInputStream());
|
||||
} catch (EOFException ex) {
|
||||
// done reading hosts
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DownloadRecentHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,12 +10,11 @@ import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -26,109 +25,19 @@ import net.i2p.data.Destination;
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
public class InspectHosts implements Runnable {
|
||||
|
||||
static final File hosts = new File(I2PAppContext.getGlobalContext().getConfigDir(),"hosts.txt");
|
||||
static File getNames() {
|
||||
return new File(Configuration.getConfDir(),"names.index");
|
||||
}
|
||||
static File getLastPos() {
|
||||
return new File(Configuration.getConfDir(),"last_position");
|
||||
}
|
||||
public class InspectHosts extends TimerTask {
|
||||
private final String id;
|
||||
|
||||
Set<Integer> knownHosts = new HashSet<Integer>();
|
||||
|
||||
/* The hosts.txt file doesn't change except the end. Hosts are added at the
|
||||
* end and not reordered or anything. Thus, hax!
|
||||
*/
|
||||
PersistentLong lastPos = new PersistentLong(getLastPos(),0);
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public void run() {
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(getNames());
|
||||
long lp = lastPos.get();
|
||||
if(lp>0) {
|
||||
in.getChannel().position(lp);
|
||||
}
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(in,Configuration.charset));
|
||||
for(;;) {
|
||||
String line = r.readLine();
|
||||
if(line==null) break;
|
||||
knownHosts.add(line.hashCode());
|
||||
}
|
||||
} catch(FileNotFoundException e) {
|
||||
// index doesn't exist yet
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if(in!=null) {
|
||||
try {
|
||||
lastPos.set(in.getChannel().position());
|
||||
lastPos.save(getLastPos());
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
in = new FileInputStream(hosts);
|
||||
BufferedReader b = new BufferedReader(
|
||||
new InputStreamReader(in,Configuration.charset));
|
||||
out = new FileOutputStream(getNames(),true);
|
||||
for(;;) {
|
||||
String line = b.readLine();
|
||||
if(line==null) break;
|
||||
int comment = line.indexOf("#");
|
||||
if(comment>0)
|
||||
line = line.substring(0,comment)
|
||||
.replaceAll(" ", "")
|
||||
.replaceAll("\t", "");
|
||||
|
||||
if(line.length()==0) continue;
|
||||
|
||||
int split = line.indexOf("=");
|
||||
if(split==-1) continue;
|
||||
String name = line.substring(0,split)
|
||||
.replaceAll(" ", "")
|
||||
.replaceAll("\t", "");
|
||||
|
||||
if(name.length()==0) continue;
|
||||
|
||||
if(knownHosts.contains(name.hashCode())) continue;
|
||||
|
||||
Record rec = RecordIndex.getInstance().newRecord();
|
||||
rec.setName(name);
|
||||
Destination address = new Destination();
|
||||
try {
|
||||
address.fromBase64(line.substring(split + 1));
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
continue;
|
||||
}
|
||||
rec.setAddress(address);
|
||||
rec.setModified(new Date());
|
||||
try {
|
||||
rec.maybeSave();
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
continue;
|
||||
}
|
||||
knownHosts.add(name.hashCode());
|
||||
out.write((name+"\n")
|
||||
.getBytes(Configuration.charset));
|
||||
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.INFO,
|
||||
"Created new address record for {0}({1})",
|
||||
new Object[]{name, rec.id});
|
||||
in = new FileInputStream(new File(I2PAppContext.getGlobalContext().getConfigDir(),"hosts.txt"));
|
||||
if(lastPos.get()<0) {
|
||||
lastPos.set(0);
|
||||
lastPos.save();
|
||||
}
|
||||
in.skip(lastPos.get());
|
||||
inspect(in);
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch(EOFException e) {
|
||||
@ -140,12 +49,95 @@ public class InspectHosts implements Runnable {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final File positions = new File(Configuration.getConfDir(),"positions");
|
||||
static {
|
||||
if (!positions.isDirectory())
|
||||
positions.mkdir();
|
||||
}
|
||||
|
||||
private final KnownHosts knownHosts;
|
||||
protected final PersistentLong lastPos;
|
||||
|
||||
InspectHosts(Context context, String id) {
|
||||
this.knownHosts = context.knownHosts;
|
||||
lastPos = new PersistentLong(new File(positions,id),0);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/* The hosts.txt file doesn't change except the end. Hosts are added at the
|
||||
* end and not reordered or anything. Thus, hax!
|
||||
*/
|
||||
|
||||
public void inspect(InputStream in) throws IOException {
|
||||
BufferedReader b = new BufferedReader(
|
||||
new InputStreamReader(in, Configuration.charset));
|
||||
for (;;) {
|
||||
String line = b.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
synchronized (lastPos) {
|
||||
lastPos.set(lastPos.get() + line.length());
|
||||
lastPos.save();
|
||||
}
|
||||
|
||||
int comment = line.indexOf("#");
|
||||
if(comment==0)
|
||||
continue;
|
||||
if (comment > 0) {
|
||||
line = line.substring(0, comment).replaceAll(" ", "").replaceAll("\t", "");
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int split = line.indexOf("=");
|
||||
if (split == -1) {
|
||||
continue;
|
||||
}
|
||||
String name = line.substring(0, split).replaceAll(" ", "").replaceAll("\t", "");
|
||||
|
||||
if (name.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (knownHosts.contains(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(split == line.length()-1)
|
||||
continue;
|
||||
|
||||
Destination address = new Destination();
|
||||
try {
|
||||
address.fromBase64(line.substring(split + 1));
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, name+" had a bad address: "+line.substring(split+1), ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
Record rec = RecordIndex.getInstance().newRecord();
|
||||
rec.setName(name);
|
||||
|
||||
rec.setAddress(address);
|
||||
rec.setModified(new Date());
|
||||
try {
|
||||
rec.maybeSave();
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
continue;
|
||||
}
|
||||
knownHosts.add(name);
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.INFO,
|
||||
"Created new address record for {0}({1})",
|
||||
new Object[]{name, rec.id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
125
addresspublisher/src/i2p/dream/addresspublisher/KnownHosts.java
Normal file
125
addresspublisher/src/i2p/dream/addresspublisher/KnownHosts.java
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
public class KnownHosts {
|
||||
|
||||
private final Set<Integer> knownHosts = new HashSet<Integer>();
|
||||
private final Set<String> needSaving = new HashSet<String>();
|
||||
|
||||
private static final File index = new File(Configuration.getConfDir(),"names.index");
|
||||
|
||||
final Timer timer;
|
||||
KnownHosts(Timer timer) {
|
||||
this.timer = timer;
|
||||
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(index);
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(in,Configuration.charset));
|
||||
for(;;) {
|
||||
String line = r.readLine();
|
||||
if(line==null) break;
|
||||
knownHosts.add(line.hashCode());
|
||||
}
|
||||
} catch(FileNotFoundException e) {
|
||||
// index doesn't exist yet
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if(in != null) try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(KnownHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add(String name) {
|
||||
synchronized(knownHosts) {
|
||||
knownHosts.add(name.hashCode());
|
||||
needSaving.add(name);
|
||||
}
|
||||
saveLater();
|
||||
}
|
||||
|
||||
boolean contains(String name) {
|
||||
synchronized(knownHosts) {
|
||||
return knownHosts.contains(name.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
boolean saving = false;
|
||||
final Object slock = new Object();
|
||||
void saveLater() {
|
||||
final KnownHosts that = this;
|
||||
synchronized(slock) {
|
||||
if(saving) return;
|
||||
saving = true;
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
that.save();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void save() {
|
||||
synchronized(slock) {
|
||||
saving = false;
|
||||
}
|
||||
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(index,true);
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out,
|
||||
Configuration.charset));
|
||||
synchronized(needSaving) {
|
||||
for(String name : needSaving) {
|
||||
writer.write(name,0,name.length());
|
||||
writer.newLine();
|
||||
}
|
||||
needSaving.clear();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(KnownHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if(out!=null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(KnownHosts.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
class LineReader implements Iterable<String> {
|
||||
|
||||
private static class LineIterator implements Iterator<String> {
|
||||
private final BufferedReader b;
|
||||
private boolean done = false;
|
||||
private String nextLine = null;
|
||||
|
||||
public LineIterator(BufferedReader b) {
|
||||
this.b = b;
|
||||
queueLine();
|
||||
}
|
||||
|
||||
final void queueLine() {
|
||||
try {
|
||||
nextLine = b.readLine();
|
||||
} catch (IOException ex) {
|
||||
done = true;
|
||||
}
|
||||
if(nextLine==null)
|
||||
done = true;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return !done;
|
||||
}
|
||||
|
||||
public String next() {
|
||||
String line = nextLine;
|
||||
queueLine();
|
||||
return line;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
private final BufferedReader b;
|
||||
|
||||
public LineReader(InputStream in) {
|
||||
this.b = new BufferedReader(
|
||||
new InputStreamReader(in, Configuration.charset));
|
||||
}
|
||||
|
||||
public Iterator<String> iterator() {
|
||||
return new LineIterator(b);
|
||||
}
|
||||
|
||||
}
|
@ -21,9 +21,11 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
class PersistentLong {
|
||||
long num;
|
||||
File f;
|
||||
PersistentLong(File f, long def) {
|
||||
this.f = f;
|
||||
num = def;
|
||||
InputStream in = null;
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
try {
|
||||
in = new FileInputStream(f);
|
||||
@ -31,6 +33,7 @@ class PersistentLong {
|
||||
num = 0;
|
||||
return;
|
||||
}
|
||||
in.getChannel().lock();
|
||||
byte[] buf = new byte[8];
|
||||
in.read(buf);
|
||||
num = buf[7]<<0x38 |
|
||||
@ -53,7 +56,7 @@ class PersistentLong {
|
||||
}
|
||||
}
|
||||
|
||||
public void save(File f) throws IOException {
|
||||
public void save() throws IOException {
|
||||
byte[] buf = new byte[8];
|
||||
buf[0] = (byte) (num & 0xff);
|
||||
buf[1] = (byte) ((num >> 8) & 0xff);
|
||||
@ -63,9 +66,10 @@ class PersistentLong {
|
||||
buf[5] = (byte) ((num >> 0x28) & 0xff);
|
||||
buf[6] = (byte) ((num >> 0x30) & 0xff);
|
||||
buf[7] = (byte) ((num >> 0x38) & 0xff);
|
||||
OutputStream out = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(f);
|
||||
out.getChannel().lock();
|
||||
out.write(buf);
|
||||
} finally {
|
||||
if(out!=null)
|
||||
|
@ -39,21 +39,21 @@ class RecordGetter implements Iterator<Record> {
|
||||
* @author dream
|
||||
*/
|
||||
class RecordIndex implements Iterable<Record> {
|
||||
PersistentLong top;
|
||||
final PersistentLong top;
|
||||
|
||||
RecordIndex() throws IOException {
|
||||
top = new PersistentLong(indexFile(),0);
|
||||
}
|
||||
|
||||
private File indexFile() {
|
||||
return new File(Configuration.getConfDir(), "index");
|
||||
top = new PersistentLong(
|
||||
new File(Configuration.getConfDir(), "top.long"),0);
|
||||
top.set(0xb10);
|
||||
top.save();
|
||||
System.out.println("***********8 top is "+Long.toHexString(top.get()));
|
||||
}
|
||||
|
||||
Record newRecord() throws IOException {
|
||||
synchronized (Record.class) {
|
||||
synchronized (top) {
|
||||
long id = top.get();
|
||||
top.set(id+1);
|
||||
top.save(indexFile());
|
||||
top.save();
|
||||
return Record.get(id);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,12 @@ package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.servlet.ServletConfig;
|
||||
@ -21,50 +26,16 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* @author dream
|
||||
*/
|
||||
|
||||
class RepeatedInspect extends Thread {
|
||||
InspectHosts guy = new InspectHosts();
|
||||
private boolean ready = false;
|
||||
@Override
|
||||
@SuppressWarnings("SleepWhileHoldingLock")
|
||||
public void run() {
|
||||
/* we can't wait/notify because java doesn't have inotify support, so
|
||||
* polling is the only option.
|
||||
* jnotify support would be really nice...
|
||||
* at least it seeks to the lowest last position every time!
|
||||
*/
|
||||
for(;;) {
|
||||
try {
|
||||
guy.run();
|
||||
synchronized(this) {
|
||||
ready = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
// we never want this thread to die.
|
||||
Logger.getLogger(RepeatedInspect.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
try {
|
||||
// poll once an hour.
|
||||
Thread.sleep(3600000);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(RepeatedInspect.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void waitUntilReady() throws InterruptedException {
|
||||
synchronized(this) {
|
||||
if(ready) return;
|
||||
this.wait(10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Servlet extends HttpServlet {
|
||||
Date myModified;
|
||||
private static RepeatedInspect inspect;
|
||||
|
||||
static final Context context = new Context("Servlet");
|
||||
|
||||
final Map<String,DownloadRecentHosts> subscriptions =
|
||||
new TreeMap<String,DownloadRecentHosts>();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ResultOfObjectAllocationIgnored")
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
try {
|
||||
for (Record r : RecordIndex.getInstance()) {
|
||||
@ -76,10 +47,20 @@ public class Servlet extends HttpServlet {
|
||||
}
|
||||
if(myModified==null)
|
||||
myModified = new Date(-1);
|
||||
|
||||
if(inspect==null) {
|
||||
inspect = new RepeatedInspect();
|
||||
inspect.start();
|
||||
|
||||
Timer timer = context.timer;
|
||||
|
||||
//timer.schedule(new InspectHosts(context,"master"),1000,60*60*1000);
|
||||
for (String uri : new String[] {
|
||||
"http://stats.i2p/cgi-bin/newhosts.txt",
|
||||
"http://i2host.i2p/cgi-bin/i2hostetag"}) {
|
||||
try {
|
||||
DownloadRecentHosts task = new DownloadRecentHosts(context, uri, new URL(uri));
|
||||
subscriptions.put(uri,task);
|
||||
timer.schedule(task,10*60*1000,60*60*1000);
|
||||
} catch (MalformedURLException ex) {
|
||||
Logger.getLogger(Servlet.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,12 +118,6 @@ public class Servlet extends HttpServlet {
|
||||
}
|
||||
|
||||
private void recentHosts(Date modified, PrintWriter out) throws IOException {
|
||||
try {
|
||||
inspect.waitUntilReady();
|
||||
} catch (InterruptedException ex) {
|
||||
out.write("# not ready yet, still loading. Try again in a bit.");
|
||||
return;
|
||||
}
|
||||
for(Record r : RecordIndex.getInstance()) {
|
||||
Date rmod = r.getModified();
|
||||
|
||||
@ -151,6 +126,17 @@ public class Servlet extends HttpServlet {
|
||||
if(rmod.after(myModified))
|
||||
myModified = rmod;
|
||||
|
||||
if(r.getModified() == null) {
|
||||
throw new RuntimeException("Augh! "+r.id);
|
||||
}
|
||||
if(r.getAddress() == null) {
|
||||
throw new RuntimeException("BORG" + r.id + ": "+r.getName());
|
||||
}
|
||||
if(r.getName() == null) {
|
||||
throw new RuntimeException("GROB");
|
||||
}
|
||||
|
||||
|
||||
out.write("# Modified="+Long.toHexString(r.getModified().getTime())+" "+"ID="+r.id
|
||||
+"\n"+r.getName()+"="+r.getAddress().toBase64()
|
||||
+"\n");
|
||||
|
@ -0,0 +1,38 @@
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.util.Iterator;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
class StupidW3C implements Iterable<Node> {
|
||||
|
||||
int position;
|
||||
final NodeList list;
|
||||
|
||||
public StupidW3C(NodeList list) {
|
||||
this.list = list;
|
||||
this.position = 0;
|
||||
}
|
||||
|
||||
public Iterator<Node> iterator() {
|
||||
return new Iterator<Node>() {
|
||||
int position = 0;
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position + 1) < list.getLength();
|
||||
}
|
||||
|
||||
public Node next() {
|
||||
return list.item(position++);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package i2p.dream.addresspublisher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dream
|
||||
*/
|
||||
|
||||
public class SubscriptionsServlet extends HttpServlet {
|
||||
static final Context context = new Context("Subscriptions Servlet");
|
||||
|
||||
static final File subscriptionsFile = new File(Configuration.getConfDir(),"subscriptions.txt");
|
||||
|
||||
final Map<String,DownloadRecentHosts> subscriptions =
|
||||
new TreeMap<String,DownloadRecentHosts>();
|
||||
|
||||
void reloadSubscriptions() throws IOException {
|
||||
Timer timer = context.timer;
|
||||
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(subscriptionsFile);
|
||||
FileLock lock = in.getChannel().lock();
|
||||
for(String line : new LineReader(in)) {
|
||||
int hash = line.indexOf('#');
|
||||
if(hash==0) continue;
|
||||
else if(hash > 0) {
|
||||
line = line.substring(0,hash);
|
||||
}
|
||||
if(line.length()==0) continue;
|
||||
|
||||
try {
|
||||
DownloadRecentHosts task = new DownloadRecentHosts(context, line, new URL(line));
|
||||
subscriptions.put(line, task);
|
||||
timer.schedule(task, 10 * 60 * 1000, 60 * 60 * 1000);
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
Logger.getLogger(SubscriptionsServlet.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(SubscriptionsServlet.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if(in != null)
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
try {
|
||||
reloadSubscriptions();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(SubscriptionsServlet.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** just check if console password option is set, jetty will do auth */
|
||||
private boolean validPassphrase() {
|
||||
String pass = I2PAppContext.getGlobalContext().getProperty("consolePassword");
|
||||
return pass != null && pass.trim().length() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
if(!validPassphrase()) return;
|
||||
|
||||
response.setContentType("text/html");
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = response.getWriter();
|
||||
out.println("<html><head><title>Addressbook Subscription Configuration</title></head>");
|
||||
out.print("<form method=POST><textarea name=\"subscriptions\">");
|
||||
for(String uri : subscriptions.keySet()) {
|
||||
out.println(uri);
|
||||
}
|
||||
out.println("</textarea><input type=\"submit\" value=\"submit\" />");
|
||||
out.println("</form></body></html>");
|
||||
} finally {
|
||||
if(out!=null)
|
||||
out.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String newsubs = request.getParameter("subscriptions");
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(subscriptionsFile);
|
||||
FileLock lock = out.getChannel().lock();
|
||||
out.write(Configuration.charset.encode(newsubs).array());
|
||||
} finally {
|
||||
if(out!=null)
|
||||
out.close();
|
||||
}
|
||||
|
||||
for(TimerTask task : subscriptions.values()) {
|
||||
task.cancel();
|
||||
}
|
||||
|
||||
reloadSubscriptions();
|
||||
|
||||
response.sendRedirect("/");
|
||||
}
|
||||
}
|
20
addresspublisher/web-config.xml
Normal file
20
addresspublisher/web-config.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
|
||||
<servlet>
|
||||
<servlet-name>AddressPublisherSubscriptions</servlet-name>
|
||||
<servlet-class>i2p.dream.addresspublisher.SubscriptionsServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>AddressPublisherBook</servlet-name>
|
||||
<servlet-class>i2p.dream.addresspublisher.BookServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>AddressPublisherSubscriptions</servlet-name>
|
||||
<url-pattern>/subscriptions/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet-mapping>
|
||||
<servlet-name>AddressPublisherBook</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
@ -6,9 +6,11 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.DataFormatException;
|
||||
|
||||
@ -41,17 +43,32 @@ public class Shortcut extends HttpServlet {
|
||||
if(name==null) {
|
||||
askForName(out);
|
||||
} else {
|
||||
if (name.startsWith("http://")) {
|
||||
name = name.substring(7);
|
||||
}
|
||||
int i = name.indexOf('/');
|
||||
if(i > 0) {
|
||||
name = name.substring(0,i);
|
||||
}
|
||||
boolean isb32 = false;
|
||||
Destination address = null;
|
||||
try {
|
||||
if(name.length()==Hash.HASH_LENGTH*5/4) {
|
||||
name = i2p.dream.BaseConvert.toBase32(name)+".b32.i2p";
|
||||
int length = name.length();
|
||||
if(length==Hash.HASH_LENGTH*5/4) {
|
||||
name = Base32.encode(Base64.decode(name))+".b32.i2p";
|
||||
isb32 = true;
|
||||
} else if(length==Hash.HASH_LENGTH*13/8) {
|
||||
name = name + ".b32.i2p";
|
||||
isb32 = true;
|
||||
} else if(name.endsWith(".b32.i2p")) {
|
||||
isb32 = true;
|
||||
}
|
||||
address = I2PTunnel.destFromName(name);
|
||||
} catch (DataFormatException ex) {
|
||||
Logger.getLogger(Shortcut.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
showShortcut(name,address,out);
|
||||
showShortcut(name,address,out,isb32);
|
||||
}
|
||||
} finally {
|
||||
if(out!=null) out.close();
|
||||
@ -69,21 +86,40 @@ public class Shortcut extends HttpServlet {
|
||||
|
||||
void showShortcut(String name,
|
||||
Destination address,
|
||||
PrintWriter out)
|
||||
PrintWriter out,
|
||||
boolean isb32)
|
||||
throws IOException
|
||||
{
|
||||
if(address==null) {
|
||||
out.println("Could not find '"+name+"'");
|
||||
return;
|
||||
}
|
||||
String b32 = Base32.encode(address.calculateHash().getData());
|
||||
String b64 = address.toBase64();
|
||||
final String b32 = Base32.encode(address.calculateHash().getData());
|
||||
final String b64 = address.toBase64();
|
||||
|
||||
out.println("<html><head><title>"+b32+"</title></head><body>");
|
||||
b32 = "http://" + b32 + ".b32.i2p";
|
||||
boolean showName = true;
|
||||
if(name.equals(b32)) {
|
||||
showName = false;
|
||||
}
|
||||
|
||||
out.println("<html><head><title>");
|
||||
if(showName) {
|
||||
out.print(name+" - ");
|
||||
}
|
||||
out.println(b32);
|
||||
out.println("</title></head><body>");
|
||||
final String b32uri = "http://" + b32 + ".b32.i2p";
|
||||
if(showName) {
|
||||
out.println("<p>Information for: "+name+"</p>");
|
||||
}
|
||||
out.println("<p>Base64:<blockquote>"+b64+"</blockquote></p>");
|
||||
out.println("<p>Base32:<blockquote>"+b32+"</blockquote></p>");
|
||||
out.println("<p>Click <a href=\"http://localhost:7657/susidns/addressbook.jsp?book=master&destination="+b64+"\">here</a> and fill in a name at the bottom to add this site to your addressbook!</p>");
|
||||
out.println("<p>Base32:<a href=\""+b32uri+"\"><blockquote>"+b32+"</blockquote></a></p>");
|
||||
out.println("<p>Click <a href=\"http://localhost:7657/susidns/addressbook.jsp?book=master&"+
|
||||
(isb32 ? "" : ("hostname="+URLEncoder.encode(name)+"&")) +
|
||||
"destination="+b64+
|
||||
"\">here</a> and " +
|
||||
(isb32 ? "fill in a name at" : "page down to") +
|
||||
" the bottom to add this site to your addressbook!</p>");
|
||||
try {
|
||||
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
StringSelection s = new StringSelection(b32);
|
||||
@ -100,4 +136,4 @@ public class Shortcut extends HttpServlet {
|
||||
}
|
||||
out.println("</body></html>");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
Reference in New Issue
Block a user