propagate from branch 'i2p.i2p' (head b7a8a00272124eec0d149224af58bd144358c009)

to branch 'i2p.i2p.zzz.test' (head a4d67a357c36f4e94718bf237a7af96b8617a4a7)
This commit is contained in:
zzz
2010-03-08 20:04:55 +00:00
270 changed files with 19651 additions and 10467 deletions

View File

@ -80,9 +80,10 @@ Public domain except as listed below:
Installer:
Launch4j 2.0.RC3:
Copyright (C) 2005 Grzegorz Kowal
See licenses/LICENSE-GPLv2.txt
Launch4j 3.0.1:
Copyright (c) 2004, 2008 Grzegorz Kowal
See licenses/LICENSE-Launch4j.txt (in binary packages)
See installer/lib/launch4j/LICENSE.txt (in source packages)
The following projects are used by Launch4j...
MinGW binutils (http://www.mingw.org/)

View File

@ -1,29 +1,38 @@
These instructions are for the 1.5 Android SDK.
These instructions are for a recent Android SDK (1.6 or later)..
Should also still work with a 1.5 SDK.
The build file is not compatible with the 1.1 SDK any more.
1.6 and 2.0 SDKs are untested.
#Download the SDK from http://developer.android.com/sdk/index.html
#Unzip the android SDK in ../../
#So then the android tools will be in ../../android-sdk-linux_x86-1.5_r2/tools/
#So then the android tools will be in ../../android-sdk-linux_86/tools/
#
# now go to the available packages tab, check the box and click refresh,
# and download an SDK Platform
# Since I2P is configured to run on 1.1 or higher
# (API 2) download that one. Otherwise you must change the
# target in default.properties from android-2 to andriod-x
# where x is the API version.
# create a file local.properties with the following line:
# sdk-location=/path/to/your/android-sdk-linux_x86-1.5_r2
# sdk-location=/path/to/your/android-sdk-linux_86
#then build the android apk file:
ant debug
# Create the android 1.5 virtual device
# Create the android 1.1 (API 2) virtual device
# (don't make a custom hardware profile)
../../android-sdk-linux_x86-1.5_r2/tools/android create avd --name i2p --target 2
# A AVD created with the 1.5 SDK will not work with the newer tools
../../android-sdk-linux_86/tools/android create avd --name i2p --target 2
#then run the emulator:
../../android-sdk-linux_x86-1.5_r2/tools/emulator -avd i2p &
../../android-sdk-linux_86/tools/emulator -avd i2p &
#then wait a couple minutes until the emulator is up
#then install the I2P app
ant install
#then run the debugger
../../android-sdk-linux_x86-1.5_r2/tools/ddms &
../../android-sdk-linux_86/tools/ddms &
#to rebuild and reinstall to emulator:
ant reinstall

View File

@ -113,6 +113,10 @@
<delete file="${external-libs-folder}/crypto.jar" />
</target>
<!-- fix for property name change sometime after SDK 1.5 -->
<property name="android-jar" value="${android.jar}" />
<property name="android-aidl" value="${android.aidl}" />
<!--
================================================================================
From here down copied from SDK platforms/android-1.1/templates/android_rules.xml

View File

@ -10,7 +10,13 @@ i2np.udp.maxConnections=30
# no I2CP
i2p.dummyClientFacade=true
# for now
i2np.ntcp.enable=false
#i2np.ntcp.enable=false
#
# UDP crashes the JVM, don't know why
#
i2np.udp.enable=false
# no COMM at all!!!
#i2p.vmCommSystem=true
# not on android
i2np.upnp.enable=false
routerconsole.geoip.enable=false

View File

@ -50,7 +50,7 @@ public class DoCMDS implements Runnable {
// FIX ME
// I need a better way to do versioning, but this will do for now.
public static final String BMAJ = "00", BMIN = "00", BREV = "0A", BEXT = "";
public static final String BMAJ = "00", BMIN = "00", BREV = "0B", BEXT = "";
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
private Socket server;
private Properties props;

View File

@ -53,7 +53,8 @@
-->
<target name="war" depends="jar, bundle">
<war destfile="../i2psnark.war" webxml="../web.xml">
<classes dir="./build/obj" includes="**/*.class" excludes="**/RunStandalone.class" />
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
<classes dir="./build/obj" includes="**/web/*.class" />
</war>
</target>

View File

@ -151,7 +151,11 @@ class PeerConnectionOut implements Runnable
{
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send " + peer + ": " + m + " on " + peer.metainfo.getName());
m.sendMessage(dout);
// This can block for quite a while.
// To help get slow peers going, and track the bandwidth better,
// move this _after_ state.uploaded() and see how it works.
//m.sendMessage(dout);
lastSent = System.currentTimeMillis();
// Remove all piece messages after sending a choke message.
@ -162,6 +166,7 @@ class PeerConnectionOut implements Runnable
if (m.type == Message.PIECE)
state.uploaded(m.len);
m.sendMessage(dout);
m = null;
}
}

View File

@ -198,6 +198,7 @@ public class SnarkManager implements Snark.CompleteListener {
String ot = _config.getProperty(I2PSnarkUtil.PROP_OPENTRACKERS);
if (ot != null)
_util.setOpenTrackerString(ot);
// FIXME set util use open trackers property somehow
getDataDir().mkdirs();
}
@ -424,10 +425,27 @@ public class SnarkManager implements Snark.CompleteListener {
FileInputStream fis = null;
try {
fis = new FileInputStream(sfile);
MetaInfo info = new MetaInfo(fis);
fis.close();
fis = null;
} catch (IOException ioe) {
// catch this here so we don't try do delete it below
addMessage(_("Cannot open \"{0}\"", sfile.getName()) + ": " + ioe.getMessage());
return;
}
try {
MetaInfo info = new MetaInfo(fis);
try {
fis.close();
fis = null;
} catch (IOException e) {}
if (!TrackerClient.isValidAnnounce(info.getAnnounce())) {
if (_util.shouldUseOpenTrackers() && _util.getOpenTrackers() != null) {
addMessage(_("Warning - Ignoring non-i2p tracker in \"{0}\", will announce to i2p open trackers only", info.getName()));
} else {
addMessage(_("Warning - Ignoring non-i2p tracker in \"{0}\", and open trackers are disabled, you must enable open trackers before starting the torrent!", info.getName()));
dontAutoStart = true;
}
}
String rejectMessage = locked_validateTorrent(info);
if (rejectMessage != null) {
sfile.delete();
@ -551,12 +569,10 @@ public class SnarkManager implements Snark.CompleteListener {
saveConfig();
}
/**
* Warning - does not validate announce URL - use TrackerClient.isValidAnnounce()
*/
private String locked_validateTorrent(MetaInfo info) throws IOException {
String announce = info.getAnnounce();
// basic validation of url
if ((!announce.startsWith("http://")) ||
(announce.indexOf(".i2p/") < 0)) // need to do better than this
return _("Non-i2p tracker in \"{0}\", deleting it from our list of trackers!", info.getName());
List files = info.getFiles();
if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) {
return _("Too many files in \"{0}\" ({1}), deleting it!", info.getName(), files.size());

View File

@ -420,13 +420,29 @@ public class Storage
}
}
private static final char[] ILLEGAL = new char[] {
'<', '>', ':', '"', '/', '\\', '|', '?', '*',
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
/**
* Removes 'suspicious' characters from the give file name.
* Removes 'suspicious' characters from the given file name.
* http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
*/
private static String filterName(String name)
{
// XXX - Is this enough?
return name.replace(File.separatorChar, '_');
if (name.equals(".") || name.equals(" "))
return "_";
String rv = name;
if (rv.startsWith("."))
rv = '_' + rv.substring(1);
if (rv.endsWith(".") || rv.endsWith(" "))
rv = rv.substring(0, rv.length() - 1) + '_';
for (int i = 0; i < ILLEGAL.length; i++) {
if (rv.indexOf(ILLEGAL[i]) >= 0)
rv = rv.replace(ILLEGAL[i], '_');
}
return rv;
}
private File createFileFromNames(File base, List names) throws IOException
@ -577,6 +593,9 @@ public class Storage
if (rafs == null) return;
for (int i = 0; i < rafs.length; i++)
{
// if we had an IOE in check(), the RAFlock may be null
if (RAFlock[i] == null)
continue;
try {
synchronized(RAFlock[i]) {
closeRAF(i);

View File

@ -24,6 +24,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -123,13 +125,19 @@ public class TrackerClient extends I2PAppThread
// followed by the secondary open trackers
// It's painful, but try to make sure if an open tracker is also
// the primary tracker, that we don't add it twice.
// todo: check for b32 matches as well
trackers = new ArrayList(2);
trackers.add(new Tracker(meta.getAnnounce(), true));
String primary = meta.getAnnounce();
if (isValidAnnounce(primary)) {
trackers.add(new Tracker(meta.getAnnounce(), true));
} else {
_log.warn("Skipping invalid or non-i2p announce: " + primary);
}
List tlist = _util.getOpenTrackers();
if (tlist != null) {
for (int i = 0; i < tlist.size(); i++) {
String url = (String)tlist.get(i);
if (!url.startsWith("http://")) {
if (!isValidAnnounce(url)) {
_log.error("Bad announce URL: [" + url + "]");
continue;
}
@ -138,22 +146,29 @@ public class TrackerClient extends I2PAppThread
_log.error("Bad announce URL: [" + url + "]");
continue;
}
if (meta.getAnnounce().startsWith(url.substring(0, slash)))
if (primary.startsWith(url.substring(0, slash)))
continue;
String dest = _util.lookup(url.substring(7, slash));
if (dest == null) {
_log.error("Announce host unknown: [" + url + "]");
continue;
}
if (meta.getAnnounce().startsWith("http://" + dest))
if (primary.startsWith("http://" + dest))
continue;
if (meta.getAnnounce().startsWith("http://i2p/" + dest))
if (primary.startsWith("http://i2p/" + dest))
continue;
trackers.add(new Tracker(url, false));
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
}
}
if (tlist.size() <= 0) {
// FIXME really need to get this message to the gui
stop = true;
_log.error("No valid trackers for infoHash: " + infoHash);
return;
}
long uploaded = coordinator.getUploaded();
long downloaded = coordinator.getDownloaded();
long left = coordinator.getLeft();
@ -399,6 +414,22 @@ public class TrackerClient extends I2PAppThread
return sb.toString();
}
/**
* @return true for i2p hosts only
* @since 0.7.12
*/
static boolean isValidAnnounce(String ann) {
URL url;
try {
url = new URL(ann);
} catch (MalformedURLException mue) {
return false;
}
return url.getProtocol().equals("http") &&
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p")) &&
url.getPort() < 0;
}
private class Tracker
{
String announce;

View File

@ -279,7 +279,9 @@ public class BDecoder
public BEValue bdecodeMap() throws IOException
{
int c = getNextIndicator();
if (c != 'd')
if (c == '<')
throw new InvalidBEncodingException("Expected a .torrent metainfo file but found HTML? Check URL or file!");
else if (c != 'd')
throw new InvalidBEncodingException("Expected 'd', not '"
+ (char)c + "'");
indicator = 0;

View File

@ -179,7 +179,7 @@ public class BEValue
if (value instanceof byte[])
{
byte[] bs = (byte[])value;
// XXX - Stupid heuristic...
// XXX - Stupid heuristic... and not UTF-8
if (bs.length <= 12)
valueString = new String(bs);
else

View File

@ -455,8 +455,11 @@ public class I2PSnarkServlet extends HttpServlet {
int i = filename.lastIndexOf(".torrent");
if (i > 0)
filename = filename.substring(0, i);
if (filename.length() > MAX_DISPLAYED_FILENAME_LENGTH)
String fullFilename = filename;
if (filename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
fullFilename = new String(filename);
filename = filename.substring(0, MAX_DISPLAYED_FILENAME_LENGTH) + "&hellip;";
}
long total = snark.meta.getTotalLength();
// Early typecast, avoid possibly overflowing a temp integer
long remaining = (long) snark.storage.needed() * (long) snark.meta.getPieceLength(0);
@ -627,13 +630,23 @@ public class I2PSnarkServlet extends HttpServlet {
out.write("<a href=\"" + uri + "?action=Remove" + parameters
+ "\" title=\"");
out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
out.write("\">");
out.write("\" onclick=\"if (!confirm('");
// Can't figure out how to escape double quotes inside the onclick string.
// Single quotes in translate strings with parameters must be doubled.
// Then the remaining single quite must be escaped
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
out.write("')) { return false; }\">");
out.write(_("Remove"));
out.write("</a><br>");
out.write("<a href=\"" + uri + "?action=Delete" + parameters
+ "\" title=\"");
out.write(_("Delete the .torrent file and the associated data file(s)"));
out.write("\">");
out.write("\" onclick=\"if (!confirm('");
// Can't figure out how to escape double quotes inside the onclick string.
// Single quotes in translate strings with parameters must be doubled.
// Then the remaining single quite must be escaped
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
out.write("')) { return false; }\">");
out.write(_("Delete"));
out.write("</a>");
}
@ -662,10 +675,10 @@ public class I2PSnarkServlet extends HttpServlet {
client = "Azureus";
else if ("CwsL".equals(ch))
client = "I2PSnarkXL";
else if ("ZV".equals(ch.substring(2,4)))
client = "Robert";
else if ("VUZP".equals(ch))
else if ("ZV".equals(ch.substring(2,4)) || "VUZP".equals(ch))
client = "Robert";
else if (ch.startsWith("LV")) // LVCS 1.0.2?; LVRS 1.0.4
client = "Transmission";
else
client = _("Unknown") + " (" + ch + ')';
out.write(client + "&nbsp;&nbsp;" + peer.toString().substring(5, 9));
@ -774,7 +787,7 @@ public class I2PSnarkServlet extends HttpServlet {
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n");
out.write(_("Data to seed"));
out.write(":<td>" + _manager.getDataDir().getAbsolutePath() + File.separatorChar
+ "<input type=\"text\" name=\"baseFile\" size=\"20\" value=\"" + baseFile
+ "<input type=\"text\" name=\"baseFile\" size=\"40\" value=\"" + baseFile
+ "\" title=\"");
out.write(_("File or directory to seed (must be within the specified path)"));
out.write("\" ><tr><td>\n");

View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P i2psnark\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-14 06:24+0000\n"
"PO-Revision-Date: 2010-01-14 06:33+0000\n"
"POT-Creation-Date: 2010-02-23 08:02+0000\n"
"PO-Revision-Date: 2010-02-23 08:27+0000\n"
"Last-Translator: 4get <forget@mail.i2p>\n"
"Language-Team: foo <foo@bar>\n"
"MIME-Version: 1.0\n"
@ -22,179 +22,184 @@ msgstr ""
msgid "Adding torrents in {0} minutes"
msgstr "Торренты будут подгружены через {0} минут(ы)"
#: ../java/src/org/klomp/snark/SnarkManager.java:241
#: ../java/src/org/klomp/snark/SnarkManager.java:242
#, java-format
msgid "Total uploaders limit changed to {0}"
msgstr "Новое значение лимита количества слотов отдачи: {0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:243
#: ../java/src/org/klomp/snark/SnarkManager.java:244
#, java-format
msgid "Minimum total uploaders limit is {0}"
msgstr "Минимально допустимое значение для количества слотов: {0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:255
#: ../java/src/org/klomp/snark/SnarkManager.java:256
#, java-format
msgid "Up BW limit changed to {0}KBps"
msgstr "Новое значение лимита скорости отдачи: {0} KBps"
#: ../java/src/org/klomp/snark/SnarkManager.java:257
#: ../java/src/org/klomp/snark/SnarkManager.java:258
#, java-format
msgid "Minimum up bandwidth limit is {0}KBps"
msgstr "Минимально допустимое значение для лимита скорости отдачи: {0} KBps"
#: ../java/src/org/klomp/snark/SnarkManager.java:301
#: ../java/src/org/klomp/snark/SnarkManager.java:302
msgid "Cannot change the I2CP settings while torrents are active"
msgstr "Невозможно изменить настройки I2CP пока есть активные торренты"
#: ../java/src/org/klomp/snark/SnarkManager.java:307
#: ../java/src/org/klomp/snark/SnarkManager.java:308
msgid "Disconnecting old I2CP destination"
msgstr "Рассоединяемся по старому адресу I2CP"
#: ../java/src/org/klomp/snark/SnarkManager.java:311
#: ../java/src/org/klomp/snark/SnarkManager.java:312
#, java-format
msgid "I2CP settings changed to {0}"
msgstr "Новые параметры I2CP: {0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:315
#: ../java/src/org/klomp/snark/SnarkManager.java:316
msgid "Unable to connect with the new settings, reverting to the old I2CP settings"
msgstr "Не удалось соединиться с использованием новых настроек I2CP, возвращаемся к старым настройкам"
#: ../java/src/org/klomp/snark/SnarkManager.java:319
#: ../java/src/org/klomp/snark/SnarkManager.java:320
msgid "Unable to reconnect with the old settings!"
msgstr "Не удалось пересоединиться с использованием старых настроек I2CP!"
#: ../java/src/org/klomp/snark/SnarkManager.java:321
#: ../java/src/org/klomp/snark/SnarkManager.java:322
msgid "Reconnected on the new I2CP destination"
msgstr "Пересоединились по новому адресу I2CP"
#: ../java/src/org/klomp/snark/SnarkManager.java:332
#: ../java/src/org/klomp/snark/SnarkManager.java:333
#, java-format
msgid "I2CP listener restarted for \"{0}\""
msgstr "I2CP-приёмник перезапущен для \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:343
#: ../java/src/org/klomp/snark/SnarkManager.java:344
msgid "Enabled autostart"
msgstr "Автостарт включен"
#: ../java/src/org/klomp/snark/SnarkManager.java:345
#: ../java/src/org/klomp/snark/SnarkManager.java:346
msgid "Disabled autostart"
msgstr "Автостарт выключен"
#: ../java/src/org/klomp/snark/SnarkManager.java:351
#: ../java/src/org/klomp/snark/SnarkManager.java:352
msgid "Enabled open trackers - torrent restart required to take effect."
msgstr "Включено использование открытых трекеров. Требуется перезапуск торрента, чтобы изменения вступили в силу."
#: ../java/src/org/klomp/snark/SnarkManager.java:353
#: ../java/src/org/klomp/snark/SnarkManager.java:354
msgid "Disabled open trackers - torrent restart required to take effect."
msgstr "Отключено использование открытых трекеров. Требуется перезапуск торрента, чтобы изменения вступили в силу."
#: ../java/src/org/klomp/snark/SnarkManager.java:360
#: ../java/src/org/klomp/snark/SnarkManager.java:361
msgid "Open Tracker list changed - torrent restart required to take effect."
msgstr "Изменен список открытых трекеров. Требуется перезапуск торрента, чтобы изменения вступили в силу."
#: ../java/src/org/klomp/snark/SnarkManager.java:367
#: ../java/src/org/klomp/snark/SnarkManager.java:368
msgid "Configuration unchanged."
msgstr "Настройки не изменились."
#: ../java/src/org/klomp/snark/SnarkManager.java:377
#: ../java/src/org/klomp/snark/SnarkManager.java:378
#, java-format
msgid "Unable to save the config to {0}"
msgstr "Не удалось сохранить настройки в {0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:395
#: ../java/src/org/klomp/snark/SnarkManager.java:396
msgid "Connecting to I2P"
msgstr "Устанавливается соединение с I2P"
#: ../java/src/org/klomp/snark/SnarkManager.java:398
#: ../java/src/org/klomp/snark/SnarkManager.java:399
msgid "Error connecting to I2P - check your I2CP settings!"
msgstr "Ошибка соединения с I2P, проверьте настройки I2CP!"
#: ../java/src/org/klomp/snark/SnarkManager.java:407
#: ../java/src/org/klomp/snark/SnarkManager.java:408
#, java-format
msgid "Error: Could not add the torrent {0}"
msgstr "Ошибка: Не удалось добавить торрент {0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:446
#: ../java/src/org/klomp/snark/SnarkManager.java:434
#, java-format
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", will announce to i2p open trackers only"
msgstr "Внимание: указанные в \"{0}\" не-i2p трекеры будут проигнорированы, будут использоваться только открытые i2p трекеры"
#: ../java/src/org/klomp/snark/SnarkManager.java:436
#, java-format
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", and open trackers are disabled, you must enable open trackers before starting the torrent!"
msgstr "Внимание: указанные в \"{0}\" не-i2p трекеры будут проигнорированы, однако использование открытых i2p трекеров отключено, Вы должны включить поддержку открытых i2p трекеров перед запуском этого торрента!"
#: ../java/src/org/klomp/snark/SnarkManager.java:455
#, java-format
msgid "Torrent in \"{0}\" is invalid"
msgstr "Торрент в \"{0}\" некорректен"
#: ../java/src/org/klomp/snark/SnarkManager.java:461
#: ../java/src/org/klomp/snark/SnarkManager.java:470
#, java-format
msgid "Torrent added and started: \"{0}\""
msgstr "Торрент добавлен и запущен: \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:463
#: ../java/src/org/klomp/snark/SnarkManager.java:472
#, java-format
msgid "Torrent added: \"{0}\""
msgstr "Торрент добавлен: \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:559
#, java-format
msgid "Non-i2p tracker in \"{0}\", deleting it from our list of trackers!"
msgstr "Обнаружен не-I2P трекер в торренте \"{0}\", удаляем его из нашего списка трекеров!"
#: ../java/src/org/klomp/snark/SnarkManager.java:562
#: ../java/src/org/klomp/snark/SnarkManager.java:569
#, java-format
msgid "Too many files in \"{0}\" ({1}), deleting it!"
msgstr "Слишком много файлов в торренте \"{0}\" ({1}), удаляем его!"
#: ../java/src/org/klomp/snark/SnarkManager.java:564
#: ../java/src/org/klomp/snark/SnarkManager.java:571
#, java-format
msgid "Torrent file \"{0}\" cannot end in \".torrent\", deleting it!"
msgstr "Торрент \"{0}\" содержит единственный файл заканчивающийся на \".torrent\", удаляем его!"
#: ../java/src/org/klomp/snark/SnarkManager.java:566
#: ../java/src/org/klomp/snark/SnarkManager.java:573
#, java-format
msgid "No pieces in \"{0}\", deleting it!"
msgstr "В торренте \"{0}\" не оказалось ни одной части, удаляем его!"
#: ../java/src/org/klomp/snark/SnarkManager.java:568
#: ../java/src/org/klomp/snark/SnarkManager.java:575
#, java-format
msgid "Too many pieces in \"{0}\", limit is {1}, deleting it!"
msgstr "Слишком много частей в торренте \"{0}\" (наш предел {1}), удаляем его!"
#: ../java/src/org/klomp/snark/SnarkManager.java:570
#: ../java/src/org/klomp/snark/SnarkManager.java:577
#, java-format
msgid "Pieces are too large in \"{0}\" ({1}B), deleting it."
msgstr "Слишком крупные части в торренте \"{0}\" ({1}B), удаляем его."
#: ../java/src/org/klomp/snark/SnarkManager.java:571
#: ../java/src/org/klomp/snark/SnarkManager.java:578
#, java-format
msgid "Limit is {0}B"
msgstr "Наш предел {0}B"
#: ../java/src/org/klomp/snark/SnarkManager.java:579
#: ../java/src/org/klomp/snark/SnarkManager.java:586
#, java-format
msgid "Torrents larger than {0}B are not supported yet, deleting \"{1}\""
msgstr "Торренты крупнее чем {0}B пока не поддерживается, удаляем \"{1}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:595
#: ../java/src/org/klomp/snark/SnarkManager.java:602
#, java-format
msgid "Error: Could not remove the torrent {0}"
msgstr "Ошибка: Невозможно удалить торрент {0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:616
#: ../java/src/org/klomp/snark/SnarkManager.java:623
#, java-format
msgid "Torrent stopped: \"{0}\""
msgstr "Торрент остановлен: \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:631
#: ../java/src/org/klomp/snark/SnarkManager.java:638
#, java-format
msgid "Torrent removed: \"{0}\""
msgstr "Торрент удален: \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:664
#: ../java/src/org/klomp/snark/SnarkManager.java:671
#, java-format
msgid "Download finished: \"{0}\""
msgstr "Завершена загрузка: \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:664
#: ../java/src/org/klomp/snark/SnarkManager.java:671
#, java-format
msgid "size: {0}B"
msgstr "размер: {0}B"
#: ../java/src/org/klomp/snark/SnarkManager.java:692
#: ../java/src/org/klomp/snark/SnarkManager.java:699
msgid "Unable to connect to I2P!"
msgstr "Не удалось установить соединение с I2P!"
@ -207,7 +212,7 @@ msgid "Refresh page"
msgstr "Обновить страницу"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:97
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:656
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:669
msgid "I2PSnark"
msgstr "I2PSnark"
@ -291,13 +296,13 @@ msgid "Torrent file {0} does not exist"
msgstr "Торрент {0} не существует"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:237
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:990
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1003
#, java-format
msgid "Torrent already running: {0}"
msgstr "Торрент уже запущен: {0}"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:239
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:992
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1005
#, java-format
msgid "Torrent already in the queue: {0}"
msgstr "Торрент уже в очереди: {0}"
@ -394,254 +399,264 @@ msgstr "Соединение с I2P закрыто."
msgid "Opening the I2P tunnel and starting all torrents."
msgstr "Соединяемся с I2P и запускаем все торренты."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:502
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:670
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:683
msgid "Unknown"
msgstr "Неизвестный"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:513
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:508
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:516
msgid "TrackerErr"
msgstr "ОшибкаТрекера"
# TODO should replace "uploader limit NN peers" with "global number of upload slots: NN"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:507
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:519
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:528
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:534
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:862
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:510
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:522
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:531
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:537
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
msgid "peers"
msgstr "пир."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:517
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:520
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
msgid "Seeding"
msgstr "Раздается"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:523
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:526
msgid "Complete"
msgstr "Завершен"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:526
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:529
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
msgid "OK"
msgstr "Загружается"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:532
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:535
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
msgid "Stalled"
msgstr "Простаивает"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:538
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:541
msgid "No Peers"
msgstr "Нет Пиров"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:540
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:543
msgid "Stopped"
msgstr "Остановлен"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:553
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:556
msgid "View files"
msgstr "Открыть директорию"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:555
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:558
msgid "Open file"
msgstr "Открыть файл"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:579
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:781
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:582
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:794
msgid "Tracker"
msgstr "Трекер"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:580
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:583
msgid "Details"
msgstr "Подробнее"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:614
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:617
msgid "Stop the torrent"
msgstr "Остановить торрент"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:616
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:619
msgid "Stop"
msgstr "Остановить"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:622
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:625
msgid "Start the torrent"
msgstr "Запустить торрент"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:624
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:627
msgid "Start"
msgstr "Запустить"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:629
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:632
msgid "Remove the torrent from the active list, deleting the .torrent file"
msgstr "Удалить торрент из списка и с диска"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:631
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
#, java-format
msgid "Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?"
msgstr "Вы действительно хотите удалить \\''{0}.torrent\\''? (загруженные файлы удаляться НЕ будут)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:639
msgid "Remove"
msgstr "Удалить"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:635
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:643
msgid "Delete the .torrent file and the associated data file(s)"
msgstr "Удалить торрент и стереть загруженные файлы"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:648
#, java-format
msgid "Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?"
msgstr "Вы действительно хотите удалить торрент \\''{0}\\'' и все загруженные файлы?"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:650
msgid "Delete"
msgstr "Стереть"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:680
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:693
msgid "Seed"
msgstr "Сид"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:698
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:711
msgid "Uninteresting (The peer has no pieces we need)"
msgstr "Uninteresting (У пира нет нужных нам частей торрента)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:700
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:713
msgid "Choked (The peer is not allowing us to request pieces)"
msgstr "Choked (Этот пир не позволяет нам запрашивать части торрента)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:714
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:727
msgid "Uninterested (We have no pieces the peer needs)"
msgstr "Uninterested (У нас нужных этому пиру частей торрента)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:716
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:729
msgid "Choking (We are not allowing the peer to request pieces)"
msgstr "Choking (Мы не позволяем этому пиру запрашивать у нас части торрента)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:743
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
msgid "Add Torrent"
msgstr "Добавить Торрент"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:745
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
msgid "From URL"
msgstr "Из URL"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:750
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:763
msgid "Add torrent"
msgstr "Добавить торрент"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:753
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
#, java-format
msgid "Alternately, you can copy .torrent files to the directory {0}."
msgstr "Ну или вы можете скопировать .torrent-файлы в директорию {0}."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:755
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:768
msgid "Removing a .torrent file will cause the torrent to stop."
msgstr "Удаление .torrent-файла приведет к остановке торрента."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:772
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:785
msgid "Create Torrent"
msgstr "Создать Торрент"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:775
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:788
msgid "Data to seed"
msgstr "Файлы для раздачи"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:779
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:792
msgid "File or directory to seed (must be within the specified path)"
msgstr "Файл или директория для раздачи (вводите только название файла или директории, указание абсолютных путей не поддерживается)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:783
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
msgid "Select a tracker"
msgstr "Выбрать трекер"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:809
msgid "or"
msgstr "или"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:799
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:812
msgid "Specify custom tracker announce URL"
msgstr "Задать URL анонсера вручную"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:802
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:815
msgid "Create torrent"
msgstr "Создать торрент"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:820
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:833
msgid "Configuration"
msgstr "Настройки"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:823
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
msgid "Data directory"
msgstr "Директория для файлов"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:826
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:839
msgid "Directory to store torrents and data"
msgstr "Директория, где будут храниться торренты и загружаемые файлы"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:828
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:841
msgid "Edit i2psnark.config and restart to change"
msgstr "Для изменения отредактируйте файл i2psnark.config и перезагрузите I2PSnark"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:832
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:845
msgid "Auto start"
msgstr "Автозапуск"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:849
msgid "If checked, automatically start torrents that are added"
msgstr "Автоматически запускать торренты после добавления"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:859
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:872
msgid "Total uploader limit"
msgstr "Ограничение количества слотов отдачи"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:866
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
msgid "Up bandwidth limit"
msgstr "Ограничение скорости отдачи"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:869
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:882
msgid "Half available bandwidth recommended."
msgstr "Рекомендуется использовать половину от доступной пропускной способности."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:871
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:884
msgid "View or change router bandwidth"
msgstr "Посмотреть/настроить ограничения скорости в маршрутизаторе I2P"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:888
msgid "Use open trackers also"
msgstr "Дополнительно использовать открытые трекеры"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:892
msgid "If checked, announce torrents to open trackers as well as the tracker listed in the torrent file"
msgstr "Анонсировать торренты на открытых трекерах, дополнительно к тем, что указаны внутри торрента"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:883
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:896
msgid "Open tracker announce URLs"
msgstr "URL открытых трекеров"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:894
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:907
msgid "I2CP host"
msgstr "Адрес I2CP"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:899
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
msgid "I2CP port"
msgstr "Порт I2CP"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:925
msgid "I2CP options"
msgstr "Параметры I2CP"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:917
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:930
msgid "Save configuration"
msgstr "Сохранить настройки"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:970
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:983
#, java-format
msgid "Torrent fetched from {0}"
msgstr "Получен торрент из: {0}"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:998
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
#, java-format
msgid "Torrent at {0} was not valid"
msgstr "Торрент полученный из {0} некорректен"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1003
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1016
#, java-format
msgid "Torrent was not retrieved from {0}"
msgstr "Не удалось получить торрент из: {0}"

View File

@ -8,9 +8,9 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P i2psnark\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-29 07:17+0000\n"
"PO-Revision-Date: 2010-01-29 15:30+0800\n"
"Last-Translator: walking <zhazhenzhong@gmail.com>\n"
"POT-Creation-Date: 2010-03-05 12:41+0000\n"
"PO-Revision-Date: 2010-03-05 21:58+0800\n"
"Last-Translator: walking <walking@mail.i2p>\n"
"Language-Team: foo <foo@bar>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -22,179 +22,189 @@ msgstr ""
msgid "Adding torrents in {0} minutes"
msgstr "{0}分钟内完成添加"
#: ../java/src/org/klomp/snark/SnarkManager.java:241
#: ../java/src/org/klomp/snark/SnarkManager.java:242
#, java-format
msgid "Total uploaders limit changed to {0}"
msgstr ""
#: ../java/src/org/klomp/snark/SnarkManager.java:243
#: ../java/src/org/klomp/snark/SnarkManager.java:244
#, java-format
msgid "Minimum total uploaders limit is {0}"
msgstr ""
#: ../java/src/org/klomp/snark/SnarkManager.java:255
#: ../java/src/org/klomp/snark/SnarkManager.java:256
#, java-format
msgid "Up BW limit changed to {0}KBps"
msgstr "上传带宽限制改为 {0} KBps"
#: ../java/src/org/klomp/snark/SnarkManager.java:257
#: ../java/src/org/klomp/snark/SnarkManager.java:258
#, java-format
msgid "Minimum up bandwidth limit is {0}KBps"
msgstr "最小上传带宽限制为 {0} KBps"
#: ../java/src/org/klomp/snark/SnarkManager.java:301
#: ../java/src/org/klomp/snark/SnarkManager.java:302
msgid "Cannot change the I2CP settings while torrents are active"
msgstr "正在下载/上传无法更改I2CP设置"
#: ../java/src/org/klomp/snark/SnarkManager.java:307
#: ../java/src/org/klomp/snark/SnarkManager.java:308
msgid "Disconnecting old I2CP destination"
msgstr "正在断开旧的I2CP目标"
#: ../java/src/org/klomp/snark/SnarkManager.java:311
#: ../java/src/org/klomp/snark/SnarkManager.java:312
#, java-format
msgid "I2CP settings changed to {0}"
msgstr "I2CP设置改为{0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:315
#: ../java/src/org/klomp/snark/SnarkManager.java:316
msgid "Unable to connect with the new settings, reverting to the old I2CP settings"
msgstr "无法通过新设置连接恢复I2CP的旧设置"
#: ../java/src/org/klomp/snark/SnarkManager.java:319
#: ../java/src/org/klomp/snark/SnarkManager.java:320
msgid "Unable to reconnect with the old settings!"
msgstr "旧设置也无法连接!"
#: ../java/src/org/klomp/snark/SnarkManager.java:321
#: ../java/src/org/klomp/snark/SnarkManager.java:322
msgid "Reconnected on the new I2CP destination"
msgstr "重新连接新I2CP目标"
#: ../java/src/org/klomp/snark/SnarkManager.java:332
#: ../java/src/org/klomp/snark/SnarkManager.java:333
#, java-format
msgid "I2CP listener restarted for \"{0}\""
msgstr "\"{0}\"的I2CP监听端口已启动"
#: ../java/src/org/klomp/snark/SnarkManager.java:343
#: ../java/src/org/klomp/snark/SnarkManager.java:344
msgid "Enabled autostart"
msgstr "启用自动启动"
#: ../java/src/org/klomp/snark/SnarkManager.java:345
#: ../java/src/org/klomp/snark/SnarkManager.java:346
msgid "Disabled autostart"
msgstr "禁用自动启动"
#: ../java/src/org/klomp/snark/SnarkManager.java:351
#: ../java/src/org/klomp/snark/SnarkManager.java:352
msgid "Enabled open trackers - torrent restart required to take effect."
msgstr "启用OpenTracker-重新启动种子后生效"
#: ../java/src/org/klomp/snark/SnarkManager.java:353
#: ../java/src/org/klomp/snark/SnarkManager.java:354
msgid "Disabled open trackers - torrent restart required to take effect."
msgstr "禁用OpenTracker - 重新启动种子后生效"
#: ../java/src/org/klomp/snark/SnarkManager.java:360
#: ../java/src/org/klomp/snark/SnarkManager.java:361
msgid "Open Tracker list changed - torrent restart required to take effect."
msgstr "OpenTracker列表已改变 - 重新启动种子后生效"
#: ../java/src/org/klomp/snark/SnarkManager.java:367
#: ../java/src/org/klomp/snark/SnarkManager.java:368
msgid "Configuration unchanged."
msgstr "设置未改变"
#: ../java/src/org/klomp/snark/SnarkManager.java:377
#: ../java/src/org/klomp/snark/SnarkManager.java:378
#, java-format
msgid "Unable to save the config to {0}"
msgstr "无法保存设置到{0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:395
#: ../java/src/org/klomp/snark/SnarkManager.java:396
msgid "Connecting to I2P"
msgstr "正在连接到I2P"
#: ../java/src/org/klomp/snark/SnarkManager.java:398
#: ../java/src/org/klomp/snark/SnarkManager.java:399
msgid "Error connecting to I2P - check your I2CP settings!"
msgstr "连接I2P时发生错误 - 请检查I2CP设置!"
#: ../java/src/org/klomp/snark/SnarkManager.java:407
#: ../java/src/org/klomp/snark/SnarkManager.java:408
#, java-format
msgid "Error: Could not add the torrent {0}"
msgstr "错误:无法添加种子{0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:446
#: ../java/src/org/klomp/snark/SnarkManager.java:430
#, java-format
msgid "Cannot open \"{0}\""
msgstr "无法打开 \"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:443
#, java-format
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", will announce to i2p open trackers only"
msgstr "警告 - 忽略\"{0}\"文件中I2P网络外的Tracker服务器文件将仅发布至 I2P 内的 Open Tracker 服务器。"
#: ../java/src/org/klomp/snark/SnarkManager.java:445
#, java-format
msgid "Warning - Ignoring non-i2p tracker in \"{0}\", and open trackers are disabled, you must enable open trackers before starting the torrent!"
msgstr "警告 - 忽略\"{0}\"文件中I2P网络外的Tracker服务器OpenTracker已禁用启动此种子前您必须启用OpenTracker。"
#: ../java/src/org/klomp/snark/SnarkManager.java:464
#, java-format
msgid "Torrent in \"{0}\" is invalid"
msgstr "无效种子 \"{0}\" "
#: ../java/src/org/klomp/snark/SnarkManager.java:461
#: ../java/src/org/klomp/snark/SnarkManager.java:479
#, java-format
msgid "Torrent added and started: \"{0}\""
msgstr "已添加并启动种子:\"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:463
#: ../java/src/org/klomp/snark/SnarkManager.java:481
#, java-format
msgid "Torrent added: \"{0}\""
msgstr "已添加种子:\"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:559
#, java-format
msgid "Non-i2p tracker in \"{0}\", deleting it from our list of trackers!"
msgstr "【匿名性警告】\"{0}\" 中含有非I2P Tracker程序将从Tracker列表中将其删除。"
#: ../java/src/org/klomp/snark/SnarkManager.java:562
#: ../java/src/org/klomp/snark/SnarkManager.java:578
#, java-format
msgid "Too many files in \"{0}\" ({1}), deleting it!"
msgstr "\"{0}\" ({1}) 含有太多文件,删除之!"
#: ../java/src/org/klomp/snark/SnarkManager.java:564
#: ../java/src/org/klomp/snark/SnarkManager.java:580
#, java-format
msgid "Torrent file \"{0}\" cannot end in \".torrent\", deleting it!"
msgstr "种子文件 \"{0}\" 不以 \".torrent\"结尾,正在删除!"
#: ../java/src/org/klomp/snark/SnarkManager.java:566
#: ../java/src/org/klomp/snark/SnarkManager.java:582
#, java-format
msgid "No pieces in \"{0}\", deleting it!"
msgstr "\"{0}\" 中没有数据片,删除之!"
#: ../java/src/org/klomp/snark/SnarkManager.java:568
#: ../java/src/org/klomp/snark/SnarkManager.java:584
#, java-format
msgid "Too many pieces in \"{0}\", limit is {1}, deleting it!"
msgstr "\"{0}\" 中文件分片太多,限额为{1},删除之!"
#: ../java/src/org/klomp/snark/SnarkManager.java:570
#: ../java/src/org/klomp/snark/SnarkManager.java:586
#, java-format
msgid "Pieces are too large in \"{0}\" ({1}B), deleting it."
msgstr "\"{0}\" ({1}B) 中文件分片过大,删除之。"
#: ../java/src/org/klomp/snark/SnarkManager.java:571
#: ../java/src/org/klomp/snark/SnarkManager.java:587
#, java-format
msgid "Limit is {0}B"
msgstr "限额为 {0}B"
#: ../java/src/org/klomp/snark/SnarkManager.java:579
#: ../java/src/org/klomp/snark/SnarkManager.java:595
#, java-format
msgid "Torrents larger than {0}B are not supported yet, deleting \"{1}\""
msgstr "目前不支持大于{0}B 的种子,正在删除\"{1}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:595
#: ../java/src/org/klomp/snark/SnarkManager.java:611
#, java-format
msgid "Error: Could not remove the torrent {0}"
msgstr "错误:无法删除种子{0}"
#: ../java/src/org/klomp/snark/SnarkManager.java:616
#: ../java/src/org/klomp/snark/SnarkManager.java:632
#, java-format
msgid "Torrent stopped: \"{0}\""
msgstr "种子已停止:\"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:631
#: ../java/src/org/klomp/snark/SnarkManager.java:647
#, java-format
msgid "Torrent removed: \"{0}\""
msgstr "种子已删除:\"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:664
#: ../java/src/org/klomp/snark/SnarkManager.java:680
#, java-format
msgid "Download finished: \"{0}\""
msgstr "下载已完成:\"{0}\""
#: ../java/src/org/klomp/snark/SnarkManager.java:664
#: ../java/src/org/klomp/snark/SnarkManager.java:680
#, java-format
msgid "size: {0}B"
msgstr "大小:{0}B"
#: ../java/src/org/klomp/snark/SnarkManager.java:692
#: ../java/src/org/klomp/snark/SnarkManager.java:708
msgid "Unable to connect to I2P!"
msgstr "无法连接至I2P!"
@ -207,7 +217,7 @@ msgid "Refresh page"
msgstr "刷新页面"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:97
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:656
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:669
msgid "I2PSnark"
msgstr ""
@ -291,13 +301,13 @@ msgid "Torrent file {0} does not exist"
msgstr "种子文件{0}不存在"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:237
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:990
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1003
#, java-format
msgid "Torrent already running: {0}"
msgstr "种子已启动:{0}"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:239
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:992
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1005
#, java-format
msgid "Torrent already in the queue: {0}"
msgstr "种子排队中:{0}"
@ -394,257 +404,270 @@ msgstr "I2P隧道已关闭"
msgid "Opening the I2P tunnel and starting all torrents."
msgstr "正在打开I2P隧道并启动所有种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:502
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:670
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:683
msgid "Unknown"
msgstr "未知"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:505
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:513
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:508
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:516
msgid "TrackerErr"
msgstr "Tracker错误"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:507
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:509
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:519
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:528
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:534
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:862
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:510
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:512
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:522
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:531
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:537
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
msgid "peers"
msgstr "用户"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:517
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:521
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:520
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:524
msgid "Seeding"
msgstr "正做种"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:523
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:526
msgid "Complete"
msgstr "完成"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:526
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:530
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:529
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:533
msgid "OK"
msgstr "确定"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:532
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:536
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:535
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:539
msgid "Stalled"
msgstr "等待"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:538
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:541
msgid "No Peers"
msgstr "没有用户"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:540
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:543
msgid "Stopped"
msgstr "已停用"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:553
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:556
msgid "View files"
msgstr "浏览文件"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:555
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:558
msgid "Open file"
msgstr "打开文件"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:579
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:781
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:582
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:794
msgid "Tracker"
msgstr "Tracker服务器"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:580
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:583
msgid "Details"
msgstr "详情"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:614
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:617
msgid "Stop the torrent"
msgstr "停止种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:616
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:619
msgid "Stop"
msgstr "停止"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:622
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:625
msgid "Start the torrent"
msgstr "启动种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:624
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:627
msgid "Start"
msgstr "启动"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:629
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:632
msgid "Remove the torrent from the active list, deleting the .torrent file"
msgstr "取消下载任务并删除对应种子文件。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:631
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
#, java-format
msgid "Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?"
msgstr "您确定要删除文件“{0}.torrent”(下载的数据文件不会被删除)?"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:639
msgid "Remove"
msgstr "移除"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:635
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:643
msgid "Delete the .torrent file and the associated data file(s)"
msgstr "删除种子及所下载的文件"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:637
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:648
#, java-format
msgid "Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?"
msgstr "您确定要删除种子“{0}”(下载的数据文件会一并被删除)?"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:650
msgid "Delete"
msgstr "删除"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:680
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:693
msgid "Seed"
msgstr "种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:698
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:711
msgid "Uninteresting (The peer has no pieces we need)"
msgstr "无需要部分"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:700
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:713
msgid "Choked (The peer is not allowing us to request pieces)"
msgstr "拒绝请求"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:714
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:727
msgid "Uninterested (We have no pieces the peer needs)"
msgstr "无需要部分"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:716
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:729
msgid "Choking (We are not allowing the peer to request pieces)"
msgstr "拒绝请求"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:743
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:756
msgid "Add Torrent"
msgstr "添加种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:745
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:758
msgid "From URL"
msgstr "从URL"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:750
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:763
msgid "Add torrent"
msgstr "添加种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:753
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:766
#, java-format
msgid "Alternately, you can copy .torrent files to the directory {0}."
msgstr "或者您可以将.torrent文件复制到以下目录{0}."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:755
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:768
msgid "Removing a .torrent file will cause the torrent to stop."
msgstr "删除种子文件将导致中止该下载任务。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:772
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:785
msgid "Create Torrent"
msgstr "创建种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:775
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:788
msgid "Data to seed"
msgstr "做种数据"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:779
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:792
msgid "File or directory to seed (must be within the specified path)"
msgstr "做种文件或文件夹(必须下面为Snark指定的文件夹中)"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:783
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
msgid "Select a tracker"
msgstr "选择一个Tracker"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:796
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:809
msgid "or"
msgstr "或"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:799
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:812
msgid "Specify custom tracker announce URL"
msgstr "指定Open Tracker发布链接"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:802
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:815
msgid "Create torrent"
msgstr "创建种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:820
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:833
msgid "Configuration"
msgstr "设置"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:823
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
msgid "Data directory"
msgstr "数据文件夹"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:826
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:839
msgid "Directory to store torrents and data"
msgstr "种子及被做种文件的保存位置。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:828
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:841
msgid "Edit i2psnark.config and restart to change"
msgstr "编辑 i2psnark.config 并重启Snark后生效"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:832
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:845
msgid "Auto start"
msgstr "自动启动"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:836
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:849
msgid "If checked, automatically start torrents that are added"
msgstr "选中后Snark将自动启动已添加的所有种子。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:859
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:872
msgid "Total uploader limit"
msgstr ""
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:866
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
msgid "Up bandwidth limit"
msgstr "上传带宽限制"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:869
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:882
msgid "Half available bandwidth recommended."
msgstr "推荐设置为可用带宽的一半。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:871
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:884
msgid "View or change router bandwidth"
msgstr "浏览或修改路由器带宽"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:875
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:888
msgid "Use open trackers also"
msgstr "同时使用OpenTracker"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:879
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:892
msgid "If checked, announce torrents to open trackers as well as the tracker listed in the torrent file"
msgstr "选择后在OpenTracker及种子文件中的Tracker上同时发布。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:883
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:896
msgid "Open tracker announce URLs"
msgstr "Open Tracker发布链接"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:894
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:907
msgid "I2CP host"
msgstr "I2CP主机"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:899
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
msgid "I2CP port"
msgstr "I2CP端口"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:912
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:925
msgid "I2CP options"
msgstr "I2CP选项"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:917
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:930
msgid "Save configuration"
msgstr "保存设置"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:970
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:983
#, java-format
msgid "Torrent fetched from {0}"
msgstr "从{0}获取种子成功"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:998
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1011
#, java-format
msgid "Torrent at {0} was not valid"
msgstr "{0}的种子中有错误"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1003
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1016
#, java-format
msgid "Torrent was not retrieved from {0}"
msgstr "从{0}获得种子失败"
#~ msgid "Non-i2p tracker in \"{0}\", deleting it from our list of trackers!"
#~ msgstr ""
#~ "【匿名性警告】\"{0}\" 中含有非I2P Tracker程序将从Tracker列表中将其删除。"
#~ msgid "Custom tracker URL"
#~ msgstr "自定义TrackerURL"
#~ msgid "Configure"

View File

@ -61,6 +61,7 @@ import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.socks.I2PSOCKSIRCTunnel;
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
import net.i2p.i2ptunnel.streamr.StreamrConsumer;
import net.i2p.i2ptunnel.streamr.StreamrProducer;
@ -895,6 +896,39 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
}
/**
* Run an SOCKS IRC tunnel on the given port number
* @since 0.7.12
*/
public void runSOCKSIRCTunnel(String args[], Logging l) {
if (args.length >= 1 && args.length <= 2) {
int _port = -1;
try {
_port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
return;
}
boolean isShared = false;
if (args.length > 1)
isShared = "true".equalsIgnoreCase(args[1].trim());
ownDest = !isShared;
I2PTunnelTask task;
task = new I2PSOCKSIRCTunnel(_port, l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
} else {
l.log("sockstunnel <port>");
l.log(" creates a tunnel that distributes SOCKS requests.");
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
}
}
/**
* Streamr client
*

View File

@ -26,6 +26,7 @@ import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Base32;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
@ -46,8 +47,14 @@ import net.i2p.util.Translate;
* $method http://i2p/$b64key/$path $protocolVersion
* or
* $method /$site/$path $protocolVersion
* or (deprecated)
* $method /eepproxy/$site/$path $protocolVersion
* </pre>
*
* Note that http://i2p/$b64key/... and /eepproxy/$site/... are not recommended
* in browsers or other user-visible applications, as relative links will not
* resolve correctly, cookies won't work, etc.
*
* If the $site resolves with the I2P naming service, then it is directed towards
* that eepsite, otherwise it is directed towards this client's outproxy (typically
* "squid.i2p"). Only HTTP is supported (no HTTPS, ftp, mailto, etc). Both GET
@ -303,14 +310,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (method == null) { // first line (GET /base64/realaddr)
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Method is null for [" + line + "]");
_log.debug(getPrefix(requestId) + "First line [" + line + "]");
int pos = line.indexOf(" ");
if (pos == -1) break;
method = line.substring(0, pos);
// TODO use Java URL class to make all this simpler and more robust
// That will also fix IPV6 [a:b:c]
String request = line.substring(pos + 1);
if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
// what is this for ???
request = "http://i2p" + request;
} else if (request.startsWith("/eepproxy/")) {
// /eepproxy/foo.i2p/bar/baz.html HTTP/1.0
@ -322,6 +331,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
// "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0"
request = "http://" + uri + subRequest.substring(protopos);
} else if (request.toLowerCase().startsWith("http://i2p/")) {
// http://i2p/b64key/bar/baz.html HTTP/1.0
String subRequest = request.substring("http://i2p/".length());
int protopos = subRequest.indexOf(" ");
String uri = subRequest.substring(0, protopos);
if (uri.indexOf("/") == -1) {
uri = uri + "/";
}
// "http://" + "b64key/bar/baz.html" + " HTTP/1.0"
request = "http://" + uri + subRequest.substring(protopos);
}
pos = request.indexOf("//");
@ -334,6 +353,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
targetRequest = request;
// pos is the start of the path
pos = request.indexOf("/");
if (pos == -1) {
method = null;
@ -354,9 +374,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
if (host.toLowerCase().equals("proxy.i2p")) {
// Go through the various types of host names, set
// the host and destination variables accordingly,
// and transform the first line.
// For all i2p network hosts, ensure that the host is a
// Base 32 hostname so that we do not reveal our name for it
// in our addressbook (all naming is local),
// and it is removed from the request line.
if (host.length() >= 516 && host.indexOf(".") < 0) {
// http://b64key/bar/baz.html
destination = host;
host = getHostName(destination);
line = method + ' ' + request.substring(pos);
} else if (host.toLowerCase().equals("proxy.i2p")) {
// so we don't do any naming service lookups
destination = "proxy.i2p";
destination = host;
usingInternalServer = true;
} else if (host.toLowerCase().endsWith(".i2p")) {
// Destination gets the host name
@ -392,6 +425,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
// Key contains data, lets not ignore it
if (ahelperKey != null) {
// ahelperKey will be validated later
// Host resolvable only with addresshelper
if ( (host == null) || ("i2p".equals(host)) )
@ -401,12 +435,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
} else {
// Host resolvable from database, verify addresshelper key
// Silently bypass correct keys, otherwise alert
if (!host.equals(ahelperKey))
String destB64 = null;
try {
Destination dest = I2PTunnel.destFromName(host);
if (dest != null)
destB64 = dest.toBase64();
} catch (DataFormatException dfe) {}
if (destB64 != null && !destB64.equals(ahelperKey))
{
// Conflict: handle when URL reconstruction done
ahelperConflict = true;
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + host + "], specified key [" + ahelperKey + "].");
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
}
}
}
@ -428,14 +468,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
{
if (out != null) {
long alias = I2PAppContext.getGlobalContext().random().nextLong();
String trustedURL = protocol + uriPath + urlEncoding;
String conflictURL = protocol + alias + ".i2p/?" + initialFragments;
byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
out.write(header);
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
out.write(("<p></div>").getBytes());
writeFooter(out);
// convert ahelperKey to b32
String alias = getHostName(ahelperKey);
if (alias.equals("i2p")) {
// bad ahelperKey
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
writeErrorMessage(header, out, targetRequest, false, destination, null);
} else {
String trustedURL = protocol + uriPath + urlEncoding;
// Fixme - any path is lost
String conflictURL = protocol + alias + '/' + urlEncoding;
byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
out.write(header);
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
out.write(("<p></div>").getBytes());
writeFooter(out);
}
}
s.close();
return;
@ -482,6 +530,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
} else {
// what is left for here? a hostname with no dots, and != "i2p"
// and not a destination ???
// Perhaps something in privatehosts.txt ...
request = request.substring(pos + 1);
pos = request.indexOf("/");
if (pos < 0) {
@ -494,31 +545,49 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
return;
}
destination = request.substring(0, pos);
host = getHostName(destination);
line = method + " " + request.substring(pos);
} // end host name processing
if (port != 80 && !usingWWWProxy) {
if (out != null) {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
writeFooter(out);
}
s.close();
return;
}
boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
if (!isValid) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
method = null;
destination = null;
break;
} else if ((!usingWWWProxy) && (!usingInternalServer)) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "host=getHostName(" + destination + ")");
host = getHostName(destination); // hide original host
}
// don't do this, it forces yet another hostname lookup,
// and in all cases host was already set above
//if ((!usingWWWProxy) && (!usingInternalServer)) {
// String oldhost = host;
// host = getHostName(destination); // hide original host
// if (_log.shouldLog(Log.INFO))
// _log.info(getPrefix(requestId) + " oldhost " + oldhost + " newhost " + host + " dest " + destination);
//}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + "METHOD:" + method + ":");
_log.debug(getPrefix(requestId) + "PROTOC:" + protocol + ":");
_log.debug(getPrefix(requestId) + "HOST :" + host + ":");
_log.debug(getPrefix(requestId) + "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 + "\"");
}
// end first line processing
} else {
if (lowercaseLine.startsWith("host: ") && !usingWWWProxy) {
// Note that we only pass the original Host: line through to the outproxy
// But we don't create a Host: line if it wasn't sent to us
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix(requestId) + "Setting host = " + host);
@ -575,7 +644,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
if (method == null || destination == null) {
l.log("No HTTP method found in the request.");
//l.log("No HTTP method found in the request.");
if (out != null) {
if ("http://".equalsIgnoreCase(protocol))
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
@ -598,7 +667,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
return;
}
Destination clientDest = I2PTunnel.destFromName(destination);
// If the host is "i2p", the getHostName() lookup failed, don't try to
// look it up again as the naming service does not do negative caching
// so it will be slow.
Destination clientDest;
if ("i2p".equals(host))
clientDest = null;
else
clientDest = I2PTunnel.destFromName(destination);
if (clientDest == null) {
//l.log("Could not resolve " + destination + ".");
if (_log.shouldLog(Log.WARN))
@ -679,12 +757,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
/**
* @return b32hash.b32.i2p, or "i2p" on lookup failure.
* Prior to 0.7.12, returned b64 key
*/
private final static String getHostName(String host) {
if (host == null) return null;
if (host.length() == 60 && host.toLowerCase().endsWith(".b32.i2p"))
return host;
try {
Destination dest = I2PTunnel.destFromName(host);
if (dest == null) return "i2p";
return dest.toBase64();
return Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
} catch (DataFormatException dfe) {
return "i2p";
}
@ -858,8 +942,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
private final static String SUPPORTED_HOSTS[] = { "i2p", "www.i2p.com", "i2p."};
/** @param host ignored */
private static boolean isSupportedAddress(String host, String protocol) {
if ((host == null) || (protocol == null)) return false;
/****
* Let's not look up the name _again_
* and now that host is a b32, this was failing
*
boolean found = false;
String lcHost = host.toLowerCase();
for (int i = 0; i < SUPPORTED_HOSTS.length; i++) {
@ -876,7 +966,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
} catch (DataFormatException dfe) {
}
}
****/
return protocol.equalsIgnoreCase("http://");
}

View File

@ -82,10 +82,10 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
try {
i2ps = createI2PSocket(clientDest);
i2ps.setReadTimeout(readTimeout);
StringBuilder expectedPong = new StringBuilder();
Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in");
StringBuffer expectedPong = new StringBuffer();
Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in", true);
in.start();
Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out");
Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out", true);
out.start();
} catch (Exception ex) {
if (_log.shouldLog(Log.ERROR))
@ -117,13 +117,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
/*************************************************************************
*
*/
private class IrcInboundFilter implements Runnable {
public static class IrcInboundFilter implements Runnable {
private Socket local;
private I2PSocket remote;
private StringBuilder expectedPong;
private StringBuffer expectedPong;
IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuilder pong) {
public IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
local=_local;
remote=_remote;
expectedPong=pong;
@ -191,13 +191,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
/*************************************************************************
*
*/
private class IrcOutboundFilter implements Runnable {
public static class IrcOutboundFilter implements Runnable {
private Socket local;
private I2PSocket remote;
private StringBuilder expectedPong;
private StringBuffer expectedPong;
IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuilder pong) {
public IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
local=_local;
remote=_remote;
expectedPong=pong;
@ -266,7 +266,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
*
*/
public String inboundFilter(String s, StringBuilder expectedPong) {
public static String inboundFilter(String s, StringBuffer expectedPong) {
String field[]=s.split(" ",4);
String command;
@ -353,7 +353,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
return null;
}
public String outboundFilter(String s, StringBuilder expectedPong) {
public static String outboundFilter(String s, StringBuffer expectedPong) {
String field[]=s.split(" ",3);
String command;
@ -378,7 +378,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
"KICK",
"HELPME",
"RULES",
"TOPIC"
"TOPIC",
"ISON" // jIRCii uses this for a ping (response is 303)
};
if(field[0].length()==0)
@ -390,7 +391,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
command = field[0].toUpperCase();
if ("PING".equalsIgnoreCase(command)) {
if ("PING".equals(command)) {
// Most clients just send a PING and are happy with any old PONG. Others,
// like BitchX, actually expect certain behavior. It sends two different pings:
// "PING :irc.freshcoffee.i2p" and "PING 1234567890 127.0.0.1" (where the IP is the proxy)
@ -426,19 +427,19 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
return rv;
}
if ("PONG".equalsIgnoreCase(command))
if ("PONG".equals(command))
return "PONG 127.0.0.1"; // no way to know what the ircd to i2ptunnel server con is, so localhost works
// Allow all allowedCommands
for(int i=0;i<allowedCommands.length;i++)
{
if(allowedCommands[i].equalsIgnoreCase(command))
if(allowedCommands[i].equals(command))
return s;
}
// mIRC sends "NOTICE user :DCC Send file (IP)"
// in addition to the CTCP version
if("NOTICE".equalsIgnoreCase(command))
if("NOTICE".equals(command))
{
String msg = field[2];
if(msg.startsWith(":DCC "))
@ -447,7 +448,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
}
// Allow PRIVMSG, but block CTCP (except ACTION).
if("PRIVMSG".equalsIgnoreCase(command) || "NOTICE".equalsIgnoreCase(command))
if("PRIVMSG".equals(command) || "NOTICE".equals(command))
{
String msg;
msg = field[2];
@ -465,14 +466,16 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
return s;
}
if("USER".equalsIgnoreCase(command)) {
if("USER".equals(command)) {
int idx = field[2].lastIndexOf(":");
if(idx<0)
return "USER user hostname localhost :realname";
String realname = field[2].substring(idx+1);
String ret = "USER "+field[1]+" hostname localhost :"+realname;
return ret;
} else if ("QUIT".equalsIgnoreCase(command)) {
}
if ("QUIT".equals(command)) {
return "QUIT :leaving";
}

View File

@ -124,11 +124,14 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
if (initialI2PData != null) {
synchronized (slock) {
// this does not increment totalSent
i2pout.write(initialI2PData);
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
//i2pout.flush();
}
}
if (initialSocketData != null) {
// this does not increment totalReceived
out.write(initialSocketData);
}
if (_log.shouldLog(Log.DEBUG))
@ -150,6 +153,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
if (_log.shouldLog(Log.DEBUG))
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
+ " totalSent = " + totalSent + " job = " + onTimeout);
// should we only look at totalReceived?
if ( (totalSent <= 0) && (totalReceived <= 0) )
onTimeout.run();
}
@ -271,7 +275,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Flushing after sending " + len + " bytes through");
if (_log.shouldLog(Log.DEBUG))
_log.debug(direction + ": " + len + " bytes flushed through to "
_log.debug(direction + ": " + len + " bytes flushed through " + (_toI2P ? "to " : "from ")
+ i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6));
try {
Thread.sleep(I2PTunnel.PACKET_DELAY);

View File

@ -144,6 +144,8 @@ public class TunnelController implements Logging {
startIrcClient();
} else if("sockstunnel".equals(type)) {
startSocksClient();
} else if("socksirctunnel".equals(type)) {
startSocksIRCClient();
} else if("connectclient".equals(type)) {
startConnectClient();
} else if ("client".equals(type)) {
@ -211,6 +213,14 @@ public class TunnelController implements Logging {
_tunnel.runSOCKSTunnel(new String[] { listenPort, sharedClient }, this);
}
/** @since 0.7.12 */
private void startSocksIRCClient() {
setListenOn();
String listenPort = getListenPort();
String sharedClient = getSharedClient();
_tunnel.runSOCKSIRCTunnel(new String[] { listenPort, sharedClient }, this);
}
/*
* Streamr client is a UDP server, use the listenPort field for targetPort
* and the listenOnInterface field for the targetHost

View File

@ -0,0 +1,62 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.net.Socket;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
import net.i2p.i2ptunnel.Logging;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
/*
* Pipe SOCKS IRC connections through I2PTunnelIRCClient filtering,
* to get the best of both worlds:
*
* - SOCKS lets you specify the host so you don't have to set up
* a tunnel for each IRC server in advance
* - IRC filtering for security
*
* @since 0.7.12
* @author zzz
*/
public class I2PSOCKSIRCTunnel extends I2PSOCKSTunnel {
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PSOCKSIRCTunnel.class);
private static int __clientId = 0;
public I2PSOCKSIRCTunnel(int localPort, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(localPort, l, ownDest, notifyThis, tunnel);
setName(getLocalPort() + " -> SOCKSIRCTunnel");
}
/**
* Same as in I2PSOCKSTunnel, but run the filters from I2PTunnelIRCClient
* instead of I2PTunnelRunner
*/
@Override
protected void clientConnectionRun(Socket s) {
try {
_log.error("SOCKS IRC Tunnel Start");
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket(this);
StringBuffer expectedPong = new StringBuffer();
Thread in = new I2PAppThread(new I2PTunnelIRCClient.IrcInboundFilter(clientSock, destSock, expectedPong), "SOCKS IRC Client " + (++__clientId) + " in", true);
in.start();
Thread out = new I2PAppThread(new I2PTunnelIRCClient.IrcOutboundFilter(clientSock, destSock, expectedPong), "SOCKS IRC Client " + __clientId + " out", true);
out.start();
} catch (SOCKSException e) {
_log.error("Error from SOCKS connection", e);
closeSocket(s);
}
}
}

View File

@ -89,10 +89,10 @@ public class SOCKS5Server extends SOCKSServer {
int method = Method.NO_ACCEPTABLE_METHODS;
for (int i = 0; i < nMethods; ++i) {
method = in.readByte() & 0xff;
if (method == Method.NO_AUTH_REQUIRED) {
int meth = in.readByte() & 0xff;
if (meth == Method.NO_AUTH_REQUIRED) {
// That's fine, we do support this method
break;
method = meth;
}
}
@ -119,7 +119,7 @@ public class SOCKS5Server extends SOCKSServer {
int socksVer = in.readByte() & 0xff;
if (socksVer != SOCKS_VERSION_5) {
_log.debug("error in SOCKS5 request (protocol != 5? wtf?)");
throw new SOCKSException("Invalid protocol version in request");
throw new SOCKSException("Invalid protocol version in request: " + socksVer);
}
int command = in.readByte() & 0xff;

View File

@ -349,6 +349,7 @@ public class IndexBean {
return ( ("client".equals(type)) ||
("httpclient".equals(type)) ||
("sockstunnel".equals(type)) ||
("socksirctunnel".equals(type)) ||
("connectclient".equals(type)) ||
("streamrclient".equals(type)) ||
("ircclient".equals(type)));
@ -385,6 +386,7 @@ public class IndexBean {
else if ("server".equals(internalType)) return _("Standard server");
else if ("httpserver".equals(internalType)) return _("HTTP server");
else if ("sockstunnel".equals(internalType)) return _("SOCKS 4/4a/5 proxy");
else if ("socksirctunnel".equals(internalType)) return _("SOCKS IRC proxy");
else if ("connectclient".equals(internalType)) return _("CONNECT/SSL/HTTPS proxy");
else if ("ircserver".equals(internalType)) return _("IRC server");
else if ("streamrclient".equals(internalType)) return _("Streamr client");

View File

@ -250,7 +250,8 @@
}
%></div>
<% if (!"sockstunnel".equals(indexBean.getInternalType(curClient))) { %>
<% if (!("sockstunnel".equals(indexBean.getInternalType(curClient)) ||
"socksirctunnel".equals(indexBean.getInternalType(curClient)))) { %>
<div class="destinationField rowItem">
<label>
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient))) { %>
@ -288,6 +289,7 @@
<option value="httpclient">HTTP</option>
<option value="ircclient">IRC</option>
<option value="sockstunnel">SOCKS 4/4a/5</option>
<option value="socksirctunnel">SOCKS IRC</option>
<option value="connectclient">CONNECT</option>
<option value="streamrclient">Streamr</option>
</select>

View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P i2ptunnel\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-14 06:24+0000\n"
"PO-Revision-Date: 2010-01-18 18:40+0000\n"
"POT-Creation-Date: 2010-03-04 09:53+0000\n"
"PO-Revision-Date: 2010-03-04 12:28+0000\n"
"Last-Translator: 4get <forget@mail.i2p>\n"
"Language-Team: foo <foo@bar>\n"
"MIME-Version: 1.0\n"
@ -17,12 +17,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Russian\n"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:436
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:483
#, java-format
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"{1}\">here</a>."
msgstr "Для перехода по ссылке из локальной адресной книги, нажмите <a href=\"{0}\">здесь</a>. Для перехода по новой addresshelper-ссылке с временным присвоением ей случайного имени, нажмите <a href=\"{1}\">здесь</a>."
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>."
msgstr "Для перехода по ссылке из локальной адресной книги, нажмите <a href=\"{0}\">здесь</a>. Для перехода по новой addresshelper-ссылке, нажмите <a href=\"{1}\">здесь</a>."
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:802
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:886
msgid "Click a link below to look for an address helper by using a \"jump\" service:"
msgstr "Jump-сервисы, которые, возможно, знают нужную Вам addresshelper-ссылку:"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P i2ptunnel\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-29 07:17+0000\n"
"POT-Creation-Date: 2010-03-05 12:41+0000\n"
"PO-Revision-Date: 2010-01-29 15:31+0800\n"
"Last-Translator: walking <zhazhenzhong@gmail.com>\n"
"Language-Team: foo <foo@bar>\n"
@ -17,14 +17,23 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Chinese\n"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:436
#, java-format
msgid "To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"{1}\">here</a>."
msgstr "要访问您本地【地址簿】中规定的主机(相当与IP),请点击<a href=\"{0}\">这里</a>。要访问【地址助手】返回的主机请点<a href=\"{1}\">这里</a>(主机的域名会被临时强制替换)。"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:483
#, fuzzy, java-format
msgid ""
"To visit the destination in your host database, click <a href=\"{0}\">here</"
"a>. To visit the conflicting addresshelper destination, click <a href=\"{1}"
"\">here</a>."
msgstr ""
"要访问您本地【地址簿】中规定的主机(相当与IP),请点击<a href=\"{0}\">这里</"
"a>。要访问【地址助手】返回的主机请点<a href=\"{1}\">这里</a>(主机的域名会被临"
"时强制替换)。"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:802
msgid "Click a link below to look for an address helper by using a \"jump\" service:"
msgstr "请点击下面的链接通过【跳转(Jump)】服务提供的【地址助手】链接跳转至域名对应的主机:"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:886
msgid ""
"Click a link below to look for an address helper by using a \"jump\" service:"
msgstr ""
"请点击下面的链接通过【跳转(Jump)】服务提供的【地址助手】链接跳转至域名对应的"
"主机:"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:362
msgid "New Tunnel"
@ -195,7 +204,9 @@ msgid "Shared Client"
msgstr "共享客户端"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:243
msgid "(Share tunnels with other clients and irc/httpclients? Change requires restart of client proxy)"
msgid ""
"(Share tunnels with other clients and irc/httpclients? Change requires "
"restart of client proxy)"
msgstr "(与其他客户端例如IRC/HTTP共享隧道修改需要重新启动)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:247
@ -214,8 +225,12 @@ msgid "Advanced networking options"
msgstr "高级网络设置"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:255
msgid "(NOTE: when this client proxy is configured to share tunnels, then these options are for all the shared proxy clients!)"
msgstr "(注意:此客户代理被设置使用共享隧道时,这些设置将影响所有使用共享隧道的客户端!)"
msgid ""
"(NOTE: when this client proxy is configured to share tunnels, then these "
"options are for all the shared proxy clients!)"
msgstr ""
"(注意:此客户代理被设置使用共享隧道时,这些设置将影响所有使用共享隧道的客户"
"端!)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:257
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:268
@ -264,12 +279,14 @@ msgstr "隧道长度恒定(随机性无,性能稳定)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:303
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:314
msgid "+ 0-1 hop variance (medium additive randomisation, subtractive performance)"
msgid ""
"+ 0-1 hop variance (medium additive randomisation, subtractive performance)"
msgstr "隧道长度+ 0-1(随机性中,影响性能)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:307
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:318
msgid "+ 0-2 hop variance (high additive randomisation, subtractive performance)"
msgid ""
"+ 0-2 hop variance (high additive randomisation, subtractive performance)"
msgstr "隧道长度+ 0-2(随机性高,影响性能)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:311
@ -299,12 +316,15 @@ msgstr "出/入站隧道x1(带宽低,低可靠性)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:343
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:354
msgid "2 inbound, 2 outbound tunnels (standard bandwidth usage, standard reliability)"
msgid ""
"2 inbound, 2 outbound tunnels (standard bandwidth usage, standard "
"reliability)"
msgstr "出/入站隧道x2(带宽标准,标准稳定性)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:347
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:358
msgid "3 inbound, 3 outbound tunnels (higher bandwidth usage, higher reliability)"
msgid ""
"3 inbound, 3 outbound tunnels (higher bandwidth usage, higher reliability)"
msgstr "出/入站隧道x3(带宽高,高稳定性)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:356
@ -329,7 +349,8 @@ msgstr "备用隧道对x1 (低冗余,低资源占用)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:376
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:387
msgid "2 backup tunnels each direction (medium redundancy, medium resource usage)"
msgid ""
"2 backup tunnels each direction (medium redundancy, medium resource usage)"
msgstr "备用隧道对x2 (中冗余,中资源占用)"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:380
@ -428,8 +449,12 @@ msgstr "自定义选项"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:472
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:495
msgid "NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted."
msgstr "注意:如果当前隧道已经启动,设置需要【停止】并重新【启动】相应隧道后才能生效。"
msgid ""
"NOTE: If tunnel is currently running, most changes will not take effect "
"until tunnel is stopped and restarted."
msgstr ""
"注意:如果当前隧道已经启动,设置需要【停止】并重新【启动】相应隧道后才能生"
"效。"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editClient_jsp.java:474
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/editServer_jsp.java:497
@ -671,4 +696,3 @@ msgstr "目标"
#: ../jsp/WEB-INF/classes/net/i2p/i2ptunnel/jsp/index_jsp.java:365
msgid "New client tunnel"
msgstr "新建客户隧道"

View File

@ -110,10 +110,10 @@
</target>
<target name="cleandep" depends="clean" />
<target name="distclean" depends="clean">
<delete dir="./jettylib" />
<echo message="Not actually deleting the jetty libs (since they're so large)" />
</target>
<target name="reallyclean" depends="distclean">
<delete dir="./jettylib" />
</target>
<target name="totallyclean" depends="clean">
<delete dir="./jettylib" />

View File

@ -0,0 +1,617 @@
// ========================================================================
// $Id: Server.java,v 1.40 2005/10/21 13:52:11 gregwilkins Exp $
// Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.mortbay.jetty;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.mortbay.log.LogFactory;
import org.mortbay.http.HttpContext;
import org.mortbay.http.HttpServer;
import org.mortbay.jetty.servlet.ServletHttpContext;
import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.util.LogSupport;
import org.mortbay.util.Resource;
import org.mortbay.xml.XmlConfiguration;
/* ------------------------------------------------------------ */
/** The Jetty HttpServer.
*
* This specialization of org.mortbay.http.HttpServer adds knowledge
* about servlets and their specialized contexts. It also included
* support for initialization from xml configuration files
* that follow the XmlConfiguration dtd.
*
* HttpContexts created by Server are of the type
* org.mortbay.jetty.servlet.ServletHttpContext unless otherwise
* specified.
*
* This class also provides a main() method which starts a server for
* each config file passed on the command line. If the system
* property JETTY_NO_SHUTDOWN_HOOK is not set to true, then a shutdown
* hook is thread is registered to stop these servers.
*
* @see org.mortbay.xml.XmlConfiguration
* @see org.mortbay.jetty.servlet.ServletHttpContext
* @version $Revision: 1.40 $
* @author Greg Wilkins (gregw)
*/
public class Server extends HttpServer
{
static Log log = LogFactory.getLog(Server.class);
private String[] _webAppConfigurationClassNames =
new String[]{"org.mortbay.jetty.servlet.XMLConfiguration", "org.mortbay.jetty.servlet.JettyWebConfiguration"};
private String _configuration;
private String _rootWebApp;
private static ShutdownHookThread hookThread = new ShutdownHookThread();
/* ------------------------------------------------------------ */
/** Constructor.
*/
public Server()
{
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param configuration The filename or URL of the XML
* configuration file.
*/
public Server(String configuration)
throws IOException
{
this(Resource.newResource(configuration).getURL());
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param configuration The filename or URL of the XML
* configuration file.
*/
public Server(Resource configuration)
throws IOException
{
this(configuration.getURL());
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param configuration The filename or URL of the XML
* configuration file.
*/
public Server(URL configuration)
throws IOException
{
_configuration=configuration.toString();
Server.hookThread.add(this);
try
{
XmlConfiguration config=new XmlConfiguration(configuration);
config.configure(this);
}
catch(IOException e)
{
throw e;
}
catch(InvocationTargetException e)
{
log.warn(LogSupport.EXCEPTION,e.getTargetException());
throw new IOException("Jetty configuration problem: "+e.getTargetException());
}
catch(Exception e)
{
log.warn(LogSupport.EXCEPTION,e);
throw new IOException("Jetty configuration problem: "+e);
}
}
/* ------------------------------------------------------------ */
public boolean getStopAtShutdown()
{
return hookThread.contains(this);
}
/* ------------------------------------------------------------ */
public void setStopAtShutdown(boolean stop)
{
if (stop)
hookThread.add(this);
else
hookThread.remove(this);
}
/* ------------------------------------------------------------ */
/** Get the root webapp name.
* @return The name of the root webapp (eg. "root" for root.war).
*/
public String getRootWebApp()
{
return _rootWebApp;
}
/* ------------------------------------------------------------ */
/** Set the root webapp name.
* @param rootWebApp The name of the root webapp (eg. "root" for root.war).
*/
public void setRootWebApp(String rootWebApp)
{
_rootWebApp = rootWebApp;
}
/* ------------------------------------------------------------ */
/** Configure the server from an XML file.
* @param configuration The filename or URL of the XML
* configuration file.
*/
public void configure(String configuration)
throws IOException
{
URL url=Resource.newResource(configuration).getURL();
if (_configuration!=null && _configuration.equals(url.toString()))
return;
if (_configuration!=null)
throw new IllegalStateException("Already configured with "+_configuration);
try
{
XmlConfiguration config=new XmlConfiguration(url);
_configuration=url.toString();
config.configure(this);
}
catch(IOException e)
{
throw e;
}
catch(Exception e)
{
log.warn(LogSupport.EXCEPTION,e);
throw new IOException("Jetty configuration problem: "+e);
}
}
/* ------------------------------------------------------------ */
public String getConfiguration()
{
return _configuration;
}
/* ------------------------------------------------------------ */
/** Create a new ServletHttpContext.
* Ths method is called by HttpServer to creat new contexts. Thus
* calls to addContext or getContext that result in a new Context
* being created will return an
* org.mortbay.jetty.servlet.ServletHttpContext instance.
* @return ServletHttpContext
*/
protected HttpContext newHttpContext()
{
return new ServletHttpContext();
}
/* ------------------------------------------------------------ */
/** Create a new WebApplicationContext.
* Ths method is called by Server to creat new contexts for web
* applications. Thus calls to addWebApplication that result in
* a new Context being created will return an correct class instance.
* Derived class can override this method to create instance of its
* own class derived from WebApplicationContext in case it needs more
* functionality.
* @param webApp The Web application directory or WAR file.
* @return WebApplicationContext
*/
protected WebApplicationContext newWebApplicationContext(
String webApp
)
{
return new WebApplicationContext(webApp);
}
/* ------------------------------------------------------------ */
/** Add Web Application.
* @param contextPathSpec The context path spec. Which must be of
* the form / or /path/*
* @param webApp The Web application directory or WAR file.
* @return The WebApplicationContext
* @exception IOException
*/
public WebApplicationContext addWebApplication(String contextPathSpec,
String webApp)
throws IOException
{
return addWebApplication(null,contextPathSpec,webApp);
}
/* ------------------------------------------------------------ */
/** Add Web Application.
* @param virtualHost Virtual host name or null
* @param contextPathSpec The context path spec. Which must be of
* the form / or /path/*
* @param webApp The Web application directory or WAR file.
* @return The WebApplicationContext
* @exception IOException
*/
public WebApplicationContext addWebApplication(String virtualHost,
String contextPathSpec,
String webApp)
throws IOException
{
WebApplicationContext appContext =
newWebApplicationContext(webApp);
appContext.setContextPath(contextPathSpec);
addContext(virtualHost,appContext);
if(log.isDebugEnabled())log.debug("Web Application "+appContext+" added");
return appContext;
}
/* ------------------------------------------------------------ */
/** Add Web Applications.
* Add auto webapplications to the server. The name of the
* webapp directory or war is used as the context name. If a
* webapp is called "root" it is added at "/".
* @param webapps Directory file name or URL to look for auto webapplication.
* @exception IOException
*/
public WebApplicationContext[] addWebApplications(String webapps)
throws IOException
{
return addWebApplications(null,webapps,null,false);
}
/* ------------------------------------------------------------ */
/** Add Web Applications.
* Add auto webapplications to the server. The name of the
* webapp directory or war is used as the context name. If the
* webapp matches the rootWebApp it is added as the "/" context.
* @param host Virtual host name or null
* @param webapps Directory file name or URL to look for auto webapplication.
* @exception IOException
*/
public WebApplicationContext[] addWebApplications(String host,
String webapps)
throws IOException
{
return addWebApplications(host,webapps,null,false);
}
/* ------------------------------------------------------------ */
/** Add Web Applications.
* Add auto webapplications to the server. The name of the
* webapp directory or war is used as the context name. If the
* webapp matches the rootWebApp it is added as the "/" context.
* @param host Virtual host name or null
* @param webapps Directory file name or URL to look for auto
* webapplication.
* @param extract If true, extract war files
* @exception IOException
*/
public WebApplicationContext[] addWebApplications(String host,
String webapps,
boolean extract)
throws IOException
{
return addWebApplications(host,webapps,null,extract);
}
/* ------------------------------------------------------------ */
/** Add Web Applications.
* Add auto webapplications to the server. The name of the
* webapp directory or war is used as the context name. If the
* webapp matches the rootWebApp it is added as the "/" context.
* @param host Virtual host name or null
* @param webapps Directory file name or URL to look for auto
* webapplication.
* @param defaults The defaults xml filename or URL which is
* loaded before any in the web app. Must respect the web.dtd.
* If null the default defaults file is used. If the empty string, then
* no defaults file is used.
* @param extract If true, extract war files
* @exception IOException
*/
public WebApplicationContext[] addWebApplications(String host,
String webapps,
String defaults,
boolean extract)
throws IOException
{
return addWebApplications(host,webapps,defaults,extract,true,null);
}
/* ------------------------------------------------------------ */
/** Add Web Applications.
* Add auto webapplications to the server. The name of the
* webapp directory or war is used as the context name. If the
* webapp matches the rootWebApp it is added as the "/" context.
* @param host Virtual host name or null
* @param webapps Directory file name or URL to look for auto
* webapplication.
* @param defaults The defaults xml filename or URL which is
* loaded before any in the web app. Must respect the web.dtd.
* If null the default defaults file is used. If the empty string, then
* no defaults file is used.
* @param extract If true, extract war files
* @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
* @exception IOException
*/
public WebApplicationContext[] addWebApplications(String host,
String webapps,
String defaults,
boolean extract,
boolean java2CompliantClassLoader)
throws IOException
{
return addWebApplications(host,webapps,defaults,extract,java2CompliantClassLoader,null);
}
/* ------------------------------------------------------------ */
/** Add Web Applications.
* Add auto webapplications to the server. The name of the
* webapp directory or war is used as the context name. If the
* webapp matches the rootWebApp it is added as the "/" context.
* @param host Virtual host name or null
* @param webapps Directory file name or URL to look for auto
* webapplication.
* @param defaults The defaults xml filename or URL which is
* loaded before any in the web app. Must respect the web.dtd.
* If null the default defaults file is used. If the empty string, then
* no defaults file is used.
* @param extract If true, extract war files
* @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
* @param Attributes[] A set of attributes to pass to setAttribute, format is first item is the key, second item is the value.
* @exception IOException
*/
public WebApplicationContext[] addWebApplications(String host,
String webapps,
String defaults,
boolean extract,
boolean java2CompliantClassLoader,
String Attributes[])
throws IOException
{
ArrayList wacs = new ArrayList();
Resource r=Resource.newResource(webapps);
if (!r.exists())
throw new IllegalArgumentException("No such webapps resource "+r);
if (!r.isDirectory())
throw new IllegalArgumentException("Not directory webapps resource "+r);
if(Attributes != null) {
if(((Attributes.length / 2) * 2) != Attributes.length) {
throw new IllegalArgumentException("Attributes must be in pairs of key,value.");
}
}
String[] files=r.list();
for (int f=0;files!=null && f<files.length;f++)
{
String context=files[f];
if (context.equalsIgnoreCase("CVS/") ||
context.equalsIgnoreCase("CVS") ||
context.startsWith("."))
continue;
String app = r.addPath(r.encode(files[f])).toString();
if (context.toLowerCase().endsWith(".war") ||
context.toLowerCase().endsWith(".jar"))
{
context=context.substring(0,context.length()-4);
Resource unpacked=r.addPath(context);
if (unpacked!=null && unpacked.exists() && unpacked.isDirectory())
continue;
}
if (_rootWebApp!=null && (context.equals(_rootWebApp)||context.equals(_rootWebApp+"/")))
context="/";
else
context="/"+context;
WebApplicationContext wac= addWebApplication(host,
context,
app);
wac.setExtractWAR(extract);
wac.setClassLoaderJava2Compliant(java2CompliantClassLoader);
if (defaults!=null)
{
if (defaults.length()==0)
wac.setDefaultsDescriptor(null);
else
wac.setDefaultsDescriptor(defaults);
}
if(Attributes != null) {
for(int i = 0; i < Attributes.length; i++, i++) {
wac.setAttribute(Attributes[i],Attributes[i + 1]);
}
}
wacs.add(wac);
}
return (WebApplicationContext[])wacs.toArray(new WebApplicationContext[wacs.size()]);
}
/* ------------------------------------------------------------ */
/** setWebApplicationConfigurationClasses
* Set up the list of classnames of WebApplicationContext.Configuration
* implementations that will be applied to configure every webapp.
* The list can be overridden by individual WebApplicationContexts.
* @param configurationClasses
*/
public void setWebApplicationConfigurationClassNames (String[] configurationClassNames)
{
if (configurationClassNames != null)
{
_webAppConfigurationClassNames = new String[configurationClassNames.length];
System.arraycopy(configurationClassNames, 0, _webAppConfigurationClassNames, 0, configurationClassNames.length);
}
}
public String[] getWebApplicationConfigurationClassNames ()
{
return _webAppConfigurationClassNames;
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static void main(String[] arg)
{
String[] dftConfig={"etc/jetty.xml"};
if (arg.length==0)
{
log.info("Using default configuration: etc/jetty.xml");
arg=dftConfig;
}
final Server[] servers=new Server[arg.length];
// create and start the servers.
for (int i=0;i<arg.length;i++)
{
try
{
servers[i] = new Server(arg[i]);
servers[i].setStopAtShutdown(true);
servers[i].start();
}
catch(Exception e)
{
log.warn(LogSupport.EXCEPTION,e);
}
}
// create and start the servers.
for (int i=0;i<arg.length;i++)
{
try{servers[i].join();}
catch (Exception e){LogSupport.ignore(log,e);}
}
}
/**
* ShutdownHook thread for stopping all servers.
*
* Thread is hooked first time list of servers is changed.
*/
private static class ShutdownHookThread extends Thread {
private boolean hooked = false;
private ArrayList servers = new ArrayList();
/**
* Hooks this thread for shutdown.
* @see java.lang.Runtime#addShutdownHook(java.lang.Thread)
*/
private void createShutdownHook() {
if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK") && !hooked) {
try {
Method shutdownHook = java.lang.Runtime.class.getMethod("addShutdownHook",
new Class[] { java.lang.Thread.class });
shutdownHook.invoke(Runtime.getRuntime(), new Object[] { this });
this.hooked = true;
} catch (Exception e) {
if (log.isDebugEnabled()) log.debug("No shutdown hook in JVM ", e);
}
}
}
/**
* Add Server to servers list.
*/
public boolean add(Server server) {
createShutdownHook();
return this.servers.add(server);
}
/**
* Contains Server in servers list?
*/
public boolean contains(Server server) {
return this.servers.contains(server);
}
/**
* Append all Servers from Collection
*/
public boolean addAll(Collection c) {
createShutdownHook();
return this.servers.addAll(c);
}
/**
* Clear list of Servers.
*/
public void clear() {
createShutdownHook();
this.servers.clear();
}
/**
* Remove Server from list.
*/
public boolean remove(Server server) {
createShutdownHook();
return this.servers.remove(server);
}
/**
* Remove all Servers in Collection from list.
*/
public boolean removeAll(Collection c) {
createShutdownHook();
return this.servers.removeAll(c);
}
/**
* Stop all Servers in list.
*/
public void run() {
setName("Shutdown");
log.info("Shutdown hook executing");
Iterator it = servers.iterator();
while (it.hasNext()) {
Server svr = (Server) it.next();
if (svr == null) continue;
try {
svr.stop();
} catch (Exception e) {
log.warn(LogSupport.EXCEPTION, e);
}
log.info("Shutdown hook complete");
// Try to avoid JVM crash
try {
Thread.sleep(1000);
} catch (Exception e) {
log.warn(LogSupport.EXCEPTION, e);
}
}
}
}
}

View File

@ -64,13 +64,16 @@
<target name="jar" depends="compile">
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class">
<manifest>
<attribute name="Class-Path" value="i2p.jar router.jar" />
<!-- top level installer will rename to jrobin.jar -->
<attribute name="Class-Path" value="i2p.jar router.jar jrobin.jar" />
</manifest>
</jar>
<delete dir="./tmpextract" />
<!-- jrobin taken out of routerconsole.jar in 0.7.12
<unjar src="../../jrobin/jrobin-1.4.0.jar" dest="./tmpextract" />
<jar destfile="./build/routerconsole.jar" basedir="./tmpextract" update="true" />
<delete dir="./tmpextract" />
-->
<ant target="war" />

View File

@ -39,7 +39,8 @@ ROUTERFILES="\
../../../router/java/src/net/i2p/router/transport/TransportManager.java \
../../../router/java/src/net/i2p/router/transport/GetBidsJob.java \
../../../router/java/src/net/i2p/router/Blocklist.java \
../../../router/java/src/net/i2p/router/transport/ntcp/EstablishState.java"
../../../router/java/src/net/i2p/router/transport/ntcp/EstablishState.java \
../../../router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java"
# add ../java/ so the refs will work in the po file
JPATHS="../java/src ../jsp/WEB-INF ../java/strings $JFILE $ROUTERFILES"

View File

@ -1,7 +1,7 @@
package net.i2p.router.web;
import java.io.File;
import java.util.Collection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -13,7 +13,6 @@ import net.i2p.router.startup.ClientAppConfig;
import net.i2p.router.startup.LoadClientAppsJob;
import net.i2p.util.Log;
import org.mortbay.http.HttpListener;
import org.mortbay.jetty.Server;
/**
@ -37,6 +36,14 @@ public class ConfigClientsHandler extends FormHandler {
saveWebAppChanges();
return;
}
if (_action.equals(_("Save Plugin Configuration"))) {
savePluginChanges();
return;
}
if (_action.equals(_("Install Plugin"))) {
installPlugin();
return;
}
// value
if (_action.startsWith("Start ")) {
String app = _action.substring(6);
@ -44,10 +51,15 @@ public class ConfigClientsHandler extends FormHandler {
try {
appnum = Integer.parseInt(app);
} catch (NumberFormatException nfe) {}
if (appnum >= 0)
if (appnum >= 0) {
startClient(appnum);
else
startWebApp(app);
} else {
List<String> plugins = PluginStarter.getPlugins();
if (plugins.contains(app))
startPlugin(app);
else
startWebApp(app);
}
return;
}
@ -58,10 +70,48 @@ public class ConfigClientsHandler extends FormHandler {
try {
appnum = Integer.parseInt(app);
} catch (NumberFormatException nfe) {}
if (appnum >= 0)
if (appnum >= 0) {
deleteClient(appnum);
} else {
try {
PluginStarter.stopPlugin(_context, app);
PluginStarter.deletePlugin(_context, app);
addFormNotice(_("Deleted plugin {0}", app));
} catch (Throwable e) {
addFormError(_("Error deleting plugin {0}", app) + ": " + e);
_log.error("Error deleting plugin " + app, e);
}
}
return;
}
// value
if (_action.startsWith("Stop ")) {
String app = _action.substring(5);
try {
PluginStarter.stopPlugin(_context, app);
addFormNotice(_("Stopped plugin {0}", app));
} catch (Throwable e) {
addFormError(_("Error stopping plugin {0}", app) + ": " + e);
_log.error("Error stopping plugin " + app, e);
}
return;
}
// value
if (_action.startsWith("Update ")) {
String app = _action.substring(7);
updatePlugin(app);
return;
}
// value
if (_action.startsWith("Check ")) {
String app = _action.substring(6);
checkPlugin(app);
return;
}
// label (IE)
String xStart = _("Start");
if (_action.toLowerCase().startsWith(xStart + "<span class=hide> ") &&
@ -72,13 +122,19 @@ public class ConfigClientsHandler extends FormHandler {
try {
appnum = Integer.parseInt(app);
} catch (NumberFormatException nfe) {}
if (appnum >= 0)
if (appnum >= 0) {
startClient(appnum);
else
startWebApp(app);
} else {
List<String> plugins = PluginStarter.getPlugins();
if (plugins.contains(app))
startPlugin(app);
else
startWebApp(app);
}
} else {
addFormError(_("Unsupported") + ' ' + _action + '.');
}
}
public void setSettings(Map settings) { _settings = new HashMap(settings); }
@ -91,7 +147,7 @@ public class ConfigClientsHandler extends FormHandler {
if (! ("webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName)))
ca.disabled = val == null;
// edit of an existing entry
String desc = getString("desc" + cur);
String desc = getJettyString("desc" + cur);
if (desc != null) {
int spc = desc.indexOf(" ");
String clss = desc;
@ -102,12 +158,12 @@ public class ConfigClientsHandler extends FormHandler {
}
ca.className = clss;
ca.args = args;
ca.clientName = getString("name" + cur);
ca.clientName = getJettyString("name" + cur);
}
}
int newClient = clients.size();
String newDesc = getString("desc" + newClient);
String newDesc = getJettyString("desc" + newClient);
if (newDesc != null && newDesc.trim().length() > 0) {
// new entry
int spc = newDesc.indexOf(" ");
@ -117,7 +173,7 @@ public class ConfigClientsHandler extends FormHandler {
clss = newDesc.substring(0, spc);
args = newDesc.substring(spc + 1);
}
String name = getString("name" + newClient);
String name = getJettyString("name" + newClient);
if (name == null || name.trim().length() <= 0) name = "new client";
ClientAppConfig ca = new ClientAppConfig(clss, name, args, 2*60*1000,
_settings.get(newClient + ".enabled") != null);
@ -130,7 +186,7 @@ public class ConfigClientsHandler extends FormHandler {
}
/** curses Jetty for returning arrays */
private String getString(String key) {
private String getJettyString(String key) {
String[] arr = (String[]) _settings.get(key);
if (arr == null)
return null;
@ -173,32 +229,109 @@ public class ConfigClientsHandler extends FormHandler {
props.setProperty(name, "" + (val != null));
}
RouterConsoleRunner.storeWebAppProperties(props);
addFormNotice(_("WebApp configuration saved successfully - restart required to take effect."));
addFormNotice(_("WebApp configuration saved."));
}
// Big hack for the moment, not using properties for directory and port
// Go through all the Jetty servers, find the one serving port 7657,
// requested and add the .war to that one
private void savePluginChanges() {
Properties props = PluginStarter.pluginProperties();
Set keys = props.keySet();
int cur = 0;
for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
if (! (name.startsWith(PluginStarter.PREFIX) && name.endsWith(PluginStarter.ENABLED)))
continue;
String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED));
Object val = _settings.get(app + ".enabled");
props.setProperty(name, "" + (val != null));
}
PluginStarter.storePluginProperties(props);
addFormNotice(_("Plugin configuration saved."));
}
/**
* Big hack for the moment, not using properties for directory and port
* Go through all the Jetty servers, find the one serving port 7657,
* requested and add the .war to that one
*/
private void startWebApp(String app) {
Collection c = Server.getHttpServers();
for (int i = 0; i < c.size(); i++) {
Server s = (Server) c.toArray()[i];
HttpListener[] hl = s.getListeners();
for (int j = 0; j < hl.length; j++) {
if (hl[j].getPort() == 7657) {
Server s = WebAppStarter.getConsoleServer();
if (s != null) {
try {
File path = new File(_context.getBaseDir(), "webapps");
path = new File(path, app + ".war");
s.addWebApplication("/"+ app, path.getAbsolutePath()).start();
// no passwords... initialize(wac);
WebAppStarter.startWebApp(_context, s, app, path.getAbsolutePath());
addFormNotice(_("WebApp") + " <a href=\"/" + app + "/\">" + _(app) + "</a> " + _("started") + '.');
} catch (Exception ioe) {
addFormError(_("Failed to start") + ' ' + _(app) + " " + ioe + '.');
} catch (Throwable e) {
addFormError(_("Failed to start") + ' ' + _(app) + " " + e + '.');
_log.error("Failed to start webapp " + app, e);
}
return;
}
}
}
addFormError(_("Failed to find server."));
}
private void installPlugin() {
String url = getJettyString("pluginURL");
if (url == null || url.length() <= 0) {
addFormError(_("No plugin URL specified."));
return;
}
installPlugin(url);
}
private void updatePlugin(String app) {
Properties props = PluginStarter.pluginProperties(_context, app);
String url = props.getProperty("updateURL");
if (url == null) {
addFormError(_("No update URL specified for {0}",app));
return;
}
installPlugin(url);
}
private void installPlugin(String url) {
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
addFormError(_("Plugin or update download already in progress."));
return;
}
PluginUpdateHandler puh = PluginUpdateHandler.getInstance(_context);
if (puh.isRunning()) {
addFormError(_("Plugin or update download already in progress."));
return;
}
puh.update(url);
addFormNotice(_("Downloading plugin from {0}", url));
// So that update() will post a status to the summary bar before we reload
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {}
}
private void checkPlugin(String app) {
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
addFormError(_("Plugin or update download already in progress."));
return;
}
PluginUpdateChecker puc = PluginUpdateChecker.getInstance(_context);
if (puc.isRunning()) {
addFormError(_("Plugin or update download already in progress."));
return;
}
puc.update(app);
addFormNotice(_("Checking plugin {0} for updates", app));
// So that update() will post a status to the summary bar before we reload
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {}
}
private void startPlugin(String app) {
try {
PluginStarter.startPlugin(_context, app);
addFormNotice(_("Started plugin {0}", app));
} catch (Throwable e) {
addFormError(_("Error starting plugin {0}", app) + ": " + e);
_log.error("Error starting plugin " + app, e);
}
}
}

View File

@ -1,5 +1,7 @@
package net.i2p.router.web;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
@ -33,18 +35,19 @@ public class ConfigClientsHelper extends HelperBase {
public String getForm1() {
StringBuilder buf = new StringBuilder(1024);
buf.append("<table>\n");
buf.append("<tr><th align=\"right\">" + _("Client") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Start Now") + "</th><th align=\"left\">" + _("Class and arguments") + "</th></tr>\n");
buf.append("<tr><th align=\"right\">" + _("Client") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Control") + "</th><th align=\"left\">" + _("Class and arguments") + "</th></tr>\n");
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
for (int cur = 0; cur < clients.size(); cur++) {
ClientAppConfig ca = clients.get(cur);
renderForm(buf, ""+cur, ca.clientName, false, !ca.disabled,
"webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName),
ca.className + ((ca.args != null) ? " " + ca.args : ""), (""+cur).equals(_edit), true);
ca.className + ((ca.args != null) ? " " + ca.args : ""), (""+cur).equals(_edit),
true, false, false, true);
}
if ("new".equals(_edit))
renderForm(buf, "" + clients.size(), "", false, false, false, "", true, false);
renderForm(buf, "" + clients.size(), "", false, false, false, "", true, false, false, false, false);
buf.append("</table>\n");
return buf.toString();
}
@ -52,7 +55,7 @@ public class ConfigClientsHelper extends HelperBase {
public String getForm2() {
StringBuilder buf = new StringBuilder(1024);
buf.append("<table>\n");
buf.append("<tr><th align=\"right\">" + _("WebApp") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Start Now") + "</th><th align=\"left\">" + _("Description") + "</th></tr>\n");
buf.append("<tr><th align=\"right\">" + _("WebApp") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Control") + "</th><th align=\"left\">" + _("Description") + "</th></tr>\n");
Properties props = RouterConsoleRunner.webAppProperties();
Set<String> keys = new TreeSet(props.keySet());
for (Iterator<String> iter = keys.iterator(); iter.hasNext(); ) {
@ -61,7 +64,92 @@ public class ConfigClientsHelper extends HelperBase {
String app = name.substring(RouterConsoleRunner.PREFIX.length(), name.lastIndexOf(RouterConsoleRunner.ENABLED));
String val = props.getProperty(name);
renderForm(buf, app, app, !"addressbook".equals(app),
"true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war", false, false);
"true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war",
false, false, false, false, false);
}
}
buf.append("</table>\n");
return buf.toString();
}
public boolean showPlugins() {
return PluginStarter.pluginsEnabled(_context);
}
public String getForm3() {
StringBuilder buf = new StringBuilder(1024);
buf.append("<table>\n");
buf.append("<tr><th align=\"right\">" + _("Plugin") + "</th><th>" + _("Run at Startup?") + "</th><th>" + _("Control") + "</th><th align=\"left\">" + _("Description") + "</th></tr>\n");
Properties props = PluginStarter.pluginProperties();
Set<String> keys = new TreeSet(props.keySet());
for (Iterator<String> iter = keys.iterator(); iter.hasNext(); ) {
String name = iter.next();
if (name.startsWith(PluginStarter.PREFIX) && name.endsWith(PluginStarter.ENABLED)) {
String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED));
String val = props.getProperty(name);
Properties appProps = PluginStarter.pluginProperties(_context, app);
if (appProps.size() <= 0)
continue;
StringBuilder desc = new StringBuilder(256);
desc.append("<table border=\"0\">")
.append("<tr><td><b>").append(_("Version")).append("<td>").append(stripHTML(appProps, "version"))
.append("<tr><td><b>")
.append(_("Signed by")).append("<td>");
String s = stripHTML(appProps, "signer");
if (s != null) {
if (s.indexOf("@") > 0)
desc.append("<a href=\"mailto:").append(s).append("\">").append(s).append("</a>");
else
desc.append(s);
}
s = stripHTML(appProps, "date");
if (s != null) {
long ms = 0;
try {
ms = Long.parseLong(s);
} catch (NumberFormatException nfe) {}
if (ms > 0) {
String date = (new SimpleDateFormat("yyyy-MM-dd HH:mm")).format(new Date(ms));
desc.append("<tr><td><b>")
.append(_("Date")).append("<td>").append(date);
}
}
s = stripHTML(appProps, "author");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("Author")).append("<td>");
if (s.indexOf("@") > 0)
desc.append("<a href=\"mailto:").append(s).append("\">").append(s).append("</a>");
else
desc.append(s);
}
s = stripHTML(appProps, "description_" + Messages.getLanguage(_context));
if (s == null)
s = stripHTML(appProps, "description");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("Description")).append("<td>").append(s);
}
s = stripHTML(appProps, "license");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("License")).append("<td>").append(s);
}
s = stripHTML(appProps, "websiteURL");
if (s != null) {
desc.append("<tr><td>")
.append("<a href=\"").append(s).append("\">").append(_("Website")).append("</a><td>&nbsp;");
}
String updateURL = stripHTML(appProps, "updateURL");
if (updateURL != null) {
desc.append("<tr><td>")
.append("<a href=\"").append(updateURL).append("\">").append(_("Update link")).append("</a><td>&nbsp;");
}
desc.append("</table>");
boolean enableStop = !Boolean.valueOf(appProps.getProperty("disableStop")).booleanValue();
renderForm(buf, app, app, false,
"true".equals(val), false, desc.toString(), false, false,
updateURL != null, enableStop, true);
}
}
buf.append("</table>\n");
@ -70,7 +158,9 @@ public class ConfigClientsHelper extends HelperBase {
/** ro trumps edit and showEditButton */
private void renderForm(StringBuilder buf, String index, String name, boolean urlify,
boolean enabled, boolean ro, String desc, boolean edit, boolean showEditButton) {
boolean enabled, boolean ro, String desc, boolean edit,
boolean showEditButton, boolean showUpdateButton, boolean showStopButton,
boolean showDeleteButton) {
buf.append("<tr><td class=\"mediumtags\" align=\"right\" width=\"25%\">");
if (urlify && enabled) {
String link = "/";
@ -92,13 +182,24 @@ public class ConfigClientsHelper extends HelperBase {
if (ro)
buf.append("disabled=\"true\" ");
}
buf.append("/></td><td align=\"center\" width=\"15%\">");
buf.append("></td><td align=\"center\" width=\"15%\">");
if ((!enabled) && !edit) {
buf.append("<button type=\"submit\" name=\"action\" value=\"Start ").append(index).append("\" >" + _("Start") + "<span class=hide> ").append(index).append("</span></button>");
}
if (showEditButton && (!edit) && !ro) {
if (showEditButton && (!edit) && !ro)
buf.append("<button type=\"submit\" name=\"edit\" value=\"Edit ").append(index).append("\" >" + _("Edit") + "<span class=hide> ").append(index).append("</span></button>");
buf.append("<button type=\"submit\" name=\"action\" value=\"Delete ").append(index).append("\" >" + _("Delete") + "<span class=hide> ").append(index).append("</span></button>");
if (showStopButton && (!edit))
buf.append("<button type=\"submit\" name=\"action\" value=\"Stop ").append(index).append("\" >" + _("Stop") + "<span class=hide> ").append(index).append("</span></button>");
if (showUpdateButton && (!edit) && !ro) {
buf.append("<button type=\"submit\" name=\"action\" value=\"Check ").append(index).append("\" >" + _("Check for updates") + "<span class=hide> ").append(index).append("</span></button>");
buf.append("<button type=\"submit\" name=\"action\" value=\"Update ").append(index).append("\" >" + _("Update") + "<span class=hide> ").append(index).append("</span></button>");
}
if (showDeleteButton && (!edit) && !ro) {
buf.append("<button type=\"submit\" name=\"action\" value=\"Delete ").append(index)
.append("\" onclick=\"if (!confirm('")
.append(_("Are you sure you want to delete {0}?", _(name)))
.append("')) { return false; }\">")
.append(_("Delete")).append("<span class=hide> ").append(index).append("</span></button>");
}
buf.append("</td><td align=\"left\" width=\"50%\">");
if (edit && !ro) {
@ -110,4 +211,16 @@ public class ConfigClientsHelper extends HelperBase {
}
buf.append("</td></tr>\n");
}
/**
* Like in DataHelper but doesn't convert null to ""
* There's a lot worse things a plugin could do but...
*/
static String stripHTML(Properties props, String key) {
String orig = props.getProperty(key);
if (orig == null) return null;
String t1 = orig.replace('<', ' ');
String rv = t1.replace('>', ' ');
return rv;
}
}

View File

@ -58,7 +58,7 @@ public class ConfigPeerHandler extends FormHandler {
}
addFormError(_("Invalid peer"));
} else if (_action.startsWith("Check")) {
addFormError("Unsupported");
addFormError(_("Unsupported"));
}
}

View File

@ -1,6 +1,7 @@
package net.i2p.router.web;
import java.io.File;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Set;
@ -19,6 +20,8 @@ public class ConfigUIHelper extends HelperBase {
return buf.toString();
}
static final String PROP_THEME_PFX = "routerconsole.theme.";
/** @return standard and user-installed themes, sorted (untranslated) */
private Set<String> themeSet() {
Set<String> rv = new TreeSet();
@ -33,6 +36,13 @@ public class ConfigUIHelper extends HelperBase {
if (files[i].isDirectory() && ! name.equals("images"))
rv.add(name);
}
// user themes
Set props = _context.getPropertyNames();
for (Iterator iter = props.iterator(); iter.hasNext(); ) {
String prop = (String) iter.next();
if (prop.startsWith(PROP_THEME_PFX) && prop.length() > PROP_THEME_PFX.length())
rv.add(prop.substring(PROP_THEME_PFX.length()));
}
return rv;
}

View File

@ -65,6 +65,10 @@ public class ConfigUpdateHandler extends FormHandler {
addFormNotice(_("Update available, attempting to download now"));
else
addFormNotice(_("Update available, click button on left to download"));
// So that update() will post a status to the summary bar before we reload
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {}
} else
addFormNotice(_("No update available"));
return;
@ -124,6 +128,7 @@ public class ConfigUpdateHandler extends FormHandler {
}
if ( (_trustedKeys != null) && (_trustedKeys.length() > 0) ) {
_trustedKeys = _trustedKeys.replaceAll("\r\n", ",").replaceAll("\n", ",");
String oldKeys = new TrustedUpdate(_context).getTrustedKeysString();
if ( (oldKeys == null) || (!_trustedKeys.equals(oldKeys)) ) {
_context.router().setConfigSetting(PROP_TRUSTED_KEYS, _trustedKeys);

View File

@ -160,7 +160,7 @@ public class FormHandler {
if ( (expected != null) && (expected.trim().length() > 0) && (expected.equals(_passphrase)) ) {
// ok
} else {
addFormError("Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.");
addFormError(_("Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit."));
_valid = false;
}
}

View File

@ -1,14 +1,13 @@
package net.i2p.router.web;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
public class NavHelper extends HelperBase {
private static Map _apps = new HashMap();
public NavHelper() {}
public class NavHelper {
private static Map<String, String> _apps = new ConcurrentHashMap();
/**
* To register a new client application so that it shows up on the router
@ -25,13 +24,16 @@ public class NavHelper extends HelperBase {
_apps.remove(name);
}
public String getClientAppLinks() {
/**
* Translated string is loaded by PluginStarter
*/
public static String getClientAppLinks(I2PAppContext ctx) {
StringBuilder buf = new StringBuilder(1024);
for (Iterator iter = _apps.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String path = (String)_apps.get(name);
buf.append("<a href=\"").append(path).append("\">");
buf.append(name).append("</a> |");
for (Iterator<String> iter = _apps.keySet().iterator(); iter.hasNext(); ) {
String name = iter.next();
String path = _apps.get(name);
buf.append(" <a target=\"_top\" href=\"").append(path).append("\">");
buf.append(name).append("</a>");
}
return buf.toString();
}

View File

@ -0,0 +1,434 @@
package net.i2p.router.web;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.router.startup.ClientAppConfig;
import net.i2p.router.startup.LoadClientAppsJob;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
import net.i2p.util.Translate;
import org.mortbay.jetty.Server;
/**
* Start/stop/delete plugins that are already installed
* Get properties of installed plugins
* Get or change settings in plugins.config
*
* @since 0.7.12
* @author zzz
*/
public class PluginStarter implements Runnable {
private RouterContext _context;
static final String PREFIX = "plugin.";
static final String ENABLED = ".startOnLoad";
private static final String[] STANDARD_WEBAPPS = { "i2psnark", "i2ptunnel", "susidns",
"susimail", "addressbook", "routerconsole" };
private static final String[] STANDARD_THEMES = { "images", "light", "dark", "classic",
"midnight" };
public PluginStarter(RouterContext ctx) {
_context = ctx;
}
static boolean pluginsEnabled(I2PAppContext ctx) {
return Boolean.valueOf(ctx.getProperty("router.enablePlugins", "true")).booleanValue();
}
public void run() {
startPlugins(_context);
}
/** this shouldn't throw anything */
static void startPlugins(RouterContext ctx) {
Log log = ctx.logManager().getLog(PluginStarter.class);
Properties props = pluginProperties();
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) {
if (Boolean.valueOf(props.getProperty(name)).booleanValue()) {
String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED));
try {
if (!startPlugin(ctx, app))
log.error("Failed to start plugin: " + app);
} catch (Throwable e) {
log.error("Failed to start plugin: " + app, e);
}
}
}
}
}
/**
* @return true on success
* @throws just about anything, caller would be wise to catch Throwable
*/
static boolean startPlugin(RouterContext ctx, String appName) throws Exception {
Log log = ctx.logManager().getLog(PluginStarter.class);
File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
log.error("Cannot start nonexistent plugin: " + appName);
return false;
}
//log.error("Starting plugin: " + appName);
// register themes
File dir = new File(pluginDir, "console/themes");
File[] tfiles = dir.listFiles();
if (tfiles != null) {
for (int i = 0; i < tfiles.length; i++) {
String name = tfiles[i].getName();
if (tfiles[i].isDirectory() && (!Arrays.asList(STANDARD_THEMES).contains(tfiles[i])))
ctx.router().setConfigSetting(ConfigUIHelper.PROP_THEME_PFX + name, tfiles[i].getAbsolutePath());
// we don't need to save
}
}
// load and start things in clients.config
File clientConfig = new File(pluginDir, "clients.config");
if (clientConfig.exists()) {
Properties props = new Properties();
DataHelper.loadProps(props, clientConfig);
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
runClientApps(ctx, pluginDir, clients, "start");
}
// start console webapps in console/webapps
Server server = WebAppStarter.getConsoleServer();
if (server != null) {
File consoleDir = new File(pluginDir, "console");
Properties props = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath());
File webappDir = new File(consoleDir, "webapps");
String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance());
if (fileNames != null) {
for (int i = 0; i < fileNames.length; i++) {
try {
String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war"));
//log.error("Found webapp: " + warName);
// check for duplicates in $I2P
if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
log.error("Skipping duplicate webapp " + warName + " in plugin " + appName);
continue;
}
String enabled = props.getProperty(PREFIX + warName + ENABLED);
if (! "false".equals(enabled)) {
//log.error("Starting webapp: " + warName);
String path = new File(webappDir, fileNames[i]).getCanonicalPath();
WebAppStarter.startWebApp(ctx, server, warName, path);
}
} catch (IOException ioe) {
log.error("Error resolving '" + fileNames[i] + "' in '" + webappDir, ioe);
}
}
}
}
// add translation jars in console/locale
// These will not override existing resource bundles since we are adding them
// later in the classpath.
File localeDir = new File(pluginDir, "console/locale");
if (localeDir.exists() && localeDir.isDirectory()) {
File[] files = localeDir.listFiles();
if (files != null) {
boolean added = false;
for (int i = 0; i < files.length; i++) {
File f = files[i];
if (f.getName().endsWith(".jar")) {
try {
addPath(f.toURI().toURL());
log.error("INFO: Adding translation plugin to classpath: " + f);
added = true;
} catch (Exception e) {
log.error("Plugin " + appName + " bad classpath element: " + f, e);
}
}
}
if (added)
Translate.clearCache();
}
}
// add summary bar link
Properties props = pluginProperties(ctx, appName);
String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
if (name == null)
name = ConfigClientsHelper.stripHTML(props, "consoleLinkName");
String url = ConfigClientsHelper.stripHTML(props, "consoleLinkURL");
if (name != null && url != null && name.length() > 0 && url.length() > 0)
NavHelper.registerApp(name, url);
return true;
}
/**
* @return true on success
* @throws just about anything, caller would be wise to catch Throwable
*/
static boolean stopPlugin(RouterContext ctx, String appName) throws IOException {
Log log = ctx.logManager().getLog(PluginStarter.class);
File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
log.error("Cannot stop nonexistent plugin: " + appName);
return false;
}
// stop things in clients.config
File clientConfig = new File(pluginDir, "clients.config");
if (clientConfig.exists()) {
Properties props = new Properties();
DataHelper.loadProps(props, clientConfig);
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
runClientApps(ctx, pluginDir, clients, "stop");
}
// stop console webapps in console/webapps
Server server = WebAppStarter.getConsoleServer();
if (server != null) {
File consoleDir = new File(pluginDir, "console");
Properties props = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath());
File webappDir = new File(consoleDir, "webapps");
String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance());
if (fileNames != null) {
for (int i = 0; i < fileNames.length; i++) {
String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war"));
if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
continue;
}
WebAppStarter.stopWebApp(server, warName);
}
}
}
// remove summary bar link
Properties props = pluginProperties(ctx, appName);
String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
if (name == null)
name = ConfigClientsHelper.stripHTML(props, "consoleLinkName");
if (name != null && name.length() > 0)
NavHelper.unregisterApp(name);
log.error("Stopping plugin: " + appName);
return true;
}
/** @return true on success - caller should call stopPlugin() first */
static boolean deletePlugin(RouterContext ctx, String appName) throws IOException {
Log log = ctx.logManager().getLog(PluginStarter.class);
File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
log.error("Cannot delete nonexistent plugin: " + appName);
return false;
}
// uninstall things in clients.config
File clientConfig = new File(pluginDir, "clients.config");
if (clientConfig.exists()) {
Properties props = new Properties();
DataHelper.loadProps(props, clientConfig);
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
runClientApps(ctx, pluginDir, clients, "uninstall");
}
// unregister themes, and switch to default if we are unregistering the current theme
File dir = new File(pluginDir, "console/themes");
File[] tfiles = dir.listFiles();
if (tfiles != null) {
String current = ctx.getProperty(CSSHelper.PROP_THEME_NAME);
for (int i = 0; i < tfiles.length; i++) {
String name = tfiles[i].getName();
if (tfiles[i].isDirectory() && (!name.equals("images")) && (!name.equals("classic")) &&
(!name.equals("dark")) && (!name.equals("light")) && (!name.equals("midnight"))) {
ctx.router().removeConfigSetting(ConfigUIHelper.PROP_THEME_PFX + name);
if (name.equals(current))
ctx.router().setConfigSetting(CSSHelper.PROP_THEME_NAME, CSSHelper.DEFAULT_THEME);
}
}
ctx.router().saveConfig();
}
FileUtil.rmdir(pluginDir, false);
Properties props = pluginProperties();
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
if (name.startsWith(PREFIX + appName))
iter.remove();
}
storePluginProperties(props);
return true;
}
/** plugin.config */
public static Properties pluginProperties(I2PAppContext ctx, String appName) {
File cfgFile = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName + '/' + "plugin.config");
Properties rv = new Properties();
try {
DataHelper.loadProps(rv, cfgFile);
} catch (IOException ioe) {}
return rv;
}
/**
* plugins.config
* this auto-adds a propery for every dir in the plugin directory
*/
public static Properties pluginProperties() {
File dir = I2PAppContext.getGlobalContext().getConfigDir();
Properties rv = new Properties();
File cfgFile = new File(dir, "plugins.config");
try {
DataHelper.loadProps(rv, cfgFile);
} catch (IOException ioe) {}
List<String> names = getPlugins();
for (String name : names) {
String prop = PREFIX + name + ENABLED;
if (rv.getProperty(prop) == null)
rv.setProperty(prop, "true");
}
return rv;
}
/**
* all installed plugins whether enabled or not
*/
public static List<String> getPlugins() {
List<String> rv = new ArrayList();
File pluginDir = new File(I2PAppContext.getGlobalContext().getAppDir(), PluginUpdateHandler.PLUGIN_DIR);
File[] files = pluginDir.listFiles();
if (files == null)
return rv;
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory())
rv.add(files[i].getName());
}
return rv;
}
/**
* The signing keys from all the plugins
* @return Map of key to keyname
* Last one wins if a dup (installer should prevent dups)
*/
public static Map<String, String> getPluginKeys(I2PAppContext ctx) {
Map<String, String> rv = new HashMap();
List<String> names = getPlugins();
for (String name : names) {
Properties props = pluginProperties(ctx, name);
String pubkey = props.getProperty("key");
String signer = props.getProperty("signer");
if (pubkey != null && signer != null && pubkey.length() == 172 && signer.length() > 0)
rv.put(pubkey, signer);
}
return rv;
}
/**
* plugins.config
*/
public static void storePluginProperties(Properties props) {
File cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), "plugins.config");
try {
DataHelper.storeProps(props, cfgFile);
} catch (IOException ioe) {}
}
/** @param action "start" or "stop" or "uninstall" */
private static void runClientApps(RouterContext ctx, File pluginDir, List<ClientAppConfig> apps, String action) {
Log log = ctx.logManager().getLog(PluginStarter.class);
for(ClientAppConfig app : apps) {
if (action.equals("start") && app.disabled)
continue;
String argVal[];
if (action.equals("start")) {
// start
argVal = LoadClientAppsJob.parseArgs(app.args);
} else {
String args;
if (action.equals("stop"))
args = app.stopargs;
else if (action.equals("uninstall"))
args = app.uninstallargs;
else
throw new IllegalArgumentException("bad action");
// args must be present
if (args == null || args.length() <= 0)
continue;
argVal = LoadClientAppsJob.parseArgs(args);
}
// do this after parsing so we don't need to worry about quoting
for (int i = 0; i < argVal.length; i++) {
if (argVal[i].indexOf("$") >= 0) {
argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
}
}
if (app.classpath != null) {
String cp = new String(app.classpath);
if (cp.indexOf("$") >= 0) {
cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath());
cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath());
}
addToClasspath(cp, app.clientName, log);
}
if (app.delay == 0 || !action.equals("start")) {
// run this guy now
LoadClientAppsJob.runClient(app.className, app.clientName, argVal, log);
} else {
// wait before firing it up
ctx.jobQueue().addJob(new LoadClientAppsJob.DelayedRunClient(ctx, app.className, app.clientName, argVal, app.delay));
}
}
}
/**
* Perhaps there's an easy way to use Thread.setContextClassLoader()
* but I don't see how to make it magically get used for everything.
* So add this to the whole JVM's classpath.
*/
private static void addToClasspath(String classpath, String clientName, Log log) {
StringTokenizer tok = new StringTokenizer(classpath, ",");
while (tok.hasMoreTokens()) {
String elem = tok.nextToken().trim();
File f = new File(elem);
if (!f.isAbsolute()) {
log.error("Plugin client " + clientName + " classpath element is not absolute: " + f);
continue;
}
try {
addPath(f.toURI().toURL());
log.error("INFO: Adding plugin to classpath: " + f);
} catch (Exception e) {
log.error("Plugin client " + clientName + " bad classpath element: " + f, e);
}
}
}
/**
* http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/
*/
public static void addPath(URL u) throws Exception {
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
}
}

View File

@ -0,0 +1,138 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.PartialEepGet;
import net.i2p.util.VersionComparator;
/**
* Download and install a plugin.
* A plugin is a standard .sud file with a 40-byte signature,
* a 16-byte version, and a .zip file.
* Unlike for router updates, we need not have the public key
* for the signature in advance.
*
* The zip file must have a standard directory layout, with
* a plugin.config file at the top level.
* The config file contains properties for the package name, version,
* signing public key, and other settings.
* The zip file will typically contain a webapps/ or lib/ dir,
* and a webapps.config and/or clients.config file.
*
* @since 0.7.12
* @author zzz
*/
public class PluginUpdateChecker extends UpdateHandler {
private static PluginUpdateCheckerRunner _pluginUpdateCheckerRunner;
private String _appName;
private String _oldVersion;
private String _xpi2pURL;
private static PluginUpdateChecker _instance;
public static final synchronized PluginUpdateChecker getInstance(RouterContext ctx) {
if (_instance != null)
return _instance;
_instance = new PluginUpdateChecker(ctx);
return _instance;
}
private PluginUpdateChecker(RouterContext ctx) {
super(ctx);
}
public void update(String appName) {
// don't block waiting for the other one to finish
if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS))) {
_log.error("Update already running");
return;
}
synchronized (UpdateHandler.class) {
Properties props = PluginStarter.pluginProperties(_context, appName);
String oldVersion = props.getProperty("version");
String xpi2pURL = props.getProperty("updateURL");
if (oldVersion == null || xpi2pURL == null) {
updateStatus("<b>" + _("Cannot check, plugin {0} is not installed", appName) + "</b>");
return;
}
if (_pluginUpdateCheckerRunner == null)
_pluginUpdateCheckerRunner = new PluginUpdateCheckerRunner();
if (_pluginUpdateCheckerRunner.isRunning())
return;
_xpi2pURL = xpi2pURL;
_appName = appName;
_oldVersion = oldVersion;
System.setProperty(PROP_UPDATE_IN_PROGRESS, "true");
I2PAppThread update = new I2PAppThread(_pluginUpdateCheckerRunner, "AppChecker", true);
update.start();
}
}
public boolean isRunning() {
return _pluginUpdateCheckerRunner != null && _pluginUpdateCheckerRunner.isRunning();
}
@Override
public boolean isDone() {
// FIXME
return false;
}
public class PluginUpdateCheckerRunner extends UpdateRunner implements Runnable, EepGet.StatusListener {
ByteArrayOutputStream _baos;
public PluginUpdateCheckerRunner() {
super();
_baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES);
}
@Override
protected void update() {
updateStatus("<b>" + _("Checking for update of plugin {0}", _appName) + "</b>");
// use the same settings as for updater
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
try {
_get = new PartialEepGet(_context, proxyHost, proxyPort, _baos, _xpi2pURL, TrustedUpdate.HEADER_BYTES);
_get.addStatusListener(PluginUpdateCheckerRunner.this);
_get.fetch();
} catch (Throwable t) {
_log.error("Error checking update for plugin", t);
}
}
@Override
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
}
@Override
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray()));
boolean newer = (new VersionComparator()).compare(newVersion, _oldVersion) > 0;
if (newer)
updateStatus("<b>" + _("New plugin version {0} is available", newVersion) + "</b>");
else
updateStatus("<b>" + _("No new version is available for plugin {0}", _appName) + "</b>");
}
@Override
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
File f = new File(_updateFile);
f.delete();
updateStatus("<b>" + _("Update check failed for plugin {0}", _appName) + "</b>");
}
}
}

View File

@ -0,0 +1,376 @@
package net.i2p.router.web;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.data.DataHelper;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.EepGet;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
import net.i2p.util.VersionComparator;
/**
* Download and install a plugin.
* A plugin is a standard .sud file with a 40-byte signature,
* a 16-byte version, and a .zip file.
* Unlike for router updates, we need not have the public key
* for the signature in advance.
*
* The zip file must have a standard directory layout, with
* a plugin.config file at the top level.
* The config file contains properties for the package name, version,
* signing public key, and other settings.
* The zip file will typically contain a webapps/ or lib/ dir,
* and a webapps.config and/or clients.config file.
*
* @since 0.7.12
* @author zzz
*/
public class PluginUpdateHandler extends UpdateHandler {
private static PluginUpdateRunner _pluginUpdateRunner;
private String _xpi2pURL;
private String _appStatus;
private static final String XPI2P = "app.xpi2p";
private static final String ZIP = XPI2P + ".zip";
public static final String PLUGIN_DIR = "plugins";
private static PluginUpdateHandler _instance;
public static final synchronized PluginUpdateHandler getInstance(RouterContext ctx) {
if (_instance != null)
return _instance;
_instance = new PluginUpdateHandler(ctx);
return _instance;
}
private PluginUpdateHandler(RouterContext ctx) {
super(ctx);
_appStatus = "";
}
public void update(String xpi2pURL) {
// don't block waiting for the other one to finish
if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS))) {
_log.error("Update already running");
return;
}
synchronized (UpdateHandler.class) {
if (_pluginUpdateRunner == null)
_pluginUpdateRunner = new PluginUpdateRunner(_xpi2pURL);
if (_pluginUpdateRunner.isRunning())
return;
_xpi2pURL = xpi2pURL;
_updateFile = (new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + XPI2P)).getAbsolutePath();
System.setProperty(PROP_UPDATE_IN_PROGRESS, "true");
I2PAppThread update = new I2PAppThread(_pluginUpdateRunner, "AppDownload");
update.start();
}
}
public String getAppStatus() {
return _appStatus;
}
public boolean isRunning() {
return _pluginUpdateRunner != null && _pluginUpdateRunner.isRunning();
}
@Override
public boolean isDone() {
// FIXME
return false;
}
public class PluginUpdateRunner extends UpdateRunner implements Runnable, EepGet.StatusListener {
public PluginUpdateRunner(String url) {
super();
}
@Override
protected void update() {
updateStatus("<b>" + _("Downloading plugin from {0}", _xpi2pURL) + "</b>");
// use the same settings as for updater
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
try {
if (shouldProxy)
// 10 retries!!
_get = new EepGet(_context, proxyHost, proxyPort, 10, _updateFile, _xpi2pURL, false);
else
_get = new EepGet(_context, 1, _updateFile, _xpi2pURL, false);
_get.addStatusListener(PluginUpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_log.error("Error downloading plugin", t);
}
}
@Override
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
StringBuilder buf = new StringBuilder(64);
buf.append("<b>").append(_("Downloading plugin")).append(' ');
double pct = ((double)alreadyTransferred + (double)currentWrite) /
((double)alreadyTransferred + (double)currentWrite + (double)bytesRemaining);
synchronized (_pct) {
buf.append(_pct.format(pct));
}
buf.append(": ");
buf.append(_("{0}B transferred", DataHelper.formatSize(currentWrite + alreadyTransferred)));
updateStatus(buf.toString());
}
@Override
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
updateStatus("<b>" + _("Plugin downloaded") + "</b>");
File f = new File(_updateFile);
File appDir = new File(_context.getAppDir(), PLUGIN_DIR);
if ((!appDir.exists()) && (!appDir.mkdir())) {
f.delete();
updateStatus("<b>" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + "</b>");
return;
}
TrustedUpdate up = new TrustedUpdate(_context);
File to = new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + ZIP);
// extract to a zip file whether the sig is good or not, so we can get the properties file
String err = up.migrateFile(f, to);
if (err != null) {
updateStatus("<b>" + err + ' ' + _("from {0}", url) + " </b>");
f.delete();
to.delete();
return;
}
File tempDir = new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + "-unzip");
if (!FileUtil.extractZip(to, tempDir)) {
f.delete();
to.delete();
FileUtil.rmdir(tempDir, false);
updateStatus("<b>" + _("Plugin from {0} is corrupt", url) + "</b>");
return;
}
File installProps = new File(tempDir, "plugin.config");
Properties props = new OrderedProperties();
try {
DataHelper.loadProps(props, installProps);
} catch (IOException ioe) {
f.delete();
to.delete();
FileUtil.rmdir(tempDir, false);
updateStatus("<b>" + _("Plugin from {0} does not contain the required configuration file", url) + "</b>");
return;
}
// we don't need this anymore, we will unzip again
FileUtil.rmdir(tempDir, false);
// ok, now we check sigs and deal with a bad sig
String pubkey = props.getProperty("key");
String signer = props.getProperty("signer");
if (pubkey == null || signer == null || pubkey.length() != 172 || signer.length() <= 0) {
f.delete();
to.delete();
//updateStatus("<b>" + "Plugin contains an invalid key" + ' ' + pubkey + ' ' + signer + "</b>");
updateStatus("<b>" + _("Plugin from {0} contains an invalid key", url) + "</b>");
return;
}
// add all existing plugin keys, so any conflicts with existing keys
// will be discovered and rejected
Map<String, String> existingKeys = PluginStarter.getPluginKeys(_context);
for (Map.Entry<String, String> e : existingKeys.entrySet()) {
// ignore dups/bad keys
up.addKey(e.getKey(), e.getValue());
}
if (up.haveKey(pubkey)) {
// the key is already in the TrustedUpdate keyring
// verify the sig and verify that it is signed by the signer in the plugin.config file
String signingKeyName = up.verifyAndGetSigner(f);
if (!signer.equals(signingKeyName)) {
f.delete();
to.delete();
updateStatus("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>");
return;
}
} else {
// add to keyring...
if(!up.addKey(pubkey, signer)) {
// bad or duplicate key
f.delete();
to.delete();
updateStatus("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>");
return;
}
// ...and try the verify again
// verify the sig and verify that it is signed by the signer in the plugin.config file
String signingKeyName = up.verifyAndGetSigner(f);
if (!signer.equals(signingKeyName)) {
f.delete();
to.delete();
updateStatus("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>");
return;
}
}
String sudVersion = TrustedUpdate.getVersionString(f);
f.delete();
String appName = props.getProperty("name");
String version = props.getProperty("version");
if (appName == null || version == null || appName.length() <= 0 || version.length() <= 0 ||
appName.indexOf("<") >= 0 || appName.indexOf(">") >= 0 ||
version.indexOf("<") >= 0 || version.indexOf(">") >= 0 ||
appName.startsWith(".") || appName.indexOf("/") >= 0 || appName.indexOf("\\") >= 0) {
to.delete();
updateStatus("<b>" + _("Plugin from {0} has invalid name or version", url) + "</b>");
return;
}
if (!version.equals(sudVersion)) {
to.delete();
updateStatus("<b>" + _("Plugin {0} has mismatched versions", appName) + "</b>");
return;
}
String minVersion = ConfigClientsHelper.stripHTML(props, "min-i2p-version");
if (minVersion != null &&
(new VersionComparator()).compare(CoreVersion.VERSION, minVersion) < 0) {
to.delete();
updateStatus("<b>" + _("This plugin requires I2P version {0} or higher", minVersion) + "</b>");
return;
}
minVersion = ConfigClientsHelper.stripHTML(props, "min-java-version");
if (minVersion != null &&
(new VersionComparator()).compare(System.getProperty("java.version"), minVersion) < 0) {
to.delete();
updateStatus("<b>" + _("This plugin requires Java version {0} or higher", minVersion) + "</b>");
return;
}
File destDir = new File(appDir, appName);
if (destDir.exists()) {
if (Boolean.valueOf(props.getProperty("install-only")).booleanValue()) {
to.delete();
updateStatus("<b>" + _("Downloaded plugin is for new installs only, but the plugin is already installed", url) + "</b>");
return;
}
// compare previous version
File oldPropFile = new File(destDir, "plugin.config");
Properties oldProps = new OrderedProperties();
try {
DataHelper.loadProps(oldProps, oldPropFile);
} catch (IOException ioe) {
to.delete();
FileUtil.rmdir(tempDir, false);
updateStatus("<b>" + _("Installed plugin does not contain the required configuration file", url) + "</b>");
return;
}
String oldPubkey = oldProps.getProperty("key");
String oldKeyName = oldProps.getProperty("signer");
String oldAppName = props.getProperty("name");
if ((!pubkey.equals(oldPubkey)) || (!signer.equals(oldKeyName)) || (!appName.equals(oldAppName))) {
to.delete();
updateStatus("<b>" + _("Signature of downloaded plugin does not match installed plugin") + "</b>");
return;
}
String oldVersion = oldProps.getProperty("version");
if (oldVersion == null ||
(new VersionComparator()).compare(oldVersion, version) >= 0) {
to.delete();
updateStatus("<b>" + _("Downloaded plugin version {0} is not newer than installed plugin", version) + "</b>");
return;
}
minVersion = ConfigClientsHelper.stripHTML(props, "min-installed-version");
if (minVersion != null &&
(new VersionComparator()).compare(minVersion, oldVersion) > 0) {
to.delete();
updateStatus("<b>" + _("Plugin update requires installed plugin version {0} or higher", minVersion) + "</b>");
return;
}
String maxVersion = ConfigClientsHelper.stripHTML(props, "max-installed-version");
if (maxVersion != null &&
(new VersionComparator()).compare(maxVersion, oldVersion) < 0) {
to.delete();
updateStatus("<b>" + _("Plugin update requires installed plugin version {0} or lower", maxVersion) + "</b>");
return;
}
// check if it is running first?
try {
if (!PluginStarter.stopPlugin(_context, appName)) {
// failed, ignore
}
} catch (Throwable e) {
// no updateStatus() for this one
_log.error("Error stopping plugin " + appName, e);
}
} else {
if (Boolean.valueOf(props.getProperty("update-only")).booleanValue()) {
to.delete();
updateStatus("<b>" + _("Plugin is for upgrades only, but the plugin is not installed") + "</b>");
return;
}
if (!destDir.mkdir()) {
to.delete();
updateStatus("<b>" + _("Cannot create plugin directory {0}", destDir.getAbsolutePath()) + "</b>");
return;
}
}
// Finally, extract the zip to the plugin directory
if (!FileUtil.extractZip(to, destDir)) {
to.delete();
updateStatus("<b>" + _("Failed to install plugin in {0}", destDir.getAbsolutePath()) + "</b>");
return;
}
to.delete();
if (Boolean.valueOf(props.getProperty("dont-start-at-install")).booleanValue()) {
if (Boolean.valueOf(props.getProperty("router-restart-required")).booleanValue())
updateStatus("<b>" + _("Plugin {0} installed, router restart required", appName) + "</b>");
else {
updateStatus("<b>" + _("Plugin {0} installed", appName) + "</b>");
Properties pluginProps = PluginStarter.pluginProperties();
pluginProps.setProperty(PluginStarter.PREFIX + appName + PluginStarter.ENABLED, "false");
PluginStarter.storePluginProperties(pluginProps);
}
} else {
// start everything
try {
if (PluginStarter.startPlugin(_context, appName))
updateStatus("<b>" + _("Plugin {0} installed and started", appName) + "</b>");
else
updateStatus("<b>" + _("Plugin {0} installed but failed to start, check logs", appName) + "</b>");
} catch (Throwable e) {
updateStatus("<b>" + _("Plugin {0} installed but failed to start", appName) + ": " + e + "</b>");
_log.error("Error starting plugin " + appName, e);
}
}
}
@Override
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
File f = new File(_updateFile);
f.delete();
updateStatus("<b>" + _("Failed to download plugin from {0}", url) + "</b>");
}
}
@Override
protected void updateStatus(String s) {
super.updateStatus(s);
_appStatus = s;
}
}

View File

@ -30,6 +30,7 @@ public class RouterConsoleRunner {
private String _webAppsDir = "./webapps/";
private static final String PROP_WEBAPP_CONFIG_FILENAME = "router.webappsConfigFile";
private static final String DEFAULT_WEBAPP_CONFIG_FILENAME = "webapps.config";
private static final DigestAuthenticator authenticator = new DigestAuthenticator();
public static final String ROUTERCONSOLE = "routerconsole";
public static final String PREFIX = "webapps.";
public static final String ENABLED = ".startOnLoad";
@ -69,6 +70,8 @@ public class RouterConsoleRunner {
if (!workDirCreated)
System.err.println("ERROR: Unable to create Jetty temporary work directory");
// so Jetty can find WebAppConfiguration
System.setProperty("jetty.class.path", I2PAppContext.getGlobalContext().getBaseDir() + "/lib/routerconsole.jar");
_server = new Server();
boolean rewrite = false;
Properties props = webAppProperties();
@ -127,11 +130,9 @@ public class RouterConsoleRunner {
String enabled = props.getProperty(PREFIX + appName + ENABLED);
if (! "false".equals(enabled)) {
String path = new File(dir, fileNames[i]).getCanonicalPath();
wac = _server.addWebApplication("/"+ appName, path);
tmpdir = new File(workDir, appName + "-" + _listenPort);
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
initialize(wac);
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir);
if (enabled == null) {
// do this so configclients.jsp knows about all apps from reading the config
props.setProperty(PREFIX + appName + ENABLED, "true");
@ -181,23 +182,29 @@ public class RouterConsoleRunner {
}
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
Thread t = new I2PAppThread(fetcher, "NewsFetcher");
t.setDaemon(true);
Thread t = new I2PAppThread(fetcher, "NewsFetcher", true);
t.start();
Thread st = new I2PAppThread(new StatSummarizer(), "StatSummarizer");
st.setDaemon(true);
st.start();
t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true);
t.start();
List<RouterContext> contexts = RouterContext.listContexts();
if (contexts != null) {
if (PluginStarter.pluginsEnabled(contexts.get(0))) {
t = new I2PAppThread(new PluginStarter(contexts.get(0)), "PluginStarter", true);
t.start();
}
}
}
private void initialize(WebApplicationContext context) {
static void initialize(WebApplicationContext context) {
String password = getPassword();
if (password != null) {
HashUserRealm realm = new HashUserRealm("i2prouter");
realm.put("admin", password);
realm.addUserToRole("admin", "routerAdmin");
context.setRealm(realm);
context.setAuthenticator(new DigestAuthenticator());
context.setAuthenticator(authenticator);
context.addHandler(0, new SecurityHandler());
SecurityConstraint constraint = new SecurityConstraint("admin", "routerAdmin");
constraint.setAuthenticate(true);
@ -205,11 +212,11 @@ public class RouterConsoleRunner {
}
}
private String getPassword() {
List contexts = RouterContext.listContexts();
static String getPassword() {
List<RouterContext> contexts = RouterContext.listContexts();
if (contexts != null) {
for (int i = 0; i < contexts.size(); i++) {
RouterContext ctx = (RouterContext)contexts.get(i);
RouterContext ctx = contexts.get(i);
String password = ctx.getProperty("consolePassword");
if (password != null) {
password = password.trim();
@ -237,10 +244,14 @@ public class RouterConsoleRunner {
********/
public static Properties webAppProperties() {
return webAppProperties(I2PAppContext.getGlobalContext().getConfigDir().getAbsolutePath());
}
public static Properties webAppProperties(String dir) {
Properties rv = new Properties();
// String webappConfigFile = ctx.getProperty(PROP_WEBAPP_CONFIG_FILENAME, DEFAULT_WEBAPP_CONFIG_FILENAME);
String webappConfigFile = DEFAULT_WEBAPP_CONFIG_FILENAME;
File cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), webappConfigFile);
File cfgFile = new File(dir, webappConfigFile);
try {
DataHelper.loadProps(rv, cfgFile);
@ -263,11 +274,12 @@ public class RouterConsoleRunner {
}
}
private static class WarFilenameFilter implements FilenameFilter {
static class WarFilenameFilter implements FilenameFilter {
private static final WarFilenameFilter _filter = new WarFilenameFilter();
public static WarFilenameFilter instance() { return _filter; }
public boolean accept(File dir, String name) {
return (name != null) && (name.endsWith(".war") && !name.equals(ROUTERCONSOLE + ".war"));
}
}
}

View File

@ -70,7 +70,11 @@ public class SummaryBarRenderer {
.append(_("Anonymous resident webserver"))
.append("\">")
.append(_("Webserver"))
.append("</a></td></tr></table>\n" +
.append("</a>")
.append(NavHelper.getClientAppLinks(_context))
.append("</td></tr></table>\n" +
"<hr><h3><a href=\"/config.jsp\" target=\"_top\" title=\"")
.append(_("Configure I2P Router"))
@ -110,11 +114,11 @@ public class SummaryBarRenderer {
.append(_("Logs"))
.append("</a>\n" +
"<a href=\"/jobs.jsp\" target=\"_top\" title=\"")
.append(_("Show the router's workload, and how it's performing"))
.append("\">")
.append(_("Jobs"))
.append("</a>\n" +
// "<a href=\"/jobs.jsp\" target=\"_top\" title=\"")
// .append(_("Show the router's workload, and how it's performing"))
// .append("\">")
// .append(_("Jobs"))
// .append("</a>\n" +
"<a href=\"/graphs.jsp\" target=\"_top\" title=\"")
.append(_("Graph router performance"))
@ -181,10 +185,10 @@ public class SummaryBarRenderer {
.append("</a></h4><hr>\n");
// display all the time so we display the final failure message, and plugin update messages too
buf.append(UpdateHandler.getStatus());
if (_helper.updateAvailable() || _helper.unsignedUpdateAvailable()) {
// display all the time so we display the final failure message
buf.append(UpdateHandler.getStatus());
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) {
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
// nothing
} else if(
// isDone() is always false for now, see UpdateHandler

View File

@ -37,7 +37,7 @@ public class UpdateHandler {
private String _nonce;
protected static final String SIGNED_UPDATE_FILE = "i2pupdate.sud";
protected static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress";
static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress";
protected static final String PROP_LAST_UPDATE_TIME = "router.updateLastDownloaded";
public UpdateHandler() {
@ -124,7 +124,7 @@ public class UpdateHandler {
protected boolean _isRunning;
protected boolean done;
protected EepGet _get;
private final DecimalFormat _pct = new DecimalFormat("0.0%");
protected final DecimalFormat _pct = new DecimalFormat("0.0%");
public UpdateRunner() {
_isRunning = false;

View File

@ -0,0 +1,96 @@
package net.i2p.router.web;
import java.io.File;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import org.mortbay.jetty.servlet.WebApplicationContext;
/**
* Add to the webapp classpath as specified in webapps.config.
* This allows us to reference classes that are not in the classpath
* specified in wrapper.config, since old installations have
* individual jars and not lib/*.jar specified in wrapper.config.
*
* A sample line in webapps.config is:
* webapps.appname.path=foo.jar,$I2P/lib/bar.jar
* Unless $I2P is specified the path will be relative to $I2P/lib for
* webapps in the installation and appDir/plugins/appname/lib for plugins.
*
* Sadly, setting Class-Path in MANIFEST.MF doesn't work for jetty wars.
* We could look there ourselves, or look for another properties file in the war,
* but let's just do it in webapps.config.
*
* No, wac.addClassPath() does not work. For more info see:
*
* http://servlets.com/archive/servlet/ReadMsg?msgId=511113&listName=jetty-support
*
* @since 0.7.12
* @author zzz
*/
public class WebAppConfiguration implements WebApplicationContext.Configuration {
private WebApplicationContext _wac;
private static final String CLASSPATH = ".classpath";
public void setWebApplicationContext(WebApplicationContext context) {
_wac = context;
}
public WebApplicationContext getWebApplicationContext() {
return _wac;
}
public void configureClassPath() throws Exception {
String ctxPath = _wac.getContextPath();
//System.err.println("Configure Class Path " + ctxPath);
if (ctxPath.equals("/"))
return;
String appName = ctxPath.substring(1);
I2PAppContext i2pContext = I2PAppContext.getGlobalContext();
File libDir = new File(i2pContext.getBaseDir(), "lib");
// FIXME this only works if war is the same name as the plugin
File pluginDir = new File(i2pContext.getAppDir(),
PluginUpdateHandler.PLUGIN_DIR + ctxPath);
File dir = libDir;
String cp;
if (ctxPath.equals("/susidns")) {
// jars moved from the .war to lib/ in 0.7.12
cp = "jstl.jar,standard.jar";
} else if (ctxPath.equals("/i2psnark")) {
// duplicate classes removed from the .war in 0.7.12
cp = "i2psnark.jar";
} else if (pluginDir.exists()) {
File consoleDir = new File(pluginDir, "console");
Properties props = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath());
cp = props.getProperty(RouterConsoleRunner.PREFIX + appName + CLASSPATH);
dir = pluginDir;
} else {
Properties props = RouterConsoleRunner.webAppProperties();
cp = props.getProperty(RouterConsoleRunner.PREFIX + appName + CLASSPATH);
}
if (cp == null)
return;
StringTokenizer tok = new StringTokenizer(cp, " ,");
while (tok.hasMoreTokens()) {
String elem = tok.nextToken().trim();
String path;
if (elem.startsWith("$I2P"))
path = i2pContext.getBaseDir().getAbsolutePath() + elem.substring(4);
else if (elem.startsWith("$PLUGIN"))
path = dir.getAbsolutePath() + elem.substring(7);
else
path = dir.getAbsolutePath() + '/' + elem;
System.err.println("Adding " + path + " to classpath for " + appName);
_wac.addClassPath(path);
}
}
public void configureDefaults() {}
public void configureWebApp() {}
}

View File

@ -0,0 +1,91 @@
package net.i2p.router.web;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import org.mortbay.http.HttpContext;
import org.mortbay.http.HttpListener;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.WebApplicationContext;
/**
* Start a webappapp classpath as specified in webapps.config.
*
* Sadly, setting Class-Path in MANIFEST.MF doesn't work for jetty wars.
* We could look there ourselves, or look for another properties file in the war,
* but let's just do it in webapps.config.
*
* No, wac.addClassPath() does not work.
*
* http://servlets.com/archive/servlet/ReadMsg?msgId=511113&listName=jetty-support
*
* @since 0.7.12
* @author zzz
*/
public class WebAppStarter {
/**
* adds and starts
* @throws just about anything, caller would be wise to catch Throwable
*/
static void startWebApp(I2PAppContext ctx, Server server, String appName, String warPath) throws Exception {
File tmpdir = new File(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt());
WebApplicationContext wac = addWebApp(ctx, server, appName, warPath, tmpdir);
wac.start();
}
/**
* add but don't start
*/
static WebApplicationContext addWebApp(I2PAppContext ctx, Server server, String appName, String warPath, File tmpdir) throws IOException {
WebApplicationContext wac = server.addWebApplication("/"+ appName, warPath);
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
// this does the passwords...
RouterConsoleRunner.initialize(wac);
// see WebAppConfiguration for info
String[] classNames = server.getWebApplicationConfigurationClassNames();
String[] newClassNames = new String[classNames.length + 1];
for (int j = 0; j < classNames.length; j++)
newClassNames[j] = classNames[j];
newClassNames[classNames.length] = WebAppConfiguration.class.getName();
wac.setConfigurationClassNames(newClassNames);
return wac;
}
/**
* stop it
* @throws just about anything, caller would be wise to catch Throwable
*/
static void stopWebApp(Server server, String appName) {
// this will return a new context if one does not exist
HttpContext wac = server.getContext('/' + appName);
try {
// false -> not graceful
wac.stop(false);
} catch (InterruptedException ie) {}
}
/** see comments in ConfigClientsHandler */
static Server getConsoleServer() {
Collection c = Server.getHttpServers();
for (int i = 0; i < c.size(); i++) {
Server s = (Server) c.toArray()[i];
HttpListener[] hl = s.getListeners();
for (int j = 0; j < hl.length; j++) {
if (hl[j].getPort() == 7657)
return s;
}
}
return null;
}
}

View File

@ -54,4 +54,20 @@ button span.hide{
<i><%=intl._("All changes require restart to take effect.")%></i>
</p><hr><div class="formaction">
<input type="submit" name="action" value="<%=intl._("Save WebApp Configuration")%>" />
</div></div></form></div></div></body></html>
</div></div>
<% if (clientshelper.showPlugins()) { %>
<h3><a name="webapp"></a><%=intl._("Plugin Configuration")%></h3><p>
<%=intl._("The plugins listed below are started by the webConsole client.")%>
</p><div class="wideload"><p>
<jsp:getProperty name="clientshelper" property="form3" />
</p><hr><div class="formaction">
<input type="submit" name="action" value="<%=intl._("Save Plugin Configuration")%>" />
</div></div><h3><a name="plugin"></a><%=intl._("Plugin Installation")%></h3><p>
<%=intl._("To install a plugin, enter the download URL:")%>
</p><div class="wideload"><p>
<input type="text" size="60" name="pluginURL" >
</p><hr><div class="formaction">
<input type="submit" name="action" value="<%=intl._("Install Plugin")%>" />
</div></div>
<% } %>
</form></div></div></body></html>

View File

@ -59,12 +59,14 @@
<%=intl._("You may want to consider shutting down gracefully, as above, then running uninstall_i2p_service_winnt.bat.")%></p>
<% } %>
<% if (System.getProperty("wrapper.version") != null) { %>
<h3><%=intl._("Debugging")%></h3>
<p><a href="/jobs.jsp"><%=intl._("View the job queue")%></a>
<% if (System.getProperty("wrapper.version") != null) { %>
<p><%=intl._("At times, it may be helpful to debug I2P by getting a thread dump. To do so, please select the following option and review the thread dumped to <a href=\"logs.jsp#servicelogs\">wrapper.log</a>.")%></p>
<hr><div class="formaction">
<input type="submit" name="action" value="<%=intl._("Dump threads")%>" >
<% } %></div>
</div>
<% } %>
<h3><%=intl._("Launch browser on router startup?")%></h3>
<p><%=intl._("I2P's main configuration interface is this web console, so for your convenience I2P can launch a web browser on startup pointing at")%>

View File

@ -20,7 +20,29 @@ if (uri.endsWith(".css")) {
response.setContentType("image/x-icon");
}
response.setHeader("Cache-Control", "max-age=86400"); // cache for a day
String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath() +
/*
* User or plugin themes
* If the request is for /themes/console/foo/bar/baz,
* and the property routerconsole.theme.foo=/path/to/foo,
* get the file from /path/to/foo/bar/baz
*/
String themePath = null;
final String PFX = "/themes/console/";
if (uri.startsWith(PFX) && uri.length() > PFX.length() + 1) {
String theme = uri.substring(PFX.length());
int slash = theme.indexOf('/');
if (slash > 0) {
theme = theme.substring(0, slash);
themePath = net.i2p.I2PAppContext.getGlobalContext().getProperty("routerconsole.theme." + theme);
if (themePath != null)
uri = uri.substring(PFX.length() + theme.length()); // /bar/baz
}
}
String base;
if (themePath != null)
base = themePath;
else
base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath() +
java.io.File.separatorChar + "docs";
net.i2p.util.FileUtil.readFile(uri, base, response.getOutputStream());
%>

View File

@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: I2P routerconsole\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-11-01 02:59+0000\n"
"PO-Revision-Date: 2010-01-17 22:09+0100\n"
"PO-Revision-Date: 2010-03-06 14:19+0100\n"
"Last-Translator: \n"
"Language-Team: foo <foo@bar>\n"
"MIME-Version: 1.0\n"
@ -177,7 +177,7 @@ msgstr "Configuration UPnP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:363
msgid "Enable UPnP to open firewall ports"
msgstr ""
msgstr "Activer UPnP afin d'ouvrir les ports du pare-feu"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:365
msgid "UPnP status"
@ -190,15 +190,15 @@ msgstr "Configuration IP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:369
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:425
msgid "Externally reachable hostname or IP address"
msgstr ""
msgstr "Adresse IP ou nom d'hôte qui est joignable depuis l'exterieur"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:373
msgid "Use all auto-detect methods"
msgstr ""
msgstr "Utiliser toute méthode d'auto-détection"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:377
msgid "Disable UPnP IP address detection"
msgstr ""
msgstr "Desactiver la détection de l'adresse IP par UPnP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:381
msgid "Ignore local interface IP address"
@ -206,7 +206,7 @@ msgstr "Ignorer l'adresse IP de l'interface locale"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:385
msgid "Use SSU IP address detection only"
msgstr ""
msgstr "Utiliser seulement SSU pour détecter l'adresse IP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:389
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:443
@ -245,7 +245,7 @@ msgstr "Configuration TCP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:429
msgid "Use auto-detected IP address"
msgstr ""
msgstr "Utiliser l'adresse IP qui a été auto-détectée"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:431
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:463
@ -258,11 +258,11 @@ msgstr "s'il n'y a pas de pare-feu"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:439
msgid "Always use auto-detected IP address (Not firewalled)"
msgstr ""
msgstr "Utiliser toujours l'adresse IP qui a été auto-détectée (pas de pare-feu)"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:449
msgid "Disable inbound (Firewalled)"
msgstr ""
msgstr "Desactiver les connexions entrantes (derrière un pare-feu)"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:453
msgid "Completely disable"
@ -270,7 +270,7 @@ msgstr "Desactiver complètement"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:455
msgid "(select only if behind a firewall that throttles or blocks outbound TCP)"
msgstr ""
msgstr "(selectionner seulement si derrière un pare-feu qui limite les connexions sortantes TCP)"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:457
msgid "Externally reachable TCP port"
@ -278,7 +278,7 @@ msgstr ""
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:461
msgid "Use the same port configured for UDP"
msgstr ""
msgstr "Utiliser le même port qui a été configuré pour UDP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:469
msgid "Specify Port"
@ -301,67 +301,67 @@ msgstr "Aide avec la configuration"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:484
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:526
msgid "While I2P will work fine behind most firewalls, your speeds and network integration will generally improve if the I2P port (generally 8887) is forwarded for both UDP and TCP."
msgstr ""
msgstr "I2P fonctionnera derrière le plupart des pares-feu, mais votre vitesse et votre intégration avec le réseau s'améliorera s'il y a la redirection du port I2P pour UDP et TCP."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:486
msgid "If you can, please poke a hole in your firewall to allow unsolicited UDP and TCP packets to reach you."
msgstr ""
msgstr "Si vous pouvez, ouvrez un port dans votre pare-feu afin de permettre la réception des les paquets TCP et UDP non sollicités."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:488
msgid "If you can't, I2P supports UPnP (Universal Plug and Play) and UDP hole punching with \"SSU introductions\" to relay traffic."
msgstr ""
msgstr "Si vous ne pouvez pas, I2P est compatible avec UPnp (Universal Plug and Play) et \"UDP hole punching\" avec \"SSU introductions\" afin de relayer le trafic I2P."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:490
msgid "Most of the options above are for special situations, for example where UPnP does not work correctly, or a firewall not under your control is doing harm."
msgstr ""
msgstr "Le plupart des possibilités décrites ci-avant sont pour les situtations particulieres, par exemple le cas où UPnP ne fonctionne pas correctement, ou un pare-feu empeche la connexion au réseau I2P."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:492
msgid "Certain firewalls such as symmetric NATs may not work well with I2P."
msgstr ""
msgstr "Il y a des certains types de pare-feu (tel que les NAT symétriques) qui ne fonctionnent pas bien avec I2P."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:494
msgid "UPnP is used to communicate with Internet Gateway Devices (IGDs) to detect the external IP address and forward ports."
msgstr ""
msgstr "UPnP est utilisé pour communiquer avec des \"Internet Gateway Devices (IGDs)\" afin de détecter l'adresse IP extérieure et de contrôler la redirection des ports."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:496
msgid "UPnP support is beta, and may not work for any number of reasons"
msgstr ""
msgstr "UPnP est toujours en développement, et il peut arrêter de fonctionner correctement à cause de "
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:498
msgid "No UPnP-compatible device present"
msgstr ""
msgstr "Il n'y a pas d'appareil qui est compatible avec UPnP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:500
msgid "UPnP disabled on the device"
msgstr ""
msgstr "UPnP est desactivé sur l'appareil"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:502
msgid "Software firewall interference with UPnP"
msgstr ""
msgstr "Il y a de l'intérference entre un pare-feu en software et UPnP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:504
msgid "Bugs in the device's UPnP implementation"
msgstr ""
msgstr "Il y a des bogues dans l'implementation d'UPnP dans l'appareil"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:506
msgid "Multiple firewall/routers in the internet connection path"
msgstr ""
msgstr "Il y a plusieurs routeurs/pare-feux entre le routeur I2P et l'internet"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:508
msgid "UPnP device change, reset, or address change"
msgstr ""
msgstr "Un changement de l'appareil UPnP, une redémarrage, ou une changement d'adresse IP"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:510
msgid "Review the UPnP status here."
msgstr ""
msgstr "Consulter le statut de UPnP ici."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:512
msgid "UPnP may be enabled or disabled above, but a change requires a router restart to take effect."
msgstr ""
msgstr "UPnP peut être activé ou desactivé au-dessus, mais afin de prendre en compte la changement il faut rédemarrer le routeur I2P."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:514
msgid "Hostnames entered above will be published in the network database."
msgstr ""
msgstr "Les noms d'hôtes qui ont été saisis au-dessus seront publié dans la base de données du réseau I2P."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:516
msgid "They are <b>not private</b>."
@ -369,15 +369,15 @@ msgstr "Ils ne sont pas <b>privés</b>."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:518
msgid "Also, <b>do not enter a private IP address</b> like 127.0.0.1 or 192.168.1.1."
msgstr ""
msgstr "En plus, <b>ne saisissez pas une adresse IP privée</b> tel que 127.0.0.1 ou 192.168.1.1."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:520
msgid "If you specify the wrong IP address or hostname, or do not properly configure your NAT or firewall, your network performance will degrade substantially."
msgstr ""
msgstr "Si vous saisissez une mauvaise adresse IP ou nom d'hôte, ou configurer votre NAT ou pare-feu incorrectement, votre intégration avec le réseau I2P dégradera substantiellement."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:522
msgid "When in doubt, leave the settings at the defaults."
msgstr ""
msgstr "Si vous n'êtes pas sûr de vous, laisser la configuration par défaut."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:524
msgid "Reachability Help"
@ -853,11 +853,11 @@ msgstr "Fermer le routeur"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configservice_jsp.java:286
msgid "Graceful shutdown lets the router satisfy the agreements it has already made before shutting down, but may take a few minutes."
msgstr ""
msgstr "Une fermature gracieuse permit au routeur de satisfaire ses accords en place avec d'autres routeurs avant de fermer, mais cela prendra plusieurs minutes."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configservice_jsp.java:288
msgid "If you need to kill the router immediately, that option is available as well."
msgstr ""
msgstr "Si vous avez besoin d'arrêter le routeur I2P immédiatement, cette option est aussi disponible."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configservice_jsp.java:290
#: src/net/i2p/router/web/ConfigServiceHandler.java:53
@ -889,11 +889,11 @@ msgstr ""
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configservice_jsp.java:304
msgid "A graceful restart will take a few minutes (but your peers will appreciate your patience), while a hard restart does so immediately."
msgstr ""
msgstr "Une redémarrage gracieux prendra quelques minutes (mais vos pairs apprécieront votre patience), une redémarrage immédiate est quasi instantané."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configservice_jsp.java:306
msgid "After tearing down the router, it will wait 1 minute before starting back up again."
msgstr ""
msgstr "Après la rédemmarage du routeur, il attendra 1 minute avant de se relancer."
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configservice_jsp.java:308
#: src/net/i2p/router/web/ConfigServiceHandler.java:64
@ -1223,11 +1223,11 @@ msgstr ""
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/logs_jsp.java:105
msgid "logs"
msgstr "enregistrements"
msgstr "fichier traces"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/logs_jsp.java:227
msgid "I2P Router Logs"
msgstr "Enregistrements du routeur I2P"
msgstr "Fichier traces du routeur I2P"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/logs_jsp.java:229
msgid "I2P Version & Running Environment"
@ -2213,7 +2213,7 @@ msgstr "Services I2P"
#: src/net/i2p/router/web/SummaryBarRenderer.java:48
msgid "Manage your I2P hosts file here (I2P domain name resolution)"
msgstr ""
msgstr "Gérer votre fichier 'I2P hosts' ici (I2P DNS)"
#: src/net/i2p/router/web/SummaryBarRenderer.java:50
msgid "Addressbook"
@ -2254,7 +2254,7 @@ msgstr "I2P Configuration Interne"
#: src/net/i2p/router/web/SummaryBarRenderer.java:80
#: src/net/i2p/router/web/SummaryBarRenderer.java:344
msgid "View existing tunnels and tunnel build status"
msgstr ""
msgstr "Montrer les tunnels existants et le statut de création des tunnels"
#: src/net/i2p/router/web/SummaryBarRenderer.java:86
#: src/net/i2p/router/web/SummaryBarRenderer.java:221
@ -2263,7 +2263,7 @@ msgstr "Montrer toutes les connexions actuelles aux pairs"
#: src/net/i2p/router/web/SummaryBarRenderer.java:92
msgid "Show recent peer performance profiles"
msgstr ""
msgstr "Montrer les profils de la performance récente des pairs"
#: src/net/i2p/router/web/SummaryBarRenderer.java:94
msgid "Profiles"
@ -2279,15 +2279,15 @@ msgstr ""
#: src/net/i2p/router/web/SummaryBarRenderer.java:104
msgid "Health Report"
msgstr ""
msgstr "Fichier traces du routeur"
#: src/net/i2p/router/web/SummaryBarRenderer.java:106
msgid "Logs"
msgstr "Enregistrements"
msgstr "Fichier traces"
#: src/net/i2p/router/web/SummaryBarRenderer.java:110
msgid "Show the router's workload, and how it's performing"
msgstr ""
msgstr "Montrer les tâches en cours"
#: src/net/i2p/router/web/SummaryBarRenderer.java:112
msgid "Jobs"
@ -2295,7 +2295,7 @@ msgstr "Tâches"
#: src/net/i2p/router/web/SummaryBarRenderer.java:116
msgid "Graph router performance"
msgstr ""
msgstr "Montrer la performance du routeur avec des graphes"
#: src/net/i2p/router/web/SummaryBarRenderer.java:118
msgid "Graphs"
@ -2303,7 +2303,7 @@ msgstr "Graphes"
#: src/net/i2p/router/web/SummaryBarRenderer.java:122
msgid "Textual router performance statistics"
msgstr ""
msgstr "La performance statistique du routeur en texte"
#: src/net/i2p/router/web/SummaryBarRenderer.java:134
msgid "I2P Router Help"
@ -2375,7 +2375,7 @@ msgstr ""
#: src/net/i2p/router/web/SummaryBarRenderer.java:309
msgid "Configure router bandwidth allocation"
msgstr ""
msgstr "Configurer la bande passante du routeur"
#: src/net/i2p/router/web/SummaryBarRenderer.java:311
msgid "Bandwidth in/out"
@ -2403,7 +2403,7 @@ msgstr "Participant"
#: src/net/i2p/router/web/SummaryBarRenderer.java:373
msgid "What's in the router's job queue?"
msgstr ""
msgstr "Montrer les tâches du routeur qui sont à traiter "
#: src/net/i2p/router/web/SummaryBarRenderer.java:375
msgid "Congestion"
@ -2471,7 +2471,7 @@ msgstr "WARN - Pare-feu avec UDP desactivé"
#: src/net/i2p/router/web/SummaryHelper.java:360
msgid "Add/remove/edit &amp; control your client and server tunnels"
msgstr ""
msgstr "Ajouter/enlever/éditer &amp; contrôler vos tunnels client et serveur"
#: src/net/i2p/router/web/SummaryHelper.java:360
msgid "Local Destinations"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -194,7 +194,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
}
/**
* @param timeoutMs MessageOutputStream is the only caller, often with -1 ??????
* @param maxWaitMs MessageOutputStream is the only caller, often with -1 ??????
*/
public void waitForAccept(int maxWaitMs) {
if (_connection == null)

View File

@ -0,0 +1,395 @@
# I2P
# Copyright (C) 2009 The I2P Project
# This file is distributed under the same license as the susidns package.
# To contribute translations, see http://www.i2p2.de/newdevelopers
# foo <foo@bar>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: I2P susidns\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-03-05 12:44+0000\n"
"PO-Revision-Date: 2010-03-05 21:37+0800\n"
"Last-Translator: walking <walking@mail.i2p>\n"
"Language-Team: foo <foo@bar>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Chinese\n"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:197
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:193
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:197
msgid "Search"
msgstr "搜索"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:201
msgid "Search within filtered list"
msgstr "在过滤结果中搜索"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:203
msgid "Filtered list"
msgstr "过滤结果列表"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:207
msgid "no matches"
msgstr "无匹配项目"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:210
msgid "Addressbook"
msgstr "地址簿"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:212
msgid "contains no entries"
msgstr "包含 0 个项目"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:214
msgid "contains 1 entry"
msgstr "包含 1 个项目"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:216
#, java-format
msgid "contains {0} entries"
msgstr "包含 {0} 个项目"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:226
#, java-format
msgid "Showing {0} of {1}"
msgstr "显示 {0} 个项目共 {1}"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:257
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:364
msgid "Add"
msgstr "添加"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:261
msgid "Destination added."
msgstr "目标已添加"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:265
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:312
msgid "Delete"
msgstr "删除"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:275
#, java-format
msgid "Destination {0} deleted."
msgstr "目标 {0} 已删除"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:277
#, java-format
msgid "{0} destinations deleted."
msgstr "{0} 个目标已删除"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:283
msgid "Addressbook saved."
msgstr "地址簿已保存"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:286
msgid "ERROR: Could not write addressbook file."
msgstr "错误:无法写入地址簿文件"
#: ../src/java/src/i2p/susi/dns/AddressbookBean.java:291
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:148
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:150
msgid "Invalid form submission, probably because you used the \"back\" or \"reload\" button on your browser. Please resubmit."
msgstr "提交数据无效,可能的原因是您使用了浏览器中的“前进”或“后退”按钮造成会话过期,请重新提交。"
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:139
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:129
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:128
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:128
msgid "Save"
msgstr "保存"
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:141
msgid "Configuration saved."
msgstr "配置已保存"
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:142
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:144
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:130
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:130
msgid "Reload"
msgstr "刷新"
#: ../src/java/src/i2p/susi/dns/ConfigBean.java:144
msgid "Configuration reloaded."
msgstr "配置已重新载入"
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:138
msgid "Subscriptions saved, updating addressbook from subscription sources now."
msgstr "订阅已保存,正在通过订阅地址更新地址簿。"
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:142
msgid "Subscriptions saved."
msgstr "订阅已保存。"
#: ../src/java/src/i2p/susi/dns/SubscriptionsBean.java:146
msgid "Subscriptions reloaded."
msgstr "订阅设置已重新载入。"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:125
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:145
msgid "addressbook"
msgstr "地址簿"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:127
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:104
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:93
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:104
msgid "addressbooks"
msgstr "地址簿"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:129
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:106
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:95
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:106
msgid "private"
msgstr "私人"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:131
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:108
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:97
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:108
msgid "master"
msgstr "主要"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:133
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:110
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:99
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:110
msgid "router"
msgstr "路由"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:135
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:112
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:101
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:112
msgid "published"
msgstr "发布"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:137
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:114
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:103
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:102
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:114
msgid "subscriptions"
msgstr "订阅"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:139
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:102
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:116
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:105
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:116
msgid "configuration"
msgstr "配置"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:141
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:118
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:107
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:118
msgid "overview"
msgstr "介绍"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:163
msgid "Filter"
msgstr "过滤器"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:166
msgid "all"
msgstr "全部"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:177
msgid "Current filter"
msgstr "当前过滤器"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:182
msgid "clear filter"
msgstr "清除过滤器"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:224
msgid "Name"
msgstr "名称"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:226
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:360
msgid "Destination"
msgstr "目标"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:257
msgid "Mark for deletion"
msgstr "标记为删除"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:279
msgid "address helper link"
msgstr "地址簿助手链接"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:343
msgid "This addressbook is empty."
msgstr "此地址簿为空"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:354
msgid "Add new destination"
msgstr "添加新目标"
#: ../src/tmp/i2p/susi/dns/jsp/addressbook_jsp.java:356
msgid "Hostname"
msgstr "主机名称"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:132
msgid "Hints"
msgstr "提示"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:134
msgid "File and directory paths here are relative to the addressbook's working directory, which is normally ~/.i2p/addressbook/ (Linux) or %APPDATA%\\I2P\\addressbook\\ (Windows)."
msgstr "此处使用的路径是以地址簿工作目录为参照的相对路径,通常为 ~/.i2p/addressbook/ (Linux) 或 %APPDATA%\\I2P\\addressbook\\ (Windows)."
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:136
msgid "If you want to manually add lines to an addressbook, add them to the private or master addressbooks."
msgstr "如果您希望想地址簿手动添加地址条目,请将其加入私有地址簿或主地址簿。"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:138
msgid "The router addressbook and the published addressbook are updated by the addressbook application."
msgstr "路由地址簿与已发布地址簿会由地址簿程序负责更新。"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:140
msgid "When you publish your addressbook, ALL destinations from the master and router addressbooks appear there."
msgstr "当您发布地址簿时,主地址簿与路由地址簿中的所有地址条目都会显示于已发布地址簿中。"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:142
msgid "Use the private addressbook for private destinations, these are not published."
msgstr "私有地址簿用来记录需要保密的私人目标,其中的条目不会被发布。"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:144
msgid "Options"
msgstr "选项"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:146
msgid "File containing the list of subscriptions URLs (no need to change)"
msgstr "含有订阅URL列表的文件(无需修改)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:148
msgid "Update interval in hours"
msgstr "更新周期(小时)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:150
msgid "Your public hosts.txt file (choose a path within your webserver document root)"
msgstr "您发布的公开地址簿 hosts.txt (请指定为您Web服务器目录中的某个路径以便发布。)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:152
msgid "Your hosts.txt (don't change)"
msgstr "您的 host.txt (无需修改)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:154
msgid "Your personal addressbook, these hosts will be published"
msgstr "您的个人地址簿,其中的地址会参与发布。"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:156
msgid "Your private addressbook, it is never published"
msgstr "您的私有地址簿,其中的地址不会被发布。"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:158
msgid "Port for your eepProxy (no need to change)"
msgstr "您的I2P代理端口(无需修改)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:160
msgid "Hostname for your eepProxy (no need to change)"
msgstr "您的I2P代理主机名(无需修改)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:162
msgid "Whether to update the published addressbook"
msgstr "是否更新发布地址簿"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:164
msgid "File containing the etags header from the fetched subscription URLs (no need to change)"
msgstr "此文件含有订阅URL返回的etag头(无需修改)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:166
msgid "File containing the modification timestamp for each fetched subscription URL (no need to change)"
msgstr "此文件含有每个地址簿订阅URL上次获取时的修改时间(无需修改)"
#: ../src/tmp/i2p/susi/dns/jsp/config_jsp.java:168
msgid "File to log activity to (change to /dev/null if you like)"
msgstr "活动日志文件(关闭可设为/dev/null [Linux])"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:91
msgid "Introduction"
msgstr "介绍"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:109
msgid "What is the addressbook?"
msgstr "什么是地址簿"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:111
msgid "The addressbook application is part of your i2p installation."
msgstr "地址簿程序是I2P安装程序的一部分。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:113
msgid "It regularly updates your hosts.txt file from distributed sources or \"subscriptions\"."
msgstr "他通过分散的来源或称地址簿“订阅”(概念类似RSS)更新您的地址簿文件 host.txt。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:115
msgid "In the default configuration, the addressbook is only subscribed to www.i2p2.i2p."
msgstr "默认设置中地址簿仅订阅 www.i2p2.i2p 的项目。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:117
msgid "Subscribing to additional sites is easy, just add them to your <a href=\"subscriptions.jsp\">subscriptions</a> file."
msgstr "订阅其他网站的地址簿也很简单,只需将它们加入 <a href=\"subscriptions.jsp\">订阅</a> 文件即可。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:119
msgid "For more information on naming in i2p, see <a href=\"http://www.i2p2.i2p/naming.html\">the overview on www.i2p2.i2p</a>."
msgstr "关于I2P网络域名系统的更多信息,参见 <a href=\"http://www.i2p2.i2p/naming.html\"> www.i2p2.i2p 网站上的“概述” </a>."
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:121
msgid "How does the addressbook work?"
msgstr "那么地址簿是如何工作的呢?"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:123
msgid "The addressbook application regularly polls your subscriptions and merges their content into your \"router\" addressbook, stored in the hosts.txt file."
msgstr "地址簿程序定期通过订阅的URL获取新的地址簿条目并将它们合并入您的“路由”地址簿中保存与 hosts.txt 文件内。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:125
msgid "Then it merges your \"master\" addressbook (userhosts.txt) into the router addressbook as well."
msgstr "随后他还会将您的“主要”地址簿(userhost.txt)合并入路由器地址簿。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:127
msgid "If configured, the router addressbook is now written to the \"published\" addressbook, which will be publicly available if you are running an eepsite."
msgstr "如果启用了相关配置路由器地址簿会写入您的“发布”地址簿中如果您有自己的eepsite匿名站点那么它可以通过您的站点公开发布。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:129
msgid "The router also uses a private addressbook (privatehosts.txt, not shown in the picture), which is not merged or published."
msgstr "路由器同时提供了私有地址簿(privatehost.txt,图片中为出现),其中的项目不会被合并或发布。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:131
msgid "Hosts in the private addressbook can be accessed by you but their addresses are never distributed to others."
msgstr "您可以访问私有地址簿中的网站,但它们的地址绝不会通过您的公开地址簿发布给别人。"
#: ../src/tmp/i2p/susi/dns/jsp/index_jsp.java:133
msgid "The private addressbook can also be used for aliases of hosts in your other addressbooks."
msgstr "私有地址簿还可以被用来设置网站别名。"
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:132
msgid "The subscription file contains a list of i2p URLs."
msgstr "订阅文件是一个I2P链接列表。"
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:134
msgid "The addressbook application regularly checks this list for new eepsites."
msgstr "地址簿程序定期通过这些链接检查是否有新的I2P匿名网站。"
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:136
msgid "Those URLs refer to published hosts.txt files."
msgstr "这些URL指向公开发布的hosts.txt文件。"
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:138
msgid "The default subscription is the hosts.txt from www.i2p2.i2p, which is updated infrequently."
msgstr "默认订阅是 www.i2p2.i2p 提供的 hosts.txt,此文件很少更新。"
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:140
msgid "So it is a good idea to add additional subscriptions to sites that have the latest addresses."
msgstr "所以订阅一些网站的最新地址簿是个不错的主意。"
#: ../src/tmp/i2p/susi/dns/jsp/subscriptions_jsp.java:142
msgid "See the FAQ for a list of subscription URLs."
msgstr "其他来源的订阅链接参见I2P站点的 <a href=\"http://www.i2p2.i2p/faq_zh.html\">FAQ</a>"

View File

@ -66,7 +66,9 @@
<war destfile="${project}.war" webxml="WEB-INF/web-out.xml">
<fileset dir=".">
<include name="WEB-INF/**/*.class"/>
<!-- pulled out of the jar in 0.7.12
<include name="WEB-INF/lib/*.jar"/>
-->
<include name="images/*.png"/>
<include name="css.css"/>
<include name="index.html"/>

View File

@ -314,6 +314,8 @@
<copy file="build/org.mortbay.jetty.jar" todir="pkg-temp/lib/" />
<copy file="build/router.jar" todir="pkg-temp/lib/" />
<copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
<!-- pulled out of routerconsole.jar in 0.7.12; name without version so we can overwrite if we upgrade -->
<copy file="apps/jrobin/jrobin-1.4.0.jar" tofile="pkg-temp/lib/jrobin.jar" />
<copy file="build/sam.jar" todir="pkg-temp/lib/" />
<copy file="build/BOB.jar" todir="pkg-temp/lib/" />
<copy file="build/systray.jar" todir="pkg-temp/lib" />
@ -325,6 +327,8 @@
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
<copy file="build/susidns.war" todir="pkg-temp/webapps/" />
<copy file="apps/susidns/src/WEB-INF/lib/jstl.jar" todir="pkg-temp/lib/" />
<copy file="apps/susidns/src/WEB-INF/lib/standard.jar" todir="pkg-temp/lib/" />
<copy file="build/i2psnark.war" todir="pkg-temp/webapps/" />
<copy file="apps/i2psnark/launch-i2psnark" todir="pkg-temp/" />
<copy file="apps/i2psnark/jetty-i2psnark.xml" todir="pkg-temp/" />
@ -380,6 +384,13 @@
<copy todir="pkg-temp/licenses/" >
<fileset dir="licenses/" />
</copy>
<!--
The license in launch4j/ is a BSD license for launch4j
The license in launch4j/head is a MIT license for the code that is actually wrapped around the jars
So we include the MIT one in our binary package
-->
<copy file="installer/lib/launch4j/head/LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-Launch4j.txt" />
<!-- Not sure if these are used or should be included -->
<copy file="installer/lib/launch4j/lib/foxtrot.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-Foxtrot.txt" />
<copy file="installer/lib/launch4j/lib/JGoodies.Forms.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-JGoodies-Forms.txt" />
<copy file="installer/lib/launch4j/lib/JGoodies.Looks.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-JGoodies-Looks.txt" />
@ -433,13 +444,15 @@
</target>
<target name="prepconsoleDocs" depends="prepgeoupdate">
<copy todir="pkg-temp/docs/" >
<fileset dir="." includes="readme*.html" />
<fileset dir="installer/resources/readme/" includes="readme*.html" />
<fileset dir="installer/resources/proxy" />
</copy>
</target>
<target name="consoleDocs" depends="deletepkg-temp, prepconsoleDocs">
<zip destfile="docs.zip" basedir="pkg-temp" whenempty="fail" />
</target>
<target name="updater200" depends="prepupdate, preplicenses, pack200, zipit" />
<target name="updater" depends="prepupdate, preplicenses, zipit" />
<target name="updaterWithGeoIP" depends="prepupdate, prepgeoupdate, preplicenses, zipit" />
<target name="updaterWithJetty" depends="prepjupdate, preplicenses, zipit" />
@ -449,7 +462,19 @@
<target name="updaterRouter" depends="prepupdateRouter, zipit" />
<target name="zipit">
<zip destfile="i2pupdate.zip" basedir="pkg-temp" whenempty="fail" />
<!-- just a test, makes almost no difference
<tar destfile="i2pupdate.tgz" basedir="pkg-temp" compression="gzip" />
<tar destfile="i2pupdate.tbz" basedir="pkg-temp" compression="bzip2" />
-->
</target>
<target name="pack200">
<exec executable="sh" failifexecutionfails="true">
<arg value="-c" />
<!-- pack200 will only pack to a .pack file, and only from a .jar file, so we put another .jar on the end -->
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
</exec>
</target>
<target name="updateTest" depends="prepupdate">
<ant dir="core/java/" target="jarTest" />
<copy file="core/java/build/i2ptest.jar" todir="pkg-temp/lib" />
@ -463,6 +488,9 @@
<copy file="build/systray.jar" todir="pkg-temp/lib/" />
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
<copy file="build/susidns.war" todir="pkg-temp/webapps/" />
<!-- as of 0.7.12; someday, we can remove these from the updater -->
<copy file="apps/susidns/src/WEB-INF/lib/jstl.jar" todir="pkg-temp/lib/" />
<copy file="apps/susidns/src/WEB-INF/lib/standard.jar" todir="pkg-temp/lib/" />
<copy file="build/i2psnark.war" todir="pkg-temp/webapps/" />
<copy file="history.txt" todir="pkg-temp/" />
<!-- the following overwrites history.txt on unix to shrink the update file -->
@ -482,6 +510,9 @@
<copy file="build/mstreaming.jar" todir="pkg-temp/lib/" />
<copy file="build/streaming.jar" todir="pkg-temp/lib/" />
<copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
<!-- pulled out of routerconsole.jar in 0.7.12, someday we can take out of updater -->
<!-- name without version so we can overwrite if we upgrade -->
<copy file="apps/jrobin/jrobin-1.4.0.jar" tofile="pkg-temp/lib/jrobin.jar" />
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />

View File

@ -16,7 +16,7 @@ package net.i2p;
public class CoreVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = "0.7.10";
public final static String VERSION = "0.7.11";
public static void main(String args[]) {
System.out.println("I2P Core version: " + VERSION);

View File

@ -12,7 +12,7 @@ import net.i2p.crypto.CryptixAESEngine;
import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.DummyDSAEngine;
import net.i2p.crypto.DummyElGamalEngine;
import net.i2p.crypto.DummyPooledRandomSource;
//import net.i2p.crypto.DummyPooledRandomSource;
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.crypto.ElGamalEngine;
import net.i2p.crypto.HMAC256Generator;
@ -29,7 +29,7 @@ import net.i2p.util.FileUtil;
import net.i2p.util.FortunaRandomSource;
import net.i2p.util.KeyRing;
import net.i2p.util.LogManager;
import net.i2p.util.PooledRandomSource;
//import net.i2p.util.PooledRandomSource;
import net.i2p.util.RandomSource;
/**
@ -352,6 +352,32 @@ public class I2PAppContext {
return ival;
}
/**
* Return a boolean with a boolean default
* @since 0.7.12
*/
public boolean getProperty(String propName, boolean defaultVal) {
String val = getProperty(propName);
if (val == null)
return defaultVal;
return Boolean.valueOf(val).booleanValue();
}
/**
* Default false
* @since 0.7.12
*/
public boolean getBooleanProperty(String propName) {
return Boolean.valueOf(getProperty(propName)).booleanValue();
}
/**
* @since 0.7.12
*/
public boolean getBooleanPropertyDefaultTrue(String propName) {
return getProperty(propName, true);
}
/**
* Access the configuration attributes of this context, listing the properties
* provided during the context construction, as well as the ones included in
@ -703,12 +729,12 @@ public class I2PAppContext {
private void initializeRandom() {
synchronized (this) {
if (_random == null) {
if (true)
//if (true)
_random = new FortunaRandomSource(this);
else if ("true".equals(getProperty("i2p.weakPRNG", "false")))
_random = new DummyPooledRandomSource(this);
else
_random = new PooledRandomSource(this);
//else if ("true".equals(getProperty("i2p.weakPRNG", "false")))
// _random = new DummyPooledRandomSource(this);
//else
// _random = new PooledRandomSource(this);
}
_randomInitialized = true;
}

View File

@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.io.UnsupportedEncodingException;
@ -104,7 +105,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
*/
private static final int VERSION_BYTES = 16;
private static final int HEADER_BYTES = Signature.SIGNATURE_BYTES + VERSION_BYTES;
public static final int HEADER_BYTES = Signature.SIGNATURE_BYTES + VERSION_BYTES;
private static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
private static I2PAppContext _context;
@ -178,30 +179,50 @@ D8usM7Dxp5yrDrCYZ5AIijc=
return true;
}
/**
* Do we know about the following key?
* @since 0.7.12
*/
public boolean haveKey(String key) {
if (key.length() != KEYSIZE_B64_BYTES)
return false;
SigningPublicKey signingPublicKey = new SigningPublicKey();
try {
signingPublicKey.fromBase64(key);
} catch (DataFormatException dfe) {
return false;
}
return _trustedKeys.containsKey(signingPublicKey);
}
/**
* Parses command line arguments when this class is used from the command
* line.
* Exits 1 on failure so this can be used in scripts.
*
* @param args Command line parameters.
*/
public static void main(String[] args) {
boolean ok = false;
try {
if ("keygen".equals(args[0])) {
genKeysCLI(args[1], args[2]);
ok = genKeysCLI(args[1], args[2]);
} else if ("showversion".equals(args[0])) {
showVersionCLI(args[1]);
ok = showVersionCLI(args[1]);
} else if ("sign".equals(args[0])) {
signCLI(args[1], args[2], args[3], args[4]);
ok = signCLI(args[1], args[2], args[3], args[4]);
} else if ("verifysig".equals(args[0])) {
verifySigCLI(args[1]);
ok = verifySigCLI(args[1]);
} else if ("verifyupdate".equals(args[0])) {
verifyUpdateCLI(args[1]);
ok = verifyUpdateCLI(args[1]);
} else {
showUsageCLI();
}
} catch (ArrayIndexOutOfBoundsException aioobe) {
showUsageCLI();
}
if (!ok)
System.exit(1);
}
/**
@ -217,7 +238,8 @@ D8usM7Dxp5yrDrCYZ5AIijc=
return (new VersionComparator()).compare(currentVersion, newVersion) < 0;
}
private static final void genKeysCLI(String publicKeyFile, String privateKeyFile) {
/** @return success */
private static final boolean genKeysCLI(String publicKeyFile, String privateKeyFile) {
FileOutputStream fileOutputStream = null;
_context = I2PAppContext.getGlobalContext();
@ -240,6 +262,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
} catch (Exception e) {
System.err.println("Error writing keys:");
e.printStackTrace();
return false;
} finally {
if (fileOutputStream != null)
try {
@ -247,6 +270,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
} catch (IOException ioe) {
}
}
return true;
}
private static final void showUsageCLI() {
@ -257,40 +281,48 @@ D8usM7Dxp5yrDrCYZ5AIijc=
System.err.println(" TrustedUpdate verifyupdate signedFile");
}
private static final void showVersionCLI(String signedFile) {
String versionString = new TrustedUpdate().getVersionString(new File(signedFile));
/** @return success */
private static final boolean showVersionCLI(String signedFile) {
String versionString = getVersionString(new File(signedFile));
if (versionString.equals(""))
System.out.println("No version string found in file '" + signedFile + "'");
else
System.out.println("Version: " + versionString);
return !versionString.equals("");
}
private static final void signCLI(String inputFile, String signedFile, String privateKeyFile, String version) {
/** @return success */
private static final boolean signCLI(String inputFile, String signedFile, String privateKeyFile, String version) {
Signature signature = new TrustedUpdate().sign(inputFile, signedFile, privateKeyFile, version);
if (signature != null)
System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
else
System.out.println("Error signing input file '" + inputFile + "'");
return signature != null;
}
private static final void verifySigCLI(String signedFile) {
/** @return valid */
private static final boolean verifySigCLI(String signedFile) {
boolean isValidSignature = new TrustedUpdate().verify(new File(signedFile));
if (isValidSignature)
System.out.println("Signature VALID");
else
System.out.println("Signature INVALID");
return isValidSignature;
}
private static final void verifyUpdateCLI(String signedFile) {
/** @return if newer */
private static final boolean verifyUpdateCLI(String signedFile) {
boolean isUpdate = new TrustedUpdate().isUpdatedVersion(CoreVersion.VERSION, new File(signedFile));
if (isUpdate)
System.out.println("File version is newer than current version.");
else
System.out.println("File version is older than or equal to current version.");
return isUpdate;
}
/**
@ -331,7 +363,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
* @return The version string read, or an empty string if no version string
* is present.
*/
public String getVersionString(File signedFile) {
public static String getVersionString(File signedFile) {
FileInputStream fileInputStream = null;
try {
@ -365,6 +397,45 @@ D8usM7Dxp5yrDrCYZ5AIijc=
}
}
/**
* Reads the version string from an input stream
*
* @param inputStream containing at least 56 bytes
*
* @return The version string read, or an empty string if no version string
* is present.
*/
public static String getVersionString(InputStream inputStream) {
try {
long skipped = inputStream.skip(Signature.SIGNATURE_BYTES);
if (skipped != Signature.SIGNATURE_BYTES)
return "";
byte[] data = new byte[VERSION_BYTES];
int bytesRead = DataHelper.read(inputStream, data);
if (bytesRead != VERSION_BYTES) {
return "";
}
for (int i = 0; i < VERSION_BYTES; i++)
if (data[i] == 0x00) {
return new String(data, 0, i, "UTF-8");
}
return new String(data, "UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + uee.getMessage());
} catch (IOException ioe) {
return "";
} finally {
if (inputStream != null)
try {
inputStream.close();
} catch (IOException ioe) {
}
}
}
/** version in the .sud file, valid only after calling migrateVerified() */
public String newVersion() {
return _newVersion;
@ -410,6 +481,22 @@ D8usM7Dxp5yrDrCYZ5AIijc=
if (!verify(signedFile))
return "Unknown signing key or corrupt file";
return migrateFile(signedFile, outputFile);
}
/**
* Extract the file. Skips and ignores the signature and version. No verification.
*
* @param signedFile A signed update file.
* @param outputFile The file to write the verified data to.
*
* @return <code>null</code> if the
* data was moved, and an error <code>String</code> otherwise.
*/
public String migrateFile(File signedFile, File outputFile) {
if (!signedFile.exists())
return "File not found: " + signedFile.getAbsolutePath();
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
@ -610,6 +697,23 @@ D8usM7Dxp5yrDrCYZ5AIijc=
return false;
}
/**
* Verifies the DSA signature of a signed update file.
*
* @param signedFile The signed update file to check.
*
* @return signer (could be empty string) or null if invalid
* @since 0.7.12
*/
public String verifyAndGetSigner(File signedFile) {
for (SigningPublicKey signingPublicKey : _trustedKeys.keySet()) {
boolean isValidSignature = verify(signedFile, signingPublicKey);
if (isValidSignature)
return _trustedKeys.get(signingPublicKey);
}
return null;
}
/**
* Verifies the DSA signature of a signed update file.
*

View File

@ -17,6 +17,8 @@ import net.i2p.data.DataHelper;
* (likely) small buffer to reduce the frequency of prng recalcs (though
* the recalcs are now more time consuming).
*
* @deprecated Unused! See FortunaRandomSource
*
*/
public class BufferedRandomSource extends RandomSource {
private byte _buffer[];

View File

@ -12,9 +12,12 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* General helper methods for messing with files
*
@ -75,6 +78,10 @@ public class FileUtil {
}
}
/**
* As of release 0.7.12, any files inside the zip that have a .jar.pack or .war.pack suffix
* are transparently unpacked to a .jar or .war file using unpack200.
*/
public static boolean extractZip(File zipfile, File targetDir) {
ZipFile zip = null;
try {
@ -109,15 +116,22 @@ public class FileUtil {
} else {
try {
InputStream in = zip.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(target);
int read = 0;
while ( (read = in.read(buf)) != -1) {
fos.write(buf, 0, read);
if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) {
target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length()));
JarOutputStream fos = new JarOutputStream(new FileOutputStream(target));
Pack200.newUnpacker().unpack(in, fos);
fos.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked");
} else {
FileOutputStream fos = new FileOutputStream(target);
int read = 0;
while ( (read = in.read(buf)) != -1) {
fos.write(buf, 0, read);
}
fos.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted");
}
fos.close();
in.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted");
} catch (IOException ioe) {
System.err.println("ERROR: Error extracting the zip entry (" + entry.getName() + "]");
ioe.printStackTrace();

View File

@ -0,0 +1,130 @@
package net.i2p.util;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import net.i2p.I2PAppContext;
/**
* Fetch exactly the first 'size' bytes into a stream
* Anything less or more will throw an IOException
* No retries, no min and max size options, no timeout option
* Useful for checking .sud versions
*
* @since 0.7.12
* @author zzz
*/
public class PartialEepGet extends EepGet {
long _fetchSize;
/** @param size fetch exactly this many bytes */
public PartialEepGet(I2PAppContext ctx, String proxyHost, int proxyPort,
OutputStream outputStream, String url, long size) {
// we're using this constructor:
// public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
super(ctx, true, proxyHost, proxyPort, 0, size, size, null, outputStream, url, true, null, null);
_fetchSize = size;
}
/**
* PartialEepGet [-p 127.0.0.1:4444] [-l #bytes] url
*
*/
public static void main(String args[]) {
String proxyHost = "127.0.0.1";
int proxyPort = 4444;
// 40 sig + 16 version for .suds
long size = 56;
String url = null;
try {
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-p")) {
proxyHost = args[i+1].substring(0, args[i+1].indexOf(':'));
String port = args[i+1].substring(args[i+1].indexOf(':')+1);
proxyPort = Integer.parseInt(port);
i++;
} else if (args[i].equals("-l")) {
size = Long.parseLong(args[i+1]);
i++;
} else if (args[i].startsWith("-")) {
usage();
return;
} else {
url = args[i];
}
}
} catch (Exception e) {
e.printStackTrace();
usage();
return;
}
if (url == null) {
usage();
return;
}
String saveAs = suggestName(url);
OutputStream out;
try {
// resume from a previous eepget won't work right doing it this way
out = new FileOutputStream(saveAs);
} catch (IOException ioe) {
System.err.println("Failed to create output file " + saveAs);
return;
}
EepGet get = new PartialEepGet(I2PAppContext.getGlobalContext(), proxyHost, proxyPort, out, url, size);
get.addStatusListener(get.new CLIStatusListener(1024, 40));
if (get.fetch(45*1000, -1, 60*1000)) {
System.err.println("Last-Modified: " + get.getLastModified());
System.err.println("Etag: " + get.getETag());
} else {
System.err.println("Failed " + url);
}
}
private static void usage() {
System.err.println("PartialEepGet [-p 127.0.0.1:4444] [-l #bytes] url");
}
@Override
protected String getRequest() throws IOException {
StringBuilder buf = new StringBuilder(2048);
URL url = new URL(_actualURL);
String proto = url.getProtocol();
String host = url.getHost();
int port = url.getPort();
String path = url.getPath();
String query = url.getQuery();
if (query != null)
path = path + '?' + query;
if (!path.startsWith("/"))
path = "/" + path;
if ( (port == 80) || (port == 443) || (port <= 0) ) path = proto + "://" + host + path;
else path = proto + "://" + host + ":" + port + path;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Requesting " + path);
buf.append("GET ").append(_actualURL).append(" HTTP/1.1\r\n");
buf.append("Host: ").append(url.getHost()).append("\r\n");
buf.append("Range: bytes=");
buf.append(_alreadyTransferred);
buf.append('-');
buf.append(_fetchSize - 1);
buf.append("\r\n");
if (_shouldProxy)
buf.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
buf.append("Cache-control: no-cache\r\n" +
"Pragma: no-cache\r\n");
// This will be replaced if we are going through I2PTunnelHTTPClient
buf.append("User-Agent: " + USER_AGENT + "\r\n" +
"Accept-Encoding: \r\n" +
"Connection: close\r\n\r\n");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Request: [" + buf.toString() + "]");
return buf.toString();
}
}

View File

@ -15,6 +15,9 @@ import net.i2p.data.Base64;
/**
* Maintain a set of PRNGs to feed the apps
*
* @deprecated Unused! See FortunaRandomSource
*
*/
public class PooledRandomSource extends RandomSource {
private Log _log;

View File

@ -126,4 +126,13 @@ public abstract class Translate {
}
return rv;
}
/**
* Clear the cache.
* Call this after adding new bundles to the classpath.
* @since 0.7.12
*/
public static void clearCache() {
_missing.clear();
}
}

View File

@ -5,7 +5,8 @@ import java.util.StringTokenizer;
/**
* Compares versions.
* Characters other than [0-9.] are ignored.
* Characters other than [0-9.-_] are ignored.
* I2P only uses '.' but Sun Java uses '_' and plugins may use any of '.-_'
* Moved from TrustedUpdate.java
* @since 0.7.10
*/
@ -15,8 +16,8 @@ public class VersionComparator implements Comparator<String> {
// try it the easy way first
if (l.equals(r))
return 0;
StringTokenizer lTokens = new StringTokenizer(sanitize(l), ".");
StringTokenizer rTokens = new StringTokenizer(sanitize(r), ".");
StringTokenizer lTokens = new StringTokenizer(sanitize(l), VALID_SEPARATOR_CHARS);
StringTokenizer rTokens = new StringTokenizer(sanitize(r), VALID_SEPARATOR_CHARS);
while (lTokens.hasMoreTokens() && rTokens.hasMoreTokens()) {
String lNumber = lTokens.nextToken();
@ -48,7 +49,8 @@ public class VersionComparator implements Comparator<String> {
return left - right;
}
private static final String VALID_VERSION_CHARS = "0123456789.";
private static final String VALID_SEPARATOR_CHARS = ".-_";
private static final String VALID_VERSION_CHARS = "0123456789" + VALID_SEPARATOR_CHARS;
private static final String sanitize(String versionString) {
StringBuilder versionStringBuilder = new StringBuilder(versionString);

View File

@ -1,41 +0,0 @@
<? xml version="1.0" encoding="UTF-8" ?>
<project basedir="." default="help" name="deploy">
<property name="deploy.google.script" value="http://ant-googlecode.googlecode.com/files/ant-googlecode-0.0.1.jar" />
<property name="dir" value="tmp-deploy" />
<property file="deploy.properties" />
<target name="help">
<echo message="Useful targets: " />
<echo message=" fetch: Fetch the files from the gatekeeper's site" />
<echo message=" deploy: Deploy the files to the remote sites(currently google code only)" />
</target>
<target name="init">
<mkdir dir="${dir}" />
</target>
<target name="fetch">
<!-- TODO: write this(using eepget?) -->
</target>
<target name="pre-deploy" depends="init">
<get src="${deploy.google.script}" dest="${dir}/ant-googlecode.jar" />
<taskdef classname="net.bluecow.googlecode.ant.GoogleCodeUploadTask" classpath="path/to/ant-googlecode.jar" name="gcupload"/>
</target>
<target name="validate-deploy">
<!-- check deploy.properties -->
<fail "Version not set" unless="app.version" />
<fail "Google: Username not set" unless="deploy.google.username" />
<fail "Google: Password not set" unless="deploy.google.password" />
<!-- TODO: check for existence of files -->
<input message="Want to continue?" validargs="y,j,n" addproperty="deploy.continue" />
<fail message="Aborted.">
<condition>
<equals arg1="${deploy.continue}" arg2="n" />
</condition>
</fail>
</target>
<target name="deploy" depends="pre-deploy,validate-deploy">
<!-- TODO: add all files -->
</target>
</project>

View File

@ -1,3 +1,205 @@
2010-03-08 zzz
* Floodfills: Increase max to 60 (was 28) and min to 45 (was 20)
* i2psnark: Better track outgoing bandwidth by incrementing
counter before the blocking write
* Random: Remove and deprecate some old classses
* Reseeder: Reduce max response size to 1MB (was 8MB)
2010-03-05 zzz
* Console:
- Tag reseed messages
- Translate country names on flag popups
* I2PSOCKSIRCTunnel:
- New, for filtering IRC client traffic when using SOCKS
* I2PTunnelIRCClient:
- Make filter classes static and public for use by SOCKS
- Eliminate redundant case conversion
- Pass ISON message through (jIRCii uses it for pings)
- Switch back to StringBuffer since it's used by 2 threads
- Set daemon on filter threads
* SOCKS5Server:
- Fix handling of multiple authentication methods
2010-03-02 zzz
* Console:
- Add link to jobs.jsp on configservice.jsp
- Add plugin disableStop support
* Context: Add boolean getProperty methods
* HTTP Proxy:
- Fix address helper conflicts caused by last checkin
- Use B32 instead of random hostname for conflict link
* LoadClientAppsJob:
- Fix unquoted arg after quoted arg
- Logging cleanup
2010-02-27 zzz
* eepsite: Add some help to index.html
* HTTP Proxy:
- Put B32 instead of B64 in Host: header, saves 450 bytes
- Eliminate some redundant lookups
- Fix http://i2p/b64/ and /eepproxy/site/ requests
- Disallow a port specified for an i2p address
- Cleanup and comments
- For more info see http://zzz.i2p/topics/566
* i2psnark:
- Fix NPE after create file failure
- Sanitize more characters in file names
* netdb: Fix NPE after OOM http://trac.i2p2.i2p/ticket/38
* NTCP Transport:
- Replace lists with concurrent queues in EventPumper
and NTCPConnection to remove global locks
- Java 5 cleanup
* Plugins: Support console themes
* UDP Transport:
- Replace the unused-since-2006 TimedWeightedPriorityMessageQueue
with DummyThrottle
- Don't instantiate and start TWPMQ Cleaner and OutboundRefiller
threads, part of priority queues unused since 0.6.1.11
- Don't instantiate and start UDPFlooder, it is for testing only
- Prevent NPE http://zzz.i2p/topics/571
2010-02-23 zzz
* Unzip: Any files in the zip with a .jar.pack or .war.pack extension
will be transparently unpacked with unpack200. Savings is about 60%.
Someday we will do this for suds, but we can do it for xpi2ps now.
* build: Add updater200 target
2010-02-22 zzz
* configclients.jsp:
- Add js delete confirm
- Remove delete button for webapps
* i2psnark:
- Ignore a non-i2p tracker in a torrent rather than deleting
the torrent, thus "converting" a torrent to in-netowrk use
via the open trackers
- Add js delete confirm
2010-02-19 zzz
* i2psnark: Make file box bigger
* Plugins:
- Fix display of download status
- Unhide
2010-02-18 zzz
* Clock: Slew tweak
* i2psnark: Improve error message when finding HTML
* HTTP Proxy: Fix blank page instead of error page for eepsite unreachable
* Plugins:
- Fix plugin start button
- Change signer prop to match docs
- Tweaks
* Transport:
- Fix recognition of IP change when not firewalled
- Require consecutive identical results from two peers before changing IP
* TrustedUpdate: CLI exits 1 on failure for ease of use in scripts
2010-02-15 zzz
Propagate from 3 dev branches.
i2p.i2p.zzz.test:
* Clock:
- getFramedAveragePeerClockSkew() now returns a long (ms);
was a Long (s)
- Implement NTP-style clock slewing so the clock is adjusted
gradually
- Implement clock strata so we prefer better clocks
- Implement a timestamper in the transport so we will periodically
update the clock even if NTP is not working.
This allows the router to converge the clock instead of simply
hoping the first connected peer is correct.
- Slow down NTP attempts after several consecutive failures
* Console: refactor and tag update messages
* Streaming: MessageOutputStream logging tweaks
* Transport:
- Prepare for using address costs
- Adjust bids based on address cost
- Increase cost if near transport capacity, disabled until 0.7.12
- Clear the geoip negative cache periodically
i2p.i2p.zzz.plugin:
* Jrobin: Move from routerconsole.jar to its own jrobin.jar,
adjust classpath in routerconsole.jar manifest
* Plugins:
New plugin support, hidden for now, enable with router.enablePlugins=true
Configure and add plugins on configclients.jsp
Supports the following:
- Console webapps
- New translation bundles
- Link on console
- Anything that can be started in clients.config
- Additions to classpath for clients and webapps
* TrustedUpdate:
- Add method to check if we know about a key
- Add method to add a key
- Add method to extract without verifying
- Add method to get key signer name
* Webapps: Allow additions to a webapp classpath.
- Pull jstl.jar and standard.jar out of susidns.war
- Remove 100KB of duplicate classes from i2psnark.war
i2p.i2p.zzz.VTBM:
* I2NP: Add UnknownI2NPMessage so we can route unknown message types
* Tunnel Builds:
- Add getRecordCount() to TunnelBuildMessage and TunnelBuildReplyMessage so they can be extended.
- New I2NP Messages VariableTunnelBuildMessage and VariableTunnelBuildReplyMessage,
which contain the number of request slots in them.
- Convert all static assumptions of 8 slots to getRecordCount()
- Use the new VTBM if all hops in the tunnel and the OBEP or IBGW of the reply tunnel
support it, and the tunnel is 4 hops or shorter.
- Reply to a VTBM with a VTBRM of the same size
- Make BuildReplyHandler static
- Convert the currentlyBuilding List to a ConcurrentHashMap to speed reply lookups
and eliminate a global lock; don't put fallback tunnels in there
- Add new tunnel.corruptBuildReply stat
- Various cleanups and javadoc
- Fix first hop expiration for Build Messages, was way too long
- Randomize Build Message expiration to make it harder to guess hop position
- Save expired tunnel build configs for a while, so that we will still use the tunnel
and update peer stats if the reply comes in late
- Don't update our own profile for Tunnel Build Replies
- VTBM generation only through routers >= 0.7.12
- VTBM generation disabled for now
* 2010-02-15 0.7.11 released
2010-02-13 sponge
* Fix addWebApplications API goofup
* Bump BOB version, which I forgot to do.
2010-02-13 zzz
* Floodfills: Increase max to 28 (was 15) and min to 20 (was 10)
2010-02-12 sponge
* org.mortbay.jetty.Server modified method to accept attributes for
batch webapp launches via addWebApplications.
2010-02-10 zzz
* I2PTunnelRunner: Flush initial data, for some reason it wasn't
getting flushed ever in some cases.
2010-02-10 zzz
64-bit windows installer fixes. Still no 64-bit wrapper.
Thanks eche|on for testing!
* Izpack:
Add 64-bit windows dll so installer doesn't die trying to add shortcuts
* Launch4j:
Upgrade to launch4j 3.0.1 2008-07-20.
The license is BSD for launch4j and MIT for the wrapper code in head/
Changelog is in installer/lib/launch4j/web/changelog.html
Hopefully this will fix installs for 64-bit JRE on 64-bit windows.
The previous version was 2.0-RC3 2005-08-13.
The previous license was GPLv2 for launch4j and LGPLv2.1 for the wrapper code in head/
The bin/ld.exe and bin/windres.exe files were contributed by
i2p users in 2005 so the i2p installer could be built on windows.
They have not been updated for 3.0.1, so pkg builds on windows
will presumably still get 2.0-RC3.
2010-02-06 zzz
* Console: Fix saving update keys, was broken in 0.7.10
* i2psnark: Add transmission ID
* news.xml: Wrap i2p version tags in XML comment
* Transport: Try yet again to prevent two NTCP pumpers
2010-02-04 zzz
* i2psnark: Fix sending stopped events to the tracker

View File

@ -4,7 +4,7 @@
<info>
<appname>i2p</appname>
<appversion>0.7.10</appversion>
<appversion>0.7.11</appversion>
<authors>
<author name="I2P" email="http://forum.i2p2.de/"/>
</authors>
@ -78,7 +78,20 @@
<langpack iso3="ukr"/>
</locale>
<!--
The <os> tag can be used to restrict the inclusion into the uninstaller
to a specific operating system family, architecture or version.
The inclusion into the installer will be always done.
Here's a sample :
<native type="izpack" name="ShellLink.dll">
<os family="windows"/>
</native>
This doesn't appear to be necessary, the dlls don't get put in Uninstaller/uninstaller.jar on linux
-->
<native type="izpack" name="ShellLink.dll" />
<native type="izpack" name="ShellLink_x64.dll" />
<resources>
<res id="Installer.image" src="installer/resources/i2plogo.png" />

View File

@ -1,340 +1,30 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Copyright (c) 2004, 2008 Grzegorz Kowal
Preamble
All rights reserved.
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.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
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.
* 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 Launch4j nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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.
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.

View File

@ -0,0 +1,13 @@
This is launch4j 3.0.1 2008-07-20.
The license is BSD for launch4j and MIT for the wrapper code in head/
Changelog is in web/changelog.html
We upgraded to get support for 64-bit JRE on 64-bit windows.
The previous version was 2.0-RC3 2005-08-13.
The license was GPLv2 for launch4j and LGPLv2.1 for the wrapper code in head/
The bin/ld.exe and bin/windres.exe files were contributed by
i2p users so the i2p installer could be built on windows.
They have not been updated for 3.0.1.

View File

@ -1,340 +0,0 @@
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) 19yy <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) 19yy 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.

View File

@ -1,13 +1,9 @@
<project name="launch4j" default="compile" basedir=".">
<property name="versionNumber" value="2.0.0.0" />
<property name="version" value="2.0.RC3" />
<property name="src" location="src" />
<property name="lib" location="lib" />
<property name="build" location="build" />
<property name="dist" location="${user.home}/dist/${ant.project.name}" />
<property name="jar" location="./${ant.project.name}.jar" />
<property name="launch4j.dir" location="." />
<property name="javac.compilerargs" value="" />
<path id="dist.classpath">
<pathelement path="${build}" />
@ -19,18 +15,20 @@
<target name="init">
<tstamp />
<mkdir dir="${build}" />
<mkdir dir="${dist}" />
</target>
<target name="compile" depends="init" description="compile the source">
<javac srcdir="${src}" destdir="${build}" classpathref="dist.classpath" source="1.4" debug="on" />
<compilerarg line="${javac.compilerargs}" />
</javac>
<copy todir="${build}/images">
<fileset dir="${src}/images">
<include name="**/*" />
</fileset>
</copy>
<copy todir="${build}">
<fileset dir="${src}">
<include name="**/*.properties" />
</fileset>
</copy>
</target>
<target name="jar" depends="compile" description="create jar">
@ -42,19 +40,12 @@
</pathconvert>
<!-- Put everything in ${build} into a jar file -->
<jar jarfile="${jar}">
<fileset dir="${build}" includes="**/*" />
<fileset dir="${build}" excludes="**/messages_es.properties" />
<manifest>
<attribute name="Main-Class" value="net.sf.launch4j.Main" />
<attribute name="Class-Path" value=". ${dist.classpath}" />
</manifest>
</jar>
<jar jarfile="./launcher.jar">
<fileset dir="${build}" includes="net/sf/launch4j/Launcher.class" />
<manifest>
<attribute name="Main-Class" value="net.sf.launch4j.Launcher" />
<attribute name="Class-Path" value=". ./${ant.project.name}.jar ${dist.classpath}" />
</manifest>
</jar>
</target>
<target name="demo" depends="jar" description="build the demos">
@ -62,41 +53,10 @@
<ant dir="./demo/SimpleApp" inheritAll="false" />
</target>
<target name="dist.linux" depends="jar" description="generate the Linux distribution">
<!-- changes executables to default mode!
<tar tarfile="${dist}/${ant.project.name}-${version}-linux.tgz" basedir="."
compression="gzip" excludes="**/build/** **/CVS/** **/*.exe"/> -->
<exec executable="tar" failonerror="true">
<arg line="-czC .. --exclude build --exclude CVS --exclude *.bat --exclude *.exe --exclude launch4j/l4j --exclude launch4j/launcher.jar -f ${dist}/${ant.project.name}-${version}-linux.tgz ./launch4j" />
</exec>
</target>
<target name="dist.win32" depends="jar" description="generate the Windows distribution">
<taskdef name="launch4j" classname="net.sf.launch4j.ant.Launch4jTask" classpath="${build}:./lib/xstream.jar" />
<launch4j configFile="./l4j/launch4j.xml"
fileVersion="${versionNumber}" txtFileVersion="${version}"
productVersion="${versionNumber}" txtProductVersion="${version}" />
<launch4j configFile="./l4j/launch4jc.xml"
fileVersion="${versionNumber}" txtFileVersion="${version}"
productVersion="${versionNumber}" txtProductVersion="${version}" />
<zip destfile="${dist}/${ant.project.name}-${version}-win32.zip">
<zipfileset dir="." prefix="launch4j" excludes="**/build/** **/CVS/** bin/ld bin/windres l4j/** launch4j launcher.jar" />
</zip>
</target>
<target name="dist" depends="demo, dist.linux, dist.win32, clean" description="generate all distributions" />
<target name="clean" description="clean up">
<delete dir="${build}" />
<delete file="${jar}" />
<delete file="./launcher.jar" />
<delete>
<fileset dir="." includes="*.exe"/>
</delete>
<ant dir="./demo/ConsoleApp" target="clean" inheritAll="false" />
<ant dir="./demo/SimpleApp" target="clean" inheritAll="false" />
</target>
<target name="clean.all" depends="clean" description="clean up">
<delete dir="${build}" />
</target>
</project>

View File

@ -1 +0,0 @@
build

View File

@ -1,59 +1,57 @@
<project name="ConsoleApp" default="exe" basedir=".">
<property name="src" location="src"/>
<property name="lib" location="lib"/>
<property name="build" location="build"/>
<property name="src" location="src" />
<property name="lib" location="lib" />
<property name="build" location="build" />
<property name="launch4j.dir" location="../.." />
<path id="dist.classpath">
<pathelement path="${build}"/>
<pathelement path="${build}" />
<fileset dir="${lib}">
<include name="**/*.jar"/>
</fileset>
<include name="**/*.jar" />
</fileset>
</path>
<target name="init">
<tstamp/>
<mkdir dir="${build}"/>
<tstamp />
<mkdir dir="${build}" />
</target>
<target name="compile" depends="init" description="compile the source">
<javac srcdir="${src}" destdir="${build}" classpathref="dist.classpath" debug="on"/>
<javac srcdir="${src}" destdir="${build}" classpathref="dist.classpath" source="1.4" debug="on" />
</target>
<target name="jar" depends="compile" description="create the jar">
<fileset dir="${lib}" id="lib.dist.fileset">
<include name="**/*.jar"/>
<include name="**/*.jar" />
</fileset>
<pathconvert pathsep=" " property="dist.classpath" refid="lib.dist.fileset">
<map from="${lib}" to=".\lib"/>
<map from="${lib}" to=".\lib" />
</pathconvert>
<!-- Put everything in ${build} into a jar file -->
<jar jarfile="${ant.project.name}.jar">
<fileset dir="${build}" includes="**/*"/>
<fileset dir="${build}" includes="**/*" />
<manifest>
<!-- SET YOUR MAIN CLASS HERE -->
<attribute name="Main-Class" value="net.sf.launch4j.example.ConsoleApp"/>
<attribute name="Class-Path" value=". ${dist.classpath}"/>
<attribute name="Main-Class" value="net.sf.launch4j.example.ConsoleApp" />
<attribute name="Class-Path" value=". ${dist.classpath}" />
</manifest>
</jar>
</target>
<target name="exe" depends="jar">
<taskdef name="launch4j"
classname="net.sf.launch4j.ant.Launch4jTask"
classpath="${launch4j.dir}/launch4j.jar
<taskdef name="launch4j" classname="net.sf.launch4j.ant.Launch4jTask" classpath="${launch4j.dir}/launch4j.jar
:${launch4j.dir}/lib/xstream.jar" />
<launch4j>
<config headerType="1" jar="ConsoleApp.jar" outfile="ConsoleApp.exe"
errTitle="ConsoleApp" chdir="." customProcName="true" icon="l4j/ConsoleApp.ico">
<jre minVersion="1.4.0" />
</config>
<config headerType="console" jar="ConsoleApp.jar" outfile="ConsoleApp.exe" errTitle="ConsoleApp" chdir="." customProcName="true" icon="l4j/ConsoleApp.ico">
<singleInstance mutexName="net.sf.launch4j.example.ConsoleApp" />
<jre minVersion="1.4.0" />
</config>
</launch4j>
</target>
<target name="clean" description="clean up" >
<delete dir="${build}"/>
<delete file="${ant.project.name}.jar"/>
<delete file="${ant.project.name}.exe"/>
<target name="clean" description="clean up">
<delete dir="${build}" />
<delete file="${ant.project.name}.jar" />
<delete file="${ant.project.name}.exe" />
</target>
</project>

View File

@ -1,20 +1,34 @@
/*
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2005 Grzegorz Kowal
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
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.
Copyright (c) 2004, 2007 Grzegorz Kowal
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.
All rights reserved.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
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 Launch4j nor the names of its 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.
*/
package net.sf.launch4j.example;
@ -46,10 +60,11 @@ public class ConsoleApp {
try {
BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = is.readLine()) != null) {
while ((line = is.readLine()) != null && !line.equalsIgnoreCase("quit")) {
System.out.print("You wrote: " + line + "\n\n>");
}
is.close();
System.exit(123);
} catch (IOException e) {
System.err.print(e);
}

View File

@ -1,340 +1,30 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Copyright (c) 2004, 2007 Grzegorz Kowal
Preamble
All rights reserved.
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.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
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.
* 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 Launch4j nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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.
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.

Binary file not shown.

View File

@ -0,0 +1,52 @@
<project name="SimpleApp" default="exe" basedir=".">
<property name="src" location="src" />
<property name="lib" location="lib" />
<property name="build" location="build" />
<property name="launch4j.dir" location="../.." />
<path id="dist.classpath">
<pathelement path="${build}" />
<fileset dir="${lib}">
<include name="**/*.jar" />
</fileset>
</path>
<target name="init">
<tstamp />
<mkdir dir="${build}" />
</target>
<target name="compile" depends="init" description="compile the source">
<javac srcdir="${src}" destdir="${build}" classpathref="dist.classpath" source="1.4" debug="on" />
</target>
<target name="jar" depends="compile" description="create the jar">
<fileset dir="${lib}" id="lib.dist.fileset">
<include name="**/*.jar" />
</fileset>
<pathconvert pathsep=" " property="dist.classpath" refid="lib.dist.fileset">
<map from="${lib}" to=".\lib" />
</pathconvert>
<!-- Put everything in ${build} into a jar file -->
<jar jarfile="${ant.project.name}.jar">
<fileset dir="${build}" includes="**/*" />
<manifest>
<!-- SET YOUR MAIN CLASS HERE -->
<attribute name="Main-Class" value="net.sf.launch4j.example.SimpleApp" />
<attribute name="Class-Path" value=". ${dist.classpath}" />
</manifest>
</jar>
</target>
<target name="exe" depends="jar">
<taskdef name="launch4j" classname="net.sf.launch4j.ant.Launch4jTask" classpath="${launch4j.dir}/launch4j.jar
:${launch4j.dir}/lib/xstream.jar" />
<launch4j configFile="./l4j/SimpleApp.xml" />
</target>
<target name="clean" description="clean up">
<delete dir="${build}" />
<delete file="${ant.project.name}.jar" />
<delete file="${ant.project.name}.exe" />
</target>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@ -0,0 +1,18 @@
<launch4jConfig>
<headerType>gui</headerType>
<jar>../SimpleApp.jar</jar>
<outfile>../SimpleApp.exe</outfile>
<errTitle>SimpleApp</errTitle>
<chdir>.</chdir>
<customProcName>true</customProcName>
<icon>SimpleApp.ico</icon>
<jre>
<minVersion>1.4.0</minVersion>
</jre>
<splash>
<file>splash.bmp</file>
<waitForWindow>true</waitForWindow>
<timeout>60</timeout>
<timeoutErr>true</timeoutErr>
</splash>
</launch4jConfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,8 @@
Put your jar libs here and the build script will include them
in the classpath stored inside the jar manifest.
In order to run your application move the output exe file from
the dist directory to the same level as lib.
SimpleApp.exe
lib/
lib/xml.jar

View File

@ -0,0 +1 @@
To build the example application set JAVA_HOME and ANT_HOME environment variables.

View File

@ -0,0 +1,104 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
Copyright (c) 2004, 2007 Grzegorz Kowal
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 Launch4j nor the names of its 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.
*/
package net.sf.launch4j.example;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
public class SimpleApp extends JFrame {
public SimpleApp(String[] args) {
super("Java Application");
final int inset = 100;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds (inset, inset,
screenSize.width - inset * 2, screenSize.height - inset * 2);
JMenu menu = new JMenu("File");
menu.add(new JMenuItem("Open"));
menu.add(new JMenuItem("Save"));
JMenuBar mb = new JMenuBar();
mb.setOpaque(true);
mb.add(menu);
setJMenuBar(mb);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(123);
}});
setVisible(true);
StringBuffer sb = new StringBuffer("Java version: ");
sb.append(System.getProperty("java.version"));
sb.append("\nJava home: ");
sb.append(System.getProperty("java.home"));
sb.append("\nCurrent dir: ");
sb.append(System.getProperty("user.dir"));
if (args.length > 0) {
sb.append("\nArgs: ");
for (int i = 0; i < args.length; i++) {
sb.append(args[i]);
sb.append(' ');
}
}
JOptionPane.showMessageDialog(this,
sb.toString(),
"Info",
JOptionPane.INFORMATION_MESSAGE);
}
public static void setLAF() {
JFrame.setDefaultLookAndFeelDecorated(true);
Toolkit.getDefaultToolkit().setDynamicLayout(true);
System.setProperty("sun.awt.noerasebackground","true");
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
System.err.println("Failed to set LookAndFeel");
}
}
public static void main(String[] args) {
setLAF();
new SimpleApp(args);
}
}

View File

@ -1,504 +1,23 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 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.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; 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.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
Copyright (c) 2004, 2007 Grzegorz Kowal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Binary file not shown.

View File

@ -1,504 +1,23 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 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.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; 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.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
Copyright (c) 2004, 2008 Grzegorz Kowal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -5,11 +5,11 @@ CPP = g++.exe
CC = gcc.exe
WINDRES = windres.exe
RES =
OBJ = consolehead.o ../head.o $(RES)
LINKOBJ = consolehead.o ../head.o $(RES)
LIBS = -L"lib" -s
INCS = -I"include"
CXXINCS = -I"lib/gcc/mingw32/3.4.2/include" -I"include/c++/3.4.2/backward" -I"include/c++/3.4.2/mingw32" -I"include/c++/3.4.2" -I"include"
OBJ = ../../head/consolehead.o ../../head/head.o $(RES)
LINKOBJ = ../../head/consolehead.o ../../head/head.o $(RES)
LIBS = -L"C:/Dev-Cpp/lib" -n -s
INCS = -I"C:/Dev-Cpp/include"
CXXINCS = -I"C:/Dev-Cpp/lib/gcc/mingw32/3.4.2/include" -I"C:/Dev-Cpp/include/c++/3.4.2/backward" -I"C:/Dev-Cpp/include/c++/3.4.2/mingw32" -I"C:/Dev-Cpp/include/c++/3.4.2" -I"C:/Dev-Cpp/include"
BIN = consolehead.exe
CXXFLAGS = $(CXXINCS) -fexpensive-optimizations -O3
CFLAGS = $(INCS) -fexpensive-optimizations -O3
@ -24,10 +24,10 @@ clean: clean-custom
${RM} $(OBJ) $(BIN)
$(BIN): $(OBJ)
$(CC) $(LINKOBJ) -o "consolehead.exe" $(LIBS)
# $(CC) $(LINKOBJ) -o "consolehead.exe" $(LIBS)
consolehead.o: consolehead.c
$(CC) -c consolehead.c -o consolehead.o $(CFLAGS)
../../head/consolehead.o: consolehead.c
$(CC) -c consolehead.c -o ../../head/consolehead.o $(CFLAGS)
../head.o: ../head.c
$(CC) -c ../head.c -o ../head.o $(CFLAGS)
../../head/head.o: ../head.c
$(CC) -c ../head.c -o ../../head/head.o $(CFLAGS)

View File

@ -1,24 +1,30 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2004-2005 Grzegorz Kowal
Copyright (c) 2004, 2007 Grzegorz Kowal
Compiled with Mingw port of GCC, Bloodshed Dev-C++ IDE (http://www.bloodshed.net/devcpp.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "../resource.h"
@ -27,29 +33,32 @@
int main(int argc, char* argv[])
{
setConsoleFlag();
HMODULE hLibrary = NULL;
char cmdLine[BIG_STR] = "";
int i; // causes error: 'for' loop initial declaration used outside C99 mode.
for (i = 1; i < argc; i++) {
strcat(cmdLine, argv[i]);
if (i < argc - 1) {
strcat(cmdLine, " ");
LPTSTR cmdLine = GetCommandLine();
if (*cmdLine == '"') {
if (*(cmdLine = strchr(cmdLine + 1, '"') + 1)) {
cmdLine++;
}
} else if ((cmdLine = strchr(cmdLine, ' ')) != NULL) {
cmdLine++;
} else {
cmdLine = "";
}
if (!prepare(hLibrary, cmdLine)) {
if (hLibrary != NULL) {
FreeLibrary(hLibrary);
}
return 1;
}
FreeLibrary(hLibrary);
int result = (int) execute(TRUE);
if (result == -1) {
char errMsg[BIG_STR] = "could not start the application: ";
strcat(errMsg, strerror(errno));
int result = prepare(cmdLine);
if (result == ERROR_ALREADY_EXISTS) {
char errMsg[BIG_STR] = {0};
loadString(INSTANCE_ALREADY_EXISTS_MSG, errMsg);
msgBox(errMsg);
closeLogFile();
return 2;
}
if (result != TRUE) {
signalError();
return 1;
}
result = (int) execute(TRUE);
if (result == -1) {
signalError();
} else {
return result;
}

View File

@ -7,23 +7,23 @@ Ver=1
ObjFiles=
Includes=
Libs=
PrivateResource=consolehead_private.rc
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=
Linker=-n_@@_
IsCpp=0
Icon=
ExeOutput=
ObjectOutput=
ObjectOutput=..\..\head
OverrideOutput=0
OverrideOutputName=consolehead.exe
HostApplication=
Folders=
CommandLine=
UseCustomMakefile=0
CustomMakefile=
CustomMakefile=Makefile.win
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=0

View File

@ -5,11 +5,11 @@ CPP = g++.exe
CC = gcc.exe
WINDRES = windres.exe
RES =
OBJ = guihead.o ../head.o $(RES)
LINKOBJ = guihead.o ../head.o $(RES)
LIBS = -L"lib" -mwindows
INCS = -I"include"
CXXINCS = -I"lib/gcc/mingw32/3.4.2/include" -I"include/c++/3.4.2/backward" -I"include/c++/3.4.2/mingw32" -I"include/c++/3.4.2" -I"include"
OBJ = ../../head/guihead.o ../../head/head.o $(RES)
LINKOBJ = ../../head/guihead.o ../../head/head.o $(RES)
LIBS = -L"C:/Dev-Cpp/lib" -mwindows -n -s
INCS = -I"C:/Dev-Cpp/include"
CXXINCS = -I"C:/Dev-Cpp/lib/gcc/mingw32/3.4.2/include" -I"C:/Dev-Cpp/include/c++/3.4.2/backward" -I"C:/Dev-Cpp/include/c++/3.4.2/mingw32" -I"C:/Dev-Cpp/include/c++/3.4.2" -I"C:/Dev-Cpp/include"
BIN = guihead.exe
CXXFLAGS = $(CXXINCS) -fexpensive-optimizations -O3
CFLAGS = $(INCS) -fexpensive-optimizations -O3
@ -24,10 +24,10 @@ clean: clean-custom
${RM} $(OBJ) $(BIN)
$(BIN): $(OBJ)
$(CC) $(LINKOBJ) -o "guihead.exe" $(LIBS)
# $(CC) $(LINKOBJ) -o "guihead.exe" $(LIBS)
guihead.o: guihead.c
$(CC) -c guihead.c -o guihead.o $(CFLAGS)
../../head/guihead.o: guihead.c
$(CC) -c guihead.c -o ../../head/guihead.o $(CFLAGS)
../head.o: ../head.c
$(CC) -c ../head.c -o ../head.o $(CFLAGS)
../../head/head.o: ../head.c
$(CC) -c ../head.c -o ../../head/head.o $(CFLAGS)

View File

@ -1,30 +1,38 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2004-2005 Grzegorz Kowal
Copyright (c) 2004, 2007 Grzegorz Kowal
Sylvain Mina (single instance patch)
Compiled with Mingw port of GCC, Bloodshed Dev-C++ IDE (http://www.bloodshed.net/devcpp.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "../resource.h"
#include "../head.h"
#include "guihead.h"
extern FILE* hLog;
extern PROCESS_INFORMATION pi;
HWND hWnd;
@ -33,39 +41,44 @@ BOOL stayAlive = FALSE;
BOOL splash = FALSE;
BOOL splashTimeoutErr;
BOOL waitForWindow;
int splashTimeout; // tick count (s)
int splashTimeout = DEFAULT_SPLASH_TIMEOUT;
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
HMODULE hLibrary = NULL;
if (!prepare(hLibrary, lpCmdLine)) {
if (hLibrary != NULL) {
FreeLibrary(hLibrary);
}
int result = prepare(lpCmdLine);
if (result == ERROR_ALREADY_EXISTS) {
HWND handle = getInstanceWindow();
ShowWindow(handle, SW_SHOW);
SetForegroundWindow(handle);
closeLogFile();
return 2;
}
if (result != TRUE) {
signalError();
return 1;
}
splash = loadBoolString(hLibrary, SHOW_SPLASH);
stayAlive = loadBoolString(hLibrary, GUI_HEADER_STAYS_ALIVE);
splash = loadBool(SHOW_SPLASH)
&& strstr(lpCmdLine, "--l4j-no-splash") == NULL;
stayAlive = loadBool(GUI_HEADER_STAYS_ALIVE)
&& strstr(lpCmdLine, "--l4j-dont-wait") == NULL;
if (splash || stayAlive) {
hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "STATIC", "",
WS_POPUP | SS_BITMAP,
0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (splash) {
char timeout[10] = "";
if (!loadString(hLibrary, SPLASH_TIMEOUT, timeout)) {
msgBox("Cannot load splash timeout property.");
return 1;
char timeout[10] = {0};
if (loadString(SPLASH_TIMEOUT, timeout)) {
splashTimeout = atoi(timeout);
if (splashTimeout <= 0 || splashTimeout > MAX_SPLASH_TIMEOUT) {
splashTimeout = DEFAULT_SPLASH_TIMEOUT;
}
}
splashTimeout = atoi(timeout);
if (splashTimeout <= 0 || splashTimeout > 60 * 15 /* 15 minutes */) {
msgBox("Invalid splash timeout property.");
return 1;
}
splashTimeoutErr = loadBoolString(hLibrary, SPLASH_TIMEOUT_ERR);
waitForWindow = loadBoolString(hLibrary, SPLASH_WAITS_FOR_WINDOW);
splashTimeoutErr = loadBool(SPLASH_TIMEOUT_ERR)
&& strstr(lpCmdLine, "--l4j-no-splash-err") == NULL;
waitForWindow = loadBool(SPLASH_WAITS_FOR_WINDOW);
HANDLE hImage = LoadImage(hInstance, // handle of the instance containing the image
MAKEINTRESOURCE(SPLASH_BITMAP), // name or identifier of image
IMAGE_BITMAP, // type of image
@ -73,7 +86,7 @@ int APIENTRY WinMain(HINSTANCE hInstance,
0, // desired height
LR_DEFAULTSIZE);
if (hImage == NULL) {
msgBox("Cannot load splash screen.");
signalError();
return 1;
}
SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hImage);
@ -86,29 +99,47 @@ int APIENTRY WinMain(HINSTANCE hInstance,
UpdateWindow (hWnd);
}
if (!SetTimer (hWnd, ID_TIMER, 1000 /* 1s */, TimerProc)) {
signalError();
return 1;
}
}
FreeLibrary(hLibrary);
if (execute(FALSE) == -1) {
char errMsg[BIG_STR] = "Error occured while starting the application...\n";
strcat(errMsg, strerror(errno));
msgBox(errMsg);
signalError();
return 1;
}
if (!(splash || stayAlive)) {
debug("Exit code:\t0\n");
closeHandles();
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
debug("Exit code:\t%d\n", dwExitCode);
closeHandles();
return dwExitCode;
}
HWND getInstanceWindow() {
char windowTitle[STR];
char instWindowTitle[STR] = {0};
if (loadString(INSTANCE_WINDOW_TITLE, instWindowTitle)) {
HWND handle = FindWindowEx(NULL, NULL, NULL, NULL);
while (handle != NULL) {
GetWindowText(handle, windowTitle, STR - 1);
if (strstr(windowTitle, instWindowTitle) != NULL) {
return handle;
} else {
handle = FindWindowEx(NULL, handle, NULL, NULL);
}
}
}
return NULL;
}
BOOL CALLBACK enumwndfn(HWND hwnd, LPARAM lParam) {
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
@ -135,7 +166,7 @@ VOID CALLBACK TimerProc(
ShowWindow(hWnd, SW_HIDE);
if (waitForWindow && splashTimeoutErr) {
KillTimer(hwnd, ID_TIMER);
msgBox("Could not start the application, operation timed out.");
signalError();
PostQuitMessage(0);
}
} else {
@ -150,6 +181,5 @@ VOID CALLBACK TimerProc(
|| !(splash || stayAlive)) {
KillTimer(hWnd, ID_TIMER);
PostQuitMessage(0);
return;
}
}

View File

@ -12,22 +12,22 @@ ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=_@@_
Linker=-n_@@_
IsCpp=0
Icon=
ExeOutput=
ObjectOutput=
ObjectOutput=..\..\head
OverrideOutput=0
OverrideOutputName=guihead.exe
HostApplication=
Folders=
CommandLine=
UseCustomMakefile=0
CustomMakefile=
UseCustomMakefile=1
CustomMakefile=Makefile.win
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=0
CompilerSettings=0000000001001000000000
CompilerSettings=0000000001001000000100
[Unit1]
FileName=guihead.c
@ -37,7 +37,7 @@ Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
BuildCmd=$(CC) -c guihead.c -o ../../head/guihead.o $(CFLAGS)
[Unit2]
FileName=guihead.h
@ -78,16 +78,6 @@ OverrideBuildCmd=0
BuildCmd=
[Unit6]
FileName=..\resource.h
Folder=guihead
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
CompileCpp=0
[Unit7]
FileName=..\resid.h
CompileCpp=0
Folder=guihead

View File

@ -1,27 +1,39 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2004-2005 Grzegorz Kowal
Copyright (c) 2004, 2007 Grzegorz Kowal
Compiled with Mingw port of GCC, Bloodshed Dev-C++ IDE (http://www.bloodshed.net/devcpp.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define ID_TIMER 1
#define DEFAULT_SPLASH_TIMEOUT 60 /* 60 seconds */
#define MAX_SPLASH_TIMEOUT 60 * 15 /* 15 minutes */
HWND getInstanceWindow();
BOOL CALLBACK enumwndfn(HWND hwnd, LPARAM lParam);
VOID CALLBACK TimerProc(
HWND hwnd, // handle of window for timer messages

View File

@ -1,43 +1,85 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2004-2005 Grzegorz Kowal
Copyright (c) 2004, 2008 Grzegorz Kowal,
Ian Roberts (jdk preference patch)
Sylvain Mina (single instance patch)
Compiled with Mingw port of GCC, Bloodshed Dev-C++ IDE (http://www.bloodshed.net/devcpp.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "resource.h"
#include "head.h"
HMODULE hModule;
FILE* hLog;
BOOL console = FALSE;
BOOL wow64 = FALSE;
int foundJava = NO_JAVA_FOUND;
struct _stat statBuf;
PROCESS_INFORMATION pi;
DWORD priority;
char errTitle[STR] = "launch4j";
char javaMinVer[STR] = "";
char javaMaxVer[STR] = "";
char foundJavaVer[STR] = "";
char mutexName[STR] = {0};
char workingDir[_MAX_PATH] = "";
char cmd[_MAX_PATH] = "";
char args[BIG_STR * 2] = "";
char errUrl[256] = {0};
char errTitle[STR] = "Launch4j";
char errMsg[BIG_STR] = {0};
char javaMinVer[STR] = {0};
char javaMaxVer[STR] = {0};
char foundJavaVer[STR] = {0};
char foundJavaKey[_MAX_PATH] = {0};
char oldPwd[_MAX_PATH] = {0};
char workingDir[_MAX_PATH] = {0};
char cmd[_MAX_PATH] = {0};
char args[MAX_ARGS] = {0};
FILE* openLogFile(const char* exePath, const int pathLen) {
char path[_MAX_PATH] = {0};
strncpy(path, exePath, pathLen);
strcat(path, "\\launch4j.log");
return fopen(path, "a");
}
void closeLogFile() {
if (hLog != NULL) {
fclose(hLog);
}
}
void setWow64Flag() {
LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
if (fnIsWow64Process != NULL) {
fnIsWow64Process(GetCurrentProcess(), &wow64);
}
debug("WOW64:\t\t%s\n", wow64 ? "yes" : "no");
}
void setConsoleFlag() {
console = TRUE;
@ -51,19 +93,43 @@ void msgBox(const char* text) {
}
}
void showJavaWebPage() {
ShellExecute(NULL, "open", "http://java.com", NULL, NULL, SW_SHOWNORMAL);
void signalError() {
DWORD err = GetLastError();
if (err) {
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL);
debug("Error:\t\t%s\n", (LPCTSTR) lpMsgBuf);
strcat(errMsg, "\n\n");
strcat(errMsg, (LPCTSTR) lpMsgBuf);
msgBox(errMsg);
LocalFree(lpMsgBuf);
} else {
msgBox(errMsg);
}
if (*errUrl) {
debug("Open URL:\t%s\n", errUrl);
ShellExecute(NULL, "open", errUrl, NULL, NULL, SW_SHOWNORMAL);
}
closeLogFile();
}
BOOL loadString(HMODULE hLibrary, int resID, char* buffer) {
BOOL loadString(const int resID, char* buffer) {
HRSRC hResource;
HGLOBAL hResourceLoaded;
LPBYTE lpBuffer;
hResource = FindResourceEx(hLibrary, RT_RCDATA, MAKEINTRESOURCE(resID),
hResource = FindResourceEx(hModule, RT_RCDATA, MAKEINTRESOURCE(resID),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
if (NULL != hResource) {
hResourceLoaded = LoadResource(hLibrary, hResource);
hResourceLoaded = LoadResource(hModule, hResource);
if (NULL != hResourceLoaded) {
lpBuffer = (LPBYTE) LockResource(hResourceLoaded);
if (NULL != lpBuffer) {
@ -71,24 +137,76 @@ BOOL loadString(HMODULE hLibrary, int resID, char* buffer) {
do {
buffer[x] = (char) lpBuffer[x];
} while (buffer[x++] != 0);
// debug("Resource %d:\t%s\n", resID, buffer);
return TRUE;
}
}
} else {
SetLastError(0);
}
return FALSE;
}
BOOL loadBoolString(HMODULE hLibrary, int resID) {
char boolStr[10] = "";
loadString(hLibrary, resID, boolStr);
BOOL loadBool(const int resID) {
char boolStr[20] = {0};
loadString(resID, boolStr);
return strcmp(boolStr, TRUE_STR) == 0;
}
void regSearch(HKEY hKey, char* keyName, int searchType) {
int loadInt(const int resID) {
char intStr[20] = {0};
loadString(resID, intStr);
return atoi(intStr);
}
BOOL regQueryValue(const char* regPath, unsigned char* buffer,
unsigned long bufferLength) {
HKEY hRootKey;
char* key;
char* value;
if (strstr(regPath, HKEY_CLASSES_ROOT_STR) == regPath) {
hRootKey = HKEY_CLASSES_ROOT;
} else if (strstr(regPath, HKEY_CURRENT_USER_STR) == regPath) {
hRootKey = HKEY_CURRENT_USER;
} else if (strstr(regPath, HKEY_LOCAL_MACHINE_STR) == regPath) {
hRootKey = HKEY_LOCAL_MACHINE;
} else if (strstr(regPath, HKEY_USERS_STR) == regPath) {
hRootKey = HKEY_USERS;
} else if (strstr(regPath, HKEY_CURRENT_CONFIG_STR) == regPath) {
hRootKey = HKEY_CURRENT_CONFIG;
} else {
return FALSE;
}
key = strchr(regPath, '\\') + 1;
value = strrchr(regPath, '\\') + 1;
*(value - 1) = 0;
HKEY hKey;
unsigned long datatype;
BOOL result = FALSE;
if ((wow64 && RegOpenKeyEx(hRootKey,
key,
0,
KEY_READ | KEY_WOW64_64KEY,
&hKey) == ERROR_SUCCESS)
|| RegOpenKeyEx(hRootKey,
key,
0,
KEY_READ,
&hKey) == ERROR_SUCCESS) {
result = RegQueryValueEx(hKey, value, NULL, &datatype, buffer, &bufferLength)
== ERROR_SUCCESS;
RegCloseKey(hKey);
}
*(value - 1) = '\\';
return result;
}
void regSearch(const HKEY hKey, const char* keyName, const int searchType) {
DWORD x = 0;
unsigned long size = BIG_STR;
FILETIME time;
char buffer[BIG_STR] = "";
char buffer[BIG_STR] = {0};
while (RegEnumKeyEx(
hKey, // handle to key to enumerate
x++, // index of subkey to enumerate
@ -98,61 +216,92 @@ void regSearch(HKEY hKey, char* keyName, int searchType) {
NULL, // address of buffer for class string
NULL, // address for size of class buffer
&time) == ERROR_SUCCESS) {
if (strcmp(buffer, javaMinVer) >= 0
&& (javaMaxVer[0] == 0 || strcmp(buffer, javaMaxVer) <= 0)
&& (!*javaMaxVer || strcmp(buffer, javaMaxVer) <= 0)
&& strcmp(buffer, foundJavaVer) > 0) {
strcpy(foundJavaVer, buffer);
strcpy(foundJavaKey, keyName);
appendPath(foundJavaKey, buffer);
foundJava = searchType;
debug("Match:\t\t%s\\%s\n", keyName, buffer);
} else {
debug("Ignore:\t\t%s\\%s\n", keyName, buffer);
}
size = BIG_STR;
}
}
BOOL findJavaHome(char* path) {
void regSearchWow(const char* keyName, const int searchType) {
HKEY hKey;
char jre[] = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
char sdk[] = "SOFTWARE\\JavaSoft\\Java Development Kit";
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT(jre),
debug("64-bit search:\t%s...\n", keyName);
if (wow64 && RegOpenKeyEx(HKEY_LOCAL_MACHINE,
keyName,
0,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
KEY_READ | KEY_WOW64_64KEY,
&hKey) == ERROR_SUCCESS) {
regSearch(hKey, jre, FOUND_JRE);
regSearch(hKey, keyName, searchType | KEY_WOW64_64KEY);
RegCloseKey(hKey);
if ((foundJava & KEY_WOW64_64KEY) != NO_JAVA_FOUND)
{
debug("Using 64-bit runtime.\n");
return;
}
}
debug("32-bit search:\t%s...\n", keyName);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
keyName,
0,
KEY_READ,
&hKey) == ERROR_SUCCESS) {
regSearch(hKey, keyName, searchType);
RegCloseKey(hKey);
}
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT(sdk),
0,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
&hKey) == ERROR_SUCCESS) {
regSearch(hKey, sdk, FOUND_SDK);
RegCloseKey(hKey);
}
void regSearchJreSdk(const char* jreKeyName, const char* sdkKeyName,
const int jdkPreference) {
if (jdkPreference == JDK_ONLY || jdkPreference == PREFER_JDK) {
regSearchWow(sdkKeyName, FOUND_SDK);
if (jdkPreference != JDK_ONLY) {
regSearchWow(jreKeyName, FOUND_JRE);
}
} else { // jdkPreference == JRE_ONLY or PREFER_JRE
regSearchWow(jreKeyName, FOUND_JRE);
if (jdkPreference != JRE_ONLY) {
regSearchWow(sdkKeyName, FOUND_SDK);
}
}
}
BOOL findJavaHome(char* path, const int jdkPreference) {
regSearchJreSdk("SOFTWARE\\JavaSoft\\Java Runtime Environment",
"SOFTWARE\\JavaSoft\\Java Development Kit",
jdkPreference);
if (foundJava == NO_JAVA_FOUND) {
regSearchJreSdk("SOFTWARE\\IBM\\Java2 Runtime Environment",
"SOFTWARE\\IBM\\Java Development Kit",
jdkPreference);
}
if (foundJava != NO_JAVA_FOUND) {
char keyBuffer[BIG_STR];
unsigned long datatype;
unsigned long bufferlength = BIG_STR;
if (foundJava == FOUND_JRE) {
strcpy(keyBuffer, jre);
} else {
strcpy(keyBuffer, sdk);
}
strcat(keyBuffer, "\\");
strcat(keyBuffer, foundJavaVer);
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT(keyBuffer),
0,
KEY_QUERY_VALUE,
&hKey) == ERROR_SUCCESS) {
unsigned char buffer[BIG_STR];
if (RegQueryValueEx(hKey, "JavaHome", NULL, &datatype, buffer, &bufferlength)
== ERROR_SUCCESS) {
foundJavaKey,
0,
KEY_READ | (foundJava & KEY_WOW64_64KEY),
&hKey) == ERROR_SUCCESS) {
unsigned char buffer[BIG_STR] = {0};
unsigned long bufferlength = BIG_STR;
unsigned long datatype;
if (RegQueryValueEx(hKey, "JavaHome", NULL, &datatype, buffer,
&bufferlength) == ERROR_SUCCESS) {
int i = 0;
do {
path[i] = buffer[i];
} while (path[i++] != 0);
if (foundJava == FOUND_SDK) {
strcat(path, "\\jre");
if (foundJava & FOUND_SDK) {
appendPath(path, "jre");
}
RegCloseKey(hKey);
return TRUE;
@ -164,66 +313,93 @@ BOOL findJavaHome(char* path) {
}
/*
* extract the executable name, returns path length.
* Extract the executable name, returns path length.
*/
int getExePath(char* exePath) {
HMODULE hModule = GetModuleHandle(NULL);
if (hModule == 0
|| GetModuleFileName(hModule, exePath, _MAX_PATH) == 0) {
if (GetModuleFileName(hModule, exePath, _MAX_PATH) == 0) {
return -1;
}
return strrchr(exePath, '\\') - exePath;
}
void appendPath(char* basepath, const char* path) {
if (basepath[strlen(basepath) - 1] != '\\') {
strcat(basepath, "\\");
}
strcat(basepath, path);
}
void appendJavaw(char* jrePath) {
if (console) {
strcat(jrePath, "\\bin\\java.exe");
appendPath(jrePath, "bin\\java.exe");
} else {
strcat(jrePath, "\\bin\\javaw.exe");
appendPath(jrePath, "bin\\javaw.exe");
}
}
void appendLauncher(BOOL setProcName, char* exePath, int pathLen, char* cmd) {
void appendLauncher(const BOOL setProcName, char* exePath,
const int pathLen, char* cmd) {
if (setProcName) {
char tmpspec[_MAX_PATH] = "";
char tmpfile[_MAX_PATH] = "";
char tmpspec[_MAX_PATH];
char tmpfile[_MAX_PATH];
strcpy(tmpspec, cmd);
strcat(tmpspec, LAUNCH4J_TMP_DIR);
tmpspec[strlen(tmpspec) - 1] = 0;
if (_stat(tmpspec, &statBuf) == 0) {
// remove temp launchers
// Remove temp launchers and manifests
struct _finddata_t c_file;
long hFile;
strcat(tmpspec, "\\*.exe");
appendPath(tmpspec, "*.exe");
strcpy(tmpfile, cmd);
strcat(tmpfile, LAUNCH4J_TMP_DIR);
char* filename = tmpfile + strlen(tmpfile);
if ((hFile = _findfirst(tmpspec, &c_file)) != -1L) {
do {
strcpy(filename, c_file.name);
debug("Unlink:\t\t%s\n", tmpfile);
_unlink(tmpfile);
strcat(tmpfile, MANIFEST);
debug("Unlink:\t\t%s\n", tmpfile);
_unlink(tmpfile);
} while (_findnext(hFile, &c_file) == 0);
}
_findclose(hFile);
} else {
if (_mkdir(tmpspec) != 0) {
debug("Mkdir failed:\t%s\n", tmpspec);
appendJavaw(cmd);
return;
}
}
char javaw[_MAX_PATH] = "";
char javaw[_MAX_PATH];
strcpy(javaw, cmd);
appendJavaw(javaw);
strcpy(tmpfile, cmd);
strcat(tmpfile, LAUNCH4J_TMP_DIR);
char* tmpfilename = tmpfile + strlen(tmpfile);
char* exeFilePart = exePath + pathLen + 1;
strcat(tmpfile, exeFilePart);
// Copy manifest
char manifest[_MAX_PATH] = {0};
strcpy(manifest, exePath);
strcat(manifest, MANIFEST);
if (_stat(manifest, &statBuf) == 0) {
strcat(tmpfile, exeFilePart);
strcat(tmpfile, MANIFEST);
debug("Copy:\t\t%s -> %s\n", manifest, tmpfile);
CopyFile(manifest, tmpfile, FALSE);
}
// Copy launcher
strcpy(tmpfilename, exeFilePart);
debug("Copy:\t\t%s -> %s\n", javaw, tmpfile);
if (CopyFile(javaw, tmpfile, FALSE)) {
strcpy(cmd, tmpfile);
return;
} else {
} else if (_stat(javaw, &statBuf) == 0) {
long fs = statBuf.st_size;
if (_stat(tmpfile, &statBuf) == 0 && fs == statBuf.st_size) {
debug("Reusing:\t\t%s\n", tmpfile);
strcpy(cmd, tmpfile);
return;
}
@ -232,123 +408,396 @@ void appendLauncher(BOOL setProcName, char* exePath, int pathLen, char* cmd) {
appendJavaw(cmd);
}
BOOL isJrePathOk(char* path) {
if (!*path) {
return FALSE;
void appendAppClasspath(char* dst, const char* src, const char* classpath) {
strcat(dst, src);
if (*classpath) {
strcat(dst, ";");
}
char javaw[_MAX_PATH];
strcpy(javaw, path);
appendJavaw(javaw);
return _stat(javaw, &statBuf) == 0;
}
BOOL prepare(HMODULE hLibrary, char *lpCmdLine) {
// open executable
char exePath[_MAX_PATH] = "";
BOOL isJrePathOk(const char* path) {
char javaw[_MAX_PATH];
BOOL result = FALSE;
if (*path) {
strcpy(javaw, path);
appendJavaw(javaw);
result = _stat(javaw, &statBuf) == 0;
}
debug("Check launcher:\t%s %s\n", javaw, result ? "(OK)" : "(n/a)");
return result;
}
/*
* Expand environment %variables%
*/
BOOL expandVars(char *dst, const char *src, const char *exePath, const int pathLen) {
char varName[STR];
char varValue[MAX_VAR_SIZE];
while (strlen(src) > 0) {
char *start = strchr(src, '%');
if (start != NULL) {
char *end = strchr(start + 1, '%');
if (end == NULL) {
return FALSE;
}
// Copy content up to %VAR%
strncat(dst, src, start - src);
// Insert value of %VAR%
*varName = 0;
strncat(varName, start + 1, end - start - 1);
// Remember value start for logging
char *varValue = dst + strlen(dst);
if (strcmp(varName, "EXEDIR") == 0) {
strncat(dst, exePath, pathLen);
} else if (strcmp(varName, "EXEFILE") == 0) {
strcat(dst, exePath);
} else if (strcmp(varName, "PWD") == 0) {
GetCurrentDirectory(_MAX_PATH, dst + strlen(dst));
} else if (strcmp(varName, "OLDPWD") == 0) {
strcat(dst, oldPwd);
} else if (strstr(varName, HKEY_STR) == varName) {
regQueryValue(varName, dst + strlen(dst), BIG_STR);
} else if (GetEnvironmentVariable(varName, varValue, MAX_VAR_SIZE) > 0) {
strcat(dst, varValue);
}
debug("Substitute:\t%s = %s\n", varName, varValue);
src = end + 1;
} else {
// Copy remaining content
strcat(dst, src);
break;
}
}
return TRUE;
}
void appendHeapSizes(char *dst) {
MEMORYSTATUS m;
memset(&m, 0, sizeof(m));
GlobalMemoryStatus(&m);
appendHeapSize(dst, INITIAL_HEAP_SIZE, INITIAL_HEAP_PERCENT,
m.dwAvailPhys, "-Xms");
appendHeapSize(dst, MAX_HEAP_SIZE, MAX_HEAP_PERCENT,
m.dwAvailPhys, "-Xmx");
}
void appendHeapSize(char *dst, const int absID, const int percentID,
const DWORD freeMemory, const char *option) {
const int mb = 1048576; // 1 MB
int abs = loadInt(absID);
int percent = loadInt(percentID);
int free = (long long) freeMemory * percent / (100 * mb); // 100% * 1 MB
int size = free > abs ? free : abs;
if (size > 0) {
debug("Heap %s:\t%d MB / %d%%, Free: %d MB, Heap size: %d MB\n",
option, abs, percent, freeMemory / mb, size);
strcat(dst, option);
_itoa(size, dst + strlen(dst), 10); // 10 -- radix
strcat(dst, "m ");
}
}
int prepare(const char *lpCmdLine) {
char tmp[MAX_ARGS] = {0};
hModule = GetModuleHandle(NULL);
if (hModule == NULL) {
return FALSE;
}
// Get executable path
char exePath[_MAX_PATH] = {0};
int pathLen = getExePath(exePath);
if (pathLen == -1) {
msgBox("Cannot determinate exe file name.");
return FALSE;
}
hLibrary = LoadLibrary(exePath);
if (hLibrary == NULL) {
char msg[BIG_STR] = "Cannot find file: ";
strcat(msg, exePath);
msgBox(msg);
return FALSE;
}
// error message box title
loadString(hLibrary, ERR_TITLE, errTitle);
// working dir
char tmp_path[_MAX_PATH] = "";
if (loadString(hLibrary, CHDIR, tmp_path)) {
strncpy(workingDir, exePath, pathLen);
strcat(workingDir, "/");
strcat(workingDir, tmp_path);
_chdir(workingDir);
}
// custom process name
const BOOL setProcName = loadBoolString(hLibrary, SET_PROC_NAME);
// use bundled jre or find java
if (loadString(hLibrary, JRE_PATH, tmp_path)) {
strncpy(cmd, exePath, pathLen);
strcat(cmd, "/");
strcat(cmd, tmp_path);
}
if (!isJrePathOk(cmd)) {
if (!loadString(hLibrary, JAVA_MIN_VER, javaMinVer)) {
msgBox("Cannot find bundled JRE or javaw.exe is missing.");
// Initialize logging
if (strstr(lpCmdLine, "--l4j-debug") != NULL) {
hLog = openLogFile(exePath, pathLen);
if (hLog == NULL) {
return FALSE;
}
loadString(hLibrary, JAVA_MAX_VER, javaMaxVer);
if (!findJavaHome(cmd)) {
char txt[BIG_STR] = "Cannot find Java ";
strcat(txt, javaMinVer);
debug("\n\nCmdLine:\t%s %s\n", exePath, lpCmdLine);
}
setWow64Flag();
// Set default error message, title and optional support web site url.
loadString(SUPPORT_URL, errUrl);
loadString(ERR_TITLE, errTitle);
if (!loadString(STARTUP_ERR, errMsg)) {
return FALSE;
}
// Single instance
loadString(MUTEX_NAME, mutexName);
if (*mutexName) {
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;
CreateMutexA(&security, FALSE, mutexName);
if (GetLastError() == ERROR_ALREADY_EXISTS) {
debug("Instance already exists.");
return ERROR_ALREADY_EXISTS;
}
}
// Working dir
char tmp_path[_MAX_PATH] = {0};
GetCurrentDirectory(_MAX_PATH, oldPwd);
if (loadString(CHDIR, tmp_path)) {
strncpy(workingDir, exePath, pathLen);
appendPath(workingDir, tmp_path);
_chdir(workingDir);
debug("Working dir:\t%s\n", workingDir);
}
// Use bundled jre or find java
if (loadString(JRE_PATH, tmp_path)) {
char jrePath[MAX_ARGS] = {0};
expandVars(jrePath, tmp_path, exePath, pathLen);
debug("Bundled JRE:\t%s\n", jrePath);
if (jrePath[0] == '\\' || jrePath[1] == ':') {
// Absolute
strcpy(cmd, jrePath);
} else {
// Relative
strncpy(cmd, exePath, pathLen);
appendPath(cmd, jrePath);
}
}
if (!isJrePathOk(cmd)) {
if (!loadString(JAVA_MIN_VER, javaMinVer)) {
loadString(BUNDLED_JRE_ERR, errMsg);
return FALSE;
}
loadString(JAVA_MAX_VER, javaMaxVer);
if (!findJavaHome(cmd, loadInt(JDK_PREFERENCE))) {
loadString(JRE_VERSION_ERR, errMsg);
strcat(errMsg, " ");
strcat(errMsg, javaMinVer);
if (*javaMaxVer) {
strcat(txt, " - ");
strcat(txt, javaMaxVer);
strcat(errMsg, " - ");
strcat(errMsg, javaMaxVer);
}
msgBox(txt);
showJavaWebPage();
loadString(DOWNLOAD_URL, errUrl);
return FALSE;
}
if (!isJrePathOk(cmd)) {
msgBox("Java found, but javaw.exe seems to be missing.");
loadString(LAUNCHER_ERR, errMsg);
return FALSE;
}
}
// Append a path to the Path environment variable
char jreBinPath[_MAX_PATH];
strcpy(jreBinPath, cmd);
strcat(jreBinPath, "\\bin");
if (!appendToPathVar(jreBinPath)) {
return FALSE;
}
// Set environment variables
char envVars[MAX_VAR_SIZE] = {0};
loadString(ENV_VARIABLES, envVars);
char *var = strtok(envVars, "\t");
while (var != NULL) {
char *varValue = strchr(var, '=');
*varValue++ = 0;
*tmp = 0;
expandVars(tmp, varValue, exePath, pathLen);
debug("Set var:\t%s = %s\n", var, tmp);
SetEnvironmentVariable(var, tmp);
var = strtok(NULL, "\t");
}
*tmp = 0;
// Process priority
priority = loadInt(PRIORITY_CLASS);
// Custom process name
const BOOL setProcName = loadBool(SET_PROC_NAME)
&& strstr(lpCmdLine, "--l4j-default-proc") == NULL;
const BOOL wrapper = loadBool(WRAPPER);
appendLauncher(setProcName, exePath, pathLen, cmd);
char jarArgs[BIG_STR] = "";
loadString(hLibrary, JAR_ARGS, jarArgs);
loadString(hLibrary, JVM_ARGS, args);
if (*args) {
strcat(args, " ");
}
strcat(args, "-jar \"");
strcat(args, exePath);
strcat(args, "\"");
if (*jarArgs) {
strcat(args, " ");
strcat(args, jarArgs);
}
if (*lpCmdLine) {
strcat(args, " ");
strcat(args, lpCmdLine);
// Heap sizes
appendHeapSizes(args);
// JVM options
if (loadString(JVM_OPTIONS, tmp)) {
strcat(tmp, " ");
} else {
*tmp = 0;
}
/*
* Load additional JVM options from .l4j.ini file
* Options are separated by spaces or CRLF
* # starts an inline comment
*/
strncpy(tmp_path, exePath, strlen(exePath) - 3);
strcat(tmp_path, "l4j.ini");
long hFile;
if ((hFile = _open(tmp_path, _O_RDONLY)) != -1) {
const int jvmOptLen = strlen(tmp);
char* src = tmp + jvmOptLen;
char* dst = src;
const int len = _read(hFile, src, MAX_ARGS - jvmOptLen - BIG_STR);
BOOL copy = TRUE;
int i;
for (i = 0; i < len; i++, src++) {
if (*src == '#') {
copy = FALSE;
} else if (*src == 13 || *src == 10) {
copy = TRUE;
if (dst > tmp && *(dst - 1) != ' ') {
*dst++ = ' ';
}
} else if (copy) {
*dst++ = *src;
}
}
*dst = 0;
if (len > 0 && *(dst - 1) != ' ') {
strcat(tmp, " ");
}
_close(hFile);
}
// msgBox(cmd);
// msgBox(args);
// msgBox(workingDir);
// Expand environment %variables%
expandVars(args, tmp, exePath, pathLen);
// MainClass + Classpath or Jar
char mainClass[STR] = {0};
char jar[_MAX_PATH] = {0};
loadString(JAR, jar);
if (loadString(MAIN_CLASS, mainClass)) {
if (!loadString(CLASSPATH, tmp)) {
return FALSE;
}
char exp[MAX_ARGS] = {0};
expandVars(exp, tmp, exePath, pathLen);
strcat(args, "-classpath \"");
if (wrapper) {
appendAppClasspath(args, exePath, exp);
} else if (*jar) {
appendAppClasspath(args, jar, exp);
}
// Deal with wildcards or >> strcat(args, exp); <<
char* cp = strtok(exp, ";");
while(cp != NULL) {
debug("Add classpath:\t%s\n", cp);
if (strpbrk(cp, "*?") != NULL) {
int len = strrchr(cp, '\\') - cp + 1;
strncpy(tmp_path, cp, len);
char* filename = tmp_path + len;
*filename = 0;
struct _finddata_t c_file;
long hFile;
if ((hFile = _findfirst(cp, &c_file)) != -1L) {
do {
strcpy(filename, c_file.name);
strcat(args, tmp_path);
strcat(args, ";");
debug(" \" :\t%s\n", tmp_path);
} while (_findnext(hFile, &c_file) == 0);
}
_findclose(hFile);
} else {
strcat(args, cp);
strcat(args, ";");
}
cp = strtok(NULL, ";");
}
*(args + strlen(args) - 1) = 0;
strcat(args, "\" ");
strcat(args, mainClass);
} else if (wrapper) {
strcat(args, "-jar \"");
strcat(args, exePath);
strcat(args, "\"");
} else {
strcat(args, "-jar \"");
strncat(args, exePath, pathLen);
appendPath(args, jar);
strcat(args, "\"");
}
// Constant command line args
if (loadString(CMD_LINE, tmp)) {
strcat(args, " ");
strcat(args, tmp);
}
// Command line args
if (*lpCmdLine) {
strcpy(tmp, lpCmdLine);
char* dst;
while ((dst = strstr(tmp, "--l4j-")) != NULL) {
char* src = strchr(dst, ' ');
if (src == NULL || *(src + 1) == 0) {
*dst = 0;
} else {
strcpy(dst, src + 1);
}
}
if (*tmp) {
strcat(args, " ");
strcat(args, tmp);
}
}
debug("Launcher:\t%s\n", cmd);
debug("Launcher args:\t%s\n", args);
debug("Args length:\t%d/32768 chars\n", strlen(args));
return TRUE;
}
void closeHandles() {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
closeLogFile();
}
DWORD execute(BOOL wait) {
/*
* Append a path to the Path environment variable
*/
BOOL appendToPathVar(const char* path) {
char chBuf[MAX_VAR_SIZE] = {0};
const int pathSize = GetEnvironmentVariable("Path", chBuf, MAX_VAR_SIZE);
if (MAX_VAR_SIZE - pathSize - 1 < strlen(path)) {
return FALSE;
}
strcat(chBuf, ";");
strcat(chBuf, path);
return SetEnvironmentVariable("Path", chBuf);
}
DWORD execute(const BOOL wait) {
STARTUPINFO si;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
DWORD dwExitCode = -1;
char cmdline[_MAX_PATH + BIG_STR] = "\"";
char cmdline[MAX_ARGS];
strcpy(cmdline, "\"");
strcat(cmdline, cmd);
strcat(cmdline, "\" ");
strcat(cmdline, args);
if (CreateProcess(NULL, cmdline, NULL, NULL,
FALSE, 0, NULL, NULL, &si, &pi)) {
TRUE, priority, NULL, NULL, &si, &pi)) {
if (wait) {
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &dwExitCode);
debug("Exit code:\t%d\n", dwExitCode);
closeHandles();
} else {
dwExitCode = 0;

View File

@ -1,24 +1,31 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2004-2005 Grzegorz Kowal
Copyright (c) 2004, 2008 Grzegorz Kowal,
Ian Roberts (jdk preference patch)
Compiled with Mingw port of GCC, Bloodshed Dev-C++ IDE (http://www.bloodshed.net/devcpp.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef _LAUNCH4J_HEAD__INCLUDED_
@ -36,6 +43,7 @@
#include <tchar.h>
#include <shellapi.h>
#include <direct.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <io.h>
@ -45,25 +53,61 @@
#define FOUND_JRE 1
#define FOUND_SDK 2
#define JRE_ONLY 0
#define PREFER_JRE 1
#define PREFER_JDK 2
#define JDK_ONLY 3
#define LAUNCH4J_TMP_DIR "\\launch4j-tmp\\"
#define MANIFEST ".manifest"
#define KEY_WOW64_64KEY 0x0100
#define HKEY_STR "HKEY"
#define HKEY_CLASSES_ROOT_STR "HKEY_CLASSES_ROOT"
#define HKEY_CURRENT_USER_STR "HKEY_CURRENT_USER"
#define HKEY_LOCAL_MACHINE_STR "HKEY_LOCAL_MACHINE"
#define HKEY_USERS_STR "HKEY_USERS"
#define HKEY_CURRENT_CONFIG_STR "HKEY_CURRENT_CONFIG"
#define STR 128
#define BIG_STR 1024
#define MAX_VAR_SIZE 32767
#define MAX_ARGS 32768
#define TRUE_STR "true"
#define FALSE_STR "false"
#define debug(args...) if (hLog != NULL) fprintf(hLog, ## args);
typedef void (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
FILE* openLogFile(const char* exePath, const int pathLen);
void closeLogFile();
void msgBox(const char* text);
void showJavaWebPage();
BOOL loadString(HMODULE hLibrary, int resID, char* buffer);
BOOL loadBoolString(HMODULE hLibrary, int resID);
void regSearch(HKEY hKey, char* keyName, int searchType);
BOOL findJavaHome(char* path);
void signalError();
BOOL loadString(const int resID, char* buffer);
BOOL loadBool(const int resID);
int loadInt(const int resID);
BOOL regQueryValue(const char* regPath, unsigned char* buffer,
unsigned long bufferLength);
void regSearch(const HKEY hKey, const char* keyName, const int searchType);
void regSearchWow(const char* keyName, const int searchType);
void regSearchJreSdk(const char* jreKeyName, const char* sdkKeyName,
const int jdkPreference);
BOOL findJavaHome(char* path, const int jdkPreference);
int getExePath(char* exePath);
void catJavaw(char* jrePath);
BOOL isJrePathOk(char* path);
BOOL prepare(HMODULE hLibrary, char *lpCmdLine);
void appendPath(char* basepath, const char* path);
void appendJavaw(char* jrePath);
void appendAppClasspath(char* dst, const char* src, const char* classpath);
BOOL isJrePathOk(const char* path);
BOOL expandVars(char *dst, const char *src, const char *exePath, const int pathLen);
void appendHeapSizes(char *dst);
void appendHeapSize(char *dst, const int absID, const int percentID,
const DWORD freeMemory, const char *option);
int prepare(const char *lpCmdLine);
void closeHandles();
DWORD execute(BOOL wait);
BOOL appendToPathVar(const char* path);
DWORD execute(const BOOL wait);
#endif // _LAUNCH4J_HEAD__INCLUDED_

View File

@ -1,43 +1,71 @@
/*
Launch4j (http://launch4j.sourceforge.net/)
Cross-platform Java application wrapper for creating Windows native executables.
launch4j :: Cross-platform Java application wrapper for creating Windows native executables
Copyright (C) 2004-2005 Grzegorz Kowal
Copyright (c) 2004, 2008 Grzegorz Kowal
Ian Roberts (jdk preference patch)
Compiled with Mingw port of GCC, Bloodshed Dev-C++ IDE (http://www.bloodshed.net/devcpp.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// ICON
#define APP_ICON 1
#define APP_ICON 1
// BITMAP
#define SPLASH_BITMAP 1
#define SPLASH_BITMAP 1
// RCDATA
#define JRE_PATH 1
#define JAVA_MIN_VER 2
#define JAVA_MAX_VER 3
#define SHOW_SPLASH 4
#define SPLASH_WAITS_FOR_WINDOW 5
#define SPLASH_TIMEOUT 6
#define SPLASH_TIMEOUT_ERR 7
#define CHDIR 8
#define SET_PROC_NAME 9
#define ERR_TITLE 10
#define GUI_HEADER_STAYS_ALIVE 11
#define JVM_ARGS 12
#define JAR_ARGS 13
#define JRE_PATH 1
#define JAVA_MIN_VER 2
#define JAVA_MAX_VER 3
#define SHOW_SPLASH 4
#define SPLASH_WAITS_FOR_WINDOW 5
#define SPLASH_TIMEOUT 6
#define SPLASH_TIMEOUT_ERR 7
#define CHDIR 8
#define SET_PROC_NAME 9
#define ERR_TITLE 10
#define GUI_HEADER_STAYS_ALIVE 11
#define JVM_OPTIONS 12
#define CMD_LINE 13
#define JAR 14
#define MAIN_CLASS 15
#define CLASSPATH 16
#define WRAPPER 17
#define JDK_PREFERENCE 18
#define ENV_VARIABLES 19
#define PRIORITY_CLASS 20
#define DOWNLOAD_URL 21
#define SUPPORT_URL 22
#define MUTEX_NAME 23
#define INSTANCE_WINDOW_TITLE 24
#define INITIAL_HEAP_SIZE 25
#define INITIAL_HEAP_PERCENT 26
#define MAX_HEAP_SIZE 27
#define MAX_HEAP_PERCENT 28
#define STARTUP_ERR 101
#define BUNDLED_JRE_ERR 102
#define JRE_VERSION_ERR 103
#define LAUNCHER_ERR 104
#define INSTANCE_ALREADY_EXISTS_MSG 105

Some files were not shown because too many files have changed in this diff Show More