forked from I2P_Developers/i2p.i2p
Compare commits
185 Commits
i2p_0_4_2_
...
i2p_0_5_0_
Author | SHA1 | Date | |
---|---|---|---|
![]() |
265d5e306e | ||
![]() |
10ed058c2e | ||
![]() |
8a21f0efec | ||
![]() |
b8291ac5a4 | ||
![]() |
c17433cb93 | ||
![]() |
35fe7f8203 | ||
![]() |
21f13dba43 | ||
![]() |
0db239a3fe | ||
![]() |
4745d61f9b | ||
![]() |
b9a4c3ba52 | ||
![]() |
cbf6a70a1a | ||
![]() |
7d4e093b58 | ||
![]() |
d27feabcb3 | ||
![]() |
0d9efa17de | ||
![]() |
b125b04c8d | ||
![]() |
0539f1d794 | ||
![]() |
a4b6709f02 | ||
![]() |
f2db143a6f | ||
![]() |
b615f54d41 | ||
![]() |
db2328e03e | ||
![]() |
e2071935ad | ||
![]() |
1c40ff773f | ||
![]() |
37a3645663 | ||
![]() |
eb8accd1e0 | ||
![]() |
3af97894b4 | ||
![]() |
15a0dcf4d8 | ||
![]() |
aa3a44c42a | ||
![]() |
40f4b47b87 | ||
![]() |
dca09d96b3 | ||
![]() |
dd10747460 | ||
![]() |
77176162af | ||
![]() |
8b9ee4dfd7 | ||
![]() |
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 |
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)));
|
||||
}
|
||||
|
||||
}
|
174
apps/addressbook/java/src/addressbook/Daemon.java
Normal file
174
apps/addressbook/java/src/addressbook/Daemon.java
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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));
|
||||
if (published != null)
|
||||
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 = null;
|
||||
if ("true".equals(settings.get("should_publish")))
|
||||
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://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/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("should_publish", "false");
|
||||
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>
|
@@ -31,6 +31,9 @@
|
||||
</war>
|
||||
</target>
|
||||
<target name="precompilejsp">
|
||||
<delete dir="../jsp/WEB-INF/" />
|
||||
<delete file="../jsp/web-fragment.xml" />
|
||||
<delete file="../jsp/web-out.xml" />
|
||||
<mkdir dir="../jsp/WEB-INF/" />
|
||||
<mkdir dir="../jsp/WEB-INF/classes" />
|
||||
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
|
||||
@@ -39,12 +42,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 +56,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,6 +284,7 @@ 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");
|
||||
@@ -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 />
|
||||
|
@@ -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,12 +36,12 @@ 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;
|
||||
protected I2PSocketManager sockMgr;
|
||||
protected List mySockets = new ArrayList();
|
||||
|
||||
protected Destination dest = null;
|
||||
@@ -58,7 +61,30 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
private String handlerName;
|
||||
|
||||
private Object conLock = new Object();
|
||||
private int pendingConnections = 0;
|
||||
|
||||
/** 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) {
|
||||
@@ -108,6 +134,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
}
|
||||
|
||||
configurePool(tunnel);
|
||||
|
||||
if (open && listenerReady) {
|
||||
l.log("Ready! Port " + getLocalPort());
|
||||
notifyEvent("openBaseClientResult", "ok");
|
||||
@@ -116,6 +144,37 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
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;
|
||||
|
||||
@@ -250,6 +309,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
|
||||
return;
|
||||
}
|
||||
ss = new ServerSocket(localPort, 0, addr);
|
||||
@@ -292,6 +352,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
synchronized (_waitingSockets) {
|
||||
_waitingSockets.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,16 +363,52 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* @param s Socket to take care of
|
||||
*/
|
||||
protected void manageConnection(Socket s) {
|
||||
boolean useBlocking = false;
|
||||
synchronized (conLock) {
|
||||
pendingConnections++;
|
||||
if (pendingConnections > 5)
|
||||
useBlocking = true;
|
||||
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");
|
||||
}
|
||||
}
|
||||
if (useBlocking)
|
||||
clientConnectionRun(s);
|
||||
else
|
||||
new ClientConnectionRunner(s, handlerName);
|
||||
}
|
||||
|
||||
public boolean close(boolean forced) {
|
||||
@@ -341,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) {
|
||||
@@ -352,22 +453,29 @@ 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);
|
||||
synchronized (conLock) {
|
||||
pendingConnections--;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -152,10 +152,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
I2PSocketOptions opts = super.getDefaultOptions();
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
opts.setReadTimeout(DEFAULT_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;
|
||||
}
|
||||
|
||||
@@ -164,14 +168,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
I2PSocketOptions opts = super.getDefaultOptions(overrides);
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_READ_TIMEOUT);
|
||||
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;
|
||||
@@ -203,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;
|
||||
@@ -348,7 +367,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)");
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
|
||||
newRequest.append("Connection: close\r\n\r\n");
|
||||
break;
|
||||
} else {
|
||||
@@ -398,7 +417,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
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, mySockets);
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
} catch (SocketException ex) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
@@ -428,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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,21 +40,37 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
Object slock, finishLock = new Object();
|
||||
boolean finished = false;
|
||||
HashMap ostreams, sockets;
|
||||
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;
|
||||
|
||||
private volatile long __forwarderId;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList) {
|
||||
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))
|
||||
@@ -97,24 +113,27 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
|
||||
public void run() {
|
||||
boolean closedCleanly = false;
|
||||
try {
|
||||
InputStream in = s.getInputStream();
|
||||
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
i2ps.setSocketErrorListener(this);
|
||||
InputStream i2pin = i2ps.getInputStream();
|
||||
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialData != null) {
|
||||
if (initialI2PData != null) {
|
||||
synchronized (slock) {
|
||||
i2pout.write(initialData);
|
||||
i2pout.write(initialI2PData);
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
if (initialSocketData != null) {
|
||||
out.write(initialSocketData);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Initial data " + (initialData != null ? initialData.length : 0)
|
||||
+ " written, starting forwarders");
|
||||
Thread t1 = new StreamForwarder(in, i2pout, "toI2P");
|
||||
Thread t2 = new StreamForwarder(i2pin, out, "fromI2P");
|
||||
_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();
|
||||
@@ -122,12 +141,21 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
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();
|
||||
i2ps.close();
|
||||
t1.join(30*1000);
|
||||
t2.join(30*1000);
|
||||
closedCleanly = true;
|
||||
} catch (InterruptedException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Interrupted", ex);
|
||||
@@ -140,21 +168,20 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
} finally {
|
||||
removeRef();
|
||||
try {
|
||||
if ( (s != null) && (!closedCleanly) )
|
||||
if (s != null)
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close java socket", ex);
|
||||
}
|
||||
try {
|
||||
if (i2ps != null) {
|
||||
if (!closedCleanly)
|
||||
i2ps.close();
|
||||
i2ps.setSocketErrorListener(null);
|
||||
if (i2ps != null) {
|
||||
try {
|
||||
i2ps.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
i2ps.setSocketErrorListener(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,13 +208,15 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
String direction;
|
||||
private boolean _toI2P;
|
||||
private ByteCache _cache;
|
||||
|
||||
private StreamForwarder(InputStream in, OutputStream out, String dir) {
|
||||
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
direction = dir;
|
||||
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
|
||||
_toI2P = toI2P;
|
||||
direction = (toI2P ? "toI2P" : "fromI2P");
|
||||
_cache = ByteCache.getInstance(16, NETWORK_BUFFER_SIZE);
|
||||
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
|
||||
start();
|
||||
}
|
||||
@@ -207,6 +236,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
if (_toI2P)
|
||||
totalSent += len;
|
||||
else
|
||||
totalReceived += len;
|
||||
|
||||
if (len > 0) updateActivity();
|
||||
|
||||
@@ -242,8 +275,8 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
+ ex.getMessage() + "\")");
|
||||
} catch (IOException ex) {
|
||||
if (!finished) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error(direction + ": Error forwarding", ex);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error forwarding", ex);
|
||||
}
|
||||
//else
|
||||
// _log.warn("You may ignore this", ex);
|
||||
@@ -259,10 +292,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
_log.warn(direction + ": Error closing input stream", ex);
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
out.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error closing output stream", ex);
|
||||
_log.warn(direction + ": Error flushing to close", ioe);
|
||||
}
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -57,7 +58,7 @@ 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();
|
||||
_starting = getStartOnLoad();
|
||||
}
|
||||
@@ -99,6 +100,12 @@ 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)
|
||||
*
|
||||
@@ -132,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 + "]");
|
||||
@@ -206,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) ) {
|
||||
@@ -233,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;
|
||||
@@ -294,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"); }
|
||||
@@ -311,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);
|
||||
}
|
||||
@@ -364,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) )
|
||||
@@ -375,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");
|
||||
|
@@ -180,7 +180,7 @@ 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());
|
||||
}
|
||||
|
||||
|
@@ -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 + "]";
|
||||
}
|
||||
@@ -103,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\" ");
|
||||
@@ -129,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)
|
||||
*
|
||||
@@ -141,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) )
|
||||
@@ -208,9 +256,10 @@ class WebEditPageFormGenerator {
|
||||
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);
|
||||
@@ -218,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);
|
||||
@@ -234,6 +283,14 @@ class WebEditPageFormGenerator {
|
||||
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> ");
|
||||
@@ -280,12 +337,20 @@ class WebEditPageFormGenerator {
|
||||
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=\"");
|
||||
@@ -302,9 +367,14 @@ 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++;
|
||||
|
@@ -38,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;
|
||||
@@ -51,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"
|
||||
*/
|
||||
@@ -139,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);
|
||||
@@ -168,6 +183,9 @@ public class WebEditPageHelper {
|
||||
public void setConnectDelay(String moo) {
|
||||
_connectDelay = true;
|
||||
}
|
||||
public void setProfile(String profile) {
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the form and display any resulting messages
|
||||
@@ -219,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))
|
||||
@@ -252,6 +273,8 @@ public class WebEditPageHelper {
|
||||
// creating new
|
||||
cur = new TunnelController(config, "", _privKeyGenerate);
|
||||
TunnelControllerGroup.getInstance().addController(cur);
|
||||
if (cur.getStartOnLoad())
|
||||
cur.startTunnelBackground();
|
||||
} else {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
@@ -265,14 +288,26 @@ public class WebEditPageHelper {
|
||||
if (c == cur) continue;
|
||||
if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) {
|
||||
Properties cOpt = c.getConfig("");
|
||||
if (_tunnelCount != null)
|
||||
cOpt.setProperty("option.tunnels.numInbound", _tunnelCount);
|
||||
if (_tunnelDepth != null)
|
||||
cOpt.setProperty("option.tunnels.depthInbound", _tunnelDepth);
|
||||
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, "");
|
||||
}
|
||||
}
|
||||
@@ -320,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;
|
||||
}
|
||||
|
||||
@@ -347,23 +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,28 +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");
|
||||
@@ -82,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))
|
||||
@@ -139,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());
|
||||
}
|
||||
|
||||
|
@@ -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" />
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -105,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();
|
||||
}
|
||||
}
|
||||
|
@@ -100,8 +100,10 @@ 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);
|
||||
|
@@ -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;
|
||||
|
||||
@@ -46,6 +48,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
private I2PSocketOptions _defaultOptions;
|
||||
private long _acceptTimeout;
|
||||
private String _name;
|
||||
private List _listeners;
|
||||
private static int __managerId = 0;
|
||||
|
||||
public static final short ACK = 0x51;
|
||||
@@ -76,6 +79,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
_inSockets = new HashMap(16);
|
||||
_outSockets = new HashMap(16);
|
||||
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
|
||||
_listeners = new ArrayList(1);
|
||||
setSession(session);
|
||||
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 });
|
||||
@@ -109,6 +113,15 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
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) {
|
||||
@@ -707,6 +720,17 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
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";
|
||||
|
@@ -35,6 +35,18 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
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);
|
||||
|
@@ -115,6 +115,7 @@ public class StreamSinkServer {
|
||||
}
|
||||
public void run() {
|
||||
if (_fos == null) return;
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
InputStream in = _sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
@@ -126,7 +127,8 @@ public class StreamSinkServer {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read and wrote " + read);
|
||||
}
|
||||
_log.error("Got EOF from client socket [written=" + written + "]");
|
||||
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 {
|
||||
|
@@ -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>";
|
||||
@@ -229,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);
|
||||
@@ -135,4 +137,26 @@ public class ConfigNetHelper {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@@ -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,253 @@
|
||||
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());
|
||||
|
||||
if ( (in == null) || (out == null) ) continue;
|
||||
|
||||
String name = in.getDestinationNickname();
|
||||
if (name == null)
|
||||
name = out.getDestinationNickname();
|
||||
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>Randomization</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");
|
||||
}
|
||||
}
|
@@ -41,6 +41,22 @@ public class LogsHelper {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getCriticalLogs() {
|
||||
List msgs = _context.logManager().getBuffer().getMostRecentCriticalMessages();
|
||||
StringBuffer buf = new StringBuffer(16*1024);
|
||||
buf.append("<ul>");
|
||||
buf.append("<code>\n");
|
||||
for (int i = msgs.size(); i > 0; i--) {
|
||||
String msg = (String)msgs.get(i - 1);
|
||||
buf.append("<li>");
|
||||
buf.append(msg);
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</code></ul>\n");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getServiceLogs() {
|
||||
String str = FileUtil.readTextFile("wrapper.log", 500, false);
|
||||
if (str == null)
|
||||
|
@@ -4,7 +4,9 @@ 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;
|
||||
@@ -98,13 +100,42 @@ public class ReseedHandler {
|
||||
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat");
|
||||
|
||||
byte data[] = readURL(url);
|
||||
//System.out.println("read: " + (data != null ? data.length : -1));
|
||||
writeSeed(peer, data);
|
||||
}
|
||||
|
||||
private static byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
URLConnection con = url.openConnection();
|
||||
InputStream in = con.getInputStream();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*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();
|
||||
// skip the HTTP response headers
|
||||
// (if we were smart, we'd check for HTTP 200, content-length, etc)
|
||||
int consecutiveNL = 0;
|
||||
while (true) {
|
||||
int cur = in.read();
|
||||
switch (cur) {
|
||||
case -1:
|
||||
return null;
|
||||
case '\n':
|
||||
case '\r':
|
||||
consecutiveNL++;
|
||||
break;
|
||||
default:
|
||||
consecutiveNL = 0;
|
||||
}
|
||||
if (consecutiveNL == 4)
|
||||
break;
|
||||
}
|
||||
// ok, past the headers, grab the goods
|
||||
byte buf[] = new byte[1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
@@ -113,9 +144,24 @@ public class ReseedHandler {
|
||||
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);
|
||||
@@ -126,4 +172,9 @@ public class ReseedHandler {
|
||||
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";
|
||||
}
|
||||
}
|
@@ -3,14 +3,23 @@ package net.i2p.router.web;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
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
|
||||
@@ -65,7 +74,32 @@ public class SummaryHelper {
|
||||
else
|
||||
return DataHelper.formatDuration(router.getUptime());
|
||||
}
|
||||
|
||||
|
||||
private static final DateFormat _fmt = new java.text.SimpleDateFormat("HH:mm:ss", Locale.UK);
|
||||
public String getTime() {
|
||||
if (_context == null) return "";
|
||||
|
||||
String now = null;
|
||||
synchronized (_fmt) {
|
||||
now = _fmt.format(new Date(_context.clock().now()));
|
||||
}
|
||||
|
||||
long ms = _context.clock().getOffset();
|
||||
|
||||
if (ms < 60 * 1000) {
|
||||
return now + " (" + (ms / 1000) + "s)";
|
||||
} else if (ms < 60 * 1000) {
|
||||
return now + " (" + (ms / (60 * 1000)) + "m)";
|
||||
} else if (ms < 24 * 60 * 60 * 1000) {
|
||||
return now + " (" + (ms / (60 * 60 * 1000)) + "h)";
|
||||
} else {
|
||||
return now + " (" + (ms / (24 * 60 * 60 * 1000)) + "d)";
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allowReseed() {
|
||||
return (_context.netDb().getKnownRouters() < 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve amount of used memory.
|
||||
@@ -333,16 +367,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); }
|
||||
}
|
@@ -39,14 +39,17 @@
|
||||
|
||||
<b>Bandwidth limiter</b><br />
|
||||
Inbound rate:
|
||||
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second<br />
|
||||
Inbound burst duration:
|
||||
<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 />
|
||||
Outbound rate:
|
||||
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second<br />
|
||||
Outbound burst duration:
|
||||
<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 />
|
||||
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
|
||||
@@ -61,17 +64,7 @@
|
||||
<hr />
|
||||
<b>Advanced network config:</b>
|
||||
<p>
|
||||
There are two other network settings, but no one reads this text so there's no reason
|
||||
to tell you about them. In case you actually do read this, here's the deal: by default,
|
||||
I2P will attempt to guess your IP address by having whomever it talks to tell it what
|
||||
address they think you are. If and only if you have no working TCP connections and you
|
||||
have not overridden the IP address, your router will believe them. If that doesn't sound
|
||||
ok to you, thats fine - go to the <a href="/configadvanced.jsp">advanced config</a> page
|
||||
and add "i2np.tcp.hostname=yourHostname", then go to the
|
||||
<a href="/configservice.jsp">service</a> page and do a graceful restart. We used to make
|
||||
people enter a hostname/IP address on this page, but too many people got it wrong ;)</p>
|
||||
|
||||
<p>The other advanced network option has to do with reseeding - you should never need to
|
||||
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
|
||||
|
@@ -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) {
|
||||
|
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" />
|
@@ -33,8 +33,12 @@ 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
|
||||
lets you tunnel normal TCP/IP traffic over I2P (such as the eepproxy and the irc proxy).</p>
|
||||
(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). There is also a
|
||||
<a href="http://susi.i2p/">susimail</a> web based mail client <a href="susimail/susimail">available</a> on
|
||||
the console, which is a GPL'ed application written by susi23. The addressbook application, written by
|
||||
<a href="http://ragnarok.i2p/">Ragnarok</a> helps maintain your hosts.txt files (see ./addressbook/ for
|
||||
more information).</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 the <a href="http://duck.i2p/i2p-bt/">bittorrent port</a>) can use.
|
||||
|
@@ -19,6 +19,9 @@
|
||||
<h4>Connection logs:</h4><a name="connectionlogs"> </a>
|
||||
<jsp:getProperty name="logsHelper" property="connectionLogs" />
|
||||
<hr />
|
||||
<h4>Critical logs:</h4><a name="criticallogs"> </a>
|
||||
<jsp:getProperty name="logsHelper" property="criticalLogs" />
|
||||
<hr />
|
||||
<h4>Service logs:</h4><a name="servicelogs"> </a>
|
||||
<jsp:getProperty name="logsHelper" property="serviceLogs" />
|
||||
</div>
|
||||
|
@@ -15,12 +15,14 @@
|
||||
</div>
|
||||
|
||||
<h4>
|
||||
<a href="tunnels.jsp">Tunnels</a> |
|
||||
<a href="profiles.jsp">Profiles</a> |
|
||||
<a href="netdb.jsp">Network Database</a> |
|
||||
<a href="netdb.jsp">NetDB</a> |
|
||||
<a href="logs.jsp">Logs</a> |
|
||||
<a href="oldconsole.jsp">Old console</a> |
|
||||
<a href="oldconsole.jsp">Internals</a> |
|
||||
<a href="oldstats.jsp">Stats</a> |
|
||||
<a href="i2ptunnel/" target="_blank">I2PTunnel</a>
|
||||
<a href="i2ptunnel/" target="_blank">I2PTunnel</a> |
|
||||
<a href="susimail/susimail" target="_blank">Susimail</a>
|
||||
<jsp:useBean class="net.i2p.router.web.NavHelper" id="navhelper" scope="request" />
|
||||
<jsp:setProperty name="navhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:getProperty name="navhelper" property="clientAppLinks" />
|
||||
|
@@ -10,6 +10,7 @@
|
||||
<b>Ident:</b> <jsp:getProperty name="helper" property="ident" /><br />
|
||||
<b>Version:</b> <jsp:getProperty name="helper" property="version" /><br />
|
||||
<b>Uptime:</b> <jsp:getProperty name="helper" property="uptime" /><br />
|
||||
<b>Now:</b> <jsp:getProperty name="helper" property="time" /><br />
|
||||
<b>Memory:</b> <jsp:getProperty name="helper" property="memory" /><br />
|
||||
<hr />
|
||||
|
||||
@@ -23,7 +24,7 @@
|
||||
if (helper.getActivePeers() <= 0) {
|
||||
%><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
|
||||
}
|
||||
if (helper.getActiveProfiles() <= 4) { // 4 is the min fallback
|
||||
if (helper.allowReseed()) {
|
||||
if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
|
||||
out.print(" <i>reseeding</i>");
|
||||
} else {
|
||||
|
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,10 +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
|
||||
* Switch to GNU Autoconf (?)
|
||||
* Improve build system
|
||||
|
||||
Anyone can help with these things:
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
/* 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
|
||||
|
@@ -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,15 +28,20 @@
|
||||
* 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
|
||||
|
||||
|
||||
/*
|
||||
@@ -67,18 +72,6 @@ extern "C" {
|
||||
* Some LibSAM 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
|
||||
|
||||
typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */
|
||||
|
||||
typedef char sam_pubkey_t[SAM_PUBKEY_LEN]; /* base 64 public key */
|
||||
@@ -88,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 */
|
||||
@@ -119,8 +113,8 @@ void sam_session_free(sam_sess_t **session);
|
||||
/* SAM controls - connection */
|
||||
bool sam_close(sam_sess_t *session);
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
unsigned short samport, const char *destname, sam_conn_t style,
|
||||
unsigned int tunneldepth);
|
||||
/* SAM controls - utilities */
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
bool sam_read_buffer(sam_sess_t *session);
|
||||
@@ -128,7 +122,8 @@ const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
void (*sam_diedback)(sam_sess_t *session);
|
||||
void (*sam_logback)(char *str);
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name,
|
||||
sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/* Stream commands */
|
||||
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
@@ -168,4 +163,4 @@ 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();
|
||||
@@ -79,7 +79,8 @@ void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
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,
|
||||
@@ -146,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;
|
||||
@@ -386,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) {
|
||||
@@ -394,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) {
|
||||
@@ -402,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;
|
||||
@@ -723,10 +724,10 @@ static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
|
||||
p = buf;
|
||||
printf("*RR* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(((uchar_t*)p)[x]))
|
||||
printf("%c,", ((uchar_t*)p)[x]);
|
||||
if (isprint(((byte*)p)[x]))
|
||||
printf("%c,", ((byte*)p)[x]);
|
||||
else
|
||||
printf("%03d,", ((uint8_t*)p)[x]);
|
||||
printf("%03d,", ((byte*)p)[x]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("*RR* (read2() read %d bytes)\n", n);
|
||||
@@ -877,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"
|
||||
@@ -936,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;
|
||||
@@ -963,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;
|
||||
@@ -1080,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;
|
||||
@@ -1103,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;
|
||||
@@ -1141,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));
|
||||
@@ -1399,7 +1386,7 @@ 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]))
|
||||
|
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("")]
|
160
apps/sam/csharp/src/I2P.SAM.Client/SamBaseEventHandler.cs
Normal file
160
apps/sam/csharp/src/I2P.SAM.Client/SamBaseEventHandler.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Threading;
|
||||
|
||||
namespace I2P.SAM.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional base class providing basic SAM event handling and helper
|
||||
/// methods.
|
||||
/// </summary>
|
||||
public class SamBaseEventHandler
|
||||
{
|
||||
private object _helloLock = new Object();
|
||||
private string _helloOk;
|
||||
private NameValueCollection _namingReplies = new NameValueCollection();
|
||||
private object _namingReplyLock = new Object();
|
||||
private SamReader _samReader;
|
||||
private object _sessionCreateLock = new Object();
|
||||
private string _sessionCreateOk;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <c>SamBaseEventHandler</c> instance and registers
|
||||
/// overridable handler methods for all events generated by the given
|
||||
/// <see cref="SamReader">SamReader</see>.
|
||||
/// </summary>
|
||||
public SamBaseEventHandler(SamReader samReader) {
|
||||
_samReader = samReader;
|
||||
_samReader.DestReplyReceived += new DestReplyReceivedHandler(OnDestReplyReceived);
|
||||
_samReader.HelloReplyReceived += new HelloReplyReceivedHandler(OnHelloReplyReceived);
|
||||
_samReader.NamingReplyReceived += new NamingReplyReceivedHandler(OnNamingReplyReceived);
|
||||
_samReader.SessionStatusReceived += new SessionStatusReceivedHandler(OnSessionStatusReceived);
|
||||
_samReader.StreamClosedReceived += new StreamClosedReceivedHandler(OnStreamClosedReceived);
|
||||
_samReader.StreamConnectedReceived += new StreamConnectedReceivedHandler(OnStreamConnectedReceived);
|
||||
_samReader.StreamDataReceived += new StreamDataReceivedHandler(OnStreamDataReceived);
|
||||
_samReader.StreamStatusReceived += new StreamStatusReceivedHandler(OnStreamStatusReceived);
|
||||
_samReader.UnknownMessageReceived += new UnknownMessageReceivedHandler(OnUnknownMessageReceived);
|
||||
}
|
||||
|
||||
public virtual void OnDestReplyReceived(string publicKey, string privateKey) {
|
||||
}
|
||||
|
||||
public virtual void OnHelloReplyReceived(bool ok) {
|
||||
lock (_helloLock) {
|
||||
if (ok)
|
||||
_helloOk = Boolean.TrueString;
|
||||
else
|
||||
_helloOk = Boolean.FalseString;
|
||||
|
||||
Monitor.PulseAll(_helloLock);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnNamingReplyReceived(string name, string result, string valueString, string message) {
|
||||
lock (_namingReplyLock) {
|
||||
if (result.Equals(SamBridgeMessages.NAMING_REPLY_OK))
|
||||
_namingReplies.Add(name, valueString);
|
||||
else
|
||||
_namingReplies.Add(name, result);
|
||||
|
||||
Monitor.PulseAll(_namingReplyLock);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnSessionStatusReceived(string result, string destination, string message) {
|
||||
lock (_sessionCreateLock) {
|
||||
if (result.Equals(SamBridgeMessages.SESSION_STATUS_OK))
|
||||
_sessionCreateOk = Boolean.TrueString;
|
||||
else
|
||||
_sessionCreateOk = Boolean.FalseString;
|
||||
|
||||
Monitor.PulseAll(_sessionCreateLock);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnStreamClosedReceived(string result, int id, string message) {
|
||||
}
|
||||
|
||||
public virtual void OnStreamConnectedReceived(string remoteDestination, int id) {
|
||||
}
|
||||
|
||||
public virtual void OnStreamDataReceived(int id, byte[] data, int offset, int length) {
|
||||
}
|
||||
|
||||
public virtual void OnStreamStatusReceived(string result, int id, string message) {
|
||||
}
|
||||
|
||||
public virtual void OnUnknownMessageReceived(string major, string minor, NameValueCollection parameters) {
|
||||
Console.WriteLine("wrt, [" + major + "] [" + minor + "] [" + parameters + "]");
|
||||
}
|
||||
|
||||
// Helper methods below.
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a SAM connection to be established.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method blocks until the connection is established.
|
||||
/// </remarks>
|
||||
/// <returns><c>true</c> if the handshake was successful.</returns>
|
||||
public virtual bool WaitForHelloReply() {
|
||||
while (true) {
|
||||
lock (_helloLock) {
|
||||
if (_helloOk == null)
|
||||
Monitor.Wait(_helloLock);
|
||||
else
|
||||
return Boolean.Parse(_helloOk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a SAM naming reply message.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method blocks until all naming replies are received.
|
||||
/// </remarks>
|
||||
/// <param name="name">The name to be looked for, or <c>ME</c>.</param>
|
||||
/// <returns>The matching destination for <c>name</c>, or <c>null</c> if
|
||||
/// the key was not able to be retrieved.</returns>
|
||||
public virtual string WaitForNamingReply(string name) {
|
||||
while (true) {
|
||||
lock (_namingReplyLock) {
|
||||
try {
|
||||
string valueString = _namingReplies[name];
|
||||
_namingReplies.Remove(name);
|
||||
|
||||
if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_INVALID_KEY))
|
||||
return null;
|
||||
else if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_KEY_NOT_FOUND))
|
||||
return null;
|
||||
else
|
||||
return valueString;
|
||||
|
||||
} catch (ArgumentNullException ane) {
|
||||
Monitor.Wait(_namingReplyLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a SAM session to be created.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method blocks until a SAM session is created.
|
||||
/// </remarks>
|
||||
/// <returns><c>true</c> if the SAM session was created successfully.
|
||||
// </returns>
|
||||
public virtual bool WaitForSessionCreateReply() {
|
||||
while (true) {
|
||||
lock (_sessionCreateLock) {
|
||||
if (_sessionCreateOk == null)
|
||||
Monitor.Wait(_sessionCreateLock);
|
||||
else
|
||||
return Boolean.Parse(_sessionCreateOk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
apps/sam/csharp/src/I2P.SAM.Client/SamBridgeMessages.cs
Normal file
26
apps/sam/csharp/src/I2P.SAM.Client/SamBridgeMessages.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace I2P.SAM.Client
|
||||
{
|
||||
public struct SamBridgeMessages
|
||||
{
|
||||
public const string NAMING_REPLY_INVALID_KEY = "INVALID_KEY";
|
||||
public const string NAMING_REPLY_KEY_NOT_FOUND = "KEY_NOT_FOUND";
|
||||
public const string NAMING_REPLY_OK = "OK";
|
||||
|
||||
public const string SESSION_STATUS_DUPLICATE_DEST = "DUPLICATE_DEST";
|
||||
public const string SESSION_STATUS_I2P_ERROR = "I2P_ERROR";
|
||||
public const string SESSION_STATUS_INVALID_KEY = "INVALID_KEY";
|
||||
public const string SESSION_STATUS_OK = "OK";
|
||||
|
||||
public const string STREAM_CLOSED_CANT_REACH_PEER = "CANT_REACH_PEER";
|
||||
public const string STREAM_CLOSED_I2P_ERROR = "I2P_ERROR";
|
||||
public const string STREAM_CLOSED_PEER_NOT_FOUND = "PEER_NOT_FOUND";
|
||||
public const string STREAM_CLOSED_TIMEOUT = "CLOSED";
|
||||
public const string STREAM_CLOSED_OK = "OK";
|
||||
|
||||
public const string STREAM_STATUS_CANT_REACH_PEER = "CANT_REACH_PEER";
|
||||
public const string STREAM_STATUS_I2P_ERROR = "I2P_ERROR";
|
||||
public const string STREAM_STATUS_INVALID_KEY = "INVALID_KEY";
|
||||
public const string STREAM_STATUS_TIMEOUT = "TIMEOUT";
|
||||
public const string STREAM_STATUS_OK = "OK";
|
||||
}
|
||||
}
|
278
apps/sam/csharp/src/I2P.SAM.Client/SamReader.cs
Normal file
278
apps/sam/csharp/src/I2P.SAM.Client/SamReader.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace I2P.SAM.Client
|
||||
{
|
||||
public delegate void DestReplyReceivedHandler(string publicKey, string privateKey);
|
||||
public delegate void HelloReplyReceivedHandler(bool ok);
|
||||
public delegate void NamingReplyReceivedHandler(string name, string result, string valueString, string message);
|
||||
public delegate void SessionStatusReceivedHandler(string result, string destination, string message);
|
||||
public delegate void StreamClosedReceivedHandler(string result, int id, string message);
|
||||
public delegate void StreamConnectedReceivedHandler(string remoteDestination, int id);
|
||||
public delegate void StreamDataReceivedHandler(int id, byte[] data, int offset, int length);
|
||||
public delegate void StreamStatusReceivedHandler(string result, int id, string message);
|
||||
public delegate void UnknownMessageReceivedHandler(string major, string minor, NameValueCollection parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Reads from a socket stream, producing events for any SAM message read.
|
||||
/// </summary>
|
||||
public class SamReader
|
||||
{
|
||||
public event DestReplyReceivedHandler DestReplyReceived;
|
||||
public event HelloReplyReceivedHandler HelloReplyReceived;
|
||||
public event NamingReplyReceivedHandler NamingReplyReceived;
|
||||
public event SessionStatusReceivedHandler SessionStatusReceived;
|
||||
public event StreamClosedReceivedHandler StreamClosedReceived;
|
||||
public event StreamConnectedReceivedHandler StreamConnectedReceived;
|
||||
public event StreamDataReceivedHandler StreamDataReceived;
|
||||
public event StreamStatusReceivedHandler StreamStatusReceived;
|
||||
public event UnknownMessageReceivedHandler UnknownMessageReceived;
|
||||
|
||||
private bool _isLive;
|
||||
private NetworkStream _samStream;
|
||||
private StreamReader _samStreamReader;
|
||||
|
||||
public SamReader(NetworkStream samStream) {
|
||||
_samStream = samStream;
|
||||
}
|
||||
|
||||
public void RunThread() {
|
||||
|
||||
NameValueCollection parameters = new NameValueCollection();
|
||||
|
||||
while (_isLive) {
|
||||
|
||||
string line = null;
|
||||
|
||||
_samStreamReader = new StreamReader(_samStream);
|
||||
|
||||
try {
|
||||
line = _samStreamReader.ReadLine();
|
||||
_samStreamReader.Close();
|
||||
} catch (IOException ioe) {
|
||||
Console.Error.WriteLine("Error reading from SAM: {1}", ioe);
|
||||
} catch (OutOfMemoryException oome) {
|
||||
Console.Error.WriteLine("Out of memory while reading from SAM: {1}", oome);
|
||||
return;
|
||||
}
|
||||
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
string[] tokens = line.Split(new char[1] { ' ' });
|
||||
|
||||
if (tokens.Length < 2) {
|
||||
Console.Error.WriteLine("Invalid SAM line: [" + line + "]");
|
||||
_isLive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerator tokensEnumerator = tokens.GetEnumerator();
|
||||
tokensEnumerator.MoveNext();
|
||||
string major = (string) tokensEnumerator.Current;
|
||||
tokensEnumerator.MoveNext();
|
||||
string minor = (string) tokensEnumerator.Current;
|
||||
|
||||
parameters.Clear();
|
||||
|
||||
while (tokensEnumerator.MoveNext()) {
|
||||
|
||||
string pair = (string) tokensEnumerator.Current;
|
||||
int equalsPosition = pair.IndexOf('=');
|
||||
|
||||
if ( (equalsPosition > 0) && (equalsPosition < pair.Length - 1) ) {
|
||||
|
||||
string name = pair.Substring(0, equalsPosition);
|
||||
string valueString = pair.Substring(equalsPosition + 1);
|
||||
|
||||
while ( (valueString[0] == '\"') && (valueString.Length > 0) )
|
||||
valueString = valueString.Substring(1);
|
||||
|
||||
while ( (valueString.Length > 0) && (valueString[valueString.Length - 1] == '\"') )
|
||||
valueString = valueString.Substring(0, valueString.Length - 1);
|
||||
|
||||
parameters.Set(name, valueString);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessEvent(major, minor, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessEvent(string major, string minor, NameValueCollection parameters) {
|
||||
|
||||
switch (major)
|
||||
{
|
||||
case "HELLO" :
|
||||
|
||||
if (minor.Equals("REPLY")) {
|
||||
|
||||
string result = parameters.Get("RESULT");
|
||||
|
||||
if (result.Equals("OK"))
|
||||
HelloReplyReceived(true);
|
||||
else
|
||||
HelloReplyReceived(false);
|
||||
} else {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "SESSION" :
|
||||
|
||||
if (minor.Equals("STATUS")) {
|
||||
|
||||
string result = parameters.Get("RESULT");
|
||||
string destination = parameters.Get("DESTINATION");
|
||||
string message = parameters.Get("MESSAGE");
|
||||
|
||||
SessionStatusReceived(result, destination, message);
|
||||
} else {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "STREAM" :
|
||||
|
||||
ProcessStream(major, minor, parameters);
|
||||
break;
|
||||
|
||||
case "NAMING" :
|
||||
|
||||
if (minor.Equals("REPLY")) {
|
||||
|
||||
string name = parameters.Get("NAME");
|
||||
string result = parameters.Get("RESULT");
|
||||
string valueString = parameters.Get("VALUE");
|
||||
string message = parameters.Get("MESSAGE");
|
||||
|
||||
NamingReplyReceived(name, result, valueString, message);
|
||||
} else {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "DEST" :
|
||||
|
||||
if (minor.Equals("REPLY")) {
|
||||
|
||||
string pub = parameters.Get("PUB");
|
||||
string priv = parameters.Get("PRIV");
|
||||
|
||||
DestReplyReceived(pub, priv);
|
||||
} else {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default :
|
||||
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessStream(string major, string minor, NameValueCollection parameters) {
|
||||
|
||||
/*
|
||||
* Would use another tidy switch() statement here but the Mono
|
||||
* compiler presently gets variable scopes confused within nested
|
||||
* switch() contexts. Nested switch() is broken with Mono/mcs 1.0.5,
|
||||
* 1.1.3, and SVN head.
|
||||
*/
|
||||
if (minor.Equals("STATUS")) {
|
||||
|
||||
string result = parameters.Get("RESULT");
|
||||
string id = parameters.Get("ID");
|
||||
string message = parameters.Get("MESSAGE");
|
||||
|
||||
try {
|
||||
StreamStatusReceived(result, Int32.Parse(id), message);
|
||||
} catch {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
} else if (minor.Equals("CONNECTED")) {
|
||||
|
||||
string destination = parameters.Get("DESTINATION");
|
||||
string id = parameters.Get("ID");
|
||||
|
||||
try {
|
||||
StreamConnectedReceived(destination, Int32.Parse(id));
|
||||
} catch {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
} else if (minor.Equals("CLOSED")) {
|
||||
|
||||
string result = parameters.Get("RESULT");
|
||||
string id = parameters.Get("ID");
|
||||
string message = parameters.Get("MESSAGE");
|
||||
|
||||
try {
|
||||
StreamClosedReceived(result, Int32.Parse(id), message);
|
||||
} catch {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
|
||||
} else if (minor.Equals("RECEIVED")) {
|
||||
|
||||
string id = parameters.Get("ID");
|
||||
string size = parameters.Get("SIZE");
|
||||
|
||||
if (id != null) {
|
||||
try {
|
||||
|
||||
int idValue = Int32.Parse(id);
|
||||
int sizeValue = Int32.Parse(size);
|
||||
byte[] data = new byte[sizeValue];
|
||||
int bytesRead;
|
||||
|
||||
try {
|
||||
bytesRead = _samStream.Read(data, 0, sizeValue);
|
||||
|
||||
if (bytesRead != sizeValue) {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
_isLive = false;
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
StreamDataReceived(idValue, data, 0, sizeValue);
|
||||
} catch (FormatException fe) {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
} else {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
} else {
|
||||
UnknownMessageReceived(major, minor, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartReading() {
|
||||
_isLive = true;
|
||||
ThreadStart threadStart = new ThreadStart(RunThread);
|
||||
Thread thread = new Thread(threadStart);
|
||||
thread.Name = "SAM Reader";
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
public void StopReading() {
|
||||
_isLive = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
//
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
//
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
//
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
//
|
||||
// In order to sign your assembly you must specify a key to use. Refer to the
|
||||
// Microsoft .NET Framework documentation for more information on assembly signing.
|
||||
//
|
||||
// Use the attributes below to control which key is used for signing.
|
||||
//
|
||||
// Notes:
|
||||
// (*) If no key is specified, the assembly is not signed.
|
||||
// (*) KeyName refers to a key that has been installed in the Crypto Service
|
||||
// Provider (CSP) on your machine. KeyFile refers to a file which contains
|
||||
// a key.
|
||||
// (*) If the KeyFile and the KeyName values are both specified, the
|
||||
// following processing occurs:
|
||||
// (1) If the KeyName can be found in the CSP, that key is used.
|
||||
// (2) If the KeyName does not exist and the KeyFile does exist, the key
|
||||
// in the KeyFile is installed into the CSP and used.
|
||||
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
|
||||
// When specifying the KeyFile, the location of the KeyFile should be
|
||||
// relative to the project output directory which is
|
||||
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
|
||||
// located in the project directory, you would specify the AssemblyKeyFile
|
||||
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
|
||||
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
|
||||
// documentation for more information on this.
|
||||
//
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
[assembly: AssemblyKeyName("")]
|
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
|
||||
namespace SAM.NET
|
||||
{
|
||||
class SAMTester
|
||||
{
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new SAMTester();
|
||||
}
|
||||
public SAMTester ()
|
||||
{
|
||||
SAMConnection connection1 = new SAMConnection(IPAddress.Parse("127.0.0.1"),7656);
|
||||
SAMSession session1 = new SAMSession(connection1,SAM.NET.SamSocketType.Stream,"alice");
|
||||
|
||||
SAMConnection connection2 = new SAMConnection(IPAddress.Parse("127.0.0.1"),7656);
|
||||
SAMSession session2 = new SAMSession(connection2,SAM.NET.SamSocketType.Stream,"bob");
|
||||
|
||||
SAMStream stream1 = new SAMStream(connection1,session1,233);
|
||||
stream1.Connect(session2.getKey());
|
||||
|
||||
//Wait till we are connected to destination
|
||||
while (!stream1.isConnected)
|
||||
Thread.Sleep(1000);
|
||||
|
||||
//Send some bytes
|
||||
stream1.Write(Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString() + "Hi!!!!!!"));
|
||||
|
||||
//Wait till a stream magically appears on the other side
|
||||
while (session2.getStreams().Count == 0) Thread.Sleep(1000);
|
||||
|
||||
Thread.Sleep(1000);
|
||||
foreach (SAMStream stream in session2.getStreams().Values)
|
||||
{
|
||||
Console.WriteLine("Text received on " + stream.getID() + " at " + DateTime.Now.ToLongTimeString());
|
||||
Console.WriteLine(Encoding.ASCII.GetString(stream.ReadToEnd()));
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
stream1.Close();
|
||||
connection1.Close();
|
||||
connection2.Close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
//
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
//
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
//
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
//
|
||||
// In order to sign your assembly you must specify a key to use. Refer to the
|
||||
// Microsoft .NET Framework documentation for more information on assembly signing.
|
||||
//
|
||||
// Use the attributes below to control which key is used for signing.
|
||||
//
|
||||
// Notes:
|
||||
// (*) If no key is specified, the assembly is not signed.
|
||||
// (*) KeyName refers to a key that has been installed in the Crypto Service
|
||||
// Provider (CSP) on your machine. KeyFile refers to a file which contains
|
||||
// a key.
|
||||
// (*) If the KeyFile and the KeyName values are both specified, the
|
||||
// following processing occurs:
|
||||
// (1) If the KeyName can be found in the CSP, that key is used.
|
||||
// (2) If the KeyName does not exist and the KeyFile does exist, the key
|
||||
// in the KeyFile is installed into the CSP and used.
|
||||
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
|
||||
// When specifying the KeyFile, the location of the KeyFile should be
|
||||
// relative to the project output directory which is
|
||||
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
|
||||
// located in the project directory, you would specify the AssemblyKeyFile
|
||||
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
|
||||
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
|
||||
// documentation for more information on this.
|
||||
//
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
[assembly: AssemblyKeyName("")]
|
@@ -1,271 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
|
||||
namespace SAM.NET
|
||||
{
|
||||
public enum SamSocketType
|
||||
{
|
||||
Stream,
|
||||
Datagram,
|
||||
Raw
|
||||
}
|
||||
|
||||
public class SAMConnection
|
||||
{
|
||||
private const string propertyMinVersion = "1.0";
|
||||
private const string propertyMaxVersion = "1.0";
|
||||
|
||||
private Socket _sock;
|
||||
private NetworkStream _sockStream;
|
||||
private StreamReader _sockStreamIn;
|
||||
private StreamWriter _sockStreamOut;
|
||||
|
||||
public SAMConnection(IPAddress routerIP, int port)
|
||||
{
|
||||
_sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
|
||||
IPEndPoint rEP = new IPEndPoint(routerIP,port);
|
||||
_sock.Connect(rEP);
|
||||
_sockStream = new NetworkStream(_sock);
|
||||
_sockStreamIn = new StreamReader(_sockStream);
|
||||
_sockStreamOut = new StreamWriter(_sockStream);
|
||||
try
|
||||
{
|
||||
sendVersion(propertyMinVersion,propertyMinVersion);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sock.Close();
|
||||
throw (new Exception("No SAM for you :("));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sendVersion(string min, string max)
|
||||
{
|
||||
_sockStreamOut.WriteLine("HELLO VERSION MIN=" + propertyMinVersion + " MAX=" + propertyMaxVersion);
|
||||
_sockStreamOut.Flush();
|
||||
Hashtable response = SAMUtil.parseKeyValues(_sockStreamIn.ReadLine(),2);
|
||||
if (response["RESULT"].ToString() != "OK") throw (new Exception("Version mismatch"));
|
||||
}
|
||||
|
||||
public StreamWriter getOutputStream()
|
||||
{
|
||||
return _sockStreamOut;
|
||||
}
|
||||
|
||||
public StreamReader getInputStream()
|
||||
{
|
||||
return _sockStreamIn;
|
||||
}
|
||||
|
||||
public NetworkStream getStream()
|
||||
{
|
||||
return _sockStream;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_sock.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creating a SAMSession object will automatically:
|
||||
* 1) create a sesion on SAM
|
||||
* 1) query for the base64key
|
||||
* 2) start a listening thread to catch all stream commands
|
||||
*/
|
||||
public class SAMSession
|
||||
{
|
||||
private Hashtable _streams;
|
||||
private string _sessionKey;
|
||||
|
||||
public SAMSession (SAMConnection connection, SamSocketType type, string destination)
|
||||
{
|
||||
_streams = new Hashtable();
|
||||
StreamWriter writer = connection.getOutputStream();
|
||||
StreamReader reader = connection.getInputStream();
|
||||
writer.WriteLine("SESSION CREATE STYLE=STREAM DESTINATION=" + destination);
|
||||
writer.Flush();
|
||||
Hashtable response = SAMUtil.parseKeyValues(reader.ReadLine(),2);
|
||||
if (response["RESULT"].ToString() != "OK")
|
||||
{
|
||||
throw (new Exception(response["MESSAGE"].ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteLine("NAMING LOOKUP NAME=ME");
|
||||
writer.Flush();
|
||||
response = SAMUtil.parseKeyValues(reader.ReadLine(),2);
|
||||
_sessionKey = response["VALUE"].ToString();
|
||||
SAMSessionListener listener = new SAMSessionListener(connection,this,_streams);
|
||||
new Thread(new ThreadStart(listener.startListening)).Start();
|
||||
}
|
||||
}
|
||||
public void addStream(SAMStream stream)
|
||||
{
|
||||
_streams.Add(stream.getID(),stream);
|
||||
}
|
||||
public string getKey()
|
||||
{
|
||||
return _sessionKey;
|
||||
}
|
||||
public Hashtable getStreams()
|
||||
{
|
||||
return _streams;
|
||||
}
|
||||
}
|
||||
|
||||
public class SAMSessionListener
|
||||
{
|
||||
private Hashtable _streams;
|
||||
private SAMConnection _connection;
|
||||
private SAMSession _session;
|
||||
private bool stayAlive = true;
|
||||
|
||||
public SAMSessionListener(SAMConnection connection,SAMSession session, Hashtable streams)
|
||||
{
|
||||
_streams = streams;
|
||||
_connection = connection;
|
||||
_session = session;
|
||||
}
|
||||
public void startListening()
|
||||
{
|
||||
StreamReader reader = _connection.getInputStream();
|
||||
while (stayAlive)
|
||||
{
|
||||
string response = reader.ReadLine();
|
||||
if (response.StartsWith("STREAM STATUS"))
|
||||
{
|
||||
Hashtable values = SAMUtil.parseKeyValues(response,2);
|
||||
SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())];
|
||||
if (theStream != null) theStream.ReceivedStatus(values);
|
||||
}
|
||||
if (response.StartsWith("STREAM CONNECTED"))
|
||||
{
|
||||
Hashtable values = SAMUtil.parseKeyValues(response,2);
|
||||
SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())];
|
||||
if (theStream != null) theStream.isConnected = true;
|
||||
}
|
||||
if (response.StartsWith("STREAM RECEIVED"))
|
||||
{
|
||||
Hashtable values = SAMUtil.parseKeyValues(response,2);
|
||||
int streamID = int.Parse(values["ID"].ToString());
|
||||
SAMStream theStream = (SAMStream)_streams[streamID];
|
||||
if (theStream == null) new SAMStream(_connection,_session,streamID);
|
||||
theStream = (SAMStream)_streams[streamID];
|
||||
theStream.ReceivedData(int.Parse(values["SIZE"].ToString()));
|
||||
}
|
||||
if (response.StartsWith("STREAM CLOSE"))
|
||||
{
|
||||
Hashtable values = SAMUtil.parseKeyValues(response,2);
|
||||
SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())];
|
||||
if (theStream != null) theStream.isConnected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SAMStream
|
||||
{
|
||||
private int _ID;
|
||||
private byte[] _data;
|
||||
private int _position=0;
|
||||
private int _size=0;
|
||||
private SAMSession _session;
|
||||
private SAMConnection _connection;
|
||||
public bool isConnected=false;
|
||||
|
||||
public SAMStream (SAMConnection connection,SAMSession session, int ID)
|
||||
{
|
||||
_data = new byte[100000]; //FIXME: change to non-static structure for storing stream data
|
||||
_ID = ID;
|
||||
_connection = connection;
|
||||
_session = session;
|
||||
_session.addStream(this);
|
||||
}
|
||||
|
||||
public void Connect(string destination)
|
||||
{
|
||||
StreamWriter writer = _connection.getOutputStream();
|
||||
writer.WriteLine("STREAM CONNECT ID=" + _ID.ToString() + " DESTINATION=" + destination);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
public void ReceivedData(int size) //FIXME: WTF is going on when reading the payload here? All zeros and way too many of them.
|
||||
{
|
||||
NetworkStream stream = _connection.getStream();
|
||||
int bytesRead = stream.Read(_data,_size,size);
|
||||
_size = _size + bytes;
|
||||
}
|
||||
|
||||
public void ReceivedStatus(Hashtable response)
|
||||
{
|
||||
if (response["RESULT"].ToString() != "OK")
|
||||
{
|
||||
throw (new Exception(response["RESULT"].ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
isConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getID() {return _ID;}
|
||||
|
||||
public bool DataAvailable()
|
||||
{
|
||||
return _position != _size;
|
||||
}
|
||||
|
||||
public void Write(byte[] buf)
|
||||
{
|
||||
NetworkStream stream = _connection.getStream();
|
||||
int sent = 0;
|
||||
while (sent < buf.Length)
|
||||
{
|
||||
int toSend = Math.Min(buf.Length - sent,32768);
|
||||
string header = "STREAM SEND ID=" + _ID.ToString() + " SIZE=" + toSend.ToString() + "\n";
|
||||
byte[] headerbytes = Encoding.ASCII.GetBytes(header);
|
||||
stream.Write(headerbytes,0,headerbytes.Length);
|
||||
stream.Write(buf,sent,toSend);
|
||||
sent = sent + toSend;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ReadToEnd()
|
||||
{
|
||||
byte[] ret = new byte[_size - _position];
|
||||
Array.Copy(_data,_position,ret,0,_size - _position);
|
||||
_position = _size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
StreamWriter writer = _connection.getOutputStream();
|
||||
writer.WriteLine("STREAM CLOSE " + _ID.ToString());
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public class SAMUtil
|
||||
{
|
||||
public static Hashtable parseKeyValues(string str, int startingWord)
|
||||
{
|
||||
Hashtable hash = new Hashtable();
|
||||
string strTruncated = string.Join(" ",str.Split(' '),startingWord,str.Split(' ').Length - startingWord);
|
||||
string[] sets = strTruncated.Split('=',' ');
|
||||
for (int i=0; i<sets.Length; i=i+2)
|
||||
{
|
||||
hash.Add(sets[i],sets[i+1]);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./src"
|
||||
srcdir="./src:./test"
|
||||
debug="true" deprecation="on" source="1.3" target="1.3"
|
||||
destdir="./build/obj"
|
||||
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
|
||||
|
@@ -220,6 +220,15 @@ public class SAMBridge implements Runnable {
|
||||
}
|
||||
SAMBridge bridge = new SAMBridge(host, port, opts, keyfile);
|
||||
I2PThread t = new I2PThread(bridge, "SAMListener");
|
||||
if (Boolean.valueOf(System.getProperty("sam.shutdownOnOOM", "false")).booleanValue()) {
|
||||
t.addOOMEventListener(new I2PThread.OOMEventListener() {
|
||||
public void outOfMemory(OutOfMemoryError err) {
|
||||
err.printStackTrace();
|
||||
System.err.println("OOMed, die die die");
|
||||
System.exit(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user