forked from I2P_Developers/i2p.i2p
Compare commits
214 Commits
i2p_0_3_4
...
i2p_0_3_4_
Author | SHA1 | Date | |
---|---|---|---|
78b7f228f5 | |||
84e03f8b16 | |||
288580aed7 | |||
de63bbcc86 | |||
80b8c284b4 | |||
ffff6d701f | |||
0b084ece08 | |||
28855d3fd1 | |||
f7d356dc95 | |||
104b332906 | |||
8b30852639 | |||
bdaa14c257 | |||
0234fb62fb | |||
687ca781ab | |||
3053c797e8 | |||
62d6709949 | |||
410abaf92c | |||
5e07c478f5 | |||
3eda53a97f | |||
4e25382901 | |||
fccb172e20 | |||
5a761242f5 | |||
aaaf1e14a5 | |||
3dcb9f6424 | |||
04621ff64a | |||
5053808058 | |||
9912c673bf | |||
4636f7be7b | |||
0ffc0a1959 | |||
e86032b129 | |||
87941a0975 | |||
3d6a40a683 | |||
9753470dcb | |||
a45e1b4781 | |||
54f52d37ca | |||
6e295a7afb | |||
692cd7adae | |||
7794547d30 | |||
35eaaee627 | |||
8029901ed7 | |||
342c55043d | |||
3cf363667c | |||
2f8993995b | |||
8e9c541eba | |||
7ed310ffd2 | |||
bc1b020e95 | |||
5fdff16b1e | |||
43e22a9028 | |||
e102bf9eed | |||
bf3ee5c158 | |||
2e99e3d9c5 | |||
3d7029493a | |||
a6ad2bbc5b | |||
4dc17773c4 | |||
e5d66f46c6 | |||
d2fc24e792 | |||
ec52c81f46 | |||
0eb0c4cc83 | |||
b54e6bc933 | |||
83f891138d | |||
c621940b0f | |||
a27b0a0a1e | |||
23a52dbc9a | |||
f8a57c7885 | |||
a295d0ad1e | |||
190a2147cc | |||
49573b9e72 | |||
e60b30ed44 | |||
5c10ddf54c | |||
600ece819f | |||
6bc7a3d8aa | |||
0af07e5352 | |||
8732f54c64 | |||
437d5d76e9 | |||
7378be05d3 | |||
75febe4b75 | |||
f6d8d93a1b | |||
130310fddd | |||
8bd312046d | |||
9cc96f45d0 | |||
c18fc1984d | |||
3b651076d1 | |||
352396bdc2 | |||
3c9b0273d4 | |||
5122f9989c | |||
8ebd22da96 | |||
c2d55013a6 | |||
25eda1378e | |||
dfac7bde9c | |||
348168d6c0 | |||
a1c772c8d8 | |||
f1ce1b5361 | |||
ebdc7d70a1 | |||
c5947c23bb | |||
eeb1852d95 | |||
2f28a635a9 | |||
d524c77560 | |||
0025d94aa4 | |||
bb5ae2922d | |||
fbe9fe1ba8 | |||
007194d674 | |||
cdd74505d7 | |||
0aa023189d | |||
79aa10dfcb | |||
9ecfda0110 | |||
b89e26c460 | |||
97e5952544 | |||
8627328047 | |||
ec0c912c6f | |||
953de3f1f2 | |||
e1264de514 | |||
5abd2b400c | |||
2c2a103676 | |||
44af799b66 | |||
ec22ba3248 | |||
7fcc05c037 | |||
edf17d0a46 | |||
9cccd0bfc9 | |||
e57c010e3d | |||
4dfcf1c1c8 | |||
8d7786e97d | |||
2cb519cd06 | |||
bc46ad4331 | |||
be08e8f23b | |||
f937809903 | |||
c0f32c942d | |||
39c5c830bb | |||
83c8953d1b | |||
4b100a5a64 | |||
b7e50e0b3a | |||
6933052de7 | |||
22d945f7b7 | |||
b81c5628ce | |||
cdb4576bd7 | |||
4859cd7dcf | |||
3f70593ca8 | |||
676288e6c0 | |||
1aa3e0cc5a | |||
b0f8064d0d | |||
e5e85732d4 | |||
f97c1ef0d9 | |||
83cf815160 | |||
fea62a529b | |||
2cff5ae2bb | |||
8aa29f5340 | |||
8051bfef1d | |||
85bc79ab1b | |||
97e5588184 | |||
e622fdc885 | |||
4f81e1debe | |||
9ccfd852d8 | |||
9df57a47d5 | |||
f2cadb7278 | |||
3f6e7cb84c | |||
4ed4ce8240 | |||
4373956a3f | |||
36fb99a00d | |||
8f5d325c4d | |||
8d8b6da0bf | |||
3a61d260d7 | |||
5bc433d1c8 | |||
a0e4bbac6f | |||
d44d8cc53d | |||
1305969247 | |||
94becebafa | |||
8add433966 | |||
c5b289fb1f | |||
f85ce180ed | |||
8101fa1c92 | |||
8a091e0205 | |||
edc3a54ad3 | |||
a3cd7d1068 | |||
337441b8de | |||
bd78a66bd4 | |||
96f9618081 | |||
cf7be2d601 | |||
598732915e | |||
99c18396ab | |||
6d5dd81066 | |||
7cd9451a22 | |||
29b5a7c5c2 | |||
393a04165e | |||
e97e834a5b | |||
bec685682b | |||
34f119ca23 | |||
09ed1b1f9e | |||
fcb109f46d | |||
f30823e4ac | |||
c04885449d | |||
8c31e47eeb | |||
d8ee5c180b | |||
823f4a26b3 | |||
a05e8a446d | |||
21126f766c | |||
8f46ead756 | |||
75652fc2c4 | |||
7cdc46f007 | |||
ed9f9625ae | |||
7b60d3dab9 | |||
a6993fa489 | |||
bc2774bde4 | |||
d10dc1e8d3 | |||
48556de92b | |||
7f6b477d2e | |||
11d8c67d12 | |||
fd2a4029e7 | |||
4467928845 | |||
cc85a00bfd | |||
15d58ecdcd | |||
08d93b9a78 | |||
a75a999e3b | |||
684ef709f5 | |||
2e98dd09e7 | |||
6635425bbc |
348
apps/bogobot/Bogobot.java
Normal file
348
apps/bogobot/Bogobot.java
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* bogobot - A simple join/part stats logger bot for I2P IRC.
|
||||
*
|
||||
* Bogobot.java
|
||||
* 2004 The I2P Project
|
||||
* This code is public domain.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.log4j.DailyRollingFileAppender;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.PatternLayout;
|
||||
|
||||
import org.jibble.pircbot.IrcException;
|
||||
import org.jibble.pircbot.NickAlreadyInUseException;
|
||||
import org.jibble.pircbot.PircBot;
|
||||
import org.jibble.pircbot.User;
|
||||
|
||||
/**
|
||||
* TODO 0.5 Add multi-server capability.
|
||||
*
|
||||
* @author hypercubus, oOo
|
||||
* @version 0.4
|
||||
*/
|
||||
public class Bogobot extends PircBot {
|
||||
|
||||
private static final String INTERVAL_DAILY = "daily";
|
||||
private static final String INTERVAL_MONTHLY = "monthly";
|
||||
private static final String INTERVAL_WEEKLY = "weekly";
|
||||
|
||||
private boolean _isIntentionalDisconnect = false;
|
||||
private long _lastUserlistCommandTimestamp = 0;
|
||||
private Logger _logger = Logger.getLogger(Bogobot.class);
|
||||
|
||||
private int _currentAutoRoundTripTag = 0;
|
||||
private long _lastAutoRoundTripSentTime = 0;
|
||||
private Timer _tickTimer;
|
||||
|
||||
private String _configFile;
|
||||
|
||||
private String _botPrimaryNick;
|
||||
private String _botSecondaryNick;
|
||||
private String _botNickservPassword;
|
||||
private String _botUsername;
|
||||
private String _ownerPrimaryNick;
|
||||
private String _ownerSecondaryNick;
|
||||
private String _botShutdownPassword;
|
||||
private String _ircChannel;
|
||||
private String _ircServer;
|
||||
private int _ircServerPort;
|
||||
private boolean _isLoggerEnabled;
|
||||
private String _loggedHostnamePattern;
|
||||
private boolean _isUserlistCommandEnabled;
|
||||
private String _logFilePrefix;
|
||||
private String _logFileRotationInterval;
|
||||
private long _commandAntiFloodInterval;
|
||||
private String _userlistCommandTrigger;
|
||||
private boolean _isRoundTripDelayEnabled;
|
||||
private int _roundTripDelayPeriod;
|
||||
|
||||
class BogobotTickTask extends TimerTask {
|
||||
private Bogobot _caller;
|
||||
|
||||
public BogobotTickTask(Bogobot caller) {
|
||||
_caller = caller;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_caller.onTick();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadConfigFile(String configFileName) {
|
||||
|
||||
_configFile = configFileName;
|
||||
|
||||
Properties config = new Properties();
|
||||
FileInputStream fis = null;
|
||||
|
||||
try {
|
||||
fis = new FileInputStream(configFileName);
|
||||
config.load(fis);
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error loading configuration file");
|
||||
System.exit(2);
|
||||
|
||||
} finally {
|
||||
if (fis != null) try {
|
||||
fis.close();
|
||||
} catch (IOException ioe) { // nop
|
||||
}
|
||||
}
|
||||
|
||||
_botPrimaryNick = config.getProperty("botPrimaryNick", "somebot");
|
||||
_botSecondaryNick = config.getProperty("botSecondaryNick", "somebot_");
|
||||
_botNickservPassword = config.getProperty("botNickservPassword", "");
|
||||
_botUsername = config.getProperty("botUsername", "somebot");
|
||||
|
||||
_ownerPrimaryNick = config.getProperty("ownerPrimaryNick", "somenick");
|
||||
_ownerSecondaryNick = config.getProperty("ownerSecondaryNick", "somenick_");
|
||||
|
||||
_botShutdownPassword = config.getProperty("botShutdownPassword", "take off eh");
|
||||
|
||||
_ircChannel = config.getProperty("ircChannel", "#i2p-chat");
|
||||
_ircServer = config.getProperty("ircServer", "irc.duck.i2p");
|
||||
_ircServerPort = Integer.parseInt(config.getProperty("ircServerPort", "6668"));
|
||||
|
||||
_isLoggerEnabled = Boolean.valueOf(config.getProperty("isLoggerEnabled", "true")).booleanValue();
|
||||
_loggedHostnamePattern = config.getProperty("loggedHostnamePattern", "");
|
||||
_logFilePrefix = config.getProperty("logFilePrefix", "irc.duck.i2p.i2p-chat");
|
||||
_logFileRotationInterval = config.getProperty("logFileRotationInterval", INTERVAL_DAILY);
|
||||
|
||||
_isRoundTripDelayEnabled = Boolean.valueOf(config.getProperty("isRoundTripDelayEnabled", "false")).booleanValue();
|
||||
_roundTripDelayPeriod = Integer.parseInt(config.getProperty("roundTripDelayPeriod", "300"));
|
||||
|
||||
_isUserlistCommandEnabled = Boolean.valueOf(config.getProperty("isUserlistCommandEnabled", "true")).booleanValue();
|
||||
_userlistCommandTrigger = config.getProperty("userlistCommandTrigger", "!who");
|
||||
_commandAntiFloodInterval = Long.parseLong(config.getProperty("commandAntiFloodInterval", "60"));
|
||||
}
|
||||
|
||||
public Bogobot(String configFileName) {
|
||||
|
||||
loadConfigFile(configFileName);
|
||||
|
||||
this.setName(_botPrimaryNick);
|
||||
this.setLogin(_botUsername);
|
||||
_tickTimer = new Timer();
|
||||
_tickTimer.scheduleAtFixedRate(new BogobotTickTask(this), 1000, 10 * 1000);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Bogobot bogobot;
|
||||
|
||||
if (args.length > 1) {
|
||||
System.err.println("Too many arguments, the only allowed parameter is configuration file name");
|
||||
System.exit(3);
|
||||
}
|
||||
if (args.length == 1) {
|
||||
bogobot = new Bogobot(args[0]);
|
||||
} else {
|
||||
bogobot = new Bogobot("bogobot.config");
|
||||
}
|
||||
|
||||
bogobot.setVerbose(true);
|
||||
|
||||
if (bogobot._isLoggerEnabled)
|
||||
bogobot.initLogger();
|
||||
|
||||
bogobot.connectToServer();
|
||||
}
|
||||
|
||||
protected void onTick() {
|
||||
// Tick about once every ten seconds
|
||||
|
||||
if (this.isConnected() && _isRoundTripDelayEnabled) {
|
||||
if( ( (System.currentTimeMillis() - _lastAutoRoundTripSentTime) >= (_roundTripDelayPeriod * 1000) ) && (this.getOutgoingQueueSize() == 0) ) {
|
||||
// Connected, sending queue is empty and last RoundTrip is more then 5 minutes old -> Send a new one
|
||||
_currentAutoRoundTripTag ++;
|
||||
_lastAutoRoundTripSentTime = System.currentTimeMillis();
|
||||
sendNotice(this.getNick(),"ROUNDTRIP " + _currentAutoRoundTripTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onDisconnect() {
|
||||
|
||||
if (_isIntentionalDisconnect)
|
||||
System.exit(0);
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " *** (Lost connection)");
|
||||
|
||||
try {
|
||||
Thread.sleep(60000);
|
||||
} catch (InterruptedException e) {
|
||||
// No worries.
|
||||
}
|
||||
connectToServer();
|
||||
}
|
||||
|
||||
protected void onJoin(String channel, String sender, String login, String hostname) {
|
||||
|
||||
if (_isLoggerEnabled) {
|
||||
if (sender.equals(this.getName())) {
|
||||
|
||||
_logger.info(System.currentTimeMillis() + " joins *** " + _botPrimaryNick + " ***");
|
||||
|
||||
} else {
|
||||
|
||||
String prependedHostname = "@" + hostname;
|
||||
if (prependedHostname.endsWith(_loggedHostnamePattern)) {
|
||||
_logger.info(System.currentTimeMillis() + " joins " + sender);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onMessage(String channel, String sender, String login, String hostname, String message) {
|
||||
message = message.replaceFirst("<.+?> ", "");
|
||||
if (_isUserlistCommandEnabled && message.equals(_userlistCommandTrigger)) {
|
||||
|
||||
if (System.currentTimeMillis() - _lastUserlistCommandTimestamp < _commandAntiFloodInterval * 1000)
|
||||
return;
|
||||
|
||||
Object[] users = getUsers(_ircChannel);
|
||||
String output = "Userlist for " + _ircChannel + ": ";
|
||||
|
||||
for (int i = 0; i < users.length; i++)
|
||||
output += "[" + ((User) users[i]).getNick() + "] ";
|
||||
|
||||
sendMessage(_ircChannel, output);
|
||||
_lastUserlistCommandTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPart(String channel, String sender, String login, String hostname) {
|
||||
|
||||
if (_isLoggerEnabled) {
|
||||
if (sender.equals(this.getName())) {
|
||||
_logger.info(System.currentTimeMillis() + " parts *** " + _botPrimaryNick + " ***");
|
||||
} else {
|
||||
String prependedHostname = "@" + hostname;
|
||||
if (prependedHostname.endsWith(_loggedHostnamePattern)) {
|
||||
_logger.info(System.currentTimeMillis() + " parts " + sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void onPrivateMessage(String sender, String login, String hostname, String message) {
|
||||
/*
|
||||
* Nobody else except the bot's owner can shut it down, unless of
|
||||
* course the owner's nick isn't registered and someone's spoofing it.
|
||||
*/
|
||||
if ((sender.equals(_ownerPrimaryNick) || sender.equals(_ownerSecondaryNick)) && message.equals(_botShutdownPassword)) {
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " ***");
|
||||
|
||||
_isIntentionalDisconnect = true;
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {
|
||||
String prependedHostname = "@" + sourceHostname;
|
||||
|
||||
if (sourceNick.equals(_botPrimaryNick))
|
||||
changeNick(_botPrimaryNick);
|
||||
|
||||
if (_isLoggerEnabled) {
|
||||
if (prependedHostname.endsWith(_loggedHostnamePattern)) {
|
||||
_logger.info(System.currentTimeMillis() + " quits " + sourceNick + " " + reason);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void connectToServer() {
|
||||
|
||||
int loginAttempts = 0;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
connect(_ircServer, _ircServerPort);
|
||||
break;
|
||||
} catch (NickAlreadyInUseException e) {
|
||||
if (loginAttempts == 1) {
|
||||
System.out.println("Sorry, the primary and secondary bot nicks are already taken. Exiting.");
|
||||
System.exit(1);
|
||||
}
|
||||
loginAttempts++;
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e1) {
|
||||
// Hmph.
|
||||
}
|
||||
|
||||
if (getName().equals(_botPrimaryNick))
|
||||
setName(_botSecondaryNick);
|
||||
else
|
||||
setName(_botPrimaryNick);
|
||||
|
||||
continue;
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error during login: ");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
} catch (IrcException e) {
|
||||
System.out.println("Error during login: ");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
joinChannel(_ircChannel);
|
||||
}
|
||||
|
||||
protected void onNotice(String sourceNick, String sourceLogin, String sourceHostname, String target, String notice) {
|
||||
|
||||
if (sourceNick.equals("NickServ") && (notice.indexOf("/msg NickServ IDENTIFY") >= 0) && (_botNickservPassword != "")) {
|
||||
sendRawLineViaQueue("NICKSERV IDENTIFY " + _botNickservPassword);
|
||||
}
|
||||
|
||||
if (sourceNick.equals(getNick()) && notice.equals( "ROUNDTRIP " + _currentAutoRoundTripTag)) {
|
||||
int delay = (int)((System.currentTimeMillis() - _lastAutoRoundTripSentTime) / 100);
|
||||
// sendMessage(_ircChannel, "Round-trip delay = " + (delay / 10.0f) + " seconds");
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " roundtrip " + delay);
|
||||
}
|
||||
}
|
||||
|
||||
private void initLogger() {
|
||||
|
||||
String logFilePath = "logs" + File.separator + _logFilePrefix;
|
||||
DailyRollingFileAppender rollingFileAppender = null;
|
||||
|
||||
if (!(new File("logs").exists()))
|
||||
(new File("logs")).mkdirs();
|
||||
|
||||
try {
|
||||
|
||||
if (_logFileRotationInterval.equals("monthly"))
|
||||
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM'.log'");
|
||||
else if (_logFileRotationInterval.equals("weekly"))
|
||||
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-ww'.log'");
|
||||
else
|
||||
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM-dd'.log'");
|
||||
|
||||
rollingFileAppender.setThreshold(Level.INFO);
|
||||
_logger.addAppender(rollingFileAppender);
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error: Couldn't create or open an existing log file. Exiting.");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
352
apps/bogobot/Bogoparser.java
Normal file
352
apps/bogobot/Bogoparser.java
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* bogoparser - A simple logfile analyzer for bogobot.
|
||||
*
|
||||
* Bogoparser.java
|
||||
* 2004 The I2P Project
|
||||
* This code is public domain.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author hypercubus
|
||||
* @version 0.4
|
||||
*/
|
||||
public class Bogoparser {
|
||||
|
||||
private static void displayUsageAndExit() {
|
||||
System.out.println("\r\nUsage:\r\n\r\n java Bogoparser [--by-duration] <logfile>\r\n");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Bogoparser bogoparser;
|
||||
|
||||
if (args.length < 1 || args.length > 2)
|
||||
displayUsageAndExit();
|
||||
|
||||
if (args.length == 2) {
|
||||
if (!args[0].equals("--by-duration"))
|
||||
displayUsageAndExit();
|
||||
bogoparser = new Bogoparser(args[1], true);
|
||||
}
|
||||
|
||||
if (args.length == 1)
|
||||
bogoparser = new Bogoparser(args[0], false);
|
||||
}
|
||||
|
||||
private Bogoparser(String logfile, boolean sortByDuration) {
|
||||
|
||||
ArrayList sortedSessions;
|
||||
|
||||
if (sortByDuration) {
|
||||
sortedSessions = sortSessionsByDuration(calculateSessionDurations(sortSessionsByTime(readLogfile(logfile))));
|
||||
formatAndOutputByDuration(sortedSessions);
|
||||
} else {
|
||||
sortedSessions = calculateSessionDurations(sortSessionsByQuitReason(sortSessionsByNick(sortSessionsByTime(readLogfile(logfile)))));
|
||||
formatAndOutput(sortedSessions);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList calculateSessionDurations(ArrayList sortedSessionsByQuitReasonOrDuration) {
|
||||
|
||||
ArrayList calculatedSessionDurations = new ArrayList();
|
||||
|
||||
for (int i = 0; i+1 < sortedSessionsByQuitReasonOrDuration.size(); i += 2) {
|
||||
|
||||
String joinsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i);
|
||||
String[] joinsEntryFields = joinsEntry.split(" ");
|
||||
|
||||
String quitsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i+1);
|
||||
Pattern p = Pattern.compile("^([^ ]+) [^ ]+ ([^ ]+) (.*)$");
|
||||
Matcher m = p.matcher(quitsEntry);
|
||||
|
||||
if (m.matches()) {
|
||||
|
||||
String currentJoinTime = joinsEntryFields[0];
|
||||
String currentNick = m.group(2);
|
||||
String currentQuitReason = m.group(3);
|
||||
String currentQuitTime = m.group(1);
|
||||
long joinsTimeInMilliseconds;
|
||||
long quitsTimeInMilliseconds;
|
||||
long sessionLengthInMilliseconds;
|
||||
|
||||
joinsTimeInMilliseconds = Long.parseLong(currentJoinTime);
|
||||
quitsTimeInMilliseconds = Long.parseLong(currentQuitTime);
|
||||
sessionLengthInMilliseconds = quitsTimeInMilliseconds - joinsTimeInMilliseconds;
|
||||
|
||||
String hours = "" + sessionLengthInMilliseconds/1000/60/60;
|
||||
String minutes = "" + (sessionLengthInMilliseconds/1000/60)%60;
|
||||
|
||||
if (hours.length() < 2)
|
||||
hours = "0" + hours;
|
||||
|
||||
if (hours.length() < 3)
|
||||
hours = "0" + hours;
|
||||
|
||||
if (minutes.length() < 2)
|
||||
minutes = "0" + minutes;
|
||||
|
||||
int columnPadding = 19-currentNick.length();
|
||||
String columnPaddingString = " ";
|
||||
|
||||
for (int j = 0; j < columnPadding; j++)
|
||||
columnPaddingString = columnPaddingString + " ";
|
||||
|
||||
calculatedSessionDurations.add(sessionLengthInMilliseconds + " " + currentNick + columnPaddingString + " online " + hours + " hours " + minutes + " minutes " + currentQuitReason);
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + quitsEntry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return calculatedSessionDurations;
|
||||
}
|
||||
|
||||
private void formatAndOutput(ArrayList sortedSessions) {
|
||||
|
||||
String quitReason = null;
|
||||
|
||||
for (int i = 0; i < sortedSessions.size(); i++) {
|
||||
|
||||
String entry = (String) sortedSessions.get(i);
|
||||
Pattern p = Pattern.compile("^[\\d]+ ([^ ]+ +online [\\d]+ hours [\\d]+ minutes) (.*)$");
|
||||
Matcher m = p.matcher(entry);
|
||||
|
||||
if (m.matches()) {
|
||||
|
||||
if (quitReason == null) {
|
||||
quitReason = m.group(2);
|
||||
System.out.println("\r\nQUIT: " + ((m.group(2).equals("")) ? "No Reason Given" : quitReason) + "\r\n");
|
||||
}
|
||||
|
||||
String tempQuitReason = m.group(2);
|
||||
String tempSession = m.group(1);
|
||||
|
||||
if (tempQuitReason.equals(quitReason)) {
|
||||
System.out.println(" " + tempSession);
|
||||
} else {
|
||||
quitReason = null;
|
||||
i -= 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
System.out.println("\r\n");
|
||||
}
|
||||
|
||||
private void formatAndOutputByDuration(ArrayList sortedSessions) {
|
||||
System.out.println("\r\n");
|
||||
|
||||
for (int i = 0; i < sortedSessions.size(); i++) {
|
||||
String[] columns = ((String) sortedSessions.get(i)).split(" ", 2);
|
||||
System.out.println(columns[1]);
|
||||
}
|
||||
|
||||
System.out.println("\r\n");
|
||||
}
|
||||
|
||||
private ArrayList readLogfile(String logfile) {
|
||||
|
||||
ArrayList log = new ArrayList();
|
||||
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(logfile)));
|
||||
|
||||
for (String line; (line = in.readLine()) != null; )
|
||||
log.add(line);
|
||||
|
||||
in.close();
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("\r\nError: Can't find logfile '" + logfile + "'.\r\n");
|
||||
System.exit(1);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.out.println("\r\nError: Can't read logfile '" + logfile + "'.\r\n");
|
||||
System.exit(1);
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs an odd-even transposition sort.
|
||||
*/
|
||||
private ArrayList sortSessionsByDuration(ArrayList calculatedSessionDurations) {
|
||||
|
||||
for (int i = 0; i < calculatedSessionDurations.size()/2; i++) {
|
||||
for (int j = 0; j+1 < calculatedSessionDurations.size(); j += 2) {
|
||||
|
||||
String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
|
||||
long currentDuration = Long.parseLong(currentDurationString[0]);
|
||||
String[] nextDurationString = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
|
||||
long nextDuration = Long.parseLong(nextDurationString[0]);
|
||||
|
||||
if (currentDuration > nextDuration) {
|
||||
calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
|
||||
calculatedSessionDurations.remove(j+2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 1; j+1 < calculatedSessionDurations.size(); j += 2) {
|
||||
|
||||
String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
|
||||
long currentDuration = Long.parseLong(currentDurationString[0]);
|
||||
String[] nextDurationString = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
|
||||
long nextDuration = Long.parseLong(nextDurationString[0]);
|
||||
|
||||
if (currentDuration > nextDuration) {
|
||||
calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
|
||||
calculatedSessionDurations.remove(j+2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return calculatedSessionDurations;
|
||||
}
|
||||
|
||||
private ArrayList sortSessionsByNick(ArrayList sortedSessionsByTime) {
|
||||
|
||||
ArrayList sortedSessionsByNick = new ArrayList();
|
||||
|
||||
while (sortedSessionsByTime.size() != 0) {
|
||||
|
||||
String entry = (String) sortedSessionsByTime.get(0);
|
||||
String[] entryFields = entry.split(" ");
|
||||
String currentNick = entryFields[2];
|
||||
|
||||
sortedSessionsByNick.add(entry);
|
||||
sortedSessionsByNick.add(sortedSessionsByTime.get(1));
|
||||
sortedSessionsByTime.remove(0);
|
||||
sortedSessionsByTime.remove(0);
|
||||
for (int i = 0; i+1 < sortedSessionsByTime.size(); i += 2) {
|
||||
|
||||
String nextEntry = (String) sortedSessionsByTime.get(i);
|
||||
String[] nextEntryFields = nextEntry.split(" ");
|
||||
|
||||
if (nextEntryFields[2].equals(currentNick)) {
|
||||
sortedSessionsByNick.add(nextEntry);
|
||||
sortedSessionsByNick.add(sortedSessionsByTime.get(i+1));
|
||||
sortedSessionsByTime.remove(i);
|
||||
sortedSessionsByTime.remove(i);
|
||||
i -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortedSessionsByNick;
|
||||
}
|
||||
|
||||
private ArrayList sortSessionsByQuitReason(ArrayList sortedSessionsByNick) {
|
||||
|
||||
ArrayList sortedSessionsByQuitReason = new ArrayList();
|
||||
|
||||
while (sortedSessionsByNick.size() != 0) {
|
||||
|
||||
String entry = (String) sortedSessionsByNick.get(1);
|
||||
Pattern p = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
|
||||
Matcher m = p.matcher(entry);
|
||||
|
||||
if (m.matches()) {
|
||||
|
||||
String currentQuitReason = m.group(1);
|
||||
|
||||
sortedSessionsByQuitReason.add(sortedSessionsByNick.get(0));
|
||||
sortedSessionsByQuitReason.add(entry);
|
||||
sortedSessionsByNick.remove(0);
|
||||
sortedSessionsByNick.remove(0);
|
||||
for (int i = 0; i+1 < sortedSessionsByNick.size(); i += 2) {
|
||||
|
||||
String nextEntry = (String) sortedSessionsByNick.get(i+1);
|
||||
Pattern p2 = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
|
||||
Matcher m2 = p2.matcher(nextEntry);
|
||||
|
||||
if (m2.matches()) {
|
||||
|
||||
String nextQuitReason = m2.group(1);
|
||||
|
||||
if (nextQuitReason.equals(currentQuitReason)) {
|
||||
sortedSessionsByQuitReason.add(sortedSessionsByNick.get(i));
|
||||
sortedSessionsByQuitReason.add(nextEntry);
|
||||
sortedSessionsByNick.remove(i);
|
||||
sortedSessionsByNick.remove(i);
|
||||
i -= 2;
|
||||
}
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + nextEntry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return sortedSessionsByQuitReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sessions terminated with "parts" messages instead of "quits" are filtered
|
||||
* out.
|
||||
*/
|
||||
private ArrayList sortSessionsByTime(ArrayList log) {
|
||||
|
||||
ArrayList sortedSessionsByTime = new ArrayList();
|
||||
|
||||
mainLoop:
|
||||
while (log.size() > 0) {
|
||||
|
||||
String entry = (String) log.get(0);
|
||||
String[] entryFields = entry.split(" ");
|
||||
|
||||
if (entryFields[1].equals("quits") && !entryFields[1].equals("joins")) {
|
||||
/*
|
||||
* Discard entry. The specified log either doesn't contain
|
||||
* the corresponding "joins" time for this quit entry or the
|
||||
* entry is a "parts" or unknown message, and in both cases
|
||||
* the entry's data is useless.
|
||||
*/
|
||||
log.remove(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 1; i < log.size(); i++) { // Find corresponding "quits" entry.
|
||||
|
||||
String tempEntry = (String) log.get(i);
|
||||
String[] tempEntryFields = tempEntry.split(" ");
|
||||
|
||||
if (tempEntryFields[2].equals(entryFields[2])) { // Check if the nick fields for the two entries match.
|
||||
if (!tempEntryFields[1].equals("quits")) {
|
||||
if (tempEntryFields[1].equals("joins")) { // Don't discard a subsequent "joins" entry.
|
||||
log.remove(0);
|
||||
continue mainLoop;
|
||||
}
|
||||
log.remove(i);
|
||||
continue;
|
||||
}
|
||||
sortedSessionsByTime.add(entry);
|
||||
sortedSessionsByTime.add(tempEntry);
|
||||
log.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Discard "joins" entry. The specified log doesn't contain the
|
||||
* corresponding "quits" time for this entry so the entry's
|
||||
* data is useless.
|
||||
*/
|
||||
|
||||
log.remove(0);
|
||||
}
|
||||
|
||||
return sortedSessionsByTime;
|
||||
}
|
||||
}
|
48
apps/bogobot/LICENSE_log4j.txt
Normal file
48
apps/bogobot/LICENSE_log4j.txt
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* ============================================================================
|
||||
* The Apache Software License, Version 1.1
|
||||
* ============================================================================
|
||||
*
|
||||
* Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modifica-
|
||||
* tion, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution, if any, must
|
||||
* include the following acknowledgment: "This product includes software
|
||||
* developed by the Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself, if
|
||||
* and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "log4j" and "Apache Software Foundation" must not be used to
|
||||
* endorse or promote products derived from this software without prior
|
||||
* written permission. For written permission, please contact
|
||||
* apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache", nor may
|
||||
* "Apache" appear in their name, without prior written permission of the
|
||||
* Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
|
||||
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* on behalf of the Apache Software Foundation. For more information on the
|
||||
* Apache Software Foundation, please see <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
340
apps/bogobot/LICENSE_pircbot.txt
Normal file
340
apps/bogobot/LICENSE_pircbot.txt
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
1
apps/bogobot/bogobot.bat
Normal file
1
apps/bogobot/bogobot.bat
Normal file
@ -0,0 +1 @@
|
||||
java -cp .;log4j-1.2.8.jar;pircbot.jar Bogobot
|
101
apps/bogobot/bogobot.config
Normal file
101
apps/bogobot/bogobot.config
Normal file
@ -0,0 +1,101 @@
|
||||
#####
|
||||
# Bogobot user configuration
|
||||
#####
|
||||
|
||||
###
|
||||
# The bot's nick and backup nick. You will probably want to register these with
|
||||
# the IRC server's NickServ.(a NickServ interface is forthcoming).
|
||||
#
|
||||
botPrimaryNick=somebot
|
||||
botSecondaryNick=somebot_
|
||||
|
||||
###
|
||||
# The bot's password required by Nickserv service's identify command.
|
||||
# You have to register the nickname yourself first, the bot will not.
|
||||
#
|
||||
botNickservPassword=
|
||||
|
||||
###
|
||||
# The bot's username. Appears in the whois replies
|
||||
#
|
||||
botUsername=somebot
|
||||
|
||||
#####
|
||||
# The bot owner's nick and backup nick. One of these must match the owner's
|
||||
# currently-used nick or else remote shutdown will not be possible. You will
|
||||
# probably want to register these with the IRC server's NickServ.
|
||||
#
|
||||
ownerPrimaryNick=somenick
|
||||
ownerSecondaryNick=somenick_
|
||||
|
||||
###
|
||||
# The bot will disconnect and shut down when sent this password via private
|
||||
# message (aka query) from either of the owner nicks specified above. DO NOT USE
|
||||
# THIS DEFAULT VALUE!
|
||||
#
|
||||
botShutdownPassword=take off eh
|
||||
|
||||
###
|
||||
# The server, channel, and port the bot will connect to.
|
||||
#
|
||||
ircChannel=#i2p-chat
|
||||
ircServer=irc.duck.i2p
|
||||
ircServerPort=6668
|
||||
|
||||
###
|
||||
# Set to "true" to enable logging, else "false" (but don't use quotation marks).
|
||||
#
|
||||
isLoggerEnabled=true
|
||||
|
||||
###
|
||||
# Restrict logging of joins and parts on the user hostname.
|
||||
# Leave empty to log all of them
|
||||
# Prepend with a @ for a perfect match
|
||||
# Otherwise, specify the required end of the user hostname
|
||||
#
|
||||
loggedHostnamePattern=@free.duck.i2p
|
||||
|
||||
###
|
||||
# The prefix to be used for the filenames of logs.
|
||||
#
|
||||
logFilePrefix=irc.duck.i2p.i2p-chat
|
||||
|
||||
###
|
||||
# How often the logs should be rotated. Either "daily", "weekly", or "monthly"
|
||||
# (but don't use quotation marks).
|
||||
#
|
||||
logFileRotationInterval=daily
|
||||
|
||||
###
|
||||
# Set to "true" to enable the regular round-trip delay computation,
|
||||
# else "false" (but don't use quotation marks).
|
||||
#
|
||||
isRoundTripDelayEnabled=false
|
||||
|
||||
###
|
||||
# How often should the round-trip delay be recorded.
|
||||
# (in seconds)
|
||||
#
|
||||
roundTripDelayPeriod=300
|
||||
|
||||
###
|
||||
# Set to "true" to enable the userlist command, else "false" (but don't use
|
||||
# quotation marks).
|
||||
#
|
||||
isUserlistCommandEnabled=true
|
||||
|
||||
###
|
||||
# The userlist trigger command to listen for. It is a good idea to prefix
|
||||
# triggers with some non-alphanumeric character in order to avoid accidental
|
||||
# trigger use during normal channel conversation. In most cases you will
|
||||
# probably want to choose a unique trigger here that no other bots in the
|
||||
# channel will respond to.
|
||||
#
|
||||
userlistCommandTrigger=!who
|
||||
|
||||
###
|
||||
# The number of seconds to rest after replying to a userlist command issued by
|
||||
# a user in the channel. The bot will ignore subsequent userlist commands during
|
||||
# this period. This helps prevent flooding.
|
||||
#
|
||||
commandAntiFloodInterval=60
|
2
apps/bogobot/bogobot.sh
Normal file
2
apps/bogobot/bogobot.sh
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
java -cp .:log4j-1.2.8.jar:pircbot.jar Bogobot
|
57
apps/bogobot/build-eclipse.xml
Normal file
57
apps/bogobot/build-eclipse.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- ********************************************************** -->
|
||||
<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
|
||||
<!-- -->
|
||||
<!-- build-eclipse.xml -->
|
||||
<!-- 2004 The I2P Project -->
|
||||
<!-- This code is public domain. -->
|
||||
<!-- -->
|
||||
<!-- author hypercubus, oOo -->
|
||||
<!-- version 0.4 -->
|
||||
<!-- ********************************************************** -->
|
||||
|
||||
<project basedir="." default="dist" name="Bogobot">
|
||||
|
||||
<!-- init:
|
||||
Create distribution directory if missing and initialize time stamp for
|
||||
archive naming -->
|
||||
<target name="init">
|
||||
<mkdir dir="dist" />
|
||||
<tstamp>
|
||||
<format pattern="yyyy-MM-dd" property="DSTAMP" />
|
||||
</tstamp>
|
||||
</target>
|
||||
|
||||
<!-- dist.bin:
|
||||
Create the binary distribution archive -->
|
||||
<target depends="init" description="Create the binary distribution archive" name="dist.bin">
|
||||
<zip destfile="dist/Bogobot_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist.source:
|
||||
Create the source distribution archive -->
|
||||
<target depends="init" description="Create the source distribution archive" name="dist.source">
|
||||
<zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist:
|
||||
Create both the binary and source distribution archives -->
|
||||
<target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
|
||||
<echo message="Successfully created binary and source distribution archives in directory 'dist'." />
|
||||
</target>
|
||||
|
||||
<!-- clean:
|
||||
Delete all class files and temporary directories -->
|
||||
<target description="Delete all class files and temporary directories" name="clean">
|
||||
<delete>
|
||||
<fileset dir="${basedir}" includes="**/*.class" />
|
||||
</delete>
|
||||
<echo message="Clean successful." />
|
||||
</target>
|
||||
|
||||
</project>
|
63
apps/bogobot/build.xml
Normal file
63
apps/bogobot/build.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- ********************************************************** -->
|
||||
<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
|
||||
<!-- -->
|
||||
<!-- build.xml -->
|
||||
<!-- 2004 The I2P Project -->
|
||||
<!-- This code is public domain. -->
|
||||
<!-- -->
|
||||
<!-- author hypercubus, oOo -->
|
||||
<!-- version 0.4 -->
|
||||
<!-- ********************************************************** -->
|
||||
|
||||
<project basedir="." default="compile" name="Bogobot">
|
||||
|
||||
<!-- init:
|
||||
Create distribution directory if missing and initialize time stamp for
|
||||
archive naming -->
|
||||
<target name="init">
|
||||
<mkdir dir="dist" />
|
||||
<tstamp>
|
||||
<format pattern="yyyy-MM-dd" property="DSTAMP" />
|
||||
</tstamp>
|
||||
</target>
|
||||
|
||||
<!-- compile:
|
||||
Compile source code -->
|
||||
<target depends="init" description="Compile source code" name="compile">
|
||||
<javac classpath="${basedir};log4j-1.2.8.jar;pircbot.jar" source="1.4" srcdir="." />
|
||||
</target>
|
||||
|
||||
<!-- dist.bin:
|
||||
Create the binary distribution archive -->
|
||||
<target depends="init,compile" description="Create the binary distribution archive" name="dist.bin">
|
||||
<zip destfile="dist/Bogobot_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.class Bogobot$BogobotTickTask.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist.source:
|
||||
Create the source distribution archive -->
|
||||
<target depends="init" description="Create the source distribution archive" name="dist.source">
|
||||
<zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist:
|
||||
Create both the binary and source distribution archives -->
|
||||
<target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
|
||||
<echo message="Successfully created binary and source distribution archives in directory 'dist'." />
|
||||
</target>
|
||||
|
||||
<!-- clean:
|
||||
Delete all class files and temporary directories -->
|
||||
<target description="Delete all class files and temporary directories" name="clean">
|
||||
<delete>
|
||||
<fileset dir="${basedir}" includes="**/*.class" />
|
||||
</delete>
|
||||
<echo message="Clean successful." />
|
||||
</target>
|
||||
|
||||
</project>
|
BIN
apps/bogobot/log4j-1.2.8.jar
Normal file
BIN
apps/bogobot/log4j-1.2.8.jar
Normal file
Binary file not shown.
BIN
apps/bogobot/pircbot.jar
Normal file
BIN
apps/bogobot/pircbot.jar
Normal file
Binary file not shown.
@ -22,6 +22,47 @@
|
||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<ant target="war" />
|
||||
</target>
|
||||
<target name="war" depends="precompilejsp">
|
||||
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, *.java, *.jsp">
|
||||
</war>
|
||||
</target>
|
||||
<target name="precompilejsp">
|
||||
<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" >
|
||||
<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/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" />
|
||||
<arg value="../jsp/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="../jsp/" />
|
||||
</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="build/i2ptunnel.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
|
||||
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
|
||||
<replace file="../jsp/web-out.xml">
|
||||
<replacefilter token="<!-- precompiled servlets -->" value="${jspc.web.fragment}" />
|
||||
</replace>
|
||||
</target>
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
|
@ -54,6 +54,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.naming.NamingService;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -71,16 +72,17 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
private static long __tunnelId = 0;
|
||||
private long _tunnelId;
|
||||
private Properties _clientOptions;
|
||||
private List _sessions;
|
||||
|
||||
public static final int PACKET_DELAY = 100;
|
||||
|
||||
public static boolean ownDest = false;
|
||||
public boolean ownDest = false;
|
||||
|
||||
public static String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
public static String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
public static String listenHost = host;
|
||||
public String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
public String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
public String listenHost = host;
|
||||
|
||||
public static long readTimeout = -1;
|
||||
public long readTimeout = -1;
|
||||
|
||||
private static final String nocli_args[] = { "-nocli", "-die"};
|
||||
|
||||
@ -108,6 +110,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
_event = new EventDispatcherImpl();
|
||||
_clientOptions = new Properties();
|
||||
_clientOptions.putAll(System.getProperties());
|
||||
_sessions = new ArrayList(1);
|
||||
|
||||
addConnectionEventListener(lsnr);
|
||||
boolean gui = true;
|
||||
@ -172,6 +175,24 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
List getSessions() {
|
||||
synchronized (_sessions) {
|
||||
return new ArrayList(_sessions);
|
||||
}
|
||||
}
|
||||
void addSession(I2PSession session) {
|
||||
if (session == null) return;
|
||||
synchronized (_sessions) {
|
||||
_sessions.add(session);
|
||||
}
|
||||
}
|
||||
void removeSession(I2PSession session) {
|
||||
if (session == null) return;
|
||||
synchronized (_sessions) {
|
||||
_sessions.remove(session);
|
||||
}
|
||||
}
|
||||
|
||||
public Properties getClientOptions() { return _clientOptions; }
|
||||
|
||||
private void addtask(I2PTunnelTask tsk) {
|
||||
@ -408,9 +429,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*/
|
||||
public void runClient(String args[], Logging l) {
|
||||
if (args.length == 2) {
|
||||
int port = -1;
|
||||
int portNum = -1;
|
||||
try {
|
||||
port = Integer.parseInt(args[0]);
|
||||
portNum = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
@ -418,9 +439,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
return;
|
||||
}
|
||||
I2PTunnelTask task;
|
||||
task = new I2PTunnelClient(port, args[1], l, ownDest, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("clientTaskId", new Integer(task.getId()));
|
||||
try {
|
||||
task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("clientTaskId", new Integer(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create a client [" + host + ":"+ port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
|
||||
notifyEvent("clientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("client <port> <pubkey>|file:<pubkeyfile>");
|
||||
l.log(" creates a client that forwards port to the pubkey.\n"
|
||||
@ -455,9 +482,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
proxy = args[1];
|
||||
}
|
||||
I2PTunnelTask task;
|
||||
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("httpclientTaskId", new Integer(task.getId()));
|
||||
try {
|
||||
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("httpclientTaskId", new Integer(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
|
||||
notifyEvent("httpclientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("httpclient <port> [<proxy>]");
|
||||
l.log(" creates a client that distributes HTTP requests.");
|
||||
|
@ -19,7 +19,13 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
public I2PTunnelClient(int localPort, String destination, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelClient(int localPort, String destination, Logging l,
|
||||
boolean ownDest, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
|
||||
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
|
@ -60,7 +60,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
|
||||
//}
|
||||
|
||||
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName, I2PTunnel tunnel) {
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
|
||||
* badly that we cant create a socketManager
|
||||
*/
|
||||
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l,
|
||||
EventDispatcher notifyThis, String handlerName,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException{
|
||||
super(localPort + " (uninitialized)", notifyThis, tunnel);
|
||||
_clientId = ++__clientId;
|
||||
this.localPort = localPort;
|
||||
@ -74,9 +80,14 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
sockMgr = getSocketManager();
|
||||
}
|
||||
}
|
||||
if (sockMgr == null) throw new NullPointerException();
|
||||
if (sockMgr == null) {
|
||||
l.log("Invalid I2CP configuration");
|
||||
throw new IllegalArgumentException("Socket manager could not be created");
|
||||
}
|
||||
l.log("I2P session created");
|
||||
|
||||
getTunnel().addSession(sockMgr.getSession());
|
||||
|
||||
Thread t = new I2PThread(this);
|
||||
t.setName("Client " + _clientId);
|
||||
listenerReady = false;
|
||||
@ -122,7 +133,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
props.putAll(System.getProperties());
|
||||
else
|
||||
props.putAll(tunnel.getClientOptions());
|
||||
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(I2PTunnel.host, Integer.parseInt(I2PTunnel.port), props);
|
||||
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(tunnel.host, Integer.parseInt(tunnel.port), props);
|
||||
if (sockManager == null) return null;
|
||||
sockManager.setName("Client");
|
||||
return sockManager;
|
||||
}
|
||||
@ -133,9 +145,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
protected final InetAddress getListenHost(Logging l) {
|
||||
try {
|
||||
return InetAddress.getByName(I2PTunnel.listenHost);
|
||||
return InetAddress.getByName(getTunnel().listenHost);
|
||||
} catch (UnknownHostException uhe) {
|
||||
l.log("Could not find listen host to bind to [" + I2PTunnel.host + "]");
|
||||
l.log("Could not find listen host to bind to [" + getTunnel().host + "]");
|
||||
_log.error("Error finding host to bind", uhe);
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
return null;
|
||||
@ -212,7 +224,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
localPort = ss.getLocalPort();
|
||||
}
|
||||
notifyEvent("clientLocalPort", new Integer(ss.getLocalPort()));
|
||||
l.log("Listening for clients on port " + localPort + " of " + I2PTunnel.listenHost);
|
||||
l.log("Listening for clients on port " + localPort + " of " + getTunnel().listenHost);
|
||||
|
||||
// Notify constructor that port is ready
|
||||
synchronized (this) {
|
||||
@ -265,6 +277,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
return false;
|
||||
}
|
||||
getTunnel().removeSession(sockMgr.getSession());
|
||||
l.log("Closing client " + toString());
|
||||
try {
|
||||
if (ss != null) ss.close();
|
||||
|
@ -9,8 +9,12 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -42,7 +46,7 @@ import net.i2p.util.Log;
|
||||
public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable {
|
||||
private static final Log _log = new Log(I2PTunnelHTTPClient.class);
|
||||
|
||||
private String wwwProxy;
|
||||
private List proxyList;
|
||||
|
||||
private final static byte[] ERR_REQUEST_DENIED =
|
||||
("HTTP/1.1 403 Access Denied\r\n"+
|
||||
@ -78,10 +82,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
"the following Destination:<BR><BR>")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_NO_OUTPROXY =
|
||||
("HTTP/1.1 503 Service Unavailable\r\n"+
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||
"Cache-control: no-cache\r\n"+
|
||||
"\r\n"+
|
||||
"<html><body><H1>I2P ERROR: No outproxy found</H1>"+
|
||||
"Your request was for a site outside of I2P, but you have no "+
|
||||
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
|
||||
.getBytes();
|
||||
|
||||
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
||||
private static volatile long __clientId = 0;
|
||||
|
||||
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest,
|
||||
String wwwProxy, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId), tunnel);
|
||||
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
@ -89,22 +109,47 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
|
||||
this.wwwProxy = wwwProxy;
|
||||
proxyList = new ArrayList();
|
||||
if (wwwProxy != null) {
|
||||
StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
|
||||
while (tok.hasMoreTokens())
|
||||
proxyList.add(tok.nextToken().trim());
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> HTTPClient [WWW outproxy: " + this.wwwProxy + "]");
|
||||
setName(getLocalPort() + " -> HTTPClient [WWW outproxy list: " + wwwProxy + "]");
|
||||
|
||||
startRunning();
|
||||
|
||||
notifyEvent("openHTTPClientResult", "ok");
|
||||
}
|
||||
|
||||
private String getPrefix() { return "Client[" + _clientId + "]: "; }
|
||||
private String getPrefix(long requestId) { return "Client[" + _clientId + "/" + requestId + "]: "; }
|
||||
|
||||
private String selectProxy() {
|
||||
synchronized (proxyList) {
|
||||
int size = proxyList.size();
|
||||
if (size <= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Proxy list is empty - no outproxy available");
|
||||
l.log("Proxy list is emtpy - no outproxy available");
|
||||
return null;
|
||||
}
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
if (index >= size) index = size - 1;
|
||||
if (index < 0) return null;
|
||||
String proxy = (String)proxyList.get(index);
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
private static long __requestId = 0;
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
OutputStream out = null;
|
||||
String targetRequest = null;
|
||||
boolean usingWWWProxy = false;
|
||||
String currentProxy = null;
|
||||
InactivityTimeoutThread timeoutThread = null;
|
||||
long requestId = ++__requestId;
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "ISO-8859-1"));
|
||||
@ -112,7 +157,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
StringBuffer newRequest = new StringBuffer();
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Line=[" + line + "]");
|
||||
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
|
||||
|
||||
if (line.startsWith("Connection: ") ||
|
||||
line.startsWith("Keep-Alive: ") ||
|
||||
@ -121,7 +166,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
if (method == null) { // first line (GET /base64/realaddr)
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Method is null for [" + line + "]");
|
||||
_log.debug(getPrefix(requestId) + "Method is null for [" + line + "]");
|
||||
|
||||
int pos = line.indexOf(" ");
|
||||
if (pos == -1) break;
|
||||
@ -154,10 +199,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
line = method + " " + request.substring(pos);
|
||||
} else if (host.indexOf(".") != -1) {
|
||||
// The request must be forwarded to a WWW proxy
|
||||
destination = wwwProxy;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Before selecting outproxy for " + host);
|
||||
currentProxy = selectProxy();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("After selecting outproxy for " + host + ": " + currentProxy);
|
||||
if (currentProxy == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
|
||||
l.log("No HTTP outproxy found for the request.");
|
||||
if (out != null) {
|
||||
out.write(ERR_NO_OUTPROXY);
|
||||
out.write("<p /><i>Generated on: ".getBytes());
|
||||
out.write(new Date().toString().getBytes());
|
||||
out.write("</i></body></html>\n".getBytes());
|
||||
out.flush();
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
destination = currentProxy;
|
||||
usingWWWProxy = true;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
|
||||
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
|
||||
} else {
|
||||
request = request.substring(pos + 1);
|
||||
pos = request.indexOf("/");
|
||||
@ -167,27 +231,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
boolean isValid = usingWWWProxy || isSupportedAddress(host, protocol);
|
||||
if (!isValid) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "notValid(" + host + ")");
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
|
||||
method = null;
|
||||
destination = null;
|
||||
break;
|
||||
} else if (!usingWWWProxy) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "host=getHostName(" + destination + ")");
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "host=getHostName(" + destination + ")");
|
||||
host = getHostName(destination); // hide original host
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix() + "METHOD:" + method + ":");
|
||||
_log.debug(getPrefix() + "PROTOC:" + protocol + ":");
|
||||
_log.debug(getPrefix() + "HOST :" + host + ":");
|
||||
_log.debug(getPrefix() + "DEST :" + destination + ":");
|
||||
_log.debug(getPrefix(requestId) + "METHOD:" + method + ":");
|
||||
_log.debug(getPrefix(requestId) + "PROTOC:" + protocol + ":");
|
||||
_log.debug(getPrefix(requestId) + "HOST :" + host + ":");
|
||||
_log.debug(getPrefix(requestId) + "DEST :" + destination + ":");
|
||||
}
|
||||
|
||||
} else {
|
||||
if (line.startsWith("Host: ") && !usingWWWProxy) {
|
||||
line = "Host: " + host;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Setting host = " + host);
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +263,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "NewRequest header: [" + newRequest.toString() + "]");
|
||||
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
|
||||
|
||||
while (br.ready()) { // empty the buffer (POST requests)
|
||||
int i = br.read();
|
||||
@ -221,7 +285,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Destination: " + destination);
|
||||
_log.debug(getPrefix(requestId) + "Destination: " + destination);
|
||||
|
||||
Destination dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null) {
|
||||
@ -236,25 +300,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
I2PSocket i2ps = createI2PSocket(dest);
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data);
|
||||
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, s);
|
||||
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, currentProxy, s, requestId);
|
||||
timeoutThread.start();
|
||||
} catch (SocketException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix() + "Error trying to connect", ex);
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (IOException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix() + "Error trying to connect", ex);
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (I2PException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info("getPrefix() + Error trying to connect", ex);
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
@ -263,25 +327,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
private static volatile long __timeoutId = 0;
|
||||
|
||||
private class InactivityTimeoutThread extends I2PThread {
|
||||
|
||||
|
||||
private Socket s;
|
||||
private I2PTunnelRunner _runner;
|
||||
private OutputStream _out;
|
||||
private String _targetRequest;
|
||||
private boolean _useWWWProxy;
|
||||
private String _currentProxy;
|
||||
private long _requestId;
|
||||
private boolean _disabled;
|
||||
private Object _disableLock = new Object();
|
||||
|
||||
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
|
||||
boolean useWWWProxy, Socket s) {
|
||||
boolean useWWWProxy, String currentProxy, Socket s, long requestId) {
|
||||
this.s = s;
|
||||
_runner = runner;
|
||||
_out = out;
|
||||
_targetRequest = targetRequest;
|
||||
_useWWWProxy = useWWWProxy;
|
||||
_currentProxy = currentProxy;
|
||||
_disabled = false;
|
||||
_requestId = requestId;
|
||||
long timeoutId = ++__timeoutId;
|
||||
setName("InactivityThread " + getPrefix() + timeoutId);
|
||||
setName("InactivityThread " + getPrefix(requestId) + timeoutId);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
@ -294,13 +362,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
public void run() {
|
||||
while (!_disabled) {
|
||||
if (_runner.isFinished()) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "HTTP client request completed prior to timeout");
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(_requestId) + "HTTP client request completed prior to timeout");
|
||||
return;
|
||||
}
|
||||
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "HTTP client request timed out (lastActivity: "
|
||||
_log.warn(getPrefix(_requestId) + "HTTP client request timed out (lastActivity: "
|
||||
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
|
||||
+ new Date(_runner.getStartedOn()) + ")");
|
||||
timeout();
|
||||
@ -321,26 +389,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
private void timeout() {
|
||||
_log.info(getPrefix() + "Inactivity timeout reached");
|
||||
_log.info(getPrefix(_requestId) + "Inactivity timeout reached");
|
||||
l.log("Inactivity timeout reached");
|
||||
if (_out != null) {
|
||||
try {
|
||||
if (_runner.getLastActivityOn() > 0) {
|
||||
// some data has been sent, so don't 404 it
|
||||
} else {
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, wwwProxy);
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, _currentProxy);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.warn(getPrefix() + "Error writing out the 'timeout' message", ioe);
|
||||
_log.warn(getPrefix(_requestId) + "Error writing out the 'timeout' message", ioe);
|
||||
}
|
||||
} else {
|
||||
_log.warn(getPrefix() + "Client disconnected before we could say we timed out");
|
||||
_log.warn(getPrefix(_requestId) + "Client disconnected before we could say we timed out");
|
||||
}
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
|
||||
private final static String getHostName(String host) {
|
||||
if (host == null) return null;
|
||||
try {
|
||||
Destination dest = I2PTunnel.destFromName(host);
|
||||
if (dest == null) return "i2p";
|
||||
@ -366,18 +435,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
private void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest,
|
||||
boolean usingWWWProxy, String wwwProxy) {
|
||||
boolean usingWWWProxy, String wwwProxy, long requestId) {
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
|
||||
_log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
|
||||
if (out != null) {
|
||||
try {
|
||||
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
} catch (IOException ioe) {
|
||||
_log.warn(getPrefix() + "Error writing out the 'destination was unknown' " + "message", ioe);
|
||||
_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe);
|
||||
}
|
||||
} else {
|
||||
_log.warn(getPrefix() + "Client disconnected before we could say that destination " + "was unknown", ex);
|
||||
_log.warn(getPrefix(requestId) + "Client disconnected before we could say that destination " + "was unknown", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,11 +75,12 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
Properties props = new Properties();
|
||||
props.putAll(getTunnel().getClientOptions());
|
||||
synchronized (slock) {
|
||||
sockMgr = I2PSocketManagerFactory.createManager(privData, I2PTunnel.host, Integer.parseInt(I2PTunnel.port),
|
||||
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
|
||||
props);
|
||||
|
||||
}
|
||||
sockMgr.setName("Server");
|
||||
getTunnel().addSession(sockMgr.getSession());
|
||||
l.log("Ready!");
|
||||
notifyEvent("openServerResult", "ok");
|
||||
open = true;
|
||||
@ -130,6 +131,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
l.log("Shutting down server " + toString());
|
||||
try {
|
||||
if (i2pss != null) i2pss.close();
|
||||
getTunnel().removeSession(sockMgr.getSession());
|
||||
sockMgr.getSession().destroySession();
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error destroying the session", ex);
|
||||
|
@ -64,6 +64,7 @@ public abstract class I2PTunnelTask implements EventDispatcher {
|
||||
|
||||
public void disconnected(I2PSession session) {
|
||||
routerDisconnected();
|
||||
getTunnel().removeSession(session);
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
|
365
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
Normal file
365
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
Normal file
@ -0,0 +1,365 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Coordinate the runtime operation and configuration of a tunnel.
|
||||
* These objects are bundled together under a TunnelControllerGroup where the
|
||||
* entire group is stored / loaded from a single config file.
|
||||
*
|
||||
*/
|
||||
public class TunnelController implements Logging {
|
||||
private Log _log;
|
||||
private Properties _config;
|
||||
private I2PTunnel _tunnel;
|
||||
private List _messages;
|
||||
private boolean _running;
|
||||
|
||||
/**
|
||||
* Create a new controller for a tunnel out of the specific config options.
|
||||
* The config may contain a large number of options - only ones that begin in
|
||||
* the prefix should be used (and, in turn, that prefix should be stripped off
|
||||
* before being interpreted by this controller)
|
||||
*
|
||||
* @param config original key=value mapping
|
||||
* @param prefix beginning of key values that are relevent to this tunnel
|
||||
*/
|
||||
public TunnelController(Properties config, String prefix) {
|
||||
this(config, prefix, true);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param createKey for servers, whether we want to create a brand new destination
|
||||
* with private keys at the location specified or not (does not
|
||||
* overwrite existing ones)
|
||||
*/
|
||||
public TunnelController(Properties config, String prefix, boolean createKey) {
|
||||
_tunnel = new I2PTunnel();
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class);
|
||||
setConfig(config, prefix);
|
||||
_messages = new ArrayList(4);
|
||||
_running = false;
|
||||
if (createKey && ("server".equals(getType())) )
|
||||
createPrivateKey();
|
||||
if (getStartOnLoad())
|
||||
startTunnel();
|
||||
}
|
||||
|
||||
private void createPrivateKey() {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
String filename = getPrivKeyFile();
|
||||
if ( (filename == null) || (filename.trim().length() <= 0) ) {
|
||||
log("No filename specified for the private key");
|
||||
return;
|
||||
}
|
||||
|
||||
File keyFile = new File(getPrivKeyFile());
|
||||
if (keyFile.exists()) {
|
||||
log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
|
||||
return;
|
||||
} else {
|
||||
File parent = keyFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
parent.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyFile);
|
||||
Destination dest = client.createDestination(fos);
|
||||
String destStr = dest.toBase64();
|
||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||
log("New destination: " + destStr);
|
||||
} catch (I2PException ie) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating new destination", ie);
|
||||
log("Error creating new destination: " + ie.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating writing the destination to " + keyFile.getAbsolutePath(), ioe);
|
||||
log("Error writing the keys to " + keyFile.getAbsolutePath());
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the tunnel (if it isn't already running)
|
||||
*
|
||||
*/
|
||||
public void startTunnel() {
|
||||
if (_running) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Already running");
|
||||
log("Tunnel " + getName() + " is already running");
|
||||
return;
|
||||
}
|
||||
String type = getType();
|
||||
if ( (type == null) || (type.length() <= 0) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Cannot start the tunnel - no type specified");
|
||||
return;
|
||||
}
|
||||
if ("httpclient".equals(type)) {
|
||||
startHttpClient();
|
||||
} else if ("client".equals(type)) {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
startServer();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.error("Cannot start tunnel - unknown type [" + type + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private void startHttpClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String proxyList = getProxyList();
|
||||
if (proxyList == null)
|
||||
_tunnel.runHttpClient(new String[] { listenPort }, this);
|
||||
else
|
||||
_tunnel.runHttpClient(new String[] { listenPort, proxyList }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String dest = getTargetDestination();
|
||||
_tunnel.runClient(new String[] { listenPort, dest }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startServer() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
String privKeyFile = getPrivKeyFile();
|
||||
_tunnel.runServer(new String[] { targetHost, targetPort, privKeyFile }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void setListenOn() {
|
||||
String listenOn = getListenOnInterface();
|
||||
if ( (listenOn != null) && (listenOn.length() > 0) ) {
|
||||
_tunnel.runListenOn(new String[] { listenOn }, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSessionOptions() {
|
||||
List opts = new ArrayList();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
opts.add(key + "=" + val);
|
||||
}
|
||||
}
|
||||
String args[] = new String[opts.size()];
|
||||
for (int i = 0; i < opts.size(); i++)
|
||||
args[i] = (String)opts.get(i);
|
||||
_tunnel.runClientOptions(args, this);
|
||||
}
|
||||
|
||||
private void setI2CPOptions() {
|
||||
String host = getI2CPHost();
|
||||
if ( (host != null) && (host.length() > 0) )
|
||||
_tunnel.host = host;
|
||||
String port = getI2CPPort();
|
||||
if ( (port != null) && (port.length() > 0) )
|
||||
_tunnel.port = port;
|
||||
}
|
||||
|
||||
public void stopTunnel() {
|
||||
_tunnel.runClose(new String[] { "forced", "all" }, this);
|
||||
_running = false;
|
||||
}
|
||||
|
||||
public void restartTunnel() {
|
||||
stopTunnel();
|
||||
startTunnel();
|
||||
}
|
||||
|
||||
public void setConfig(Properties config, String prefix) {
|
||||
Properties props = new Properties();
|
||||
for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = config.getProperty(key);
|
||||
if (key.startsWith(prefix)) {
|
||||
key = key.substring(prefix.length());
|
||||
props.setProperty(key, val);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Set prop [" + key + "] to [" + val + "]");
|
||||
}
|
||||
}
|
||||
_config = props;
|
||||
}
|
||||
public Properties getConfig(String prefix) {
|
||||
Properties rv = new Properties();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
rv.setProperty(prefix + key, val);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public String getType() { return _config.getProperty("type"); }
|
||||
public String getName() { return _config.getProperty("name"); }
|
||||
public String getDescription() { return _config.getProperty("description"); }
|
||||
public String getI2CPHost() { return _config.getProperty("i2cpHost"); }
|
||||
public String getI2CPPort() { return _config.getProperty("i2cpPort"); }
|
||||
public String getClientOptions() {
|
||||
StringBuffer opts = new StringBuffer(64);
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
if (opts.length() > 0) opts.append(' ');
|
||||
opts.append(key).append('=').append(val);
|
||||
}
|
||||
}
|
||||
return opts.toString();
|
||||
}
|
||||
public String getListenOnInterface() { return _config.getProperty("interface"); }
|
||||
public String getTargetHost() { return _config.getProperty("targetHost"); }
|
||||
public String getTargetPort() { return _config.getProperty("targetPort"); }
|
||||
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
|
||||
public String getListenPort() { return _config.getProperty("listenPort"); }
|
||||
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
|
||||
public String getProxyList() { return _config.getProperty("proxyList"); }
|
||||
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
|
||||
|
||||
public boolean getIsRunning() { return _running; }
|
||||
|
||||
public void getSummary(StringBuffer buf) {
|
||||
String type = getType();
|
||||
if ("httpclient".equals(type))
|
||||
getHttpClientSummary(buf);
|
||||
else if ("client".equals(type))
|
||||
getClientSummary(buf);
|
||||
else if ("server".equals(type))
|
||||
getServerSummary(buf);
|
||||
else
|
||||
buf.append("Unknown type ").append(type);
|
||||
}
|
||||
|
||||
private void getHttpClientSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("HTTP proxy listening on port ").append(getListenPort());
|
||||
String listenOn = getListenOnInterface();
|
||||
if ("0.0.0.0".equals(listenOn))
|
||||
buf.append(" (reachable by any machine)");
|
||||
else if ("127.0.0.1".equals(listenOn))
|
||||
buf.append(" (reachable locally only)");
|
||||
else
|
||||
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
|
||||
buf.append("<br />\n");
|
||||
String proxies = getProxyList();
|
||||
if ( (proxies == null) || (proxies.trim().length() <= 0) )
|
||||
buf.append("Outproxy: default [squid.i2p]<br />\n");
|
||||
else
|
||||
buf.append("Outproxy: ").append(proxies).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getClientSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Client tunnel listening on port ").append(getListenPort());
|
||||
buf.append(" pointing at ").append(getTargetDestination());
|
||||
String listenOn = getListenOnInterface();
|
||||
if ("0.0.0.0".equals(listenOn))
|
||||
buf.append(" (reachable by any machine)");
|
||||
else if ("127.0.0.1".equals(listenOn))
|
||||
buf.append(" (reachable locally only)");
|
||||
else
|
||||
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
|
||||
buf.append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getServerSummary(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("<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) )
|
||||
buf.append("Network options: ").append(opts).append("<br />\n");
|
||||
if (_running) {
|
||||
List sessions = _tunnel.getSessions();
|
||||
for (int i = 0; i < sessions.size(); i++) {
|
||||
I2PSession session = (I2PSession)sessions.get(i);
|
||||
Destination dest = session.getMyDestination();
|
||||
if (dest != null) {
|
||||
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
|
||||
buf.append("Full destination: ");
|
||||
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
|
||||
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
|
||||
if ("server".equals(getType())) {
|
||||
buf.append(" Give that out to people so they can view your service.");
|
||||
buf.append(" If you are going to share it on irc, be sure to split it on two lines");
|
||||
}
|
||||
buf.append("<br />\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void log(String s) {
|
||||
synchronized (this) {
|
||||
_messages.add(s);
|
||||
while (_messages.size() > 10)
|
||||
_messages.remove(0);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull off any messages that the I2PTunnel has produced
|
||||
*
|
||||
* @return list of messages pulled off (each is a String, earliest first)
|
||||
*/
|
||||
public List clearMessages() {
|
||||
List rv = null;
|
||||
synchronized (this) {
|
||||
rv = new ArrayList(_messages);
|
||||
_messages.clear();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -0,0 +1,290 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Coordinate a set of tunnels within the JVM, loading and storing their config
|
||||
* to disk, and building new ones as requested.
|
||||
*
|
||||
*/
|
||||
public class TunnelControllerGroup {
|
||||
private Log _log;
|
||||
private static TunnelControllerGroup _instance;
|
||||
static final String DEFAULT_CONFIG_FILE = "i2ptunnel.config";
|
||||
|
||||
private List _controllers;
|
||||
private String _configFile = DEFAULT_CONFIG_FILE;
|
||||
|
||||
public static TunnelControllerGroup getInstance() {
|
||||
synchronized (TunnelControllerGroup.class) {
|
||||
if (_instance == null)
|
||||
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelControllerGroup(String configFile) {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
|
||||
_controllers = new ArrayList();
|
||||
_configFile = configFile;
|
||||
loadControllers(_configFile);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
if ( (args == null) || (args.length <= 0) ) {
|
||||
_instance = getInstance();
|
||||
} else if (args.length == 1) {
|
||||
if (DEFAULT_CONFIG_FILE.equals(args[0]))
|
||||
_instance = getInstance();
|
||||
else
|
||||
_instance = new TunnelControllerGroup(args[0]);
|
||||
} else {
|
||||
System.err.println("Usage: TunnelControllerGroup [filename]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up all of the tunnels configured in the given file (but do not start
|
||||
* them)
|
||||
*
|
||||
*/
|
||||
public void loadControllers(String configFile) {
|
||||
Properties cfg = loadConfig(configFile);
|
||||
if (cfg == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the config from " + configFile);
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
while (true) {
|
||||
String type = cfg.getProperty("tunnel." + i + ".type");
|
||||
if (type == null)
|
||||
break;
|
||||
TunnelController controller = new TunnelController(cfg, "tunnel." + i + ".");
|
||||
_controllers.add(controller);
|
||||
i++;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(i + " controllers loaded from " + configFile);
|
||||
}
|
||||
|
||||
public void reloadControllers() {
|
||||
unloadControllers();
|
||||
loadControllers(_configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and remove reference to all known tunnels (but dont delete any config
|
||||
* file or do other silly things)
|
||||
*
|
||||
*/
|
||||
public void unloadControllers() {
|
||||
stopAllControllers();
|
||||
_controllers.clear();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("All controllers stopped and unloaded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given tunnel to the set of known controllers (but dont add it to
|
||||
* a config file or start it or anything)
|
||||
*
|
||||
*/
|
||||
public void addController(TunnelController controller) { _controllers.add(controller); }
|
||||
|
||||
/**
|
||||
* Stop and remove the given tunnel
|
||||
*
|
||||
* @return list of messages from the controller as it is stopped
|
||||
*/
|
||||
public List removeController(TunnelController controller) {
|
||||
if (controller == null) return new ArrayList();
|
||||
controller.stopTunnel();
|
||||
List msgs = controller.clearMessages();
|
||||
_controllers.remove(controller);
|
||||
msgs.add("Tunnel " + controller.getName() + " removed");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when stopped
|
||||
*/
|
||||
public List stopAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.stopTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers stopped");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when started
|
||||
*/
|
||||
public List startAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.startTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers started");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when restarted
|
||||
*/
|
||||
public List restartAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.restartTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers restarted");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all outstanding messages from any of the known tunnels
|
||||
*
|
||||
* @return list of messages the tunnels have generated
|
||||
*/
|
||||
public List clearAllMessages() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the configuration of all known tunnels to the default config
|
||||
* file
|
||||
*
|
||||
*/
|
||||
public void saveConfig() {
|
||||
saveConfig(_configFile);
|
||||
}
|
||||
/**
|
||||
* Save the configuration of all known tunnels to the given file
|
||||
*
|
||||
*/
|
||||
public void saveConfig(String configFile) {
|
||||
_configFile = configFile;
|
||||
File cfgFile = new File(configFile);
|
||||
File parent = cfgFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
parent.mkdirs();
|
||||
|
||||
|
||||
TreeMap map = new TreeMap();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
Properties cur = controller.getConfig("tunnel." + i + ".");
|
||||
map.putAll(cur);
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = (String)map.get(key);
|
||||
buf.append(key).append('=').append(val).append('\n');
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(cfgFile);
|
||||
fos.write(buf.toString().getBytes());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Config written to " + cfgFile.getPath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing out the config");
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up the config data from the file
|
||||
*
|
||||
* @return properties loaded or null if there was an error
|
||||
*/
|
||||
private Properties loadConfig(String configFile) {
|
||||
File cfgFile = new File(configFile);
|
||||
if (!cfgFile.exists()) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Unable to load the controllers from " + configFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(cfgFile);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.length() <= 0) continue;
|
||||
if (line.startsWith("#") || line.startsWith(";"))
|
||||
continue;
|
||||
int eq = line.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= line.length() - 1) )
|
||||
continue;
|
||||
String key = line.substring(0, eq);
|
||||
String val = line.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Props loaded with " + props.size() + " lines");
|
||||
return props;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error reading the controllers from " + configFile, ioe);
|
||||
return null;
|
||||
} finally {
|
||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of tunnels known
|
||||
*
|
||||
* @return list of TunnelController objects
|
||||
*/
|
||||
public List getControllers() { return _controllers; }
|
||||
|
||||
}
|
@ -0,0 +1,313 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Uuuugly... generate the edit/add forms for the various
|
||||
* I2PTunnel types (httpclient/client/server)
|
||||
*
|
||||
*/
|
||||
class WebEditPageFormGenerator {
|
||||
private static final String SELECT_TYPE_FORM =
|
||||
"<form action=\"edit.jsp\"> Type of tunnel: <select name=\"type\">" +
|
||||
"<option value=\"httpclient\">HTTP proxy</option>" +
|
||||
"<option value=\"client\">Client tunnel</option>" +
|
||||
"<option value=\"server\">Server tunnel</option>" +
|
||||
"</select> <input type=\"submit\" value=\"GO\" />" +
|
||||
"</form>\n";
|
||||
|
||||
/**
|
||||
* Retrieve the form requested
|
||||
*
|
||||
*/
|
||||
public static String getForm(WebEditPageHelper helper) {
|
||||
TunnelController controller = helper.getTunnelController();
|
||||
|
||||
if ( (helper.getType() == null) && (controller == null) )
|
||||
return SELECT_TYPE_FORM;
|
||||
|
||||
String id = helper.getNum();
|
||||
String type = helper.getType();
|
||||
if (controller != null)
|
||||
type = controller.getType();
|
||||
|
||||
if ("httpclient".equals(type))
|
||||
return getEditHttpClientForm(controller, id);
|
||||
else if ("client".equals(type))
|
||||
return getEditClientForm(controller, id);
|
||||
else if ("server".equals(type))
|
||||
return getEditServerForm(controller, id);
|
||||
else
|
||||
return "WTF, unknown type [" + type + "]";
|
||||
}
|
||||
|
||||
private static String getEditHttpClientForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>HTTP proxy</i><input type=\"hidden\" name=\"type\" value=\"httpclient\" /><br />\n");
|
||||
|
||||
addListeningOn(buf, controller, 4444);
|
||||
|
||||
buf.append("<b>Outproxies:</b> <input type=\"text\" name=\"proxyList\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getProxyList() != null) )
|
||||
buf.append("value=\"").append(controller.getProxyList()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"squid.i2p\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditClientForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>Client tunnel</i><input type=\"hidden\" name=\"type\" value=\"client\" /><br />\n");
|
||||
|
||||
addListeningOn(buf, controller, 2025 + new Random().nextInt(1000)); // 2025 since nextInt can be negative
|
||||
|
||||
buf.append("<b>Target:</b> <input type=\"text\" size=\"40\" name=\"targetDestination\" ");
|
||||
if ( (controller != null) && (controller.getTargetDestination() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
|
||||
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditServerForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>Server tunnel</i><input type=\"hidden\" name=\"type\" value=\"server\" /><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=\"localhost\" ");
|
||||
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>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</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)
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
* @param id index into the current list of tunnelControllerGroup.getControllers() list
|
||||
* (or null if we are generating an 'add' form)
|
||||
*/
|
||||
private static void addGeneral(StringBuffer buf, TunnelController controller, String id) {
|
||||
buf.append("<form action=\"edit.jsp\">");
|
||||
if (id != null)
|
||||
buf.append("<input type=\"hidden\" name=\"num\" value=\"").append(id).append("\" />");
|
||||
|
||||
buf.append("<b>Name:</b> <input type=\"text\" name=\"name\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getName() != null) )
|
||||
buf.append("value=\"").append(controller.getName()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<b>Description:</b> <input type=\"text\" name=\"description\" size=\"60\" ");
|
||||
if ( (controller != null) && (controller.getDescription() != null) )
|
||||
buf.append("value=\"").append(controller.getDescription()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the fields asking for what port and interface the tunnel should
|
||||
* listen on.
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
* @param defaultPort if we are creating a new tunnel, default the form to the given port
|
||||
*/
|
||||
private static void addListeningOn(StringBuffer buf, TunnelController controller, int defaultPort) {
|
||||
buf.append("<b>Listening on port:</b> <input type=\"text\" name=\"port\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getListenPort() != null) )
|
||||
buf.append("value=\"").append(controller.getListenPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"").append(defaultPort).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
String selectedOn = null;
|
||||
if ( (controller != null) && (controller.getListenOnInterface() != null) )
|
||||
selectedOn = controller.getListenOnInterface();
|
||||
|
||||
buf.append("<b>Reachable by:</b> ");
|
||||
buf.append("<select name=\"reachableBy\">");
|
||||
buf.append("<option value=\"127.0.0.1\" ");
|
||||
if ( (selectedOn != null) && ("127.0.0.1".equals(selectedOn)) )
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Locally (127.0.0.1)</option>\n");
|
||||
buf.append("<option value=\"0.0.0.0\" ");
|
||||
if ( (selectedOn != null) && ("0.0.0.0".equals(selectedOn)) )
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Everyone (0.0.0.0)</option>\n");
|
||||
buf.append("</select> ");
|
||||
buf.append("Other: <input type=\"text\" name=\"reachableByOther\" value=\"");
|
||||
if ( (selectedOn != null) && (!"127.0.0.1".equals(selectedOn)) && (!"0.0.0.0".equals(selectedOn)) )
|
||||
buf.append(selectedOn);
|
||||
buf.append("\"><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields for customizing the I2PSession options, including helpers for
|
||||
* tunnel depth and count, as well as I2CP host and port.
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
*/
|
||||
private static void addOptions(StringBuffer buf, TunnelController controller) {
|
||||
int tunnelDepth = 2;
|
||||
int numTunnels = 2;
|
||||
Properties opts = getOptions(controller);
|
||||
if (opts != null) {
|
||||
String depth = opts.getProperty("tunnels.depthInbound");
|
||||
if (depth != null) {
|
||||
try {
|
||||
tunnelDepth = Integer.parseInt(depth);
|
||||
} catch (NumberFormatException nfe) {
|
||||
tunnelDepth = 2;
|
||||
}
|
||||
}
|
||||
String num = opts.getProperty("tunnels.numInbound");
|
||||
if (num != null) {
|
||||
try {
|
||||
numTunnels = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
numTunnels = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append("<b>Tunnel depth:</b> ");
|
||||
buf.append("<select name=\"tunnelDepth\">");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (tunnelDepth == 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hop tunnel (low anonymity, low latency)</option>");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (tunnelDepth == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 hop tunnel (medium anonymity, medium latency)</option>");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (tunnelDepth == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 hop tunnel (high anonymity, high latency)</option>");
|
||||
if (tunnelDepth > 2) {
|
||||
buf.append("<option value=\"").append(tunnelDepth).append("\" selected=\"true\" >");
|
||||
buf.append(tunnelDepth);
|
||||
buf.append(" hop tunnel (custom)</option>");
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>Tunnel count:</b> ");
|
||||
buf.append("<select name=\"tunnelCount\">");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (numTunnels == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 inbound tunnel (low bandwidth usage, less reliability)</option>");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (numTunnels == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 inbound tunnels (standard bandwidth usage, standard reliability)</option>");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (numTunnels == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 inbound tunnels (higher bandwidth usage, higher reliability)</option>");
|
||||
|
||||
if (numTunnels > 3) {
|
||||
buf.append("<option value=\"").append(numTunnels).append("\" selected=\"true\" >");
|
||||
buf.append(numTunnels);
|
||||
buf.append(" inbound tunnels (custom)</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("\" /><br />\n");
|
||||
buf.append("<b>I2CP port:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPPort() != null) )
|
||||
buf.append(controller.getI2CPPort());
|
||||
else
|
||||
buf.append("7654");
|
||||
buf.append("\" /><br />\n");
|
||||
|
||||
buf.append("<b>Other custom options:</b> \n");
|
||||
buf.append("<input type=\"text\" name=\"customOptions\" size=\"60\" value=\"");
|
||||
if (opts != null) {
|
||||
int i = 0;
|
||||
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 (i != 0) buf.append(' ');
|
||||
buf.append(key).append('=').append(val);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>Start automatically?</b> \n");
|
||||
buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" ");
|
||||
if ( (controller != null) && (controller.getStartOnLoad()) )
|
||||
buf.append(" checked=\"true\" />\n<br />\n");
|
||||
else
|
||||
buf.append(" />\n<br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the client options from the tunnel
|
||||
*
|
||||
* @return map of name=val to be used as I2P session options
|
||||
*/
|
||||
private static Properties getOptions(TunnelController controller) {
|
||||
if (controller == null) return null;
|
||||
String opts = controller.getClientOptions();
|
||||
StringTokenizer tok = new StringTokenizer(opts);
|
||||
Properties props = new Properties();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
358
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java
Normal file
358
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java
Normal file
@ -0,0 +1,358 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* UUUUuuuuuugly glue code to handle bean interaction from the web, process
|
||||
* that data, and spit out the results (or the form requested). The basic
|
||||
* usage is to set any of the fields with data then query the bean via
|
||||
* getActionResults() which triggers the request processing (taking all the
|
||||
* provided data, doing what needs to be done) and returns the results of those
|
||||
* activites. Then a subsequent call to getEditForm() generates the HTML form
|
||||
* to either edit the currently selected tunnel (if specified) or add a new one.
|
||||
* This functionality is delegated to the WebEditPageFormGenerator.
|
||||
*
|
||||
*/
|
||||
public class WebEditPageHelper {
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private String _type;
|
||||
private String _id;
|
||||
private String _name;
|
||||
private String _description;
|
||||
private String _i2cpHost;
|
||||
private String _i2cpPort;
|
||||
private String _tunnelDepth;
|
||||
private String _tunnelCount;
|
||||
private String _customOptions;
|
||||
private String _proxyList;
|
||||
private String _port;
|
||||
private String _reachableBy;
|
||||
private String _reachableByOther;
|
||||
private String _targetDestination;
|
||||
private String _targetHost;
|
||||
private String _targetPort;
|
||||
private String _privKeyFile;
|
||||
private boolean _startOnLoad;
|
||||
private boolean _privKeyGenerate;
|
||||
private boolean _removeConfirmed;
|
||||
|
||||
public WebEditPageHelper() {
|
||||
_action = null;
|
||||
_type = null;
|
||||
_id = null;
|
||||
_removeConfirmed = false;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for form submit - either "Save" or Remove"
|
||||
*/
|
||||
public void setAction(String action) {
|
||||
_action = (action != null ? action.trim() : null);
|
||||
}
|
||||
/**
|
||||
* What type of tunnel (httpclient, client, or server). This is
|
||||
* required when adding a new tunnel.
|
||||
*
|
||||
*/
|
||||
public void setType(String type) {
|
||||
_type = (type != null ? type.trim() : null);
|
||||
}
|
||||
/**
|
||||
* Which particular tunnel should be edited (index into the current
|
||||
* TunnelControllerGroup's getControllers() list). This is required
|
||||
* when editing a tunnel, but not when adding a new one.
|
||||
*
|
||||
*/
|
||||
public void setNum(String id) {
|
||||
_id = (id != null ? id.trim() : null);
|
||||
}
|
||||
String getType() { return _type; }
|
||||
String getNum() { return _id; }
|
||||
|
||||
/** Short name of the tunnel */
|
||||
public void setName(String name) {
|
||||
_name = (name != null ? name.trim() : null);
|
||||
}
|
||||
/** one line description */
|
||||
public void setDescription(String description) {
|
||||
_description = (description != null ? description.trim() : null);
|
||||
}
|
||||
/** I2CP host the router is on */
|
||||
public void setClientHost(String host) {
|
||||
_i2cpHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** I2CP port the router is on */
|
||||
public void setClientPort(String port) {
|
||||
_i2cpPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** how many hops to use for inbound tunnels */
|
||||
public void setTunnelDepth(String tunnelDepth) {
|
||||
_tunnelDepth = (tunnelDepth != null ? tunnelDepth.trim() : null);
|
||||
}
|
||||
/** how many parallel inbound tunnels to use */
|
||||
public void setTunnelCount(String tunnelCount) {
|
||||
_tunnelCount = (tunnelCount != null ? tunnelCount.trim() : null);
|
||||
}
|
||||
/** what I2P session overrides should be used */
|
||||
public void setCustomOptions(String customOptions) {
|
||||
_customOptions = (customOptions != null ? customOptions.trim() : null);
|
||||
}
|
||||
/** what HTTP outproxies should be used (httpclient specific) */
|
||||
public void setProxyList(String proxyList) {
|
||||
_proxyList = (proxyList != null ? proxyList.trim() : null);
|
||||
}
|
||||
/** what port should this client/httpclient listen on */
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
}
|
||||
/**
|
||||
* what interface should this client/httpclient listen on (unless
|
||||
* overridden by the setReachableByOther() field)
|
||||
*/
|
||||
public void setReachableBy(String reachableBy) {
|
||||
_reachableBy = (reachableBy != null ? reachableBy.trim() : null);
|
||||
}
|
||||
/**
|
||||
* If specified, defines the exact IP interface to listen for requests
|
||||
* on (in the case of client/httpclient tunnels)
|
||||
*/
|
||||
public void setReachableByOther(String reachableByOther) {
|
||||
_reachableByOther = (reachableByOther != null ? reachableByOther.trim() : null);
|
||||
}
|
||||
/** What peer does this client tunnel point at */
|
||||
public void setTargetDestination(String dest) {
|
||||
_targetDestination = (dest != null ? dest.trim() : null);
|
||||
}
|
||||
/** What host does this server tunnel point at */
|
||||
public void setTargetHost(String host) {
|
||||
_targetHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What port does this server tunnel point at */
|
||||
public void setTargetPort(String port) {
|
||||
_targetPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
_privKeyFile = (file != null ? file.trim() : null);
|
||||
}
|
||||
/**
|
||||
* If called with any value, we want to generate a new destination
|
||||
* for this server tunnel. This won't cause any existing private keys
|
||||
* to be overwritten, however.
|
||||
*/
|
||||
public void setPrivKeyGenerate(String moo) {
|
||||
_privKeyGenerate = true;
|
||||
}
|
||||
/**
|
||||
* If called with any value (and the form submitted with action=Remove),
|
||||
* we really do want to stop and remove the tunnel.
|
||||
*/
|
||||
public void setRemoveConfirm(String moo) {
|
||||
_removeConfirmed = true;
|
||||
}
|
||||
/**
|
||||
* If called with any value, we want this tunnel to start whenever it is
|
||||
* loaded (aka right now and whenever the router is started up)
|
||||
*/
|
||||
public void setStartOnLoad(String moo) {
|
||||
_startOnLoad = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the form and display any resulting messages
|
||||
*
|
||||
*/
|
||||
public String getActionResults() {
|
||||
try {
|
||||
return processAction();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error processing request", t);
|
||||
return "Internal error - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an HTML form to edit / create a tunnel according to the
|
||||
* specified fields
|
||||
*/
|
||||
public String getEditForm() {
|
||||
try {
|
||||
return WebEditPageFormGenerator.getForm(this);
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error retrieving edit form", t);
|
||||
return "Internal error - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the tunnel pointed to by the current id
|
||||
*
|
||||
*/
|
||||
TunnelController getTunnelController() {
|
||||
if (_id == null) return null;
|
||||
int id = -1;
|
||||
try {
|
||||
id = Integer.parseInt(_id);
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if ( (id < 0) || (id >= controllers.size()) )
|
||||
return null;
|
||||
else
|
||||
return (TunnelController)controllers.get(id);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid tunnel id [" + _id + "]", nfe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return "";
|
||||
if ("Save".equals(_action))
|
||||
return save();
|
||||
else if ("Remove".equals(_action))
|
||||
return remove();
|
||||
else
|
||||
return "Action <i>" + _action + "</i> unknown";
|
||||
}
|
||||
|
||||
private String remove() {
|
||||
if (!_removeConfirmed)
|
||||
return "Please confirm removal";
|
||||
|
||||
TunnelController cur = getTunnelController();
|
||||
if (cur == null)
|
||||
return "Invalid tunnel number";
|
||||
|
||||
List msgs = TunnelControllerGroup.getInstance().removeController(cur);
|
||||
msgs.addAll(doSave());
|
||||
return getMessages(msgs);
|
||||
}
|
||||
|
||||
private String save() {
|
||||
if (_type == null)
|
||||
return "<b>Invalid form submission (no type?)</b>";
|
||||
Properties config = getConfig();
|
||||
if (config == null)
|
||||
return "<b>Invalid params</b>";
|
||||
|
||||
TunnelController cur = getTunnelController();
|
||||
if (cur == null) {
|
||||
// creating new
|
||||
cur = new TunnelController(config, "", _privKeyGenerate);
|
||||
TunnelControllerGroup.getInstance().addController(cur);
|
||||
} else {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
|
||||
|
||||
return getMessages(doSave());
|
||||
}
|
||||
private List doSave() {
|
||||
TunnelControllerGroup.getInstance().saveConfig();
|
||||
return TunnelControllerGroup.getInstance().clearAllMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* suitable for use in a TunnelController. This will replace (not add to)
|
||||
* any existing parameters, so this should return a comprehensive mapping.
|
||||
*
|
||||
*/
|
||||
private Properties getConfig() {
|
||||
Properties config = new Properties();
|
||||
updateConfigGeneric(config);
|
||||
|
||||
if ("httpclient".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_proxyList != null)
|
||||
config.setProperty("proxyList", _proxyList);
|
||||
} else if ("client".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_targetDestination != null)
|
||||
config.setProperty("targetDestination", _targetDestination);
|
||||
} else if ("server".equals(_type)) {
|
||||
if (_targetHost != null)
|
||||
config.setProperty("targetHost", _targetHost);
|
||||
if (_targetPort != null)
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private void updateConfigGeneric(Properties config) {
|
||||
config.setProperty("type", _type);
|
||||
if (_name != null)
|
||||
config.setProperty("name", _name);
|
||||
if (_description != null)
|
||||
config.setProperty("description", _description);
|
||||
if (_i2cpHost != null)
|
||||
config.setProperty("i2cpHost", _i2cpHost);
|
||||
if (_i2cpPort != null)
|
||||
config.setProperty("i2cpPort", _i2cpPort);
|
||||
|
||||
if (_customOptions != null) {
|
||||
StringTokenizer tok = new StringTokenizer(_customOptions);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty print the messages provided
|
||||
*
|
||||
*/
|
||||
private String getMessages(List msgs) {
|
||||
if (msgs == null) return "";
|
||||
int num = msgs.size();
|
||||
switch (num) {
|
||||
case 0: return "";
|
||||
case 1: return (String)msgs.get(0);
|
||||
default:
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>");
|
||||
for (int i = 0; i < num; i++)
|
||||
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Ugly hack to let the web interface access the list of known tunnels and
|
||||
* control their operation. Any data submitted by setting properties are
|
||||
* acted upon by calling getActionResults() (which returns any messages
|
||||
* generated). In addition, the getSummaryList() generates the html for
|
||||
* summarizing all of the tunnels known, including both their status and the
|
||||
* links to edit, stop, or start them.
|
||||
*
|
||||
*/
|
||||
public class WebStatusPageHelper {
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private int _controllerNum;
|
||||
|
||||
public WebStatusPageHelper() {
|
||||
_action = null;
|
||||
_controllerNum = -1;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebStatusPageHelper.class);
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
_action = action;
|
||||
}
|
||||
public void setNum(String num) {
|
||||
if (num != null) {
|
||||
try {
|
||||
_controllerNum = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_controllerNum = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getActionResults() {
|
||||
try {
|
||||
return processAction();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error processing web status", t);
|
||||
return "Internal error processing request - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSummaryList() {
|
||||
StringBuffer buf = new StringBuffer(4*1024);
|
||||
buf.append("<ul>");
|
||||
List tunnels = TunnelControllerGroup.getInstance().getControllers();
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
buf.append("<li>\n");
|
||||
getSummary(buf, i, (TunnelController)tunnels.get(i));
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</ul>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void getSummary(StringBuffer buf, int num, TunnelController controller) {
|
||||
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> ");
|
||||
} 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=\"edit.jsp?num=").append(num).append("\">edit</a> ");
|
||||
buf.append("<br />\n");
|
||||
controller.getSummary(buf);
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return getMessages();
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
else if ("Start all".equals(_action))
|
||||
return startAll();
|
||||
else if ("Restart all".equals(_action))
|
||||
return restartAll();
|
||||
else if ("Reload config".equals(_action))
|
||||
return reloadConfig();
|
||||
else if ("stop".equals(_action))
|
||||
return stop();
|
||||
else if ("start".equals(_action))
|
||||
return start();
|
||||
else
|
||||
return "Action <i>" + _action + "</i> unknown";
|
||||
}
|
||||
private String stopAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().stopAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String startAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().startAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String restartAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().restartAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String reloadConfig() {
|
||||
TunnelControllerGroup.getInstance().reloadControllers();
|
||||
return "Config reloaded";
|
||||
}
|
||||
private String start() {
|
||||
if (_controllerNum < 0) return "Invalid tunnel";
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.startTunnel();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
private String stop() {
|
||||
if (_controllerNum < 0) return "Invalid tunnel";
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.stopTunnel();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
private String getMessages() {
|
||||
return getMessages(TunnelControllerGroup.getInstance().clearAllMessages());
|
||||
}
|
||||
|
||||
private String getMessages(List msgs) {
|
||||
if (msgs == null) return "";
|
||||
int num = msgs.size();
|
||||
switch (num) {
|
||||
case 0: return "";
|
||||
case 1: return (String)msgs.get(0);
|
||||
default:
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>");
|
||||
for (int i = 0; i < num; i++)
|
||||
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
16
apps/i2ptunnel/jsp/edit.jsp
Normal file
16
apps/i2ptunnel/jsp/edit.jsp
Normal file
@ -0,0 +1,16 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2PTunnel edit</title>
|
||||
</head><body>
|
||||
|
||||
<a href="index.jsp">Back</a>
|
||||
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.WebEditPageHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="*" />
|
||||
<b><jsp:getProperty name="helper" property="actionResults" /></b>
|
||||
|
||||
<jsp:getProperty name="helper" property="editForm" />
|
||||
</body>
|
||||
</html>
|
2
apps/i2ptunnel/jsp/index.html
Normal file
2
apps/i2ptunnel/jsp/index.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>I2P Router Console</title></head>
|
||||
<body><meta http-equiv="refresh" content="0;url=index.jsp" /><a href="index.jsp">Enter</a></body></html>
|
31
apps/i2ptunnel/jsp/index.jsp
Normal file
31
apps/i2ptunnel/jsp/index.jsp
Normal file
@ -0,0 +1,31 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2PTunnel status</title>
|
||||
</head><body>
|
||||
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.WebStatusPageHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="*" />
|
||||
<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>
|
||||
<select name="type">
|
||||
<option value="httpclient">HTTP proxy</option>
|
||||
<option value="client">Client tunnel</option>
|
||||
<option value="server">Server tunnel</option>
|
||||
</select> <input type="submit" value="GO" />
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
17
apps/i2ptunnel/jsp/web.xml
Normal file
17
apps/i2ptunnel/jsp/web.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?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>
|
||||
<!-- precompiled servlets -->
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
28
apps/jetty/build.xml
Normal file
28
apps/jetty/build.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="jetty">
|
||||
|
||||
<target name="all" depends="build" />
|
||||
<target name="fetchJettylib" >
|
||||
<available property="jetty.available" file="jettylib" />
|
||||
<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="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" />
|
||||
</target>
|
||||
<target name="build" depends="fetchJettylib" />
|
||||
<target name="builddep" />
|
||||
<target name="compile" />
|
||||
<target name="jar" />
|
||||
<target name="clean" />
|
||||
<target name="cleandep" depends="clean" />
|
||||
<target name="distclean" depends="clean">
|
||||
<echo message="Not actually deleting the jetty libs (since they're so large)" />
|
||||
</target>
|
||||
</project>
|
@ -32,8 +32,18 @@ public interface I2PSocket {
|
||||
*/
|
||||
public OutputStream getOutputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Retrieve this socket's configuration
|
||||
*/
|
||||
public I2PSocketOptions getOptions();
|
||||
/**
|
||||
* Configure the socket
|
||||
*/
|
||||
public void setOptions(I2PSocketOptions options);
|
||||
|
||||
/**
|
||||
* How long we will wait blocked on a read() operation.
|
||||
* How long we will wait blocked on a read() operation. This is simply a
|
||||
* helper to query the I2PSocketOptions
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
@ -41,7 +51,8 @@ public interface I2PSocket {
|
||||
|
||||
/**
|
||||
* Define how long we will wait blocked on a read() operation (-1 will make
|
||||
* the socket wait forever).
|
||||
* the socket wait forever). This is simply a helper to adjust the
|
||||
* I2PSocketOptions
|
||||
*
|
||||
*/
|
||||
public void setReadTimeout(long ms);
|
||||
|
@ -9,6 +9,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -40,6 +41,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
private long _createdOn;
|
||||
private long _closedOn;
|
||||
private long _remoteIdSetTime;
|
||||
private I2PSocketOptions _options;
|
||||
private Object flagLock = new Object();
|
||||
|
||||
/**
|
||||
@ -73,14 +75,17 @@ class I2PSocketImpl implements I2PSocket {
|
||||
remote = peer;
|
||||
_socketId = ++__socketId;
|
||||
local = mgr.getSession().getMyDestination();
|
||||
in = new I2PInputStream();
|
||||
I2PInputStream pin = new I2PInputStream();
|
||||
String us = mgr.getSession().getMyDestination().calculateHash().toBase64().substring(0,4);
|
||||
String name = us + (outgoing ? "->" : "<-") + peer.calculateHash().toBase64().subSequence(0,4);
|
||||
in = new I2PInputStream(name + " in");
|
||||
I2PInputStream pin = new I2PInputStream(name + " out");
|
||||
out = new I2POutputStream(pin);
|
||||
new I2PSocketRunner(pin);
|
||||
this.localID = localID;
|
||||
_createdOn = I2PAppContext.getGlobalContext().clock().now();
|
||||
_remoteIdSetTime = -1;
|
||||
_closedOn = -1;
|
||||
_options = mgr.getDefaultOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,7 +181,11 @@ class I2PSocketImpl implements I2PSocket {
|
||||
*/
|
||||
public void queueData(byte[] data) {
|
||||
_bytesRead += data.length;
|
||||
in.queueData(data);
|
||||
try {
|
||||
in.queueData(data, false);
|
||||
} catch (IOException ioe) {
|
||||
_log.log(Log.CRIT, "wtf, we said DONT block, how can we timeout?", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,19 +254,36 @@ class I2PSocketImpl implements I2PSocket {
|
||||
return (byte)(I2PSocketManager.DATA_OUT + (byte)add);
|
||||
}
|
||||
|
||||
public void setOptions(I2PSocketOptions options) {
|
||||
_options = options;
|
||||
in.setReadTimeout(options.getReadTimeout());
|
||||
}
|
||||
|
||||
public I2PSocketOptions getOptions() {
|
||||
return _options;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data? If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
* How long we will wait blocked on a read() operation. This is simply a
|
||||
* helper to query the I2PSocketOptions
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return in.getReadTimeout();
|
||||
return _options.getReadTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define how long we will wait blocked on a read() operation (-1 will make
|
||||
* the socket wait forever). This is simply a helper to adjust the
|
||||
* I2PSocketOptions
|
||||
*
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_options.setReadTimeout(ms);
|
||||
in.setReadTimeout(ms);
|
||||
}
|
||||
|
||||
|
||||
public void setSocketErrorListener(SocketErrorListener lsnr) {
|
||||
_socketErrorListener = lsnr;
|
||||
}
|
||||
@ -277,15 +303,24 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
//--------------------------------------------------
|
||||
private class I2PInputStream extends InputStream {
|
||||
|
||||
private String streamName;
|
||||
private ByteCollector bc = new ByteCollector();
|
||||
private boolean inStreamClosed = false;
|
||||
|
||||
private long readTimeout = -1;
|
||||
|
||||
public I2PInputStream(String name) {
|
||||
streamName = name;
|
||||
}
|
||||
|
||||
public long getReadTimeout() {
|
||||
return readTimeout;
|
||||
}
|
||||
|
||||
private String getStreamPrefix() {
|
||||
return getPrefix() + streamName + ": ";
|
||||
}
|
||||
|
||||
public void setReadTimeout(long ms) {
|
||||
readTimeout = ms;
|
||||
}
|
||||
@ -300,20 +335,24 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Read called for " + len + " bytes (avail=" + bc.getCurrentSize() + "): " + this.hashCode());
|
||||
_log.debug(getStreamPrefix() + "Read called for " + len + " bytes (avail="
|
||||
+ bc.getCurrentSize() + "): " + this.hashCode());
|
||||
if (len == 0) return 0;
|
||||
long dieAfter = System.currentTimeMillis() + readTimeout;
|
||||
byte[] read = null;
|
||||
synchronized (bc) {
|
||||
read = bc.startToByteArray(len);
|
||||
bc.notifyAll();
|
||||
}
|
||||
boolean timedOut = false;
|
||||
|
||||
while (read.length == 0) {
|
||||
while ( (read.length == 0) && (!inStreamClosed) ) {
|
||||
synchronized (flagLock) {
|
||||
if (closed) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Closed is set after reading " + _bytesRead + " and writing " + _bytesWritten + ", so closing stream: " + hashCode());
|
||||
_log.debug(getStreamPrefix() + "Closed is set after reading "
|
||||
+ _bytesRead + " and writing " + _bytesWritten
|
||||
+ ", so closing stream: " + hashCode());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -329,18 +368,23 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
if ((readTimeout >= 0)
|
||||
&& (System.currentTimeMillis() >= dieAfter)) {
|
||||
throw new InterruptedIOException(getPrefix() + "Timeout reading from I2PSocket (" + readTimeout + " msecs)");
|
||||
throw new InterruptedIOException(getStreamPrefix() + "Timeout reading from I2PSocket ("
|
||||
+ readTimeout + " msecs)");
|
||||
}
|
||||
|
||||
synchronized (bc) {
|
||||
read = bc.startToByteArray(len);
|
||||
bc.notifyAll();
|
||||
}
|
||||
}
|
||||
if (read.length > len) throw new RuntimeException("BUG");
|
||||
if ( (inStreamClosed) && ( (read == null) || (read.length <= 0) ) )
|
||||
return -1;
|
||||
|
||||
System.arraycopy(read, 0, b, off, read.length);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
|
||||
_log.debug(getStreamPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
|
||||
+ read.length + " bytes");
|
||||
}
|
||||
//if (_log.shouldLog(Log.DEBUG)) {
|
||||
@ -357,19 +401,66 @@ class I2PSocketImpl implements I2PSocket {
|
||||
}
|
||||
}
|
||||
|
||||
public void queueData(byte[] data) {
|
||||
queueData(data, 0, data.length);
|
||||
/**
|
||||
* Add the data to the queue
|
||||
*
|
||||
* @param allowBlock if true, we will block if the buffer and the socket options
|
||||
* say so, otherwise we simply take the data regardless.
|
||||
* @throws InterruptedIOException if the queue's buffer is full, the socket has
|
||||
* a write timeout, and that timeout is exceeded
|
||||
* @throws IOException if the connection was closed while queueing up the data
|
||||
*/
|
||||
void queueData(byte[] data, boolean allowBlock) throws InterruptedIOException, IOException {
|
||||
queueData(data, 0, data.length, allowBlock);
|
||||
}
|
||||
|
||||
public void queueData(byte[] data, int off, int len) {
|
||||
/**
|
||||
* Add the data to the queue
|
||||
*
|
||||
* @param allowBlock if true, we will block if the buffer and the socket options
|
||||
* say so, otherwise we simply take the data regardless.
|
||||
* @throws InterruptedIOException if the queue's buffer is full, the socket has
|
||||
* a write timeout, and that timeout is exceeded
|
||||
* @throws IOException if the connection was closed while queueing up the data
|
||||
*/
|
||||
public void queueData(byte[] data, int off, int len, boolean allowBlock) throws InterruptedIOException, IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
|
||||
_log.debug(getStreamPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
|
||||
Clock clock = I2PAppContext.getGlobalContext().clock();
|
||||
long endAfter = clock.now() + _options.getWriteTimeout();
|
||||
synchronized (bc) {
|
||||
if (allowBlock) {
|
||||
if (_options.getMaxBufferSize() > 0) {
|
||||
while (bc.getCurrentSize() > _options.getMaxBufferSize()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getStreamPrefix() + "Buffer size exceeded: pending "
|
||||
+ bc.getCurrentSize() + " limit " + _options.getMaxBufferSize());
|
||||
if (_options.getWriteTimeout() > 0) {
|
||||
long timeLeft = endAfter - clock.now();
|
||||
if (timeLeft <= 0) {
|
||||
long waited = _options.getWriteTimeout() - timeLeft;
|
||||
throw new InterruptedIOException(getStreamPrefix() + "Waited too long ("
|
||||
+ waited + "ms) to write "
|
||||
+ len + " with a buffer at " + bc.getCurrentSize());
|
||||
}
|
||||
}
|
||||
if (inStreamClosed)
|
||||
throw new IOException(getStreamPrefix() + "Stream closed while writing");
|
||||
if (_closedOn > 0)
|
||||
throw new IOException(getStreamPrefix() + "I2PSocket closed while writing");
|
||||
try {
|
||||
bc.wait(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
bc.append(data, off, len);
|
||||
}
|
||||
synchronized (I2PInputStream.this) {
|
||||
I2PInputStream.this.notifyAll();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getStreamPrefix() + "After insert " + len + " bytes into queue: " + hashCode());
|
||||
}
|
||||
|
||||
public void notifyClosed() {
|
||||
@ -381,6 +472,12 @@ class I2PSocketImpl implements I2PSocket {
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
notifyClosed();
|
||||
synchronized (bc) {
|
||||
inStreamClosed = true;
|
||||
bc.notifyAll();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getStreamPrefix() + "After close");
|
||||
}
|
||||
|
||||
}
|
||||
@ -399,7 +496,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
_bytesWritten += len;
|
||||
sendTo.queueData(b, off, len);
|
||||
sendTo.queueData(b, off, len, true);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -428,10 +525,21 @@ class I2PSocketImpl implements I2PSocket {
|
||||
*/
|
||||
private boolean handleNextPacket(ByteCollector bc, byte buffer[])
|
||||
throws IOException, I2PSessionException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "handleNextPacket");
|
||||
int len = in.read(buffer);
|
||||
int bcsize = bc.getCurrentSize();
|
||||
int bcsize = 0;
|
||||
synchronized (bc) {
|
||||
bcsize = bc.getCurrentSize();
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "handleNextPacket len=" + len + " bcsize=" + bcsize);
|
||||
|
||||
if (len != -1) {
|
||||
bc.append(buffer, len);
|
||||
synchronized (bc) {
|
||||
bc.append(buffer, len);
|
||||
}
|
||||
} else if (bcsize == 0) {
|
||||
// nothing left in the buffer, and read(..) got EOF (-1).
|
||||
// the bart the
|
||||
@ -439,7 +547,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
}
|
||||
if ((bcsize < MAX_PACKET_SIZE) && (in.available() == 0)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Runner Point d: " + hashCode());
|
||||
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Runner Point d: " + hashCode());
|
||||
|
||||
try {
|
||||
Thread.sleep(PACKET_DELAY);
|
||||
@ -448,16 +556,22 @@ class I2PSocketImpl implements I2PSocket {
|
||||
}
|
||||
}
|
||||
if ((bcsize >= MAX_PACKET_SIZE) || (in.available() == 0)) {
|
||||
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
|
||||
byte data[] = null;
|
||||
synchronized (bc) {
|
||||
data = bc.startToByteArray(MAX_PACKET_SIZE);
|
||||
}
|
||||
if (data.length > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Message size is: " + data.length);
|
||||
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Message size is: " + data.length);
|
||||
boolean sent = sendBlock(data);
|
||||
if (!sent) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Error sending message to peer. Killing socket runner");
|
||||
_log.warn(getPrefix() + ":" + Thread.currentThread().getName() + "Error sending message to peer. Killing socket runner");
|
||||
errorOccurred();
|
||||
return false;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Message sent to peer");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -474,7 +588,14 @@ class I2PSocketImpl implements I2PSocket {
|
||||
while (keepHandling) {
|
||||
keepHandling = handleNextPacket(bc, buffer);
|
||||
packetsHandled++;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + ":" + Thread.currentThread().getName()
|
||||
+ "Packets handled: " + packetsHandled);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + ":" + Thread.currentThread().getName()
|
||||
+ "After handling packets, we're done. Packets handled: " + packetsHandled);
|
||||
|
||||
if ((bc.getCurrentSize() > 0) && (packetsHandled > 1)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "We lost some data queued up due to a network send error (input stream: "
|
||||
@ -490,16 +611,20 @@ class I2PSocketImpl implements I2PSocket {
|
||||
} // FIXME: Race here?
|
||||
if (sc) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Sending close packet: (we started? " + outgoing + ") after reading " + _bytesRead + " and writing " + _bytesWritten);
|
||||
_log.info(getPrefix() + ":" + Thread.currentThread().getName()
|
||||
+ "Sending close packet: (we started? " + outgoing
|
||||
+ ") after reading " + _bytesRead + " and writing " + _bytesWritten);
|
||||
byte[] packet = I2PSocketManager.makePacket(getMask(0x02), remoteID, new byte[0]);
|
||||
boolean sent = manager.getSession().sendMessage(remote, packet);
|
||||
if (!sent) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Error sending close packet to peer");
|
||||
_log.warn(getPrefix() + ":" + Thread.currentThread().getName()
|
||||
+ "Error sending close packet to peer");
|
||||
errorOccurred();
|
||||
}
|
||||
}
|
||||
manager.removeSocket(I2PSocketImpl.this);
|
||||
internalClose();
|
||||
} catch (InterruptedIOException ex) {
|
||||
_log.error(getPrefix() + "BUG! read() operations should not timeout!", ex);
|
||||
} catch (IOException ex) {
|
||||
|
@ -377,7 +377,7 @@ public class I2PSocketManager implements I2PSessionListener {
|
||||
*
|
||||
* @throws IllegalStateException if the socket isn't open or isn't known
|
||||
*/
|
||||
private void sendIncoming(String id, byte payload[]) {
|
||||
private void sendIncoming(String id, byte payload[]) throws IllegalStateException {
|
||||
I2PSocketImpl s = null;
|
||||
synchronized (lock) {
|
||||
s = (I2PSocketImpl) _inSockets.get(id);
|
||||
@ -469,7 +469,10 @@ public class I2PSocketManager implements I2PSessionListener {
|
||||
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
|
||||
throw new I2PException("Error sending through I2P network");
|
||||
}
|
||||
remoteID = s.getRemoteID(true, options.getConnectTimeout());
|
||||
if (options != null)
|
||||
remoteID = s.getRemoteID(true, options.getConnectTimeout());
|
||||
else
|
||||
remoteID = s.getRemoteID(true, getDefaultOptions().getConnectTimeout());
|
||||
|
||||
if (remoteID == null) {
|
||||
_context.statManager().addRateData("streaming.nackReceived", 1, 1);
|
||||
|
@ -30,7 +30,18 @@ public class I2PSocketManagerFactory {
|
||||
* @return the newly created socket manager, or null if there were errors
|
||||
*/
|
||||
public static I2PSocketManager createManager() {
|
||||
return createManager("localhost", 7654, new Properties());
|
||||
String i2cpHost = System.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
int i2cpPort = 7654;
|
||||
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
|
||||
if (i2cpPortStr != null) {
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// gobble gobble
|
||||
}
|
||||
}
|
||||
|
||||
return createManager(i2cpHost, i2cpPort, System.getProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,6 +92,7 @@ public class I2PSocketManagerFactory {
|
||||
private static I2PSocketManager createManager(I2PSession session) {
|
||||
I2PSocketManager mgr = new I2PSocketManager();
|
||||
mgr.setSession(session);
|
||||
mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
return mgr;
|
||||
}
|
||||
}
|
@ -6,9 +6,18 @@ package net.i2p.client.streaming;
|
||||
*/
|
||||
public class I2PSocketOptions {
|
||||
private long _connectTimeout;
|
||||
private long _readTimeout;
|
||||
private long _writeTimeout;
|
||||
private int _maxBufferSize;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
|
||||
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
|
||||
|
||||
public I2PSocketOptions() {
|
||||
_connectTimeout = -1;
|
||||
_readTimeout = -1;
|
||||
_writeTimeout = DEFAULT_WRITE_TIMEOUT;
|
||||
_maxBufferSize = DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,4 +36,65 @@ public class I2PSocketOptions {
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
return _maxBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,135 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple streaming lib test app that connects to a given destination and sends
|
||||
* it a particular amount of random data, then disconnects. See the {@link #main}
|
||||
*
|
||||
*/
|
||||
public class StreamSinkClient {
|
||||
private Log _log;
|
||||
private int _sendSize;
|
||||
private int _writeDelay;
|
||||
private String _peerDestFile;
|
||||
|
||||
|
||||
/**
|
||||
* Build the client but don't fire it up.
|
||||
* @param sendSize how many KB to send
|
||||
* @param writeDelayMs how long to wait between each .write (0 for no delay)
|
||||
* @param serverDestFile file containing the StreamSinkServer's binary Destination
|
||||
*/
|
||||
public StreamSinkClient(int sendSize, int writeDelayMs, String serverDestFile) {
|
||||
_sendSize = sendSize;
|
||||
_writeDelay = writeDelayMs;
|
||||
_peerDestFile = serverDestFile;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkClient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually connect and run the client - this call blocks until completion.
|
||||
*
|
||||
*/
|
||||
public void runClient() {
|
||||
I2PSocketManager mgr = I2PSocketManagerFactory.createManager();
|
||||
Destination peer = null;
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(_peerDestFile);
|
||||
peer = new Destination();
|
||||
peer.readBytes(fis);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error finding the peer destination to contact in " + _peerDestFile, ioe);
|
||||
return;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Peer destination is not valid in " + _peerDestFile, dfe);
|
||||
return;
|
||||
} finally {
|
||||
if (fis == null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
|
||||
System.out.println("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
|
||||
|
||||
try {
|
||||
I2PSocket sock = mgr.connect(peer);
|
||||
byte buf[] = new byte[32*1024];
|
||||
Random rand = new Random();
|
||||
OutputStream out = sock.getOutputStream();
|
||||
long beforeSending = System.currentTimeMillis();
|
||||
for (int i = 0; i < _sendSize; i+= 32) {
|
||||
rand.nextBytes(buf);
|
||||
out.write(buf);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote " + (i+32) + "/" + _sendSize + "KB");
|
||||
if (_writeDelay > 0) {
|
||||
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
long afterSending = System.currentTimeMillis();
|
||||
System.out.println("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
|
||||
sock.close();
|
||||
} catch (InterruptedIOException iie) {
|
||||
_log.error("Timeout connecting to the peer", iie);
|
||||
return;
|
||||
} catch (NoRouteToHostException nrthe) {
|
||||
_log.error("Unable to connect to the peer", nrthe);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error connecting to the peer", ie);
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IO error sending", ioe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the client. <code>Usage: StreamSinkClient sendSizeKB writeDelayMs serverDestFile</code> <br />
|
||||
* <ul>
|
||||
* <li><b>sendSizeKB</b>: how many KB to send</li>
|
||||
* <li><b>writeDelayMs</b>: how long to wait between each .write (0 for no delay)</li>
|
||||
* <li><b>serverDestFile</b>: file containing the StreamSinkServer's binary Destination</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 3) {
|
||||
System.out.println("Usage: StreamSinkClient sendSizeKB writeDelayMs serverDestFile");
|
||||
} else {
|
||||
int sendSizeKB = -1;
|
||||
int writeDelayMs = -1;
|
||||
try {
|
||||
sendSizeKB = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Send size invalid [" + args[0] + "]");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
writeDelayMs = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Write delay ms invalid [" + args[1] + "]");
|
||||
return;
|
||||
}
|
||||
StreamSinkClient client = new StreamSinkClient(sendSizeKB, writeDelayMs, args[2]);
|
||||
client.runClient();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.ConnectException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Listen to a destination, receiving any sockets and writing anything they
|
||||
* send to a new file. See the {@link #main}
|
||||
*
|
||||
*/
|
||||
public class StreamSinkServer {
|
||||
private Log _log;
|
||||
private String _sinkDir;
|
||||
private String _destFile;
|
||||
|
||||
/**
|
||||
* Create but do not start the streaming server.
|
||||
*
|
||||
* @param sinkDir Directory to store received files in
|
||||
* @param ourDestFile filename to write our binary destination to
|
||||
*/
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile) {
|
||||
_sinkDir = sinkDir;
|
||||
_destFile = ourDestFile;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually fire up the server - this call blocks forever (or until the server
|
||||
* socket closes)
|
||||
*
|
||||
*/
|
||||
public void runServer() {
|
||||
I2PSocketManager mgr = I2PSocketManagerFactory.createManager();
|
||||
Destination dest = mgr.getSession().getMyDestination();
|
||||
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(_destFile);
|
||||
dest.writeBytes(fos);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing out our destination to " + _destFile, ioe);
|
||||
return;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error formatting the destination", dfe);
|
||||
return;
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
I2PServerSocket sock = mgr.getServerSocket();
|
||||
while (true) {
|
||||
try {
|
||||
I2PSocket curSock = sock.accept();
|
||||
handle(curSock);
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error accepting connection", ie);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(I2PSocket socket) {
|
||||
I2PThread t = new I2PThread(new ClientRunner(socket));
|
||||
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
|
||||
t.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually deal with a client - pull anything they send us and write it to a file.
|
||||
*
|
||||
*/
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PSocket _sock;
|
||||
private FileOutputStream _fos;
|
||||
public ClientRunner(I2PSocket socket) {
|
||||
_sock = socket;
|
||||
try {
|
||||
File sink = new File(_sinkDir);
|
||||
if (!sink.exists())
|
||||
sink.mkdirs();
|
||||
File cur = File.createTempFile("clientSink", ".dat", sink);
|
||||
_fos = new FileOutputStream(cur);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error creating sink", ioe);
|
||||
_fos = null;
|
||||
}
|
||||
}
|
||||
public void run() {
|
||||
if (_fos == null) return;
|
||||
try {
|
||||
InputStream in = _sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
_fos.write(buf, 0, read);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
if (_fos != null) try { _fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the streaming server. <code>Usage: StreamSinkServer sinkDir ourDestFile</code><br />
|
||||
* <ul>
|
||||
* <li><b>sinkDir</b>: Directory to store received files in</li>
|
||||
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 2) {
|
||||
System.out.println("Usage: StreamSinkServer sinkDir ourDestFile");
|
||||
} else {
|
||||
StreamSinkServer server = new StreamSinkServer(args[0], args[1]);
|
||||
server.runServer();
|
||||
}
|
||||
}
|
||||
}
|
@ -15,4 +15,10 @@ net.i2p.client.streaming.I2PServerSocket#accept} method, which will provide an
|
||||
{@link net.i2p.client.streaming.I2PSocket} when a new one is available. If an
|
||||
application wants to create a new stream to a peer, it should do so with the
|
||||
appropriate {@link net.i2p.client.streaming.I2PSocketManager#connect} call.</p>
|
||||
|
||||
<p>There is a simple pair of demo applications available as well - {@link
|
||||
net.i2p.client.streaming.StreamSinkServer} listens to a destination and dumps
|
||||
the data from all sockets it accepts to individual files, while {@link
|
||||
net.i2p.client.streaming.StreamSinkClient} connects to a particular destination
|
||||
and sends a specific amount of random data then disconnects.</p>
|
||||
</body></html>
|
||||
|
@ -72,7 +72,7 @@ class DataHarvester {
|
||||
*/
|
||||
private void harvestRankAs(NetMonitor monitor, RouterInfo peer) {
|
||||
int numFast = 0;
|
||||
int numReliable = 0;
|
||||
int numHighCapacity = 0;
|
||||
int numNotFailing = 0;
|
||||
int numFailing = 0;
|
||||
|
||||
@ -81,10 +81,10 @@ class DataHarvester {
|
||||
String key = (String)iter.next();
|
||||
if (key.startsWith("profile.")) {
|
||||
String val = (String)props.get(key);
|
||||
if (val.indexOf("fastReliable") != -1)
|
||||
if (val.indexOf("fast") != -1)
|
||||
numFast++;
|
||||
else if (val.indexOf("reliable") != -1)
|
||||
numReliable++;
|
||||
else if (val.indexOf("highCapacity") != -1)
|
||||
numHighCapacity++;
|
||||
else if (val.indexOf("notFailing") != -1)
|
||||
numNotFailing++;
|
||||
else if (val.indexOf("failing") != -1)
|
||||
@ -94,13 +94,13 @@ class DataHarvester {
|
||||
|
||||
long rankAs[] = new long[4];
|
||||
rankAs[0] = numFast;
|
||||
rankAs[1] = numReliable;
|
||||
rankAs[1] = numHighCapacity;
|
||||
rankAs[2] = numNotFailing;
|
||||
rankAs[3] = numFailing;
|
||||
String description = "how we rank peers";
|
||||
String valDescr[] = new String[4];
|
||||
valDescr[0] = "# peers we rank as fast";
|
||||
valDescr[1] = "# peers we rank as reliable";
|
||||
valDescr[1] = "# peers we rank as high capacity";
|
||||
valDescr[2] = "# peers we rank as not failing";
|
||||
valDescr[3] = "# peers we rank as failing";
|
||||
monitor.addData(peer.getIdentity().getHash().toBase64(), "rankAs", description, valDescr, peer.getPublished(), rankAs);
|
||||
@ -115,7 +115,7 @@ class DataHarvester {
|
||||
*/
|
||||
private void harvestRank(NetMonitor monitor, RouterInfo peer, List peers) {
|
||||
int numFast = 0;
|
||||
int numReliable = 0;
|
||||
int numHighCapacity = 0;
|
||||
int numNotFailing = 0;
|
||||
int numFailing = 0;
|
||||
|
||||
@ -126,10 +126,10 @@ class DataHarvester {
|
||||
String prop = "profile." + peer.getIdentity().getHash().toBase64().replace('=', '_');
|
||||
String val = cur.getOptions().getProperty(prop);
|
||||
if ( (val == null) || (val.length() <= 0) ) continue;
|
||||
if (val.indexOf("fastReliable") != -1)
|
||||
if (val.indexOf("fast") != -1)
|
||||
numFast++;
|
||||
else if (val.indexOf("reliable") != -1)
|
||||
numReliable++;
|
||||
else if (val.indexOf("highCapacity") != -1)
|
||||
numHighCapacity++;
|
||||
else if (val.indexOf("notFailing") != -1)
|
||||
numNotFailing++;
|
||||
else if (val.indexOf("failing") != -1)
|
||||
@ -138,13 +138,13 @@ class DataHarvester {
|
||||
|
||||
long rank[] = new long[4];
|
||||
rank[0] = numFast;
|
||||
rank[1] = numReliable;
|
||||
rank[1] = numHighCapacity;
|
||||
rank[2] = numNotFailing;
|
||||
rank[3] = numFailing;
|
||||
String description = "how peers rank us";
|
||||
String valDescr[] = new String[4];
|
||||
valDescr[0] = "# peers ranking us as fast";
|
||||
valDescr[1] = "# peers ranking us as reliable";
|
||||
valDescr[1] = "# peers ranking us as high capacity";
|
||||
valDescr[2] = "# peers ranking us as not failing";
|
||||
valDescr[3] = "# peers ranking us as failing";
|
||||
// we use the current date, not the published date, since this sample doesnt come from them
|
||||
@ -173,7 +173,6 @@ class DataHarvester {
|
||||
double values[] = harvestGroupValues(peer, group);
|
||||
if (values == null) return;
|
||||
|
||||
String description = "how long it takes to do an ElGamal encryption";
|
||||
String valDescr[] = new String[group.getStatCount()];
|
||||
for (int i = 0; i < group.getStatCount(); i++)
|
||||
valDescr[i] = group.getStat(i).getStatDescription();
|
||||
|
@ -18,7 +18,7 @@ import net.i2p.util.Log;
|
||||
* Main driver for the app that harvests data about the performance of the network,
|
||||
* building summaries for each peer that change over time. <p />
|
||||
*
|
||||
* Usage: <code>NetMonitor [configFilename] [--routers filename[,filename]*]</code> <p />
|
||||
* Usage: <code>NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url] </code> <p />
|
||||
*
|
||||
*
|
||||
*
|
||||
@ -27,7 +27,7 @@ public class NetMonitor {
|
||||
private static final Log _log = new Log(NetMonitor.class);
|
||||
public static final String CONFIG_LOCATION_DEFAULT = "netmonitor.config";
|
||||
public static final String HARVEST_DELAY_PROP = "harvestDelaySeconds";
|
||||
public static final int HARVEST_DELAY_DEFAULT = 60;
|
||||
public static final int HARVEST_DELAY_DEFAULT = 5*60;
|
||||
public static final String EXPORT_DELAY_PROP = "exportDelaySeconds";
|
||||
public static final int EXPORT_DELAY_DEFAULT = 120;
|
||||
public static final String SUMMARY_DURATION_PROP = "summaryDurationHours";
|
||||
@ -41,20 +41,22 @@ public class NetMonitor {
|
||||
private int _exportDelay;
|
||||
private String _exportDir;
|
||||
private String _netDbDir;
|
||||
private String _netDbURL;
|
||||
private String _explicitRouters;
|
||||
private int _summaryDurationHours;
|
||||
private boolean _isRunning;
|
||||
private Map _peerSummaries;
|
||||
|
||||
public NetMonitor() {
|
||||
this(CONFIG_LOCATION_DEFAULT, null);
|
||||
this(CONFIG_LOCATION_DEFAULT, null, null);
|
||||
}
|
||||
public NetMonitor(String configLocation) {
|
||||
this(configLocation, null);
|
||||
this(configLocation, null, null);
|
||||
}
|
||||
public NetMonitor(String configLocation, String explicitFilenames) {
|
||||
public NetMonitor(String configLocation, String explicitFilenames, String url) {
|
||||
_configLocation = configLocation;
|
||||
_explicitRouters = explicitFilenames;
|
||||
_netDbURL = url;
|
||||
_peerSummaries = new HashMap(32);
|
||||
loadConfig();
|
||||
}
|
||||
@ -127,6 +129,9 @@ public class NetMonitor {
|
||||
public String getNetDbDir() { return _netDbDir; }
|
||||
/** if specified, contains a set of filenames we want to harvest routerInfo data from */
|
||||
public String getExplicitRouters() { return _explicitRouters; }
|
||||
/** if specified, contains a URL to fetch references from */
|
||||
public String getNetDbURL() { return _netDbURL; }
|
||||
|
||||
/**
|
||||
* what peers are we keeping track of?
|
||||
*
|
||||
@ -212,11 +217,12 @@ public class NetMonitor {
|
||||
|
||||
/**
|
||||
* main driver for the netMonitor. the usage is:
|
||||
* <code>NetMonitor [configFilename] [--routers filename[,filename]*]</code>
|
||||
* <code>NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url]</code>
|
||||
*/
|
||||
public static final void main(String args[]) {
|
||||
String cfgLocation = CONFIG_LOCATION_DEFAULT;
|
||||
String explicitFilenames = null;
|
||||
String explicitURL = null;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
break;
|
||||
@ -224,16 +230,22 @@ public class NetMonitor {
|
||||
cfgLocation = args[0];
|
||||
break;
|
||||
case 2:
|
||||
explicitFilenames = args[1];
|
||||
if ("--routers".equalsIgnoreCase(args[0]))
|
||||
explicitFilenames = args[1];
|
||||
else
|
||||
explicitURL = args[1];
|
||||
break;
|
||||
case 3:
|
||||
cfgLocation = args[0];
|
||||
explicitFilenames = args[2];
|
||||
if ("--routers".equalsIgnoreCase(args[1]))
|
||||
explicitFilenames = args[2];
|
||||
else
|
||||
explicitURL = args[2];
|
||||
break;
|
||||
default:
|
||||
System.err.println("Usage: NetMonitor [configFilename] [--routers filename[,filename]*]");
|
||||
System.err.println("Usage: NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url]");
|
||||
return;
|
||||
}
|
||||
new NetMonitor(cfgLocation, explicitFilenames).startMonitor();
|
||||
new NetMonitor(cfgLocation, explicitFilenames, explicitURL).startMonitor();
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,19 @@
|
||||
package net.i2p.netmonitor;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -64,6 +71,9 @@ class NetMonitorRunner implements Runnable {
|
||||
*
|
||||
*/
|
||||
private List getRouters() {
|
||||
if (_monitor.getNetDbURL() != null)
|
||||
return fetchRouters(_monitor.getNetDbURL());
|
||||
|
||||
File routers[] = listRouters();
|
||||
List rv = new ArrayList(64);
|
||||
if (routers != null) {
|
||||
@ -86,6 +96,65 @@ class NetMonitorRunner implements Runnable {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
private List fetchRouters(String seedURL) {
|
||||
List rv = new ArrayList();
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
String content = new String(readURL(dir));
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
int start = content.indexOf("href=\"routerInfo-", cur);
|
||||
if (start < 0)
|
||||
break;
|
||||
|
||||
int end = content.indexOf(".dat\">", start);
|
||||
String name = content.substring(start+"href=\"routerInfo-".length(), end);
|
||||
urls.add(name);
|
||||
cur = end + 1;
|
||||
}
|
||||
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
rv.add(fetchSeed((String)iter.next()));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
_log.error("Error fetching routers from " + seedURL, t);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private RouterInfo fetchSeed(String peer) throws Exception {
|
||||
URL url = new URL("http://i2p.net/i2pdb/routerInfo-" + peer + ".dat");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Fetching seed from " + url.toExternalForm());
|
||||
|
||||
byte data[] = readURL(url);
|
||||
RouterInfo info = new RouterInfo();
|
||||
try {
|
||||
info.fromByteArray(data);
|
||||
return info;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Router data at " + url.toExternalForm() + " was corrupt", dfe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
URLConnection con = url.openConnection();
|
||||
InputStream in = con.getInputStream();
|
||||
byte buf[] = new byte[1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
if (read < 0)
|
||||
break;
|
||||
baos.write(buf, 0, read);
|
||||
}
|
||||
in.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* dump the data to the filesystem
|
||||
*/
|
||||
|
@ -2,22 +2,21 @@
|
||||
<project basedir="." default="all" name="routerconsole">
|
||||
<target name="all" depends="clean, build" />
|
||||
<target name="build" depends="builddep, jar" />
|
||||
<target name="builddep" depends="jetty" >
|
||||
<target name="builddep">
|
||||
<ant dir="../../../router/java/" target="build" />
|
||||
<!-- router will build core -->
|
||||
</target>
|
||||
<target name="jetty">
|
||||
<untar src="jetty-4.2.21-min.tar.bz2" compression="bzip2" dest="." />
|
||||
<ant dir="jetty-4.2.21-min/extra/jdk1.2/" target="all" />
|
||||
<target name="prepare">
|
||||
<ant dir="../../jetty/" target="build" />
|
||||
</target>
|
||||
<target name="compile">
|
||||
<target name="compile" depends="prepare">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./src"
|
||||
debug="true" deprecation="on" source="1.3" target="1.3"
|
||||
destdir="./build/obj"
|
||||
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:jetty-4.2.21-min/extra/lib/org.mortbay.jetty-jdk1.2.jar" />
|
||||
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:../../jetty/jettylib/org.mortbay.jetty-jdk1.2.jar" />
|
||||
</target>
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class">
|
||||
@ -27,11 +26,53 @@
|
||||
</jar>
|
||||
<ant target="war" />
|
||||
</target>
|
||||
<target name="war">
|
||||
<war destfile="build/routerconsole.war" webxml="../jsp/web.xml"
|
||||
basedir="../jsp/" excludes="web.xml">
|
||||
<target name="war" depends="precompilejsp">
|
||||
<war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, *.java, *.jsp, web-fragment.xml">
|
||||
</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 -->
|
||||
<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/ant.jar" />
|
||||
<pathelement location="build/routerconsole.jar" />
|
||||
</classpath>
|
||||
<arg value="-d" />
|
||||
<arg value="../jsp/WEB-INF/classes" />
|
||||
<arg value="-v9" />
|
||||
<arg value="-p" />
|
||||
<arg value="net.i2p.router.web.jsp" />
|
||||
<arg value="-webinc" />
|
||||
<arg value="../jsp/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="../jsp/" />
|
||||
</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="build/routerconsole.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
<delete>
|
||||
<fileset dir="../jsp/WEB-INF/" includes="**/*.java" />
|
||||
<fileset dir="../jsp/WEB-INF/" includes="**/*.jsp" />
|
||||
</delete>
|
||||
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
|
||||
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
|
||||
<replace file="../jsp/web-out.xml">
|
||||
<replacefilter token="<!-- precompiled servlets -->" value="${jspc.web.fragment}" />
|
||||
</replace>
|
||||
</target>
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/javadoc" />
|
||||
@ -52,6 +93,5 @@
|
||||
<target name="distclean" depends="clean">
|
||||
<!-- router will clean core -->
|
||||
<ant dir="../../../router/java/" target="distclean" />
|
||||
<delete dir="./jetty-4.2.21-min" />
|
||||
</target>
|
||||
</project>
|
||||
|
@ -0,0 +1,84 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the advanced config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigAdvancedHandler extends FormHandler {
|
||||
private boolean _forceRestart;
|
||||
private boolean _shouldSave;
|
||||
private String _config;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_shouldSave = false;
|
||||
_forceRestart = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
public void setRestart(String moo) { _forceRestart = true; }
|
||||
|
||||
public void setConfig(String val) {
|
||||
_config = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
HashSet unsetKeys = new HashSet(_context.router().getConfigMap().keySet());
|
||||
if (_config != null) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(_config.getBytes())));
|
||||
String line = null;
|
||||
try {
|
||||
while ( (line = reader.readLine()) != null) {
|
||||
int eq = line.indexOf('=');
|
||||
if (eq == -1) continue;
|
||||
if (eq >= line.length() - 1) continue;
|
||||
String key = line.substring(0, eq).trim();
|
||||
String val = line.substring(eq + 1).trim();
|
||||
_context.router().setConfigSetting(key, val);
|
||||
unsetKeys.remove(key);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
addFormError("Error updating the configuration (IOERROR) - please see the error logs");
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator cleaner = unsetKeys.iterator();
|
||||
while (cleaner.hasNext()) {
|
||||
String unsetKey = (String)cleaner.next();
|
||||
_context.router().removeConfigSetting(unsetKey);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if (_forceRestart) {
|
||||
addFormNotice("Performing a soft restart");
|
||||
_context.router().restart();
|
||||
addFormNotice("Soft restart complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (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");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Properties;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the logging config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigLoggingHandler extends FormHandler {
|
||||
private boolean _shouldSave;
|
||||
private String _levels;
|
||||
private String _defaultLevel;
|
||||
private String _filename;
|
||||
private String _recordFormat;
|
||||
private String _dateFormat;
|
||||
private String _fileSize;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_shouldSave = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
|
||||
public void setLevels(String levels) {
|
||||
_levels = (levels != null ? levels.trim() : null);
|
||||
}
|
||||
public void setDefaultloglevel(String level) {
|
||||
_defaultLevel = (level != null ? level.trim() : null);
|
||||
}
|
||||
public void setLogfilename(String filename) {
|
||||
_filename = (filename != null ? filename.trim() : null);
|
||||
}
|
||||
public void setLogformat(String format) {
|
||||
_recordFormat = (format != null ? format.trim() : null);
|
||||
}
|
||||
public void setLogdateformat(String format) {
|
||||
_dateFormat = (format != null ? format.trim() : null);
|
||||
}
|
||||
public void setLogfilesize(String size) {
|
||||
_fileSize = (size != null ? size.trim() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
boolean shouldSave = false;
|
||||
|
||||
if (_levels != null) {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.load(new ByteArrayInputStream(_levels.getBytes()));
|
||||
_context.logManager().setLimits(props);
|
||||
shouldSave = true;
|
||||
addFormNotice("Log limits updated");
|
||||
} catch (IOException ioe) {
|
||||
_context.logManager().getLog(ConfigLoggingHandler.class).error("Error reading from the props?", ioe);
|
||||
addFormError("Error updating the log limits - levels not valid");
|
||||
}
|
||||
} else {
|
||||
_context.logManager().setLimits(null);
|
||||
addFormNotice("Log limits cleared");
|
||||
}
|
||||
|
||||
if (_defaultLevel != null) {
|
||||
String oldDefault = _context.logManager().getDefaultLimit();
|
||||
if (_defaultLevel.equals(oldDefault)) {
|
||||
// noop
|
||||
} else {
|
||||
shouldSave = true;
|
||||
_context.logManager().setDefaultLimit(_defaultLevel);
|
||||
addFormNotice("Default log level updated from " + oldDefault + " to " + _defaultLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (_dateFormat != null) {
|
||||
boolean valid = _context.logManager().setDateFormat(_dateFormat);
|
||||
if (valid) {
|
||||
shouldSave = true;
|
||||
addFormNotice("Date format updated");
|
||||
} else {
|
||||
addFormError("Specified date format is not valid (" + _dateFormat + ") - not updated");
|
||||
}
|
||||
}
|
||||
|
||||
if (_fileSize != null) {
|
||||
int newBytes = _context.logManager().getFileSize(_fileSize);
|
||||
int oldBytes = _context.logManager().getFileSize();
|
||||
if (newBytes > 0) {
|
||||
if (oldBytes != newBytes) {
|
||||
_context.logManager().setFileSize(newBytes);
|
||||
shouldSave = true;
|
||||
addFormNotice("File size updated");
|
||||
}
|
||||
} else {
|
||||
addFormError("Specified file size limit is not valid (" + _fileSize + ") - not updated");
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_filename != null) && (_filename.trim().length() > 0) ) {
|
||||
_filename = _filename.trim();
|
||||
String old = _context.logManager().getBaseLogfilename();
|
||||
if ( (old != null) && (_filename.equals(old)) ) {
|
||||
// noop - don't update since its the same
|
||||
} else {
|
||||
shouldSave = true;
|
||||
_context.logManager().setBaseLogfilename(_filename);
|
||||
addFormNotice("Log file name pattern updated to " + _filename
|
||||
+ " (note: will not take effect until next rotation)");
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_recordFormat != null) && (_recordFormat.trim().length() > 0) ) {
|
||||
_recordFormat = _recordFormat.trim();
|
||||
String old = new String(_context.logManager().getFormat());
|
||||
if (_recordFormat.equalsIgnoreCase(old)) {
|
||||
// noop - no change
|
||||
} else {
|
||||
char fmt[] = new char[_recordFormat.length()];
|
||||
for (int i = 0; i < fmt.length; i++)
|
||||
fmt[i] = _recordFormat.charAt(i);
|
||||
_context.logManager().setFormat(fmt);
|
||||
shouldSave = true;
|
||||
addFormNotice("Log record format updated");
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSave) {
|
||||
boolean saved = _context.logManager().saveConfig();
|
||||
|
||||
if (saved)
|
||||
addFormNotice("Log configuration saved and applied successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ 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 java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -49,65 +50,49 @@ public class ConfigLoggingHelper {
|
||||
}
|
||||
public String getLogLevelTable() {
|
||||
StringBuffer buf = new StringBuffer(32*1024);
|
||||
buf.append("<textarea rows=\"20\" cols=\"80\">");
|
||||
List logs = _context.logManager().getLogs();
|
||||
TreeMap sortedLogs = new TreeMap();
|
||||
for (int i = 0; i < logs.size(); i++) {
|
||||
Log l = (Log)logs.get(i);
|
||||
sortedLogs.put(l.getName(), l);
|
||||
Properties limits = _context.logManager().getLimits();
|
||||
TreeSet sortedLogs = new TreeSet();
|
||||
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
|
||||
String prefix = (String)iter.next();
|
||||
sortedLogs.add(prefix);
|
||||
}
|
||||
int i = 0;
|
||||
for (Iterator iter = sortedLogs.values().iterator(); iter.hasNext(); i++) {
|
||||
Log l = (Log)iter.next();
|
||||
buf.append(l.getName()).append('=');
|
||||
buf.append(Log.toLevelString(l.getMinimumPriority()));
|
||||
buf.append("\n");
|
||||
|
||||
buf.append("<textarea name=\"levels\" rows=\"20\" cols=\"70\">");
|
||||
for (Iterator iter = sortedLogs.iterator(); iter.hasNext(); ) {
|
||||
String prefix = (String)iter.next();
|
||||
String level = limits.getProperty(prefix);
|
||||
buf.append(prefix).append('=').append(level).append('\n');
|
||||
}
|
||||
buf.append("</textarea><br />\n");
|
||||
buf.append("<i>Valid levels are DEBUG, INFO, WARN, ERROR, CRIT</i>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
public String getLogLevelTableDetail() {
|
||||
StringBuffer buf = new StringBuffer(8*1024);
|
||||
buf.append("<table border=\"1\">\n");
|
||||
buf.append("<tr><td>Package/class</td><td>Level</td></tr>\n");
|
||||
List logs = _context.logManager().getLogs();
|
||||
TreeMap sortedLogs = new TreeMap();
|
||||
for (int i = 0; i < logs.size(); i++) {
|
||||
Log l = (Log)logs.get(i);
|
||||
sortedLogs.put(l.getName(), l);
|
||||
}
|
||||
int i = 0;
|
||||
for (Iterator iter = sortedLogs.values().iterator(); iter.hasNext(); i++) {
|
||||
Log l = (Log)iter.next();
|
||||
buf.append("<tr>\n <td><input size=\"50\" type=\"text\" name=\"logrecord.");
|
||||
buf.append(i).append(".package\" value=\"").append(l.getName());
|
||||
buf.append("\" /></td>\n");
|
||||
buf.append("<td><select name=\"logrecord.").append(i);
|
||||
buf.append(".level\">\n\t");
|
||||
buf.append("<option value=\"DEBUG\" ");
|
||||
if (l.getMinimumPriority() == Log.DEBUG)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Debug</option>\n\t");
|
||||
buf.append("<option value=\"INFO\" ");
|
||||
if (l.getMinimumPriority() == Log.INFO)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Info</option>\n\t");
|
||||
buf.append("<option value=\"WARN\" ");
|
||||
if (l.getMinimumPriority() == Log.WARN)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Warn</option>\n\t");
|
||||
buf.append("<option value=\"ERROR\" ");
|
||||
if (l.getMinimumPriority() == Log.ERROR)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Error</option>\n\t");
|
||||
buf.append("<option value=\"CRIT\" ");
|
||||
if (l.getMinimumPriority() == Log.CRIT)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Critical</option>\n\t");
|
||||
buf.append("</select></td>\n</tr>\n");
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
public String getDefaultLogLevelBox() {
|
||||
String cur = _context.logManager().getDefaultLimit();
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("<select name=\"defaultloglevel\">\n");
|
||||
|
||||
buf.append("<option value=\"DEBUG\" ");
|
||||
if ("DEBUG".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">DEBUG</option>\n");
|
||||
|
||||
buf.append("<option value=\"INFO\" ");
|
||||
if ("INFO".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">INFO</option>\n");
|
||||
|
||||
buf.append("<option value=\"WARN\" ");
|
||||
if ("WARN".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">WARN</option>\n");
|
||||
|
||||
buf.append("<option value=\"ERROR\" ");
|
||||
if ("ERROR".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">ERROR</option>\n");
|
||||
|
||||
buf.append("<option value=\"CRIT\" ");
|
||||
if ("CRIT".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">CRIT</option>\n");
|
||||
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,293 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.time.Timestamper;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the main config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigNetHandler extends FormHandler {
|
||||
private String _hostname;
|
||||
private boolean _guessRequested;
|
||||
private boolean _reseedRequested;
|
||||
private boolean _saveRequested;
|
||||
private boolean _timeSyncEnabled;
|
||||
private String _port;
|
||||
private String _inboundRate;
|
||||
private String _inboundBurst;
|
||||
private String _outboundRate;
|
||||
private String _outboundBurst;
|
||||
private String _reseedFrom;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_guessRequested = false;
|
||||
_reseedRequested = false;
|
||||
_saveRequested = false;
|
||||
_timeSyncEnabled = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_guessRequested) {
|
||||
guessHostname();
|
||||
} else if (_reseedRequested) {
|
||||
reseed();
|
||||
} else if (_saveRequested) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setGuesshost(String moo) { _guessRequested = true; }
|
||||
public void setReseed(String moo) { _reseedRequested = true; }
|
||||
public void setSave(String moo) { _saveRequested = true; }
|
||||
public void setEnabletimesync(String moo) { _timeSyncEnabled = true; }
|
||||
|
||||
public void setHostname(String hostname) {
|
||||
_hostname = (hostname != null ? hostname.trim() : null);
|
||||
}
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
}
|
||||
public void setInboundrate(String rate) {
|
||||
_inboundRate = (rate != null ? rate.trim() : null);
|
||||
}
|
||||
public void setInboundburstfactor(String factor) {
|
||||
_inboundBurst = (factor != null ? factor.trim() : null);
|
||||
}
|
||||
public void setOutboundrate(String rate) {
|
||||
_outboundRate = (rate != null ? rate.trim() : null);
|
||||
}
|
||||
public void setOutboundburstfactor(String factor) {
|
||||
_outboundBurst = (factor != null ? factor.trim() : null);
|
||||
}
|
||||
public void setReseedfrom(String url) {
|
||||
_reseedFrom = (url != null ? url.trim() : null);
|
||||
}
|
||||
|
||||
private static final String IP_PREFIX = "<h1>Your IP is ";
|
||||
private static final String IP_SUFFIX = " <br></h1>";
|
||||
private void guessHostname() {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
URL url = new URL("http://www.whatismyip.com/");
|
||||
URLConnection con = url.openConnection();
|
||||
con.connect();
|
||||
reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
String line = null;
|
||||
while ( (line = reader.readLine()) != null) {
|
||||
if (line.startsWith(IP_PREFIX)) {
|
||||
int end = line.indexOf(IP_SUFFIX);
|
||||
if (end == -1) {
|
||||
addFormError("Unable to guess the host (BAD_SUFFIX)");
|
||||
return;
|
||||
}
|
||||
String ip = line.substring(IP_PREFIX.length(), end);
|
||||
addFormNotice("Host guess: " + ip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
addFormError("Unable to guess the host (NO_PREFIX)");
|
||||
} catch (IOException ioe) {
|
||||
addFormError("Unable to guess the host (IO_ERROR)");
|
||||
_context.logManager().getLog(ConfigNetHandler.class).error("Unable to guess the host", ioe);
|
||||
} finally {
|
||||
if (reader != null) try { reader.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
|
||||
/**
|
||||
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||
* the routerInfo-*.dat files from the specified URL (or the default) and
|
||||
* save them into this router's netDb dir.
|
||||
*
|
||||
*/
|
||||
private void reseed() {
|
||||
String seedURL = DEFAULT_SEED_URL;
|
||||
if (_reseedFrom != null)
|
||||
seedURL = _reseedFrom;
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
String content = new String(readURL(dir));
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
int start = content.indexOf("href=\"routerInfo-", cur);
|
||||
if (start < 0)
|
||||
break;
|
||||
|
||||
int end = content.indexOf(".dat\">", start);
|
||||
String name = content.substring(start+"href=\"routerInfo-".length(), end);
|
||||
urls.add(name);
|
||||
cur = end + 1;
|
||||
}
|
||||
|
||||
int fetched = 0;
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
fetchSeed(seedURL, (String)iter.next());
|
||||
fetched++;
|
||||
}
|
||||
addFormNotice("Reseeded with " + fetched + " peers");
|
||||
} catch (Throwable t) {
|
||||
_context.logManager().getLog(ConfigNetHandler.class).error("Error reseeding", t);
|
||||
addFormError("Error reseeding (RESEED_EXCEPTION)");
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchSeed(String seedURL, String peer) throws Exception {
|
||||
URL url = new URL(seedURL + "/routerInfo-" + peer + ".dat");
|
||||
|
||||
byte data[] = readURL(url);
|
||||
writeSeed(peer, data);
|
||||
}
|
||||
|
||||
private byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
URLConnection con = url.openConnection();
|
||||
InputStream in = con.getInputStream();
|
||||
byte buf[] = new byte[1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
if (read < 0)
|
||||
break;
|
||||
baos.write(buf, 0, read);
|
||||
}
|
||||
in.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private void writeSeed(String name, byte data[]) throws Exception {
|
||||
// props taken from KademliaNetworkDatabaseFacade...
|
||||
String dirName = _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new File(dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
if (ok)
|
||||
addFormNotice("Network database directory created: " + dirName);
|
||||
else
|
||||
addFormNotice("Error creating network database directory: " + dirName);
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the network config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
boolean restartRequired = false;
|
||||
|
||||
if ( (_hostname != null) && (_hostname.length() > 0) ) {
|
||||
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME);
|
||||
if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_hostname)) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME, _hostname);
|
||||
addFormNotice("Updating hostname from " + oldHost + " to " + _hostname);
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
if ( (_port != null) && (_port.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
|
||||
if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
|
||||
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateRates();
|
||||
|
||||
if (_timeSyncEnabled) {
|
||||
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
|
||||
} else {
|
||||
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if (restartRequired) {
|
||||
addFormNotice("Performing a soft restart");
|
||||
_context.router().restart();
|
||||
addFormNotice("Soft restart complete");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRates() {
|
||||
boolean updated = false;
|
||||
if ( (_inboundRate != null) && (_inboundRate.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS, _inboundRate);
|
||||
updated = true;
|
||||
}
|
||||
if ( (_outboundRate != null) && (_outboundRate.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS, _outboundRate);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
String inRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS);
|
||||
|
||||
if (_inboundBurst != null) {
|
||||
int rateKBps = 0;
|
||||
int burstSeconds = 0;
|
||||
try {
|
||||
rateKBps = Integer.parseInt(inRate);
|
||||
burstSeconds = Integer.parseInt(_inboundBurst);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore
|
||||
}
|
||||
if ( (rateKBps > 0) && (burstSeconds > 0) ) {
|
||||
int kb = rateKBps * burstSeconds;
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST, "" + kb);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
String outRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS);
|
||||
|
||||
if (_outboundBurst != null) {
|
||||
int rateKBps = 0;
|
||||
int burstSeconds = 0;
|
||||
try {
|
||||
rateKBps = Integer.parseInt(outRate);
|
||||
burstSeconds = Integer.parseInt(_outboundBurst);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore
|
||||
}
|
||||
if ( (rateKBps > 0) && (burstSeconds > 0) ) {
|
||||
int kb = rateKBps * burstSeconds;
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST, "" + kb);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated)
|
||||
addFormNotice("Updated bandwidth limits");
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.time.Timestamper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
@ -30,8 +31,8 @@ public class ConfigNetHelper {
|
||||
public ConfigNetHelper() {}
|
||||
|
||||
/** copied from various private TCP components */
|
||||
private final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
|
||||
private final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
|
||||
public final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
|
||||
public final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
|
||||
|
||||
public String getHostname() {
|
||||
return _context.getProperty(PROP_I2NP_TCP_HOSTNAME);
|
||||
@ -50,8 +51,8 @@ public class ConfigNetHelper {
|
||||
}
|
||||
|
||||
public String getEnableTimeSyncChecked() {
|
||||
String enabled = System.getProperty("timestamper.enabled");
|
||||
if ( (enabled == null) || (!"true".equals(enabled)) )
|
||||
String enabled = _context.getProperty(Timestamper.PROP_DISABLED, "true");
|
||||
if ( (enabled == null) || (!"true".equalsIgnoreCase(enabled)) )
|
||||
return "";
|
||||
else
|
||||
return " checked ";
|
||||
@ -74,7 +75,7 @@ public class ConfigNetHelper {
|
||||
if (rate != null)
|
||||
return rate;
|
||||
else
|
||||
return "Unlimited";
|
||||
return "-1";
|
||||
}
|
||||
public String getInboundBurstFactorBox() {
|
||||
String rate = _context.getProperty(PROP_INBOUND_KBPS);
|
||||
|
109
apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
Normal file
109
apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
Normal file
@ -0,0 +1,109 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
/**
|
||||
* Simple form handler base class - does not depend on servlets or jsp,
|
||||
* but instead the subclasses are populated with javabean properties. e.g.
|
||||
* <jsp:setProperty name="handler" property="*" />
|
||||
*
|
||||
* The form is "processed" after the properties are set and the first output
|
||||
* property is retrieved - either getNotices() or getErrors().
|
||||
*
|
||||
*/
|
||||
public class FormHandler {
|
||||
protected RouterContext _context;
|
||||
private List _errors;
|
||||
private List _notices;
|
||||
private boolean _processed;
|
||||
|
||||
public FormHandler() {
|
||||
_errors = new ArrayList();
|
||||
_notices = new ArrayList();
|
||||
_processed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to perform the final processing (in turn, adding formNotice
|
||||
* and formError messages, etc)
|
||||
*
|
||||
*/
|
||||
protected void processForm() {}
|
||||
|
||||
/**
|
||||
* Add an error message to display
|
||||
*/
|
||||
protected void addFormError(String errorMsg) {
|
||||
if (errorMsg == null) return;
|
||||
_errors.add(errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a non-error message to display
|
||||
*/
|
||||
protected void addFormNotice(String msg) {
|
||||
if (msg == null) return;
|
||||
_notices.add(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display any error messages (processing the form if it hasn't
|
||||
* been yet)
|
||||
*
|
||||
*/
|
||||
public String getErrors() {
|
||||
return render(_errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display any non-error messages (processing the form if it hasn't
|
||||
* been yet)
|
||||
*
|
||||
*/
|
||||
public String getNotices() {
|
||||
return render(_notices);
|
||||
}
|
||||
|
||||
private String render(List source) {
|
||||
if (!_processed) {
|
||||
processForm();
|
||||
_processed = true;
|
||||
}
|
||||
if (source.size() <= 0) {
|
||||
return "";
|
||||
} else if (source.size() == 1) {
|
||||
return (String)source.get(0);
|
||||
} else {
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>\n");
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
buf.append("<li>");
|
||||
buf.append((String)source.get(i));
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -29,8 +29,8 @@ public class LogsHelper {
|
||||
StringBuffer buf = new StringBuffer(16*1024);
|
||||
buf.append("<h2>Most recent console messages:</h2><ul>");
|
||||
buf.append("<code>\n");
|
||||
for (int i = 0; i < msgs.size(); i++) {
|
||||
String msg = (String)msgs.get(i);
|
||||
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");
|
||||
|
@ -0,0 +1,48 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.admin.StatsGenerator;
|
||||
|
||||
public class OldConsoleHelper {
|
||||
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 OldConsoleHelper() {}
|
||||
|
||||
public String getConsole() {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(128*1024);
|
||||
_context.router().renderStatusHTML(baos);
|
||||
return baos.toString();
|
||||
} catch (IOException ioe) {
|
||||
return "<b>Error rending the console</b>";
|
||||
}
|
||||
}
|
||||
|
||||
public String getStats() {
|
||||
StatsGenerator gen = new StatsGenerator(_context);
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
|
||||
gen.generateStatsPage(baos);
|
||||
return baos.toString();
|
||||
} catch (IOException ioe) {
|
||||
return "<b>Error rending the console</b>";
|
||||
}
|
||||
}
|
||||
}
|
@ -32,4 +32,14 @@ public class ProfilesHelper {
|
||||
}
|
||||
return new String(baos.toByteArray());
|
||||
}
|
||||
|
||||
public String getShitlistSummary() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
|
||||
try {
|
||||
_context.shitlist().renderStatusHTML(baos);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
return new String(baos.toByteArray());
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,22 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.mortbay.jetty.servlet.WebApplicationContext;
|
||||
import org.mortbay.http.handler.SecurityHandler;
|
||||
import org.mortbay.http.HashUserRealm;
|
||||
import org.mortbay.http.HttpRequest;
|
||||
import org.mortbay.http.SecurityConstraint;
|
||||
import org.mortbay.util.MultiException;
|
||||
|
||||
public class RouterConsoleRunner {
|
||||
private Server _server;
|
||||
private String _listenPort = "7657";
|
||||
private String _listenHost = "0.0.0.0";
|
||||
private String _listenHost = "127.0.0.1";
|
||||
private String _webAppsDir = "./webapps/";
|
||||
|
||||
public RouterConsoleRunner(String args[]) {
|
||||
@ -25,10 +34,15 @@ public class RouterConsoleRunner {
|
||||
|
||||
public void startConsole() {
|
||||
_server = new Server();
|
||||
WebApplicationContext contexts[] = null;
|
||||
try {
|
||||
_server.addListener(_listenHost + ':' + _listenPort);
|
||||
_server.setRootWebApp("routerconsole");
|
||||
_server.addWebApplications(_webAppsDir);
|
||||
contexts = _server.addWebApplications(_webAppsDir);
|
||||
if (contexts != null) {
|
||||
for (int i = 0; i < contexts.length; i++)
|
||||
initialize(contexts[i]);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
@ -39,6 +53,41 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize(WebApplicationContext context) {
|
||||
String password = getPassword();
|
||||
if (password != null) {
|
||||
HashUserRealm realm = new HashUserRealm();
|
||||
realm.put("admin", password);
|
||||
realm.addUserToRole("admin", "routerAdmin");
|
||||
context.setRealm(realm);
|
||||
context.addHandler(0, new SecurityHandler());
|
||||
SecurityConstraint constraint = new SecurityConstraint("admin", "routerAdmin");
|
||||
constraint.setAuthenticate(true);
|
||||
context.addSecurityConstraint("/", constraint);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPassword() {
|
||||
List contexts = RouterContext.listContexts();
|
||||
if (contexts != null) {
|
||||
for (int i = 0; i < contexts.size(); i++) {
|
||||
RouterContext ctx = (RouterContext)contexts.get(i);
|
||||
String password = ctx.getProperty("consolePassword");
|
||||
if (password != null) {
|
||||
password = password.trim();
|
||||
if (password.length() > 0) {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
}
|
||||
// no password in any context
|
||||
return null;
|
||||
} else {
|
||||
// no contexts?!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void stopConsole() {
|
||||
try {
|
||||
_server.stop();
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -62,6 +64,18 @@ public class SummaryHelper {
|
||||
else
|
||||
return DataHelper.formatDuration(router.getUptime());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve amount of used memory.
|
||||
*
|
||||
*/
|
||||
public String getMemory() {
|
||||
DecimalFormat integerFormatter = new DecimalFormat("###,###,##0");
|
||||
long used = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024;
|
||||
long usedPc = 100 - ((Runtime.getRuntime().freeMemory() * 100) / Runtime.getRuntime().totalMemory());
|
||||
return integerFormatter.format(used) + "KB (" + usedPc + "%)";
|
||||
}
|
||||
|
||||
/**
|
||||
* How many active peers the router has.
|
||||
@ -301,11 +315,25 @@ public class SummaryHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How many client destinations are connected locally.
|
||||
*
|
||||
* @return html section summary
|
||||
*/
|
||||
public String getDestinations() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
try {
|
||||
_context.clientManager().renderStatusHTML(baos);
|
||||
return new String(baos.toByteArray());
|
||||
} catch (IOException ioe) {
|
||||
_context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How many free inbound tunnels we have.
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public int getInboundTunnels() {
|
||||
if (_context == null)
|
||||
|
@ -1,5 +1,4 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
@ -9,13 +8,19 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigNetHelper" id="nethelper" scope="request" />
|
||||
<jsp:setProperty name="nethelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigNetHandler" 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="config.jsp" method="POST">
|
||||
<b>External hostname/IP address:</b>
|
||||
<input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" />
|
||||
@ -29,7 +34,8 @@
|
||||
to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
|
||||
<hr />
|
||||
<b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized</i>
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
|
||||
be within a few seconds of "correct".</i>
|
||||
<hr />
|
||||
<b>Bandwidth limiter</b><br />
|
||||
<b>Inbound rate</b>:
|
||||
@ -44,8 +50,12 @@
|
||||
<hr />
|
||||
<b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />):
|
||||
<input type="submit" name="reseed" value="now" /><br />
|
||||
<i>May take some time to download the peer references</i>
|
||||
<hr />
|
||||
<input type="submit" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
<i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections
|
||||
and clients as if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
a few seconds to complete.</i>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -9,16 +9,25 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHelper" id="advancedhelper" scope="request" />
|
||||
<jsp:setProperty name="advancedhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHandler" 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="configadvanced.jsp" method="POST">
|
||||
<textarea rows="20" cols="80" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
|
||||
<input type="submit" value="Apply" /> <input type="reset" value="Cancel" />
|
||||
<textarea rows="20" cols="100" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
|
||||
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /> <br />
|
||||
<b>Force restart:</b> <input type="checkbox" name="restart" value="force" /> <i>(specify this
|
||||
if the changes made above require the router to reset itself - e.g. you are updating TCP ports
|
||||
or hostnames, etc)</i>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -9,13 +9,19 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.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">
|
||||
<b>Estimated number of clients/destinations:</b>
|
||||
<jsp:getProperty name="clientshelper" property="clientCountSelectBox" /><br />
|
||||
@ -24,7 +30,7 @@
|
||||
<b>Default number of hops per tunnel:</b>
|
||||
<jsp:getProperty name="clientshelper" property="tunnelDepthSelectBox" /><br />
|
||||
<hr />
|
||||
<input type="submit" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="shouldsave" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -11,10 +11,16 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigLoggingHandler" 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="configlogging.jsp" method="POST">
|
||||
<b>Logging filename:</b>
|
||||
<input type="text" name="logfilename" size="40" value="<jsp:getProperty name="logginghelper" property="logFilePattern" />" /><br />
|
||||
@ -29,9 +35,12 @@
|
||||
<input type="text" name="logfilesize" size="4" value="<jsp:getProperty name="logginghelper" property="maxFileSize" />" /><br />
|
||||
<hr />
|
||||
<b>Log levels:</b> <br />
|
||||
<b>Default log level:</b>
|
||||
<jsp:getProperty name="logginghelper" property="defaultLogLevelBox" /><br />
|
||||
<jsp:getProperty name="logginghelper" property="logLevelTable" />
|
||||
<hr />
|
||||
<input type="submit" value="Apply changes" /> <input type="submit" value="Apply and Save" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="shouldsave" value="Save changes" />
|
||||
<input type="reset" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
hmm. we should probably have some help text here.<br />
|
||||
|
2
apps/routerconsole/jsp/index.html
Normal file
2
apps/routerconsole/jsp/index.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>I2P Router Console</title></head>
|
||||
<body><meta http-equiv="refresh" content="0;url=index.jsp" /><a href="index.jsp">Enter</a></body></html>
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<h2>Welcome to your router console</h2>
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.LogsHelper" id="logsHelper" scope="request" />
|
||||
|
@ -8,11 +8,14 @@
|
||||
[<a href="config.jsp">configuration</a> | <a href="help.jsp">help</a>]
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
<h4>
|
||||
<a href="profiles.jsp">Profiles</a> |
|
||||
<a href="netdb.jsp">Network Database</a> |
|
||||
<a href="logs.jsp">Logs</a>
|
||||
<a href="logs.jsp">Logs</a> |
|
||||
<a href="oldconsole.jsp">Old console</a> |
|
||||
<a href="oldstats.jsp">Stats</a> |
|
||||
<a href="i2ptunnel/" target="_blank">I2PTunnel</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" />
|
||||
</h3>
|
||||
</h4>
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
|
||||
|
@ -1 +0,0 @@
|
||||
<%=(null != request.getParameter("i2p.console.notice") ? request.getParameter("i2p.console.notice") : "")%>
|
21
apps/routerconsole/jsp/oldconsole.jsp
Normal file
21
apps/routerconsole/jsp/oldconsole.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 - home</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.OldConsoleHelper" id="conhelper" scope="request" />
|
||||
<jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:getProperty name="conhelper" property="console" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
21
apps/routerconsole/jsp/oldstats.jsp
Normal file
21
apps/routerconsole/jsp/oldstats.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 - home</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.OldConsoleHelper" id="oldhelper" scope="request" />
|
||||
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:getProperty name="oldhelper" property="stats" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -9,12 +9,14 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" />
|
||||
<jsp:setProperty name="profilesHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:getProperty name="profilesHelper" property="profileSummary" />
|
||||
<hr />
|
||||
<a name="shitlist"> </a>
|
||||
<jsp:getProperty name="profilesHelper" property="shitlistSummary" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
@ -7,6 +7,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>Memory:</b> <jsp:getProperty name="helper" property="memory" /><br />
|
||||
<hr />
|
||||
|
||||
<u><b>Peers</b></u><br />
|
||||
@ -25,6 +26,8 @@
|
||||
<b>Used:</b> <jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /><br />
|
||||
<hr />
|
||||
|
||||
<jsp:getProperty name="helper" property="destinations" />
|
||||
|
||||
<u><b>Tunnels</b></u><br />
|
||||
<b>Inbound:</b> <jsp:getProperty name="helper" property="inboundTunnels" /><br />
|
||||
<b>Outbound:</b> <jsp:getProperty name="helper" property="outboundTunnels" /><br />
|
||||
|
@ -4,14 +4,14 @@
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<!-- precompiled servlets -->
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>
|
||||
index.jsp
|
||||
</welcome-file>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
25
apps/sam/c/Makefile.common
Normal file
25
apps/sam/c/Makefile.common
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# This Makefile contains instructions common to all platforms
|
||||
#
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: clean depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-$(RM) -f $(LIBDIR)/libsam.a $(OBJDIR)/*.o .depend
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
#
|
||||
# Your operating environment
|
||||
# Your operating system
|
||||
#
|
||||
|
||||
OS = CYGWIN
|
||||
@ -23,12 +23,13 @@ SRCDIR = src
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
@ -41,25 +42,7 @@ OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make (gmake) and should work on FreeBSD
|
||||
# This Makefile is compatible with GNU Make and should work on FreeBSD
|
||||
#
|
||||
|
||||
#
|
||||
@ -23,6 +23,7 @@ SRCDIR = src
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
@ -39,25 +40,7 @@ CFLAGS += -I$(INCDIR)
|
||||
OBJS = $(OBJDIR)/sam.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on Linux (generic)
|
||||
# This Makefile is compatible with GNU Make and should work on Linux
|
||||
#
|
||||
|
||||
#
|
||||
@ -23,6 +23,7 @@ SRCDIR = src
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
@ -40,25 +41,7 @@ OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
@ -23,12 +23,13 @@ SRCDIR = src
|
||||
|
||||
AR = C:\Dev-Cpp\bin\ar
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
@ -40,25 +41,7 @@ OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
1
apps/sam/c/README
Normal file
1
apps/sam/c/README
Normal file
@ -0,0 +1 @@
|
||||
See the `docs' directory for documentation and license.
|
3
apps/sam/c/doc/contact.txt
Normal file
3
apps/sam/c/doc/contact.txt
Normal file
@ -0,0 +1,3 @@
|
||||
The author is Matthew P. Cashdollar, who can be reached via email at
|
||||
mpc@innographx.com. The official LibSAM distribution site is at
|
||||
http://www.cashdollar.org/libsam
|
@ -5,5 +5,4 @@ I need to do these things:
|
||||
|
||||
Anyone can help with these things:
|
||||
|
||||
* Fix the example dgram-client.c
|
||||
* Compile on as many platforms as possible
|
||||
|
@ -1,6 +1,14 @@
|
||||
v1.25
|
||||
/* vi:set ts=4: */
|
||||
|
||||
v1.25 2004-07-31
|
||||
* Created I2P-Ping, a new example program (it's a clone of I2Ping). Works
|
||||
on Posix only, because it uses getopt().
|
||||
* Removed the old broken examples and added more comments to
|
||||
warhammer-dgram.c
|
||||
* Added support for sessions - now LibSAM can have multiple SAM sessions
|
||||
going at once (with different destinations)
|
||||
* Rewrote sendq functions to automatically send big packets, for better
|
||||
network performance
|
||||
network performance (still considered experimental)
|
||||
|
||||
v1.20 2004-07-11
|
||||
* Ported to FreeBSD (Makefile.freebsd)
|
||||
|
@ -1,42 +0,0 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make
|
||||
#
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = gcc
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -I../inc -L../lib
|
||||
LIBS = -lsam
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: dgram-client dgram-server warhammer-dgram
|
||||
|
||||
dgram-client:
|
||||
$(CC) $(CFLAGS) -o dgram-client.o -c dgram-client.c
|
||||
$(CC) $(CFLAGS) -o dgram-client dgram-client.o $(LIBS)
|
||||
|
||||
dgram-server:
|
||||
$(CC) $(CFLAGS) -o dgram-server.o -c dgram-server.c
|
||||
$(CC) $(CFLAGS) -o dgram-server dgram-server.o $(LIBS)
|
||||
|
||||
warhammer-dgram:
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f *.o *.exe dgram-client dgram-server warhammer-dgram
|
20
apps/sam/c/examples/Makefile.common
Normal file
20
apps/sam/c/examples/Makefile.common
Normal file
@ -0,0 +1,20 @@
|
||||
#
|
||||
# This Makefile contains instructions common to all platforms
|
||||
#
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: clean warhammer-dgram
|
||||
|
||||
warhammer-dgram: warhammer-dgram.c
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-$(RM) -f warhammer-dgram *.exe *.o
|
@ -7,36 +7,19 @@
|
||||
#
|
||||
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DWINSOCK
|
||||
CFLAGS += -I../inc -L../lib
|
||||
LIBS = -lsam -lwsock32
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: dgram-client dgram-server warhammer-dgram
|
||||
|
||||
dgram-client:
|
||||
$(CC) $(CFLAGS) -o dgram-client.o -c dgram-client.c
|
||||
$(CC) $(CFLAGS) -o dgram-client dgram-client.o $(LIBS)
|
||||
|
||||
dgram-server:
|
||||
$(CC) $(CFLAGS) -o dgram-server.o -c dgram-server.c
|
||||
$(CC) $(CFLAGS) -o dgram-server dgram-server.o $(LIBS)
|
||||
|
||||
warhammer-dgram:
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f *.o *.exe dgram-client dgram-server warhammer-dgram
|
||||
include Makefile.common
|
||||
|
24
apps/sam/c/examples/Makefile.posix
Normal file
24
apps/sam/c/examples/Makefile.posix
Normal file
@ -0,0 +1,24 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on POSIX systems
|
||||
#
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -I../inc -L../lib
|
||||
LIBS = -lsam
|
||||
|
||||
#
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
include Makefile.common
|
@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
|
||||
static void diedback(void);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/*
|
||||
* This is an extremely simple echo client which shows you how LibSAM
|
||||
* datagrams work. We lookup the name 'dgram-server' then send some data to
|
||||
* him. If everything works, we should get the same data back.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE!!!!!!!! This is currently broken!
|
||||
*/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
samerr_t rc;
|
||||
|
||||
/* Hook up the callback functions */
|
||||
sam_dgramback = &dgramback;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
|
||||
/* Connect to the SAM server -- you can use either an IP or DNS name */
|
||||
rc = sam_connect("localhost", 7656, "dgram-client", SAM_DGRAM, 0);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
exit(1);
|
||||
}
|
||||
/*
|
||||
* This is equivalent to doing a DNS lookup on the normal internet. Note
|
||||
* that the dgram-server must already be running for this to work.
|
||||
* When the lookup completes, we send them some data (see namingback()).
|
||||
*/
|
||||
sam_naming_lookup("dgram-server");
|
||||
|
||||
/*
|
||||
* Wait for something to happen, then invoke the appropriate callback
|
||||
*/
|
||||
while (true)
|
||||
sam_read_buffer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we receive some data, print it out to the screen
|
||||
*/
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
|
||||
{
|
||||
printf("Datagram received: %s\n", (char *)data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(void)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
fprintf(stderr, "LibSAM: %s\n", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* When a name is resolved, send data to that destination address
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
{
|
||||
char *data = "Hello, invisible world!";
|
||||
|
||||
printf("I got %s's base 64 destination, so now I will send him some " \
|
||||
"data...\n", name);
|
||||
sam_dgram_send(pubkey, data, strlen(data));
|
||||
/*
|
||||
* ^^^ An extra NUL is appended to the data by LibSAM, so it is not
|
||||
* necessary to send trailing NULs over the wire for strings. This doesn't
|
||||
* cause problems with binary data, because the NUL isn't included in `size'
|
||||
* in sam_dgramback().
|
||||
* That is why we use strlen(data) instead of strlen(data) + 1.
|
||||
*/
|
||||
puts("Datagram sent");
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
|
||||
static void diedback(void);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/*
|
||||
* This is an extremely simple echo server which shows you how LibSAM
|
||||
* datagrams work. We echo back every datagram that is sent to us.
|
||||
*/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
samerr_t rc;
|
||||
|
||||
/* Hook up the callback functions */
|
||||
sam_dgramback = &dgramback;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
|
||||
/* Connect to the SAM server -- you can use either an IP or DNS name */
|
||||
rc = sam_connect("127.0.0.1", 7656, "dgram-server", SAM_DGRAM, 0);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we just keep polling the buffer, which causes the
|
||||
* appropriate callbacks to be called whenever something happens
|
||||
*/
|
||||
while (true)
|
||||
sam_read_buffer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we receive some data, we just ECHO the exact same data back to them
|
||||
*/
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
|
||||
{
|
||||
puts("Echoing datagram");
|
||||
sam_dgram_send(dest, data, size);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(void)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
fprintf(stderr, "LibSAM: %s\n", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Not used, but the function has to be in the program anyway
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
{
|
||||
assert(false); /* we don't do any naming lookups in this program */
|
||||
}
|
39
apps/sam/c/examples/i2p-ping/Makefile
Normal file
39
apps/sam/c/examples/i2p-ping/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on POSIX systems
|
||||
#
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = gcc
|
||||
INSTALL = install
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -I../../inc -L../../lib
|
||||
LIBS = -lsam
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: clean i2p-ping
|
||||
|
||||
i2p-ping: i2p-ping.c
|
||||
$(CC) $(CFLAGS) -o i2p-ping.o -c i2p-ping.c
|
||||
$(CC) $(CFLAGS) -o i2p-ping i2p-ping.o $(LIBS)
|
||||
|
||||
install: i2p-ping
|
||||
$(INSTALL) i2p-ping $(HOME)/bin
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-$(RM) -f i2p-ping *.o
|
245
apps/sam/c/examples/i2p-ping/i2p-ping.c
Normal file
245
apps/sam/c/examples/i2p-ping/i2p-ping.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void usage();
|
||||
|
||||
static void closeback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
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 statusback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
bool gotdest = false;
|
||||
sam_pubkey_t dest;
|
||||
bool quiet = false;
|
||||
samerr_t laststatus = SAM_NULL;
|
||||
sam_sid_t laststream = 0;
|
||||
bool mihi = false;
|
||||
bool bell = false;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int ch;
|
||||
int count = INT_MAX; /* number of times to ping */
|
||||
char *samhost = "localhost";
|
||||
uint16_t samport = 7656;
|
||||
|
||||
while ((ch = getopt(argc, argv, "ac:h:mp:qv")) != -1) {
|
||||
switch (ch) {
|
||||
case 'a': /* bell */
|
||||
bell = true;
|
||||
break;
|
||||
case 'c': /* packet count */
|
||||
count = atoi(optarg);
|
||||
break;
|
||||
case 'h': /* SAM host */
|
||||
samhost = optarg;
|
||||
break;
|
||||
case 'm': /* I2Ping emulation mode */
|
||||
count = 3;
|
||||
mihi = true;
|
||||
quiet = true;
|
||||
break;
|
||||
case 'p': /* SAM port */
|
||||
samport = atoi(optarg);
|
||||
break;
|
||||
case 'q': /* quiet mode */
|
||||
quiet = true;
|
||||
break;
|
||||
case 'v': /* version */
|
||||
puts("$Id: i2p-ping.c,v 1.2 2004/07/31 22:20:22 mpc Exp $");
|
||||
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc == 0) { /* they forgot to specify a ping target */
|
||||
fprintf(stderr, "Ping who?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Hook up the callback functions - required by LibSAM */
|
||||
sam_closeback = &closeback;
|
||||
sam_connectback = &connectback;
|
||||
sam_databack = &databack;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
sam_statusback = &statusback;
|
||||
|
||||
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
|
||||
session = sam_session_init(session); /* malloc and set defaults */
|
||||
samerr_t rc = sam_connect(session, samhost, samport, "TRANSIENT",
|
||||
SAM_STREAM, 0);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
sam_session_free(&session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < argc; j++) {
|
||||
if (strlen(argv[j]) == 516) {
|
||||
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else
|
||||
sam_naming_lookup(session, argv[j]);
|
||||
|
||||
while (!gotdest) /* just wait for the naming lookup to complete */
|
||||
sam_read_buffer(session);
|
||||
gotdest = false;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
time_t start = time(0);
|
||||
sam_sid_t sid = sam_stream_connect(session, dest);
|
||||
while (laststream != sid && laststatus == SAM_NULL)
|
||||
sam_read_buffer(session); /* wait for the connect */
|
||||
if (laststatus == SAM_OK)
|
||||
sam_stream_close(session, laststream);
|
||||
time_t finish = time(0);
|
||||
laststream = 0;
|
||||
if (laststatus == SAM_OK) {
|
||||
if (bell)
|
||||
printf("\a"); /* putchar() doesn't work for some reason */
|
||||
if (!mihi)
|
||||
printf("%s: %.0fs\n", argv[j], difftime(finish, start));
|
||||
else
|
||||
printf("+ ");
|
||||
} else {
|
||||
if (!mihi)
|
||||
printf("%s: %s\n", argv[j], sam_strerror(laststatus));
|
||||
else
|
||||
printf("- ");
|
||||
}
|
||||
laststatus = SAM_NULL;
|
||||
}
|
||||
if (mihi)
|
||||
printf(" %s\n", argv[j]);
|
||||
}
|
||||
|
||||
sam_close(session);
|
||||
sam_session_free(&session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
puts("usage: i2p-ping [-amqv?] [-c count] [-h samhost] [-p samport] " \
|
||||
"<b64dest|name>\n\t[b64dest|name] [b64dest|name] ...");
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection closed
|
||||
*/
|
||||
static void closeback(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
{
|
||||
fprintf(stderr, "Connection closed to stream %d: %s\n", stream_id,
|
||||
sam_strerror(reason));
|
||||
}
|
||||
|
||||
/*
|
||||
* Someone connected to us - how dare they!
|
||||
*/
|
||||
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest)
|
||||
{
|
||||
sam_stream_close(session, stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* A peer sent us some data - just ignore it
|
||||
*/
|
||||
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(sam_sess_t *session)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM (typically
|
||||
* errors)
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
if (!quiet)
|
||||
fprintf(stderr, "LibSAM: %s\n", 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)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
exit(1);
|
||||
}
|
||||
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Our connection attempt returned a result
|
||||
*/
|
||||
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result)
|
||||
{
|
||||
laststatus = result;
|
||||
laststream = stream_id;
|
||||
}
|
@ -34,88 +34,149 @@
|
||||
* Use only with the utmost courtesy.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
|
||||
static void diedback(void);
|
||||
/*
|
||||
* LibSAM callbacks - functions in our code that are called by LibSAM when
|
||||
* something happens
|
||||
*/
|
||||
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);
|
||||
|
||||
/*
|
||||
* Just some ugly global variables. Don't do this in your program.
|
||||
*/
|
||||
bool gotdest = false;
|
||||
sam_pubkey_t dest;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
/*
|
||||
* The target of our attack is specified on the command line
|
||||
*/
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Syntax: %s <b64dest|name>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Hook up the callback functions - required by LibSAM */
|
||||
sam_dgramback = &dgramback;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
|
||||
/* a tunnel length of 2 is the default - adjust to your preference vv */
|
||||
samerr_t rc = sam_connect("localhost", 7656, "TRANSIENT", SAM_DGRAM, 2);
|
||||
/*
|
||||
* This tool would be more destructive if multiple SAM session were used,
|
||||
* but they aren't - at least for now.
|
||||
*/
|
||||
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
|
||||
session = sam_session_init(session); /* malloc and set defaults */
|
||||
|
||||
/* Connect to the SAM server -- you can use either an IP or DNS name */
|
||||
samerr_t rc = sam_connect(session, "localhost", 7656, "TRANSIENT",
|
||||
SAM_DGRAM, 2); /* the tunnel length of 2 can be adjusted to whatever */
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
exit(1);
|
||||
sam_session_free(&session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether they've supplied a name 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) {
|
||||
memcpy(dest, argv[1], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else {
|
||||
/*
|
||||
* If they supplied a name, we have to do a lookup on it. This is
|
||||
* equivalent to doing a DNS lookup on the normal internet. When the
|
||||
* lookup completes, we send them some data.
|
||||
*/
|
||||
sam_naming_lookup(session, argv[1]);
|
||||
}
|
||||
else
|
||||
sam_naming_lookup(argv[1]);
|
||||
|
||||
while (!gotdest)
|
||||
sam_read_buffer();
|
||||
while (!gotdest) /* just wait for the naming lookup to complete */
|
||||
sam_read_buffer(session);
|
||||
|
||||
char data[SAM_DGRAM_PAYLOAD_MAX];
|
||||
memset(data, '#', SAM_DGRAM_PAYLOAD_MAX);
|
||||
memset(data, '$', SAM_DGRAM_PAYLOAD_MAX); /* We're sending them MONEY! */
|
||||
size_t sentbytes = 0;
|
||||
while (true) {
|
||||
rc = sam_dgram_send(dest, data, SAM_DGRAM_PAYLOAD_MAX);
|
||||
/*
|
||||
* Send them a flood of the largest sized datagrams possible in an
|
||||
* infinite loop!
|
||||
*/
|
||||
rc = sam_dgram_send(session, dest, data, SAM_DGRAM_PAYLOAD_MAX);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "sam_dgram_send() failed: %s\n", sam_strerror(rc));
|
||||
sam_session_free(&session);
|
||||
return 1;
|
||||
}
|
||||
sentbytes += SAM_DGRAM_PAYLOAD_MAX;
|
||||
printf("Bombs away! (%u kbytes sent so far)\n", sentbytes / 1024);
|
||||
sam_read_buffer();
|
||||
/*
|
||||
* sam_read_buffer() just checks for incoming activity from the SAM
|
||||
* session, and invokes the appropriate callbacks. We aren't really
|
||||
* expecting any incoming activity here, but it is a good idea to check
|
||||
* anyway.
|
||||
*/
|
||||
sam_read_buffer(session);
|
||||
}
|
||||
|
||||
sam_session_free(&session); /* de-allocates memory used by the SAM session*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
|
||||
/*
|
||||
* When we receive some data from another peer, just ignore it. Denial of
|
||||
* service programs don't need input ;)
|
||||
*/
|
||||
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size)
|
||||
{
|
||||
puts("Received a datagram (ignored)");
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void diedback(void)
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM (typically
|
||||
* errors)
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
fprintf(stderr, "LibSAM: %s\n", 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)
|
||||
{
|
||||
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);
|
||||
|
@ -112,11 +112,6 @@
|
||||
/*
|
||||
* Platform-dependent variable types
|
||||
*/
|
||||
#ifdef WINSOCK
|
||||
typedef SOCKET socket_t;
|
||||
#else
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
#ifdef NO_SSIZE_T
|
||||
typedef signed long ssize_t;
|
||||
#endif
|
||||
|
@ -42,8 +42,8 @@ extern "C" {
|
||||
*/
|
||||
/* The maximum length a SAM command can be */
|
||||
#define SAM_CMD_LEN 128
|
||||
/*The maximum size of a single datagram packet (-30 temporary bug fix for SAM)*/
|
||||
#define SAM_DGRAM_PAYLOAD_MAX ((31 * 1024) - 30)
|
||||
/* The maximum size of a single datagram packet */
|
||||
#define SAM_DGRAM_PAYLOAD_MAX (31 * 1024)
|
||||
/* The longest log message */
|
||||
#define SAM_LOGMSG_LEN 256
|
||||
/* The longest `name' arg for the naming lookup callback */
|
||||
@ -66,6 +66,12 @@ 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 */
|
||||
@ -77,12 +83,20 @@ typedef struct {
|
||||
|
||||
typedef int_fast32_t sam_sid_t; /* stream id number */
|
||||
|
||||
typedef struct {
|
||||
socket_t 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 */
|
||||
} sam_sess_t; /* a SAM session */
|
||||
|
||||
typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
/* no error code - not used by me (you can use it in your program) */
|
||||
SAM_NULL = 0,
|
||||
/* error codes from SAM itself (SAM_OK is not an actual "error") */
|
||||
SAM_OK, SAM_CANT_REACH_PEER, SAM_DUPLICATED_DEST, SAM_I2P_ERROR,
|
||||
SAM_INVALID_KEY, SAM_KEY_NOT_FOUND, SAM_PEER_NOT_FOUND, SAM_TIMEOUT,
|
||||
SAM_UNKNOWN,
|
||||
/* error codes from libsam */
|
||||
/* error codes from LibSAM */
|
||||
SAM_BAD_VERSION, SAM_CALLBACKS_UNSET, SAM_SOCKET_ERROR, SAM_TOO_BIG
|
||||
} samerr_t;
|
||||
|
||||
@ -90,41 +104,53 @@ typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/* SAM controls */
|
||||
extern bool sam_close();
|
||||
extern samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth);
|
||||
extern void sam_naming_lookup(const char *name);
|
||||
extern bool sam_read_buffer();
|
||||
/* SAM controls - sessions */
|
||||
extern sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
extern void sam_session_free(sam_sess_t **session);
|
||||
/* SAM controls - connection */
|
||||
extern bool sam_close(sam_sess_t *session);
|
||||
extern samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
/* SAM controls - utilities */
|
||||
extern void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
extern bool sam_read_buffer(sam_sess_t *session);
|
||||
extern const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
extern void (*sam_diedback)();
|
||||
extern void (*sam_diedback)(sam_sess_t *session);
|
||||
extern void (*sam_logback)(char *str);
|
||||
extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream commands */
|
||||
extern void sam_stream_close(sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sid_t stream_id, const void *data,
|
||||
size_t size);
|
||||
extern void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(sam_sess_t *session,
|
||||
const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
/* Stream commands - callbacks */
|
||||
extern void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sid_t stream_id, samerr_t result);
|
||||
extern void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream send queue */
|
||||
extern void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq,
|
||||
const void *data, size_t dsize);
|
||||
extern void sam_sendq_flush(sam_sid_t stream_id, sam_sendq_t **sendq);
|
||||
/* Stream send queue (experimental) */
|
||||
extern void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
extern void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
|
||||
/* Datagram commands */
|
||||
extern samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data,
|
||||
size_t size);
|
||||
extern samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
|
||||
/* Datagram commands - callbacks */
|
||||
extern void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size);
|
||||
extern void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest,
|
||||
void *data, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -31,74 +31,77 @@
|
||||
#include "platform.h"
|
||||
#include "sam.h"
|
||||
|
||||
static bool sam_hello();
|
||||
static bool sam_hello(sam_sess_t *session);
|
||||
static void sam_log(const char *format, ...);
|
||||
static void sam_parse(char *s);
|
||||
static ssize_t sam_read1(char *buf, size_t n);
|
||||
static ssize_t sam_read2(void *buf, size_t n);
|
||||
static bool sam_readable();
|
||||
static void sam_parse(sam_sess_t *session, char *s);
|
||||
static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n);
|
||||
static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n);
|
||||
static bool sam_readable(sam_sess_t *session);
|
||||
static sam_sendq_t *sam_sendq_create();
|
||||
static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
static samerr_t sam_session_create(sam_sess_t *session,
|
||||
const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
static bool sam_socket_connect(const char *host, uint16_t port);
|
||||
static bool sam_socket_connect(sam_sess_t *session, const char *host,
|
||||
uint16_t port);
|
||||
static bool sam_socket_resolve(const char *hostname, char *ipaddr);
|
||||
#ifdef WINSOCK
|
||||
static samerr_t sam_winsock_cleanup();
|
||||
static samerr_t sam_winsock_startup();
|
||||
static const char *sam_winsock_strerror(int code);
|
||||
#endif
|
||||
static ssize_t sam_write(const void *buf, size_t n);
|
||||
static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
|
||||
|
||||
/*
|
||||
* Callback functions
|
||||
* Note: if you add a new callback be sure to check for non-NULL in sam_connect
|
||||
*/
|
||||
/* a peer closed the connection */
|
||||
void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason) = NULL;
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
= NULL;
|
||||
/* a peer connected to us */
|
||||
void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest) = NULL;
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest) = NULL;
|
||||
/* a peer sent some stream data (`data' MUST be freed) */
|
||||
void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size) = NULL;
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size) = NULL;
|
||||
/* a peer sent some datagram data (`data' MUST be freed) */
|
||||
void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size) = NULL;
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size) = NULL;
|
||||
/* we lost the connection to the SAM host */
|
||||
void (*sam_diedback)() = NULL;
|
||||
void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
/* logging callback */
|
||||
void (*sam_logback)(char *str) = NULL;
|
||||
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result) = NULL;
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result) = NULL;
|
||||
/* our connection to a peer has completed */
|
||||
void (*sam_statusback)(sam_sid_t stream_id, samerr_t result) = NULL;
|
||||
|
||||
static socket_t samd; /* The socket descriptor we're using for
|
||||
communications with SAM */
|
||||
static bool samd_connected = false; /* Whether we're connected with SAM */
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/*
|
||||
* Closes the connection to the SAM host
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
*/
|
||||
bool sam_close()
|
||||
bool sam_close(sam_sess_t *session)
|
||||
{
|
||||
if (!samd_connected)
|
||||
assert(session != NULL);
|
||||
if (!session->connected)
|
||||
return true;
|
||||
|
||||
#ifdef WINSOCK
|
||||
if (closesocket(samd) == SOCKET_ERROR) {
|
||||
if (closesocket(session->sock) == SOCKET_ERROR) {
|
||||
SAMLOG("Failed closing the SAM connection (%s)",
|
||||
sam_winsock_strerror(WSAGetLastError()));
|
||||
return false;
|
||||
}
|
||||
samd_connected = false;
|
||||
session->connected = false;
|
||||
if (sam_winsock_cleanup() == SAM_OK)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
#else
|
||||
if (close(samd) == 0) {
|
||||
samd_connected = false;
|
||||
if (close(session->sock) == 0) {
|
||||
session->connected = false;
|
||||
return true;
|
||||
} else {
|
||||
SAMLOG("Failed closing the SAM connection (%s)", strerror(errno));
|
||||
@ -110,6 +113,7 @@ bool sam_close()
|
||||
/*
|
||||
* Connects to the SAM host
|
||||
*
|
||||
* session - an unused SAM session created by sam_session_init()
|
||||
* samhost - SAM host
|
||||
* samport - SAM port
|
||||
* destname - destination name for this program, or "TRANSIENT" for a random
|
||||
@ -117,24 +121,26 @@ bool sam_close()
|
||||
* tunneldepth - length of the I2P tunnels created by this program (longer is
|
||||
* more anonymous, but slower)
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
* Returns: True on success, false on failure. If true, `session' will be ready
|
||||
* for use.
|
||||
*/
|
||||
samerr_t sam_connect(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, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
samerr_t rc;
|
||||
|
||||
if (style == SAM_STREAM) {
|
||||
if (sam_closeback == NULL || sam_connectback == NULL ||
|
||||
sam_databack == NULL || sam_diedback == NULL ||
|
||||
sam_logback == NULL || sam_namingback == NULL ||
|
||||
sam_statusback == NULL) {
|
||||
if (sam_closeback == NULL || sam_connectback == NULL
|
||||
|| sam_databack == NULL || sam_diedback == NULL
|
||||
|| sam_logback == NULL || sam_namingback == NULL
|
||||
|| sam_statusback == NULL) {
|
||||
SAMLOGS("Please set callback functions before connecting");
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else if (style == SAM_DGRAM) {
|
||||
if (sam_dgramback == NULL || sam_diedback == NULL ||
|
||||
sam_logback == NULL || sam_namingback == NULL) {
|
||||
if (sam_dgramback == NULL || sam_diedback == NULL
|
||||
|| sam_logback == NULL || sam_namingback == NULL) {
|
||||
SAMLOGS("Please set callback functions before connecting");
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
@ -150,7 +156,7 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
if (!sam_socket_connect(samhost, samport)) {
|
||||
if (!sam_socket_connect(session, samhost, samport)) {
|
||||
#ifdef WINSOCK
|
||||
SAMLOG("Couldn't connect to SAM at %s:%u (%s)",
|
||||
samhost, samport, sam_winsock_strerror(WSAGetLastError()));
|
||||
@ -162,10 +168,10 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
return SAM_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
if (!sam_hello())
|
||||
if (!sam_hello(session))
|
||||
return SAM_BAD_VERSION;
|
||||
|
||||
rc = sam_session_create(destname, style, tunneldepth);
|
||||
rc = sam_session_create(session, destname, style, tunneldepth);
|
||||
if (rc != SAM_OK)
|
||||
return rc;
|
||||
|
||||
@ -182,15 +188,17 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
*/
|
||||
samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_DGRAM_PAYLOAD_MAX) {
|
||||
#ifdef NO_Z_FORMAT
|
||||
SAMLOG("Invalid data send size (%u bytes)", size);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%dz bytes)", size);
|
||||
SAMLOG("Invalid data send size (%zu bytes)", size);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
@ -198,11 +206,11 @@ samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
|
||||
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%u\n",
|
||||
dest, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%dz\n",
|
||||
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%zu\n",
|
||||
dest, size);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(data, size);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
@ -213,14 +221,15 @@ samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
|
||||
*
|
||||
* Returns: true on success, false on reply failure
|
||||
*/
|
||||
static bool sam_hello()
|
||||
static bool sam_hello(sam_sess_t *session)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_HELLO_CMD "HELLO VERSION MIN=1.0 MAX=1.0\n"
|
||||
#define SAM_HELLO_REPLY "HELLO REPLY RESULT=OK VERSION=1.0"
|
||||
char reply[SAM_REPLY_LEN];
|
||||
|
||||
sam_write(SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
|
||||
sam_read1(reply, SAM_REPLY_LEN);
|
||||
sam_write(session, SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
|
||||
sam_read1(session, reply, SAM_REPLY_LEN);
|
||||
if (strncmp(reply, SAM_HELLO_REPLY, strlen(SAM_HELLO_REPLY)) == 0)
|
||||
return true;
|
||||
else {
|
||||
@ -251,12 +260,13 @@ static void sam_log(const char *format, ...)
|
||||
*
|
||||
* name - name to lookup, or ME to lookup our own name
|
||||
*/
|
||||
void sam_naming_lookup(const char *name)
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -266,8 +276,9 @@ void sam_naming_lookup(const char *name)
|
||||
*
|
||||
* s - string of data that we read (read past tense)
|
||||
*/
|
||||
static void sam_parse(char *s)
|
||||
static void sam_parse(sam_sess_t *session, char *s)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED"
|
||||
#define SAM_NAMING_REPLY "NAMING REPLY"
|
||||
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
|
||||
@ -305,10 +316,14 @@ static void sam_parse(char *s)
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (sam_read2(data, size) != -1) {
|
||||
if (data == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_dgramback(dest, data, size); /* `data' must be freed */
|
||||
sam_dgramback(session, dest, data, size); /* `data' must be freed */
|
||||
} else
|
||||
free(data);
|
||||
|
||||
@ -381,17 +396,17 @@ static void sam_parse(char *s)
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
if (strncmp(p, "OK", strlen("OK")) == 0)
|
||||
sam_closeback(stream_id, SAM_OK);
|
||||
sam_closeback(session, stream_id, SAM_OK);
|
||||
else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
|
||||
sam_closeback(stream_id, SAM_CANT_REACH_PEER);
|
||||
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER);
|
||||
else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
|
||||
sam_closeback(stream_id, SAM_I2P_ERROR);
|
||||
sam_closeback(session, stream_id, SAM_I2P_ERROR);
|
||||
else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
|
||||
sam_closeback(stream_id, SAM_PEER_NOT_FOUND);
|
||||
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND);
|
||||
else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
|
||||
sam_closeback(stream_id, SAM_TIMEOUT);
|
||||
sam_closeback(session, stream_id, SAM_TIMEOUT);
|
||||
else
|
||||
sam_closeback(stream_id, SAM_UNKNOWN);
|
||||
sam_closeback(session, stream_id, SAM_UNKNOWN);
|
||||
|
||||
return;
|
||||
|
||||
@ -410,7 +425,7 @@ static void sam_parse(char *s)
|
||||
p = strstr(s, "N="); /* DESTINATION= */
|
||||
p += 2;
|
||||
strlcpy(dest, p, sizeof dest);
|
||||
sam_connectback(stream_id, dest);
|
||||
sam_connectback(session, stream_id, dest);
|
||||
|
||||
return;
|
||||
|
||||
@ -439,10 +454,15 @@ static void sam_parse(char *s)
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (sam_read2(data, size) != -1) {
|
||||
if (data == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_databack(stream_id, data, size); /* `data' must be freed */
|
||||
sam_databack(session, stream_id, data, size);
|
||||
/* ^^^ `data' must be freed ^^^*/
|
||||
} else
|
||||
free(data);
|
||||
|
||||
@ -463,21 +483,21 @@ static void sam_parse(char *s)
|
||||
assert(stream_id != 0);
|
||||
if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0)
|
||||
sam_statusback(stream_id, SAM_OK);
|
||||
sam_statusback(session, stream_id, SAM_OK);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
|
||||
sam_statusback(stream_id, SAM_CANT_REACH_PEER);
|
||||
sam_statusback(session, stream_id, SAM_CANT_REACH_PEER);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
|
||||
sam_statusback(stream_id, SAM_I2P_ERROR);
|
||||
sam_statusback(session, stream_id, SAM_I2P_ERROR);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
|
||||
sam_statusback(stream_id, SAM_INVALID_KEY);
|
||||
sam_statusback(session, stream_id, SAM_INVALID_KEY);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
|
||||
sam_statusback(stream_id, SAM_TIMEOUT);
|
||||
sam_statusback(session, stream_id, SAM_TIMEOUT);
|
||||
else
|
||||
sam_statusback(stream_id, SAM_UNKNOWN);
|
||||
sam_statusback(session, stream_id, SAM_UNKNOWN);
|
||||
|
||||
return;
|
||||
|
||||
@ -492,17 +512,18 @@ static void sam_parse(char *s)
|
||||
*
|
||||
* Returns: true if we read anything, or false if nothing was there
|
||||
*/
|
||||
bool sam_read_buffer()
|
||||
bool sam_read_buffer(sam_sess_t *session)
|
||||
{
|
||||
assert(session != NULL);
|
||||
bool read_something = false;
|
||||
char reply[SAM_REPLY_LEN];
|
||||
|
||||
if (sam_readable()) {
|
||||
if (sam_readable(session)) {
|
||||
do {
|
||||
sam_read1(reply, SAM_REPLY_LEN);
|
||||
sam_read1(session, reply, SAM_REPLY_LEN);
|
||||
read_something = true;
|
||||
sam_parse(reply);
|
||||
} while (sam_readable());
|
||||
sam_parse(session, reply);
|
||||
} while (sam_readable(session));
|
||||
}
|
||||
|
||||
return read_something;
|
||||
@ -522,24 +543,25 @@ bool sam_read_buffer()
|
||||
*
|
||||
* Returns: number of bytes read, or -1 on error
|
||||
*/
|
||||
static ssize_t sam_read1(char *buf, size_t n)
|
||||
static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n)
|
||||
{
|
||||
assert(session != NULL);
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
char *p;
|
||||
|
||||
*buf = '\0'; /* this forces `buf' to be a string even if there is a
|
||||
sam_read1 error return */
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
assert(n > 0);
|
||||
p = buf;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
nread = recv(samd, p, 1, 0);
|
||||
nread = recv(session->sock, p, 1, 0);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
|
||||
continue;
|
||||
@ -550,14 +572,14 @@ static ssize_t sam_read1(char *buf, size_t n)
|
||||
#else
|
||||
SAMLOG("recv() failed: %s", strerror(errno));
|
||||
#endif
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) { /* EOF */
|
||||
SAMLOGS("Connection closed by the SAM host");
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
assert(nread == 1);
|
||||
@ -589,22 +611,23 @@ static ssize_t sam_read1(char *buf, size_t n)
|
||||
*
|
||||
* Returns: number of bytes read, or -1 on error
|
||||
*/
|
||||
static ssize_t sam_read2(void *buf, size_t n)
|
||||
static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
|
||||
{
|
||||
assert(session != NULL);
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
void *p;
|
||||
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
assert(n > 0);
|
||||
p = buf;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
nread = recv(samd, p, nleft, 0);
|
||||
nread = recv(session->sock, p, nleft, 0);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
|
||||
continue;
|
||||
@ -615,14 +638,14 @@ static ssize_t sam_read2(void *buf, size_t n)
|
||||
#else
|
||||
SAMLOG("recv() failed: %s", strerror(errno));
|
||||
#endif
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) { /* EOF */
|
||||
SAMLOGS("Connection closed by the SAM host");
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
nleft -= nread;
|
||||
@ -640,23 +663,23 @@ static ssize_t sam_read2(void *buf, size_t n)
|
||||
*
|
||||
* Returns: true if data is waiting, false otherwise
|
||||
*/
|
||||
static bool sam_readable()
|
||||
static bool sam_readable(sam_sess_t *session)
|
||||
{
|
||||
assert(session != NULL);
|
||||
fd_set rset; /* set of readable descriptors */
|
||||
struct timeval tv;
|
||||
int rc;
|
||||
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return false;
|
||||
}
|
||||
/* it seems like there should be a better way to do this (i.e. not select)*/
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(samd, &rset);
|
||||
FD_SET(session->sock, &rset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10;
|
||||
rc = select(samd + 1, &rset, NULL, NULL, &tv);
|
||||
rc = select(session->sock + 1, &rset, NULL, NULL, &tv);
|
||||
if (rc == 0)
|
||||
return false;
|
||||
else if (rc > 0)
|
||||
@ -674,15 +697,17 @@ static bool sam_readable()
|
||||
/*
|
||||
* Adds data to the send queue
|
||||
*
|
||||
* stream_id - stream number to send to if the queue is full
|
||||
* sendq - the send queue
|
||||
* data - data to add
|
||||
* dsize - the size of the data
|
||||
*
|
||||
* Returns: true on success, false on error
|
||||
*/
|
||||
void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq, const void *data,
|
||||
size_t dsize)
|
||||
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize)
|
||||
{
|
||||
assert(session != NULL);
|
||||
assert(dsize >= 0);
|
||||
if (dsize == 0) {
|
||||
SAMLOGS("dsize is 0 - doing nothing");
|
||||
@ -704,7 +729,7 @@ void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq, const void *data,
|
||||
if ((*sendq)->size + dsize == SAM_STREAM_PAYLOAD_MAX) {
|
||||
memcpy((*sendq)->data + (*sendq)->size, data, dsize);
|
||||
(*sendq)->size = SAM_STREAM_PAYLOAD_MAX;
|
||||
sam_sendq_flush(stream_id, sendq);
|
||||
sam_sendq_flush(session, stream_id, sendq);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -713,8 +738,8 @@ void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq, const void *data,
|
||||
memcpy((*sendq)->data + (*sendq)->size, data, s); //append as much as we can
|
||||
dsize -= s; /* update dsize to the size of whatever data hasn't been sent*/
|
||||
(*sendq)->size = SAM_STREAM_PAYLOAD_MAX; /* it's a full packet */
|
||||
sam_sendq_flush(stream_id, sendq); /* send the queued data */
|
||||
sam_sendq_add(stream_id, sendq, data + s, dsize); /* recurse the rest */
|
||||
sam_sendq_flush(session, stream_id, sendq); /* send the queued data */
|
||||
sam_sendq_add(session, stream_id, sendq, data + s, dsize); /* recurse */
|
||||
|
||||
return;
|
||||
}
|
||||
@ -729,7 +754,15 @@ static sam_sendq_t *sam_sendq_create()
|
||||
sam_sendq_t *sendq;
|
||||
|
||||
sendq = malloc(sizeof(sam_sendq_t));
|
||||
if (sendq == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
sendq->data = malloc(SAM_STREAM_PAYLOAD_MAX);
|
||||
if (sendq->data == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
/* ^^ a waste of memory perhaps, but more efficient than realloc'ing every
|
||||
* time data is added the to queue */
|
||||
sendq->size = 0;
|
||||
@ -740,12 +773,14 @@ static sam_sendq_t *sam_sendq_create()
|
||||
/*
|
||||
* Sends the data in the send queue to the specified stream
|
||||
*
|
||||
* sendq - the send queue
|
||||
* stream_id - stream number to send to
|
||||
* sendq - the send queue
|
||||
*/
|
||||
void sam_sendq_flush(sam_sid_t stream_id, sam_sendq_t **sendq)
|
||||
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq)
|
||||
{
|
||||
sam_stream_send(stream_id, (*sendq)->data, (*sendq)->size);
|
||||
assert(session != NULL);
|
||||
sam_stream_send(session, stream_id, (*sendq)->data, (*sendq)->size);
|
||||
/* we now free it in case they aren't going to use it anymore */
|
||||
free((*sendq)->data);
|
||||
free(*sendq);
|
||||
@ -754,18 +789,53 @@ void sam_sendq_flush(sam_sid_t stream_id, sam_sendq_t **sendq)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates memory for the session and sets its default values
|
||||
*
|
||||
* session - pointer to a previously allocated sam_sess_t to initalise, or NULL
|
||||
* if you want memory to be allocated by this function
|
||||
*/
|
||||
sam_sess_t *sam_session_init(sam_sess_t *session)
|
||||
{
|
||||
if (session == NULL) {
|
||||
session = malloc(sizeof(sam_sess_t));
|
||||
if (session == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
session->connected = false;
|
||||
session->prev_id = 0;
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees memory used by the session and sets the pointer to NULL
|
||||
*
|
||||
* session - pointer to a pointer to a sam_sess_t
|
||||
*/
|
||||
void sam_session_free(sam_sess_t **session)
|
||||
{
|
||||
assert(*session != NULL);
|
||||
free(*session);
|
||||
*session = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends the second SAM handshake command and checks the reply
|
||||
*
|
||||
* destname - destination name for this program, or "TRANSIENT" to create a
|
||||
* random temporary destination
|
||||
* style - type of connection to use (SAM_STREAM, SAM_DGRAM, or SAM_RAW)
|
||||
* tunneldepth - length of the I2P tunnels created by this program
|
||||
*
|
||||
* Returns: SAM error code
|
||||
*/
|
||||
static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth)
|
||||
static samerr_t sam_session_create(sam_sess_t *session, const char *destname,
|
||||
sam_conn_t style, uint_t tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK"
|
||||
#define SAM_SESSTATUS_REPLY_DD "SESSION STATUS RESULT=DUPLICATED_DEST"
|
||||
#define SAM_SESSTATUS_REPLY_I2E "SESSION STATUS RESULT=I2P_ERROR"
|
||||
@ -790,8 +860,8 @@ static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
assert(false); /* unimplemented */
|
||||
}
|
||||
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_read1(reply, SAM_REPLY_LEN);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_read1(session, reply, SAM_REPLY_LEN);
|
||||
if (strncmp(reply, SAM_SESSTATUS_REPLY_OK,
|
||||
strlen(SAM_SESSTATUS_REPLY_OK)) == 0)
|
||||
return SAM_OK;
|
||||
@ -816,20 +886,21 @@ static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
*
|
||||
* Returns: true on sucess, false on error, with errno set
|
||||
*/
|
||||
bool sam_socket_connect(const char *host, uint16_t port)
|
||||
bool sam_socket_connect(sam_sess_t *session, const char *host, uint16_t port)
|
||||
{
|
||||
assert(session != NULL);
|
||||
struct sockaddr_in hostaddr;
|
||||
int rc;
|
||||
char ipaddr[INET_ADDRSTRLEN];
|
||||
|
||||
samd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
session->sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
#ifdef WINSOCK
|
||||
if (samd == INVALID_SOCKET) {
|
||||
if (session->sock == INVALID_SOCKET) {
|
||||
SAMLOG("socket() failed: %s", sam_winsock_strerror(WSAGetLastError()));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (samd == -1) {
|
||||
if (session->sock == -1) {
|
||||
SAMLOG("socket() failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
@ -852,7 +923,7 @@ bool sam_socket_connect(const char *host, uint16_t port)
|
||||
} else if (rc == -1)
|
||||
return false;
|
||||
|
||||
rc = connect(samd, (struct sockaddr *)&hostaddr, sizeof hostaddr);
|
||||
rc = connect(session->sock, (struct sockaddr *)&hostaddr, sizeof hostaddr);
|
||||
if (rc == -1) {
|
||||
#ifdef WINSOCK
|
||||
SAMLOG("connect() failed: %s", sam_winsock_strerror(WSAGetLastError()));
|
||||
@ -862,7 +933,7 @@ bool sam_socket_connect(const char *host, uint16_t port)
|
||||
return false;
|
||||
}
|
||||
|
||||
samd_connected = true;
|
||||
session->connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -906,10 +977,11 @@ retry:
|
||||
}
|
||||
a.s_addr = ((struct in_addr *)h->h_addr)->s_addr;
|
||||
#ifdef NO_INET_NTOP
|
||||
/* inet_ntoa() was very poorly designed! */
|
||||
char *tmp;
|
||||
tmp = inet_ntoa(a);
|
||||
assert(tmp != NULL);
|
||||
strlcpy(ipaddr, tmp, INET_ADDRSTRLEN); /* inet_ntoa() was very poorly designed */
|
||||
strlcpy(ipaddr, tmp, INET_ADDRSTRLEN);
|
||||
return true;
|
||||
#else
|
||||
if (inet_ntop(AF_INET, &a, ipaddr, INET_ADDRSTRLEN) != NULL) {
|
||||
@ -926,8 +998,9 @@ retry:
|
||||
*
|
||||
* stream_id - stream number to close
|
||||
*/
|
||||
void sam_stream_close(sam_sid_t stream_id)
|
||||
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
|
||||
@ -935,7 +1008,7 @@ void sam_stream_close(sam_sid_t stream_id)
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -947,22 +1020,22 @@ void sam_stream_close(sam_sid_t stream_id)
|
||||
*
|
||||
* Returns: stream id number
|
||||
*/
|
||||
sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
|
||||
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
static sam_sid_t id = 0;
|
||||
|
||||
id++; /* increment the id for the connection */
|
||||
session->prev_id++; /* increment the id for the connection */
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n",
|
||||
id, dest);
|
||||
session->prev_id, dest);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n",
|
||||
id, dest);
|
||||
session->prev_id, dest);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return id;
|
||||
return session->prev_id;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -974,8 +1047,10 @@ sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
*/
|
||||
samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
|
||||
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_STREAM_PAYLOAD_MAX) {
|
||||
@ -983,7 +1058,7 @@ samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
|
||||
SAMLOG("Invalid data send size (%u bytes) for stream %d",
|
||||
size, stream_id);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%dz bytes) for stream %d",
|
||||
SAMLOG("Invalid data send size (%zu bytes) for stream %d",
|
||||
size, stream_id);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
@ -997,11 +1072,11 @@ samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
|
||||
stream_id, size);
|
||||
#endif
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%dz\n",
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%zu\n",
|
||||
stream_id, size);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(data, size);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
@ -1197,7 +1272,7 @@ const char *sam_winsock_strerror(int code)
|
||||
return "This is a nonrecoverable error";
|
||||
case WSANO_DATA:
|
||||
return "Valid name, no data record of requested type";
|
||||
/* None of this shit compiles under Mingw - who knows why...
|
||||
/* None of this shit compiles under Mingw - who knows why...
|
||||
case WSA_INVALID_HANDLE:
|
||||
return "Specified event object handle is invalid";
|
||||
case WSA_INVALID_PARAMETER:
|
||||
@ -1215,8 +1290,7 @@ const char *sam_winsock_strerror(int code)
|
||||
case WSAINVALIDPROVIDER:
|
||||
return "Invalid service provider version number";
|
||||
case WSAPROVIDERFAILEDINIT:
|
||||
return "Unable to initialize a service provider";
|
||||
*/
|
||||
return "Unable to initialize a service provider"; */
|
||||
case WSASYSCALLFAILURE:
|
||||
return "System call failure";
|
||||
default:
|
||||
@ -1233,15 +1307,16 @@ const char *sam_winsock_strerror(int code)
|
||||
*
|
||||
* Returns: `n', or -1 on error
|
||||
*/
|
||||
static ssize_t sam_write(const void *buf, size_t n)
|
||||
static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
{
|
||||
assert(session != NULL);
|
||||
size_t nleft;
|
||||
ssize_t nwritten;
|
||||
const char *p;
|
||||
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot write to SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
@ -1257,7 +1332,7 @@ static ssize_t sam_write(const void *buf, size_t n)
|
||||
p = buf;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
nwritten = send(samd, p, nleft, 0);
|
||||
nwritten = send(session->sock, p, nleft, 0);
|
||||
if (nwritten <= 0) {
|
||||
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
|
||||
continue;
|
||||
@ -1268,8 +1343,8 @@ static ssize_t sam_write(const void *buf, size_t n)
|
||||
#else
|
||||
SAMLOG("send() failed: %s", strerror(errno));
|
||||
#endif
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
|
||||
Known Bugs:
|
||||
* TunnelServer may crash the I2P router in the following
|
||||
ways when a large file is downloaded:
|
||||
|
||||
* Out of memory exception (for large files)
|
||||
* Mysterious router death with no errors in the router logs
|
||||
(more recently)
|
||||
* BUG! in SAM proxy
|
||||
See http://oregonstate.edu/~barnesc/temp/sam_crash.txt
|
||||
* A small number of datagram packets sent are lost (even in a local
|
||||
loopback). This is apparently a bug in I2P.
|
||||
* tunnel.TunnelServer may crash the I2P router when a 20+ MB file
|
||||
is downloaded at 200+ KB/s (only possible with local downloads).
|
||||
* Errors raised for sockets are non entirely consistent.
|
||||
See todo.txt for how to fix this.
|
||||
* A session does not close until a program exits.
|
||||
This should be fine once I2P is patched to allow multiple
|
||||
programs to use a single session at once.
|
||||
* i2p.router.start() does not work.
|
||||
* i2p.router.start() does not work.
|
||||
|
||||
Fixed Bugs:
|
||||
* Large downloads are no longer corrupted (fixed by jrandom in I2P
|
||||
core).
|
||||
* Datagram packets are no longer lost, for a local server and
|
||||
client (fixed by jrandom in I2P core).
|
||||
|
18
apps/sam/python/doc/epydoc/calldoc.py
Normal file
18
apps/sam/python/doc/epydoc/calldoc.py
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/local/bin/python
|
||||
|
||||
#
|
||||
# Call the command line interface for Epydoc.
|
||||
#
|
||||
|
||||
# We have to do some path magic to prevent Python from getting
|
||||
# confused about the difference between this epydoc module, and the
|
||||
# real epydoc package. So sys.path[0], which contains the directory
|
||||
# of the script.
|
||||
import sys, os.path
|
||||
script_path = os.path.abspath(sys.path[0])
|
||||
sys.path = [p for p in sys.path if
|
||||
os.path.abspath(p) != script_path]
|
||||
|
||||
from epydoc.cli import cli
|
||||
cli()
|
||||
|
35
apps/sam/python/doc/epydoc/makedoc.py
Normal file
35
apps/sam/python/doc/epydoc/makedoc.py
Normal file
@ -0,0 +1,35 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
"""
|
||||
Make epydoc HTML documentation in the 'html' subdirectory.
|
||||
"""
|
||||
|
||||
import epydoc as epydoc_
|
||||
import inspect
|
||||
import os, sys
|
||||
|
||||
def epydoc(args):
|
||||
"""Run epydoc (command line) with given argument string."""
|
||||
os.system('python calldoc.py ' + args)
|
||||
|
||||
def makedoc():
|
||||
"""Make all epydoc HTML documentation for Python I2P library."""
|
||||
modlist = [
|
||||
'i2p',
|
||||
'i2p.eep',
|
||||
'i2p.tunnel',
|
||||
'i2p.router',
|
||||
'i2p.socket',
|
||||
'i2p.select',
|
||||
'i2p.samclasses',
|
||||
'i2p.CGIHTTPServer',
|
||||
'i2p.SimpleHTTPServer',
|
||||
'i2p.BaseHTTPServer',
|
||||
'i2p.SocketServer',
|
||||
'i2p.pylib'
|
||||
]
|
||||
modlist.reverse()
|
||||
epydoc('--html ' + ' '.join(modlist))
|
||||
|
||||
if __name__ == '__main__':
|
||||
makedoc()
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
|
91
apps/sam/python/doc/guide/i2p.BaseHTTPServer.html
Normal file
91
apps/sam/python/doc/guide/i2p.BaseHTTPServer.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.BaseHTTPServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.BaseHTTPServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Emulates Python BaseHTTPServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Overview"> Overview </a></h2>
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html">http://www.python.org/doc/current/lib/module-BaseHTTPServer.html</a>
|
||||
|
||||
<p>
|
||||
To get a server going, use:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer
|
||||
>>> BaseHTTPServer.test().
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
Consult the documentation for function test() to change basic server settings, such as the session name.
|
||||
|
||||
<p>
|
||||
A fully customizable example:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer
|
||||
>>> session = "mytestxxx.i2p" # SAM session name
|
||||
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
|
||||
>>> class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass
|
||||
>>> httpd = MyServer(session, MyRequestHandler)
|
||||
>>> httpd.socket.dest
|
||||
(Base64 Destination of server)
|
||||
>>> httpd.serve_forever()
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>BaseHTTPRequestHandler</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class BaseHTTPServer.BaseHTTPRequestHandler.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>HTTPServer</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class BaseHTTPServer.HTTPServer.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
<strong>test</strong>(HandlerClass=BaseHTTPRequestHandler, ServerClass=HTTPServer, protocol='HTTP/1.0', session='mytestxxx.i2p')
|
||||
<ul ><pre>
|
||||
Test the HTTP request handler class.
|
||||
This runs an I2P TCP server under SAM session 'session'. If a single command
|
||||
line argument is given, the argument is used instead as the SAM session name.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
90
apps/sam/python/doc/guide/i2p.CGIHTTPServer.html
Normal file
90
apps/sam/python/doc/guide/i2p.CGIHTTPServer.html
Normal file
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.CGIHTTPServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.CGIHTTPServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Module <code >i2p.CGIHTTPServer</code > emulates the Python CGIHTTPServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Overview"> Overview </a></h2>
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html">http://www.python.org/doc/current/lib/module-CGIHTTPServer.html</a>
|
||||
|
||||
<p>
|
||||
To get a server going, use:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import CGIHTTPServer
|
||||
>>> CGIHTTPServer.test().
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
Consult the documentation for function test() to change basic server settings, such as the session name.
|
||||
|
||||
<p>
|
||||
A fully customizable example:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer, CGIHTTPServer
|
||||
>>> session = "mytestxxx.i2p" # SAM session name
|
||||
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
|
||||
>>> class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): pass
|
||||
>>> httpd = MyServer(session, MyRequestHandler)
|
||||
>>> httpd.socket.dest
|
||||
(Base64 Destination of server)
|
||||
>>> httpd.serve_forever()
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>CGIHTTPRequestHandler</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class CGIHTTPServer.CGIHTTPRequestHandler.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
|
||||
<strong>test</strong>(HandlerClass=CGIHTTPRequestHandler,
|
||||
ServerClass=i2p.BaseHTTPServer.HTTPServer,
|
||||
session='mytestxxx.i2p')
|
||||
<ul ><pre>
|
||||
Test the HTTP CGI request handler class.
|
||||
This runs an I2P TCP server under SAM session 'session'. If a single
|
||||
command line argument is given, the argument is used instead as the SAM session
|
||||
name.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
88
apps/sam/python/doc/guide/i2p.SimpleHTTPServer.html
Normal file
88
apps/sam/python/doc/guide/i2p.SimpleHTTPServer.html
Normal file
@ -0,0 +1,88 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.SimpleHTTPServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.SimpleHTTPServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Emulates Python SimpleHTTPServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Overview"> Overview </a></h2>
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html">http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html</a>
|
||||
|
||||
<p>
|
||||
To get a server going, use:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import SimpleHTTPServer
|
||||
>>> SimpleHTTPServer.test().
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
Consult the documentation for function test() to change basic server settings, such as the session name.
|
||||
|
||||
<p>
|
||||
A fully customizable example:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer, SimpleHTTPServer
|
||||
>>> session = "mytestxxx.i2p" # SAM session name
|
||||
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
|
||||
>>> class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): pass
|
||||
>>> httpd = MyServer(session, MyRequestHandler)
|
||||
>>> httpd.socket.dest
|
||||
(Base64 Destination of server)
|
||||
>>> httpd.serve_forever()
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>SimpleHTTPRequestHandler</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class SimpleHTTPServer.SimpleHTTPRequestHandler.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
|
||||
<strong>test</strong>(HandlerClass=SimpleHTTPRequestHandler, ServerClass= i2p.BaseHTTPServer.HTTPServer, session='mytestxxx.i2p')
|
||||
<ul ><pre>
|
||||
Test the HTTP simple request handler class.
|
||||
This runs an I2P TCP server under SAM session 'session'. If a single command
|
||||
line argument is given, the argument is used instead as the SAM session name.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
|
||||
</body></html>
|
50
apps/sam/python/doc/guide/i2p.SocketServer.html
Normal file
50
apps/sam/python/doc/guide/i2p.SocketServer.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.SocketServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.SocketServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Emulates Python SocketServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SocketServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SocketServer.html">http://www.python.org/doc/current/lib/module-SocketServer.html</a>
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
BaseRequestHandler
|
||||
BaseServer
|
||||
DatagramRequestHandler
|
||||
ForkingMixIn
|
||||
ForkingTCPServer
|
||||
ForkingUDPServer
|
||||
StreamRequestHandler
|
||||
TCPServer
|
||||
ThreadingMixIn
|
||||
ThreadingTCPServer
|
||||
ThreadingUDPServer
|
||||
UDPServer
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
@ -24,19 +22,23 @@ Package <code >i2p</code > is a container package for more specific modules.
|
||||
|
||||
|
||||
<p>
|
||||
It exports the following names:
|
||||
It contains the following modules:
|
||||
|
||||
<p>
|
||||
<ul >
|
||||
|
||||
<pre> <a href="./i2p.sam.html" class='printable' title ="User's Guide:i2p.sam">sam</a>
|
||||
<a href="./i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">eep</a>
|
||||
<a href="./i2p.router.html" class='printable' title ="User's Guide:i2p.router">router</a>
|
||||
<a href="#Error" class='printable' title ="User's Guide:i2p">Error</a>
|
||||
<a href="#RouterError" class='printable' title ="User's Guide:i2p">RouterError</a>
|
||||
<pre> <a href="i2p.BaseHTTPServer.html" class='printable' title ="User's Guide:i2p.BaseHTTPServer">i2p.BaseHTTPServer</a> (Emulate Python BaseHTTPServer module)
|
||||
<a href="i2p.CGIHTTPServer.html" class='printable' title ="User's Guide:i2p.CGIHTTPServer">i2p.CGIHTTPServer</a> (Emulate Python CGIHTTPServer module)
|
||||
<a href="i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">i2p.eep</a> (Retrieve eepsites)
|
||||
<a href="i2p.router.html" class='printable' title ="User's Guide:i2p.router">i2p.router</a> (Manage the I2P router)
|
||||
<a href="i2p.select.html" class='printable' title ="User's Guide:i2p.select">i2p.select</a> (Emulate Python select module)
|
||||
<a href="i2p.SimpleHTTPServer.html" class='printable' title ="User's Guide:i2p.SimpleHTTPServer">i2p.SimpleHTTPServer</a> (Emulate Python SimpleHTTPServer module)
|
||||
<a href="i2p.socket.html" class='printable' title ="User's Guide:i2p.socket">i2p.socket</a> (Send and receive across the I2P network)
|
||||
<a href="i2p.SocketServer.html" class='printable' title ="User's Guide:i2p.SocketServer">i2p.SocketServer</a> (Emulate Python SocketServer module)
|
||||
<a href="i2p.tunnel.html" class='printable' title ="User's Guide:i2p.tunnel">i2p.tunnel</a> (Exchange data between I2P and regular sockets)
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
class <strong>Error</strong>(Exception):
|
||||
<ul >
|
||||
|
||||
@ -46,7 +48,6 @@ class <strong>Error</strong>(Exception):
|
||||
|
||||
<p>
|
||||
class <strong>RouterError</strong>(Error):
|
||||
|
||||
<ul >
|
||||
|
||||
<pre> Could not connect to router.
|
||||
@ -55,7 +56,4 @@ class <strong>RouterError</strong>(Error):
|
||||
|
||||
<p>
|
||||
</div>
|
||||
<p><em>
|
||||
</em><!-- Time since request: 0.85 secs. -->
|
||||
|
||||
</body></html>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user