forked from I2P_Developers/i2p.i2p
Compare commits
232 Commits
i2p_0_4_1_
...
i2p_0_5_po
Author | SHA1 | Date | |
---|---|---|---|
6e8e77b9ec | |||
7ef9ce8cc6 | |||
9646ac2911 | |||
566a713baa | |||
36f7e98e90 | |||
4da755816a | |||
3ef0258faf | |||
293ceaee93 | |||
7b58d0fa0f | |||
bd68c1e056 | |||
200162d973 | |||
4b37a53f1c | |||
2d41de7ae0 | |||
bc5bc62c18 | |||
a0d680024e | |||
45013feea7 | |||
2abbe992dd | |||
d7081b3eeb | |||
a2f5289bd9 | |||
b366a4b942 | |||
27e92653fe | |||
80120b7b7d | |||
af8a618826 | |||
af0e554562 | |||
382cbb18db | |||
252b523155 | |||
4303b3b716 | |||
87715dc21a | |||
8552494fc1 | |||
1c2290b613 | |||
5f6060b801 | |||
b39958604d | |||
22ca1491bc | |||
690d7e30cf | |||
4fac2f1094 | |||
eb0935d577 | |||
425fedf55b | |||
a33de09ae6 | |||
5018e56103 | |||
de2c975ac2 | |||
d86e2c0f59 | |||
14023163b3 | |||
d85dc8213e | |||
f6a34055ac | |||
3beb0d9c12 | |||
60968fe6f1 | |||
517c3101c7 | |||
998f03ba68 | |||
f3b0e0cfc7 | |||
a65e6c888c | |||
cd939d3379 | |||
29e5aeff5c | |||
0e5cf81fca | |||
61f217c610 | |||
ccb1f491c7 | |||
49fdac9b4e | |||
6b6a9490f6 | |||
2c783e9876 | |||
ecd971c0e5 | |||
c48875a6fb | |||
a245ccb8b7 | |||
75a18debcb | |||
1a15d3bb55 | |||
ffdcae47e3 | |||
34a2bc8590 | |||
8ae4d00ccb | |||
9ed6d5e7fb | |||
c9243b241c | |||
9c364a64e3 | |||
b34306205c | |||
77f778dbf9 | |||
23fa4e4161 | |||
5b6fd0b829 | |||
8fa8d7739f | |||
dc552c7a29 | |||
cf84f453d3 | |||
daf32a24bc | |||
34ecfd9857 | |||
4838564460 | |||
3dd2f67ff3 | |||
0ccec3dde0 | |||
ad77879caa | |||
27999983cc | |||
48b039940d | |||
84dc7d9d82 | |||
70d6332bad | |||
aec0b0c86a | |||
099f6a88c2 | |||
00a5d42d3d | |||
28f4a2cb67 | |||
1ac18ba10e | |||
1503ee2dfa | |||
484b528d4f | |||
758293dc02 | |||
6cb316b33e | |||
1d31831e7d | |||
ee32b07995 | |||
81f04ca692 | |||
1756997608 | |||
ec11ea4ca7 | |||
a1ebf85e1b | |||
4b2a734cda | |||
97ae8f78a0 | |||
834665c3ba | |||
d969dd2d8d | |||
3cb727561c | |||
cbc89376d3 | |||
66aa29e3d4 | |||
5c72aca5ee | |||
8824815d6d | |||
ad72e5cbdf | |||
b2f183fc17 | |||
9e16bc203a | |||
83c6eac017 | |||
d5b277a536 | |||
77ce6c33e3 | |||
60f8d349cf | |||
f539c3df70 | |||
fe1cf1758c | |||
8c71c26487 | |||
2ce39d1fd4 | |||
88a994b712 | |||
24c8cc1a0c | |||
caf684394c | |||
4b74510450 | |||
0ddcfc423e | |||
b4ac56e204 | |||
3b19ac3942 | |||
af52cad4ea | |||
d88396c1e2 | |||
4c5f7b9451 | |||
e601cedbb8 | |||
fa12dc867f | |||
acfb6c4578 | |||
e52d637092 | |||
2fba055696 | |||
88bb176f3b | |||
499eeb275b | |||
61a8d679bb | |||
2bbde91625 | |||
9ce098ee06 | |||
927ae57d24 | |||
d65c2d3539 | |||
2d9d8f32dc | |||
1a30cd5f4a | |||
f54687f398 | |||
9f4b4c5de1 | |||
33bfa94229 | |||
61e5f190a6 | |||
a4946272d0 | |||
8abd99d134 | |||
97e8ab7c5b | |||
cb930a7ab5 | |||
610f1f7dd4 | |||
516d0b4db8 | |||
df61ae5c6f | |||
9f6584b55e | |||
e4b41f5bb0 | |||
8d0cea93e9 | |||
d294d07919 | |||
153eea2bd5 | |||
571e3c5c13 | |||
a2d268f3d6 | |||
02d456d7a0 | |||
b3626ad86f | |||
9b6eab451f | |||
72be9b5f04 | |||
35e94a7f65 | |||
8e02586cc9 | |||
0b5a640896 | |||
64b5089909 | |||
e0e09bfa45 | |||
8c7f9f2c65 | |||
aff5cea949 | |||
8bd99f699f | |||
b0513fff8a | |||
f10db9d91a | |||
9b5fb17068 | |||
608d713dca | |||
6d5fc8ca21 | |||
8c3145b70f | |||
12a6f3e938 | |||
2c59435762 | |||
7336bf5c55 | |||
2b21b97277 | |||
603bc99a2f | |||
426ede1c99 | |||
21506c1d1b | |||
0b48b18e7e | |||
c8f6d9c7a1 | |||
ed8eced9dd | |||
4a029b7853 | |||
6bd9e58ece | |||
cd075fc8a6 | |||
e733427920 | |||
107da0ae22 | |||
3629d7a32c | |||
71e1152cde | |||
d01ab7fd23 | |||
f46d0a720c | |||
d943b4993a | |||
4a4f57d6ac | |||
085da16268 | |||
306f6b0037 | |||
3780d290fa | |||
ad7dc66f90 | |||
258244fed8 | |||
5f7982540f | |||
b1c0de4b77 | |||
45b3fecfff | |||
9774ded4dd | |||
7ec027854e | |||
b457001b42 | |||
6fc6866eb4 | |||
299e5528bc | |||
881524a5e4 | |||
ffc405138d | |||
f6ff74af16 | |||
73a12d47de | |||
83165df7e5 | |||
30074be5a5 | |||
16715aa309 | |||
53f3802a81 | |||
07626b5cc2 | |||
9ea603caf2 | |||
18ab9b80d2 | |||
71c1cb4e12 | |||
0c049f39d9 | |||
096b807c37 | |||
9018af4765 | |||
323f28e306 | |||
e9dbd00f42 |
43
apps/addressbook/README.txt
Normal file
43
apps/addressbook/README.txt
Normal file
@ -0,0 +1,43 @@
|
||||
addressbook v2.0.2 - A simple name resolution mechanism for I2P
|
||||
|
||||
addressbook is a simple implementation of subscribable address books for I2P.
|
||||
Addresses are stored in userhosts.txt and a second copy of the address book is
|
||||
placed on your eepsite as hosts.txt.
|
||||
|
||||
subscriptions.txt contains a list of urls to check for new addresses.
|
||||
Since the urls are checked in order, and conflicting addresses are not added,
|
||||
addressbook.subscriptions can be considered to be ranked in order of trust.
|
||||
|
||||
The system created by addressbook is similar to the early days of DNS,
|
||||
when everyone ran a local name server. The major difference is the lack of
|
||||
authority. Name cannot be guaranteed to be globally unique, but in practise
|
||||
they probably will be, for a variety of social reasons.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
i2p with a running http proxy
|
||||
|
||||
Installation and Usage
|
||||
**********************
|
||||
|
||||
1. Unzip addressbook-%ver.zip into your i2p directory.
|
||||
2. Restart your router.
|
||||
|
||||
The addressbook daemon will automatically run while the router is up.
|
||||
|
||||
Aside from the daemon itself, the other elements of the addressbook interface
|
||||
are the config.txt, myhosts.txt, and subscriptions.txt files found in the addressbook
|
||||
directory.
|
||||
|
||||
config.txt is the configuration file for addressbook.
|
||||
|
||||
myhosts.txt is the addressbook master address book. Addresses placed in this file
|
||||
take precidence over those in the router address book and in remote address books.
|
||||
If changes are made to this file, they will be reflected in the router address book
|
||||
and published address book after the next update. Do not make changes directly to the
|
||||
router address book, as they could be lost during an update.
|
||||
|
||||
subscriptions.txt is the subscription list for addressbook. Each entry is an absolute
|
||||
url to a file in hosts.txt format. Since the list is checked in order, url's should be
|
||||
listed in order of trust.
|
46
apps/addressbook/build.xml
Normal file
46
apps/addressbook/build.xml
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="addressbook" default="war" basedir=".">
|
||||
|
||||
<property name="src" value="java/src/addressbook"/>
|
||||
<property name="build" value="build"/>
|
||||
<property name="dist" location="dist"/>
|
||||
<property name="jar" value="addressbook.jar"/>
|
||||
<property name="war" value="addressbook.war"/>
|
||||
<property name="servlet" value="../jetty/jettylib/javax.servlet.jar"/>
|
||||
|
||||
<target name="init">
|
||||
<mkdir dir="${build}"/>
|
||||
<mkdir dir="${dist}"/>
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="${build}"/>
|
||||
<delete dir="${dist}"/>
|
||||
</target>
|
||||
|
||||
<target name="distclean" depends="clean" />
|
||||
|
||||
<target name="compile" depends="init">
|
||||
<javac srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<jar basedir="${build}" destfile="${dist}/${jar}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="addressbook.Daemon"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile">
|
||||
<mkdir dir="${dist}/tmp"/>
|
||||
<mkdir dir="${dist}/tmp/WEB-INF"/>
|
||||
<mkdir dir="${dist}/tmp/WEB-INF/classes"/>
|
||||
<copy todir="${dist}/tmp/WEB-INF/classes">
|
||||
<fileset dir="${build}"/>
|
||||
</copy>
|
||||
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}"/>
|
||||
<delete dir="${dist}/tmp"/>
|
||||
</target>
|
||||
|
||||
</project>
|
43
apps/addressbook/config.txt
Normal file
43
apps/addressbook/config.txt
Normal file
@ -0,0 +1,43 @@
|
||||
# This is the configuration file for addressbook.
|
||||
#
|
||||
# Options
|
||||
# *******
|
||||
# All paths are realitive to i2p/addressbook. Default value for
|
||||
# each option is given in parentheses.
|
||||
#
|
||||
# proxy_host The hostname of your I2P http proxy.
|
||||
# (localhost)
|
||||
#
|
||||
# proxy_port The port of your I2P http proxy. (4444)
|
||||
#
|
||||
# master_addressbook The path to your master address book, used for local
|
||||
# changes only. (myhosts.txt)
|
||||
#
|
||||
# router_addressbook The path to the address book used by the router.
|
||||
# Contains the addresses from your master address book
|
||||
# and your subscribed address books. (../userhosts.txt)
|
||||
#
|
||||
# published_addressbook The path to the copy of your address book made
|
||||
# available on i2p. (../eepsite/docroot/hosts.txt)
|
||||
#
|
||||
# log The path to your addressbook log. (log.txt)
|
||||
#
|
||||
# subscriptions The path to your subscription file. (subscriptions.txt)
|
||||
#
|
||||
# etags The path to the etags header storage file. (etags)
|
||||
#
|
||||
# last_modified The path to the last-modified header storage file.
|
||||
# (last_modified)
|
||||
#
|
||||
# update_delay The time (in hours) between each update. (1)
|
||||
|
||||
proxy_host=localhost
|
||||
proxy_port=4444
|
||||
master_addressbook=myhosts.txt
|
||||
router_addressbook=../userhosts.txt
|
||||
published_addressbook=../eepsite/docroot/hosts.txt
|
||||
log=log.txt
|
||||
subscriptions=subscriptions.txt
|
||||
etags=etags
|
||||
last_modified=last_modified
|
||||
update_delay=1
|
246
apps/addressbook/java/src/addressbook/AddressBook.java
Normal file
246
apps/addressbook/java/src/addressbook/AddressBook.java
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An address book for storing human readable names mapped to base64 i2p
|
||||
* destinations. AddressBooks can be created from local and remote files, merged
|
||||
* together, and written out to local files.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class AddressBook {
|
||||
|
||||
private String location;
|
||||
|
||||
private Map addresses;
|
||||
|
||||
private boolean modified;
|
||||
|
||||
/**
|
||||
* Construct an AddressBook from the contents of the Map addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* A Map containing human readable addresses as keys, mapped to
|
||||
* base64 i2p destinations.
|
||||
*/
|
||||
public AddressBook(Map addresses) {
|
||||
this.addresses = addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an AddressBook from the contents of the file at url. If the
|
||||
* remote file cannot be read, construct an empty AddressBook
|
||||
*
|
||||
* @param url
|
||||
* A URL pointing at a file with lines in the format "key=value",
|
||||
* where key is a human readable name, and value is a base64 i2p
|
||||
* destination.
|
||||
*/
|
||||
public AddressBook(URL url) {
|
||||
this.location = url.getHost();
|
||||
|
||||
try {
|
||||
this.addresses = ConfigParser.parse(url);
|
||||
} catch (IOException exp) {
|
||||
this.addresses = new HashMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an AddressBook from the Subscription subscription. If the
|
||||
* address book at subscription has not changed since the last time it was
|
||||
* read or cannot be read, return an empty AddressBook.
|
||||
*
|
||||
* @param subscription
|
||||
* A Subscription instance pointing at a remote address book.
|
||||
*/
|
||||
public AddressBook(Subscription subscription) {
|
||||
this.location = subscription.getLocation();
|
||||
|
||||
try {
|
||||
URL url = new URL(subscription.getLocation());
|
||||
HttpURLConnection connection = (HttpURLConnection) url
|
||||
.openConnection();
|
||||
if (subscription.getEtag() != null) {
|
||||
connection.addRequestProperty("If-None-Match", subscription
|
||||
.getEtag());
|
||||
}
|
||||
if (subscription.getLastModified() != null) {
|
||||
connection.addRequestProperty("If-Modified-Since", subscription
|
||||
.getLastModified());
|
||||
}
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||
connection.disconnect();
|
||||
this.addresses = new HashMap();
|
||||
return;
|
||||
}
|
||||
if (connection.getHeaderField("ETag") != null) {
|
||||
subscription.setEtag(connection.getHeaderField("ETag"));
|
||||
}
|
||||
if (connection.getHeaderField("Last-Modified") != null) {
|
||||
subscription.setLastModified(connection
|
||||
.getHeaderField("Last-Modified"));
|
||||
}
|
||||
} catch (IOException exp) {
|
||||
}
|
||||
|
||||
try {
|
||||
this.addresses = ConfigParser.parse(new URL(subscription
|
||||
.getLocation()));
|
||||
} catch (IOException exp) {
|
||||
this.addresses = new HashMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an AddressBook from the contents of the file at file. If the
|
||||
* file cannot be read, construct an empty AddressBook
|
||||
*
|
||||
* @param file
|
||||
* A File pointing at a file with lines in the format
|
||||
* "key=value", where key is a human readable name, and value is
|
||||
* a base64 i2p destination.
|
||||
*/
|
||||
public AddressBook(File file) {
|
||||
this.location = file.toString();
|
||||
try {
|
||||
this.addresses = ConfigParser.parse(file);
|
||||
} catch (IOException exp) {
|
||||
this.addresses = new HashMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map containing the addresses in the AddressBook.
|
||||
*
|
||||
* @return A Map containing the addresses in the AddressBook, where the key
|
||||
* is a human readable name, and the value is a base64 i2p
|
||||
* destination.
|
||||
*/
|
||||
public Map getAddresses() {
|
||||
return this.addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location of the file this AddressBook was constructed from.
|
||||
*
|
||||
* @return A String representing either an abstract path, or a url,
|
||||
* depending on how the instance was constructed.
|
||||
*/
|
||||
public String getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation of the contents of the AddressBook.
|
||||
*
|
||||
* @return A String representing the contents of the AddressBook.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.addresses.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this AddressBook with AddressBook other, writing messages about new
|
||||
* addresses or conflicts to log. Addresses in AddressBook other that are
|
||||
* not in this AddressBook are added to this AddressBook. In case of a
|
||||
* conflict, addresses in this AddressBook take precedence
|
||||
*
|
||||
* @param other
|
||||
* An AddressBook to merge with.
|
||||
* @param log
|
||||
* The log to write messages about new addresses or conflicts to.
|
||||
*/
|
||||
public void merge(AddressBook other, Log log) {
|
||||
Iterator otherIter = other.addresses.keySet().iterator();
|
||||
|
||||
while (otherIter.hasNext()) {
|
||||
String otherKey = (String) otherIter.next();
|
||||
String otherValue = (String) other.addresses.get(otherKey);
|
||||
|
||||
if (otherKey.endsWith(".i2p") && otherValue.length() >= 516) {
|
||||
if (this.addresses.containsKey(otherKey)) {
|
||||
if (!this.addresses.get(otherKey).equals(otherValue)
|
||||
&& log != null) {
|
||||
log.append("Conflict for " + otherKey + " from "
|
||||
+ other.location
|
||||
+ ". Destination in remote address book is "
|
||||
+ otherValue);
|
||||
}
|
||||
} else {
|
||||
this.addresses.put(otherKey, otherValue);
|
||||
this.modified = true;
|
||||
if (log != null) {
|
||||
log.append("New address " + otherKey
|
||||
+ " added to address book.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this AddressBook with other, without logging.
|
||||
*
|
||||
* @param other
|
||||
* An AddressBook to merge with.
|
||||
*/
|
||||
public void merge(AddressBook other) {
|
||||
this.merge(other, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of this AddressBook out to the File file. If the file
|
||||
* cannot be writen to, this method will silently fail.
|
||||
*
|
||||
* @param file
|
||||
* The file to write the contents of this AddressBook too.
|
||||
*/
|
||||
public void write(File file) {
|
||||
if (this.modified) {
|
||||
try {
|
||||
ConfigParser.write(this.addresses, file);
|
||||
} catch (IOException exp) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this AddressBook out to the file it was read from. Requires that
|
||||
* AddressBook was constructed from a file on the local filesystem. If the
|
||||
* file cannot be writen to, this method will silently fail.
|
||||
*/
|
||||
public void write() {
|
||||
this.write(new File(this.location));
|
||||
}
|
||||
}
|
323
apps/addressbook/java/src/addressbook/ConfigParser.java
Normal file
323
apps/addressbook/java/src/addressbook/ConfigParser.java
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Iterator;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Utility class providing methods to parse and write files in config file
|
||||
* format, and subscription file format.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*/
|
||||
public class ConfigParser {
|
||||
|
||||
/**
|
||||
* Strip the comments from a String. Lines that begin with '#' and ';' are
|
||||
* considered comments, as well as any part of a line after a '#'.
|
||||
*
|
||||
* @param inputLine
|
||||
* A String to strip comments from.
|
||||
* @return A String without comments, but otherwise identical to inputLine.
|
||||
*/
|
||||
public static String stripComments(String inputLine) {
|
||||
if (inputLine.startsWith(";")) {
|
||||
return "";
|
||||
}
|
||||
if (inputLine.split("#").length > 0) {
|
||||
return inputLine.split("#")[0];
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map using the contents of BufferedReader input. input must have
|
||||
* a single key, value pair on each line, in the format: key=value. Lines
|
||||
* starting with '#' or ';' are considered comments, and ignored. Lines that
|
||||
* are obviously not in the format key=value are also ignored.
|
||||
*
|
||||
* @param input
|
||||
* A BufferedReader with lines in key=value format to parse into
|
||||
* a Map.
|
||||
* @return A Map containing the key, value pairs from input.
|
||||
* @throws IOException
|
||||
* if the BufferedReader cannot be read.
|
||||
*
|
||||
*/
|
||||
public static Map parse(BufferedReader input) throws IOException {
|
||||
Map result = new HashMap();
|
||||
String inputLine;
|
||||
inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = ConfigParser.stripComments(inputLine);
|
||||
String[] splitLine = inputLine.split("=");
|
||||
if (splitLine.length == 2) {
|
||||
result.put(splitLine[0].trim(), splitLine[1].trim());
|
||||
}
|
||||
inputLine = input.readLine();
|
||||
}
|
||||
input.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map using the contents of the file at url. See
|
||||
* parseBufferedReader for details of the input format.
|
||||
*
|
||||
* @param url
|
||||
* A url pointing to a file to parse.
|
||||
* @return A Map containing the key, value pairs from url.
|
||||
* @throws IOException
|
||||
* if url cannot be read.
|
||||
*/
|
||||
public static Map parse(URL url) throws IOException {
|
||||
InputStream urlStream;
|
||||
urlStream = url.openConnection().getInputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
urlStream));
|
||||
return ConfigParser.parse(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map using the contents of the File file. See parseBufferedReader
|
||||
* for details of the input format.
|
||||
*
|
||||
* @param file
|
||||
* A File to parse.
|
||||
* @return A Map containing the key, value pairs from file.
|
||||
* @throws IOException
|
||||
* if file cannot be read.
|
||||
*/
|
||||
public static Map parse(File file) throws IOException {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
fileStream));
|
||||
return ConfigParser.parse(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map using the contents of the String string. See
|
||||
* parseBufferedReader for details of the input format.
|
||||
*
|
||||
* @param string
|
||||
* A String to parse.
|
||||
* @return A Map containing the key, value pairs from string.
|
||||
* @throws IOException
|
||||
* if file cannot be read.
|
||||
*/
|
||||
public static Map parse(String string) throws IOException {
|
||||
StringReader stringReader = new StringReader(string);
|
||||
BufferedReader input = new BufferedReader(stringReader);
|
||||
return ConfigParser.parse(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map using the contents of the File file. If file cannot be read,
|
||||
* use map instead, and write the result to where file should have been.
|
||||
*
|
||||
* @param file
|
||||
* A File to attempt to parse.
|
||||
* @param map
|
||||
* A Map to use as the default, if file fails.
|
||||
* @return A Map containing the key, value pairs from file, or if file
|
||||
* cannot be read, map.
|
||||
*/
|
||||
public static Map parse(File file, Map map) {
|
||||
Map result = new HashMap();
|
||||
try {
|
||||
result = ConfigParser.parse(file);
|
||||
} catch (IOException exp) {
|
||||
result = map;
|
||||
try {
|
||||
ConfigParser.write(result, file);
|
||||
} catch (IOException exp2) {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a List where each element is a line from the BufferedReader input.
|
||||
*
|
||||
* @param input
|
||||
* A BufferedReader to parse.
|
||||
* @return A List consisting of one element for each line in input.
|
||||
* @throws IOException
|
||||
* if input cannot be read.
|
||||
*/
|
||||
public static List parseSubscriptions(BufferedReader input)
|
||||
throws IOException {
|
||||
List result = new LinkedList();
|
||||
String inputLine = input.readLine();
|
||||
while (inputLine != null) {
|
||||
inputLine = ConfigParser.stripComments(inputLine).trim();
|
||||
if (inputLine.length() > 0) {
|
||||
result.add(inputLine);
|
||||
}
|
||||
inputLine = input.readLine();
|
||||
}
|
||||
input.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a List where each element is a line from the File file.
|
||||
*
|
||||
* @param file
|
||||
* A File to parse.
|
||||
* @return A List consisting of one element for each line in file.
|
||||
* @throws IOException
|
||||
* if file cannot be read.
|
||||
*/
|
||||
public static List parseSubscriptions(File file) throws IOException {
|
||||
FileInputStream fileStream = new FileInputStream(file);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(
|
||||
fileStream));
|
||||
return ConfigParser.parseSubscriptions(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a List where each element is a line from the String string.
|
||||
*
|
||||
* @param string
|
||||
* A String to parse.
|
||||
* @return A List consisting of one element for each line in string.
|
||||
* @throws IOException
|
||||
* if string cannot be read.
|
||||
*/
|
||||
public static List parseSubscriptions(String string) throws IOException {
|
||||
StringReader stringReader = new StringReader(string);
|
||||
BufferedReader input = new BufferedReader(stringReader);
|
||||
return ConfigParser.parseSubscriptions(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a List using the contents of the File file. If file cannot be
|
||||
* read, use list instead, and write the result to where file should have
|
||||
* been.
|
||||
*
|
||||
* @param file
|
||||
* A File to attempt to parse.
|
||||
* @param string
|
||||
* A List to use as the default, if file fails.
|
||||
* @return A List consisting of one element for each line in file, or if
|
||||
* file cannot be read, list.
|
||||
*/
|
||||
public static List parseSubscriptions(File file, List list) {
|
||||
List result = new LinkedList();
|
||||
try {
|
||||
result = ConfigParser.parseSubscriptions(file);
|
||||
} catch (IOException exp) {
|
||||
result = list;
|
||||
try {
|
||||
ConfigParser.writeSubscriptions(result, file);
|
||||
} catch (IOException exp2) {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents of Map map to BufferedWriter output. Output is written
|
||||
* with one key, value pair on each line, in the format: key=value.
|
||||
*
|
||||
* @param map
|
||||
* A Map to write to output.
|
||||
* @param output
|
||||
* A BufferedWriter to write the Map to.
|
||||
* @throws IOException
|
||||
* if the BufferedWriter cannot be written to.
|
||||
*/
|
||||
public static void write(Map map, BufferedWriter output) throws IOException {
|
||||
Iterator keyIter = map.keySet().iterator();
|
||||
|
||||
while (keyIter.hasNext()) {
|
||||
String key = (String) keyIter.next();
|
||||
output.write(key + "=" + (String) map.get(key));
|
||||
output.newLine();
|
||||
}
|
||||
output.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents of Map map to the File file. Output is written
|
||||
* with one key, value pair on each line, in the format: key=value.
|
||||
*
|
||||
* @param map
|
||||
* A Map to write to file.
|
||||
* @param file
|
||||
* A File to write the Map to.
|
||||
* @throws IOException
|
||||
* if file cannot be written to.
|
||||
*/
|
||||
public static void write(Map map, File file) throws IOException {
|
||||
ConfigParser
|
||||
.write(map, new BufferedWriter(new FileWriter(file, false)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents of List list to BufferedReader output. Output is written
|
||||
* with each element of list on a new line.
|
||||
*
|
||||
* @param list
|
||||
* A List to write to file.
|
||||
* @param output
|
||||
* A BufferedReader to write list to.
|
||||
* @throws IOException
|
||||
* if output cannot be written to.
|
||||
*/
|
||||
public static void writeSubscriptions(List list, BufferedWriter output)
|
||||
throws IOException {
|
||||
Iterator iter = list.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
output.write((String) iter.next());
|
||||
output.newLine();
|
||||
}
|
||||
output.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents of List list to File file. Output is written with each
|
||||
* element of list on a new line.
|
||||
*
|
||||
* @param list
|
||||
* A List to write to file.
|
||||
* @param file
|
||||
* A File to write list to.
|
||||
* @throws IOException
|
||||
* if output cannot be written to.
|
||||
*/
|
||||
public static void writeSubscriptions(List list, File file)
|
||||
throws IOException {
|
||||
ConfigParser.writeSubscriptions(list, new BufferedWriter(
|
||||
new FileWriter(file, false)));
|
||||
}
|
||||
|
||||
}
|
171
apps/addressbook/java/src/addressbook/Daemon.java
Normal file
171
apps/addressbook/java/src/addressbook/Daemon.java
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Main class of addressbook. Performs updates, and runs the main loop.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class Daemon {
|
||||
public static final String VERSION = "2.0.3";
|
||||
|
||||
/**
|
||||
* Update the router and published address books using remote data from the
|
||||
* subscribed address books listed in subscriptions.
|
||||
*
|
||||
* @param master
|
||||
* The master AddressBook. This address book is never
|
||||
* overwritten, so it is safe for the user to write to.
|
||||
* @param router
|
||||
* The router AddressBook. This is the address book read by
|
||||
* client applications.
|
||||
* @param published
|
||||
* The published AddressBook. This address book is published on
|
||||
* the user's eepsite so that others may subscribe to it.
|
||||
* @param subscriptions
|
||||
* A SubscriptionList listing the remote address books to update
|
||||
* from.
|
||||
* @param log
|
||||
* The log to write changes and conflicts to.
|
||||
*/
|
||||
public static void update(AddressBook master, AddressBook router,
|
||||
File published, SubscriptionList subscriptions, Log log) {
|
||||
String routerLocation = router.getLocation();
|
||||
master.merge(router);
|
||||
Iterator iter = subscriptions.iterator();
|
||||
while (iter.hasNext()) {
|
||||
master.merge((AddressBook) iter.next(), log);
|
||||
}
|
||||
master.write(new File(routerLocation));
|
||||
master.write(published);
|
||||
subscriptions.write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an update, using the Map settings to provide the parameters.
|
||||
*
|
||||
* @param settings
|
||||
* A Map containg the parameters needed by update.
|
||||
* @param home
|
||||
* The directory containing addressbook's configuration files.
|
||||
*/
|
||||
public static void update(Map settings, String home) {
|
||||
File masterFile = new File(home, (String) settings
|
||||
.get("master_addressbook"));
|
||||
File routerFile = new File(home, (String) settings
|
||||
.get("router_addressbook"));
|
||||
File published = new File(home, (String) settings
|
||||
.get("published_addressbook"));
|
||||
File subscriptionFile = new File(home, (String) settings
|
||||
.get("subscriptions"));
|
||||
File logFile = new File(home, (String) settings.get("log"));
|
||||
File etagsFile = new File(home, (String) settings.get("etags"));
|
||||
File lastModifiedFile = new File(home, (String) settings
|
||||
.get("last_modified"));
|
||||
|
||||
AddressBook master = new AddressBook(masterFile);
|
||||
AddressBook router = new AddressBook(routerFile);
|
||||
|
||||
List defaultSubs = new LinkedList();
|
||||
defaultSubs.add("http://dev.i2p/i2p/hosts.txt");
|
||||
defaultSubs.add("http://duck.i2p/hosts.txt");
|
||||
|
||||
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
|
||||
etagsFile, lastModifiedFile, defaultSubs);
|
||||
Log log = new Log(logFile);
|
||||
|
||||
Daemon.update(master, router, published, subscriptions, log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings, set the proxy, then enter into the main loop. The main
|
||||
* loop performs an immediate update, and then an update every number of
|
||||
* hours, as configured in the settings file.
|
||||
*
|
||||
* @param args
|
||||
* Command line arguments. If there are any arguments provided,
|
||||
* the first is taken as addressbook's home directory, and the
|
||||
* others are ignored.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
String settingsLocation = "config.txt";
|
||||
Map settings = new HashMap();
|
||||
String home;
|
||||
if (args.length > 0) {
|
||||
home = args[0];
|
||||
} else {
|
||||
home = ".";
|
||||
}
|
||||
|
||||
Map defaultSettings = new HashMap();
|
||||
defaultSettings.put("proxy_host", "localhost");
|
||||
defaultSettings.put("proxy_port", "4444");
|
||||
defaultSettings.put("master_addressbook", "../userhosts.txt");
|
||||
defaultSettings.put("router_addressbook", "../hosts.txt");
|
||||
defaultSettings.put("published_addressbook", "../eepsite/docroot/hosts.txt");
|
||||
defaultSettings.put("log", "log.txt");
|
||||
defaultSettings.put("subscriptions", "subscriptions.txt");
|
||||
defaultSettings.put("etags", "etags");
|
||||
defaultSettings.put("last_modified", "last_modified");
|
||||
defaultSettings.put("update_delay", "1");
|
||||
|
||||
File homeFile = new File(home);
|
||||
if (!homeFile.exists()) {
|
||||
boolean created = homeFile.mkdirs();
|
||||
if (created)
|
||||
System.out.println("INFO: Addressbook directory " + homeFile.getName() + " created");
|
||||
else
|
||||
System.out.println("ERROR: Addressbook directory " + homeFile.getName() + " could not be created");
|
||||
}
|
||||
|
||||
File settingsFile = new File(homeFile, settingsLocation);
|
||||
|
||||
while (true) {
|
||||
settings = ConfigParser.parse(settingsFile, defaultSettings);
|
||||
|
||||
System.setProperty("proxySet", "true");
|
||||
System.setProperty("http.proxyHost", (String) settings
|
||||
.get("proxy_host"));
|
||||
System.setProperty("http.proxyPort", (String) settings
|
||||
.get("proxy_port"));
|
||||
long delay = Long.parseLong((String) settings.get("update_delay"));
|
||||
if (delay < 1) {
|
||||
delay = 1;
|
||||
}
|
||||
|
||||
Daemon.update(settings, home);
|
||||
try {
|
||||
Thread.sleep(delay * 60 * 60 * 1000);
|
||||
} catch (InterruptedException exp) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
apps/addressbook/java/src/addressbook/DaemonThread.java
Normal file
53
apps/addressbook/java/src/addressbook/DaemonThread.java
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
/**
|
||||
* A thread that waits five minutes, then runs the addressbook daemon.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class DaemonThread extends Thread {
|
||||
|
||||
private String[] args;
|
||||
|
||||
/**
|
||||
* Construct a DaemonThread with the command line arguments args.
|
||||
* @param args
|
||||
* A String array to pass to Daemon.main().
|
||||
*/
|
||||
public DaemonThread(String[] args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run()
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(5 * 60 * 1000);
|
||||
} catch (InterruptedException exp) {
|
||||
}
|
||||
Daemon.main(this.args);
|
||||
}
|
||||
}
|
76
apps/addressbook/java/src/addressbook/Log.java
Normal file
76
apps/addressbook/java/src/addressbook/Log.java
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* A simple log with automatic time stamping.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class Log {
|
||||
|
||||
private File file;
|
||||
|
||||
/**
|
||||
* Construct a Log instance that writes to the File file.
|
||||
*
|
||||
* @param file
|
||||
* A File for the log to write to.
|
||||
*/
|
||||
public Log(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write entry to a new line in the log, with appropriate time stamp.
|
||||
*
|
||||
* @param entry
|
||||
* A String containing a message to append to the log.
|
||||
*/
|
||||
public void append(String entry) {
|
||||
try {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(this.file,
|
||||
true));
|
||||
String timestamp = new Date().toString();
|
||||
bw.write(timestamp + " -- " + entry);
|
||||
bw.newLine();
|
||||
bw.close();
|
||||
} catch (IOException exp) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the File that the Log is writing to.
|
||||
*
|
||||
* @return The File that the log is writing to.
|
||||
*/
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
}
|
61
apps/addressbook/java/src/addressbook/Servlet.java
Normal file
61
apps/addressbook/java/src/addressbook/Servlet.java
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import javax.servlet.GenericServlet;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
/**
|
||||
* A wrapper for addressbook to allow it to be started as a web application.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class Servlet extends GenericServlet {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||
*/
|
||||
public void service(ServletRequest request, ServletResponse response) {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
|
||||
*/
|
||||
public void init(ServletConfig config) {
|
||||
try {
|
||||
super.init(config);
|
||||
} catch (ServletException exp) {
|
||||
}
|
||||
String[] args = new String[1];
|
||||
args[0] = config.getInitParameter("home");
|
||||
DaemonThread thread = new DaemonThread(args);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
System.out.println("INFO: Starting Addressbook " + Daemon.VERSION);
|
||||
System.out.println("INFO: config root under " + args[0]);
|
||||
}
|
||||
|
||||
}
|
105
apps/addressbook/java/src/addressbook/Subscription.java
Normal file
105
apps/addressbook/java/src/addressbook/Subscription.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
/**
|
||||
* A subscription to a remote address book.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class Subscription {
|
||||
|
||||
private String location;
|
||||
|
||||
private String etag;
|
||||
|
||||
private String lastModified;
|
||||
|
||||
/**
|
||||
* Construct a Subscription pointing to the address book at location, that
|
||||
* was last read at the time represented by etag and lastModified.
|
||||
*
|
||||
* @param location
|
||||
* A String representing a url to a remote address book.
|
||||
* @param etag
|
||||
* The etag header that we recieved the last time we read this
|
||||
* subscription.
|
||||
* @param lastModified
|
||||
* the last-modified header we recieved the last time we read
|
||||
* this subscription.
|
||||
*/
|
||||
public Subscription(String location, String etag, String lastModified) {
|
||||
this.location = location;
|
||||
this.etag = etag;
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location this Subscription points at.
|
||||
*
|
||||
* @return A String representing a url to a remote address book.
|
||||
*/
|
||||
public String getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the etag header that we recieved the last time we read this
|
||||
* subscription.
|
||||
*
|
||||
* @return A String containing the etag header.
|
||||
*/
|
||||
public String getEtag() {
|
||||
return this.etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the etag header.
|
||||
*
|
||||
* @param etag
|
||||
* A String containing the etag header.
|
||||
*/
|
||||
public void setEtag(String etag) {
|
||||
this.etag = etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last-modified header that we recieved the last time we read
|
||||
* this subscription.
|
||||
*
|
||||
* @return A String containing the last-modified header.
|
||||
*/
|
||||
public String getLastModified() {
|
||||
return this.lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last-modified header.
|
||||
*
|
||||
* @param lastModified
|
||||
* A String containing the last-modified header.
|
||||
*/
|
||||
public void setLastModified(String lastModified) {
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
|
||||
* returns AddressBook objects, and not Subscription objects.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*/
|
||||
public class SubscriptionIterator implements Iterator {
|
||||
|
||||
private Iterator subIterator;
|
||||
|
||||
/**
|
||||
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
|
||||
*
|
||||
* @param subscriptions
|
||||
* List of Subscription objects that represent address books.
|
||||
*/
|
||||
public SubscriptionIterator(List subscriptions) {
|
||||
this.subIterator = subscriptions.iterator();
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
return subIterator.hasNext();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Iterator#next()
|
||||
*/
|
||||
public Object next() {
|
||||
Subscription sub = (Subscription) subIterator.next();
|
||||
return new AddressBook(sub);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
123
apps/addressbook/java/src/addressbook/SubscriptionList.java
Normal file
123
apps/addressbook/java/src/addressbook/SubscriptionList.java
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ragnarok
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package addressbook;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A list of Subscriptions loaded from a file.
|
||||
*
|
||||
* @author Ragnarok
|
||||
*
|
||||
*/
|
||||
public class SubscriptionList {
|
||||
|
||||
private List subscriptions;
|
||||
|
||||
private File etagsFile;
|
||||
|
||||
private File lastModifiedFile;
|
||||
|
||||
/**
|
||||
* Construct a SubscriptionList using the urls from locationsFile and, if
|
||||
* available, the etags and last-modified headers loaded from etagsFile and
|
||||
* lastModifiedFile.
|
||||
*
|
||||
* @param locationsFile
|
||||
* A file containing one url on each line.
|
||||
* @param etagsFile
|
||||
* A file containg the etag headers used for conditional GET. The
|
||||
* file is in the format "url=etag".
|
||||
* @param lastModifiedFile
|
||||
* A file containg the last-modified headers used for conditional
|
||||
* GET. The file is in the format "url=leastmodified".
|
||||
*/
|
||||
public SubscriptionList(File locationsFile, File etagsFile,
|
||||
File lastModifiedFile, List defaultSubs) {
|
||||
this.subscriptions = new LinkedList();
|
||||
this.etagsFile = etagsFile;
|
||||
this.lastModifiedFile = lastModifiedFile;
|
||||
List locations;
|
||||
Map etags;
|
||||
Map lastModified;
|
||||
String location;
|
||||
locations = ConfigParser.parseSubscriptions(locationsFile, defaultSubs);
|
||||
try {
|
||||
etags = ConfigParser.parse(etagsFile);
|
||||
} catch (IOException exp) {
|
||||
etags = new HashMap();
|
||||
}
|
||||
try {
|
||||
lastModified = ConfigParser.parse(lastModifiedFile);
|
||||
} catch (IOException exp) {
|
||||
lastModified = new HashMap();
|
||||
}
|
||||
Iterator iter = locations.iterator();
|
||||
while (iter.hasNext()) {
|
||||
location = (String) iter.next();
|
||||
subscriptions.add(new Subscription(location, (String) etags
|
||||
.get(location), (String) lastModified.get(location)));
|
||||
}
|
||||
|
||||
iter = this.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator over the AddressBooks represented by the Subscriptions
|
||||
* in this SubscriptionList.
|
||||
*
|
||||
* @return A SubscriptionIterator.
|
||||
*/
|
||||
public SubscriptionIterator iterator() {
|
||||
return new SubscriptionIterator(this.subscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the etag and last-modified headers for each Subscription to files.
|
||||
*/
|
||||
public void write() {
|
||||
Iterator iter = this.subscriptions.iterator();
|
||||
Subscription sub;
|
||||
Map etags = new HashMap();
|
||||
Map lastModified = new HashMap();
|
||||
while (iter.hasNext()) {
|
||||
sub = (Subscription) iter.next();
|
||||
if (sub.getEtag() != null) {
|
||||
etags.put(sub.getLocation(), sub.getEtag());
|
||||
}
|
||||
if (sub.getLastModified() != null) {
|
||||
lastModified.put(sub.getLocation(), sub.getLastModified());
|
||||
}
|
||||
}
|
||||
try {
|
||||
ConfigParser.write(etags, this.etagsFile);
|
||||
ConfigParser.write(lastModified, this.lastModifiedFile);
|
||||
} catch (IOException exp) {
|
||||
}
|
||||
}
|
||||
}
|
10
apps/addressbook/myhosts.txt
Normal file
10
apps/addressbook/myhosts.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# addressbook master address book. Addresses placed in this file take precidence
|
||||
# over those in the router address book and in remote address books. If changes
|
||||
# are made to this file, they will be reflected in the router address book and
|
||||
# published address book after the next update.
|
||||
#
|
||||
# Do not make changes directly to the router address book, as they could be lost
|
||||
# during an update.
|
||||
#
|
||||
# This file takes addresses in the hosts.txt format, i.e.
|
||||
# example.i2p=somereallylongbase64thingAAAA
|
7
apps/addressbook/subscriptions.txt
Normal file
7
apps/addressbook/subscriptions.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# Subscription list for addressbook
|
||||
#
|
||||
# Each entry is an absolute url to a file in hosts.txt format.
|
||||
# Since the list is checked in order, url's should be listed in order of trust.
|
||||
#
|
||||
http://dev.i2p/i2p/hosts.txt
|
||||
http://duck.i2p/hosts.txt
|
16
apps/addressbook/web.xml
Normal file
16
apps/addressbook/web.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<servlet>
|
||||
<servlet-name>addressbook</servlet-name>
|
||||
<servlet-class>addressbook.Servlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>home</param-name>
|
||||
<param-value>./addressbook</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
</web-app>
|
106
apps/fortuna/build.xml
Normal file
106
apps/fortuna/build.xml
Normal file
@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="fortuna">
|
||||
|
||||
<property name="cvs.base.dir" value="java/gnu-crypto" />
|
||||
<property name="cvs.etc.dir" value="${cvs.base.dir}/etc" />
|
||||
<property name="cvs.lib.dir" value="${cvs.base.dir}/lib" />
|
||||
<property name="cvs.object.dir" value="${cvs.base.dir}/classes" />
|
||||
<property name="cvs.base.crypto.object.dir" value="${cvs.object.dir}/gnu/crypto" />
|
||||
<property name="cvs.cipher.object.dir" value="${cvs.base.crypto.object.dir}/cipher" />
|
||||
<property name="cvs.hash.object.dir" value="${cvs.base.crypto.object.dir}/hash" />
|
||||
<property name="cvs.prng.object.dir" value="${cvs.base.crypto.object.dir}/prng" />
|
||||
|
||||
<patternset id="fortuna.files">
|
||||
<include name="${cvs.base.crypto.object.dir}/Registry.class"/>
|
||||
<include name="${cvs.prng.object.dir}/Fortuna*.class"/>
|
||||
<include name="${cvs.prng.object.dir}/BasePRNG.class"/>
|
||||
<include name="${cvs.prng.object.dir}/RandomEventListener.class"/>
|
||||
<include name="${cvs.prng.object.dir}/IRandom.class"/>
|
||||
<include name="${cvs.cipher.object.dir}/CipherFactory.class"/>
|
||||
<include name="${cvs.cipher.object.dir}/IBlockCipher.class"/>
|
||||
<include name="${cvs.hash.object.dir}/HashFactory.class"/>
|
||||
<include name="${cvs.hash.object.dir}/IMessageDigest.class"/>
|
||||
</patternset>
|
||||
|
||||
<target name="all" depends="build,jar"
|
||||
description="Create and test the custom Fortuna library" />
|
||||
|
||||
<target name="build" depends="-init,checkout"
|
||||
description="Build the source and tests">
|
||||
<ant dir="${cvs.base.dir}" target="jar" />
|
||||
</target>
|
||||
|
||||
<target name="builddep" />
|
||||
|
||||
<target name="checkout" depends="-init" unless="cvs.source.available"
|
||||
description="Check out GNU Crypto sources from CVS HEAD">
|
||||
<cvs cvsRoot=":ext:anoncvs@savannah.gnu.org:/cvsroot/gnu-crypto"
|
||||
cvsRsh="ssh"
|
||||
dest="java"
|
||||
package="gnu-crypto" />
|
||||
</target>
|
||||
|
||||
<target name="clean"
|
||||
description="Remove generated tests and object files">
|
||||
<ant dir="${cvs.base.dir}" target="clean" />
|
||||
</target>
|
||||
|
||||
<target name="cleandep" />
|
||||
|
||||
<target name="compile" />
|
||||
|
||||
<target name="distclean" depends="clean"
|
||||
description="Remove all generated files">
|
||||
<delete dir="build" />
|
||||
<delete dir="jartemp" />
|
||||
<!--
|
||||
Annoyingly the GNU Crypto distclean task called here doesn't clean
|
||||
*all* derived files from java/gnu-crypto/lib like it should.....
|
||||
-->
|
||||
<ant dir="${cvs.base.dir}" target="distclean" />
|
||||
<!--
|
||||
.....and so we mop up the rest ourselves.
|
||||
-->
|
||||
<delete dir="${cvs.lib.dir}" />
|
||||
</target>
|
||||
|
||||
<target name="-init">
|
||||
<available property="cvs.source.available" file="${cvs.base.dir}" />
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="build"
|
||||
description="Create the custom Fortuna jar library">
|
||||
<delete dir="build" />
|
||||
<delete dir="jartemp" />
|
||||
<mkdir dir="build" />
|
||||
<mkdir dir="jartemp/${cvs.object.dir}" />
|
||||
<copy todir="jartemp">
|
||||
<fileset dir=".">
|
||||
<patternset refid="fortuna.files" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<jar basedir="jartemp/${cvs.object.dir}" jarfile="build/fortuna.jar">
|
||||
<manifest>
|
||||
<section name="fortuna">
|
||||
<attribute name="Implementation-Title" value="I2P Custom GNU Crypto Fortuna Library" />
|
||||
<attribute name="Implementation-Version" value="CVS HEAD" />
|
||||
<attribute name="Implementation-Vendor" value="Free Software Foundation" />
|
||||
<attribute name="Implementation-Vendor-Id" value="FSF" />
|
||||
<attribute name="Implementation-URL" value="http://www.gnu.org/software/gnu-crypto" />
|
||||
</section>
|
||||
</manifest>
|
||||
</jar>
|
||||
<delete dir="jartemp" />
|
||||
</target>
|
||||
|
||||
<target name="test" depends="jar"
|
||||
description="Perform crypto tests on custom Fortuna jar library" />
|
||||
<!--
|
||||
Add this when Fortuna tests are added to GNU Crypto, else write some
|
||||
-->
|
||||
|
||||
<target name="update" depends="checkout"
|
||||
description="Update GNU Crypto sources to latest CVS HEAD">
|
||||
<cvs command="update -d" cvsRsh="ssh" dest="java/gnu-crypto" />
|
||||
</target>
|
||||
</project>
|
@ -39,12 +39,13 @@
|
||||
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-el.jar" />
|
||||
<pathelement location="../../jetty/jettylib/ant.jar" />
|
||||
<pathelement location="build/i2ptunnel.jar" />
|
||||
</classpath>
|
||||
<arg value="-d" />
|
||||
<arg value="../jsp/WEB-INF/classes" />
|
||||
<arg value="-v9" />
|
||||
<arg value="-p" />
|
||||
<arg value="net.i2p.i2ptunnel.jsp" />
|
||||
<arg value="-webinc" />
|
||||
@ -52,10 +53,12 @@
|
||||
<arg value="-webapp" />
|
||||
<arg value="../jsp/" />
|
||||
</java>
|
||||
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="*.java">
|
||||
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-el.jar" />
|
||||
<pathelement location="build/i2ptunnel.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
|
@ -183,7 +183,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
void addSession(I2PSession session) {
|
||||
if (session == null) return;
|
||||
synchronized (_sessions) {
|
||||
_sessions.add(session);
|
||||
if (!_sessions.contains(session))
|
||||
_sessions.add(session);
|
||||
}
|
||||
}
|
||||
void removeSession(I2PSession session) {
|
||||
@ -229,6 +230,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
runClientOptions(args, l);
|
||||
} else if ("server".equals(cmdname)) {
|
||||
runServer(args, l);
|
||||
} else if ("httpserver".equals(cmdname)) {
|
||||
runHttpServer(args, l);
|
||||
} else if ("textserver".equals(cmdname)) {
|
||||
runTextServer(args, l);
|
||||
} else if ("client".equals(cmdname)) {
|
||||
@ -281,10 +284,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("owndest yes|no");
|
||||
l.log("ping <args>");
|
||||
l.log("server <host> <port> <privkeyfile>");
|
||||
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
|
||||
l.log("textserver <host> <port> <privkey>");
|
||||
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
|
||||
l.log("gentextkeys");
|
||||
l.log("client <port> <pubkey>|file:<pubkeyfile>");
|
||||
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>");
|
||||
l.log("httpclient <port>");
|
||||
l.log("lookup <name>");
|
||||
l.log("quit");
|
||||
@ -370,6 +374,65 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the HTTP server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the specified file, replacing the HTTP headers
|
||||
* so that the Host: specified is the one spoofed. <p />
|
||||
*
|
||||
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
|
||||
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
|
||||
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
|
||||
*
|
||||
* @param args {hostname, portNumber, spoofedHost, privKeyFilename}
|
||||
* @param l logger to receive events and output
|
||||
*/
|
||||
public void runHttpServer(String args[], Logging l) {
|
||||
if (args.length == 4) {
|
||||
InetAddress serverHost = null;
|
||||
int portNum = -1;
|
||||
File privKeyFile = null;
|
||||
try {
|
||||
serverHost = InetAddress.getByName(args[0]);
|
||||
} catch (UnknownHostException uhe) {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
portNum = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
String spoofedHost = args[2];
|
||||
|
||||
privKeyFile = new File(args[3]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
serv.startRunning();
|
||||
addtask(serv);
|
||||
notifyEvent("serverTaskId", new Integer(serv.getId()));
|
||||
return;
|
||||
} else {
|
||||
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
|
||||
l.log(" creates an HTTP server that sends all incoming data\n"
|
||||
+ " of its destination to host:port., filtering the HTTP\n"
|
||||
+ " headers so it looks like the request is to the spoofed host.");
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the given base64 stream. <p />
|
||||
@ -449,9 +512,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
notifyEvent("clientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("client <port> <pubkey>|file:<pubkeyfile>");
|
||||
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>");
|
||||
l.log(" creates a client that forwards port to the pubkey.\n"
|
||||
+ " use 0 as port to get a free port assigned.");
|
||||
+ " use 0 as port to get a free port assigned. If you specify\n"
|
||||
+ " a comma delimited list of pubkeys, it will rotate among them\n"
|
||||
+ " randomlyl");
|
||||
notifyEvent("clientTaskId", new Integer(-1));
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,11 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
@ -15,15 +19,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
|
||||
private static final Log _log = new Log(I2PTunnelClient.class);
|
||||
|
||||
protected Destination dest;
|
||||
/** list of Destination objects that we point at */
|
||||
protected List dests;
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
/**
|
||||
* @param destinations comma delimited list of peers we target
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelClient(int localPort, String destination, Logging l,
|
||||
public I2PTunnelClient(int localPort, String destinations, Logging l,
|
||||
boolean ownDest, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
|
||||
@ -33,19 +39,28 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null) {
|
||||
l.log("Could not resolve " + destination + ".");
|
||||
return;
|
||||
StringTokenizer tok = new StringTokenizer(destinations, ",");
|
||||
dests = new ArrayList(1);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String destination = tok.nextToken();
|
||||
try {
|
||||
Destination dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null)
|
||||
l.log("Could not resolve " + destination);
|
||||
else
|
||||
dests.add(dest);
|
||||
} catch (DataFormatException dfe) {
|
||||
l.log("Bad format parsing \"" + destination + "\"");
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
l.log("Bad format in destination \"" + destination + "\".");
|
||||
}
|
||||
|
||||
if (dests.size() <= 0) {
|
||||
l.log("No target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> " + destination);
|
||||
setName(getLocalPort() + " -> " + destinations);
|
||||
|
||||
startRunning();
|
||||
|
||||
@ -56,14 +71,34 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
public long getReadTimeout() { return readTimeout; }
|
||||
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
Destination dest = pickDestination();
|
||||
I2PSocket i2ps = null;
|
||||
try {
|
||||
I2PSocket i2ps = createI2PSocket(dest);
|
||||
i2ps = createI2PSocket(dest);
|
||||
i2ps.setReadTimeout(readTimeout);
|
||||
new I2PTunnelRunner(s, i2ps, sockLock, null);
|
||||
new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets);
|
||||
} catch (Exception ex) {
|
||||
_log.info("Error connecting", ex);
|
||||
l.log(ex.getMessage());
|
||||
closeSocket(s);
|
||||
if (i2ps != null) {
|
||||
synchronized (sockLock) {
|
||||
mySockets.remove(sockLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Destination pickDestination() {
|
||||
int size = dests.size();
|
||||
if (size <= 0) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("No client targets?!");
|
||||
return null;
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return (Destination)dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
return (Destination)dests.get(index);
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,10 @@ import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
@ -26,6 +28,7 @@ import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
|
||||
@ -33,13 +36,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
private static final Log _log = new Log(I2PTunnelClientBase.class);
|
||||
protected Logging l;
|
||||
|
||||
private static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
|
||||
private static volatile long __clientId = 0;
|
||||
protected long _clientId;
|
||||
protected Object sockLock = new Object(); // Guards sockMgr and mySockets
|
||||
private I2PSocketManager sockMgr;
|
||||
private List mySockets = new ArrayList();
|
||||
protected I2PSocketManager sockMgr;
|
||||
protected List mySockets = new ArrayList();
|
||||
|
||||
protected Destination dest = null;
|
||||
private int localPort;
|
||||
@ -57,6 +60,32 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
private String handlerName;
|
||||
|
||||
private Object conLock = new Object();
|
||||
|
||||
/** List of Socket for those accept()ed but not yet started up */
|
||||
private List _waitingSockets;
|
||||
/** How many connections will we allow to be in the process of being built at once? */
|
||||
private int _numConnectionBuilders;
|
||||
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
|
||||
private int _maxWaitTime;
|
||||
|
||||
/**
|
||||
* How many concurrent connections this I2PTunnel instance will allow to be
|
||||
* in the process of connecting (or if less than 1, there is no limit)?
|
||||
*/
|
||||
public static final String PROP_NUM_CONNECTION_BUILDERS = "i2ptunnel.numConnectionBuilders";
|
||||
/**
|
||||
* How long will we let a socket wait after being accept()ed without getting
|
||||
* pumped through a connection builder (in milliseconds). If this time is
|
||||
* reached, the socket is unceremoniously closed and discarded. If the max
|
||||
* wait time is less than 1, there is no limit.
|
||||
*
|
||||
*/
|
||||
public static final String PROP_MAX_WAIT_TIME = "i2ptunnel.maxWaitTime";
|
||||
|
||||
private static final int DEFAULT_NUM_CONNECTION_BUILDERS = 5;
|
||||
private static final int DEFAULT_MAX_WAIT_TIME = 30*1000;
|
||||
|
||||
//public I2PTunnelClientBase(int localPort, boolean ownDest,
|
||||
// Logging l) {
|
||||
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
|
||||
@ -96,7 +125,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
t.start();
|
||||
open = true;
|
||||
synchronized (this) {
|
||||
while (!listenerReady) {
|
||||
while (!listenerReady && open) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
@ -105,14 +134,47 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
}
|
||||
|
||||
configurePool(tunnel);
|
||||
|
||||
if (open && listenerReady) {
|
||||
l.log("Ready! Port " + getLocalPort());
|
||||
notifyEvent("openBaseClientResult", "ok");
|
||||
} else {
|
||||
l.log("Error!");
|
||||
l.log("Error listening - please see the logs!");
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* build and configure the pool handling accept()ed but not yet
|
||||
* established connections
|
||||
*
|
||||
*/
|
||||
private void configurePool(I2PTunnel tunnel) {
|
||||
_waitingSockets = new ArrayList(4);
|
||||
|
||||
Properties opts = tunnel.getClientOptions();
|
||||
String maxWait = opts.getProperty(PROP_MAX_WAIT_TIME, DEFAULT_MAX_WAIT_TIME+"");
|
||||
try {
|
||||
_maxWaitTime = Integer.parseInt(maxWait);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_maxWaitTime = DEFAULT_MAX_WAIT_TIME;
|
||||
}
|
||||
|
||||
String numBuild = opts.getProperty(PROP_NUM_CONNECTION_BUILDERS, DEFAULT_NUM_CONNECTION_BUILDERS+"");
|
||||
try {
|
||||
_numConnectionBuilders = Integer.parseInt(numBuild);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_numConnectionBuilders = DEFAULT_NUM_CONNECTION_BUILDERS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _numConnectionBuilders; i++) {
|
||||
String name = "ClientBuilder" + _clientId + '.' + i;
|
||||
I2PThread b = new I2PThread(new TunnelConnectionBuilder(), name);
|
||||
b.setDaemon(true);
|
||||
b.start();
|
||||
}
|
||||
}
|
||||
|
||||
private static I2PSocketManager socketManager;
|
||||
|
||||
@ -181,9 +243,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
private I2PSocketOptions getDefaultOptions() {
|
||||
I2PSocketOptions opts = new I2PSocketOptions();
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
@ -227,7 +304,14 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
public final void run() {
|
||||
try {
|
||||
InetAddress addr = getListenHost(l);
|
||||
if (addr == null) return;
|
||||
if (addr == null) {
|
||||
open = false;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
|
||||
return;
|
||||
}
|
||||
ss = new ServerSocket(localPort, 0, addr);
|
||||
|
||||
// If a free port was requested, find out what we got
|
||||
@ -258,8 +342,18 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
manageConnection(s);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error listening for connections", ex);
|
||||
_log.error("Error listening for connections on " + localPort, ex);
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
synchronized (sockLock) {
|
||||
mySockets.clear();
|
||||
}
|
||||
open = false;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
synchronized (_waitingSockets) {
|
||||
_waitingSockets.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,7 +363,52 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* @param s Socket to take care of
|
||||
*/
|
||||
protected void manageConnection(Socket s) {
|
||||
new ClientConnectionRunner(s, handlerName);
|
||||
if (s == null) return;
|
||||
if (_numConnectionBuilders <= 0) {
|
||||
new I2PThread(new BlockingRunner(s), "Clinet run").start();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_maxWaitTime > 0)
|
||||
SimpleTimer.getInstance().addEvent(new CloseEvent(s), _maxWaitTime);
|
||||
|
||||
synchronized (_waitingSockets) {
|
||||
_waitingSockets.add(s);
|
||||
_waitingSockets.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking runner, used during the connection establishment whenever we
|
||||
* are not using the queued builders.
|
||||
*
|
||||
*/
|
||||
private class BlockingRunner implements Runnable {
|
||||
private Socket _s;
|
||||
public BlockingRunner(Socket s) { _s = s; }
|
||||
public void run() {
|
||||
clientConnectionRun(_s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and close the socket from the waiting list, if it is still there.
|
||||
*
|
||||
*/
|
||||
private class CloseEvent implements SimpleTimer.TimedEvent {
|
||||
private Socket _s;
|
||||
public CloseEvent(Socket s) { _s = s; }
|
||||
public void timeReached() {
|
||||
boolean stillWaiting = false;
|
||||
synchronized (_waitingSockets) {
|
||||
stillWaiting = _waitingSockets.remove(_s);
|
||||
}
|
||||
if (stillWaiting) {
|
||||
try { _s.close(); } catch (IOException ioe) {}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Closed a waiting socket because of backlog");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean close(boolean forced) {
|
||||
@ -301,8 +440,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
l.log("Client closed.");
|
||||
open = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void closeSocket(Socket s) {
|
||||
@ -312,20 +453,30 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
_log.error("Could not close socket", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static volatile long __runnerId = 0;
|
||||
|
||||
public class ClientConnectionRunner extends I2PThread {
|
||||
private Socket s;
|
||||
|
||||
public ClientConnectionRunner(Socket s, String name) {
|
||||
this.s = s;
|
||||
setName(name + '.' + (++__runnerId));
|
||||
start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
clientConnectionRun(s);
|
||||
/**
|
||||
* Pool runner pulling sockets off the waiting list and pushing them
|
||||
* through clientConnectionRun. This dies when the I2PTunnel instance
|
||||
* is closed.
|
||||
*
|
||||
*/
|
||||
private class TunnelConnectionBuilder implements Runnable {
|
||||
public void run() {
|
||||
Socket s = null;
|
||||
while (open) {
|
||||
try {
|
||||
synchronized (_waitingSockets) {
|
||||
if (_waitingSockets.size() <= 0)
|
||||
_waitingSockets.wait();
|
||||
else
|
||||
s = (Socket)_waitingSockets.remove(0);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
|
||||
if (s != null)
|
||||
clientConnectionRun(s);
|
||||
s = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,12 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Clock;
|
||||
@ -142,14 +144,48 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
//if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
// defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
private static long __requestId = 0;
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
OutputStream out = null;
|
||||
String targetRequest = null;
|
||||
boolean usingWWWProxy = false;
|
||||
String currentProxy = null;
|
||||
InactivityTimeoutThread timeoutThread = null;
|
||||
long requestId = ++__requestId;
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
@ -175,7 +211,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
String request = line.substring(pos + 1);
|
||||
if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
|
||||
request = "http://i2p" + request;
|
||||
} else if (request.startsWith("/eepproxy/")) {
|
||||
// /eepproxy/foo.i2p/bar/baz.html HTTP/1.0
|
||||
String subRequest = request.substring("/eepproxy/".length());
|
||||
int protopos = subRequest.indexOf(" ");
|
||||
String uri = subRequest.substring(0, protopos);
|
||||
if (uri.indexOf("/") == -1) {
|
||||
uri = uri + "/";
|
||||
}
|
||||
// "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0"
|
||||
request = "http://" + uri + subRequest.substring(protopos);
|
||||
}
|
||||
|
||||
pos = request.indexOf("//");
|
||||
if (pos == -1) {
|
||||
method = null;
|
||||
@ -295,7 +342,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
} else if (line.startsWith("User-Agent: ")) {
|
||||
line = "User-Agent: MYOB/6.66 (AN/ON)";
|
||||
// always stripped, added back at the end
|
||||
line = null;
|
||||
continue;
|
||||
} else if (line.startsWith("Accept")) {
|
||||
// strip the accept-blah headers, as they vary dramatically from
|
||||
// browser to browser
|
||||
line = null;
|
||||
continue;
|
||||
} else if (line.startsWith("Referer: ")) {
|
||||
// Shouldn't we be more specific, like accepting in-site referers ?
|
||||
//line = "Referer: i2p";
|
||||
@ -313,6 +367,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
|
||||
newRequest.append("Connection: close\r\n\r\n");
|
||||
break;
|
||||
} else {
|
||||
@ -354,25 +409,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
String remoteID;
|
||||
I2PSocket i2ps = createI2PSocket(dest);
|
||||
|
||||
Properties opts = new Properties();
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
|
||||
// dont want to hard link to here
|
||||
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data);
|
||||
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, currentProxy, s, requestId);
|
||||
timeoutThread.start();
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
} catch (SocketException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (IOException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (I2PException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
@ -380,91 +437,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private static final long INACTIVITY_TIMEOUT = 120 * 1000;
|
||||
private static volatile long __timeoutId = 0;
|
||||
|
||||
private class InactivityTimeoutThread extends I2PThread {
|
||||
|
||||
private Socket s;
|
||||
private I2PTunnelRunner _runner;
|
||||
private OutputStream _out;
|
||||
private String _targetRequest;
|
||||
private boolean _useWWWProxy;
|
||||
private String _currentProxy;
|
||||
private long _requestId;
|
||||
private boolean _disabled;
|
||||
private Object _disableLock = new Object();
|
||||
|
||||
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
|
||||
boolean useWWWProxy, String currentProxy, Socket s, long requestId) {
|
||||
this.s = s;
|
||||
_runner = runner;
|
||||
_out = out;
|
||||
_targetRequest = targetRequest;
|
||||
_useWWWProxy = useWWWProxy;
|
||||
_currentProxy = currentProxy;
|
||||
_disabled = false;
|
||||
_requestId = requestId;
|
||||
long timeoutId = ++__timeoutId;
|
||||
setName("InactivityThread " + getPrefix(requestId) + timeoutId);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
_disabled = true;
|
||||
synchronized (_disableLock) {
|
||||
_disableLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!_disabled) {
|
||||
if (_runner.isFinished()) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(_requestId) + "HTTP client request completed prior to timeout");
|
||||
return;
|
||||
}
|
||||
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(_requestId) + "HTTP client request timed out (lastActivity: "
|
||||
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
|
||||
+ new Date(_runner.getStartedOn()) + ")");
|
||||
timeout();
|
||||
return;
|
||||
} else {
|
||||
// runner hasn't been going to long enough
|
||||
}
|
||||
} else {
|
||||
// there has been activity in the period
|
||||
}
|
||||
synchronized (_disableLock) {
|
||||
try {
|
||||
_disableLock.wait(INACTIVITY_TIMEOUT);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void timeout() {
|
||||
_log.info(getPrefix(_requestId) + "Inactivity timeout reached");
|
||||
l.log("Inactivity timeout reached");
|
||||
if (_out != null) {
|
||||
try {
|
||||
if (_runner.getLastActivityOn() > 0) {
|
||||
// some data has been sent, so don't 404 it
|
||||
} else {
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, _currentProxy);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.warn(getPrefix(_requestId) + "Error writing out the 'timeout' message", ioe);
|
||||
}
|
||||
} else {
|
||||
_log.warn(getPrefix(_requestId) + "Client disconnected before we could say we timed out");
|
||||
}
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
|
||||
private final static String getHostName(String host) {
|
||||
if (host == null) return null;
|
||||
try {
|
||||
@ -476,6 +448,30 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private class OnTimeout implements Runnable {
|
||||
private Socket _socket;
|
||||
private OutputStream _out;
|
||||
private String _target;
|
||||
private boolean _usingProxy;
|
||||
private String _wwwProxy;
|
||||
private long _requestId;
|
||||
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
|
||||
_socket = s;
|
||||
_out = out;
|
||||
_target = target;
|
||||
_usingProxy = usingProxy;
|
||||
_wwwProxy = wwwProxy;
|
||||
_requestId = id;
|
||||
}
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Timeout occured requesting " + _target);
|
||||
handleHTTPClientException(new RuntimeException("Timeout"), _out,
|
||||
_target, _usingProxy, _wwwProxy, _requestId);
|
||||
closeSocket(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
|
||||
boolean usingWWWProxy, String wwwProxy) throws IOException {
|
||||
if (out != null) {
|
||||
|
@ -0,0 +1,162 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple extension to the I2PTunnelServer that filters the HTTP
|
||||
* headers sent from the client to the server, replacing the Host
|
||||
* header with whatever this instance has been configured with.
|
||||
*
|
||||
*/
|
||||
public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
private final static Log _log = new Log(I2PTunnelHTTPServer.class);
|
||||
/** what Host: should we seem to be to the webserver? */
|
||||
private String _spoofHost;
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
I2PServerSocket i2pss = sockMgr.getServerSocket();
|
||||
while (true) {
|
||||
I2PSocket i2ps = i2pss.accept();
|
||||
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
|
||||
I2PThread t = new I2PThread(new Handler(i2ps));
|
||||
t.start();
|
||||
}
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async handler to keep .accept() from blocking too long.
|
||||
* todo: replace with a thread pool so we dont get overrun by threads if/when
|
||||
* receiving a lot of connection requests concurrently.
|
||||
*
|
||||
*/
|
||||
private class Handler implements Runnable {
|
||||
private I2PSocket _handleSocket;
|
||||
public Handler(I2PSocket socket) {
|
||||
_handleSocket = socket;
|
||||
}
|
||||
public void run() {
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
//threads.
|
||||
try {
|
||||
_handleSocket.setReadTimeout(readTimeout);
|
||||
String modifiedHeader = getModifiedHeader();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Modified header: [" + modifiedHeader + "]");
|
||||
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null, modifiedHeader.getBytes(), null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
_handleSocket.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error while closing the received i2p con", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
|
||||
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
|
||||
long timeToHandle = afterHandle - afterAccept;
|
||||
if (timeToHandle > 1000)
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: "
|
||||
+ (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
private String getModifiedHeader() throws IOException {
|
||||
InputStream in = _handleSocket.getInputStream();
|
||||
|
||||
StringBuffer command = new StringBuffer(128);
|
||||
Properties headers = readHeaders(in, command);
|
||||
headers.setProperty("Host", _spoofHost);
|
||||
headers.setProperty("Connection", "close");
|
||||
return formatHeaders(headers, command);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatHeaders(Properties headers, StringBuffer command) {
|
||||
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
|
||||
buf.append(command.toString()).append('\n');
|
||||
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String val = headers.getProperty(name);
|
||||
buf.append(name).append(": ").append(val).append('\n');
|
||||
}
|
||||
buf.append('\n');
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Properties readHeaders(InputStream in, StringBuffer command) throws IOException {
|
||||
Properties headers = new Properties();
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
|
||||
boolean ok = DataHelper.readLine(in, command);
|
||||
if (!ok) throw new IOException("EOF reached while reading the HTTP command [" + command.toString() + "]");
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read the http command [" + command.toString() + "]");
|
||||
|
||||
while (true) {
|
||||
buf.setLength(0);
|
||||
ok = DataHelper.readLine(in, buf);
|
||||
if (!ok) throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
|
||||
if ( (buf.length() <= 1) && ( (buf.charAt(0) == '\n') || (buf.charAt(0) == '\r') ) ) {
|
||||
// end of headers reached
|
||||
return headers;
|
||||
} else {
|
||||
int split = buf.indexOf(": ");
|
||||
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
|
||||
String name = buf.substring(0, split);
|
||||
String value = buf.substring(split+2); // ": "
|
||||
headers.setProperty(name, value);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read the header [" + name + "] = [" + value + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,11 @@ import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
@ -38,23 +40,43 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
Object slock, finishLock = new Object();
|
||||
boolean finished = false;
|
||||
HashMap ostreams, sockets;
|
||||
I2PSession session;
|
||||
byte[] initialData;
|
||||
byte[] initialI2PData;
|
||||
byte[] initialSocketData;
|
||||
/** when the last data was sent/received (or -1 if never) */
|
||||
private long lastActivityOn;
|
||||
/** when the runner started up */
|
||||
private long startedOn;
|
||||
private List sockList;
|
||||
/** if we die before receiving any data, run this job */
|
||||
private Runnable onTimeout;
|
||||
private long totalSent;
|
||||
private long totalReceived;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData) {
|
||||
private volatile long __forwarderId;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList) {
|
||||
this(s, i2ps, slock, initialI2PData, null, sockList, null);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList) {
|
||||
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
|
||||
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList, Runnable onTimeout) {
|
||||
this.sockList = sockList;
|
||||
this.s = s;
|
||||
this.i2ps = i2ps;
|
||||
this.slock = slock;
|
||||
this.initialData = initialData;
|
||||
this.initialI2PData = initialI2PData;
|
||||
this.initialSocketData = initialSocketData;
|
||||
this.onTimeout = onTimeout;
|
||||
lastActivityOn = -1;
|
||||
startedOn = Clock.getInstance().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("I2PTunnelRunner started");
|
||||
_runnerId = ++__runnerId;
|
||||
__forwarderId = i2ps.hashCode();
|
||||
setName("I2PTunnelRunner " + _runnerId);
|
||||
start();
|
||||
}
|
||||
@ -93,48 +115,77 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
public void run() {
|
||||
try {
|
||||
InputStream in = s.getInputStream();
|
||||
OutputStream out = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
i2ps.setSocketErrorListener(this);
|
||||
InputStream i2pin = i2ps.getInputStream();
|
||||
OutputStream i2pout = new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialData != null) {
|
||||
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialI2PData != null) {
|
||||
synchronized (slock) {
|
||||
i2pout.write(initialData);
|
||||
i2pout.flush();
|
||||
i2pout.write(initialI2PData);
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
Thread t1 = new StreamForwarder(in, i2pout);
|
||||
Thread t2 = new StreamForwarder(i2pin, out);
|
||||
if (initialSocketData != null) {
|
||||
out.write(initialSocketData);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Initial data " + (initialI2PData != null ? initialI2PData.length : 0)
|
||||
+ " written to I2P, " + (initialSocketData != null ? initialSocketData.length : 0)
|
||||
+ " written to the socket, starting forwarders");
|
||||
Thread t1 = new StreamForwarder(in, i2pout, true);
|
||||
Thread t2 = new StreamForwarder(i2pin, out, false);
|
||||
synchronized (finishLock) {
|
||||
while (!finished) {
|
||||
finishLock.wait();
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("At least one forwarder completed, closing and joining");
|
||||
|
||||
// this task is useful for the httpclient
|
||||
if (onTimeout != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
|
||||
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
||||
if ( (totalSent <= 0) && (totalReceived <= 0) )
|
||||
onTimeout.run();
|
||||
}
|
||||
|
||||
// now one connection is dead - kill the other as well.
|
||||
s.close();
|
||||
s = null;
|
||||
i2ps.close();
|
||||
i2ps = null;
|
||||
t1.join();
|
||||
t2.join();
|
||||
t1.join(30*1000);
|
||||
t2.join(30*1000);
|
||||
} catch (InterruptedException ex) {
|
||||
_log.error("Interrupted", ex);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Interrupted", ex);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
_log.debug("Error forwarding", ex);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Error forwarding", ex);
|
||||
} catch (Exception e) {
|
||||
_log.error("Internal error", e);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Internal error", e);
|
||||
} finally {
|
||||
removeRef();
|
||||
try {
|
||||
if (s != null) s.close();
|
||||
if (i2ps != null) i2ps.close();
|
||||
if (s != null)
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
_log.error("Could not close socket", ex);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close java socket", ex);
|
||||
}
|
||||
if (i2ps != null) {
|
||||
try {
|
||||
i2ps.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
}
|
||||
i2ps.setSocketErrorListener(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void errorOccurred() {
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
@ -142,69 +193,116 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
}
|
||||
|
||||
private volatile long __forwarderId = 0;
|
||||
private void removeRef() {
|
||||
if (sockList != null) {
|
||||
synchronized (slock) {
|
||||
boolean removed = sockList.remove(i2ps);
|
||||
//System.out.println("Removal of i2psocket " + i2ps + " successful? "
|
||||
// + removed + " remaining: " + sockList.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StreamForwarder extends I2PThread {
|
||||
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
String direction;
|
||||
private boolean _toI2P;
|
||||
private ByteCache _cache;
|
||||
|
||||
private StreamForwarder(InputStream in, OutputStream out) {
|
||||
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
_toI2P = toI2P;
|
||||
direction = (toI2P ? "toI2P" : "fromI2P");
|
||||
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
|
||||
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
|
||||
start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
byte[] buffer = new byte[NETWORK_BUFFER_SIZE];
|
||||
String from = i2ps.getThisDestination().calculateHash().toBase64().substring(0,6);
|
||||
String to = i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(direction + ": Forwarding between "
|
||||
+ from + " and " + to);
|
||||
}
|
||||
|
||||
ByteArray ba = _cache.acquire();
|
||||
byte[] buffer = ba.getData(); // new byte[NETWORK_BUFFER_SIZE];
|
||||
try {
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
if (_toI2P)
|
||||
totalSent += len;
|
||||
else
|
||||
totalReceived += len;
|
||||
|
||||
if (len > 0) updateActivity();
|
||||
|
||||
if (in.available() == 0) {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Flushing after sending " + len + " bytes through");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(direction + ": " + len + " bytes flushed through to "
|
||||
+ i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6));
|
||||
try {
|
||||
Thread.sleep(I2PTunnel.PACKET_DELAY);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (in.available() == 0) {
|
||||
out.flush(); // make sure the data get though
|
||||
|
||||
if (in.available() <= 0)
|
||||
out.flush(); // make sure the data get though
|
||||
}
|
||||
}
|
||||
//out.flush(); // close() flushes
|
||||
} catch (SocketException ex) {
|
||||
// this *will* occur when the other threads closes the socket
|
||||
synchronized (finishLock) {
|
||||
if (!finished) {
|
||||
_log.debug("Socket closed - error reading and writing",
|
||||
ex);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(direction + ": Socket closed - error reading and writing",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException ex) {
|
||||
_log.warn("Closing connection due to timeout (error: \""
|
||||
+ ex.getMessage() + "\")");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Closing connection due to timeout (error: \""
|
||||
+ ex.getMessage() + "\")");
|
||||
} catch (IOException ex) {
|
||||
if (!finished)
|
||||
_log.error("Error forwarding", ex);
|
||||
if (!finished) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error forwarding", ex);
|
||||
}
|
||||
//else
|
||||
// _log.warn("You may ignore this", ex);
|
||||
} finally {
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info(direction + ": done forwarding between "
|
||||
+ from + " and " + to);
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing streams", ex);
|
||||
_log.warn(direction + ": Error closing input stream", ex);
|
||||
}
|
||||
try {
|
||||
out.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error flushing to close", ioe);
|
||||
}
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
finishLock.notifyAll();
|
||||
// the main thread will close sockets etc. now
|
||||
}
|
||||
_cache.release(ba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,19 +31,20 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
private final static Log _log = new Log(I2PTunnelServer.class);
|
||||
|
||||
private I2PSocketManager sockMgr;
|
||||
private I2PServerSocket i2pss;
|
||||
protected I2PSocketManager sockMgr;
|
||||
protected I2PServerSocket i2pss;
|
||||
|
||||
private Object lock = new Object(), slock = new Object();
|
||||
private Object lock = new Object();
|
||||
protected Object slock = new Object();
|
||||
|
||||
private InetAddress remoteHost;
|
||||
private int remotePort;
|
||||
protected InetAddress remoteHost;
|
||||
protected int remotePort;
|
||||
|
||||
private Logging l;
|
||||
|
||||
private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
|
||||
/** default timeout to 3 minutes - override if desired */
|
||||
private long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host + ":" + port + " <- " + privData, notifyThis, tunnel);
|
||||
@ -179,7 +180,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
_handleSocket.setReadTimeout(readTimeout);
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null);
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
_handleSocket.close();
|
||||
|
@ -16,6 +16,7 @@ import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ public class TunnelController implements Logging {
|
||||
private List _messages;
|
||||
private List _sessions;
|
||||
private boolean _running;
|
||||
private boolean _starting;
|
||||
|
||||
/**
|
||||
* Create a new controller for a tunnel out of the specific config options.
|
||||
@ -56,10 +58,9 @@ public class TunnelController implements Logging {
|
||||
setConfig(config, prefix);
|
||||
_messages = new ArrayList(4);
|
||||
_running = false;
|
||||
if (createKey && ("server".equals(getType())) )
|
||||
if (createKey && ("server".equals(getType()) || "httpserver".equals(getType())) )
|
||||
createPrivateKey();
|
||||
if (getStartOnLoad())
|
||||
startTunnel();
|
||||
_starting = getStartOnLoad();
|
||||
}
|
||||
|
||||
private void createPrivateKey() {
|
||||
@ -99,17 +100,25 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
}
|
||||
|
||||
public void startTunnelBackground() {
|
||||
if (_running) return;
|
||||
_starting = true;
|
||||
new I2PThread(new Runnable() { public void run() { startTunnel(); } }).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the tunnel (if it isn't already running)
|
||||
*
|
||||
*/
|
||||
public void startTunnel() {
|
||||
_starting = true;
|
||||
try {
|
||||
doStartTunnel();
|
||||
} catch (Exception e) {
|
||||
_log.error("Error starting up the tunnel", e);
|
||||
log("Error starting up the tunnel - " + e.getMessage());
|
||||
}
|
||||
_starting = false;
|
||||
}
|
||||
private void doStartTunnel() {
|
||||
if (_running) {
|
||||
@ -130,6 +139,8 @@ public class TunnelController implements Logging {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
startServer();
|
||||
} else if ("httpserver".equals(type)) {
|
||||
startHttpServer();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Cannot start tunnel - unknown type [" + type + "]");
|
||||
@ -204,6 +215,18 @@ public class TunnelController implements Logging {
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startHttpServer() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
String spoofedHost = getSpoofedHost();
|
||||
String privKeyFile = getPrivKeyFile();
|
||||
_tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this);
|
||||
acquire();
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void setListenOn() {
|
||||
String listenOn = getListenOnInterface();
|
||||
if ( (listenOn != null) && (listenOn.length() > 0) ) {
|
||||
@ -231,6 +254,9 @@ public class TunnelController implements Logging {
|
||||
String host = getI2CPHost();
|
||||
if ( (host != null) && (host.length() > 0) )
|
||||
_tunnel.host = host;
|
||||
// woohah, special casing for people with ipv6/etc
|
||||
if ("localhost".equals(_tunnel.host))
|
||||
_tunnel.host = "127.0.0.1";
|
||||
String port = getI2CPPort();
|
||||
if ( (port != null) && (port.length() > 0) )
|
||||
_tunnel.port = port;
|
||||
@ -292,6 +318,7 @@ public class TunnelController implements Logging {
|
||||
public String getListenOnInterface() { return _config.getProperty("interface"); }
|
||||
public String getTargetHost() { return _config.getProperty("targetHost"); }
|
||||
public String getTargetPort() { return _config.getProperty("targetPort"); }
|
||||
public String getSpoofedHost() { return _config.getProperty("spoofedHost"); }
|
||||
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
|
||||
public String getListenPort() { return _config.getProperty("listenPort"); }
|
||||
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
|
||||
@ -299,6 +326,7 @@ public class TunnelController implements Logging {
|
||||
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
|
||||
|
||||
public boolean getIsRunning() { return _running; }
|
||||
public boolean getIsStarting() { return _starting; }
|
||||
|
||||
public void getSummary(StringBuffer buf) {
|
||||
String type = getType();
|
||||
@ -308,6 +336,8 @@ public class TunnelController implements Logging {
|
||||
getClientSummary(buf);
|
||||
else if ("server".equals(type))
|
||||
getServerSummary(buf);
|
||||
else if ("httpserver".equals(type))
|
||||
getHttpServerSummary(buf);
|
||||
else
|
||||
buf.append("Unknown type ").append(type);
|
||||
}
|
||||
@ -361,6 +391,18 @@ public class TunnelController implements Logging {
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getHttpServerSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Server tunnel pointing at port ").append(getTargetPort());
|
||||
buf.append(" on ").append(getTargetHost());
|
||||
buf.append(" for the site ").append(getSpoofedHost());
|
||||
buf.append("<br />\n");
|
||||
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getOptionSummary(StringBuffer buf) {
|
||||
String opts = getClientOptions();
|
||||
if ( (opts != null) && (opts.length() > 0) )
|
||||
@ -372,7 +414,7 @@ public class TunnelController implements Logging {
|
||||
Destination dest = session.getMyDestination();
|
||||
if (dest != null) {
|
||||
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
|
||||
if ("server".equals(getType())) {
|
||||
if ( ("server".equals(getType())) || ("httpserver".equals(getType())) ) {
|
||||
buf.append("Full destination: ");
|
||||
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
|
||||
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
|
||||
|
@ -8,6 +8,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -20,6 +21,8 @@ import java.util.TreeMap;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -43,28 +46,34 @@ public class TunnelControllerGroup {
|
||||
*/
|
||||
private Map _sessions;
|
||||
|
||||
public static TunnelControllerGroup getInstance() { return _instance; }
|
||||
public static TunnelControllerGroup getInstance() {
|
||||
synchronized (TunnelControllerGroup.class) {
|
||||
if (_instance == null)
|
||||
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelControllerGroup(String configFile) {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
|
||||
_instance = this;
|
||||
_controllers = new ArrayList();
|
||||
_controllers = Collections.synchronizedList(new ArrayList());
|
||||
_configFile = configFile;
|
||||
_sessions = new HashMap(4);
|
||||
loadControllers(_configFile);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
if ( (args == null) || (args.length <= 0) ) {
|
||||
new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
} else if (args.length == 1) {
|
||||
if (DEFAULT_CONFIG_FILE.equals(args[0]))
|
||||
new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
else
|
||||
new TunnelControllerGroup(args[0]);
|
||||
} else {
|
||||
System.err.println("Usage: TunnelControllerGroup [filename]");
|
||||
return;
|
||||
synchronized (TunnelControllerGroup.class) {
|
||||
if (_instance != null) return; // already loaded through the web
|
||||
|
||||
if ( (args == null) || (args.length <= 0) ) {
|
||||
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
} else if (args.length == 1) {
|
||||
_instance = new TunnelControllerGroup(args[0]);
|
||||
} else {
|
||||
System.err.println("Usage: TunnelControllerGroup [filename]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,10 +98,24 @@ public class TunnelControllerGroup {
|
||||
_controllers.add(controller);
|
||||
i++;
|
||||
}
|
||||
I2PThread startupThread = new I2PThread(new StartControllers(), "Startup tunnels");
|
||||
startupThread.start();
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(i + " controllers loaded from " + configFile);
|
||||
}
|
||||
|
||||
private class StartControllers implements Runnable {
|
||||
public void run() {
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
if (controller.getStartOnLoad())
|
||||
controller.startTunnel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void reloadControllers() {
|
||||
unloadControllers();
|
||||
loadControllers(_configFile);
|
||||
@ -143,7 +166,6 @@ public class TunnelControllerGroup {
|
||||
controller.stopTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers stopped");
|
||||
return msgs;
|
||||
@ -158,10 +180,10 @@ public class TunnelControllerGroup {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.startTunnel();
|
||||
controller.startTunnelBackground();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers started");
|
||||
return msgs;
|
||||
@ -259,33 +281,13 @@ public class TunnelControllerGroup {
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(cfgFile);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.length() <= 0) continue;
|
||||
if (line.startsWith("#") || line.startsWith(";"))
|
||||
continue;
|
||||
int eq = line.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= line.length() - 1) )
|
||||
continue;
|
||||
String key = line.substring(0, eq);
|
||||
String val = line.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Props loaded with " + props.size() + " lines");
|
||||
DataHelper.loadProps(props, cfgFile);
|
||||
return props;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error reading the controllers from " + configFile, ioe);
|
||||
return null;
|
||||
} finally {
|
||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ class WebEditPageFormGenerator {
|
||||
"<option value=\"httpclient\">HTTP proxy</option>" +
|
||||
"<option value=\"client\">Client tunnel</option>" +
|
||||
"<option value=\"server\">Server tunnel</option>" +
|
||||
"<option value=\"httpserver\">HTTP server tunnel</option>" +
|
||||
"</select> <input type=\"submit\" value=\"GO\" />" +
|
||||
"</form>\n";
|
||||
|
||||
@ -42,6 +43,8 @@ class WebEditPageFormGenerator {
|
||||
return getEditClientForm(controller, id);
|
||||
else if ("server".equals(type))
|
||||
return getEditServerForm(controller, id);
|
||||
else if ("httpserver".equals(type))
|
||||
return getEditHttpServerForm(controller, id);
|
||||
else
|
||||
return "WTF, unknown type [" + type + "]";
|
||||
}
|
||||
@ -60,6 +63,9 @@ class WebEditPageFormGenerator {
|
||||
buf.append("value=\"squid.i2p\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<hr />Note: the following options are shared across all client tunnels and");
|
||||
buf.append(" HTTP proxies<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
@ -80,6 +86,9 @@ class WebEditPageFormGenerator {
|
||||
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
|
||||
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
|
||||
|
||||
buf.append("<hr />Note: the following options are shared across all client tunnels and");
|
||||
buf.append(" HTTP proxies<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
@ -97,7 +106,7 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"localhost\" ");
|
||||
buf.append("value=\"127.0.0.1\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
@ -123,6 +132,48 @@ class WebEditPageFormGenerator {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditHttpServerForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>HTTP server tunnel</i><input type=\"hidden\" name=\"type\" value=\"httpserver\" /><br />\n");
|
||||
|
||||
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"127.0.0.1\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
if ( (controller != null) && (controller.getTargetPort() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"80\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Website hostname:</b> <input type=\"text\" size=\"16\" name=\"spoofedHost\" ");
|
||||
if ( (controller != null) && (controller.getSpoofedHost() != null) )
|
||||
buf.append("value=\"").append(controller.getSpoofedHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"mysite.i2p\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
|
||||
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
|
||||
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
|
||||
} else {
|
||||
buf.append("myServer.privKey\" /><br />");
|
||||
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
|
||||
}
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start off the form and add some common fields (name, num, description)
|
||||
*
|
||||
@ -135,6 +186,9 @@ class WebEditPageFormGenerator {
|
||||
buf.append("<form action=\"edit.jsp\">");
|
||||
if (id != null)
|
||||
buf.append("<input type=\"hidden\" name=\"num\" value=\"").append(id).append("\" />");
|
||||
long nonce = new Random().nextLong();
|
||||
System.setProperty(WebEditPageHelper.class.getName() + ".nonce", nonce+"");
|
||||
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" />");
|
||||
|
||||
buf.append("<b>Name:</b> <input type=\"text\" name=\"name\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getName() != null) )
|
||||
@ -145,6 +199,13 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getDescription() != null) )
|
||||
buf.append("value=\"").append(controller.getDescription()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<b>Start automatically?</b> \n");
|
||||
buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" ");
|
||||
if ( (controller != null) && (controller.getStartOnLoad()) )
|
||||
buf.append(" checked=\"true\" />\n<br />\n");
|
||||
else
|
||||
buf.append(" />\n<br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,9 +255,11 @@ class WebEditPageFormGenerator {
|
||||
private static void addOptions(StringBuffer buf, TunnelController controller) {
|
||||
int tunnelDepth = 2;
|
||||
int numTunnels = 2;
|
||||
int connectDelay = 0;
|
||||
int maxWindowSize = -1;
|
||||
Properties opts = getOptions(controller);
|
||||
if (opts != null) {
|
||||
String depth = opts.getProperty("tunnels.depthInbound");
|
||||
String depth = opts.getProperty("inbound.length");
|
||||
if (depth != null) {
|
||||
try {
|
||||
tunnelDepth = Integer.parseInt(depth);
|
||||
@ -204,7 +267,7 @@ class WebEditPageFormGenerator {
|
||||
tunnelDepth = 2;
|
||||
}
|
||||
}
|
||||
String num = opts.getProperty("tunnels.numInbound");
|
||||
String num = opts.getProperty("inbound.quantity");
|
||||
if (num != null) {
|
||||
try {
|
||||
numTunnels = Integer.parseInt(num);
|
||||
@ -212,6 +275,22 @@ class WebEditPageFormGenerator {
|
||||
numTunnels = 2;
|
||||
}
|
||||
}
|
||||
String delay = opts.getProperty("i2p.streaming.connectDelay");
|
||||
if (delay != null) {
|
||||
try {
|
||||
connectDelay = Integer.parseInt(delay);
|
||||
} catch (NumberFormatException nfe) {
|
||||
connectDelay = 0;
|
||||
}
|
||||
}
|
||||
String max = opts.getProperty("i2p.streaming.maxWindowSize");
|
||||
if (max != null) {
|
||||
try {
|
||||
maxWindowSize = Integer.parseInt(max);
|
||||
} catch (NumberFormatException nfe) {
|
||||
maxWindowSize = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append("<b>Tunnel depth:</b> ");
|
||||
@ -251,12 +330,27 @@ class WebEditPageFormGenerator {
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>Delay connection briefly? </b> ");
|
||||
buf.append("<input type=\"checkbox\" name=\"connectDelay\" value=\"");
|
||||
buf.append((connectDelay > 0 ? connectDelay : 1000)).append("\" ");
|
||||
if (connectDelay > 0)
|
||||
buf.append("checked=\"true\" ");
|
||||
buf.append("/> (useful for brief request/response connections)<br />\n");
|
||||
|
||||
buf.append("<b>Communication profile:</b>");
|
||||
buf.append("<select name=\"profile\">");
|
||||
if (maxWindowSize <= 0)
|
||||
buf.append("<option value=\"interactive\">Interactive</option><option value=\"bulk\" selected=\"true\">Bulk</option>");
|
||||
else
|
||||
buf.append("<option value=\"interactive\" selected=\"true\">Interactive</option><option value=\"bulk\">Bulk</option>");
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>I2CP host:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPHost() != null) )
|
||||
buf.append(controller.getI2CPHost());
|
||||
else
|
||||
buf.append("localhost");
|
||||
buf.append("127.0.0.1");
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>I2CP port:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
|
||||
@ -273,20 +367,20 @@ class WebEditPageFormGenerator {
|
||||
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = opts.getProperty(key);
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if ("inbound.length".equals(key)) continue;
|
||||
if ("outbound.length".equals(key)) continue;
|
||||
if ("inbound.quantity".equals(key)) continue;
|
||||
if ("outbound.quantity".equals(key)) continue;
|
||||
if ("inbound.nickname".equals(key)) continue;
|
||||
if ("outbound.nickname".equals(key)) continue;
|
||||
if ("i2p.streaming.connectDelay".equals(key)) continue;
|
||||
if ("i2p.streaming.maxWindowSize".equals(key)) continue;
|
||||
if (i != 0) buf.append(' ');
|
||||
buf.append(key).append('=').append(val);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>Start automatically?</b> \n");
|
||||
buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" ");
|
||||
if ( (controller != null) && (controller.getStartOnLoad()) )
|
||||
buf.append(" checked=\"true\" />\n<br />\n");
|
||||
else
|
||||
buf.append(" />\n<br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,6 +29,7 @@ public class WebEditPageHelper {
|
||||
private String _i2cpPort;
|
||||
private String _tunnelDepth;
|
||||
private String _tunnelCount;
|
||||
private boolean _connectDelay;
|
||||
private String _customOptions;
|
||||
private String _proxyList;
|
||||
private String _port;
|
||||
@ -37,10 +38,13 @@ public class WebEditPageHelper {
|
||||
private String _targetDestination;
|
||||
private String _targetHost;
|
||||
private String _targetPort;
|
||||
private String _spoofedHost;
|
||||
private String _privKeyFile;
|
||||
private String _profile;
|
||||
private boolean _startOnLoad;
|
||||
private boolean _privKeyGenerate;
|
||||
private boolean _removeConfirmed;
|
||||
private long _nonce;
|
||||
|
||||
public WebEditPageHelper() {
|
||||
_action = null;
|
||||
@ -50,6 +54,14 @@ public class WebEditPageHelper {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class);
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
if (nonce != null) {
|
||||
try {
|
||||
_nonce = Long.parseLong(nonce);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for form submit - either "Save" or Remove"
|
||||
*/
|
||||
@ -138,6 +150,10 @@ public class WebEditPageHelper {
|
||||
public void setTargetPort(String port) {
|
||||
_targetPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** What host does this http server tunnel spoof */
|
||||
public void setSpoofedHost(String host) {
|
||||
_spoofedHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
_privKeyFile = (file != null ? file.trim() : null);
|
||||
@ -164,6 +180,12 @@ public class WebEditPageHelper {
|
||||
public void setStartOnLoad(String moo) {
|
||||
_startOnLoad = true;
|
||||
}
|
||||
public void setConnectDelay(String moo) {
|
||||
_connectDelay = true;
|
||||
}
|
||||
public void setProfile(String profile) {
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the form and display any resulting messages
|
||||
@ -215,6 +237,9 @@ public class WebEditPageHelper {
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return "";
|
||||
String expected = System.getProperty(getClass().getName() + ".nonce");
|
||||
if ( (expected == null) || (!expected.equals(Long.toString(_nonce))) )
|
||||
return "<b>Invalid nonce, are you being spoofed?</b>";
|
||||
if ("Save".equals(_action))
|
||||
return save();
|
||||
else if ("Remove".equals(_action))
|
||||
@ -248,10 +273,45 @@ public class WebEditPageHelper {
|
||||
// creating new
|
||||
cur = new TunnelController(config, "", _privKeyGenerate);
|
||||
TunnelControllerGroup.getInstance().addController(cur);
|
||||
if (cur.getStartOnLoad())
|
||||
cur.startTunnelBackground();
|
||||
} else {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
|
||||
if ("httpclient".equals(cur.getType()) || "client".equals(cur.getType())) {
|
||||
// all clients use the same I2CP session, and as such, use the same
|
||||
// I2CP options
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
for (int i = 0; i < controllers.size(); i++) {
|
||||
TunnelController c = (TunnelController)controllers.get(i);
|
||||
if (c == cur) continue;
|
||||
if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) {
|
||||
Properties cOpt = c.getConfig("");
|
||||
if (_tunnelCount != null) {
|
||||
cOpt.setProperty("option.inbound.quantity", _tunnelCount);
|
||||
cOpt.setProperty("option.outbound.quantity", _tunnelCount);
|
||||
}
|
||||
if (_tunnelDepth != null) {
|
||||
cOpt.setProperty("option.inbound.length", _tunnelDepth);
|
||||
cOpt.setProperty("option.outbound.length", _tunnelDepth);
|
||||
}
|
||||
if (_connectDelay)
|
||||
cOpt.setProperty("option.i2p.streaming.connectDelay", "1000");
|
||||
else
|
||||
cOpt.setProperty("option.i2p.streaming.connectDelay", "0");
|
||||
if ("interactive".equals(_profile))
|
||||
cOpt.setProperty("option.i2p.streaming.maxWindowSize", "1");
|
||||
else
|
||||
cOpt.remove("option.i2p.streaming.maxWindowSize");
|
||||
if (_name != null) {
|
||||
cOpt.setProperty("option.inbound.nickname", _name);
|
||||
cOpt.setProperty("option.outbound.nickname", _name);
|
||||
}
|
||||
c.setConfig(cOpt, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getMessages(doSave());
|
||||
}
|
||||
@ -295,10 +355,18 @@ public class WebEditPageHelper {
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
} else if ("httpserver".equals(_type)) {
|
||||
if (_targetHost != null)
|
||||
config.setProperty("targetHost", _targetHost);
|
||||
if (_targetPort != null)
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
if (_spoofedHost != null)
|
||||
config.setProperty("spoofedHost", _spoofedHost);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@ -322,18 +390,40 @@ public class WebEditPageHelper {
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
if ("inbound.length".equals(key)) continue;
|
||||
if ("outbound.length".equals(key)) continue;
|
||||
if ("inbound.quantity".equals(key)) continue;
|
||||
if ("outbound.quantity".equals(key)) continue;
|
||||
if ("inbound.nickname".equals(key)) continue;
|
||||
if ("outbound.nickname".equals(key)) continue;
|
||||
if ("i2p.streaming.connectDelay".equals(key)) continue;
|
||||
if ("i2p.streaming.maxWindowSize".equals(key)) continue;
|
||||
config.setProperty("option." + key, val);
|
||||
}
|
||||
}
|
||||
|
||||
config.setProperty("startOnLoad", _startOnLoad + "");
|
||||
|
||||
if (_tunnelCount != null)
|
||||
config.setProperty("option.tunnels.numInbound", _tunnelCount);
|
||||
if (_tunnelDepth != null)
|
||||
config.setProperty("option.tunnels.depthInbound", _tunnelDepth);
|
||||
if (_tunnelCount != null) {
|
||||
config.setProperty("option.inbound.quantity", _tunnelCount);
|
||||
config.setProperty("option.outbound.quantity", _tunnelCount);
|
||||
}
|
||||
if (_tunnelDepth != null) {
|
||||
config.setProperty("option.inbound.length", _tunnelDepth);
|
||||
config.setProperty("option.outbound.length", _tunnelDepth);
|
||||
}
|
||||
if (_connectDelay)
|
||||
config.setProperty("option.i2p.streaming.connectDelay", "1000");
|
||||
else
|
||||
config.setProperty("option.i2p.streaming.connectDelay", "0");
|
||||
if (_name != null) {
|
||||
config.setProperty("option.inbound.nickname", _name);
|
||||
config.setProperty("option.outbound.nickname", _name);
|
||||
}
|
||||
if ("interactive".equals(_profile))
|
||||
config.setProperty("option.i2p.streaming.maxWindowSize", "1");
|
||||
else
|
||||
config.remove("option.i2p.streaming.maxWindowSize");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,14 +14,17 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class WebStatusPageHelper {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private int _controllerNum;
|
||||
private long _nonce;
|
||||
|
||||
public WebStatusPageHelper() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_action = null;
|
||||
_controllerNum = -1;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebStatusPageHelper.class);
|
||||
_log = _context.logManager().getLog(WebStatusPageHelper.class);
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
@ -36,6 +39,14 @@ public class WebStatusPageHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
public void setNonce(long nonce) { _nonce = nonce; }
|
||||
public void setNonce(String nonce) {
|
||||
if (nonce != null) {
|
||||
try {
|
||||
_nonce = Long.parseLong(nonce);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
}
|
||||
|
||||
public String getActionResults() {
|
||||
try {
|
||||
@ -51,26 +62,44 @@ public class WebStatusPageHelper {
|
||||
if (group == null)
|
||||
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
|
||||
|
||||
long nonce = _context.random().nextLong();
|
||||
StringBuffer buf = new StringBuffer(4*1024);
|
||||
buf.append("<ul>");
|
||||
List tunnels = group.getControllers();
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
buf.append("<li>\n");
|
||||
getSummary(buf, i, (TunnelController)tunnels.get(i));
|
||||
getSummary(buf, i, (TunnelController)tunnels.get(i), nonce);
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</ul>");
|
||||
|
||||
buf.append("<hr /><form action=\"index.jsp\" method=\"GET\">\n");
|
||||
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Stop all\" />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Start all\" />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Restart all\" />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Reload config\" />\n");
|
||||
buf.append("</form>\n");
|
||||
|
||||
System.setProperty(getClass().getName() + ".nonce", nonce+"");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void getSummary(StringBuffer buf, int num, TunnelController controller) {
|
||||
private void getSummary(StringBuffer buf, int num, TunnelController controller, long nonce) {
|
||||
buf.append("<b>").append(controller.getName()).append("</b>: ");
|
||||
if (controller.getIsRunning()) {
|
||||
buf.append("<i>running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=stop\">stop</a> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num);
|
||||
buf.append("&nonce=").append(nonce);
|
||||
buf.append("&action=stop\">stop</a> ");
|
||||
} else if (controller.getIsStarting()) {
|
||||
buf.append("<i>startup in progress (please be patient)</i>");
|
||||
} else {
|
||||
buf.append("<i>not running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=start\">start</a> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num);
|
||||
buf.append("&nonce=").append(nonce);
|
||||
buf.append("&action=start\">start</a> ");
|
||||
}
|
||||
buf.append("<a href=\"edit.jsp?num=").append(num).append("\">edit</a> ");
|
||||
buf.append("<br />\n");
|
||||
@ -80,6 +109,9 @@ public class WebStatusPageHelper {
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return getMessages();
|
||||
String expected = System.getProperty(getClass().getName() + ".nonce");
|
||||
if ( (expected == null) || (!expected.equals(Long.toString(_nonce))) )
|
||||
return "<b>Invalid nonce, are you being spoofed?</b>";
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
else if ("Start all".equals(_action))
|
||||
@ -137,7 +169,7 @@ public class WebStatusPageHelper {
|
||||
List controllers = group.getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.startTunnel();
|
||||
controller.startTunnelBackground();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
||||
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
|
||||
Socket clientSock = serv.getClientSocket();
|
||||
I2PSocket destSock = serv.getDestinationI2PSocket();
|
||||
new I2PTunnelRunner(clientSock, destSock, sockLock, null);
|
||||
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
|
||||
} catch (SOCKSException e) {
|
||||
_log.error("Error from SOCKS connection: " + e.getMessage());
|
||||
closeSocket(s);
|
||||
|
@ -81,7 +81,7 @@ public abstract class SOCKSServer {
|
||||
if (connHostName.toLowerCase().endsWith(".i2p")) {
|
||||
_log.debug("connecting to " + connHostName + "...");
|
||||
I2PSocketManager sm = I2PSocketManagerFactory.createManager();
|
||||
destSock = sm.connect(I2PTunnel.destFromName(connHostName), new I2PSocketOptions());
|
||||
destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
|
||||
confirmConnection();
|
||||
_log.debug("connection confirmed - exchanging data...");
|
||||
} else {
|
||||
|
@ -11,13 +11,6 @@
|
||||
<b><jsp:getProperty name="helper" property="actionResults" /></b>
|
||||
|
||||
<jsp:getProperty name="helper" property="summaryList" />
|
||||
<hr />
|
||||
<form action="index.jsp" method="GET">
|
||||
<input type="submit" name="action" value="Stop all" />
|
||||
<input type="submit" name="action" value="Start all" />
|
||||
<input type="submit" name="action" value="Restart all" />
|
||||
<input type="submit" name="action" value="Reload config" />
|
||||
</form>
|
||||
|
||||
<form action="edit.jsp">
|
||||
<b>Add new:</b>
|
||||
@ -25,6 +18,7 @@
|
||||
<option value="httpclient">HTTP proxy</option>
|
||||
<option value="client">Client tunnel</option>
|
||||
<option value="server">Server tunnel</option>
|
||||
<option value="httpserver">HTTP server tunnel</option>
|
||||
</select> <input type="submit" value="GO" />
|
||||
</form>
|
||||
|
||||
|
@ -3,18 +3,38 @@
|
||||
|
||||
<target name="all" depends="build" />
|
||||
<target name="fetchJettylib" >
|
||||
<available property="jetty.available" file="jettylib" />
|
||||
<available property="jetty.available" file="jetty-5.1.2.zip" />
|
||||
<ant target="doFetchJettylib" />
|
||||
</target>
|
||||
<target name="doFetchJettylib" unless="jetty.available" >
|
||||
<echo message="The libraries contained within the fetched file are from Jetty's 4.2.21 " />
|
||||
<echo message="distribution (http://jetty.mortbay.org/) which we have copied to our website since" />
|
||||
<echo message="theirs doesn't have direct HTTP access to the libs. These are not " />
|
||||
<echo message="The libraries contained within the fetched file are from Jetty's 5.1.2" />
|
||||
<echo message="distribution (http://jetty.mortbay.org/). These are not " />
|
||||
<echo message="necessary for using I2P, but are used by some applications on top of I2P," />
|
||||
<echo message="such as the routerconsole." />
|
||||
<get src="http://dev.i2p.net/jettylib.tar.bz2" verbose="true" dest="jettylib.tar.bz2" />
|
||||
<untar src="jettylib.tar.bz2" compression="bzip2" dest="." />
|
||||
<delete file="jettylib.tar.bz2" />
|
||||
<get src="http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-5.1.2.zip" verbose="true" dest="jetty-5.1.2.zip" />
|
||||
<ant target="doExtract" />
|
||||
</target>
|
||||
<target name="doExtract">
|
||||
<unzip src="jetty-5.1.2.zip" dest="." />
|
||||
<mkdir dir="jettylib" />
|
||||
<copy todir="jettylib">
|
||||
<fileset dir="jetty-5.1.2/lib">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="jettylib">
|
||||
<fileset dir="jetty-5.1.2/ext">
|
||||
<include name="ant.jar" />
|
||||
<include name="commons-el.jar" />
|
||||
<include name="commons-logging.jar" />
|
||||
<include name="jasper-compiler.jar" />
|
||||
<include name="jasper-runtime.jar" />
|
||||
<include name="javax.servlet.jar" />
|
||||
<include name="org.mortbay.jetty.jar" />
|
||||
<include name="xercesImpl.jar" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<delete dir="jetty-5.1.2" />
|
||||
</target>
|
||||
<target name="build" depends="fetchJettylib" />
|
||||
<target name="builddep" />
|
||||
|
@ -5,7 +5,7 @@ package net.i2p.client.streaming;
|
||||
* so care should be taken when using in a multithreaded environment.
|
||||
*
|
||||
*/
|
||||
public class ByteCollector {
|
||||
class ByteCollector {
|
||||
byte[] contents;
|
||||
int size;
|
||||
|
||||
|
@ -32,7 +32,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
private Object remoteIDWaiter = new Object();
|
||||
private I2PInputStream in;
|
||||
private I2POutputStream out;
|
||||
private SocketErrorListener _socketErrorListener;
|
||||
private I2PSocket.SocketErrorListener _socketErrorListener;
|
||||
private boolean outgoing;
|
||||
private long _socketId;
|
||||
private static long __socketId = 0;
|
||||
@ -284,7 +284,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
in.setReadTimeout(ms);
|
||||
}
|
||||
|
||||
public void setSocketErrorListener(SocketErrorListener lsnr) {
|
||||
public void setSocketErrorListener(I2PSocket.SocketErrorListener lsnr) {
|
||||
_socketErrorListener = lsnr;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,9 @@ public interface I2PSocketManager {
|
||||
public void setDefaultOptions(I2PSocketOptions options);
|
||||
public I2PSocketOptions getDefaultOptions();
|
||||
public I2PServerSocket getServerSocket();
|
||||
|
||||
public I2PSocketOptions buildOptions();
|
||||
public I2PSocketOptions buildOptions(Properties opts);
|
||||
|
||||
/**
|
||||
* Create a new connected socket (block until the socket is created)
|
||||
@ -102,4 +105,11 @@ public interface I2PSocketManager {
|
||||
public void setName(String name);
|
||||
|
||||
public void init(I2PAppContext context, I2PSession session, Properties opts, String name);
|
||||
|
||||
public void addDisconnectListener(DisconnectListener lsnr);
|
||||
public void removeDisconnectListener(DisconnectListener lsnr);
|
||||
|
||||
public static interface DisconnectListener {
|
||||
public void sessionDisconnected();
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ public class I2PSocketManagerFactory {
|
||||
private final static Log _log = new Log(I2PSocketManagerFactory.class);
|
||||
|
||||
public static final String PROP_MANAGER = "i2p.streaming.manager";
|
||||
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
|
||||
//public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
|
||||
//public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
|
||||
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
|
||||
|
||||
/**
|
||||
* Create a socket manager using a brand new destination connected to the
|
||||
@ -90,7 +90,7 @@ public class I2PSocketManagerFactory {
|
||||
opts.setProperty(name, System.getProperty(name));
|
||||
}
|
||||
boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
|
||||
if (oldLib) {
|
||||
if (oldLib && false) {
|
||||
// for the old streaming lib
|
||||
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
//opts.setProperty("tunnels.depthInbound", "0");
|
||||
@ -100,13 +100,18 @@ public class I2PSocketManagerFactory {
|
||||
//p.setProperty("tunnels.depthInbound", "0");
|
||||
}
|
||||
|
||||
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
|
||||
if (i2cpHost != null)
|
||||
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
if (i2cpPort > 0)
|
||||
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
|
||||
|
||||
try {
|
||||
I2PSession session = client.createSession(myPrivateKeyStream, opts);
|
||||
session.connect();
|
||||
return createManager(session, opts, "manager");
|
||||
I2PSocketManager sockMgr = createManager(session, opts, "manager");
|
||||
if (sockMgr != null)
|
||||
sockMgr.setDefaultOptions(sockMgr.buildOptions(opts));
|
||||
return sockMgr;
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error creating session for socket manager", ise);
|
||||
return null;
|
||||
@ -117,7 +122,7 @@ public class I2PSocketManagerFactory {
|
||||
if (false) {
|
||||
I2PSocketManagerImpl mgr = new I2PSocketManagerImpl();
|
||||
mgr.setSession(session);
|
||||
mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
//mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
return mgr;
|
||||
} else {
|
||||
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
|
||||
|
@ -10,9 +10,11 @@ import java.io.InterruptedIOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
@ -35,7 +37,7 @@ import net.i2p.util.Log;
|
||||
* or receive any messages with its .receiveMessage
|
||||
*
|
||||
*/
|
||||
public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private I2PSession _session;
|
||||
@ -46,6 +48,7 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
private I2PSocketOptions _defaultOptions;
|
||||
private long _acceptTimeout;
|
||||
private String _name;
|
||||
private List _listeners;
|
||||
private static int __managerId = 0;
|
||||
|
||||
public static final short ACK = 0x51;
|
||||
@ -76,8 +79,9 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
_inSockets = new HashMap(16);
|
||||
_outSockets = new HashMap(16);
|
||||
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
|
||||
_listeners = new ArrayList(1);
|
||||
setSession(session);
|
||||
setDefaultOptions(new I2PSocketOptions());
|
||||
setDefaultOptions(buildOptions(opts));
|
||||
_context.statManager().createRateStat("streaming.lifetime", "How long before the socket is closed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("streaming.sent", "How many bytes are sent in the stream?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("streaming.received", "How many bytes are received in the stream?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
@ -109,6 +113,15 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
public void disconnected(I2PSession session) {
|
||||
_log.info(getName() + ": Disconnected from the session");
|
||||
destroySocketManager();
|
||||
List listeners = null;
|
||||
synchronized (_listeners) {
|
||||
listeners = new ArrayList(_listeners);
|
||||
_listeners.clear();
|
||||
}
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
I2PSocketManager.DisconnectListener lsnr = (I2PSocketManager.DisconnectListener)listeners.get(i);
|
||||
lsnr.sessionDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
@ -433,6 +446,11 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
public I2PSocketOptions getDefaultOptions() {
|
||||
return _defaultOptions;
|
||||
}
|
||||
|
||||
public I2PSocketOptions buildOptions() { return buildOptions(null); }
|
||||
public I2PSocketOptions buildOptions(Properties opts) {
|
||||
return new I2PSocketOptionsImpl(opts);
|
||||
}
|
||||
|
||||
public I2PServerSocket getServerSocket() {
|
||||
if (_serverSocket == null) {
|
||||
@ -702,6 +720,17 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
public String getName() { return _name; }
|
||||
public void setName(String name) { _name = name; }
|
||||
|
||||
public void addDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
|
||||
synchronized (_listeners) {
|
||||
_listeners.add(lsnr);
|
||||
}
|
||||
}
|
||||
public void removeDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
|
||||
synchronized (_listeners) {
|
||||
_listeners.remove(lsnr);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getReadableForm(String id) {
|
||||
if (id == null) return "(null)";
|
||||
if (id.length() != 3) return "Bogus";
|
||||
|
@ -6,67 +6,38 @@ import java.util.Properties;
|
||||
* Define the configuration for streaming and verifying data on the socket.
|
||||
*
|
||||
*/
|
||||
public class I2PSocketOptions {
|
||||
private long _connectTimeout;
|
||||
private long _readTimeout;
|
||||
private long _writeTimeout;
|
||||
private int _maxBufferSize;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
|
||||
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
|
||||
|
||||
public I2PSocketOptions() {
|
||||
_connectTimeout = -1;
|
||||
_readTimeout = -1;
|
||||
_writeTimeout = DEFAULT_WRITE_TIMEOUT;
|
||||
_maxBufferSize = DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
public I2PSocketOptions(I2PSocketOptions opts) {
|
||||
_connectTimeout = opts.getConnectTimeout();
|
||||
_readTimeout = opts.getReadTimeout();
|
||||
_writeTimeout = opts.getWriteTimeout();
|
||||
_maxBufferSize = opts.getMaxBufferSize();
|
||||
}
|
||||
|
||||
public I2PSocketOptions(Properties opts) {
|
||||
|
||||
}
|
||||
public interface I2PSocketOptions {
|
||||
public static final String PROP_BUFFER_SIZE = "i2p.streaming.bufferSize";
|
||||
public static final String PROP_CONNECT_TIMEOUT = "i2p.streaming.connectTimeout";
|
||||
public static final String PROP_READ_TIMEOUT = "i2p.streaming.readTimeout";
|
||||
public static final String PROP_WRITE_TIMEOUT = "i2p.streaming.writeTimeout";
|
||||
|
||||
/**
|
||||
* How long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getConnectTimeout() {
|
||||
return _connectTimeout;
|
||||
}
|
||||
public long getConnectTimeout();
|
||||
|
||||
/**
|
||||
* Define how long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
*/
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
}
|
||||
public void setConnectTimeout(long ms);
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
public long getReadTimeout();
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
}
|
||||
public void setReadTimeout(long ms);
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
@ -76,9 +47,7 @@ public class I2PSocketOptions {
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
return _maxBufferSize;
|
||||
}
|
||||
public int getMaxBufferSize();
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
@ -87,9 +56,7 @@ public class I2PSocketOptions {
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
}
|
||||
public void setMaxBufferSize(int numBytes);
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
@ -97,9 +64,7 @@ public class I2PSocketOptions {
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
}
|
||||
public long getWriteTimeout();
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
@ -107,7 +72,5 @@ public class I2PSocketOptions {
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
}
|
||||
public void setWriteTimeout(long ms);
|
||||
}
|
||||
|
@ -0,0 +1,148 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Define the configuration for streaming and verifying data on the socket.
|
||||
*
|
||||
*/
|
||||
class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
private long _connectTimeout;
|
||||
private long _readTimeout;
|
||||
private long _writeTimeout;
|
||||
private int _maxBufferSize;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
|
||||
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
|
||||
public static final int DEFAULT_CONNECT_TIMEOUT = 60*1000;
|
||||
|
||||
public I2PSocketOptionsImpl() {
|
||||
this(System.getProperties());
|
||||
}
|
||||
|
||||
public I2PSocketOptionsImpl(I2PSocketOptions opts) {
|
||||
this(System.getProperties());
|
||||
if (opts != null) {
|
||||
_connectTimeout = opts.getConnectTimeout();
|
||||
_readTimeout = opts.getReadTimeout();
|
||||
_writeTimeout = opts.getWriteTimeout();
|
||||
_maxBufferSize = opts.getMaxBufferSize();
|
||||
}
|
||||
}
|
||||
|
||||
public I2PSocketOptionsImpl(Properties opts) {
|
||||
init(opts);
|
||||
}
|
||||
|
||||
public void setProperties(Properties opts) {
|
||||
if (opts == null) return;
|
||||
if (opts.containsKey(PROP_BUFFER_SIZE))
|
||||
_maxBufferSize = getInt(opts, PROP_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
|
||||
if (opts.containsKey(PROP_CONNECT_TIMEOUT))
|
||||
_connectTimeout = getInt(opts, PROP_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
|
||||
if (opts.containsKey(PROP_READ_TIMEOUT))
|
||||
_readTimeout = getInt(opts, PROP_READ_TIMEOUT, -1);
|
||||
if (opts.containsKey(PROP_WRITE_TIMEOUT))
|
||||
_writeTimeout = getInt(opts, PROP_WRITE_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
protected void init(Properties opts) {
|
||||
_maxBufferSize = getInt(opts, PROP_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
|
||||
_connectTimeout = getInt(opts, PROP_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
|
||||
_readTimeout = getInt(opts, PROP_READ_TIMEOUT, -1);
|
||||
_writeTimeout = getInt(opts, PROP_WRITE_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
protected int getInt(Properties opts, String name, int defaultVal) {
|
||||
if (opts == null) return defaultVal;
|
||||
String val = opts.getProperty(name);
|
||||
if (val == null) {
|
||||
return defaultVal;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getConnectTimeout() {
|
||||
return _connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define how long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
*/
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
return _maxBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
}
|
||||
}
|
@ -115,14 +115,20 @@ public class StreamSinkServer {
|
||||
}
|
||||
public void run() {
|
||||
if (_fos == null) return;
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
InputStream in = _sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
long written = 0;
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
_fos.write(buf, 0, read);
|
||||
written += read;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read and wrote " + read);
|
||||
}
|
||||
_log.error("Got EOF from client socket");
|
||||
long lifetime = System.currentTimeMillis() - start;
|
||||
_log.error("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
@ -143,6 +149,9 @@ public class StreamSinkServer {
|
||||
public static void main(String args[]) {
|
||||
StreamSinkServer server = null;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
server = new StreamSinkServer("dataDir", "server.key", "localhost", 10001);
|
||||
break;
|
||||
case 2:
|
||||
server = new StreamSinkServer(args[0], args[1]);
|
||||
break;
|
||||
|
@ -0,0 +1,218 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
/**
|
||||
* Sit around on a destination, receiving lots of data and sending lots of
|
||||
* data to whomever talks to us.
|
||||
*
|
||||
* Usage: TestSwarm myKeyFile [peerDestFile ]*
|
||||
*
|
||||
*/
|
||||
public class TestSwarm {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private String _destFile;
|
||||
private String _peerDestFiles[];
|
||||
private String _conOptions;
|
||||
private I2PSocketManager _manager;
|
||||
private boolean _dead;
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length < 1) {
|
||||
System.err.println("Usage: TestSwarm myDestFile [peerDestFile ]*");
|
||||
return;
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
String files[] = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, files, 0, files.length);
|
||||
TestSwarm swarm = new TestSwarm(ctx, args[0], files);
|
||||
swarm.startup();
|
||||
}
|
||||
|
||||
public TestSwarm(I2PAppContext ctx, String destFile, String peerDestFiles[]) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(TestSwarm.class);
|
||||
_dead = false;
|
||||
_destFile = destFile;
|
||||
_peerDestFiles = peerDestFiles;
|
||||
_conOptions = "";
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
_log.debug("Starting up");
|
||||
File keys = new File(_destFile);
|
||||
if (!keys.exists()) {
|
||||
try {
|
||||
I2PClientFactory.createClient().createDestination(new FileOutputStream(keys));
|
||||
} catch (Exception e) {
|
||||
_log.error("Error creating a new destination on " + keys, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), null, -1, null);
|
||||
} catch (Exception e) {
|
||||
_log.error("Error creatign the manager", e);
|
||||
return;
|
||||
}
|
||||
|
||||
I2PThread listener = new I2PThread(new Listener(), "Listener");
|
||||
listener.start();
|
||||
|
||||
connectWithPeers();
|
||||
}
|
||||
|
||||
|
||||
private void connectWithPeers() {
|
||||
if (_peerDestFiles != null) {
|
||||
for (int i = 0; i < _peerDestFiles.length; i++) {
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(_peerDestFiles[i]);
|
||||
Destination dest = new Destination();
|
||||
dest.readBytes(fin);
|
||||
|
||||
I2PThread flooder = new I2PThread(new Flooder(dest), "Flooder+" + dest.calculateHash().toBase64().substring(0,4));
|
||||
flooder.start();
|
||||
} catch (Exception e) {
|
||||
_log.error("Unable to read the peer from " + _peerDestFiles[i], e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Listener implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
I2PServerSocket ss = _manager.getServerSocket();
|
||||
I2PSocket s = null;
|
||||
while ( (s = ss.accept()) != null) {
|
||||
I2PThread flooder = new I2PThread(new Flooder(s), "Flooder-" + s.getPeerDestination().calculateHash().toBase64().substring(0,4));
|
||||
flooder.start();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error listening", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static volatile long __conId = 0;
|
||||
private class Flooder implements Runnable {
|
||||
private Destination _remoteDestination;
|
||||
private I2PSocket _socket;
|
||||
private boolean _closed;
|
||||
private long _started;
|
||||
private long _totalSent;
|
||||
private long _totalReceived;
|
||||
private long _lastReceived;
|
||||
private long _lastReceivedOn;
|
||||
private long _connectionId;
|
||||
|
||||
public Flooder(Destination dest) {
|
||||
_socket = null;
|
||||
_remoteDestination = dest;
|
||||
_connectionId = ++__conId;
|
||||
_closed = false;
|
||||
_lastReceived = -1;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
}
|
||||
|
||||
public Flooder(I2PSocket socket) {
|
||||
_socket = socket;
|
||||
_remoteDestination = socket.getPeerDestination();
|
||||
_connectionId = ++__conId;
|
||||
_closed = false;
|
||||
_lastReceived = -1;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
}
|
||||
|
||||
public long getConnectionId() { return _connectionId; }
|
||||
public Destination getDestination() { return _remoteDestination; }
|
||||
|
||||
public void run() {
|
||||
_started = _context.clock().now();
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
|
||||
byte data[] = new byte[32*1024];
|
||||
long value = 0;
|
||||
long lastSend = _context.clock().now();
|
||||
if (_socket == null) {
|
||||
try {
|
||||
_socket = _manager.connect(_remoteDestination);
|
||||
} catch (Exception e) {
|
||||
_log.error("Error connecting to " + _remoteDestination.calculateHash().toBase64().substring(0,4));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
I2PThread floodListener = new I2PThread(new FloodListener(), "FloodListener" + _connectionId);
|
||||
floodListener.start();
|
||||
|
||||
try {
|
||||
OutputStream out = _socket.getOutputStream();
|
||||
while (!_closed) {
|
||||
out.write(data);
|
||||
// out.flush();
|
||||
_totalSent += data.length;
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
|
||||
//try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
long now = _context.clock().now();
|
||||
_log.debug("Sending " + _connectionId + " after " + (now-lastSend));
|
||||
lastSend = now;
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error sending", e);
|
||||
}
|
||||
}
|
||||
|
||||
private class FloodListener implements Runnable {
|
||||
public void run() {
|
||||
long lastRead = System.currentTimeMillis();
|
||||
long now = lastRead;
|
||||
try {
|
||||
InputStream in = _socket.getInputStream();
|
||||
byte buf[] = new byte[32*1024];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
now = System.currentTimeMillis();
|
||||
_totalReceived += read;
|
||||
_context.statManager().addRateData("swarm." + getConnectionId() + ".totalReceived", _totalReceived, 0);
|
||||
_log.debug("Receiving " + _connectionId + " with " + read + " after " + (now-lastRead));
|
||||
lastRead = now;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error listening to the flood", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
236
apps/pants/build.xml
Normal file
236
apps/pants/build.xml
Normal file
@ -0,0 +1,236 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
Ports + Ant = Pants, a simple Ant-based package manager
|
||||
|
||||
free (adj.): unencumbered; not under the control of others
|
||||
|
||||
Written by smeghead in 2005 and released into the public domain with no
|
||||
warranty of any kind, either expressed or implied. It probably won't make
|
||||
your computer catch on fire, or eat your children, but it might. Use at your
|
||||
own risk.
|
||||
-->
|
||||
|
||||
<project basedir="." default="help" name="pants-interface">
|
||||
|
||||
<!-- .......................... Global Properties .......................... -->
|
||||
|
||||
|
||||
|
||||
<!-- ........................... Internal Tasks ............................ -->
|
||||
|
||||
<target name="-fetchCvs" unless="cvs.source.available" if="using.cvs">
|
||||
<cvs compressionlevel="${cvs.compression.level}"
|
||||
date="${cvs.date}"
|
||||
dest="./distfiles/cvs-src/${pbuild}"
|
||||
failonerror="true"
|
||||
package="${cvs.package}"
|
||||
passfile="${cvs.passfile}"
|
||||
port="${cvs.port}"
|
||||
cvsRoot="${cvs.root}"
|
||||
cvsRsh="${cvs.rsh}"
|
||||
tag="${cvs.tag}"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="-fetchPackage" unless="using.cvs">
|
||||
<get src="${package.url}"
|
||||
verbose="true"
|
||||
dest="./distfiles"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="-init">
|
||||
<!--
|
||||
TODO: Create dist/ and working/ folders for each pbuild subdir in case
|
||||
they've been wiped.
|
||||
-->
|
||||
<loadproperties srcfile="world" />
|
||||
<taskdef name="mergetypedproperties"
|
||||
classname="net.i2p.pants.MergeTypedPropertiesTask"
|
||||
classpath="./lib/pants.jar"
|
||||
/>
|
||||
<mergetypedproperties input="./pbuilds/${pbuild}/pbuild.properties"
|
||||
output="./pbuilds/${pbuild}/merged-properties.temp"
|
||||
booleanList="version.latest.find.regex.canonicaleq, version.latest.find.regex.caseinsensitive, version.latest.find.regex.comments, version.latest.find.regex.dotall, version.latest.find.regex.multiline, version.latest.find.regex.unicodecase, version.latest.find.regex.unixlines"
|
||||
stringList="cvs.compression.level, cvs.date, cvs.package, cvs.passfile, cvs.port, cvs.root, cvs.rsh, cvs.tag, package.url, version.latest, version.latest.find.url, version.latest.find.regex"
|
||||
/>
|
||||
<loadproperties srcfile="./pbuilds/${pbuild}/merged-properties.temp" />
|
||||
<delete file="./pbuilds/${pbuild}/merged-properties.temp" />
|
||||
<!--
|
||||
If '-Dpbuild={name}' isn't specified, the 'build', 'fetch', 'update'
|
||||
and 'version' commands should default to 'world' behavior.
|
||||
-->
|
||||
<antcall target="-setWorld" />
|
||||
<condition property="using.cvs">
|
||||
<or>
|
||||
<equals arg1="CVS" arg2="${version.using.${pbuild}}" />
|
||||
<equals arg1="cvs" arg2="${version.using.${pbuild}}" />
|
||||
</or>
|
||||
</condition>
|
||||
<!--
|
||||
If 'version.recommended' isn't defined in pbuild.properties, default
|
||||
to latest available version.
|
||||
-->
|
||||
</target>
|
||||
|
||||
<target name="-setWorld" unless="pbuild">
|
||||
<property name="pbuild" value="world" />
|
||||
</target>
|
||||
|
||||
<target name="-unpackTarBz2">
|
||||
<untar src="${pbuild.package}"
|
||||
compression="bzip2"
|
||||
dest="./${pbuild}/working"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="-unpackTarGzip">
|
||||
<untar src="${pbuild.package}"
|
||||
compression="gzip"
|
||||
dest="./${pbuild}/working"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="-unpackZip">
|
||||
<unzip src="${pbuild.package}" dest="./${pbuild}/working" />
|
||||
</target>
|
||||
|
||||
<target name="-updateCvs" if="using.cvs">
|
||||
<cvs command="update -d"
|
||||
compressionlevel="${compression.level}"
|
||||
date="${cvs.date}"
|
||||
dest="./distfiles/cvs-src"
|
||||
failonerror="true"
|
||||
package="${cvs.package}"
|
||||
passfile="${cvs.passfile}"
|
||||
port="${cvs.port}"
|
||||
cvsRoot="${cvs.root}"
|
||||
cvsRsh="${cvs.rsh}"
|
||||
tag="${cvs.tag}"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="-updateConfirm" if="confirm.update" unless="no.prompts">
|
||||
<input validargs="y,Y,n,N"
|
||||
defaultvalue="n"
|
||||
addproperty="confirm.update.answer">
|
||||
You currently have the recommended version installed. A newer
|
||||
version will be installed if you continue and this may break some
|
||||
applications which depend on this package. Are you sure you want
|
||||
to update? [y/N]
|
||||
</input>
|
||||
<condition property="abort.update">
|
||||
<or>
|
||||
<equals arg1="n" arg2="${confirm.update.answer}" />
|
||||
<equals arg1="N" arg2="${confirm.update.answer}" />
|
||||
</or>
|
||||
</condition>
|
||||
<fail if="abort.update">Update aborted.</fail>
|
||||
</target>
|
||||
|
||||
<target name="-versionLatest">
|
||||
<get src="${version.latest.find.url}"
|
||||
dest="version.latest.in.temp"
|
||||
verbose="true"
|
||||
/>
|
||||
<taskdef name="match"
|
||||
classname="net.i2p.pants.MatchTask"
|
||||
classpath="./lib/pants.jar"
|
||||
/>
|
||||
<match input="version.latest.in.temp"
|
||||
output="version.latest.parsed.temp"
|
||||
regex="${version.latest.find.regex}"
|
||||
canonicaleq="${version.latest.find.regex.canonicaleq}"
|
||||
caseinsensitive="${version.latest.find.regex.caseinsensitive}"
|
||||
comments="${version.latest.find.regex.comments}"
|
||||
dotall="${version.latest.find.regex.dotall}"
|
||||
multiline="${version.latest.find.regex.multiline}"
|
||||
unicodecase="${version.latest.find.regex.unicodecase}"
|
||||
unixlines="${version.latest.find.regex.unixlines}"
|
||||
/>
|
||||
<loadproperties srcFile="version.latest.parsed.temp" />
|
||||
<delete file="version.latest.in.temp" />
|
||||
<delete file="version.latest.parsed.temp" />
|
||||
<property name="version.latest" value="${group.1}" />
|
||||
</target>
|
||||
|
||||
<target name="-versionRecommended">
|
||||
<property name="version.recommended" value="x" />
|
||||
</target>
|
||||
|
||||
<target name="-versionUsing">
|
||||
<property name="version.using" value="x" />
|
||||
</target>
|
||||
|
||||
<!-- .......................... Public Interface ........................... -->
|
||||
|
||||
<target name="build" depends="-init,fetch"
|
||||
description="Build a pbuild and its dependencies">
|
||||
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="clean" />
|
||||
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="build" />
|
||||
<!--
|
||||
Perform a 'clean' on the target first (but not 'distclean')
|
||||
-->
|
||||
</target>
|
||||
|
||||
<target name="fetch" depends="-init"
|
||||
description="Get package only">
|
||||
<antcall target="-fetchPackage" />
|
||||
<antcall target="-fetchCvs" />
|
||||
<copydir dest="./pbuilds/${pbuild}/working"
|
||||
src="./distfiles/cvs-src/${pbuild}"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="help"
|
||||
description="Display usage synopsis">
|
||||
<echo>
|
||||
Pants usage:
|
||||
|
||||
ant [--usejikes] [-Dpbuild={name}] [-Dpbuild.version={version}]
|
||||
[-D{property}={value}] [-Dno.prompts=true] build | fetch |
|
||||
help | install | uninstall | update | version
|
||||
|
||||
build Build a pbuild and its dependencies
|
||||
fetch Get package only
|
||||
help Display usage synopsis
|
||||
install Fetch, build and install a pbuild
|
||||
uninstall Uninstall a pbuild
|
||||
update Update pbuild(s) to the latest version(s)
|
||||
version Display pbuild version information
|
||||
</echo>
|
||||
</target>
|
||||
|
||||
<!--
|
||||
Install recommended version by default unless 'version' property is set.
|
||||
Do not install if package is already installed.
|
||||
-->
|
||||
<target name="install" depends="-init, build"
|
||||
description="Install a pbuild">
|
||||
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="dist" />
|
||||
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}"
|
||||
target="distclean"
|
||||
/>
|
||||
</target>
|
||||
|
||||
<target name="uninstall" depends="-init"
|
||||
description="Uninstall a pbuild" />
|
||||
|
||||
<target name="update" depends="-init"
|
||||
description="Update pbuild(s) to the latest version(s)">
|
||||
<condition property="${confirm.update}">
|
||||
<equals arg1="${version.using}" arg2="${version.recommended}" />
|
||||
</condition>
|
||||
<antcall target="-updateConfirm" />
|
||||
</target>
|
||||
|
||||
<target name="version"
|
||||
depends="-init, -versionRecommended, -versionUsing, -versionLatest"
|
||||
description="Display pbuild version information">
|
||||
<echo message="Latest version: ${version.recommended}" />
|
||||
<echo message="Latest version: ${version.using}" />
|
||||
<echo message="Latest version: ${version.latest}" />
|
||||
</target>
|
||||
|
||||
</project>
|
45
apps/pants/pants/build.xml
Normal file
45
apps/pants/pants/build.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="build" name="build-pants">
|
||||
|
||||
<target name="build"
|
||||
description="Build the source">
|
||||
<mkdir dir="./java/build"/>
|
||||
<javac srcdir="./java/src" source="1.3" target="1.3" deprecation="on" destdir="./java/build" />
|
||||
</target>
|
||||
|
||||
<target name="clean"
|
||||
description="Remove all object files">
|
||||
<delete dir="./java/build" />
|
||||
<delete dir="./java/jartemp" />
|
||||
</target>
|
||||
|
||||
<target name="dist" depends="build, jar"
|
||||
description="Create the jar and copy it to ../lib">
|
||||
<copy todir="../lib" file="./java/build/pants.jar" />
|
||||
</target>
|
||||
|
||||
<target name="distclean" depends="clean"
|
||||
description="Remove the jar and all object files" >
|
||||
<delete file="../lib/pants.jar" />
|
||||
</target>
|
||||
|
||||
<target name="jar">
|
||||
<delete dir="./java/jartemp" />
|
||||
<mkdir dir="./java/jartemp" />
|
||||
<copy todir="./java/jartemp">
|
||||
<fileset dir="./java/build" includes="**/*.class" />
|
||||
</copy>
|
||||
<jar basedir="./java/jartemp" jarfile="./java/build/pants.jar">
|
||||
<manifest>
|
||||
<section name="net.i2p.pants">
|
||||
<attribute name="Implementation-Title" value="Pants" />
|
||||
<attribute name="Implementation-Version" value="0.0.1" />
|
||||
<attribute name="Implementation-Vendor" value="I2P" />
|
||||
<attribute name="Implementation-Vendor-Id" value="I2P" />
|
||||
<attribute name="Implementation-URL" value="http://www.i2p.net" />
|
||||
</section>
|
||||
</manifest>
|
||||
</jar>
|
||||
<delete dir="./java/jartemp" />
|
||||
</target>
|
||||
</project>
|
212
apps/pants/pants/java/src/net/i2p/pants/MatchTask.java
Normal file
212
apps/pants/pants/java/src/net/i2p/pants/MatchTask.java
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Ports + Ant = Pants, a simple Ant-based package manager
|
||||
*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
*
|
||||
* Written by smeghead in 2005 and released into the public domain with no
|
||||
* warranty of any kind, either expressed or implied. It probably won't make
|
||||
* your computer catch on fire, or eat your children, but it might. Use at your
|
||||
* own risk.
|
||||
*/
|
||||
|
||||
package net.i2p.pants;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
/**
|
||||
* <p>Custom Ant task for matching the contents of a file against a given
|
||||
* regular expression and writing any matching groups to a file in
|
||||
* <code>java.util.Properties</code> format.
|
||||
* </p>
|
||||
* <p>Each key in the properties file is named after the number corresponding to
|
||||
* its matching group and its value is the contents of the matching group.
|
||||
* </p>
|
||||
* <p>Regular expressions passed to this task must conform to the specification
|
||||
* used by Sun's <code>java.util.regex</code> package and thus are mostly
|
||||
* compatible with Perl 5 regular expressions.
|
||||
* </p>
|
||||
* <p>When calling the <code>match</code> task, the attributes
|
||||
* <code>input</code>, <code>output</code>, and <code>regex</code> are required.
|
||||
* </p>
|
||||
* <p>Optional boolean attributes may be used to toggle various modes for the
|
||||
* regular expression engine (all are set to <code>false</code> by default):
|
||||
* </p>
|
||||
* <table>
|
||||
* <tr><td><code>canonicaleq</code></td><td>Enable canonical equivalence</td></tr>
|
||||
* <tr><td><code>caseinsensitive</code></td><td>Enable case-insensitive matching</td></tr>
|
||||
* <tr><td><code>comments</code></td><td>Permit whitespace and comments in pattern</td></tr>
|
||||
* <tr><td><code>dotall</code></td><td>Enable dotall mode</td></tr>
|
||||
* <tr><td><code>multiline</code></td><td>Enable multi-line mode</td></tr>
|
||||
* <tr><td><code>unicodecase</code></td><td>Enable Unicode-aware case folding</td></tr>
|
||||
* <tr><td><code>unixlines</code></td><td>Enable Unix lines mode</td></tr>
|
||||
* </table>
|
||||
* <p>There is one additional optional boolean attribute,
|
||||
* <code>failOnNoMatch</code>. If this attribute is <code>true</code> it causes
|
||||
* the <code>match</code> task to throw a
|
||||
* <code>org.apache.tools.ant.BuildException</code> and fail if no matches for
|
||||
* the regular expression are found. The default value is <code>false</code>,
|
||||
* meaning a failed match will simply result in a warning message to
|
||||
* <code>STDERR</code> and an empty (0 byte) <code>output</code> file being
|
||||
* created.
|
||||
* </p>
|
||||
* <p>
|
||||
* <h4>Example</h4>
|
||||
* </p>
|
||||
* <p>Contents of input file <code>letter.txt</code>:
|
||||
* <pre>
|
||||
* Dear Alice,
|
||||
*
|
||||
* How's about you and me gettin' together for some anonymous foo action?
|
||||
*
|
||||
* Kisses,
|
||||
* Bob
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>Ant <code>match</code> task and a <code>taskdef</code> defining it:
|
||||
* <pre>
|
||||
* <taskdef name="match" classname="net.i2p.pants.MatchTask" classpath="../../lib/pants.jar" />
|
||||
* <match input="letter.txt"
|
||||
* output="matches.txt"
|
||||
* regex="about (\S*?) and (\S*?) .+anonymous (\S*?)"
|
||||
* />
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>Contents of properties file <code>matches.txt</code> written by this task:
|
||||
* <pre>
|
||||
* group.0=about you and me gettin' together for some anonymous foo
|
||||
* group.1=you
|
||||
* group.2=me
|
||||
* group.3=foo
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>These values can be loaded from <code>matches.txt</code> into Ant
|
||||
* properties like so:
|
||||
* <pre>
|
||||
* <loadproperties srcFile="matches.txt" />
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author smeghead
|
||||
*/
|
||||
public class MatchTask extends Task {
|
||||
|
||||
private boolean _failOnNoMatch;
|
||||
private String _inputFile;
|
||||
private String _outputFile;
|
||||
private String _regex;
|
||||
private int _regexFlags;
|
||||
|
||||
public void execute() throws BuildException {
|
||||
int charRead = 0;
|
||||
FileReader fileReader = null;
|
||||
FileWriter fileWriter = null;
|
||||
Matcher matcher = null;
|
||||
Pattern pattern = null;
|
||||
PrintWriter printWriter = null;
|
||||
StringBuffer text = new StringBuffer();
|
||||
|
||||
if (_inputFile == null)
|
||||
throw new BuildException("Error: 'match' task requires 'input' attribute");
|
||||
|
||||
if (_outputFile == null)
|
||||
throw new BuildException("Error: 'match' task requires 'output' attribute");
|
||||
|
||||
if (_regex == null)
|
||||
throw new BuildException("Error: 'match' task requires 'regex' attribute");
|
||||
|
||||
pattern = Pattern.compile(_regex, _regexFlags);
|
||||
|
||||
try {
|
||||
fileReader = new FileReader(_inputFile);
|
||||
|
||||
while ((charRead = fileReader.read()) != -1)
|
||||
text.append((char) charRead);
|
||||
|
||||
fileReader.close();
|
||||
matcher = pattern.matcher(text);
|
||||
|
||||
if (matcher.find()) {
|
||||
printWriter = new PrintWriter(new FileWriter(_outputFile));
|
||||
|
||||
for (int i = 0; i <= matcher.groupCount(); i++)
|
||||
printWriter.println("group." + Integer.toString(i) + "=" + matcher.group(i));
|
||||
|
||||
printWriter.flush();
|
||||
printWriter.close();
|
||||
} else {
|
||||
if (_failOnNoMatch) {
|
||||
throw new BuildException("Error: No matches found in " + _inputFile);
|
||||
} else {
|
||||
System.err.println("Warning: No matches found in " + _inputFile);
|
||||
// Create 0 byte output file.
|
||||
fileWriter = new FileWriter(_outputFile);
|
||||
fileWriter.close();
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
throw new BuildException("File " + _inputFile + " not found", fnfe);
|
||||
} catch (IOException ioe) {
|
||||
throw new BuildException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCanonicalEq(boolean enableCanonicalEq) {
|
||||
if (enableCanonicalEq)
|
||||
_regexFlags |= Pattern.CANON_EQ;
|
||||
}
|
||||
|
||||
public void setCaseInsensitive(boolean enableCaseInsensitive) {
|
||||
if (enableCaseInsensitive)
|
||||
_regexFlags |= Pattern.CASE_INSENSITIVE;
|
||||
}
|
||||
|
||||
public void setComments(boolean enableComments) {
|
||||
if (enableComments)
|
||||
_regexFlags |= Pattern.COMMENTS;
|
||||
}
|
||||
|
||||
public void setDotall(boolean enableDotall) {
|
||||
if (enableDotall)
|
||||
_regexFlags |= Pattern.DOTALL;
|
||||
}
|
||||
|
||||
public void setFailOnNoMatch(boolean failOnNoMatch) {
|
||||
_failOnNoMatch = failOnNoMatch;
|
||||
}
|
||||
|
||||
public void setInput(String inputFile) {
|
||||
_inputFile = inputFile;
|
||||
}
|
||||
|
||||
public void setMultiLine(boolean enableMultiLine) {
|
||||
if (enableMultiLine)
|
||||
_regexFlags |= Pattern.MULTILINE;
|
||||
}
|
||||
|
||||
public void setOutput(String outputFile) {
|
||||
_outputFile = outputFile;
|
||||
}
|
||||
|
||||
public void setRegex(String regex) {
|
||||
_regex = regex;
|
||||
}
|
||||
|
||||
public void setUnicodeCase(boolean enableUnicodeCase) {
|
||||
if (enableUnicodeCase)
|
||||
_regexFlags |= Pattern.UNICODE_CASE;
|
||||
}
|
||||
|
||||
public void setUnixLines(boolean enableUnixLines) {
|
||||
if (enableUnixLines)
|
||||
_regexFlags |= Pattern.UNIX_LINES;
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Ports + Ant = Pants, a simple Ant-based package manager
|
||||
*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
*
|
||||
* Written by smeghead in 2005 and released into the public domain with no
|
||||
* warranty of any kind, either expressed or implied. It probably won't make
|
||||
* your computer catch on fire, or eat your children, but it might. Use at your
|
||||
* own risk.
|
||||
*/
|
||||
|
||||
package net.i2p.pants;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
/**
|
||||
* <p>Custom Ant task for loading properties from a
|
||||
* <code>java.util.Properties</code> file then merging them with lists of
|
||||
* expected properties. When an expected property is found in the properties
|
||||
* file it is set to the value given for it in the file. If an expected property
|
||||
* from a list isn't found in the properties file its value will be set to "" or
|
||||
* "false", depending on the property's data type.
|
||||
* </p>
|
||||
* <p>A property's data type is determined by membership in one of two lists
|
||||
* which can be passed into an instance of this class: a string-typed list and a
|
||||
* boolean-typed list. Values for string-typed properties may be any valid
|
||||
* string accepted by <code>java.util.Properties</code>, and values for
|
||||
* boolean-typed properties must be either "false" or "true".
|
||||
* </p>
|
||||
* <p>Lists holding more than one property must be comma-delimited.
|
||||
* </p>
|
||||
* <p>The output of this class is a temporary <code>java.util.Properties</code>
|
||||
* file which is suitable for reading by the standard Ant
|
||||
* <code>loadproperties</code> task.
|
||||
* </p>
|
||||
* <p>Note that if any properties in the given lists have already been defined
|
||||
* before the <code>mergetypedproperties</code> task is called, their values
|
||||
* cannot be changed since Ant properties are immutable.
|
||||
* </p>
|
||||
* <h4>Example</h4>
|
||||
* </p>
|
||||
* <p>Contents of a properties file <code>my.properties</code>:
|
||||
* <pre>
|
||||
* some.property.exists=true
|
||||
* hasValue=false
|
||||
* some.property=this is a value
|
||||
* property0=bork bork
|
||||
* propertyX=this property wasn't passed in a list
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>Ant <code>mergetypedproperties</code> task and a <code>taskdef</code>
|
||||
* defining it:
|
||||
* <pre>
|
||||
* <taskdef name="mergetypedproperties" classname="net.i2p.pants.MergeTypedPropertiesTask" classpath="../../lib/pants.jar" />
|
||||
* <mergetypedproperties input="my.properties"
|
||||
* output="merged-properties.temp"
|
||||
* booleanList="some.property.exists,is.valid,hasValue"
|
||||
* stringList="some.property,another.property,property0"
|
||||
* />
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>Contents of properties file <code>merged-properties.temp</code> written by this task:
|
||||
* <pre>
|
||||
* some.property.exists=true
|
||||
* is.valid=false
|
||||
* hasValue=false
|
||||
* some.property=this is a value
|
||||
* another.property=
|
||||
* property0=bork bork
|
||||
* propertyX=this property wasn't passed in a list
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>If you don't want this task's output to include properties which weren't
|
||||
* in the lists of expected properties, you can set the attribute
|
||||
* <code>onlyExpected</code> to <code>true</code>. In the example, this would
|
||||
* result in the file <code>merged-properties.temp</code> containing only the
|
||||
* following properties:
|
||||
* <pre>
|
||||
* some.property.exists=true
|
||||
* is.valid=false
|
||||
* hasValue=false
|
||||
* some.property=this is a value
|
||||
* another.property=
|
||||
* property0=bork bork
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author smeghead
|
||||
*/
|
||||
public class MergeTypedPropertiesTask extends Task {
|
||||
|
||||
private String _booleanList = "";
|
||||
private String _inputFile;
|
||||
private boolean _onlyExpected;
|
||||
private String _outputFile;
|
||||
private Properties _propertiesIn = new Properties();
|
||||
private Properties _propertiesOut = new Properties();
|
||||
private String _stringList = "";
|
||||
|
||||
public void execute() throws BuildException {
|
||||
StringTokenizer strtokBoolean = new StringTokenizer(_booleanList, ",");
|
||||
StringTokenizer strtokString = new StringTokenizer(_stringList, ",");
|
||||
String property = "";
|
||||
|
||||
if (_inputFile == null)
|
||||
throw new BuildException("Error: 'mergetypedproperties' task requires 'input' attribute");
|
||||
|
||||
if (_outputFile == null)
|
||||
throw new BuildException("Error: 'mergetypedproperties' task requires 'output' attribute");
|
||||
|
||||
// Add some type-checking on the list elements
|
||||
|
||||
try {
|
||||
_propertiesIn.load(new FileInputStream(_inputFile));
|
||||
|
||||
while (strtokBoolean.hasMoreTokens())
|
||||
_propertiesOut.setProperty(strtokBoolean.nextToken().trim(), "false");
|
||||
|
||||
while (strtokString.hasMoreTokens())
|
||||
_propertiesOut.setProperty(strtokString.nextToken().trim(), "");
|
||||
|
||||
for (Enumeration enum = _propertiesIn.elements(); enum.hasMoreElements(); ) {
|
||||
property = (String) enum.nextElement();
|
||||
|
||||
if (_onlyExpected && !_propertiesOut.containsKey(property))
|
||||
continue;
|
||||
else
|
||||
_propertiesOut.setProperty(property, _propertiesIn.getProperty(property));
|
||||
}
|
||||
|
||||
_propertiesOut.store(new FileOutputStream(_inputFile), "This is a temporary file. It is safe to delete it.");
|
||||
} catch (IOException ioe) {
|
||||
throw new BuildException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBooleanList(String booleanList) {
|
||||
_booleanList = booleanList;
|
||||
}
|
||||
|
||||
public void setInput(String inputFile) {
|
||||
_inputFile = inputFile;
|
||||
}
|
||||
|
||||
public void setOnlyExpected(boolean onlyExpected) {
|
||||
_onlyExpected = onlyExpected;
|
||||
}
|
||||
|
||||
public void setOutput(String outputFile) {
|
||||
_outputFile = outputFile;
|
||||
}
|
||||
|
||||
public void setStringList(String stringList) {
|
||||
_stringList = stringList;
|
||||
}
|
||||
}
|
116
apps/pants/pants/resources/README
Normal file
116
apps/pants/pants/resources/README
Normal file
@ -0,0 +1,116 @@
|
||||
What is Pants?
|
||||
--------------
|
||||
|
||||
Pants is an Apache Ant-based package manager for the management of 3rd party
|
||||
dependencies in Java development projects. It's loosely modeled after
|
||||
FreeBSD's Ports and Gentoo Linux's Portage, with two major differences:
|
||||
|
||||
* Pants isn't intended for system-wide package management. It's tailored for
|
||||
per-project 3rd party package management. You will typically have one
|
||||
Pants repository per project and each repository will be located somewhere
|
||||
under your project's root directory. If you're familiar with Ports or
|
||||
Portage, a Pants repository is roughly analogous to /usr/ports or
|
||||
/usr/portage.
|
||||
|
||||
* Pants is extremely portable. It goes anywhere Apache Ant goes.
|
||||
|
||||
Pants takes a modular approach to the standard Ant buildfile, breaking it
|
||||
into 3 files for functionality and convenience:
|
||||
|
||||
1. The Pants public interface, pants/build.xml, provides a single consistent
|
||||
way to access and manipulate dependency packages and relieves some of the
|
||||
developer's burden by providing implementations for some frequently-used
|
||||
and complex Ant operations.
|
||||
|
||||
2. pbuild.xml is a specially-structured and slimmed-down Ant buildfile in
|
||||
which you implement custom handling for a package your project depends
|
||||
on. This is known as the "pbuild" and is roughly analogous to a FreeBSD
|
||||
port or a Gentoo ebuild. A fairly explanatory template for pbuilds,
|
||||
pbuild.template.xml, is provided.
|
||||
|
||||
3. pbuild.properties contains those properties for a specific pbuild which
|
||||
are most likely to change over time. It uses the java.util.Properties
|
||||
format which is more human-friendly for hand-editing than Ant/XML. A
|
||||
fairly explanatory template, pbuild.template.properties, is provided.
|
||||
|
||||
There is one more file that completes the Pants system: the metadata file
|
||||
pants/world is a database for keeping track of all packages managed by Pants
|
||||
for your project.
|
||||
|
||||
Pants automatically handles versioning for your project's dependency
|
||||
packages and keeps track of their recommended versions, currently used
|
||||
versions, and latest available versions. This makes it extremely simple for
|
||||
project developers to switch back and forth between different versions of a
|
||||
dependency, and makes it just as easy to update a dependency. You can even
|
||||
update all your project's Pants-managed packages with a single command.
|
||||
|
||||
Pbuilds are designed to automatically handle the downloading, building,
|
||||
repackaging and deployment of source archives, binary archives, and CVS
|
||||
sources, all in a manner that's completely transparent to the project
|
||||
developer. Pbuilds currently support tar + gzip, tar + bzip2, and zip
|
||||
archives.
|
||||
|
||||
Because it is based on Ant, Pants integrates very well with Ant buildfiles
|
||||
and will fit easily into your project's Ant build framework. However, its
|
||||
interface is simple enough to be called just as easily by more traditional
|
||||
build systems such as GNU Make.
|
||||
|
||||
|
||||
Why Should I Use Pants?
|
||||
-----------------------
|
||||
|
||||
There are many applications for Pants, but a few use cases should best serve
|
||||
to illustrate its usefulness:
|
||||
|
||||
1. You have a project that you ship with several 3rd party libraries but the
|
||||
versions you're using are stale. With a single command, Pants can
|
||||
automatically discover the latest release versions for all of these, then
|
||||
download, build, and place the fresh libraries where your project's main
|
||||
build system expects them to be at build time.
|
||||
|
||||
2. You want to test multiple versions of a 3rd party library against your
|
||||
project. Pants only requires you to issue a single command to switch
|
||||
library versions, so can spend more time testing and less time hunting
|
||||
packages down, unpackaging them, symlinking, etc.
|
||||
|
||||
3. Pants is public domain. You can ship it with your project if you need to
|
||||
without having to worry about petty intellectual property or licensing
|
||||
issues.
|
||||
|
||||
|
||||
Minimum Requirements
|
||||
--------------------
|
||||
|
||||
* Apache Ant 1.6.2 or higher is recommended
|
||||
|
||||
* Any Java runtime and operating system that will run Ant
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Not finished yet.
|
||||
|
||||
|
||||
Why the Silly Name?
|
||||
-------------------
|
||||
|
||||
Ports + Ant = Pants. Any other explanation is purely a product of your
|
||||
twisted imagination.
|
||||
|
||||
|
||||
Miscellaneous Pocket Fluff
|
||||
--------------------------
|
||||
|
||||
Author: smeghead <smeghead@i2pmail.org> <smeghead@mail.i2p>
|
||||
|
||||
License: No license necessary. This work is released into the public domain.
|
||||
|
||||
Price: Free! But if you really appreciate Pants, or you're just a sicko,
|
||||
please send me a picture of your worst or most unusual pair of
|
||||
pants so I can add it to the Whirling Hall of Pants on pants.i2p,
|
||||
the official Pants eepsite (that's an anonymous website on I2P--see
|
||||
http://www.i2p.net for more information).
|
||||
|
||||
|
||||
$Id$
|
110
apps/pants/pants/resources/pbuild.template.properties
Normal file
110
apps/pants/pants/resources/pbuild.template.properties
Normal file
@ -0,0 +1,110 @@
|
||||
# The properties defined in this file can be overridden on the command line by
|
||||
# passing them in as parameters like so:
|
||||
#
|
||||
# ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
|
||||
#
|
||||
# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
|
||||
|
||||
|
||||
# Recommended Package Version
|
||||
#
|
||||
# Set this property's value to the package version you want Pants to use for the
|
||||
# pbuild by default. The version string specified must match the version
|
||||
# substring from the package's filename if the filename contains a version
|
||||
# number.
|
||||
#
|
||||
# Comment out this property to force use of the latest available version.
|
||||
#
|
||||
# If the pbuild is CVS-based rather than package-based, this property must be
|
||||
# set to 'CVS'.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# version.recommended=2.0.4
|
||||
|
||||
|
||||
# Latest Package Version
|
||||
#
|
||||
# There are currently two ways to inform Pants of the latest version number for
|
||||
# your package.
|
||||
#
|
||||
# Method 1: Manually modify the property 'version.latest' to reflect the latest
|
||||
# version number.
|
||||
#
|
||||
# Method 2: Provide a URL for a page on the package's website and a regular
|
||||
# expression with which to parse it in order to extract the version
|
||||
# number of the latest available package. For this you must define the
|
||||
# properties 'version.latest.find.url', 'version.latest.find.regex',
|
||||
# and any regular expression engine mode flags needed. The pattern
|
||||
# defined must have exactly one capturing group to encapsulate the
|
||||
# version string, otherwise the operation will fail.
|
||||
#
|
||||
# You may use both methods, in which case the version number specified by Method
|
||||
# 1 will be used as the fallback value if Method 2 for some reason is
|
||||
# unsuccessful.
|
||||
#
|
||||
# If neither method is enabled here or they fail to return a valid value to
|
||||
# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
|
||||
# the pbuild is CVS-based (none of the version.latest.* properties are used by
|
||||
# CVS-based pbuilds).
|
||||
#
|
||||
# The following is a list of boolean properties for optional mode flags used by
|
||||
# the regular expression engine. Set a value of "true" for any you wish to use.
|
||||
#
|
||||
# version.latest.find.regex.canonicaleq - Enable canonical equivalence
|
||||
# version.latest.find.regex.caseinsensitive - Enable case-insensitive matching
|
||||
# version.latest.find.regex.comments - Permit whitespace and comments
|
||||
# version.latest.find.regex.dotall - Enable dotall mode
|
||||
# version.latest.find.regex.multiline - Enable multi-line mode
|
||||
# version.latest.find.regex.unicodecase - Enable Unicode-aware case folding
|
||||
# version.latest.find.regex.unixlines - Enable Unix lines mode
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# version.latest=5.1.2
|
||||
# version.latest.find.url=http://sourceforge.net/projects/jetty/
|
||||
# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
|
||||
|
||||
|
||||
# Package URL
|
||||
#
|
||||
# Specify the URL pointing to the pbuild's package from here. The token
|
||||
# '${pbuild.version}' if used will automatically be expanded to the appropriate
|
||||
# version string.
|
||||
#
|
||||
# The package URL property is not used by CVS-based pbuilds.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
|
||||
# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
|
||||
|
||||
|
||||
# CVS Repository
|
||||
#
|
||||
# The values expected for CVS properties here are the same as those expected by
|
||||
# their corresponding Apache Ant 'Cvs' task attributes. For details see:
|
||||
#
|
||||
# http://ant.apache.org/manual/CoreTasks/cvs.html
|
||||
#
|
||||
# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
|
||||
# The following is a list of all valid CVS properties for Pants (and their
|
||||
# default values if applicable):
|
||||
#
|
||||
# cvs.compression.level
|
||||
# cvs.date
|
||||
# cvs.package
|
||||
# cvs.passfile=~/.cvspass
|
||||
# cvs.port=2401
|
||||
# cvs.root
|
||||
# cvs.rsh
|
||||
# cvs.tag
|
||||
#
|
||||
# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
|
||||
# cvs.rsh=ssh
|
||||
# cvs.package=borkbork
|
||||
|
69
apps/pants/pants/resources/pbuild.template.xml
Normal file
69
apps/pants/pants/resources/pbuild.template.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
This is a template for Pants pbuilds. Pbuilds use standard Apache Ant syntax.
|
||||
For each target in the Public Interface section you must provide either an
|
||||
implementation or a stub. You may also add your own custom tasks and
|
||||
properties to this file. Be careful that none of your custom properties'
|
||||
names clash with the properties defined in pants/build.xml.
|
||||
-->
|
||||
|
||||
<project basedir="." default="build" name="name-of-pbuild-here">
|
||||
|
||||
<!-- ....................... Begin Public Interface ........................ -->
|
||||
|
||||
<!--
|
||||
When this target is called, the pbuild's sources and/or binaries have
|
||||
already been extracted/copied by Pants into the pbuild's working/
|
||||
subdirectory. This target must prepare those sources and/or binaries in
|
||||
the working/ subdirectory into deployable form, for example by building
|
||||
all necessary classes and jar files.
|
||||
|
||||
This target must not create or modify any files outside the pbuild's
|
||||
working/ subdirectory. (An automatic sandboxing mechanism should be added
|
||||
to Pants at some point.) It is however acceptable for a task called by
|
||||
'builddep' to modify files outside of this pbuild's working/ directory.
|
||||
-->
|
||||
<target name="build" depends="builddep" />
|
||||
|
||||
<!--
|
||||
Use this to call targets from other pbuilds, Ant buildfiles, Makefiles,
|
||||
etc. which perform tasks this pbuild's 'build' target depends on. If other
|
||||
pbuilds are called here, they must be called through the Pants interface
|
||||
or else it may leave Pants in an inconsistent state.
|
||||
|
||||
Most pbuilds probably won't need to implement this target.
|
||||
-->
|
||||
<target name="builddep" />
|
||||
|
||||
<!--
|
||||
This target must undo the actions performed by the 'build' target.
|
||||
-->
|
||||
<target name="clean" depends="depclean" />
|
||||
|
||||
<!--
|
||||
If the 'builddep' target is implemented, this target must be implemented
|
||||
to undo its actions.
|
||||
-->
|
||||
<target name="depclean" />
|
||||
|
||||
<!--
|
||||
This target must copy all deployable files generated by the 'build' target
|
||||
into the pbuild's dist/ subdirectory (for use by other pbuilds or Ant
|
||||
processes) or to their final deployment locations outside the pants/
|
||||
directory hierarchy. Note that the latter may require the user to gain
|
||||
superuser/admin privileges.
|
||||
-->
|
||||
<target name="dist" depends="build" />
|
||||
|
||||
<!--
|
||||
This target must remove all files from the pbuild's dist/ subdirectory
|
||||
and final deployment locations, reversing the actions of the 'dist'
|
||||
target. Note that removal of files from their final deployment locations
|
||||
may require the user to gain superuser/admin privileges.
|
||||
-->
|
||||
<target name="distclean" depends="clean" />
|
||||
|
||||
<!-- ........................ End Public Interface ......................... -->
|
||||
|
||||
</project>
|
112
apps/pants/pbuilds/fortuna/pbuild.properties
Normal file
112
apps/pants/pbuilds/fortuna/pbuild.properties
Normal file
@ -0,0 +1,112 @@
|
||||
# The properties defined in this file can be overridden on the command line by
|
||||
# passing them in as parameters like so:
|
||||
#
|
||||
# ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
|
||||
#
|
||||
# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
|
||||
|
||||
|
||||
# Recommended Package Version
|
||||
#
|
||||
# Set this property's value to the package version you want Pants to use for the
|
||||
# pbuild by default. The version string specified must match the version
|
||||
# substring from the package's filename if the filename contains a version
|
||||
# number.
|
||||
#
|
||||
# Comment out this property to force use of the latest available version.
|
||||
#
|
||||
# If the pbuild is CVS-based rather than package-based, this property must be
|
||||
# set to 'CVS'.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# version.recommended=2.0.4
|
||||
version.recommended=CVS
|
||||
|
||||
# Latest Package Version
|
||||
#
|
||||
# There are currently two ways to inform Pants of the latest version number for
|
||||
# your package.
|
||||
#
|
||||
# Method 1: Manually modify the property 'version.latest' to reflect the latest
|
||||
# version number.
|
||||
#
|
||||
# Method 2: Provide a URL for a page on the package's website and a regular
|
||||
# expression with which to parse it in order to extract the version
|
||||
# number of the latest available package. For this you must define the
|
||||
# properties 'version.latest.find.url', 'version.latest.find.regex',
|
||||
# and any regular expression engine mode flags needed. The pattern
|
||||
# defined must have exactly one capturing group to encapsulate the
|
||||
# version string, otherwise the operation will fail.
|
||||
#
|
||||
# You may use both methods, in which case the version number specified by Method
|
||||
# 1 will be used as the fallback value if Method 2 for some reason is
|
||||
# unsuccessful.
|
||||
#
|
||||
# If neither method is enabled here or they fail to return a valid value to
|
||||
# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
|
||||
# the pbuild is CVS-based (none of the version.latest.* properties are used by
|
||||
# CVS-based pbuilds).
|
||||
#
|
||||
# The following is a list of boolean properties for optional mode flags used by
|
||||
# the regular expression engine. Set a value of "true" for any you wish to use.
|
||||
#
|
||||
# version.latest.find.regex.canonicaleq - Enable canonical equivalence
|
||||
# version.latest.find.regex.caseinsensitive - Enable case-insensitive matching
|
||||
# version.latest.find.regex.comments - Permit whitespace and comments
|
||||
# version.latest.find.regex.dotall - Enable dotall mode
|
||||
# version.latest.find.regex.multiline - Enable multi-line mode
|
||||
# version.latest.find.regex.unicodecase - Enable Unicode-aware case folding
|
||||
# version.latest.find.regex.unixlines - Enable Unix lines mode
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# version.latest=5.1.2
|
||||
# version.latest.find.url=http://sourceforge.net/projects/jetty/
|
||||
# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
|
||||
|
||||
|
||||
# Package URL
|
||||
#
|
||||
# Specify the URL pointing to the pbuild's package from here. The token
|
||||
# '${pbuild.version}' if used will automatically be expanded to the appropriate
|
||||
# version string.
|
||||
#
|
||||
# The package URL property is not used by CVS-based pbuilds.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
|
||||
# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
|
||||
|
||||
|
||||
# CVS Repository
|
||||
#
|
||||
# The values expected for CVS properties here are the same as those expected by
|
||||
# their corresponding Apache Ant 'Cvs' task attributes. For details see:
|
||||
#
|
||||
# http://ant.apache.org/manual/CoreTasks/cvs.html
|
||||
#
|
||||
# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
|
||||
# The following is a list of all valid CVS properties for Pants (and their
|
||||
# default values if applicable):
|
||||
#
|
||||
# cvs.compression.level
|
||||
# cvs.date
|
||||
# cvs.package
|
||||
# cvs.passfile=~/.cvspass
|
||||
# cvs.port=2401
|
||||
# cvs.root
|
||||
# cvs.rsh
|
||||
# cvs.tag
|
||||
#
|
||||
# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
|
||||
# cvs.rsh=ssh
|
||||
# cvs.package=borkbork
|
||||
cvs.root=:ext:anoncvs@savannah.gnu.org:/cvsroot/gnu-crypto
|
||||
cvs.rsh=ssh
|
||||
cvs.package=gnu-crypto
|
127
apps/pants/pbuilds/fortuna/pbuild.xml
Normal file
127
apps/pants/pbuilds/fortuna/pbuild.xml
Normal file
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="build" name="fortuna-pbuild">
|
||||
|
||||
<property name="gnucrypt.base.dir" value="./working/gnu-crypto" />
|
||||
<property name="gnucrypt.etc.dir" value="${gnucrypt.base.dir}/etc" />
|
||||
<property name="gnucrypt.lib.dir" value="${gnucrypt.base.dir}/lib" />
|
||||
<property name="gnucrypt.object.dir" value="${gnucrypt.base.dir}/classes" />
|
||||
<property name="gnucrypt.base.crypto.object.dir" value="${gnucrypt.object.dir}/gnu/crypto" />
|
||||
<property name="gnucrypt.cipher.object.dir" value="${gnucrypt.base.crypto.object.dir}/cipher" />
|
||||
<property name="gnucrypt.hash.object.dir" value="${gnucrypt.base.crypto.object.dir}/hash" />
|
||||
<property name="gnucrypt.prng.object.dir" value="${gnucrypt.base.crypto.object.dir}/prng" />
|
||||
|
||||
<patternset id="fortuna.files">
|
||||
<include name="${gnucrypt.base.crypto.object.dir}/Registry.class" />
|
||||
<include name="${gnucrypt.prng.object.dir}/Fortuna*.class" />
|
||||
<include name="${gnucrypt.prng.object.dir}/BasePRNG.class" />
|
||||
<include name="${gnucrypt.prng.object.dir}/RandomEventListener.class" />
|
||||
<include name="${gnucrypt.prng.object.dir}/IRandom.class" />
|
||||
<include name="${gnucrypt.cipher.object.dir}/CipherFactory.class" />
|
||||
<include name="${gnucrypt.cipher.object.dir}/IBlockCipher.class" />
|
||||
<include name="${gnucrypt.hash.object.dir}/HashFactory.class" />
|
||||
<include name="${gnucrypt.hash.object.dir}/IMessageDigest.class" />
|
||||
</patternset>
|
||||
|
||||
<!--
|
||||
Add this when Fortuna tests are added to GNU Crypto, else write some
|
||||
-->
|
||||
<target name="-test" />
|
||||
|
||||
<!-- ....................... Begin Public Interface ........................ -->
|
||||
|
||||
<!--
|
||||
When this target is called, the pbuild's sources and/or binaries have
|
||||
already been extracted/copied by Pants into the pbuild's working/
|
||||
subdirectory. This target must prepare those sources and/or binaries in
|
||||
the working/ subdirectory into deployable form, for example by building
|
||||
all necessary classes and jar files.
|
||||
|
||||
This target must not create or modify any files outside the pbuild's
|
||||
working/ subdirectory. (An automatic sandboxing mechanism should be added
|
||||
to Pants at some point.) It is however acceptable for a task called by
|
||||
'builddep' to modify files outside of this pbuild's working/ directory.
|
||||
-->
|
||||
<target name="build" depends="builddep">
|
||||
<delete dir="./working/build" />
|
||||
<delete dir="./working/jartemp" />
|
||||
<mkdir dir="./working/build" />
|
||||
<mkdir dir="./working/jartemp/${gnucrypt.object.dir}" />
|
||||
<copy todir="./working/jartemp">
|
||||
<fileset dir=".">
|
||||
<patternset refid="fortuna.files" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<jar basedir="./working/jartemp/${gnucrypt.object.dir}" jarfile="./working/build/fortuna.jar">
|
||||
<manifest>
|
||||
<section name="fortuna">
|
||||
<attribute name="Implementation-Title" value="I2P Custom GNU Crypto Fortuna Library" />
|
||||
<attribute name="Implementation-Version" value="CVS HEAD" />
|
||||
<attribute name="Implementation-Vendor" value="Free Software Foundation" />
|
||||
<attribute name="Implementation-Vendor-Id" value="FSF" />
|
||||
<attribute name="Implementation-URL" value="http://www.gnu.org/software/gnu-crypto" />
|
||||
</section>
|
||||
</manifest>
|
||||
</jar>
|
||||
<delete dir="./working/jartemp" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
Use this to call targets from other pbuilds, Ant buildfiles, Makefiles,
|
||||
etc. which perform tasks this pbuild's 'build' target depends on. If other
|
||||
pbuilds are called here, they must be called through the Pants interface
|
||||
or else it may leave Pants in an inconsistent state.
|
||||
|
||||
Most pbuilds probably won't need to implement this target.
|
||||
-->
|
||||
<target name="builddep">
|
||||
<ant dir="${gnucrypt.base.dir}" target="jar" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
This target must undo the actions performed by the 'build' target.
|
||||
-->
|
||||
<target name="clean" depends="depclean">
|
||||
<delete dir="./working/jartemp" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
If the 'builddep' target is implemented, this target must be implemented
|
||||
to undo its actions.
|
||||
-->
|
||||
<target name="depclean">
|
||||
<!--
|
||||
Annoyingly the GNU Crypto distclean task called here doesn't clean
|
||||
*all* derived files from java/gnu-crypto/lib like it should (because
|
||||
a couple of lines are commented out).....
|
||||
-->
|
||||
<ant dir="${gnucrypt.base.dir}" target="distclean" />
|
||||
<!--
|
||||
.....and so we mop up the rest ourselves.
|
||||
-->
|
||||
<delete dir="${gnucrypt.lib.dir}" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
This target must copy all deployable files generated by the 'build' target
|
||||
into the pbuild's dist/ subdirectory (for use by other pbuilds or Ant
|
||||
processes) or to their final deployment locations outside the pants/
|
||||
directory hierarchy. Note that the latter may require the user to gain
|
||||
superuser/admin privileges.
|
||||
-->
|
||||
<target name="dist" depends="build">
|
||||
<copy todir="./dist/fortuna.jar" file="./working/build/fortuna.jar" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
This target must remove all files from the pbuild's dist/ subdirectory
|
||||
and final deployment locations, reversing the actions of the 'dist'
|
||||
target. Note that removal of files from their final deployment locations
|
||||
may require the user to gain superuser/admin privileges.
|
||||
-->
|
||||
<target name="distclean" depends="clean">
|
||||
<delete file="./dist/fortuna.jar" />
|
||||
</target>
|
||||
|
||||
<!-- ........................ End Public Interface ......................... -->
|
||||
|
||||
</project>
|
112
apps/pants/pbuilds/jetty/pbuild.properties
Normal file
112
apps/pants/pbuilds/jetty/pbuild.properties
Normal file
@ -0,0 +1,112 @@
|
||||
# The properties defined in this file can be overridden on the command line by
|
||||
# passing them in as parameters like so:
|
||||
#
|
||||
# ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
|
||||
#
|
||||
# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
|
||||
|
||||
|
||||
# Recommended Package Version
|
||||
#
|
||||
# Set this property's value to the package version you want Pants to use for the
|
||||
# pbuild by default. The version string specified must match the version
|
||||
# substring from the package's filename if the filename contains a version
|
||||
# number.
|
||||
#
|
||||
# Comment out this property to force use of the latest available version.
|
||||
#
|
||||
# If the pbuild is CVS-based rather than package-based, this property must be
|
||||
# set to 'CVS'.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# version.recommended=2.0.4
|
||||
version.recommended=5.1.2
|
||||
|
||||
# Latest Package Version
|
||||
#
|
||||
# There are currently two ways to inform Pants of the latest version number for
|
||||
# your package.
|
||||
#
|
||||
# Method 1: Manually modify the property 'version.latest' to reflect the latest
|
||||
# version number.
|
||||
#
|
||||
# Method 2: Provide a URL for a page on the package's website and a regular
|
||||
# expression with which to parse it in order to extract the version
|
||||
# number of the latest available package. For this you must define the
|
||||
# properties 'version.latest.find.url', 'version.latest.find.regex',
|
||||
# and any regular expression engine mode flags needed. The pattern
|
||||
# defined must have exactly one capturing group to encapsulate the
|
||||
# version string, otherwise the operation will fail.
|
||||
#
|
||||
# You may use both methods, in which case the version number specified by Method
|
||||
# 1 will be used as the fallback value if Method 2 for some reason is
|
||||
# unsuccessful.
|
||||
#
|
||||
# If neither method is enabled here or they fail to return a valid value to
|
||||
# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
|
||||
# the pbuild is CVS-based (none of the version.latest.* properties are used by
|
||||
# CVS-based pbuilds).
|
||||
#
|
||||
# The following is a list of boolean properties for optional mode flags used by
|
||||
# the regular expression engine. Set a value of "true" for any you wish to use.
|
||||
#
|
||||
# version.latest.find.regex.canonicaleq - Enable canonical equivalence
|
||||
# version.latest.find.regex.caseinsensitive - Enable case-insensitive matching
|
||||
# version.latest.find.regex.comments - Permit whitespace and comments
|
||||
# version.latest.find.regex.dotall - Enable dotall mode
|
||||
# version.latest.find.regex.multiline - Enable multi-line mode
|
||||
# version.latest.find.regex.unicodecase - Enable Unicode-aware case folding
|
||||
# version.latest.find.regex.unixlines - Enable Unix lines mode
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# version.latest=5.1.2
|
||||
# version.latest.find.url=http://sourceforge.net/projects/jetty/
|
||||
# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
|
||||
version.latest=5.1.2
|
||||
version.latest.find.url=http://sourceforge.net/projects/jetty/
|
||||
version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
|
||||
|
||||
# Package URL
|
||||
#
|
||||
# Specify the URL pointing to the pbuild's package from here. The token
|
||||
# '${pbuild.version}' if used will automatically be expanded to the appropriate
|
||||
# version string.
|
||||
#
|
||||
# The package URL property is not used by CVS-based pbuilds.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
|
||||
# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
|
||||
package.url=http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-${pbuild.version}.zip
|
||||
|
||||
# CVS Repository
|
||||
#
|
||||
# The values expected for CVS properties here are the same as those expected by
|
||||
# their corresponding Apache Ant 'Cvs' task attributes. For details see:
|
||||
#
|
||||
# http://ant.apache.org/manual/CoreTasks/cvs.html
|
||||
#
|
||||
# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
|
||||
# The following is a list of all valid CVS properties for Pants (and their
|
||||
# default values if applicable):
|
||||
#
|
||||
# cvs.compression.level
|
||||
# cvs.date
|
||||
# cvs.package
|
||||
# cvs.passfile=~/.cvspass
|
||||
# cvs.port=2401
|
||||
# cvs.root
|
||||
# cvs.rsh
|
||||
# cvs.tag
|
||||
#
|
||||
# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
|
||||
# cvs.rsh=ssh
|
||||
# cvs.package=borkbork
|
||||
|
89
apps/pants/pbuilds/jetty/pbuild.xml
Normal file
89
apps/pants/pbuilds/jetty/pbuild.xml
Normal file
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="jetty">
|
||||
|
||||
<!-- make this generic, place variables in properties file -->
|
||||
|
||||
<target name="all" depends="build"
|
||||
description="Run the build target" />
|
||||
|
||||
<target name="assignProperties" if="group.0">
|
||||
<property name="latest.jetty.version" value="${group.1}" />
|
||||
<available property="jetty.package.available" file="jetty-${latest.jetty.version}.zip" />
|
||||
<available property="jetty.package.unpacked.available" file="jettypkg/jetty-${latest.jetty.version}" />
|
||||
<echo message="Properties assigned" />
|
||||
</target>
|
||||
|
||||
<target name="build" depends="init, unpackJettyPackage" if="latest.jetty.version"
|
||||
description="Download latest Jetty package and copy needed libs to jettylib/">
|
||||
<property name="unpack.dir" value="jettypkg/jetty-${latest.jetty.version}" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/ant.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/jasper-compiler.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/jasper-runtime.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/xercesImpl.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/xml-apis.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/extra/lib/org.mortbay.jetty-jdk1.2.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/lib/javax.servlet.jar" />
|
||||
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/lib/org.mortbay.jetty.jar" />
|
||||
<copy todir="jettylib" overwrite="true">
|
||||
<fileset dir="${unpack.dir}/ext" includes="xmlParserAPIs*.jar" />
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="builddep"
|
||||
description="Build the custom helper Ant task for this buildfile">
|
||||
<mkdir dir="java/build"/>
|
||||
<javac srcdir="./java/src" source="1.3" target="1.3" deprecation="on" destdir="./java/build" />
|
||||
</target>
|
||||
|
||||
<target name="clean"
|
||||
description="Remove temp files and zip only; jettypkg/ requires manual deletion">
|
||||
<echo message="Not actually deleting the Jetty package directory since it's so large" />
|
||||
<delete>
|
||||
<fileset dir="." includes="*.zip jettytemp.html parsed.temp" />
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
<target name="cleandep"
|
||||
description="Remove custom helper Ant task">
|
||||
<delete dir="java/build" />
|
||||
</target>
|
||||
|
||||
<target name="compile" />
|
||||
|
||||
<target name="distclean" depends="clean"
|
||||
description="Remove temp files, zip and jettylib/ contents" >
|
||||
<delete>
|
||||
<fileset dir="jettylib" includes="*.jar"/>
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
<target name="fetchJettyPackage" if="latest.jetty.version" unless="jetty.package.available">
|
||||
<echo message="The Jetty libs are not necessary for using I2P, but are used by some" />
|
||||
<echo message="applications on top of I2P such as the routerconsole." />
|
||||
<get src="http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-${latest.jetty.version}.zip" verbose="true" dest="jetty-${latest.jetty.version}.zip" />
|
||||
</target>
|
||||
|
||||
<target name="init" depends="builddep">
|
||||
<echo message="Checking SourceForge for latest Jetty version....." />
|
||||
<get src="http://sourceforge.net/projects/jetty/" dest="jettytemp.html" verbose="true" />
|
||||
<taskdef name="match" classname="net.i2p.pants.MatchTask" classpath="../../lib/pants.jar" />
|
||||
<match input="jettytemp.html"
|
||||
output="parsed.temp"
|
||||
regex="Stable.+?Jetty-(.+?)</A>"
|
||||
/>
|
||||
<loadproperties srcFile="parsed.temp" />
|
||||
<antcall target="assignProperties" />
|
||||
</target>
|
||||
|
||||
<target name="jar" />
|
||||
|
||||
<target name="showlatest" depends="init"
|
||||
description="Display latest version number for Jetty">
|
||||
<echo message="Latest Jetty version: ${latest.jetty.version}" />
|
||||
</target>
|
||||
|
||||
<target name="unpackJettyPackage" depends="fetchJettyPackage" if="latest.jetty.version" unless="jetty.package.unpacked.available">
|
||||
<mkdir dir="jettypkg" />
|
||||
<unzip src="jetty-${latest.jetty.version}.zip" dest="jettypkg" />
|
||||
</target>
|
||||
</project>
|
2
apps/pants/world
Normal file
2
apps/pants/world
Normal file
@ -0,0 +1,2 @@
|
||||
version.using.fortuna=CVS
|
||||
version.using.jetty=5.1.2
|
@ -20,7 +20,7 @@
|
||||
<classpath>
|
||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
||||
<pathelement location="../../../router/java/build/router.jar" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty-jdk1.2.jar" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../systray/java/build/systray.jar" />
|
||||
<pathelement location="../../systray/java/lib/systray4j.jar" />
|
||||
@ -48,20 +48,24 @@
|
||||
<mkdir dir="../jsp/WEB-INF/" />
|
||||
<mkdir dir="../jsp/WEB-INF/classes" />
|
||||
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
|
||||
<java classname="org.apache.jasper.JspC" fork="true" >
|
||||
<java classname="org.apache.jasper.JspC" fork="true">
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-el.jar" />
|
||||
<pathelement location="../../jetty/jettylib/ant.jar" />
|
||||
<pathelement location="../../systray/java/build/obj" />
|
||||
<pathelement location="../../systray/java/lib/systray4j.jar" /> <!-- some javacs resolve recursively... -->
|
||||
<pathelement location="../../../installer/lib/wrapper/win32/wrapper.jar" /> <!-- we dont care if we're not on win32 -->
|
||||
<pathelement location="../../systray/java/lib/systray4j.jar" />
|
||||
<pathelement location="../../../installer/lib/wrapper/win32/wrapper.jar" />
|
||||
<pathelement location="build/routerconsole.jar" />
|
||||
<pathelement location="../../../router/java/build/router.jar" />
|
||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
||||
</classpath>
|
||||
<arg value="-d" />
|
||||
<arg value="../jsp/WEB-INF/classes" />
|
||||
<arg value="-v9" />
|
||||
<arg value="-v" />
|
||||
<arg value="-p" />
|
||||
<arg value="net.i2p.router.web.jsp" />
|
||||
<arg value="-webinc" />
|
||||
@ -69,10 +73,13 @@
|
||||
<arg value="-webapp" />
|
||||
<arg value="../jsp/" />
|
||||
</java>
|
||||
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="*.java">
|
||||
|
||||
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
|
||||
<pathelement location="../../jetty/jettylib/commons-el.jar" />
|
||||
<pathelement location="build/routerconsole.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
|
@ -1,84 +0,0 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the client config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigClientsHandler extends FormHandler {
|
||||
private String _numClients;
|
||||
private String _numTunnels;
|
||||
private String _numHops;
|
||||
private String _numHopsOutbound;
|
||||
private boolean _shouldSave;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_shouldSave = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
|
||||
public void setClientcount(String num) {
|
||||
_numClients = (num != null ? num.trim(): null);
|
||||
}
|
||||
public void setTunnelcount(String num) {
|
||||
_numTunnels = (num != null ? num.trim() : null);
|
||||
}
|
||||
public void setTunneldepth(String num) {
|
||||
_numHops = (num != null ? num.trim() : null);
|
||||
}
|
||||
public void setTunneldepthoutbound(String num) {
|
||||
_numHopsOutbound = (num != null ? num.trim() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the network config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
boolean saveRequired = false;
|
||||
|
||||
if ( (_numClients != null) && (_numClients.length() > 0) ) {
|
||||
_context.router().setConfigSetting("router.targetClients", _numClients);
|
||||
addFormNotice("Updating estimated number of clients to " + _numClients);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if ( (_numTunnels != null) && (_numTunnels.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ClientTunnelSettings.PROP_NUM_INBOUND, _numTunnels);
|
||||
addFormNotice("Updating default number of tunnels per client to " + _numTunnels);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if ( (_numHops != null) && (_numHops.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ClientTunnelSettings.PROP_DEPTH_INBOUND, _numHops);
|
||||
addFormNotice("Updating default tunnel length to " + _numHops);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if ( (_numHopsOutbound != null) && (_numHopsOutbound.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ClientTunnelSettings.PROP_DEPTH_OUTBOUND, _numHopsOutbound);
|
||||
addFormNotice("Updating default outbound tunnel length to " + _numHopsOutbound);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if (saveRequired) {
|
||||
boolean saved = _context.router().saveConfig();
|
||||
if (saved)
|
||||
addFormNotice("Configuration saved successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
public class ConfigClientsHelper {
|
||||
private RouterContext _context;
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** copied from the package private {@link net.i2p.router.tunnelmanager.TunnelPool} */
|
||||
public final static String TARGET_CLIENTS_PARAM = "router.targetClients";
|
||||
/** copied from the package private {@link net.i2p.router.tunnelmanager.TunnelPool} */
|
||||
public final static int TARGET_CLIENTS_DEFAULT = 3;
|
||||
|
||||
public ConfigClientsHelper() {}
|
||||
|
||||
public String getClientCountSelectBox() {
|
||||
int count = TARGET_CLIENTS_DEFAULT;
|
||||
String val = _context.router().getConfigSetting(TARGET_CLIENTS_PARAM);
|
||||
if (val != null) {
|
||||
try {
|
||||
count = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore, use default from above
|
||||
}
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
buf.append("<select name=\"clientcount\">\n");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (count == i)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">").append(i).append("</option>\n");
|
||||
}
|
||||
if (count >= 5) {
|
||||
buf.append("<option value=\"").append(count);
|
||||
buf.append("\" selected>").append(count);
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getTunnelCountSelectBox() {
|
||||
int count = ClientTunnelSettings.DEFAULT_NUM_INBOUND;
|
||||
String val = _context.router().getConfigSetting(ClientTunnelSettings.PROP_NUM_INBOUND);
|
||||
if (val != null) {
|
||||
try {
|
||||
count = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore, use default from above
|
||||
}
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
buf.append("<select name=\"tunnelcount\">\n");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (count == i)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">").append(i).append("</option>\n");
|
||||
}
|
||||
if (count >= 4) {
|
||||
buf.append("<option value=\"").append(count);
|
||||
buf.append("\" selected>").append(count);
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getTunnelDepthSelectBox() {
|
||||
int count = ClientTunnelSettings.DEFAULT_DEPTH_INBOUND;
|
||||
String val = _context.router().getConfigSetting(ClientTunnelSettings.PROP_DEPTH_INBOUND);
|
||||
if (val != null) {
|
||||
try {
|
||||
count = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore, use default from above
|
||||
}
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
buf.append("<select name=\"tunneldepth\">\n");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (count == i)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">").append(i).append("</option>\n");
|
||||
}
|
||||
if (count >= 4) {
|
||||
buf.append("<option value=\"").append(count);
|
||||
buf.append("\" selected>").append(count);
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getTunnelDepthOutboundSelectBox() {
|
||||
int count = ClientTunnelSettings.DEFAULT_DEPTH_OUTBOUND;
|
||||
String val = _context.router().getConfigSetting(ClientTunnelSettings.PROP_DEPTH_OUTBOUND);
|
||||
if (val != null) {
|
||||
try {
|
||||
count = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore, use default from above
|
||||
}
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
buf.append("<select name=\"tunneldepthoutbound\">\n");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (count == i)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">").append(i).append("</option>\n");
|
||||
}
|
||||
if (count >= 4) {
|
||||
buf.append("<option value=\"").append(count);
|
||||
buf.append("\" selected>").append(count);
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ public class ConfigNetHandler extends FormHandler {
|
||||
private String _outboundRate;
|
||||
private String _outboundBurst;
|
||||
private String _reseedFrom;
|
||||
private String _sharePct;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_guessRequested = false;
|
||||
@ -85,6 +86,9 @@ public class ConfigNetHandler extends FormHandler {
|
||||
public void setReseedfrom(String url) {
|
||||
_reseedFrom = (url != null ? url.trim() : null);
|
||||
}
|
||||
public void setSharePercentage(String pct) {
|
||||
_sharePct = (pct != null ? pct.trim() : null);
|
||||
}
|
||||
|
||||
private static final String IP_PREFIX = "<h1>Your IP is ";
|
||||
private static final String IP_SUFFIX = " <br></h1>";
|
||||
@ -217,7 +221,10 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
if ( (_port != null) && (_port.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
|
||||
if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
if ( (oldPort == null) && (_port.equals("8887")) ) {
|
||||
// still on default.. noop
|
||||
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
// its not the default OR it has changed
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
|
||||
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
|
||||
restartRequired = true;
|
||||
@ -226,6 +233,14 @@ public class ConfigNetHandler extends FormHandler {
|
||||
|
||||
updateRates();
|
||||
|
||||
if (_sharePct != null) {
|
||||
String old = _context.router().getConfigSetting(ConfigNetHelper.PROP_SHARE_PERCENTAGE);
|
||||
if ( (old == null) || (!old.equalsIgnoreCase(_sharePct)) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_SHARE_PERCENTAGE, _sharePct);
|
||||
addFormNotice("Updating bandwidth share percentage");
|
||||
}
|
||||
}
|
||||
|
||||
if (_timeSyncEnabled) {
|
||||
// Time sync enable, means NOT disabled
|
||||
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
|
||||
|
@ -62,6 +62,8 @@ public class ConfigNetHelper {
|
||||
public static final String PROP_OUTBOUND_KBPS = "i2np.bandwidth.outboundKBytesPerSecond";
|
||||
public static final String PROP_INBOUND_BURST = "i2np.bandwidth.inboundBurstKBytes";
|
||||
public static final String PROP_OUTBOUND_BURST = "i2np.bandwidth.outboundBurstKBytes";
|
||||
public static final String PROP_SHARE_PERCENTAGE = "router.sharePercentage";
|
||||
public static final int DEFAULT_SHARE_PERCENTAGE = 80;
|
||||
|
||||
public String getInboundRate() {
|
||||
String rate = _context.getProperty(PROP_INBOUND_KBPS);
|
||||
@ -120,15 +122,39 @@ public class ConfigNetHelper {
|
||||
private static String getBurstFactor(int numSeconds, String name) {
|
||||
StringBuffer buf = new StringBuffer(256);
|
||||
buf.append("<select name=\"").append(name).append("\">\n");
|
||||
for (int i = 1; i < 10; i++) {
|
||||
boolean found = false;
|
||||
for (int i = 10; i <= 60; i += 10) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if ( (i == numSeconds) || (i == 10) )
|
||||
if (i == numSeconds) {
|
||||
buf.append("selected ");
|
||||
found = true;
|
||||
} else if ( (i == 60) && (!found) ) {
|
||||
buf.append("selected ");
|
||||
}
|
||||
buf.append(">");
|
||||
if (i == 1)
|
||||
buf.append("1 second (no burst)</option>\n");
|
||||
else
|
||||
buf.append(i).append(" seconds</option>\n");
|
||||
buf.append(i).append(" seconds</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getSharePercentageBox() {
|
||||
String pctStr = _context.getProperty(PROP_SHARE_PERCENTAGE);
|
||||
int pct = DEFAULT_SHARE_PERCENTAGE;
|
||||
if (pctStr != null)
|
||||
try { pct = Integer.parseInt(pctStr); } catch (NumberFormatException nfe) {}
|
||||
StringBuffer buf = new StringBuffer(256);
|
||||
buf.append("<select name=\"sharePercentage\">\n");
|
||||
boolean found = false;
|
||||
for (int i = 30; i <= 100; i += 10) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if (pct == i) {
|
||||
buf.append("selected=\"true\" ");
|
||||
found = true;
|
||||
} else if ( (i == DEFAULT_SHARE_PERCENTAGE) && (!found) ) {
|
||||
buf.append("selected=\"true\" ");
|
||||
}
|
||||
buf.append(">Up to ").append(i).append("%</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
|
@ -1,10 +1,17 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.apps.systray.SysTray;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import org.tanukisoftware.wrapper.WrapperManager;
|
||||
|
||||
/**
|
||||
@ -86,6 +93,12 @@ public class ConfigServiceHandler extends FormHandler {
|
||||
} catch (Throwable t) {
|
||||
addFormError("Warning: unable to contact the systray manager - " + t.getMessage());
|
||||
}
|
||||
} else if ("View console on startup".equals(_action)) {
|
||||
browseOnStartup(true);
|
||||
addFormNotice("Console is to be shown on startup");
|
||||
} else if ("Do not view console on startup".equals(_action)) {
|
||||
browseOnStartup(false);
|
||||
addFormNotice("Console is not to be shown on startup");
|
||||
} else {
|
||||
addFormNotice("Blah blah blah. whatever. I'm not going to " + _action);
|
||||
}
|
||||
@ -107,4 +120,81 @@ public class ConfigServiceHandler extends FormHandler {
|
||||
addFormError("Warning: unable to remove the service - " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private final static String NL = System.getProperty("line.separator");
|
||||
private void browseOnStartup(boolean shouldLaunchBrowser) {
|
||||
File f = new File("clients.config");
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
DataHelper.loadProps(p, f);
|
||||
|
||||
int i = 0;
|
||||
int launchIndex = -1;
|
||||
while (true) {
|
||||
String className = p.getProperty("clientApp." + i + ".main");
|
||||
if (className == null) break;
|
||||
if (UrlLauncher.class.getName().equals(className)) {
|
||||
launchIndex = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((launchIndex >= 0) && shouldLaunchBrowser)
|
||||
return;
|
||||
if ((launchIndex < 0) && !shouldLaunchBrowser)
|
||||
return;
|
||||
|
||||
if (shouldLaunchBrowser) {
|
||||
p.setProperty("clientApp." + i + ".main", UrlLauncher.class.getName());
|
||||
p.setProperty("clientApp." + i + ".name", "BrowserLauncher");
|
||||
p.setProperty("clientApp." + i + ".args", "http://localhost:7657/index.jsp");
|
||||
p.setProperty("clientApp." + i + ".delay", "5");
|
||||
} else {
|
||||
p.remove("clientApp." + launchIndex + ".main");
|
||||
p.remove("clientApp." + launchIndex + ".name");
|
||||
p.remove("clientApp." + launchIndex + ".args");
|
||||
p.remove("clientApp." + launchIndex + ".onBoot");
|
||||
p.remove("clientApp." + launchIndex + ".delay");
|
||||
|
||||
i = launchIndex + 1;
|
||||
while (true) {
|
||||
String main = p.getProperty("clientApp." + i + ".main");
|
||||
String name = p.getProperty("clientApp." + i + ".name");
|
||||
String args = p.getProperty("clientApp." + i + ".args");
|
||||
String boot = p.getProperty("clientApp." + i + ".onBoot");
|
||||
String delay= p.getProperty("clientApp." + i + ".delay");
|
||||
|
||||
if (main == null) break;
|
||||
|
||||
p.setProperty("clientApp." + (i-1) + ".main", main);
|
||||
p.setProperty("clientApp." + (i-1) + ".name", name);
|
||||
p.setProperty("clientApp." + (i-1) + ".args", args);
|
||||
if (boot != null)
|
||||
p.setProperty("clientApp." + (i-1) + ".onBoot", boot);
|
||||
if (delay != null)
|
||||
p.setProperty("clientApp." + (i-1) + ".delay", delay);
|
||||
|
||||
p.remove("clientApp." + i + ".main");
|
||||
p.remove("clientApp." + i + ".name");
|
||||
p.remove("clientApp." + i + ".args");
|
||||
p.remove("clientApp." + i + ".onBoot");
|
||||
p.remove("clientApp." + i + ".delay");
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
TreeMap sorted = new TreeMap(p);
|
||||
FileWriter out = new FileWriter(f);
|
||||
for (Iterator iter = sorted.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String val = (String)sorted.get(name);
|
||||
out.write(name + "=" + val + NL);
|
||||
}
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
addFormError("Error updating the client config");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the tunnel config form and act
|
||||
* upon the values. Holy crap, this is UUUUGLY
|
||||
*
|
||||
*/
|
||||
public class ConfigTunnelsHandler extends FormHandler {
|
||||
private Log _log;
|
||||
private Map _settings;
|
||||
private boolean _shouldSave;
|
||||
|
||||
public ConfigTunnelsHandler() {
|
||||
_shouldSave = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) {
|
||||
if ( (moo != null) && (moo.equals("Save changes")) )
|
||||
_shouldSave = true;
|
||||
}
|
||||
|
||||
public void setSettings(Map settings) { _settings = new HashMap(settings); }
|
||||
|
||||
/**
|
||||
* The user made changes to the network config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
_log = _context.logManager().getLog(ConfigTunnelsHandler.class);
|
||||
boolean saveRequired = false;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Saving changes, with props = " + _settings);
|
||||
|
||||
int updated = 0;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
Object val = _settings.get("pool." + index);
|
||||
if (val == null) break;
|
||||
Hash client = new Hash();
|
||||
|
||||
String poolName = (val instanceof String ? (String)val : ((String[])val)[0]);
|
||||
|
||||
TunnelPoolSettings in = null;
|
||||
TunnelPoolSettings out = null;
|
||||
if ("exploratory".equals(poolName)) {
|
||||
in = _context.tunnelManager().getInboundSettings();
|
||||
out = _context.tunnelManager().getOutboundSettings();
|
||||
} else {
|
||||
try {
|
||||
client.fromBase64(poolName);
|
||||
} catch (DataFormatException dfe) {
|
||||
addFormError("Internal error (pool name could not resolve - " + poolName + ")");
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
in = _context.tunnelManager().getInboundSettings(client);
|
||||
out = _context.tunnelManager().getOutboundSettings(client);
|
||||
}
|
||||
|
||||
if ( (in == null) || (out == null) ) {
|
||||
addFormError("Internal error (pool settings cound not be fuond for " + poolName + ")");
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
in.setLength(getInt(_settings.get(index + ".depthInbound")));
|
||||
out.setLength(getInt(_settings.get(index + ".depthOutbound")));
|
||||
in.setLengthVariance(getInt(_settings.get(index + ".varianceInbound")));
|
||||
out.setLengthVariance(getInt(_settings.get(index + ".varianceOutbound")));
|
||||
in.setQuantity(getInt(_settings.get(index + ".quantityInbound")));
|
||||
out.setQuantity(getInt(_settings.get(index + ".quantityOutbound")));
|
||||
in.setBackupQuantity(getInt(_settings.get(index + ".backupInbound")));
|
||||
out.setBackupQuantity(getInt(_settings.get(index + ".backupOutbound")));
|
||||
|
||||
if ("exploratory".equals(poolName)) {
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_LENGTH, in.getLength()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_LENGTH, out.getLength()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_LENGTH_VARIANCE, in.getLengthVariance()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_LENGTH_VARIANCE, out.getLengthVariance()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_QUANTITY, in.getQuantity()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_QUANTITY, out.getQuantity()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_BACKUP_QUANTITY, in.getBackupQuantity()+"");
|
||||
_context.router().setConfigSetting(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY +
|
||||
TunnelPoolSettings.PROP_BACKUP_QUANTITY, out.getBackupQuantity()+"");
|
||||
}
|
||||
|
||||
if ("exploratory".equals(poolName)) {
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Inbound exploratory settings: " + in);
|
||||
_log.debug("Outbound exploratory settings: " + out);
|
||||
}
|
||||
_context.tunnelManager().setInboundSettings(in);
|
||||
_context.tunnelManager().setOutboundSettings(out);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Inbound settings for " + client.toBase64() + ": " + in);
|
||||
_log.debug("Outbound settings for " + client.toBase64() + ": " + out);
|
||||
}
|
||||
_context.tunnelManager().setInboundSettings(client, in);
|
||||
_context.tunnelManager().setOutboundSettings(client, out);
|
||||
}
|
||||
|
||||
updated++;
|
||||
saveRequired = true;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (updated > 0)
|
||||
addFormNotice("Updated settings for " + updated + " pools");
|
||||
|
||||
if (saveRequired) {
|
||||
boolean saved = _context.router().saveConfig();
|
||||
if (saved)
|
||||
addFormNotice("Configuration saved successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
}
|
||||
}
|
||||
private static final int getInt(Object val) {
|
||||
if (val == null) return 0;
|
||||
String str = null;
|
||||
if (val instanceof String)
|
||||
str = (String)val;
|
||||
else
|
||||
str = ((String[])val)[0];
|
||||
|
||||
if (str.trim().length() <= 0) return 0;
|
||||
try { return Integer.parseInt(str); } catch (NumberFormatException nfe) { return 0; }
|
||||
}
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
|
||||
public class ConfigTunnelsHelper {
|
||||
private RouterContext _context;
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigTunnelsHelper() {}
|
||||
|
||||
|
||||
public String getForm() {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
buf.append("<table border=\"1\">\n");
|
||||
TunnelPoolSettings exploratoryIn = _context.tunnelManager().getInboundSettings();
|
||||
TunnelPoolSettings exploratoryOut = _context.tunnelManager().getOutboundSettings();
|
||||
|
||||
buf.append("<input type=\"hidden\" name=\"pool.0\" value=\"exploratory\" >");
|
||||
renderForm(buf, 0, "exploratory", "Exploratory tunnels", exploratoryIn, exploratoryOut);
|
||||
|
||||
int cur = 1;
|
||||
Set clients = _context.clientManager().listClients();
|
||||
for (Iterator iter = clients.iterator(); iter.hasNext(); ) {
|
||||
Destination dest = (Destination)iter.next();
|
||||
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(dest.calculateHash());
|
||||
TunnelPoolSettings out = _context.tunnelManager().getOutboundSettings(dest.calculateHash());
|
||||
|
||||
String name = (in != null ? in.getDestinationNickname() : null);
|
||||
if (name == null)
|
||||
name = (out != null ? out.getDestinationNickname() : null);
|
||||
if (name == null)
|
||||
name = dest.calculateHash().toBase64().substring(0,6);
|
||||
|
||||
String prefix = dest.calculateHash().toBase64().substring(0,4);
|
||||
buf.append("<input type=\"hidden\" name=\"pool.").append(cur).append("\" value=\"");
|
||||
buf.append(dest.calculateHash().toBase64()).append("\" >");
|
||||
renderForm(buf, cur, prefix, "Client tunnels for " + name, in, out);
|
||||
cur++;
|
||||
}
|
||||
|
||||
buf.append("</table>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void renderForm(StringBuffer buf, int index, String prefix, String name, TunnelPoolSettings in, TunnelPoolSettings out) {
|
||||
|
||||
buf.append("<tr><td colspan=\"3\"><b><a name=\"").append(prefix).append("\">");
|
||||
buf.append(name).append("</a></b></td></tr>\n");
|
||||
buf.append("<tr><td></td><td><b>Inbound</b></td><td><b>Outbound</b></td></tr>\n");
|
||||
|
||||
// tunnel depth
|
||||
buf.append("<tr><td>Depth</td>\n");
|
||||
buf.append("<td><select name=\"").append(index).append(".depthInbound\">\n");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (in.getLength() <= 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hops</option>\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (in.getLength() == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 hop</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (in.getLength() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 hops</option>\n");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (in.getLength() == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 hops</option>\n");
|
||||
if (in.getLength() > 3)
|
||||
buf.append("<option value=\"").append(in.getLength()).append("\">").append(in.getLength()).append(" hops</option>\n");
|
||||
buf.append("</td>\n");
|
||||
|
||||
buf.append("<td><select name=\"").append(index).append(".depthOutbound\">\n");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (out.getLength() <= 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hops</option>\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (out.getLength() == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 hop</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (out.getLength() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 hops</option>\n");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (out.getLength() == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 hops</option>\n");
|
||||
if (out.getLength() > 3)
|
||||
buf.append("<option value=\"").append(out.getLength()).append("\">").append(out.getLength()).append(" hops</option>\n");
|
||||
buf.append("</td>\n");
|
||||
buf.append("</tr>\n");
|
||||
|
||||
// tunnel depth variance
|
||||
buf.append("<tr><td>Variance</td>\n");
|
||||
buf.append("<td><select name=\"").append(index).append(".varianceInbound\">\n");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (in.getLengthVariance() == 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hops</option>\n");
|
||||
buf.append("<option value=\"-1\" ");
|
||||
if (in.getLengthVariance() == -1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+/- 0-1 hops</option>\n");
|
||||
buf.append("<option value=\"-2\" ");
|
||||
if (in.getLengthVariance() == -2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+/- 0-2 hops</option>\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (in.getLengthVariance() == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+ 0-1 hops</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (in.getLengthVariance() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+ 0-2 hops</option>\n");
|
||||
if (in.getLengthVariance() < -2)
|
||||
buf.append("<option value=\"").append(in.getLengthVariance()).append("\">+/- 0-").append(in.getLengthVariance()).append(" hops</option>\n");
|
||||
if (in.getLengthVariance() > 2)
|
||||
buf.append("<option value=\"").append(in.getLengthVariance()).append("\">+ 0-").append(in.getLengthVariance()).append(" hops</option>\n");
|
||||
buf.append("</td>\n");
|
||||
|
||||
buf.append("<td><select name=\"").append(index).append(".varianceOutbound\">\n");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (out.getLengthVariance() == 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hops</option>\n");
|
||||
buf.append("<option value=\"-1\" ");
|
||||
if (out.getLengthVariance() == -1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+/- 0-1 hops</option>\n");
|
||||
buf.append("<option value=\"-2\" ");
|
||||
if (out.getLengthVariance() == -2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+/- 0-2 hops</option>\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (out.getLengthVariance() == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+ 0-1 hops</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (out.getLengthVariance() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">+ 0-2 hops</option>\n");
|
||||
if (out.getLengthVariance() < -2)
|
||||
buf.append("<option value=\"").append(out.getLengthVariance()).append("\">+/- 0-").append(out.getLengthVariance()).append(" hops</option>\n");
|
||||
if (out.getLengthVariance() > 2)
|
||||
buf.append("<option value=\"").append(out.getLengthVariance()).append("\">+ 0-").append(out.getLengthVariance()).append(" hops</option>\n");
|
||||
buf.append("</td>\n");
|
||||
|
||||
// tunnel quantity
|
||||
buf.append("<tr><td>Quantity</td>\n");
|
||||
buf.append("<td><select name=\"").append(index).append(".quantityInbound\">\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (in.getQuantity() <= 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 tunnel</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (in.getQuantity() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 tunnels</option>\n");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (in.getQuantity() == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 tunnels</option>\n");
|
||||
if (in.getQuantity() > 3)
|
||||
buf.append("<option value=\"").append(in.getQuantity()).append("\">").append(in.getQuantity()).append(" tunnels</option>\n");
|
||||
buf.append("</td>\n");
|
||||
|
||||
buf.append("<td><select name=\"").append(index).append(".quantityOutbound\">\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (out.getQuantity() <= 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 tunnel</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (out.getQuantity() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 tunnels</option>\n");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (out.getQuantity() == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 tunnels</option>\n");
|
||||
if (out.getQuantity() > 3)
|
||||
buf.append("<option value=\"").append(out.getQuantity()).append("\">").append(out.getQuantity()).append(" tunnels</option>\n");
|
||||
buf.append("</td>\n");
|
||||
buf.append("</tr>\n");
|
||||
|
||||
// tunnel backup quantity
|
||||
buf.append("<tr><td>Backup quantity</td>\n");
|
||||
buf.append("<td><select name=\"").append(index).append(".backupInbound\">\n");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (in.getBackupQuantity() <= 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 tunnels</option>\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (in.getBackupQuantity() == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 tunnel</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (in.getBackupQuantity() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 tunnels</option>\n");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (in.getBackupQuantity() == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 tunnels</option>\n");
|
||||
if (in.getBackupQuantity() > 3)
|
||||
buf.append("<option value=\"").append(in.getBackupQuantity()).append("\">").append(in.getBackupQuantity()).append(" tunnels</option>\n");
|
||||
buf.append("</td>\n");
|
||||
|
||||
buf.append("<td><select name=\"").append(index).append(".backupOutbound\">\n");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (out.getBackupQuantity() <= 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 tunnel</option>\n");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (out.getBackupQuantity() == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 tunnel</option>\n");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (out.getBackupQuantity() == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 tunnels</option>\n");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (out.getBackupQuantity() == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 tunnels</option>\n");
|
||||
if (out.getBackupQuantity() > 3)
|
||||
buf.append("<option value=\"").append(out.getBackupQuantity()).append("\">").append(out.getBackupQuantity()).append(" tunnels</option>\n");
|
||||
buf.append("</td>\n");
|
||||
buf.append("</tr>\n");
|
||||
|
||||
// custom options
|
||||
buf.append("<tr><td>Inbound options:</td>\n");
|
||||
buf.append("<td colspan=\"2\"><input name=\"").append(index);
|
||||
buf.append(".inboundOptions\" type=\"text\" size=\"40\" ");
|
||||
buf.append("value=\"");
|
||||
Properties props = in.getUnknownOptions();
|
||||
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
|
||||
String prop = (String)iter.next();
|
||||
String val = (String)props.getProperty(prop);
|
||||
buf.append(prop).append("=").append(val).append(" ");
|
||||
}
|
||||
buf.append("\"/></td></tr>\n");
|
||||
buf.append("<tr><td>Outbound options:</td>\n");
|
||||
buf.append("<td colspan=\"2\"><input name=\"").append(index);
|
||||
buf.append(".outboundOptions\" type=\"text\" size=\"40\" ");
|
||||
buf.append("value=\"");
|
||||
props = in.getUnknownOptions();
|
||||
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
|
||||
String prop = (String)iter.next();
|
||||
String val = (String)props.getProperty(prop);
|
||||
buf.append(prop).append("=").append(val).append(" ");
|
||||
}
|
||||
buf.append("\"/></td></tr>\n");
|
||||
buf.append("<tr><td colspan=\"3\"><hr /></td></tr>\n");
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import net.i2p.util.FileUtil;
|
||||
public class ContentHelper {
|
||||
private String _page;
|
||||
private int _maxLines;
|
||||
private boolean _startAtBeginning;
|
||||
private RouterContext _context;
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
@ -28,6 +29,10 @@ public class ContentHelper {
|
||||
public ContentHelper() {}
|
||||
|
||||
public void setPage(String page) { _page = page; }
|
||||
public void setStartAtBeginning(String moo) {
|
||||
_startAtBeginning = Boolean.valueOf(""+moo).booleanValue();
|
||||
}
|
||||
|
||||
public void setMaxLines(String lines) {
|
||||
if (lines != null) {
|
||||
try {
|
||||
@ -40,14 +45,14 @@ public class ContentHelper {
|
||||
}
|
||||
}
|
||||
public String getContent() {
|
||||
String str = FileUtil.readTextFile(_page, _maxLines);
|
||||
String str = FileUtil.readTextFile(_page, _maxLines, _startAtBeginning);
|
||||
if (str == null)
|
||||
return "";
|
||||
else
|
||||
return str;
|
||||
}
|
||||
public String getTextContent() {
|
||||
String str = FileUtil.readTextFile(_page, _maxLines);
|
||||
String str = FileUtil.readTextFile(_page, _maxLines, _startAtBeginning);
|
||||
if (str == null)
|
||||
return "";
|
||||
else
|
||||
|
@ -42,7 +42,7 @@ public class LogsHelper {
|
||||
}
|
||||
|
||||
public String getServiceLogs() {
|
||||
String str = FileUtil.readTextFile("wrapper.log", 500);
|
||||
String str = FileUtil.readTextFile("wrapper.log", 500, false);
|
||||
if (str == null)
|
||||
return "";
|
||||
else
|
||||
|
@ -0,0 +1,160 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handler to deal with reseed requests. This reseed from the URL
|
||||
* http://dev.i2p.net/i2pdb/ unless the java env property "i2p.reseedURL" is
|
||||
* set. It always writes to ./netDb/, so don't mess with that.
|
||||
*
|
||||
*/
|
||||
public class ReseedHandler {
|
||||
private static ReseedRunner _reseedRunner = new ReseedRunner();
|
||||
|
||||
public void setReseedNonce(String nonce) {
|
||||
if (nonce == null) return;
|
||||
if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) ||
|
||||
nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.noncePrev"))) {
|
||||
synchronized (_reseedRunner) {
|
||||
if (_reseedRunner.isRunning()) {
|
||||
return;
|
||||
} else {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true");
|
||||
I2PThread reseed = new I2PThread(_reseedRunner, "Reseed");
|
||||
reseed.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReseedRunner implements Runnable {
|
||||
private boolean _isRunning;
|
||||
public ReseedRunner() { _isRunning = false; }
|
||||
public boolean isRunning() { return _isRunning; }
|
||||
public void run() {
|
||||
_isRunning = true;
|
||||
reseed();
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false");
|
||||
_isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
|
||||
/**
|
||||
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||
* the routerInfo-*.dat files from the specified URL (or the default) and
|
||||
* save them into this router's netDb dir.
|
||||
*
|
||||
*/
|
||||
private static void reseed() {
|
||||
String seedURL = System.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
|
||||
if ( (seedURL == null) || (seedURL.trim().length() <= 0) )
|
||||
seedURL = DEFAULT_SEED_URL;
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
String content = new String(readURL(dir));
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
int start = content.indexOf("href=\"routerInfo-", cur);
|
||||
if (start < 0)
|
||||
break;
|
||||
|
||||
int end = content.indexOf(".dat\">", start);
|
||||
String name = content.substring(start+"href=\"routerInfo-".length(), end);
|
||||
urls.add(name);
|
||||
cur = end + 1;
|
||||
}
|
||||
|
||||
int fetched = 0;
|
||||
int errors = 0;
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
try {
|
||||
fetchSeed(seedURL, (String)iter.next());
|
||||
fetched++;
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class).error("Error reseeding", t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchSeed(String seedURL, String peer) throws Exception {
|
||||
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat");
|
||||
|
||||
byte data[] = readURL(url);
|
||||
writeSeed(peer, data);
|
||||
}
|
||||
|
||||
private static byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
String hostname = url.getHost();
|
||||
int port = url.getPort();
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
Socket s = new Socket(hostname, port);
|
||||
OutputStream out = s.getOutputStream();
|
||||
InputStream in = s.getInputStream();
|
||||
String request = getRequest(url);
|
||||
System.out.println("Sending to " + hostname +":"+ port + ": " + request);
|
||||
out.write(request.getBytes());
|
||||
out.flush();
|
||||
byte buf[] = new byte[1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
if (read < 0)
|
||||
break;
|
||||
baos.write(buf, 0, read);
|
||||
}
|
||||
in.close();
|
||||
s.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private static String getRequest(URL url) {
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
String path = url.getPath();
|
||||
if ("".equals(path))
|
||||
path = "/";
|
||||
buf.append("GET ").append(path).append(" HTTP/1.0\n");
|
||||
buf.append("Host: ").append(url.getHost());
|
||||
int port = url.getPort();
|
||||
if ( (port > 0) && (port != 80) )
|
||||
buf.append(":").append(port);
|
||||
buf.append("\nConnection: close\n\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void writeSeed(String name, byte data[]) throws Exception {
|
||||
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new File(dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
reseed();
|
||||
System.out.println("Done reseeding");
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import org.mortbay.http.handler.SecurityHandler;
|
||||
import org.mortbay.http.HashUserRealm;
|
||||
import org.mortbay.http.HttpRequest;
|
||||
import org.mortbay.http.SecurityConstraint;
|
||||
import org.mortbay.http.Authenticator;
|
||||
import org.mortbay.util.MultiException;
|
||||
|
||||
public class RouterConsoleRunner {
|
||||
@ -64,7 +65,7 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
try {
|
||||
_server.start();
|
||||
} catch (MultiException me) {
|
||||
} catch (Exception me) {
|
||||
me.printStackTrace();
|
||||
}
|
||||
try {
|
||||
|
@ -0,0 +1,42 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* uuuugly. dump the peer profile data if given a peer.
|
||||
*
|
||||
*/
|
||||
public class StatHelper {
|
||||
private String _peer;
|
||||
private Writer _writer;
|
||||
|
||||
public void setPeer(String peer) { _peer = peer; }
|
||||
public void setWriter(Writer writer) { _writer = writer; }
|
||||
|
||||
public String getProfile() {
|
||||
RouterContext ctx = (RouterContext)net.i2p.router.RouterContext.listContexts().get(0);
|
||||
Set peers = ctx.profileOrganizer().selectAllPeers();
|
||||
for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
|
||||
Hash peer = (Hash)iter.next();
|
||||
if (peer.toBase64().startsWith(_peer)) {
|
||||
try {
|
||||
WriterOutputStream wos = new WriterOutputStream(_writer);
|
||||
ctx.profileOrganizer().exportProfile(peer, wos);
|
||||
wos.flush();
|
||||
_writer.flush();
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
@ -4,13 +4,18 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.RouterVersion;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
|
||||
/**
|
||||
* Simple helper to query the appropriate router for data necessary to render
|
||||
@ -333,16 +338,39 @@ public class SummaryHelper {
|
||||
* @return html section summary
|
||||
*/
|
||||
public String getDestinations() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
try {
|
||||
OutputStreamWriter osw = new OutputStreamWriter(baos);
|
||||
_context.clientManager().renderStatusHTML(osw);
|
||||
osw.flush();
|
||||
return new String(baos.toByteArray());
|
||||
} catch (IOException ioe) {
|
||||
_context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe);
|
||||
return "";
|
||||
Set clients = _context.clientManager().listClients();
|
||||
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<u><b>Local destinations</b></u><br />");
|
||||
|
||||
for (Iterator iter = clients.iterator(); iter.hasNext(); ) {
|
||||
Destination client = (Destination)iter.next();
|
||||
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(client.calculateHash());
|
||||
TunnelPoolSettings out = _context.tunnelManager().getOutboundSettings(client.calculateHash());
|
||||
String name = (in != null ? in.getDestinationNickname() : null);
|
||||
if (name == null)
|
||||
name = (out != null ? out.getDestinationNickname() : null);
|
||||
if (name == null)
|
||||
name = client.calculateHash().toBase64().substring(0,6);
|
||||
|
||||
buf.append("<b>*</b> ").append(name).append("<br />\n");
|
||||
LeaseSet ls = _context.netDb().lookupLeaseSetLocally(client.calculateHash());
|
||||
if (ls != null) {
|
||||
long timeToExpire = ls.getEarliestLeaseDate() - _context.clock().now();
|
||||
if (timeToExpire < 0) {
|
||||
buf.append("<i>expired ").append(DataHelper.formatDuration(0-timeToExpire));
|
||||
buf.append(" ago</i><br />\n");
|
||||
}
|
||||
} else {
|
||||
buf.append("<i>No leases</i><br />\n");
|
||||
}
|
||||
buf.append("<a href=\"tunnels.jsp#").append(client.calculateHash().toBase64().substring(0,4));
|
||||
buf.append("\">Details</a> ");
|
||||
buf.append("<a href=\"configtunnels.jsp#").append(client.calculateHash().toBase64().substring(0,4));
|
||||
buf.append("\">Config</a><br />\n");
|
||||
}
|
||||
buf.append("<hr />\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,46 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
public class TunnelHelper {
|
||||
private RouterContext _context;
|
||||
private Writer _out;
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public TunnelHelper() {}
|
||||
|
||||
public void setWriter(Writer writer) { _out = writer; }
|
||||
|
||||
public String getTunnelSummary() {
|
||||
try {
|
||||
if (_out != null) {
|
||||
_context.tunnelManager().renderStatusHTML(_out);
|
||||
return "";
|
||||
} else {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
|
||||
_context.tunnelManager().renderStatusHTML(new OutputStreamWriter(baos));
|
||||
return new String(baos.toByteArray());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Treat a writer as an output stream. Quick 'n dirty, none
|
||||
* of that "intarnasheeonaleyzayshun" stuff. So we can treat
|
||||
* the jsp's PrintWriter as an OutputStream
|
||||
*/
|
||||
public class WriterOutputStream extends OutputStream {
|
||||
private Writer _writer;
|
||||
|
||||
public WriterOutputStream(Writer writer) { _writer = writer; }
|
||||
public void write(int b) throws IOException { _writer.write(b); }
|
||||
}
|
@ -28,44 +28,51 @@
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
|
||||
<b>External hostname/IP address:</b>
|
||||
<input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" />
|
||||
<input type="submit" name="guesshost" value="Guess" /><br />
|
||||
<b>Externally reachable TCP port:</b>
|
||||
TCP port:
|
||||
<input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br />
|
||||
<i>The hostname/IP address and TCP port must be reachable from the outside world. If
|
||||
you are behind a firewall or NAT, this means you must poke a hole for this port. If
|
||||
you are using DHCP and do not have a static IP address, you should either use a service like
|
||||
<a href="http://dyndns.org/">dyndns</a> or leave the hostname blank. If you leave it blank,
|
||||
your router will autodetect the 'correct' IP address by asking a peer (and unconditionally
|
||||
believing them if the address is routable and you don't have any established connections yet).
|
||||
The "guess" functionality makes an HTTP request
|
||||
to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
|
||||
<hr />
|
||||
<b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
|
||||
be within a few seconds of "correct".</i>
|
||||
<b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
|
||||
connections on it.</b> Nothing will work if you don't. Sorry. We know how to make it so
|
||||
this restriction won't be necessary, but its later on in the
|
||||
<a href="http://www.i2p.net/roadmap">roadmap</a> and we only have so many coder-hours (but if you want
|
||||
to help, please <a href="http://www.i2p.net/getinvolved">get involved!</a>)
|
||||
<hr />
|
||||
|
||||
<b>Bandwidth limiter</b><br />
|
||||
<b>Inbound rate</b>:
|
||||
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second<br />
|
||||
<b>Inbound burst duration:</b>
|
||||
Inbound rate:
|
||||
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second
|
||||
bursting up to
|
||||
<jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br />
|
||||
<b>Outbound rate:</b>
|
||||
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second<br />
|
||||
<b>Outbound burst duration:</b>
|
||||
Outbound rate:
|
||||
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second
|
||||
bursting up to
|
||||
<jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br />
|
||||
<i>A negative rate means there is no limit</i><br />
|
||||
Bandwidth share percentage:
|
||||
<jsp:getProperty name="nethelper" property="sharePercentageBox" /><br />
|
||||
Sharing a higher percentage will improve your anonymity and help the network
|
||||
<hr />
|
||||
<b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />):
|
||||
<input type="submit" name="reseed" value="now" /><br />
|
||||
<i>May take some time to download the peer references</i>
|
||||
Enable internal time synchronization? <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
|
||||
be within a few seconds of "correct". You will need to be able to send outbound UDP
|
||||
packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).</i>
|
||||
<hr />
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
<i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections
|
||||
and clients as if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
<i>Changing the TCP port will force a 'soft restart' - dropping your connections and clients as
|
||||
if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
a few seconds to complete.</i>
|
||||
</form>
|
||||
<hr />
|
||||
<b>Advanced network config:</b>
|
||||
<p>
|
||||
One advanced network option has to do with reseeding - you should never need to
|
||||
reseed your router as long as you can find at least one other peer on the network. However,
|
||||
when you do need to reseed, a link will show up on the left hand side which will
|
||||
fetch all of the routerInfo-* files from http://dev.i2p.net/i2pdb/. That URL is just an
|
||||
apache folder pointing at the netDb/ directory of a router - anyone can run one, and you can
|
||||
configure your router to seed off an alternate URL by adding the java environmental property
|
||||
"i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...). You can
|
||||
also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC,
|
||||
whatever) and saving them to your netDb/ directory.</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
@ -29,10 +29,12 @@
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigAdvancedHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
<textarea rows="20" cols="100" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
|
||||
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /> <br />
|
||||
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /><!-- <br />
|
||||
<b>Force restart:</b> <input type="checkbox" name="restart" value="force" /> <i>(specify this
|
||||
if the changes made above require the router to reset itself - e.g. you are updating TCP ports
|
||||
or hostnames, etc)</i>
|
||||
or hostnames, etc)</i>-->
|
||||
If you are changing any of the I2NP settings, you should go to the
|
||||
<a href="configservice.jsp">service config</a> page and do a graceful restart after saving.
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -1,45 +0,0 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - config clients</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head><body>
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigClientsHelper" id="clientshelper" scope="request" />
|
||||
<jsp:setProperty name="clientshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigClientsHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<form action="configclients.jsp" method="POST">
|
||||
<% String prev = System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ConfigClientsHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ConfigClientsHandler.nonce", new java.util.Random().nextLong()+""); %>
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
<b>Estimated number of clients/destinations:</b>
|
||||
<jsp:getProperty name="clientshelper" property="clientCountSelectBox" /><br />
|
||||
<b>Default number of inbound tunnels per client:</b>
|
||||
<jsp:getProperty name="clientshelper" property="tunnelCountSelectBox" /><br />
|
||||
<b>Default number of hops per tunnel:</b>
|
||||
<jsp:getProperty name="clientshelper" property="tunnelDepthSelectBox" /><br />
|
||||
<b>Hops per outbound tunnel:</b>
|
||||
<jsp:getProperty name="clientshelper" property="tunnelDepthOutboundSelectBox" /><br />
|
||||
<hr />
|
||||
<input type="submit" name="shouldsave" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -2,8 +2,8 @@
|
||||
%>Network | <% } else { %><a href="config.jsp">Network</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configservice.jsp") != -1) {
|
||||
%>Service | <% } else { %><a href="configservice.jsp">Service</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configclients.jsp") != -1) {
|
||||
%>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configtunnels.jsp") != -1) {
|
||||
%>Tunnels | <% } else { %><a href="configtunnels.jsp">Tunnels</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
|
||||
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configadvanced.jsp") != -1) {
|
||||
|
@ -67,6 +67,14 @@
|
||||
please select the following option and review the thread dumped to
|
||||
<a href="logs.jsp#servicelogs">wrapper.log</a>.</p>
|
||||
<input type="submit" name="action" value="Dump threads" />
|
||||
|
||||
<h4>Launch browser on router startup?</h4>
|
||||
<p>I2P's main configuration interface is this web console, so for your convenience
|
||||
I2P can launch a web browser pointing at
|
||||
<a href="http://localhost:7657/index.jsp">http://localhost:7657/index.jsp</a> whenever
|
||||
the router starts up.</p>
|
||||
<input type="submit" name="action" value="View console on startup" />
|
||||
<input type="submit" name="action" value="Do not view console on startup" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
41
apps/routerconsole/jsp/configtunnels.jsp
Normal file
41
apps/routerconsole/jsp/configtunnels.jsp
Normal file
@ -0,0 +1,41 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - config tunnels</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head><body>
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigTunnelsHelper" id="tunnelshelper" scope="request" />
|
||||
<jsp:setProperty name="tunnelshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigTunnelsHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:setProperty name="formhandler" property="shouldsave" value="<%=request.getParameter("shouldsave")%>" />
|
||||
<jsp:setProperty name="formhandler" property="action" value="<%=request.getParameter("action")%>" />
|
||||
<jsp:setProperty name="formhandler" property="nonce" value="<%=request.getParameter("nonce")%>" />
|
||||
<jsp:setProperty name="formhandler" property="settings" value="<%=request.getParameterMap()%>" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<form action="configtunnels.jsp" method="POST">
|
||||
<% String prev = System.getProperty("net.i2p.router.web.ConfigTunnelsHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ConfigTunnelsHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ConfigTunnelsHandler.nonce", new java.util.Random().nextLong()+""); %>
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigTunnelsHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
<jsp:getProperty name="tunnelshelper" property="form" />
|
||||
<hr />
|
||||
<input type="submit" name="shouldsave" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
5
apps/routerconsole/jsp/dumpprofile.jsp
Normal file
5
apps/routerconsole/jsp/dumpprofile.jsp
Normal file
@ -0,0 +1,5 @@
|
||||
<%@page contentType="text/plain"
|
||||
%><jsp:useBean id="helper" class="net.i2p.router.web.StatHelper"
|
||||
/><jsp:setProperty name="helper" property="peer" value="<%=request.getParameter("peer")%>"
|
||||
/><jsp:setProperty name="helper" property="writer" value="<%=out%>"
|
||||
/><jsp:getProperty name="helper" property="profile" />
|
BIN
apps/routerconsole/jsp/favicon.ico
Normal file
BIN
apps/routerconsole/jsp/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -33,11 +33,13 @@ by their binary code license. This product includes software developed by the A
|
||||
(http://www.apache.org/). </p>
|
||||
|
||||
<p>Another application you can see on this webpage is <a href="http://www.i2p.net/i2ptunnel">I2PTunnel</a>
|
||||
(your <a href="/i2ptunnel/" target="_blank">web interface</a>) - a GPL'ed application written by mihi that
|
||||
(your <a href="i2ptunnel/" target="_blank">web interface</a>) - a GPL'ed application written by mihi that
|
||||
lets you tunnel normal TCP/IP traffic over I2P (such as the eepproxy and the irc proxy).</p>
|
||||
|
||||
<p>The router by default also includes human's public domain <a href="http://www.i2p.net/sam">SAM</a> bridge,
|
||||
which other client applications (such as aum's <a href="http://stasher.i2p/">stasher</a>) can use. For
|
||||
which other client applications (such the <a href="http://duck.i2p/i2p-bt/">bittorrent port</a>) can use.
|
||||
There is also an optimized library for doing large number calculations - jbigi - which in turn uses the
|
||||
LGPL licensed <a href="http://swox.com/gmp/">GMP</a> library, tuned for various PC architectures. For
|
||||
details on other applications available, as well as their licenses, please see the
|
||||
<a href="http://www.i2p.net/licenses">license policy</a>. Source for the I2P code and most bundled
|
||||
client applications can be found on our <a href="http://www.i2p.net/download">download page</a>, and is
|
||||
@ -47,7 +49,14 @@ in <a href="http://www.i2p.net/cvs">cvs</a>.</p>
|
||||
<jsp:useBean class="net.i2p.router.web.ContentHelper" id="contenthelper" scope="request" />
|
||||
<jsp:setProperty name="contenthelper" property="page" value="history.txt" />
|
||||
<jsp:setProperty name="contenthelper" property="maxLines" value="500" />
|
||||
<jsp:setProperty name="contenthelper" property="startAtBeginning" value="true" />
|
||||
<jsp:getProperty name="contenthelper" property="textContent" />
|
||||
|
||||
<p>
|
||||
A more complete list of updates can be found
|
||||
<a href="http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/history.txt?rev=HEAD">online</a>
|
||||
(<a href="http://dev.i2p/cgi-bin/cvsweb.cgi/i2p/history.txt?rev=HEAD">anonymously</a>)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
@ -15,6 +15,7 @@
|
||||
</div>
|
||||
|
||||
<h4>
|
||||
<a href="tunnels.jsp">Tunnels</a> |
|
||||
<a href="profiles.jsp">Profiles</a> |
|
||||
<a href="netdb.jsp">Network Database</a> |
|
||||
<a href="logs.jsp">Logs</a> |
|
||||
|
@ -2,6 +2,9 @@
|
||||
<jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
|
||||
<jsp:setProperty name="reseed" property="*" />
|
||||
|
||||
<div class="routersummary">
|
||||
<u><b>General</b></u><br />
|
||||
<b>Ident:</b> <jsp:getProperty name="helper" property="ident" /><br />
|
||||
@ -16,8 +19,27 @@
|
||||
<b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
|
||||
<b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br />
|
||||
<b>Failing:</b> <jsp:getProperty name="helper" property="failingPeers" /><br />
|
||||
<b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br />
|
||||
<hr />
|
||||
<b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /><%
|
||||
if (helper.getActivePeers() <= 0) {
|
||||
%><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
|
||||
}
|
||||
if (helper.getActiveProfiles() <= 10) { // 10 is the min fallback
|
||||
if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
|
||||
out.print(" <i>reseeding</i>");
|
||||
} else {
|
||||
long nonce = new java.util.Random().nextLong();
|
||||
String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
|
||||
String uri = request.getRequestURI();
|
||||
if (uri.indexOf('?') > 0)
|
||||
uri = uri + "&reseedNonce=" + nonce;
|
||||
else
|
||||
uri = uri + "?reseedNonce=" + nonce;
|
||||
out.print(" <a href=\"" + uri + "\">reseed</a>");
|
||||
}
|
||||
}
|
||||
%><hr />
|
||||
|
||||
<u><b>Bandwidth in/out</b></u><br />
|
||||
<b>1m:</b> <jsp:getProperty name="helper" property="inboundMinuteKBps" />/<jsp:getProperty name="helper" property="outboundMinuteKBps" />KBps<br />
|
||||
|
21
apps/routerconsole/jsp/tunnels.jsp
Normal file
21
apps/routerconsole/jsp/tunnels.jsp
Normal file
@ -0,0 +1,21 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - tunnel summary</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head><body>
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.TunnelHelper" id="tunnelHelper" scope="request" />
|
||||
<jsp:setProperty name="tunnelHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:setProperty name="tunnelHelper" property="writer" value="<%=out%>" />
|
||||
<jsp:getProperty name="tunnelHelper" property="tunnelSummary" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -21,9 +21,9 @@ SRCDIR = src
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = C:\Dev-Cpp\bin\ar
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
AR = C:\MinGW\bin\ar
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
|
@ -1 +1 @@
|
||||
See the `docs' directory for documentation and license.
|
||||
See the `docs' directory for the documentation and license.
|
||||
|
@ -1,7 +0,0 @@
|
||||
If you would like to make a donation to the author of this library, you can use
|
||||
the following methods:
|
||||
|
||||
* E-Gold account number 1043280
|
||||
* Paypal email mpc@innographx.com
|
||||
|
||||
If you want to use some other method, just ask.
|
32
apps/sam/c/doc/install.txt
Normal file
32
apps/sam/c/doc/install.txt
Normal file
@ -0,0 +1,32 @@
|
||||
=====================
|
||||
How to Install LibSAM
|
||||
=====================
|
||||
|
||||
1) Be sure you have GNU Make installed.
|
||||
|
||||
2) Find the Makefile for your operating system in the LibSAM root. For example,
|
||||
if you are on FreeBSD, you'd use Makefile.freebsd.
|
||||
|
||||
3) Run gmake with the -f option to build LibSAM. For example, on FreeBSD, you'd
|
||||
run "gmake -f Makefile.freebsd". On Linux, GNU Make is just called 'make', so
|
||||
you'd run "make -f Makefile.linux".
|
||||
|
||||
4) If that worked, you can now try to compile some of the example programs.
|
||||
They are compiled in the same way as LibSAM, but the Makefile names are
|
||||
different.
|
||||
|
||||
I2P-Ping should compile on any Unix system using the default Makefile. It won't
|
||||
work on Win32, however, because Win32 doesn't have the getopt() function.
|
||||
|
||||
The Warhammer example should run on any OS. Use the Makefile.posix for Unix
|
||||
sytems or the Makefile.mingw for Win32.
|
||||
|
||||
*** If you have trouble ***
|
||||
|
||||
If you have trouble compiling LibSAM, try to edit the Makefiles to fix the
|
||||
problem. The "Makefile.common" is shared by all systems, and you shouldn't have
|
||||
to touch it. Just copy the Makefile of the OS most similar to your own and
|
||||
start hacking on it. Send me a patch if you get it working.
|
||||
|
||||
I realise this build system is horrible, and in the future I will probably
|
||||
replace it entirely.
|
@ -1,9 +1,10 @@
|
||||
I need to do these things:
|
||||
|
||||
* SAM raw support
|
||||
* SAM raw support (partially complete)
|
||||
* Write an instruction manual
|
||||
* Make dest a dynamic string
|
||||
* Change SAM parser to use a hashmap
|
||||
* Improve build system
|
||||
|
||||
Anyone can help with these things:
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
/* vi:set ts=4: */
|
||||
|
||||
v1.30
|
||||
* Added session to sam_namingback()
|
||||
* Removed stdint.h dependency
|
||||
* Improved WIRETAP to do more logging
|
||||
* Added "pinger.sh" shell script example for using i2p-ping
|
||||
* Added SAM_BAD_STYLE error
|
||||
* Added exit values for i2p-ping from xolo
|
||||
|
||||
v1.25 2004-07-31
|
||||
* Created I2P-Ping, a new example program (it's a clone of I2Ping). Works
|
||||
|
@ -60,7 +60,8 @@ static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size);
|
||||
static void diedback(sam_sess_t *session);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
@ -78,7 +79,7 @@ int main(int argc, char *argv[])
|
||||
int count = INT_MAX; /* number of times to ping */
|
||||
int pongcount = -1;
|
||||
char *samhost = "localhost";
|
||||
uint16_t samport = 7656;
|
||||
unsigned short samport = 7656;
|
||||
|
||||
while ((ch = getopt(argc, argv, "ac:h:mp:qv")) != -1) {
|
||||
switch (ch) {
|
||||
@ -103,7 +104,7 @@ int main(int argc, char *argv[])
|
||||
quiet = true;
|
||||
break;
|
||||
case 'v': /* version */
|
||||
puts("$Id: i2p-ping.c,v 1.4 2004/09/22 20:05:40 jrandom Exp $");
|
||||
puts("$Id: i2p-ping.c,v 1.6 2004/12/02 17:54:23 mpc Exp $");
|
||||
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
|
||||
break;
|
||||
case '?':
|
||||
@ -140,7 +141,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
pongcount = 0;
|
||||
for (int j = 0; j < argc; j++) {
|
||||
if (strlen(argv[j]) == 516) {
|
||||
if (strlen(argv[j]) == SAM_PUBKEY_LEN - 1) {
|
||||
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else
|
||||
@ -242,7 +243,8 @@ static void logback(char *s)
|
||||
* This is really hackish, but we know that we are only doing one lookup, so
|
||||
* what the hell
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
|
@ -6,8 +6,8 @@
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
|
@ -47,7 +47,8 @@ static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size);
|
||||
static void diedback(sam_sess_t *session);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
|
||||
/*
|
||||
* Just some ugly global variables. Don't do this in your program.
|
||||
@ -88,12 +89,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether they've supplied a name or a base 64 destination
|
||||
* Check whether they've supplied a hostname or a base 64 destination
|
||||
*
|
||||
* Note that this is a hack. Jrandom says that once certificates are added,
|
||||
* the length could be different depending on the certificate's size.
|
||||
*/
|
||||
if (strlen(argv[1]) == 516) {
|
||||
if (strlen(argv[1]) == SAM_PUBKEY_LEN - 1) {
|
||||
memcpy(dest, argv[1], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else {
|
||||
@ -155,7 +156,6 @@ static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
static void diedback(sam_sess_t *session)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
/* high quality code would do a sam_session_free() here */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -172,11 +172,11 @@ static void logback(char *s)
|
||||
* This is really hackish, but we know that we are only doing one lookup, so
|
||||
* what the hell
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
/* high quality code would do a sam_session_free() here */
|
||||
exit(1);
|
||||
}
|
||||
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
|
||||
|
@ -28,36 +28,19 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PLATFORM_H
|
||||
#define PLATFORM_H
|
||||
#ifndef LIBSAM_PLATFORM_H
|
||||
#define LIBSAM_PLATFORM_H
|
||||
|
||||
/*
|
||||
* Operating system
|
||||
*/
|
||||
#define FREEBSD 0 // FreeBSD
|
||||
#define MINGW 1 // Windows native (Mingw)
|
||||
#define CYGWIN 1 // Cygwin
|
||||
#define LINUX 2 // Linux
|
||||
#define CYGWIN 3 // Cygwin
|
||||
|
||||
#if OS == MINGW
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_INET_ATON /* implies NO_INET_PTON */
|
||||
#define NO_INET_NTOP
|
||||
#define NO_SSIZE_T
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#define WINSOCK
|
||||
#endif
|
||||
|
||||
#if OS == LINUX
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#endif
|
||||
#define MINGW 3 // Windows native (Mingw)
|
||||
#define MSVC 4 // Windows native (Visual C++ 2003)
|
||||
|
||||
#if OS == CYGWIN
|
||||
#define FAST32_IS_LONG
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_INET_NTOP
|
||||
@ -68,13 +51,29 @@
|
||||
#define NO_Z_FORMAT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Standard C99 includes - if your compiler doesn't have these, it's time to
|
||||
* upgrade
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#if OS == LINUX
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#endif
|
||||
|
||||
#if OS == MINGW
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_INET_ATON // implies NO_INET_PTON
|
||||
#define NO_INET_NTOP
|
||||
#define NO_SSIZE_T
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#define WINSOCK
|
||||
#endif
|
||||
|
||||
#if OS == MSVC // FIXME: doesn't work
|
||||
#define NO_STDBOOL_H
|
||||
#define NO_SSIZE_T
|
||||
#define NO_STRL
|
||||
#define WINSOCK
|
||||
#endif
|
||||
|
||||
/*
|
||||
* System includes
|
||||
@ -116,6 +115,13 @@
|
||||
typedef signed long ssize_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* I'm too lazy to type "unsigned"
|
||||
*/
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned short ushort;
|
||||
|
||||
/*
|
||||
* Prints out the file name, line number, and function name before log message
|
||||
*/
|
||||
@ -136,4 +142,4 @@
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
#endif /* PLATFORM_H */
|
||||
#endif /* LIBSAM_PLATFORM_H */
|
||||
|
@ -28,19 +28,26 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SAM_H
|
||||
#define SAM_H
|
||||
#ifndef LIBSAM_SAM_H
|
||||
#define LIBSAM_SAM_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#ifdef NO_STDBOOL_H
|
||||
typedef int bool;
|
||||
#define true 0
|
||||
#define false 1
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
|
||||
/*
|
||||
* Lengths
|
||||
*/
|
||||
|
||||
/* The maximum length a SAM command can be */
|
||||
#define SAM_CMD_LEN 128
|
||||
/* The maximum size of a single datagram packet */
|
||||
@ -49,29 +56,21 @@ extern "C" {
|
||||
#define SAM_LOGMSG_LEN 256
|
||||
/* The longest `name' arg for the naming lookup callback */
|
||||
#define SAM_NAME_LEN 256
|
||||
/* The max size of a single stream packet */
|
||||
/* The maximum size of a single stream packet */
|
||||
#define SAM_STREAM_PAYLOAD_MAX (32 * 1024)
|
||||
/* The length of a base 64 public key - it's actually 516, but +1 for '\0' */
|
||||
#define SAM_PUBKEY_LEN 517
|
||||
/* A public key SAM command's length */
|
||||
/* The maximum length of a SAM command with a public key */
|
||||
#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)
|
||||
/* The maximum size of a single raw packet */
|
||||
#define SAM_RAW_PAYLOAD_MAX (32 * 1024)
|
||||
/* The maximum length a SAM non-data reply can be */
|
||||
#define SAM_REPLY_LEN 1024
|
||||
|
||||
/*
|
||||
* Shorten some standard variable types
|
||||
*/
|
||||
typedef signed char schar_t;
|
||||
typedef unsigned char uchar_t;
|
||||
typedef unsigned int uint_t;
|
||||
typedef unsigned long ulong_t;
|
||||
typedef unsigned short ushort_t;
|
||||
|
||||
#ifdef WINSOCK
|
||||
typedef SOCKET socket_t;
|
||||
#else
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
/*
|
||||
* Some LibSAM variable types
|
||||
*/
|
||||
|
||||
typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */
|
||||
|
||||
@ -82,12 +81,13 @@ typedef struct {
|
||||
size_t size;
|
||||
} sam_sendq_t; /* sending queue to encourage large stream packet sizes */
|
||||
|
||||
typedef int_fast32_t sam_sid_t; /* stream id number */
|
||||
typedef long sam_sid_t; /* stream id number */
|
||||
|
||||
typedef struct {
|
||||
socket_t sock; /* the socket used for communications with SAM */
|
||||
int sock; /* the socket used for communications with SAM */
|
||||
bool connected; /* whether the socket is connected */
|
||||
sam_sid_t prev_id; /* the last stream id number we used */
|
||||
void *child; /* whatever you want it to be */
|
||||
} sam_sess_t; /* a SAM session */
|
||||
|
||||
typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
@ -102,59 +102,65 @@ typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
SAM_TOO_BIG
|
||||
} samerr_t;
|
||||
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/* SAM controls - sessions */
|
||||
extern sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
extern void sam_session_free(sam_sess_t **session);
|
||||
sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
void sam_session_free(sam_sess_t **session);
|
||||
/* SAM controls - connection */
|
||||
extern bool sam_close(sam_sess_t *session);
|
||||
extern samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
bool sam_close(sam_sess_t *session);
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
unsigned short samport, const char *destname, sam_conn_t style,
|
||||
unsigned int tunneldepth);
|
||||
/* SAM controls - utilities */
|
||||
extern void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
extern bool sam_read_buffer(sam_sess_t *session);
|
||||
extern const char *sam_strerror(samerr_t code);
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
bool sam_read_buffer(sam_sess_t *session);
|
||||
const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
extern void (*sam_diedback)(sam_sess_t *session);
|
||||
extern void (*sam_logback)(char *str);
|
||||
extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
void (*sam_diedback)(sam_sess_t *session);
|
||||
void (*sam_logback)(char *str);
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name,
|
||||
sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/* Stream commands */
|
||||
extern void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(sam_sess_t *session,
|
||||
const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
|
||||
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
/* Stream commands - callbacks */
|
||||
extern void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream send queue (experimental) */
|
||||
extern void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
extern void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
|
||||
/* Datagram commands */
|
||||
extern samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
/* Datagram commands - callbacks */
|
||||
extern void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest,
|
||||
void *data, size_t size);
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size);
|
||||
|
||||
/* Raw commands */
|
||||
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
/* Raw commands - callbacks */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SAM_H */
|
||||
#endif /* LIBSAM_SAM_H */
|
||||
|
@ -33,8 +33,8 @@
|
||||
* snprintf.c)
|
||||
*/
|
||||
|
||||
#ifndef SNPRINTF_H
|
||||
#define SNPRINTF_H
|
||||
#ifndef LIBSAM_SNPRINTF_H
|
||||
#define LIBSAM_SNPRINTF_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -46,4 +46,4 @@ int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SNPRINTF_H */
|
||||
#endif /* LIBSAM_SNPRINTF_H */
|
||||
|
@ -32,8 +32,8 @@
|
||||
* Note: The strl.c file retains its original license (at the top of strl.c)
|
||||
*/
|
||||
|
||||
#ifndef STRL_H
|
||||
#define STRL_H
|
||||
#ifndef LIBSAM_STRL_H
|
||||
#define LIBSAM_STRL_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -44,4 +44,4 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* STRL_H */
|
||||
#endif /* LIBSAM_STRL_H */
|
||||
|
@ -28,8 +28,8 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "sam.h"
|
||||
#include "platform.h"
|
||||
|
||||
static bool sam_hello(sam_sess_t *session);
|
||||
static void sam_log(const char *format, ...);
|
||||
@ -40,9 +40,9 @@ static bool sam_readable(sam_sess_t *session);
|
||||
static sam_sendq_t *sam_sendq_create();
|
||||
static samerr_t sam_session_create(sam_sess_t *session,
|
||||
const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
uint tunneldepth);
|
||||
static bool sam_socket_connect(sam_sess_t *session, const char *host,
|
||||
uint16_t port);
|
||||
ushort port);
|
||||
static bool sam_socket_resolve(const char *hostname, char *ipaddr);
|
||||
#ifdef WINSOCK
|
||||
static samerr_t sam_winsock_cleanup();
|
||||
@ -55,28 +55,41 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
|
||||
* Callback functions
|
||||
* Note: if you add a new callback be sure to check for non-NULL in sam_connect
|
||||
*/
|
||||
|
||||
/* a peer closed the connection */
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
= NULL;
|
||||
|
||||
/* a peer connected to us */
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest) = NULL;
|
||||
|
||||
/* a peer sent some stream data (`data' MUST be freed) */
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size) = NULL;
|
||||
|
||||
/* a peer sent some datagram data (`data' MUST be freed) */
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size) = NULL;
|
||||
|
||||
/* we lost the connection to the SAM host */
|
||||
void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
|
||||
/* logging callback */
|
||||
void (*sam_logback)(char *str) = NULL;
|
||||
|
||||
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result) = NULL;
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/* our connection to a peer has completed */
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/* a peer sent some raw data (`data' MUST be freed) */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size) = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Closes the connection to the SAM host
|
||||
*
|
||||
@ -134,8 +147,8 @@ bool sam_close(sam_sess_t *session)
|
||||
*
|
||||
* Returns: SAM error code. If SAM_OK, `session' will be ready for use.
|
||||
*/
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth)
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost, ushort samport,
|
||||
const char *destname, sam_conn_t style, uint tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
samerr_t rc;
|
||||
@ -155,7 +168,11 @@ samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else if (style == SAM_RAW) {
|
||||
abort(); /* not implemented yet */
|
||||
if (sam_diedback == NULL || sam_logback == NULL
|
||||
|| sam_namingback == NULL || sam_rawback == NULL) {
|
||||
SAMLOGS("Please set callback functions before connecting");
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else {
|
||||
SAMLOGS("Unknown connection style");
|
||||
return SAM_BAD_STYLE;
|
||||
@ -197,7 +214,7 @@ samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
* data - the data we're sending
|
||||
* size - the size of the data
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
* Returns: SAM_OK on success
|
||||
*/
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
@ -295,6 +312,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
|
||||
#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY"
|
||||
#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND"
|
||||
#define SAM_RAW_RECEIVED_REPLY "RAW RECEIVED"
|
||||
#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED"
|
||||
#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED"
|
||||
#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED"
|
||||
@ -305,6 +323,10 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY"
|
||||
#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT"
|
||||
|
||||
/*
|
||||
* TODO: add raw parsing
|
||||
*/
|
||||
|
||||
if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
|
||||
strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
|
||||
char *p;
|
||||
@ -365,7 +387,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
q++;
|
||||
strlcpy(name, p, sizeof name);
|
||||
strlcpy(pubkey, q, sizeof pubkey);
|
||||
sam_namingback(name, pubkey, SAM_OK);
|
||||
sam_namingback(session, name, pubkey, SAM_OK);
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY_IK,
|
||||
strlen(SAM_NAMING_REPLY_IK)) == 0) {
|
||||
@ -373,7 +395,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(name, NULL, SAM_INVALID_KEY);
|
||||
sam_namingback(session, name, NULL, SAM_INVALID_KEY);
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY_KNF,
|
||||
strlen(SAM_NAMING_REPLY_KNF)) == 0) {
|
||||
@ -381,14 +403,14 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(name, NULL, SAM_KEY_NOT_FOUND);
|
||||
sam_namingback(session, name, NULL, SAM_KEY_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
q = strchr(p, ' '); /* ' 'MES.. (optional) */
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(name, NULL, SAM_UNKNOWN);
|
||||
sam_namingback(session, name, NULL, SAM_UNKNOWN);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -518,6 +540,42 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends data to a destination in a raw packet
|
||||
*
|
||||
* dest - base 64 destination of who we're sending to
|
||||
* data - the data we're sending
|
||||
* size - the size of the data
|
||||
*
|
||||
* Returns: SAM_OK on success
|
||||
*/
|
||||
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_RAW_PAYLOAD_MAX) {
|
||||
#ifdef NO_Z_FORMAT
|
||||
SAMLOG("Invalid data send size (%u bytes)", size);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%zu bytes)", size);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
#ifdef NO_Z_FORMAT
|
||||
snprintf(cmd, sizeof cmd, "RAW SEND DESTINATION=%s SIZE=%u\n",
|
||||
dest, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "RAW SEND DESTINATION=%s SIZE=%zu\n",
|
||||
dest, size);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads and callbacks everything in the SAM network buffer until it is clear
|
||||
*
|
||||
@ -598,7 +656,7 @@ static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n)
|
||||
if (*p == '\n') { /* end of SAM response */
|
||||
*p = '\0';
|
||||
#if SAM_WIRETAP
|
||||
printf("<<<< %s\n", buf);
|
||||
printf("*RR* %s\n", buf);
|
||||
#endif
|
||||
return n - nleft;
|
||||
}
|
||||
@ -663,7 +721,16 @@ static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
|
||||
p += nread;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
printf("<<<< (read2() %d bytes)\n", n);
|
||||
p = buf;
|
||||
printf("*RR* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(((byte*)p)[x]))
|
||||
printf("%c,", ((byte*)p)[x]);
|
||||
else
|
||||
printf("%03d,", ((byte*)p)[x]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("*RR* (read2() read %d bytes)\n", n);
|
||||
#endif
|
||||
assert(nleft == 0);/* <---\ */
|
||||
return n - nleft; /* should be equal to initial n */
|
||||
@ -689,7 +756,7 @@ static bool sam_readable(sam_sess_t *session)
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(session->sock, &rset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10;
|
||||
tv.tv_usec = 0;
|
||||
rc = select(session->sock + 1, &rset, NULL, NULL, &tv);
|
||||
if (rc == 0)
|
||||
return false;
|
||||
@ -811,7 +878,7 @@ void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
* Returns: SAM error code
|
||||
*/
|
||||
static samerr_t sam_session_create(sam_sess_t *session, const char *destname,
|
||||
sam_conn_t style, uint_t tunneldepth)
|
||||
sam_conn_t style, uint tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK"
|
||||
@ -870,6 +937,7 @@ sam_sess_t *sam_session_init(sam_sess_t *session)
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
session->child = NULL;
|
||||
}
|
||||
session->connected = false;
|
||||
session->prev_id = 0;
|
||||
@ -897,7 +965,7 @@ void sam_session_free(sam_sess_t **session)
|
||||
*
|
||||
* Returns: true on sucess, false on error, with errno set
|
||||
*/
|
||||
bool sam_socket_connect(sam_sess_t *session, const char *host, uint16_t port)
|
||||
bool sam_socket_connect(sam_sess_t *session, const char *host, ushort port)
|
||||
{
|
||||
assert(session != NULL);
|
||||
struct sockaddr_in hostaddr;
|
||||
@ -1014,11 +1082,7 @@ void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id)
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%ld\n", stream_id);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
@ -1037,13 +1101,8 @@ sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest)
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
session->prev_id++; /* increment the id for the connection */
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n",
|
||||
session->prev_id, dest);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n",
|
||||
session->prev_id, dest);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return session->prev_id;
|
||||
@ -1075,15 +1134,9 @@ samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
#ifdef NO_Z_FORMAT
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%u\n",
|
||||
stream_id, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%u\n",
|
||||
stream_id, size);
|
||||
#endif
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%u\n", stream_id, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%zu\n",
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%zu\n",
|
||||
stream_id, size);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
@ -1333,7 +1386,8 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
return -1;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
const uchar_t *cp = buf;
|
||||
const byte *cp = buf;
|
||||
printf("*WW* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(cp[x]))
|
||||
printf("%c,", cp[x]);
|
||||
@ -1365,7 +1419,7 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
p += nwritten;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
printf(">>>> (write() %d bytes)\n", n);
|
||||
printf("*WW* (write() wrote %d bytes)\n", n);
|
||||
#endif
|
||||
|
||||
return n;
|
||||
|
84
apps/sam/csharp/README
Normal file
84
apps/sam/csharp/README
Normal file
@ -0,0 +1,84 @@
|
||||
sam-sharp
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
sam-sharp is a .NET SAM client library for I2P written in C#. It aims to be
|
||||
compatible with platforms that implement .NET's base class library API (such
|
||||
as Mono and DotGNU Portable.NET) and to be usable from all languages
|
||||
conforming to the ECMA-standardized Common Language Infrastructure, including
|
||||
C# and VB.NET.
|
||||
|
||||
|
||||
MINIMUM REQUIREMENTS
|
||||
|
||||
* Mono and mcs 1.0 or higher (Linux, Mac OS X, Windows)
|
||||
|
||||
- or -
|
||||
|
||||
MS .NET Framework SDK 1.0 or higher (Windows)
|
||||
|
||||
- or -
|
||||
|
||||
DotGNU Portable .NET, latest version recommended (*BSD, AIX, Cygwin, Linux,
|
||||
Mac OS X, MinGW, Solaris)
|
||||
|
||||
|
||||
OPTIONAL REQUIREMENTS
|
||||
|
||||
* NAnt 0.85 or higher is needed to use sam-sharp.build. Sorry, NAnt does not
|
||||
yet support Portable.NET.
|
||||
|
||||
* NUnit 2.2.1 or later is needed to run the (soon-to-be-added) unit tests. If
|
||||
you have the Mono mcs package installed then you already have NUnit.
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
Pre-generated docs will be submitted to CVS soon. In the meantime you may
|
||||
generate standalone documentation from the embedded XML doc comments by
|
||||
issuing the following commands from sam-sharp's src/ directory:
|
||||
|
||||
Mono:
|
||||
|
||||
mkdir ../doc
|
||||
mcs -doc:../doc/sam-sharp_doc.xml *.cs
|
||||
|
||||
MS .NET:
|
||||
|
||||
mkdir ../doc
|
||||
csc /doc:../doc/sam-sharp_doc.xml *.cs
|
||||
|
||||
DotGNU Portable.NET:
|
||||
|
||||
mkdir ../doc
|
||||
csdoc -o ../doc/sam-sharp_doc.xml *.cs
|
||||
|
||||
The resulting XML doc can be converted to HTML using either Visual Studio .NET
|
||||
(Tools > Build Comment Web Pages) or Portable.NET's csdoc2html tool:
|
||||
|
||||
csdoc2html -o ../doc ../doc/sam-sharp_doc.xml
|
||||
|
||||
|
||||
ACKNOWLEDGMENTS
|
||||
|
||||
sam-sharp is a port of jrandom's public domain Java SAM client library.
|
||||
|
||||
|
||||
LICENSE
|
||||
|
||||
This work is released into the public domain.
|
||||
|
||||
|
||||
AUTHORS
|
||||
|
||||
jrandom (original Java SAM client library)
|
||||
smeghead (C# port of the Java SAM client library)
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
|
||||
smeghead <smeghead@i2pmail.org> <smeghead@mail.i2p>
|
||||
|
||||
|
||||
$Id: README,v 1.1 2005/01/24 17:42:05 smeghead Exp $
|
19
apps/sam/csharp/sam-sharp.build
Normal file
19
apps/sam/csharp/sam-sharp.build
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0"?>
|
||||
<project basedir="." default="bin" name="sam-sharp">
|
||||
|
||||
<target name="bin" description="Builds assemblies from source">
|
||||
<mkdir dir="bin" />
|
||||
<csc target="dll" output="bin/sam-sharp.dll">
|
||||
<sources>
|
||||
<include name="src/**/*.cs" />
|
||||
</sources>
|
||||
</csc>
|
||||
<echo message="Build complete." />
|
||||
</target>
|
||||
|
||||
<target name="clean" description="Deletes all built assemblies">
|
||||
<delete dir="bin" failonerror="false" />
|
||||
<echo message="Clean complete." />
|
||||
</target>
|
||||
|
||||
</project>
|
32
apps/sam/csharp/src/I2P.SAM.Client/AssemblyInfo.cs
Normal file
32
apps/sam/csharp/src/I2P.SAM.Client/AssemblyInfo.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("sam-sharp")]
|
||||
[assembly: AssemblyDescription("Mono/.NET SAM client for I2P")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("sam-sharp")]
|
||||
[assembly: AssemblyCopyright("Released into the Public Domain")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("0.1")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user