Compare commits

...

261 Commits

Author SHA1 Message Date
idk
fadf25dc8f Create a branch for 1.7.1 2022-04-20 08:58:59 -04:00
zzz
3b9c26fe8a 1.7.0 2022-02-21 09:12:59 -05:00
zzz
961936f8d5 bump for review 2022-02-18 13:58:02 -05:00
zzz
7ea31835c2 more translations from Transifex 2022-02-18 13:44:08 -05:00
zzz
bf1f2e4635 pull translations from Transifex 2022-02-18 13:39:24 -05:00
zzz
198008472a i2psnark standalone: Raise open files ulimit
copied from i2prouter script
2022-02-17 13:58:10 -05:00
91e9d95df7 Merge branch 'docker-host-networking' into 'master'
Make Docker host networking safer

See merge request i2p-hackers/i2p.i2p!54
2022-02-16 15:53:32 +00:00
1b5feda517 generic advice for cloud deployments 2022-02-16 15:51:28 +00:00
29f74ba72a change interfaces 0.0.0.0->127.0.0.1 and update documentation 2022-02-16 11:14:10 +00:00
zzz
d6684403a2 Add new Japanese man pages 2022-02-12 13:47:52 -05:00
zzz
388aa233e0 Add new Slovenian translated resources 2022-02-12 13:30:33 -05:00
idk
ea92b79340 set table-width to fixed on console dark theme, and run the auto-indenter on the console light theme to fix it's inconsistent tabs. Merge the chrome-overrides rules on the console light theme CSS. Changes to light theme are entirely cosmetic, except that they improve the reliability of my CSS linter. 2022-02-11 23:01:51 -05:00
idk
3ba754d723 Merge branch 'master' of github.com:i2p/i2p.i2p 2022-02-11 15:34:41 -05:00
idk
d651d25de6 Merge pull request #26 from zlatinb/github-actions
Use GitHub Actions to publish installer.jar on each push
2022-02-11 15:32:37 -05:00
idk
00ea32938e set .wizardimg css to display: none on the dark theme so the light-theme background images don't show up. Normalize the wildly inconsistent tabs tabs in console.css 2022-02-11 15:27:50 -05:00
37002822b3 install.jar fix name 2022-02-11 19:50:40 +00:00
50d56ccbe8 gettext 2022-02-11 19:46:27 +00:00
48cb6f79ef Use GitHub Actions to publish installer.jar on each push 2022-02-11 19:38:04 +00:00
idk
a325e63426 use updateType instead of key for error message in ConsoleUpdateManager with custom UPP 2022-02-10 12:42:50 -05:00
zzz
309e306337 javadoc fixes 2022-02-10 09:02:39 -05:00
zzz
2ba56a5e17 Bump build time 2022-02-10 08:39:46 -05:00
zzz
c949ad5205 Update Manager: Add an UpdateType for the API version 2022-02-10 08:37:52 -05:00
zzz
5621b4bf97 GeoIP 2022-02 2022-02-10 08:28:19 -05:00
idk
dbfe8d24a8 log not-found key should an UPP error occur 2022-02-09 21:33:32 -05:00
zzz
548c0994a7 poupdate-source 2022-02-09 14:25:09 -05:00
zzz
d0ca1d38ca NTCP: Fixes for SSU disabled
Update RI reachability after first inbound connection
Allow local address in test mode
2022-02-09 06:39:18 -05:00
idk
96560e8590 Increase size of unicode links on proxy.css 2022-02-07 00:26:18 -05:00
zzz
19712cfd95 SSU: Fix race NPE in debug logging 2022-02-06 09:51:46 -05:00
idk
3dcc954341 Put description of jump link function on newline in proxy.css 2022-02-04 17:26:27 -05:00
zzz
568b5e303f Tunnels: Avoid buggy routers
SSU: Don't bid on connection to buggy routers
2022-02-03 10:36:03 -05:00
idk
73d90ed5c4 Don't add os-arch to pluginname if it's already correct 2022-02-03 01:51:33 -05:00
idk
b2fe36b0d3 Revise ShellService.name to match ShellService directory if the directory includes -SystemVersion.getOS or -SystemVersion.getArch 2022-02-02 17:37:02 -05:00
idk
632a1578a2 Check executable status of shellservice plugins in start. If the ShellService plugin name doesn't correspond to a directory, check plugin name+-$OS-$ARCH and name+$OS. 2022-02-02 15:18:25 -05:00
idk
e28f4be46b Merge branch 'plugin-config-redux' into 'master'
Adds the ability to use `$OS`  and `$ARCH`  variables in clients.config and plugins.config(updateURL.* only)

Closes #340

See merge request i2p-hackers/i2p.i2p!52
2022-02-02 18:57:30 +00:00
idk
73e34b3941 Adds the ability to use $OS and $ARCH variables in clients.config and plugins.config(updateURL.* only) 2022-02-02 18:57:30 +00:00
b4e2366458 Merge branch 'filefilter-fix' into 'master'
Fix for FileFilterDefinitionElement.  Issue #349

See merge request i2p-hackers/i2p.i2p!50
2022-01-31 13:22:44 +00:00
1389e89f6d Merge branch 'junit-deprecations' into 'master'
fix junit deprecations, issue #339

See merge request i2p-hackers/i2p.i2p!51
2022-01-31 13:11:17 +00:00
c3abe7b3d4 Do not use forEach 2022-01-31 13:10:26 +00:00
042c1e88aa fix junit deprecations, issue #339 2022-01-31 11:39:57 +00:00
899ce0f959 Fix for FileFilterDefinitionElement. Issue #349 2022-01-31 04:38:48 +00:00
zzz
5dd8139aad Reseed, DoH: Fixes for IPv6-only 2022-01-30 11:25:36 -05:00
zzz
13ee324d36 NTCP2: Clock skew handling improvements
as discussed in #ls2 meeting
- Bob replies with Session Created even if skewed,
  so that Alice finds out what the skew is
- Alice handles Session Created timestamp and drops if skewed,
  bans Bob, and updates clock if NTP failed
- If Alice does reply with SessionConfirmed, Bob will send a
  destroy with a skew error code
- Don't change skew error code if netdb store failed
- Fix skew adjustment for RTT by Bob
- Call setLastBadSkew() in the right places
- Fix ntcp.invalidInboundSkew and ntcp.invalidOutboundSkew stats
2022-01-26 07:28:43 -05:00
zzz
afa7278080 NTCP: Ban IP if RI signature fails 2022-01-25 12:22:57 -05:00
zzz
b6be2d7e65 bump -9 2022-01-25 09:42:33 -05:00
zzz
78ba3d1f68 Console: CSS tweak for messages 2022-01-25 09:41:12 -05:00
zzz
d930f0a64a Console: Set encoding for CSS 2022-01-25 09:40:02 -05:00
zzz
c1dc3c8275 Data: Remove dup check for negative tunnel ID 2022-01-25 09:37:26 -05:00
zzz
8bf87da4b1 Transport: BW limiter log and javadoc improvements
Portion of gitlab MR !49
2022-01-25 09:35:33 -05:00
zzz
8812e822f9 Util: CDPQ stat description tweak 2022-01-25 09:29:43 -05:00
zzz
f17cd24dc8 UDP: Pass message priority through to the packets
Change UDP-Sender queue from CoDel to CoDelPriority
No change to CoDel params

UDP msg priorities:
High priority: ack-only, session request/created/confirmed, relay request, hole punch, injected
Low priority: ping, destroy, peer test, relay intro/response
2022-01-25 09:27:49 -05:00
zzz
b9f53069bb Tunnels: Reimplement, re-enable using tunnel builds as a tunnel test,
but without ooming
disabled in 2009 because of ooms
2022-01-25 09:10:00 -05:00
zzz
21f5f7c148 Tunnels: Enable tunnel testing by default
disabled since 2011
2022-01-25 09:03:14 -05:00
idk
3057103875 tweak light proxy.css on non-mobile screens 2022-01-23 11:02:47 -05:00
idk
2752015b6e Simplify, add better logging, correctly mark state change in ShellService 2022-01-22 21:26:59 -05:00
idk
f2e0aacbf0 use Arrays.toString to convert trimmed process args to application args in ShellService. 2022-01-22 14:56:04 -05:00
idk
3e8f8a2bd3 add null check to isProcessRunning() in new ShellService 2022-01-22 00:12:37 -05:00
zzz
77e30e246d Util: Fix leak of SimpleTimer2 shutdown task 2022-01-21 09:09:32 -05:00
idk
5e7a00ede4 Merge branch 'fix-docker-configs-support' into 'master'
Allow chown to fail so files can be managed via docker configs and secrets

See merge request i2p-hackers/i2p.i2p!48
2022-01-16 16:45:56 +00:00
ba55ec09ed Allow chown to fail so files can be managed via docker configs and secrets 2022-01-16 16:45:56 +00:00
zzz
0b058c0ffd i2psnark: Add UDPTrackerClient
WIP - not hooked in, untested - target 1.8.0 / 0.9.54
Requires significant changes to TrackerClient
Adapted from mtn branch i2p.i2p.zzz.udpsnark (2014)
Ref: Proposal 160
2022-01-16 08:58:03 -05:00
idk
61422d9f7f Merge branch 'docker_update' into 'master'
Upgrade base image to latest version of Alpine

See merge request i2p-hackers/i2p.i2p!47
2022-01-15 20:32:48 +00:00
zzz
b63a2e41be i2psnark: html fix 2022-01-15 11:52:13 -05:00
zzz
606961c788 Console: Add ban counts to headers 2022-01-15 06:59:52 -05:00
9573c6ed0f Upgrade base image to latest version of Alpine 2022-01-14 20:17:52 +00:00
idk
70bb63e8ab Make the buttons on the save host form look like the links on the jump host form 2022-01-10 17:16:30 -05:00
idk
b96255a65b Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.i2p 2022-01-10 14:44:45 -05:00
idk
e15dae5c5f Turn jump button-links back into regular-looking hyperlinks when resolution indicates we are not on a mobile device 2022-01-10 14:28:40 -05:00
zzz
695cf8796d javadoc: Add API version 2022-01-10 09:07:07 -05:00
zzz
175f043819 javadoc: Add message flow chart 2022-01-10 08:57:01 -05:00
zzz
662ea995c1 javadoc note 2022-01-07 10:48:48 -05:00
zzz
b8670e1e5b hosts.txt update 2022-01-07 05:53:50 -05:00
zzz
7f4441078d Router: Prevent deadlock at startup
in the transports through PLRIJ via FNDF.publish()
2022-01-06 07:18:42 -05:00
zzz
150248d8d7 Plugins: Fix webapp classpath setting when the webapp name does not match the plugin name 2022-01-05 16:50:33 -05:00
zzz
aaa1da4c66 Plugins: console-icon path is relative to consoleLinkURL if specified,
otherwise relative to plugin name, as before
2022-01-05 15:08:35 -05:00
zzz
8167f5184d hosts.txt update 2022-01-05 06:25:01 -05:00
zzz
034a5acd37 i2pcontrol: Send translated tunnel status string 2022-01-04 13:09:34 -05:00
idk
7249f21602 redirect output and error from process builder to files within the plugin directory 2022-01-03 14:26:27 -05:00
idk
d1192f74f2 Remove platform-specific workarounds from Java 8+ version of ShellService 2022-01-03 14:18:16 -05:00
zzz
13f910be70 i2ptunnel: Add hooks to get the session from the contoller 2022-01-02 11:23:23 -05:00
zzz
2d42541b79 i2pcontrol: Handle more router states mapping to i2pcontrol states
Linkify start/stop webapp on password page
2022-01-02 11:13:15 -05:00
zzz
131da9bdb9 javadoc fixes 2021-12-30 09:59:26 -05:00
zzz
bc97e955e2 bump -7 2021-12-28 12:24:37 -05:00
zzz
faa1bf117a i2ptunnel: Add IRC filter support for IRCv3 message tags
Required for irc.ilita.i2p
2021-12-28 12:07:07 -05:00
zzz
aa386f3bdc bump -6 2021-12-28 10:02:27 -05:00
zzz
f1170b948f NetDB: StoreJob reliability improvements
- Always use a lease as the reply tunnel when publishing LS through a client tunnel
  This ensures we're not using about-to-expire tunnels for the reply,
  and ensures the ff is able pick an alternate
- Don't count skipped peers as attempted in FloodfillVerify
- Pass failed and skipped peers to FloodfillVerify job to be skipped there also
- Pass failed and skipped peers from FloodfillVerify job to the next StoreJob on failure
- Consolidate common reply token generation code in StoreJob
- Ensure tunnel diversity in StoreJob retries by only
  using tunnels closest to the target for the first request.
  This increases reliability by not reusing the same tunnels for all retries.
- Refactor StoreState to rework inefficient methods and unused data
- Clean up commented-out code in StoreState
- Log tweaks
2021-12-28 09:57:42 -05:00
zzz
59ab40779c NetDB: RepublishLeaseSetJob
Don't requeue on failure if there is a newer LS, KNDF will have already done that.
Log tweaks and cleanups
2021-12-28 09:44:39 -05:00
zzz
85b9862b64 NetDB: Ensure tunnel diversity in ISJ retries
by only using tunnels closest to the target for the first request.
This increases reliability by not reusing the same tunnels for all retries.
2021-12-28 09:24:35 -05:00
zzz
132d76a06b NetDB: SearchJob minor cleanup, only call getHash() once 2021-12-28 09:15:48 -05:00
zzz
c4b4b2d4b2 NetDB: Increase lookup throttle time
This reduces the max lookup rate
2021-12-28 09:12:29 -05:00
zzz
db6914f555 NetDB: Use same dbResponseTime rate in ff peer selector as in KNDF.getPeerTimeout()
10 minute rate is too short
Change to getAvgOrLifetimeAvg()
Reduce max time
2021-12-28 09:02:40 -05:00
zzz
bef729463d NetDB: Fix usage of dbResponseTime stat
Actually update the stat for stores in dbStoreSent();
we are generally storing to different ffs than lookups, so we need the
stat for stores as well, since we use it as the timeout in StoreJob.

Change from 1-day to 1-hour stat.
Switch to avgOrLifetimeAvg() so the rate is always valid.
Reduce max time used for timeout.
This allows more peers to be tried before total timeout
Previously, the per-peer timeout was almost always the max.
Make sendStore() package private.
Javadocs and cleanups.
2021-12-28 08:56:47 -05:00
zzz
d0e72aca66 Console: Partial string case-insensitive match for netdb family search 2021-12-28 08:55:29 -05:00
zzz
46ee8be9c6 i2psnark standalone: Add version number to header 2021-12-27 12:40:33 -05:00
zzz
cc3d2cf67b bump -5 2021-12-27 09:00:11 -05:00
zzz
57c997730f Console: Fix display of ip lookup param with netmask in netdb search 2021-12-27 08:55:49 -05:00
zzz
0826f431ef i2psnark standalone: Fixes for router startup and shutdown
so that torrents stop when the router stops and restart when the router restarts.

- Use BWLimits from the DirMonitor as a periodic test that the router is there
- DirMonitor does not attempt to autostart torrents if BWLimits test fails
- DirMonitor does autostart existing torrents when BWLimits test passes again
- Register disconnect listener with socket manger and stop all torrents on disconnect
- Use stopTorrent(true) on router errors to prevent changing the persisted torrent running status
- Change autostart default to true for standalone

Possibly more todo for corner cases or other start/stop/fail scenarios.
2021-12-27 08:52:56 -05:00
zzz
c63cb378e8 I2CP: Send DestroySession message when destroying session
in client-side AppContext SimpleSession, to prevent router-side
error message when closing socket, e.g. for BW limits check
2021-12-27 08:32:04 -05:00
zzz
242dc92397 Banlist: Increase ban time again for routers without netID
it's not a i2pd bug at startup that fixes itself.
2021-12-27 08:27:11 -05:00
zzz
26f34d6985 Debian: Update URL in watch file 2021-12-23 15:28:07 -05:00
idk
e002d3f558 Move ShellService into net.i2p.router.web 2021-12-23 15:10:24 -05:00
zzz
3d5dd639e3 i2psnark standalone: Use previously translated I2CP connect error 2021-12-23 13:49:18 -05:00
zzz
2bfedfbc74 i2psnark standalone: Translate I2CP connect error 2021-12-23 13:06:58 -05:00
zzz
70131c6b25 i2psnark standalone: Pass ctx to logger 2021-12-23 12:02:19 -05:00
zzz
e946040ddd i2psnark standalone: Redirect jetty logging to i2p log 2021-12-23 11:54:51 -05:00
zzz
bab37e57fe i2psnark: Add note for translators 2021-12-23 11:23:00 -05:00
zzz
70e06de846 i2psnark: Translate theme names, translated sort 2021-12-23 11:01:05 -05:00
zzz
11f60a7192 i2psnark standalone: Set launch-i2psnark +x 2021-12-23 09:47:48 -05:00
zzz
6282c365bb i2psnark standalone: Update readme 2021-12-23 09:12:57 -05:00
zzz
621ea49621 i2psnark standalone: Add da, el, and fa to language menu 2021-12-23 08:16:32 -05:00
zzz
e51738d180 i2psnark standalone: Add jbigi.jar
as requested by R4SAS
2021-12-21 06:51:06 -05:00
zzz
811442f9cb Transport: Async NTCP writes (MR !43)
- Write directly from writer threads, except for during establishment and when write doesn't complete; throw those to the pumper as usual
- New NTCPCon writelock, readlock, and statlock (formerly all on NTCPCon.this) to prevent deadlocks
- NTCPCon chan and key now volatile, remove synch to prevent deadlocks
- All interestOps changes now lock on the key via setInterest() and clearInterest() since changes may now happen in multiple threads
- Set _paddingConfig at initialization to avoid NPE

Greatly reduces pumper loops and CPU

As proposed by jogger
Reviewed by zlatinb
Ref: http://zzz.i2p/topics/3192
2021-12-21 06:37:10 -05:00
zzz
464a39b939 MaskedIPSet: More efficient string generation
and don't IAE on 8 byte negative value
2021-12-19 10:53:50 -05:00
zzz
1a05083ed0 Tunnels: Double mask value for IPv6
rather than using a fixed value of 6
so IPv6 default is now 4
2021-12-19 08:38:13 -05:00
zzz
937b6120ff i2psnark standalone: Add notes about changing browser and port 2021-12-18 07:52:30 -05:00
zzz
2a451cdb97 bump -3 2021-12-18 06:34:21 -05:00
zzz
ccba4a197d Tunnels: Do not allow failed tunnels to be rebuilt 2021-12-18 06:33:36 -05:00
zzz
feaff690a3 Debian build doc update 2021-12-18 06:28:19 -05:00
zzz
098ef9a0ff Tunnels: remove log in test timeout job 2021-12-18 06:27:18 -05:00
zzz
f317d29dd5 javadoc fix 2021-12-18 06:25:04 -05:00
zzz
f17b568f19 Tunnels: Remove old NTCP cost=2 check in MaskedIPSet 2021-12-18 06:22:54 -05:00
zzz
5029516087 i2ptunnel: Use defined SOCKS constants 2021-12-18 06:21:25 -05:00
zzz
69699638ae i2psnark: Add avif mime type 2021-12-18 06:19:49 -05:00
zzz
e6c76fa5ae Console: CSS tweak for update status box 2021-12-18 06:18:49 -05:00
zzz
b8435f5e9e Tunnels: Cleanup settings for IP restriction
Check bounds at initialization
Remove unused setIPRestriction()
2021-12-18 06:17:41 -05:00
zzz
5995b0b7a7 Tunnels: Restore support for IP restriction in client tunnels (MR !45)
Removed in May 2011 when we added fast tier slices
Also add support in exploratory tunnels
Create MaskedIPSet in peer selectors, pass to ProfileOrganizer.selectXXX() for each call.
Not required for one-hop tunnels.
Disable for test networks (i2np.allowLocal)
Reported by 'vulnerability_reports' http://zzz.i2p/topics/3215
2021-12-18 06:14:09 -05:00
zzz
80237a57bd Reseed: Renew SSL cert 2021-12-13 07:01:41 -05:00
idk
c4cfe420a6 disable any chance of JNDI lookups in log4j.properties file by setting %m{nolookups}. I don't think we're actually vulnerable to CVE-2021-44228 if I'm understanding correctly, by default it doesn't seem like we actually use log4j for much of anything and we don't do much logging of arbitrarily crafted remote inputs, but also it seems like this JNDI lookups thing is way more trouble than it could possibly be worth to us. Maybe it's a good idea to make sure it's turned off by default. 2021-12-10 21:01:37 -05:00
zzz
14c5d54f0e Reseed: Server list update 2021-12-10 07:58:08 -05:00
zzz
b1a4a8517e i2ptunnel: Refactor UDPTunnel, Streamr, and SOCKS UDP for I2CP ports
- Add fromPort and toPort to Sink interface (breaking API change)
- Change cache maps from Destination to I2PSocketAddress to include port
- Accept host:port for destination in Streamr Client, use port
  as toPort in pinger
- Change to muxed listener in I2PSource, only listen for
  specified protocols
- Eliminate thread and queue in I2PSource, process messages inline
  in the listener
- Add support for handling both repliable and raw datagrams in
  a single I2PSource instance
- Remove verify option from I2PSource and I2PTunnelUDPServerBase,
  always verify repliable datagrams
- Add getPort() method to UDPSource
- Add a constructor to UDPSink to pass in an existing DatagramSocket
- Change I2PTunnelUDPClientBase to receive both repliable and raw
- Change SOCKSUDPTunnel reply handling strategy to key on I2CP toPort;
  remove ReplyTracker; the tunnel would not have worked before, because
  it expected raw replies only but MultiSink required a destination
  to look up where to forward the reply.
- Mark SOCKSUDPTunnel as preliminary; note lack of support
  for raw replies; untested
- Change Streamr Client Pinger to support fromPort
- Change Streamr Server to remember fromPort in subscriptions
  and use it as toPort in data stream
- Move fields to top of classes for sanity
- Cleanups and log tweaks
2021-12-08 13:05:27 -05:00
zzz
22ff40bc84 Build: Add missing @Override annotations (dep-ann lint) 2021-12-07 15:33:41 -05:00
zzz
b5d7dffb08 Debian: Add explicit dependency on libservlet3.1-java (Debian #997213)
libjetty9-java used to depend on libservlet3.1-java
but now in sid it doesn't.
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=997213
2021-12-07 14:14:32 -05:00
zzz
a59cad0066 Router: Tweak shutdown messages
Change one from CRIT to WARN
Translate one of them
Attempt to translate class name in notifications
2021-12-05 07:21:57 -05:00
zzz
cbb6a6db65 DTG: Add menu items to control notifications
Fix check for successful configuration save
2021-12-05 06:38:28 -05:00
zzz
730b9790d9 Console: Remove job queue link on /configservice 2021-12-04 16:54:12 -05:00
zzz
ebf6ca5b34 Console: Case-insensitive sort of jobs on /jobs 2021-12-04 16:03:53 -05:00
zzz
0422134a86 SusiMail: Notify new messages on DTG
experimental
2021-12-04 12:47:47 -05:00
zzz
1a77352fa7 i2psnark: Notify completed downloads on DTG
experimental
2021-12-04 11:59:39 -05:00
zzz
cc971eb34f Build: Fix minimum Java version in installer config 2021-12-04 10:56:45 -05:00
zzz
fa0e59435e DTG: Change icon from white to black on Windows by default
Will be set to the opposite of the console theme.
2021-12-04 10:48:11 -05:00
zzz
962cc31f31 DTG: Show all CRIT messages on DTG.
Experimental, may add a separate config or disable later.
Show I2P starting message in DTG
2021-12-04 09:50:08 -05:00
zzz
87362fd68b i2psnark: Edit torrent page cleanup - remove unneeded info 2021-12-04 08:02:37 -05:00
zzz
51f6bef5dc i2psnark: Respect newlines in torrent comments 2021-12-04 07:38:24 -05:00
idk
a1ea48e2b6 Fix the very first ShellService bug, the long pid should be parsed from the pidString from the wrapper output, or it won't be available 2021-12-03 22:44:46 -05:00
zzz
e9aa3a55cc Add file missing from previous checkin to fix build 2021-12-03 16:51:51 -05:00
zzz
d03c690724 Tunnels: Immediately fail outbound tunnels when
we can't connect to the first hop
by attaching an onSendFailJob in OutboundSender.
Check if failed in isValidTunnel()
2021-12-03 12:51:01 -05:00
zzz
2a900a8c5b i2psnark: Add torrent edit page
Additional UI cleanup to follow
2021-12-03 06:26:14 -05:00
zzz
de995761db Tunnels: Change tunnel test failure count to AtomicInteger 2021-12-03 06:15:37 -05:00
zzz
cfbdf8385d Tunnels: Count consecutive build timeouts per-pool
Use exploratory paired tunnel after too many timeouts
2021-12-03 06:10:21 -05:00
zzz
83b959c4a1 Tunnels: Remove 2nd arg on TestJob failure stats 2021-12-03 05:51:56 -05:00
zzz
e66ec208a8 Tunnels: Refactor build completion handling
- Add result code to BuildExecutor.buildComplete() and TunnelPool.buildComplete()
- Remove BuildExecutor.buildSuccessful(), move to buildComplete()
- Move ExpireJob creation to buildComplete()
- TunnelPool.buildComplete() now calls addTunnel()
- Eliminate some now() calls
2021-12-03 05:48:27 -05:00
zzz
cf22186182 Router: Shorter ban time for localhost addresses 2021-12-01 15:51:44 -05:00
zzz
890a8927a5 DTG: Add notification service to display popup messages
unused for now
2021-12-01 08:37:51 -05:00
zzz
dd439bc9be Transport: Add NTCPConnection.getRemoteIP()
to match SSU PeerState method
2021-11-30 11:24:24 -05:00
zzz
537a8bf19b Console: Linkify router hashes on Sybil analysis 2021-11-30 11:14:51 -05:00
zzz
bd0c696b84 CLI: Allow empty blocklist when signing news feed 2021-11-30 11:11:30 -05:00
zzz
5c56884d7f bump -1 2021-11-30 10:51:33 -05:00
zzz
b53707074f DTG: Add classpath to jar, add early check for support when called by CLI
for ease of command-line testing
2021-11-30 10:26:52 -05:00
zzz
6cb8d2eeb7 i2ptunnel: Increase default priority for IRC and standard tunnels 2021-11-30 10:23:14 -05:00
zzz
3895cd1068 Console: NetDB search form improvements 2021-11-30 09:53:44 -05:00
zzz
5b2fbc4ec4 Build: Add bumpBuildTime target 2021-11-30 09:35:06 -05:00
zzz
87654e2f4f Build: Remove BOB from installer, updater, and clients.config
Source remains for now and may still be built with ant buildBOB.
Existing non-package installs will continue to work.
2021-11-30 09:31:06 -05:00
zzz
9c29f8c8ff Debian files for 1.6.1
fix a lintian warning about compare-versions
update launchpad doc for git
2021-11-30 08:53:49 -05:00
zzz
619c36d18d 1.6.1 2021-11-29 12:21:54 -05:00
zzz
cf10a2d5b6 Tunnels: Fix NPE in BuildHandler 2021-11-29 12:19:02 -05:00
zzz
56fdc244d4 1.6.0 2021-11-29 10:41:18 -05:00
zzz
adc69c0d9a Refresh Debian patch 2021-11-29 10:31:55 -05:00
zzz
dd9a5548a8 Man pages: Update bug reporting URL 2021-11-27 08:52:30 -05:00
zzz
ab88f86954 ShellService: More import cleanup 2021-11-27 08:05:57 -05:00
zzz
9466b225b6 blocklist update 2021-11-26 12:47:35 -05:00
zzz
ef1e2b02de Build: Fix the tagged string with '75%'
which causes gettext to add a java-printf-format directive,
then the testscript fails if the translated
string doesn't have a '%' in it; strip out the directive
Bump for review
2021-11-26 11:13:41 -05:00
zzz
ee68aec647 Pull translations 2021-11-26 11:06:29 -05:00
zzz
3dfeb92b63 Update bug report links 2021-11-24 10:33:30 -05:00
zzz
ee5288ebb1 i2ptunnel: AccessFilter findbugs 2021-11-24 09:08:18 -05:00
zzz
488acdfd98 Util: ShellService findbugs and cleanups 2021-11-24 08:59:08 -05:00
idk
ee2e7ec30d Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.i2p 2021-11-23 14:08:19 -05:00
idk
40466bc602 Tweak location of dark theme animation. Closes #335 2021-11-23 14:06:41 -05:00
zzz
d8d6954ef0 BuildTime update 2021-11-23 09:57:59 -05:00
zzz
0aa4550bbe Add Hungarian man pages 2021-11-23 07:51:47 -05:00
zzz
ad82946fd3 Tunnels: Drop request if hop throttle exceeded by 50%
Revert banning peer in throttles
2021-11-23 07:25:53 -05:00
zzz
77b48a48ab Console: Remove tinhat from home page at op's request 2021-11-22 09:38:35 -05:00
zzz
d948fa8db3 Update: Fix registered version of feed blocklist after update 2021-11-21 08:50:10 -05:00
zzz
31393c2bef Update: Add message about pack200 plugins 2021-11-21 08:32:09 -05:00
zzz
e3fc34ef1f Tunnels: Ban peer on excessive build requests
Drop requests if previous or next peer is banned
Console: Drop peer when manually banned
Update: Drop peer when banlisted
2021-11-21 08:17:43 -05:00
idk
d7fdd6d9dc Merge pull request #24 from kfeoktistoff/debian_mkdir_log_on_startup
debian: create a log dir on startup if absent
2021-11-20 21:53:06 +00:00
idk
5a3a7b843a Merge pull request #23 from shisheng-1/Modify_GRADLE_1
Improve GRADLE build Performance
2021-11-20 21:22:01 +00:00
e06f8961b4 debian: create a log dir on startup if absent 2021-11-20 17:35:28 +00:00
idk
9d1aa5b762 Merge branch 'shellservice' into 'master'
Manage Fork-and-Exec Plugins by Monitoring them by PID

See merge request i2p-hackers/i2p.i2p!39
2021-11-17 17:42:50 +00:00
idk
2e71a0b36a Manage Fork-and-Exec Plugins by Monitoring them by PID 2021-11-17 17:42:45 +00:00
zzz
b072f40ed1 Initial Slovenian translations for a few resources
not added to menu yet
2021-11-17 09:00:00 -05:00
zzz
35d2f118ce poupdate-source 2021-11-17 08:30:23 -05:00
idk
3f7f315951 Place .wizardnotice at bottom right of screen above the buttons on both dark and light themes(#335) 2021-11-16 12:21:40 -05:00
zzz
6ef4c74d97 NTCP: Move wantsWrite(byte[]) from EventPumper to NTCPConnection
for sanity in following the write code path, rather than
going from con to pumper to con, keep the code in con.
Prep for possible write-side improvements in a future release,
ref: http://zzz.i2p/topics/3192
2021-11-16 11:09:05 -05:00
zzz
0e4d684e7d NetDB: Add new reseed 2021-11-16 10:58:38 -05:00
zzz
e3be6b50ce Tunnels: Use connected peer for closest inbound hop when
approaching conn limits to increase tunnel build success

This should reduce chances of tunnel builds pushing us over conn limits,
and reduce build failures and watchdog warnings when at conn limits.
2021-11-12 07:21:27 -05:00
zzz
dad2bed334 GeoIP 2021-11-01 2021-11-09 08:35:11 -05:00
zzz
bbe66f0e18 Util: DoH server list update 2021-11-08 13:04:01 -05:00
27bf65c1a4 Improve GRADLE build Performance 2021-11-06 00:13:26 +08:00
zzz
9c7b415d62 Util: Don't warn for new SU3 file types 2021-11-03 08:11:05 -04:00
zzz
78e4572a8c CSS button and checkbox spacing,
NTCP table fixes
2021-10-28 09:08:52 -04:00
zzz
4507ecd5f2 javadoc PKF clarification 2021-10-28 09:06:54 -04:00
zzz
721d39c01d Remove UDPPacketReader logging 2021-10-28 08:25:43 -04:00
zzz
427fc1c1ca More javadocs on getLibDir() 2021-10-27 10:02:45 -04:00
idk
33f1b3be87 Merge branch 'add-libdir' into 'master'
Add an i2p.dir.lib property

See merge request i2p-hackers/i2p.i2p!40
2021-10-26 16:22:37 +00:00
idk
7e1c8c7983 Add an i2p.dir.lib property 2021-10-26 16:22:36 +00:00
idk
aa6b27d829 Merge branch 'backup-locale-conf' into 'master'
This adds inclusion of /etc/locale.conf to i2prouter i2p.init, per backup's...

Closes #326

See merge request i2p-hackers/i2p.i2p!41
2021-10-26 16:06:13 +00:00
idk
999e2615c3 This adds inclusion of /etc/locale.conf to i2prouter i2p.init, per backup's... 2021-10-26 16:06:12 +00:00
zzz
807b7d672f Debian: Update JRE dependencies 2021-10-23 12:03:49 -04:00
idk
685a2f1e39 4217a05ae9 and 1e70849bde were mis-tagged, cannot rewrite history on master, they apply to #335 and not #338 2021-10-21 13:53:48 -04:00
idk
4217a05ae9 Make the background images closer to exactly the same size on /welcome, #338 2021-10-21 13:40:28 -04:00
idk
1e70849bde Checkin consistency fixes for wizard themes, should address remaining issues with #338 2021-10-21 12:53:01 -04:00
zzz
1ab3e9b310 SSU: Send Bob-to-Alice Peer Test message in-session
Matches what i2pd does.
More checks to require in-session for
Alice/Bob and Bob/Charlie Peer Test messages.
2021-10-20 09:37:32 -04:00
zzz
fd2cf972bf Javadoc typos 2021-10-14 07:49:02 -04:00
zzz
d9eed6446e Util: Add more Intel processors to CPUID 2021-10-13 08:51:12 -04:00
zzz
6b823e6381 Tomcat 9.0.54 2021-10-11 10:46:26 -04:00
zzz
917b7e615e javadoc fix 2021-10-11 07:43:15 -04:00
zzz
af97381461 Jetty 9.3.30.v20211001
Remove patched SslConnection.java for Jetty #6072, fix included in this release
2021-10-10 12:09:12 -04:00
idk
4975bb1482 Fix positioning of options on welcome page between instructional text and progression buttons 2021-10-06 11:43:21 -04:00
zzz
83e2246195 Console: eepProxy -> proxy 2021-10-05 10:32:20 -04:00
zzz
3632070e3f i2ptunnel: Move the "(0 = unlimited)" text from section headers to tooltips 2021-10-05 09:56:55 -04:00
zzz
0cb30a085c i2ptunnel: Save access list as B64 to save space
Convert access list to B32 and sort in UI
Remove blank lines in get/set
2021-10-05 09:34:57 -04:00
zzz
a7a59a2b1b NetDB: Reduce ban time for routers without netId
Don't ban routers with bad netId before RI validation,
unless that router sent the RI
2021-10-05 08:40:30 -04:00
zzz
bf7155b935 NetDB: Consolidate getKBucketSetSize() calls
reported by jogger
fix javadoc
2021-10-03 10:13:04 -04:00
zzz
62fb294f54 Console: Replace n/a with -- on floodfills page for readability 2021-10-03 09:46:18 -04:00
zzz
b7e710b28f Wizard: Add theme picker page
Clean up configui.js
CSS cleanups for new page TODO
Gitlab #335
2021-10-03 09:05:26 -04:00
zzz
4a8534e4e6 SSU: Downgrade fragmentation log errors to warn
reported by drzed
2021-10-02 13:40:23 -04:00
zzz
aa4e2f5c95 Console: ConfigUIHelper minor refactor (prep for wizard) 2021-10-02 09:06:50 -04:00
zzz
fe4fbce7bd Wizard: Add a simple progress ticker to bw test status 2021-10-02 08:10:40 -04:00
idk
33374eacaa add input:disabled and button:disabled to dark theme css 2021-10-01 13:12:21 -04:00
zzz
cea76ed9d5 i2ptunnel: Fix enc type configuration logic 2021-10-01 08:18:07 -04:00
zzz
f41db2685e i2ptunnel: Center text in buttons (light) 2021-10-01 07:40:31 -04:00
zzz
95bf068b0a i2ptunnel: Remove experts-only label for X25519-only option 2021-10-01 07:39:09 -04:00
idk
e2caa246f2 Line up the columns on results page of the bandwidth wizard. Remove some unnecessary box-shadows which are causing optboxes to appear blurry in some cases. 2021-09-30 16:36:46 -04:00
idk
bed013d858 hide notification div when bandwidth test is complete, switch back to green icon for slide messages 2021-09-30 14:14:10 -04:00
zzz
282460cb3f Console: Add js to /configui to preview themes
Save theme change before form processing so no refresh required
Enable/disable reset and apply buttons on config clicks
Prep for theme picker in wizard
2021-09-30 09:55:35 -04:00
idk
f015d1f490 Merge branch 'master' of 127.0.0.1:i2p-hackers/i2p.i2p 2021-09-29 17:56:48 -04:00
idk
f0758ee36f Adjust alignment of notifications and increase contrast on dark theme /welcome, progress on #335 2021-09-29 17:55:49 -04:00
idk
c77e9537ae Adjust alignment of notifications and increase contrast on dark theme /welcome, progress on #337 2021-09-29 17:54:08 -04:00
zzz
b7de63e922 Console: Wizard HTML fixes 2021-09-28 12:15:01 -04:00
zzz
13ade14289 Console: Refactor wizard progress indicator 2021-09-28 09:52:07 -04:00
zzz
2b43e4e4b5 Router: Rekey all Android/ARM routers 2021-09-27 10:26:03 -04:00
idk
571986a78b Add transparent channel to wizardlogo.png s and blend with exclusion instead to improve appearance of logo on dark theme. 2021-09-26 02:24:42 -04:00
zzz
d7c89be9a2 Tunnels: Implement Bloom filter for short TBM
Reduce TBM Bloom filter size and interval for EC routers
2021-09-25 09:12:15 -04:00
zzz
d466fd6799 UDP: Use a single PacketBuilder everywhere 2021-09-22 12:44:28 -04:00
zzz
116ec88f56 UDP: Replace ACKSender thread with per-PeerState delayed ack timers
(low latency improvements part 2)

Timer is created in PeerState messageFullyReceived() and messagePartiallyReceived().
Don't send a delayed ack-only packet if acks are sent in a data packet first.

Reviewed and tested by zlatinb.
Related MRs: !36 !37 !38
2021-09-22 12:12:16 -04:00
346372e002 Merge branch 'ssu-low-latency-2' into 'master'
SSU low-latency changes pt1

See merge request i2p-hackers/i2p.i2p!37
2021-09-17 16:36:42 +00:00
f14b7d53a3 reduce the delay in ACKs to the minimum of rtt/2 and the constant 2021-09-17 17:32:37 +01:00
3355daa334 introduce a lock just for _sendWindowBytesRemaining field 2021-09-17 17:31:45 +01:00
zzz
67fea26638 Wizard: remove 'Tcpbw100' from test status messages 2021-09-16 11:50:44 -04:00
zzz
b1c367777d Console: Add constants for wizard page numbers
to make it easier to add/remove/reorder pages later
2021-09-16 10:50:57 -04:00
zzz
3917dc6d2f I2CP: Don't call listener.readError() after external client disconnect via destroySocketManager()
prevents log error on normal client shutdown
reported and tested by zlatinb
2021-09-10 13:43:26 -04:00
zzz
2d239edf34 Update: Get backup URLs from news feed
Remove hardcoded backup URLs
Parse i2p, clearnet, and clearnet-ssl URLs from news
Write i2p, clearnet, and clearnet-ssl URLs to old news format
Clearnet and clearnet-ssl URLs currently unused; no handler is registered
2021-09-05 10:47:10 -04:00
1fbe084b74 Update build.xml 2021-09-03 07:16:57 +00:00
7a37f09334 Merge branch 'choking-retransmission-fix' into 'master'
Prevent the sender from sending too far ahead of an unacked packet

See merge request i2p-hackers/i2p.i2p!35
2021-09-02 17:26:54 +00:00
1ae05103e4 Prevent the sender from sending too far ahead of an unacked packet 2021-09-02 18:22:12 +01:00
zzz
a66422fa3c Console: Catch error checking systray availability (gitlab issue !331) 2021-08-28 09:05:38 -04:00
zzz
dabc29f8a5 Build: Always echo JDK version 2021-08-27 09:51:32 -04:00
zzz
132da4a35a Debian: Add copyright file to apparmor list 2021-08-27 09:42:28 -04:00
zzz
ea1eac2343 Tunnels: Enable sending short tunnel build messages,
remove debug settings
2021-08-26 10:23:13 -04:00
zzz
569e035bfd Router: Increase rekey probability to 1 in 4 2021-08-26 09:57:16 -04:00
zzz
8b1b5d4eb3 Debian files for 1.5.0
Fix build error dh_installdocs linking from the libjbigi-jni arch package
to an arch:all package, this is an error for compat level 10.
2021-08-26 09:52:40 -04:00
zzz
33f64f7913 build checklists and windows scripts updates for 1.x 2021-08-24 09:58:17 -04:00
567 changed files with 89419 additions and 80067 deletions

34
.github/workflows/ant.yml vendored Normal file
View File

@ -0,0 +1,34 @@
# Mostly copied from https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant
# zlatinb
name: Java CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: GetText
run: sudo apt install gettext
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'temurin'
- name : Generate override.properties
run: |
rm -f override.properties
echo "build.built-by=GitHub Actions" >> override.properties
echo "noExe=true" >> override.properties
- name: build with Ant
run: ant distclean pkg
- name: Upload installer.jar
uses: actions/upload-artifact@v2
with:
name: I2P-install.jar-${{ github.sha }}
path: install.jar

View File

@ -96,6 +96,7 @@ trans.pt = core/locale/messages_pt.po
trans.pt_BR = core/locale/messages_pt_BR.po
trans.ro = core/locale/messages_ro.po
trans.ru_RU = core/locale/messages_ru.po
trans.sl = core/locale/messages_sl.po
trans.sv_SE = core/locale/messages_sv.po
trans.tk = core/locale/messages_tk.po
trans.tr_TR = core/locale/messages_tr.po
@ -133,6 +134,7 @@ trans.pt = router/locale/messages_pt.po
trans.pt_BR = router/locale/messages_pt_BR.po
trans.ro = router/locale/messages_ro.po
trans.ru_RU = router/locale/messages_ru.po
trans.sl = router/locale/messages_sl.po
trans.sv_SE = router/locale/messages_sv.po
trans.tk = router/locale/messages_tk.po
trans.tr_TR = router/locale/messages_tr.po
@ -206,6 +208,7 @@ trans.pt_BR = apps/routerconsole/locale-news/messages_pt_BR.po
trans.ro = apps/routerconsole/locale-news/messages_ro.po
trans.ru_RU = apps/routerconsole/locale-news/messages_ru.po
trans.sk = apps/routerconsole/locale-news/messages_sk.po
trans.sl = apps/routerconsole/locale-news/messages_sl.po
trans.sq = apps/routerconsole/locale-news/messages_sq.po
trans.sr = apps/routerconsole/locale-news/messages_sr.po
trans.sv_SE = apps/routerconsole/locale-news/messages_sv.po
@ -253,6 +256,7 @@ trans.pt_BR = apps/routerconsole/locale-countries/messages_pt_BR.po
trans.ro = apps/routerconsole/locale-countries/messages_ro.po
trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
trans.sk = apps/routerconsole/locale-countries/messages_sk.po
trans.sl = apps/routerconsole/locale-countries/messages_sl.po
trans.sq = apps/routerconsole/locale-countries/messages_sq.po
trans.sr = apps/routerconsole/locale-countries/messages_sr.po
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
@ -361,6 +365,7 @@ trans.pt_BR = apps/desktopgui/locale/messages_pt_BR.po
trans.ro = apps/desktopgui/locale/messages_ro.po
trans.ru_RU = apps/desktopgui/locale/messages_ru.po
trans.sk = apps/desktopgui/locale/messages_sk.po
trans.sl = apps/desktopgui/locale/messages_sl.po
trans.sr = apps/desktopgui/locale/messages_sr.po
trans.sv_SE = apps/desktopgui/locale/messages_sv.po
trans.sq = apps/desktopgui/locale/messages_sq.po
@ -439,6 +444,7 @@ trans.pt_BR = debian/po/pt_BR.po
trans.ro = debian/po/ro.po
trans.ru_RU = debian/po/ru.po
trans.sk = debian/po/sk.po
trans.sl = debian/po/sl.po
trans.sq = debian/po/sq.po
trans.sv_SE = debian/po/sv.po
trans.tk = debian/po/tk.po
@ -517,6 +523,7 @@ trans.pt_BR = core/java/src/gnu/getopt/MessagesBundle_pt_BR.properties
trans.ro = core/java/src/gnu/getopt/MessagesBundle_ro.properties
trans.ru_RU = core/java/src/gnu/getopt/MessagesBundle_ru.properties
trans.sk = core/java/src/gnu/getopt/MessagesBundle_sk.properties
trans.sl = core/java/src/gnu/getopt/MessagesBundle_sl.properties
trans.sq = core/java/src/gnu/getopt/MessagesBundle_sq.properties
trans.sr = core/java/src/gnu/getopt/MessagesBundle_sr.properties
trans.sv_SE = core/java/src/gnu/getopt/MessagesBundle_sv.properties
@ -550,6 +557,7 @@ trans.pt = apps/ministreaming/locale/messages_pt.po
trans.pt_BR = apps/ministreaming/locale/messages_pt_BR.po
trans.ro = apps/ministreaming/locale/messages_ro.po
trans.ru_RU = apps/ministreaming/locale/messages_ru.po
trans.sl = apps/ministreaming/locale/messages_sl.po
trans.sv_SE = apps/ministreaming/locale/messages_sv.po
trans.tk = apps/ministreaming/locale/messages_tk.po
trans.tr_TR = apps/ministreaming/locale/messages_tr.po
@ -570,8 +578,10 @@ trans.de = installer/resources/locale-man/man_de.po
trans.es = installer/resources/locale-man/man_es.po
trans.fi = installer/resources/locale-man/man_fi.po
trans.fr = installer/resources/locale-man/man_fr.po
trans.hu = installer/resources/locale-man/man_hu.po
trans.id = installer/resources/locale-man/man_id.po
trans.it = installer/resources/locale-man/man_it.po
trans.ja = installer/resources/locale-man/man_ja.po
trans.ko = installer/resources/locale-man/man_ko.po
trans.nl = installer/resources/locale-man/man_nl.po
trans.pl = installer/resources/locale-man/man_pl.po
@ -636,6 +646,7 @@ trans.pl = apps/routerconsole/resources/docs/readme_pl.html
trans.pt = apps/routerconsole/resources/docs/readme_pt.html
trans.ro = apps/routerconsole/resources/docs/readme_ro.html
trans.ru_RU = apps/routerconsole/resources/docs/readme_ru.html
trans.sl = apps/routerconsole/resources/docs/readme_sl.html
trans.tr_TR = apps/routerconsole/resources/docs/readme_tr.html
trans.uk_UA = apps/routerconsole/resources/docs/readme_uk.html
trans.zh_CN = apps/routerconsole/resources/docs/readme_zh.html

View File

@ -1,5 +1,25 @@
# I2P in Docker
### Very quick start
If you just want to give I2P a quick try or are using it on a home network, follow these steps:
1. Create two directories `i2pconfig` and `i2ptorrents`
2. Copy the following text and save it in a file `docker-compose.yml`
```
version: "3.5"
services:
i2p:
image: geti2p/i2p
network_mode: host
volumes:
- ./i2pconfig:/i2p/.i2p
- ./i2ptorrents:/i2psnark
```
3. Execute `docker-compose up`
4. Start a browser and go to `http://127.0.0.1:7657` to complete the setup wizard.
Note that this quick-start approach is not recommended for production deployments on remote servers. Please read the rest of this document for more information.
### Building an image
There is an i2P image available over at [DockerHub](https://hub.docker.com). If you do not want to use that one, you can build one yourself:
```
@ -17,21 +37,26 @@ By the default the image limits the memory available to the Java heap to 512MB.
#### Ports
There are several ports which are exposed by the image. You can choose which ones to publish depending on your specific needs.
|Port|Description|TCP/UDP|
|---|---|---|
|4444|HTTP Proxy|TCP|
|4445|HTTPS Proxy|TCP|
|6668|IRC Proxy|TCP|
|7654|I2CP Protocol|TCP|
|7656|SAM Bridge TCP|TCP|
|7657|Router console|TCP|
|7658|I2P Site|TCP|
|7659|SMTP Proxy|TCP|
|7660|POP Proxy|TCP|
|12345|I2NP Protocol|TCP and UDP|
|Port|Interface|Description|TCP/UDP|
|---|---|---|---|
|4444|127.0.0.1|HTTP Proxy|TCP|
|4445|127.0.0.1|HTTPS Proxy|TCP|
|6668|127.0.0.1|IRC Proxy|TCP|
|7654|127.0.0.1|I2CP Protocol|TCP|
|7656|127.0.0.1|SAM Bridge TCP|TCP|
|7657|127.0.0.1|Router console|TCP|
|7658|127.0.0.1|I2P Site|TCP|
|7659|127.0.0.1|SMTP Proxy|TCP|
|7660|127.0.0.1|POP Proxy|TCP|
|7652|LAN interface|UPnP|TCP|
|7653|LAN interface|UPnP|UDP|
|12345|0.0.0.0|I2NP Protocol|TCP and UDP|
You probably want at least the Router Console (7657) and the HTTP Proxy (4444). If you want I2P to be able to receive incoming connections from the internet, and hence not think it's firewalled, publish the I2NP Protocol port (12345) - but make sure you publish to a different random port, otherwise others may be able to guess you're running I2P in a Docker image.
#### Networking
A best-practices guide for cloud deployments is beyond the scope of this document, but in general you should try to minimize the number of published ports, while exposing only the `I2NP` ports to the internet. That means that the services in the list above which are bound to `127.0.0.1` (which include the router console) will need to be accessed via other methods like ssh tunneling or be manually configured to bind to a different interface.
#### Example
Here is an example container that mounts `i2phome` as home directory, `i2ptorrents` for torrents, and opens HTTP Proxy, IRC, Router Console and I2NP Protocols. It also limits the memory available to the JVM to 256MB.
```

View File

@ -1,4 +1,4 @@
FROM jlesage/baseimage:alpine-3.10-glibc as builder
FROM jlesage/baseimage:alpine-3.15-glibc as builder
ENV APP_HOME="/i2p"
@ -10,7 +10,7 @@ RUN add-pkg --virtual build-base gettext tar bzip2 apache-ant openjdk8 \
&& rm -rf pkg-temp/osid pkg-temp/lib/wrapper pkg-temp/lib/wrapper.* \
&& del-pkg build-base gettext tar bzip2 apache-ant openjdk8
FROM jlesage/baseimage:alpine-3.10-glibc
FROM jlesage/baseimage:alpine-3.15-glibc
ENV APP_HOME="/i2p"
RUN add-pkg openjdk8-jre

View File

@ -264,7 +264,7 @@ Applications:
Zxing 3.4.1:
See licenses/LICENSE-Apache2.0.txt
Jetty 9.3.29.v20201019 (jetty-*.jar, org.mortbay.*.jar):
Jetty 9.3.30.v20211001 (jetty-*.jar, org.mortbay.*.jar):
(not included in most distribution packages, except for jetty-i2p.jar)
See licenses/ABOUT-Jetty.html
See licenses/NOTICE-Jetty.html
@ -339,9 +339,9 @@ Applications:
Systray (systray.jar):
Public domain.
Tomcat 9.0.45 (jasper-runtime.jar):
Tomcat 9.0.54 (jasper-runtime.jar):
(not included in most distribution packages)
Copyright 1999-2020 The Apache Software Foundation
Copyright 1999-2021 The Apache Software Foundation
See licenses/LICENSE-Apache2.0.txt
See licenses/NOTICE-Tomcat.txt

View File

@ -64,13 +64,16 @@ your `~/.gradle/gradle.properties`:
systemProp.socksProxyHost=localhost
systemProp.socksProxyPort=9150
### Development builds
Automatic CI builds are available at the [continuous integration](https://github.com/i2p/i2p.i2p/actions/workflows/ant.yml) page.
### Docker
For more information how to run I2P in Docker, see [Docker.md](Docker.md)
## Contact info
Need help? See the IRC channel #i2p on irc.freenode.net
Bug reports: [https://trac.i2p2.de/report/1](https://trac.i2p2.de/report/1)
Bug reports: [https://i2pgit.org/i2p-hackers/i2p.i2p/-/issues](https://i2pgit.org/i2p-hackers/i2p.i2p/-/issues) [http://git.idk.i2p/i2p-hackers/i2p.i2p/-/issues](http://git.idk.i2p/i2p-hackers/i2p.i2p/-/issues)
Contact information, security issues, press inquiries: [https://geti2p.net/en/contact](https://geti2p.net/en/contact)

View File

@ -47,7 +47,8 @@ Need help?
IRC irc.freenode.net #i2p
Bug reports:
https://trac.i2p2.de/report/1
https://i2pgit.org/i2p-hackers/i2p.i2p/-/issues
http://git.idk.i2p/i2p-hackers/i2p.i2p/-/issues
Contact information, security issues, press inquiries:
https://geti2p.net/en/contact

View File

@ -10,6 +10,7 @@
<property name="javac.version" value="1.8" />
<property name="javac.release" value="8" />
<property name="require.gettext" value="true" />
<property name="manifest.classpath.name" value="Class-Path" />
<condition property="no.bundle">
<isfalse value="${require.gettext}" />
@ -86,6 +87,7 @@
<jar basedir="${build}" excludes="messages-src/**" destfile="${dist}/${jar}">
<manifest>
<attribute name="Main-Class" value="net.i2p.desktopgui.Main"/>
<attribute name="${manifest.classpath.name}" value="i2p.jar router.jar" />
<attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" />

View File

@ -10,9 +10,9 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2017-09-20 20:31+0000\n"
"Last-Translator: Vitaly Zdorovenko <stenliterziev@gmail.com>\n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2022-02-09 19:23+0000\n"
"Last-Translator: zzzi2p\n"
"Language-Team: Bulgarian (http://www.transifex.com/otf/I2P/language/bg/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -27,69 +27,81 @@ msgstr "Стартиране на I2P"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr "I2P е стартиран!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr "Стартиране"
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr "Стартиране на I2P Браузер"
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
msgid "Disable"
msgstr "Деактивиране"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr "Рестартиране на I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr "Спиране на I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr "Рестартирайте Незабавно"
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr "Изключване в {0}"
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr ""
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr ""
msgstr "Мрежа"
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:63
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P desktopgui\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-11 15:33+0000\n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2010-06-15 14:09+0100\n"
"Last-Translator: duck <duck@mail.i2p>\n"
"Language-Team: duck <duck@mail.i2p>\n"
@ -25,69 +25,81 @@ msgstr ""
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr ""
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:54
#: src/net/i2p/desktopgui/InternalTrayManager.java:206
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:75
#: src/net/i2p/desktopgui/InternalTrayManager.java:227
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
msgid "Disable"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:92
#: src/net/i2p/desktopgui/InternalTrayManager.java:244
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:109
#: src/net/i2p/desktopgui/InternalTrayManager.java:261
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:125
#: src/net/i2p/desktopgui/InternalTrayManager.java:277
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:142
#: src/net/i2p/desktopgui/InternalTrayManager.java:294
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:156
#: src/net/i2p/desktopgui/InternalTrayManager.java:308
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:362
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:364
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr ""
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:369
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr ""
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:63
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr ""

View File

@ -4,15 +4,16 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# タカハシ <indexial@outlook.jp>, 2013
# daingewuvzeevisiddfddd, 2022
# タカハシ, 2013
# Masayuki Hatta <mhatta@mhatta.org>, 2018
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2018-08-17 22:08+0000\n"
"Last-Translator: Masayuki Hatta <mhatta@mhatta.org>\n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2022-02-13 03:11+0000\n"
"Last-Translator: daingewuvzeevisiddfddd\n"
"Language-Team: Japanese (http://www.transifex.com/otf/I2P/language/ja/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -27,69 +28,81 @@ msgstr "I2P を開始"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr "I2P を起動中!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr "起動中"
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr "I2P ブラウザを起動"
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr "I2P システムトレイを設定"
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
msgid "Disable"
msgstr "無効"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr "通知を有効化"
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr "通知を無効化"
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr "システムトレイを無効化"
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr "I2P を再起動"
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr "I2P を停止"
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr "すぐに I2P を再起動"
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr "すぐに I2P を停止"
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr "I2P のシャットダウンを中止"
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr "{0} でシャットダウン"
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr "即時シャットダウン"
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr "ネットワーク"
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:63
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr "I2P: 右クリックでメニュー"

View File

@ -4,14 +4,15 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# Zagros <zagros21@cmail.nu>, 2020
# Zagros, 2021
# Zagros, 2020
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2020-09-23 21:02+0000\n"
"Last-Translator: Zagros <zagros21@cmail.nu>\n"
"PO-Revision-Date: 2021-08-30 21:05+0000\n"
"Last-Translator: Zagros\n"
"Language-Team: Kurdish (http://www.transifex.com/otf/I2P/language/ku/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -42,7 +43,7 @@ msgstr "وێبگەڕی I2P بکەوە"
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
msgid "Configure I2P System Tray"
msgstr ""
msgstr "دەستکاری سیستەمی ئاگەدارکردنەوەی I2P بکە"
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
@ -72,7 +73,7 @@ msgstr "دەستبەجێ I2P بوەستێنە"
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
msgid "Cancel I2P Shutdown"
msgstr "کووژاندنەوە I2P هەڵبوەشێنەوە"
msgstr "دەستهەڵگرتن لە کووژاندنەوەی I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
#, java-format

View File

@ -0,0 +1,106 @@
# I2P
# Copyright (C) 2009 The I2P Project
# This file is distributed under the same license as the desktopgui package.
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# Žan Šadl-Ferš, 2021
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2022-02-09 19:23+0000\n"
"Last-Translator: zzzi2p\n"
"Language-Team: Slovenian (http://www.transifex.com/otf/I2P/language/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
msgid "Start I2P"
msgstr "Zaženi I2P"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr "I2P se zaganja!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr "Zaganja"
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr "Zaženi I2P brskalnik"
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr "Konfiguriraj I2P opravilno vrstico"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr ""
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr "Ponovno zaženi I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr "Ustavi I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr "Nemudoma ponovno zaženi I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr "Nemudoma ustavi I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr "Prekliči zaustavitev od I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr "Zaustavi v {0}"
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr "Zaustavitev je neizbežna"
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr "Omrežje"
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr "I2P: Pritisnite na desno tipko miške za meni"

View File

@ -4,15 +4,16 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# Besnik <besnik@programeshqip.org>, 2016,2019
# Besnik Bleta <besnik@programeshqip.org>, 2022
# Besnik Bleta <besnik@programeshqip.org>, 2016,2019
# Shpetim <shpetim@privacysolutions.no>, 2014
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2019-01-10 14:28+0000\n"
"Last-Translator: Besnik <besnik@programeshqip.org>\n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2022-02-10 10:34+0000\n"
"Last-Translator: Besnik Bleta <besnik@programeshqip.org>\n"
"Language-Team: Albanian (http://www.transifex.com/otf/I2P/language/sq/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -27,69 +28,81 @@ msgstr "Nise I2P-në"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr "I2P po niset!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr "Po niset"
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr "Nis Shfletuesin I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr "Formësoni Panel Sistemi I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
msgid "Disable"
msgstr "Çaktivizoje"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr "Aktivizoni njoftimet"
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr "Çaktivizoni njoftimet"
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr "Çaktivizo panel sistemi"
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr "Rinise I2P-në"
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr "Ndale I2P-në"
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr "Rinise I2P-në Menjëherë"
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr "Ndale I2P-në Menjëherë"
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr "Anuloje Mbylljen e I2P-së"
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr "Mbylle për {0}"
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr "Mbyllje shumë shpejt"
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr "Rrjet"
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:63
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr "I2P: Djathtasklikoni për menu"

View File

@ -14,7 +14,7 @@ msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2021-05-28 01:02+0000\n"
"PO-Revision-Date: 2021-10-02 16:29+0000\n"
"Last-Translator: Jonatan Nyberg <jonatan@autistici.org>\n"
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
"MIME-Version: 1.0\n"
@ -51,7 +51,7 @@ msgstr "Konfigurera I2P-meddelandefältet"
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
msgid "Disable"
msgstr "Avaktivera"
msgstr "Inaktivera"
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
#: src/net/i2p/desktopgui/InternalTrayManager.java:245

View File

@ -4,20 +4,20 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# Kaya Zeren <kayazeren@gmail.com>, 2013,2016
# Kaya Zeren <kayazeren@gmail.com>, 2013,2016,2022
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2022-02-10 04:40+0000\n"
"Last-Translator: Kaya Zeren <kayazeren@gmail.com>\n"
"Language-Team: Turkish (Turkey) (http://www.transifex.com/otf/I2P/language/tr_TR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tr_TR\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
@ -26,69 +26,81 @@ msgstr "I2P başlasın"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr "I2P başlatılıyor!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr "Başlatılıyor"
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr "I2P Tarayıcısını ın"
msgstr "I2P tarayıcısını "
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr "I2P Sistem Tepsisi Ayarları"
msgstr "I2P sistem tepsisi ayarları"
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
msgid "Disable"
msgstr "Devre Dışı"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr "Bildirimleri etkinleştir"
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr "Bildirimleri devre dışı bırak"
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr "Sistem tepsisini devre dışı bırak"
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr "I2P Yeniden Başlasın"
msgstr "I2P yeniden başlasın"
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr "I2P Durdurulsun"
msgstr "I2P durdurulsun"
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr "I2P Hemen Yeniden Başlatılsın"
msgstr "I2P hemen yeniden başlatılsın"
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr "I2P Hemen Durdurulsun"
msgstr "I2P hemen durdurulsun"
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr "I2P Kapatmayı İptal Et"
msgstr "I2P kapatmayı iptal et"
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr "{0} içinde kapat"
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr "Kapatılmak üzere"
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr "Ağ"
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:63
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr "I2P: Menüde sağ tık"

View File

@ -5,15 +5,15 @@
#
# Translators:
# ducki2p <ducki2p@gmail.com>, 2011
# Scott Rhodes <starring169@gmail.com>, 2021
# Scott Rhodes <starring169@gmail.com>, 2021-2022
# walking <walking@i2pmail.org>, 2011
# YFdyh000 <yfdyh000@gmail.com>, 2016
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
"PO-Revision-Date: 2021-03-07 07:58+0000\n"
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
"PO-Revision-Date: 2022-02-16 16:05+0000\n"
"Last-Translator: Scott Rhodes <starring169@gmail.com>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/otf/I2P/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
@ -29,69 +29,81 @@ msgstr "启动 I2P"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "I2P is starting!"
msgstr " I2P 正在启动!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
msgid "Starting"
msgstr "正在启动"
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
msgid "Launch I2P Browser"
msgstr "启动 I2P 浏览器"
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
msgid "Configure I2P System Tray"
msgstr "配置 I2P 系统托盘"
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
msgid "Disable"
msgstr "禁用"
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
msgid "Enable notifications"
msgstr "启用通知"
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
msgid "Disable notifications"
msgstr "禁用通知"
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
msgid "Disable system tray"
msgstr "禁用系统托盘"
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
msgid "Restart I2P"
msgstr "重启 I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
msgid "Stop I2P"
msgstr "停止 I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
msgid "Restart I2P Immediately"
msgstr "立即重启 I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
msgid "Stop I2P Immediately"
msgstr "立即停止 I2P"
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
msgid "Cancel I2P Shutdown"
msgstr "取消 I2P 关闭"
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
#, java-format
msgid "Shutdown in {0}"
msgstr "{0} 后关闭"
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
msgid "Shutdown imminent"
msgstr "立即关闭"
#. status translations are in the console bundle
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
msgid "Network"
msgstr "网络"
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
#: src/net/i2p/desktopgui/TrayManager.java:63
#: src/net/i2p/desktopgui/TrayManager.java:73
msgid "I2P: Right-click for menu"
msgstr "I2P右击获得菜单"

View File

@ -1,5 +1,6 @@
package net.i2p.desktopgui;
import java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Desktop.Action;
import java.awt.MenuItem;
@ -30,9 +31,11 @@ class InternalTrayManager extends TrayManager {
private final RouterContext _context;
private final Log log;
private MenuItem _statusItem, _browserItem, _configItem, _restartItem, _stopItem,
_restartHardItem, _stopHardItem, _cancelItem;
_restartHardItem, _stopHardItem, _cancelItem,
_notificationItem1, _notificationItem2;
private JMenuItem _jstatusItem, _jbrowserItem, _jconfigItem, _jrestartItem, _jstopItem,
_jrestartHardItem, _jstopHardItem, _jcancelItem;
_jrestartHardItem, _jstopHardItem, _jcancelItem,
_jnotificationItem1, _jnotificationItem2;
private static final boolean CONSOLE_ENABLED = Desktop.isDesktopSupported() &&
Desktop.getDesktop().isSupported(Action.BROWSE);
@ -44,6 +47,14 @@ class InternalTrayManager extends TrayManager {
log = ctx.logManager().getLog(InternalTrayManager.class);
}
/**
* @since 0.9.53
*/
public void startManager() throws AWTException {
super.startManager();
displayMessage(Log.INFO, _t("Starting"), _t("I2P is starting!"), null);
}
public synchronized PopupMenu getMainMenu() {
PopupMenu popup = new PopupMenu();
@ -73,7 +84,35 @@ class InternalTrayManager extends TrayManager {
}
PopupMenu desktopguiConfigurationLauncher = new PopupMenu(_t("Configure I2P System Tray"));
MenuItem configSubmenu = new MenuItem(_t("Disable"));
final MenuItem notificationItem2 = new MenuItem(_t("Enable notifications"));
notificationItem2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
configureNotifications(true);
return null;
}
}.execute();
}
});
final MenuItem notificationItem1 = new MenuItem(_t("Disable notifications"));
notificationItem1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
configureNotifications(false);
return null;
}
}.execute();
}
});
MenuItem configSubmenu = new MenuItem(_t("Disable system tray"));
configSubmenu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
@ -173,6 +212,8 @@ class InternalTrayManager extends TrayManager {
popup.add(browserLauncher);
popup.addSeparator();
}
desktopguiConfigurationLauncher.add(notificationItem2);
desktopguiConfigurationLauncher.add(notificationItem1);
desktopguiConfigurationLauncher.add(configSubmenu);
popup.add(desktopguiConfigurationLauncher);
popup.addSeparator();
@ -187,6 +228,8 @@ class InternalTrayManager extends TrayManager {
_statusItem = statusItem;
_browserItem = browserLauncher;
_configItem = desktopguiConfigurationLauncher;
_notificationItem1 = notificationItem1;
_notificationItem2 = notificationItem2;
_restartItem = restartItem;
_stopItem = stopItem;
_restartHardItem = restartItem2;
@ -225,7 +268,35 @@ class InternalTrayManager extends TrayManager {
}
JMenu desktopguiConfigurationLauncher = new JMenu(_t("Configure I2P System Tray"));
JMenuItem configSubmenu = new JMenuItem(_t("Disable"));
final JMenuItem notificationItem2 = new JMenuItem(_t("Enable notifications"));
notificationItem2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
configureNotifications(true);
return null;
}
}.execute();
}
});
final JMenuItem notificationItem1 = new JMenuItem(_t("Disable notifications"));
notificationItem1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
configureNotifications(false);
return null;
}
}.execute();
}
});
JMenuItem configSubmenu = new JMenuItem(_t("Disable system tray"));
configSubmenu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
@ -325,6 +396,8 @@ class InternalTrayManager extends TrayManager {
popup.add(browserLauncher);
popup.addSeparator();
}
desktopguiConfigurationLauncher.add(notificationItem2);
desktopguiConfigurationLauncher.add(notificationItem1);
desktopguiConfigurationLauncher.add(configSubmenu);
popup.add(desktopguiConfigurationLauncher);
popup.addSeparator();
@ -339,6 +412,8 @@ class InternalTrayManager extends TrayManager {
_jstatusItem = statusItem;
_jbrowserItem = browserLauncher;
_jconfigItem = desktopguiConfigurationLauncher;
_jnotificationItem1 = notificationItem1;
_jnotificationItem2 = notificationItem2;
_jrestartItem = restartItem;
_jstopItem = stopItem;
_jrestartHardItem = restartItem2;
@ -390,6 +465,10 @@ class InternalTrayManager extends TrayManager {
_stopHardItem.setEnabled(!imminent);
if (_cancelItem != null)
_cancelItem.setEnabled(x && !imminent);
if (_notificationItem1 != null)
_notificationItem1.setEnabled(_showNotifications);
if (_notificationItem2 != null)
_notificationItem2.setEnabled(!_showNotifications);
if (_jstatusItem != null)
_jstatusItem.setText(status);
@ -407,6 +486,10 @@ class InternalTrayManager extends TrayManager {
_jstopHardItem.setVisible(!imminent);
if (_jcancelItem != null)
_jcancelItem.setVisible(x && !imminent);
if (_jnotificationItem1 != null)
_jnotificationItem1.setVisible(_showNotifications);
if (_jnotificationItem2 != null)
_jnotificationItem2.setVisible(!_showNotifications);
}
/**
@ -415,18 +498,24 @@ class InternalTrayManager extends TrayManager {
private void configureDesktopgui(boolean enable) {
String property = Main.PROP_ENABLE;
String value = Boolean.toString(enable);
try {
_context.router().saveConfig(property, value);
if (!enable) {
// TODO popup that explains how to re-enable in console
_main.shutdown(null);
}
} catch (Exception ex) {
log.error("Error saving config", ex);
if (!_context.router().saveConfig(property, value))
log.error("Error saving config");
if (!enable) {
// TODO popup that explains how to re-enable in console
_main.shutdown(null);
}
}
/**
* @since 0.9.53
*/
private void configureNotifications(boolean enable) {
_showNotifications = enable;
String value = Boolean.toString(enable);
if (!_context.router().saveConfig(PROP_NOTIFICATIONS, value))
log.error("Error saving config");
}
/**
* Build the console URL with info from the port mapper,
* and launch the browser at it.

View File

@ -5,6 +5,7 @@ package net.i2p.desktopgui;
*/
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.io.File;
import java.lang.reflect.Method;
@ -16,6 +17,7 @@ import net.i2p.I2PAppContext;
import net.i2p.app.ClientAppManager;
import net.i2p.app.ClientAppState;
import static net.i2p.app.ClientAppState.*;
import net.i2p.app.NotificationService;
import net.i2p.desktopgui.router.RouterManager;
import net.i2p.router.RouterContext;
import net.i2p.router.app.RouterApp;
@ -27,7 +29,7 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback;
/**
* The main class of the application.
*/
public class Main implements RouterApp {
public class Main implements RouterApp, NotificationService {
// non-null
private final I2PAppContext _appContext;
@ -95,6 +97,11 @@ public class Main implements RouterApp {
}
public static void main(String[] args) {
// early check so we can bail out when started via CLI
if (!SystemTray.isSupported()) {
System.err.println("SystemTray not supported");
return;
}
Main main = new Main();
main.beginStartup(args);
}
@ -198,6 +205,46 @@ public class Main implements RouterApp {
t.start();
}
/////// NotificationService methods
/**
* Send a notification to the user.
*
* @param source unsupported
* @param category unsupported
* @param priority unsupported
* @param title for the popup, translated
* @param message translated
* @param path unsupported
* @return 0, or -1 on failure
*/
public int notify(String source, String category, int priority, String title, String message, String path) {
TrayManager tm = _trayManager;
if (tm == null)
return -1;
return tm.displayMessage(priority, title, message, path);
}
/**
* Cancel a notification if possible.
* Unsupported.
*
* @return false always
*/
public boolean cancel(int id) {
return false;
}
/**
* Update the text of a notification if possible.
* Unsupported.
*
* @return false always
*/
public boolean update(int id, String title, String message, String path) {
return false;
}
/////// ClientApp methods
/** @since 0.9.26 */

View File

@ -8,21 +8,27 @@ import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.SwingWorker;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import net.i2p.I2PAppContext;
import net.i2p.apps.systray.UrlLauncher;
import net.i2p.desktopgui.i18n.DesktopguiTranslator;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
/**
@ -37,11 +43,14 @@ abstract class TrayManager {
protected SystemTray tray;
///Our tray icon, or null if unsupported
protected TrayIcon trayIcon;
protected volatile boolean _showNotifications;
private static final String PNG_DIR = "/desktopgui/resources/images/";
private static final String MAC_ICON = "itoopie_black_24.png";
private static final String WIN_ICON = "itoopie_white_24.png";
private static final String WIN_ICON_LIGHT = "itoopie_white_24.png";
private static final String WIN_ICON_DARK = "itoopie_black_24.png";
private static final String LIN_ICON = "logo.png";
protected static final String PROP_NOTIFICATIONS = "desktopgui.showNotifications";
/**
* Instantiate tray manager.
@ -58,6 +67,7 @@ abstract class TrayManager {
public synchronized void startManager() throws AWTException {
if (!SystemTray.isSupported())
throw new AWTException("SystemTray not supported");
_showNotifications = _appContext.getBooleanPropertyDefaultTrue(PROP_NOTIFICATIONS);
tray = SystemTray.getSystemTray();
// Windows typically has tooltips; Linux (at least Ubuntu) doesn't
String tooltip = SystemVersion.isWindows() ? _t("I2P: Right-click for menu") : null;
@ -191,12 +201,20 @@ abstract class TrayManager {
*/
private Image getTrayImage() throws AWTException {
String img;
if (SystemVersion.isWindows())
img = WIN_ICON;
else if (SystemVersion.isMac())
if (SystemVersion.isWindows()) {
// too hard to get the theme out of the registry,
// use our console theme as a best guess
// so we have a contrasting icon
String theme = _appContext.getProperty("routerconsole.theme", "light");
if (theme.equals("dark"))
img = WIN_ICON_LIGHT;
else
img = WIN_ICON_DARK;
} else if (SystemVersion.isMac()) {
img = MAC_ICON;
else
} else {
img = LIN_ICON;
}
URL url = getClass().getResource(PNG_DIR + img);
if (url == null)
throw new AWTException("cannot load tray image " + img);
@ -204,6 +222,74 @@ abstract class TrayManager {
return image;
}
/**
* Send a notification to the user.
*
* @param title for the popup, translated
* @param message translated
* @param path unsupported
* @return 0, or -1 on failure
*/
public int displayMessage(int priority, String title, String message, String path) {
if (!_showNotifications)
return -1;
final TrayIcon ti = trayIcon;
if (ti == null)
return -1;
TrayIcon.MessageType type;
if (priority <= Log.DEBUG)
type = TrayIcon.MessageType.NONE;
else if (priority <= Log.INFO)
type = TrayIcon.MessageType.INFO;
else if (priority <= Log.WARN)
type = TrayIcon.MessageType.WARNING;
else
type = TrayIcon.MessageType.ERROR;
ti.displayMessage(title, message, type);
/*
* There's apparently no way to bind a particular message to an action
that comes back. We can't keep a queue because we don't get
an action back when the message is removed via timeout or user x-out.
On OSX, new messages dismiss previous ones.
On LXDE (and Gnome?), new messages go under previous ones. Timeout is only 10 seconds.
Message timeout is platform-dependent.
So the order of events is unknowable.
This only works if there is only one message ever.
if (path != null && path.length() > 0) {
if (path.charAt(0) == '/');
path = path.substring(1);
final String url = _appContext.portMapper().getConsoleURL() + path;
ti.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
ti.removeActionListener(this);
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
System.out.println("DIB " + arg0);
UrlLauncher launcher = new UrlLauncher(_appContext, null, null);
try {
launcher.openUrl(url);
System.out.println("DIB success " + url);
} catch (IOException e1) {
System.out.println("DIB fail " + url);
}
return null;
}
@Override
protected void done() {
System.out.println("done " + arg0);
}
}.execute();
}
});
}
*/
return 0;
}
protected String _t(String s) {
return DesktopguiTranslator._t(_appContext, s);
}

View File

@ -156,11 +156,11 @@ public class JSONRPC2Servlet extends HttpServlet {
} else {
out.println("<p>Current API password:<input name=\"password\" type=\"password\">");
}
out.println("<p>New API password (twice):<input name=\"password2\" type=\"password\">" +
"<input name=\"password3\" type=\"password\">" +
out.println("<p>New API password (twice): <input name=\"password2\" type=\"password\"> " +
"<input name=\"password3\" type=\"password\"> " +
"<input name=\"save\" type=\"submit\" value=\"Change API Password\">" +
"<p>If you forget the API password, stop i2pcontrol, delete the file <tt>" + _conf.getConfFile() +
"</tt>, and restart i2pcontrol.");
"<p>If you forget the API password, <a href=\"/configwebapps\">stop jsonrpc</a>, delete the file <tt>" + _conf.getConfFile() +
"</tt>, and <a href=\"/configwebapps\">restart jsonrpc</a>.");
out.println("</form>");
} else {
out.println("<p><a href=\"password\">Change API Password</a>");

View File

@ -96,7 +96,7 @@ public class RouterInfoHandler implements RequestHandler {
}
if (inParams.containsKey("i2p.router.status")) {
outParams.put("i2p.router.status", _context.throttle().getTunnelStatus());
outParams.put("i2p.router.status", _context.throttle().getLocalizedTunnelStatus());
}
if (inParams.containsKey("i2p.router.net.status")) {
@ -179,25 +179,44 @@ public class RouterInfoHandler implements RequestHandler {
int status = _context.commSystem().getStatus().getCode();
switch (status) {
case CommSystemFacade.STATUS_OK:
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP");
case CommSystemFacade.STATUS_OK:
case CommSystemFacade.STATUS_IPV4_OK_IPV6_UNKNOWN:
case CommSystemFacade.STATUS_IPV4_OK_IPV6_FIREWALLED:
case CommSystemFacade.STATUS_IPV4_FIREWALLED_IPV6_OK:
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_OK:
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP2");
if (ra == null || TransportUtil.isPubliclyRoutable(ra.getIP(), true))
return NETWORK_STATUS.OK;
return NETWORK_STATUS.ERROR_PRIVATE_TCP_ADDRESS;
case CommSystemFacade.STATUS_DIFFERENT:
case CommSystemFacade.STATUS_DIFFERENT:
case CommSystemFacade.STATUS_IPV4_SNAT_IPV6_OK:
case CommSystemFacade.STATUS_IPV4_SNAT_IPV6_UNKNOWN:
return NETWORK_STATUS.ERROR_SYMMETRIC_NAT;
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
if (_context.router().getRouterInfo().getTargetAddress("NTCP") != null)
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
case CommSystemFacade.STATUS_IPV4_FIREWALLED_IPV6_UNKNOWN:
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_FIREWALLED:
if (_context.router().getRouterInfo().getTargetAddress("NTCP2") != null)
return NETWORK_STATUS.WARN_FIREWALLED_WITH_INBOUND_TCP;
if (((FloodfillNetworkDatabaseFacade) _context.netDb()).floodfillEnabled())
return NETWORK_STATUS.WARN_FIREWALLED_AND_FLOODFILL;
if (_context.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
return NETWORK_STATUS.WARN_FIREWALLED_AND_FAST;
return NETWORK_STATUS.FIREWALLED;
case CommSystemFacade.STATUS_HOSED:
case CommSystemFacade.STATUS_HOSED:
return NETWORK_STATUS.ERROR_UDP_PORT_IN_USE;
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
default:
case CommSystemFacade.STATUS_DISCONNECTED:
return NETWORK_STATUS.ERROR_NO_ACTIVE_PEERS_CHECK_CONNECTION_AND_FIREWALL;
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
case CommSystemFacade.STATUS_IPV4_UNKNOWN_IPV6_OK:
case CommSystemFacade.STATUS_IPV4_UNKNOWN_IPV6_FIREWALLED:
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_UNKNOWN:
default:
ra = _context.router().getRouterInfo().getTargetAddress("SSU");
if (ra == null && _context.router().getUptime() > 5 * 60 * 1000) {
if (_context.commSystem().countActivePeers() <= 0)

View File

@ -2,3 +2,7 @@
# This is for app context configuration of standalone i2psnark.
# Almost all configuration settings are in i2psnark.config.d/i2psnark.config
#
# disable browser launch on startup
#routerconsole.browser=/bin/false
# change browser
#routerconsole.browser=firefox

View File

@ -197,9 +197,16 @@
</target>
<target name="standalone" depends="standalone_prep">
<!-- doesn't support file permissions
<zip destfile="i2psnark-standalone.zip">
<zipfileset dir="./dist/" prefix="i2psnark/" />
<zipfileset dir="./i2psnark/" />
</zip>
-->
<exec executable="zip" failifexecutionfails="true" failonerror="true" >
<arg value="-r" />
<arg value="i2psnark-standalone.zip" />
<arg value="i2psnark" />
</exec>
</target>
<!-- make a fat jar for standalone -->
@ -228,6 +235,7 @@
<zipfileset src="../../ministreaming/java/build/mstreaming.jar" />
<zipfileset src="../../streaming/java/build/streaming.jar" />
<zipfileset src="../../systray/java/build/systray.jar" />
<zipfileset src="../../../build/jbigi.jar" />
<!-- Countries translations. The i2psnark translations are in the war but it's easier to put these here -->
<!-- 300KB just to translate "Brazil", but why not... -->
<!--
@ -306,33 +314,34 @@
</target>
<target name="standalone_prep" depends="standalone_jar, standalone_war">
<delete dir="./dist" />
<mkdir dir="./dist" />
<copy file="../launch-i2psnark" todir="./dist/" />
<copy file="../launch-i2psnark.bat" todir="./dist/" />
<mkdir dir="./dist/contexts" />
<copy file="../standalone-context.xml" tofile="./dist/contexts/context.xml" />
<mkdir dir="./dist/docroot" />
<copy file="../standalone-index.html" tofile="./dist/docroot/index.html" />
<mkdir dir="./dist/webapps" />
<copy file="../i2psnark.war" tofile="./dist/webapps/i2psnark.war" />
<copy file="../jetty-i2psnark.xml" tofile="./dist/jetty-i2psnark.xml" />
<copy file="../i2psnark-appctx.config" tofile="./dist/i2psnark-appctx.config" />
<copy file="./build/i2psnark-standalone.jar" tofile="./dist/i2psnark.jar" />
<copy file="../readme-standalone.txt" tofile="./dist/readme.txt" />
<delete dir="./i2psnark" />
<mkdir dir="./i2psnark" />
<copy file="../launch-i2psnark" todir="./i2psnark/" />
<chmod type="file" file="./i2psnark/launch-i2psnark" perm="+x" />
<copy file="../launch-i2psnark.bat" todir="./i2psnark/" />
<mkdir dir="./i2psnark/contexts" />
<copy file="../standalone-context.xml" tofile="./i2psnark/contexts/context.xml" />
<mkdir dir="./i2psnark/docroot" />
<copy file="../standalone-index.html" tofile="./i2psnark/docroot/index.html" />
<mkdir dir="./i2psnark/webapps" />
<copy file="../i2psnark.war" tofile="./i2psnark/webapps/i2psnark.war" />
<copy file="../jetty-i2psnark.xml" tofile="./i2psnark/jetty-i2psnark.xml" />
<copy file="../i2psnark-appctx.config" tofile="./i2psnark/i2psnark-appctx.config" />
<copy file="./build/i2psnark-standalone.jar" tofile="./i2psnark/i2psnark.jar" />
<copy file="../readme-standalone.txt" tofile="./i2psnark/readme.txt" />
<!-- temp so announces work -->
<copy file="../../../installer/resources/hosts.txt" tofile="./dist/hosts.txt" />
<copy todir="./dist/licenses" >
<copy file="../../../installer/resources/hosts.txt" tofile="./i2psnark/hosts.txt" />
<copy todir="./i2psnark/licenses" >
<fileset dir="../../../licenses" includes="LICENSE-GPLv2.txt, ABOUT-Jetty.html" />
</copy>
<mkdir dir="./dist/logs" />
<mkdir dir="./i2psnark/logs" />
</target>
<target name="clean">
<delete dir="./build" />
<delete file="../i2psnark.war" />
<delete file="./i2psnark-standalone.zip" />
<delete dir="./dist" />
<delete dir="./i2psnark" />
</target>
<target name="cleandep" depends="clean">
</target>

View File

@ -23,6 +23,7 @@ import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketEepGet;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Base32;
@ -47,7 +48,7 @@ import org.klomp.snark.dht.KRPC;
* so we can run multiple instances of single Snarks
* (but not multiple SnarkManagers, it is still static)
*/
public class I2PSnarkUtil {
public class I2PSnarkUtil implements DisconnectListener {
private final I2PAppContext _context;
private final Log _log;
private final String _baseName;
@ -75,6 +76,7 @@ public class I2PSnarkUtil {
private List<String> _openTrackers;
private DHT _dht;
private long _startedTime;
private final DisconnectListener _discon;
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
@ -91,17 +93,18 @@ public class I2PSnarkUtil {
public I2PSnarkUtil(I2PAppContext ctx) {
this(ctx, "i2psnark");
this(ctx, "i2psnark", null);
}
/**
* @param baseName generally "i2psnark"
* @since Jetty 7
*/
public I2PSnarkUtil(I2PAppContext ctx, String baseName) {
public I2PSnarkUtil(I2PAppContext ctx, String baseName, DisconnectListener discon) {
_context = ctx;
_log = _context.logManager().getLog(Snark.class);
_baseName = baseName;
_discon = discon;
_opts = new HashMap<String, String>();
//setProxy("127.0.0.1", 4444);
setI2CPConfig("127.0.0.1", I2PClient.DEFAULT_LISTEN_PORT, null);
@ -324,8 +327,11 @@ public class I2PSnarkUtil {
if (opts.getProperty(I2PClient.PROP_GZIP) == null)
opts.setProperty(I2PClient.PROP_GZIP, "false");
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
if (_manager != null)
if (_manager != null) {
_startedTime = _context.clock().now();
if (_discon != null)
_manager.addDisconnectListener(this);
}
_connecting = false;
}
if (_shouldUseDHT && _manager != null && _dht == null)
@ -333,6 +339,19 @@ public class I2PSnarkUtil {
return (_manager != null);
}
/**
* DisconnectListener interface
* @since 0.9.53
*/
public void sessionDisconnected() {
synchronized(this) {
_manager = null;
_connecting = false;
}
if (_discon != null)
_discon.sessionDisconnected();
}
/**
* @return null if disabled or not started
* @since 0.8.4

View File

@ -82,8 +82,9 @@ public class MetaInfo
* @param created_by may be null
* @param url_list may be null
* @param comment may be null
* @since public since 0.9.53, was package private
*/
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
public MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
int piece_length, byte[] piece_hashes, long length, boolean privateTorrent,
List<List<String>> announce_list, String created_by, List<String> url_list, String comment)
{
@ -442,9 +443,12 @@ public class MetaInfo
}
/**
* Returns the piece hashes. Only used by storage so package local.
* Returns the piece hashes.
*
* @return not a copy, do not modify
* @since public since 0.9.53, was package private
*/
byte[] getPieceHashes()
public byte[] getPieceHashes()
{
return piece_hashes;
}

View File

@ -590,8 +590,12 @@ public class Snark
private void x_startTorrent() {
boolean ok = _util.connect();
if (!ok)
fatalRouter("Unable to connect to I2P", null);
if (!ok) {
if (_util.getContext().isRouterContext())
fatalRouter(_util.getString("Unable to connect to I2P"), null);
else
fatalRouter(_util.getString("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort(), null);
}
if (coordinator == null) {
I2PServerSocket serversocket = _util.getServerSocket();
if (serversocket == null)
@ -1255,7 +1259,7 @@ public class Snark
*/
private void fatalRouter(String s, Throwable t) throws RouterException {
_log.error(s, t);
stopTorrent();
stopTorrent(true);
if (completeListener != null)
completeListener.fatal(this, s);
throw new RouterException(s, t);
@ -1321,6 +1325,15 @@ public class Snark
}
}
/**
* Call after editing torrent.
* Caller must ensure infohash, files, etc. did not change.
*
* @since 0.9.53
*/
public void replaceMetaInfo(MetaInfo metainfo) {
meta = metainfo;
}
///////////// Begin StorageListener methods

View File

@ -29,7 +29,9 @@ import net.i2p.I2PAppContext;
import net.i2p.app.ClientApp;
import net.i2p.app.ClientAppManager;
import net.i2p.app.ClientAppState;
import net.i2p.app.NotificationService;
import net.i2p.client.I2PClient;
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener;
import net.i2p.crypto.SHA1Hash;
import net.i2p.crypto.SigType;
import net.i2p.data.Base64;
@ -57,7 +59,7 @@ import org.klomp.snark.dht.KRPC;
/**
* Manage multiple snarks
*/
public class SnarkManager implements CompleteListener, ClientApp {
public class SnarkManager implements CompleteListener, ClientApp, DisconnectListener {
/**
* Map of (canonical) filename of the .torrent file to Snark instance.
@ -130,7 +132,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
public static final String PROP_OLD_AUTO_START = "i2snark.autoStart"; // oops
public static final String PROP_AUTO_START = "i2psnark.autoStart"; // convert in migration to new config file
public static final String DEFAULT_AUTO_START = "false";
private final boolean DEFAULT_AUTO_START;
//public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
//public static final String DEFAULT_LINK_PREFIX = "file:///";
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
@ -140,7 +142,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
public static final String PROP_THEME = "i2psnark.theme";
public static final String DEFAULT_THEME = "ubergine";
private static final String[] THEMES = new String[] { "dark", "light", "ubergine", "vanilla" };
// Translators: Translate "ubergine" as "aubergine" or "eggplant" or "purple"
private static final String[] THEMES = new String[] { _x("ubergine"), _x("dark"), _x("light"), _x("vanilla") };
/** From CSSHelper */
private static final String PROP_DISABLE_OLD = "routerconsole.disableOldThemes";
private static final boolean DEFAULT_DISABLE_OLD = true;
@ -267,7 +270,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
_contextName = ctxName;
_log = _context.logManager().getLog(SnarkManager.class);
_messages = new UIMessages(MAX_MESSAGES);
_util = new I2PSnarkUtil(_context, ctxName);
_util = new I2PSnarkUtil(_context, ctxName, this);
DEFAULT_AUTO_START = !ctx.isRouterContext();
String cfile = ctxName + CONFIG_FILE_SUFFIX;
File configFile = new File(cfile);
if (!configFile.isAbsolute())
@ -342,6 +346,18 @@ public class SnarkManager implements CompleteListener, ClientApp {
}
}
/**
* DisconnectListener interface
* @since 0.9.53
*/
public void sessionDisconnected() {
if (!_context.isRouterContext()) {
addMessage(_t("Unable to connect to I2P"));
stopAllTorrents(true);
_stopping = false;
}
}
/*
* Called by the webapp at Jetty shutdown.
* Stops all torrents. Does not close the tunnel, so the announces have a chance.
@ -463,7 +479,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
}
public boolean shouldAutoStart() {
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START));
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, Boolean.toString(DEFAULT_AUTO_START)));
}
/**
@ -814,7 +830,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
if (!_config.containsKey(PROP_DIR))
_config.setProperty(PROP_DIR, _contextName);
if (!_config.containsKey(PROP_AUTO_START))
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
_config.setProperty(PROP_AUTO_START, Boolean.toString(DEFAULT_AUTO_START));
if (!_config.containsKey(PROP_REFRESH_DELAY))
_config.setProperty(PROP_REFRESH_DELAY, Integer.toString(DEFAULT_REFRESH_DELAY_SECS));
if (!_config.containsKey(PROP_STARTUP_DELAY))
@ -847,7 +863,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
/**
* Get current theme.
* @return String -- the current theme
* @return String -- the current theme, untranslated
*/
public String getTheme() {
String theme;
@ -912,20 +928,29 @@ public class SnarkManager implements CompleteListener, ClientApp {
/**
* Get all themes
* @return String[] -- Array of all the themes found, non-null, unsorted
* @return Array of all the themes found, non-null, unsorted, untranslated. Not a copy, do not modify.
*/
public static String[] getThemes() {
return THEMES;
}
/** call from DirMonitor since loadConfig() is called before router I2CP is up */
private void getBWLimit() {
if (!_config.containsKey(PROP_UPBW_MAX)) {
/**
* Call from DirMonitor since loadConfig() is called before router I2CP is up.
* We also use this as a test that the router is there for standalone.
*
* @return true if we got a response from the router
*/
private boolean getBWLimit() {
boolean shouldSet = !_config.containsKey(PROP_UPBW_MAX);
if (shouldSet || !_context.isRouterContext()) {
int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort());
if (limits != null && limits[1] > 0)
if (limits == null)
return false;
if (shouldSet && limits[1] > 0)
_util.setMaxUpBW(limits[1]);
}
return true;
}
private void updateConfig() {
@ -1576,7 +1601,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
}
if (dataDir == null)
dataDir = getDataDir();
Snark torrent = null;
Snark torrent;
synchronized (_snarks) {
torrent = _snarks.get(filename);
}
@ -1696,7 +1721,10 @@ public class SnarkManager implements CompleteListener, ClientApp {
addMessage(_t("Connecting to I2P"));
boolean ok = _util.connect();
if (!ok) {
addMessage(_t("Error connecting to I2P - check your I2CP settings!"));
if (_context.isRouterContext())
addMessage(_t("Unable to connect to I2P"));
else
addMessage(_t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
// this would rename the torrent to .BAD
//return false;
}
@ -2492,6 +2520,13 @@ public class SnarkManager implements CompleteListener, ClientApp {
addMessage(_t("Torrent removed: \"{0}\"", torrent.getBaseName()));
}
/**
* This calls monitorTorrents() once a minute.
* It also gets the bandwidth limits and loads magnets on first run.
* For standalone, it also handles checking that the external router is there,
* and restarting torrents once the router appears.
*
*/
private class DirMonitor implements Runnable {
public void run() {
// don't bother delaying if auto start is false
@ -2506,17 +2541,65 @@ public class SnarkManager implements CompleteListener, ClientApp {
// here because we need to delay until I2CP is up
// although the user will see the default until then
getBWLimit();
boolean routerOK = false;
boolean doMagnets = true;
while (_running) {
File dir = getDataDir();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Directory Monitor loop over " + dir.getAbsolutePath());
if (routerOK &&
(_context.isRouterContext() || _util.connected() || _util.isConnecting())) {
autostart = shouldAutoStart();
} else {
// Test if the router is there
// For standalone, this will probe the router every 60 seconds if not connected
boolean oldOK = routerOK;
routerOK = getBWLimit();
if (routerOK) {
autostart = shouldAutoStart();
if (autostart && !oldOK && !doMagnets && !_snarks.isEmpty()) {
// Start previously added torrents
for (Snark snark : _snarks.values()) {
Properties config = getConfig(snark);
String prop = config.getProperty(PROP_META_RUNNING);
if (prop == null || Boolean.parseBoolean(prop)) {
if (!_util.connected()) {
addMessage(_t("Connecting to I2P"));
// getBWLimit() was successful so this should work
boolean ok = _util.connect();
if (!ok) {
if (_context.isRouterContext())
addMessage(_t("Unable to connect to I2P"));
else
addMessage(_t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
routerOK = false;
autostart = false;
break;
}
}
addMessageNoEscape(_t("Starting up torrent {0}", linkify(snark)));
try {
snark.startTorrent();
} catch (Snark.RouterException re) {
// Snark.fatal() will log and call fatal() here for user message before throwing
break;
} catch (RuntimeException re) {
// Snark.fatal() will log and call fatal() here for user message before throwing
}
}
}
if (routerOK)
addMessage(_t("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
}
} else {
autostart = false;
}
}
boolean ok;
try {
// Don't let this interfere with .torrent files being added or deleted
synchronized (_snarks) {
ok = monitorTorrents(dir);
ok = monitorTorrents(dir, autostart);
}
} catch (RuntimeException e) {
_log.error("Error in the DirectoryMonitor", e);
@ -2530,7 +2613,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
} catch (RuntimeException e) {
_log.error("Error in the DirectoryMonitor", e);
}
if (!_snarks.isEmpty())
if (routerOK && !_snarks.isEmpty())
addMessage(_t("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
// To fix bug where files were left behind,
// but also good for when user removes snarks when i2p is not running
@ -2539,6 +2622,12 @@ public class SnarkManager implements CompleteListener, ClientApp {
// time i2psnark starts. See ticket #1658.
if (ok)
cleanupTorrentStatus();
if (!routerOK) {
if (_context.isRouterContext())
addMessage(_t("Unable to connect to I2P"));
else
addMessage(_t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
}
}
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
}
@ -2555,8 +2644,18 @@ public class SnarkManager implements CompleteListener, ClientApp {
Storage storage = snark.getStorage();
if (meta == null || storage == null)
return;
if (snark.getDownloaded() > 0)
if (snark.getDownloaded() > 0) {
addMessageNoEscape(_t("Download finished: {0}", linkify(snark)));
ClientAppManager cmgr = _context.clientAppManager();
if (cmgr != null) {
NotificationService ns = (NotificationService) cmgr.getRegisteredApp("desktopgui");
if (ns != null) {
ns.notify("I2PSnark", null, Log.INFO, _t("I2PSnark"),
_t("Download finished: {0}", snark.getName()),
"/i2psnark/" + linkify(snark));
}
}
}
updateStatus(snark);
}
@ -2704,9 +2803,10 @@ public class SnarkManager implements CompleteListener, ClientApp {
/**
* caller must synchronize on _snarks
*
* @param shouldStart should we autostart the torrents
* @return success, false if an error adding any torrent.
*/
private boolean monitorTorrents(File dir) {
private boolean monitorTorrents(File dir, boolean shouldStart) {
boolean rv = true;
File files[] = dir.listFiles(new FileSuffixFilter(".torrent"));
List<String> foundNames = new ArrayList<String>(0);
@ -2726,7 +2826,6 @@ public class SnarkManager implements CompleteListener, ClientApp {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("DirMon found: " + DataHelper.toString(foundNames) + " existing: " + DataHelper.toString(existingNames));
// lets find new ones first...
boolean shouldStart = shouldAutoStart();
for (String name : foundNames) {
if (existingNames.contains(name)) {
// already known. noop
@ -2791,6 +2890,14 @@ public class SnarkManager implements CompleteListener, ClientApp {
return _util.getString(s, o, o2);
}
/**
* mark for translation, does not translate
* @since 0.9.53
*/
private static String _x(String s) {
return s;
}
/**
* Unsorted map of name to Tracker object
* Modifiable, not a copy

View File

@ -0,0 +1,753 @@
package org.klomp.snark;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionMuxedListener;
import net.i2p.client.SendMessageOptions;
import net.i2p.client.datagram.I2PDatagramDissector;
import net.i2p.client.datagram.I2PDatagramMaker;
import net.i2p.client.datagram.I2PInvalidDatagramException;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
/**
* One of these for all trackers and info hashes.
* Ref: BEP 15, proposal 160
*
* The main difference from BEP 15 is that the announce response
* contains a 32-byte hash instead of a 4-byte IP and a 2-byte port.
*
* This implements only "fast mode".
* We send only repliable datagrams, and
* receive only raw datagrams, as follows:
*
*<pre>
* client tracker type
* ------ ------- ----
* announce --&gt; repliable
* &lt;-- ann resp raw
*</pre>
*
* @since 0.9.53, enabled in 0.9.54
*/
class UDPTrackerClient implements I2PSessionMuxedListener {
private final I2PAppContext _context;
private final Log _log;
/** hook to inject and receive datagrams */
private final I2PSession _session;
private final I2PSnarkUtil _util;
private final Hash _myHash;
/** unsigned dgrams */
private final int _rPort;
/** dest and port to tracker data */
private final ConcurrentHashMap<HostPort, Tracker> _trackers;
/** our TID to tracker */
private final Map<Integer, ReplyWaiter> _sentQueries;
private boolean _isRunning;
public static final int EVENT_NONE = 0;
public static final int EVENT_COMPLETED = 1;
public static final int EVENT_STARTED = 2;
public static final int EVENT_STOPPED = 3;
private static final int ACTION_CONNECT = 0;
private static final int ACTION_ANNOUNCE = 1;
private static final int ACTION_SCRAPE = 2;
private static final int ACTION_ERROR = 3;
private static final int SEND_CRYPTO_TAGS = 8;
private static final int LOW_CRYPTO_TAGS = 4;
private static final long DEFAULT_TIMEOUT = 15*1000;
private static final long DEFAULT_QUERY_TIMEOUT = 60*1000;
private static final long CLEAN_TIME = 163*1000;
/** in seconds */
private static final int DEFAULT_INTERVAL = 60*60;
private static final int MIN_INTERVAL = 15*60;
private static final int MAX_INTERVAL = 8*60*60;
private enum WaitState { INIT, SUCCESS, TIMEOUT, FAIL }
/**
*
*/
public UDPTrackerClient(I2PAppContext ctx, I2PSession session, I2PSnarkUtil util) {
_context = ctx;
_session = session;
_util = util;
_log = ctx.logManager().getLog(UDPTrackerClient.class);
_rPort = TrackerClient.PORT - 1;
_myHash = session.getMyDestination().calculateHash();
_trackers = new ConcurrentHashMap<HostPort, Tracker>(8);
_sentQueries = new ConcurrentHashMap<Integer, ReplyWaiter>(32);
}
/**
* Can't be restarted after stopping?
*/
public synchronized void start() {
if (_isRunning)
return;
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM_RAW, _rPort);
_isRunning = true;
}
/**
* Stop everything.
*/
public synchronized void stop() {
if (!_isRunning)
return;
_isRunning = false;
_session.removeListener(I2PSession.PROTO_DATAGRAM_RAW, _rPort);
_trackers.clear();
for (ReplyWaiter w : _sentQueries.values()) {
w.cancel();
}
_sentQueries.clear();
}
/**
* Announce and get peers for a torrent.
* Blocking!
* Caller should run in a thread.
*
* @param ih the Info Hash (torrent)
* @param max maximum number of peers to return
* @param maxWait the maximum time to wait (ms) must be > 0
* @param fast if true, don't wait for dest, no retx, ...
* @return null on fail or if fast is true
*/
public TrackerResponse announce(byte[] ih, byte[] peerID, int max, long maxWait,
String toHost, int toPort,
long downloaded, long left, long uploaded,
int event, boolean fast) {
long now = _context.clock().now();
long end = now + maxWait;
if (toPort < 0)
throw new IllegalArgumentException();
Tracker tr = getTracker(toHost, toPort);
if (tr.getDest(fast) == null) {
if (_log.shouldInfo())
_log.info("cannot resolve " + tr);
return null;
}
long toWait = end - now;
if (!fast)
toWait = toWait * 3 / 4;
if (toWait < 1000) {
if (_log.shouldInfo())
_log.info("out of time after resolving: " + tr);
return null;
}
if (fast) {
toWait = 0;
} else {
toWait = end - now;
if (toWait < 1000) {
if (_log.shouldInfo())
_log.info("out of time after getting conn: " + tr);
return null;
}
}
ReplyWaiter w = sendAnnounce(tr, 0, ih, peerID,
downloaded, left, uploaded, event, max, toWait);
if (fast)
return null;
if (w == null) {
if (_log.shouldInfo())
_log.info("initial announce failed: " + tr);
return null;
}
boolean success = waitAndRetransmit(w, end);
_sentQueries.remove(w.getID());
if (success)
return w.getReplyObject();
if (_log.shouldInfo())
_log.info("announce failed after retx: " + tr);
return null;
}
//////// private below here
/**
* @return non-null
*/
private Tracker getTracker(String host, int port) {
Tracker ndp = new Tracker(host, port);
Tracker odp = _trackers.putIfAbsent(ndp, ndp);
if (odp != null)
ndp = odp;
return ndp;
}
///// Sending.....
/**
* Send one time with a new tid
* @param toWait if <= 0 does not register
* @return null on failure or if toWait <= 0
*/
private ReplyWaiter sendAnnounce(Tracker tr, long connID,
byte[] ih, byte[] id,
long downloaded, long left, long uploaded,
int event, int numWant, long toWait) {
int tid = _context.random().nextInt();
byte[] payload = sendAnnounce(tr, tid, connID, ih, id, downloaded, left, uploaded, event, numWant);
if (payload != null) {
if (toWait > 0) {
ReplyWaiter rv = new ReplyWaiter(tid, tr, payload, toWait);
_sentQueries.put(Integer.valueOf(tid), rv);
if (_log.shouldInfo())
_log.info("Sent: " + rv + " timeout: " + toWait);
return rv;
}
if (_log.shouldInfo())
_log.info("Sent annc " + event + " to " + tr + " no wait");
}
return null;
}
/**
* Send one time with given tid
* @return the payload or null on failure
*/
private byte[] sendAnnounce(Tracker tr, int tid, long connID,
byte[] ih, byte[] id,
long downloaded, long left, long uploaded,
int event, int numWant) {
byte[] payload = new byte[98];
DataHelper.toLong8(payload, 0, connID);
DataHelper.toLong(payload, 8, 4, ACTION_ANNOUNCE);
DataHelper.toLong(payload, 12, 4, tid);
System.arraycopy(ih, 0, payload, 16, 20);
System.arraycopy(id, 0, payload, 36, 20);
DataHelper.toLong(payload, 56, 8, downloaded);
DataHelper.toLong(payload, 64, 8, left);
DataHelper.toLong(payload, 72, 8, uploaded);
DataHelper.toLong(payload, 80, 4, event);
DataHelper.toLong(payload, 92, 4, numWant);
DataHelper.toLong(payload, 96, 2, TrackerClient.PORT);
boolean rv = sendMessage(tr.getDest(true), tr.getPort(), payload, true);
return rv ? payload : null;
}
/**
* wait after initial send, resend if necessary
*/
private boolean waitAndRetransmit(ReplyWaiter w, long untilTime) {
synchronized(w) {
while(true) {
try {
long toWait = untilTime - _context.clock().now();
if (toWait <= 0)
return false;
w.wait(toWait);
} catch (InterruptedException ie) {
return false;
}
switch (w.getState()) {
case INIT:
continue;
case SUCCESS:
return true;
case FAIL:
return false;
case TIMEOUT:
if (_log.shouldInfo())
_log.info("Timeout: " + w);
long toWait = untilTime - _context.clock().now();
if (toWait <= 1000)
return false;
boolean ok = resend(w, Math.min(toWait, w.getSentTo().getTimeout()));
if (!ok)
return false;
continue;
}
}
}
}
/**
* Resend the stored payload
* @return success
*/
private boolean resend(ReplyWaiter w, long toWait) {
Tracker tr = w.getSentTo();
int port = tr.getPort();
if (_log.shouldInfo())
_log.info("Resending: " + w + " timeout: " + toWait);
boolean rv = sendMessage(tr.getDest(true), port, w.getPayload(), true);
if (rv) {
_sentQueries.put(Integer.valueOf(w.getID()), w);
w.schedule(toWait);
}
return rv;
}
/**
* Lowest-level send message call.
* @param dest may be null, returns false
* @param repliable true for conn request, false for announce
* @return success
*/
private boolean sendMessage(Destination dest, int toPort, byte[] payload, boolean repliable) {
if (!_isRunning) {
if (_log.shouldInfo())
_log.info("send failed, not running");
return false;
}
if (dest == null) {
if (_log.shouldInfo())
_log.info("send failed, no dest");
return false;
}
if (dest.calculateHash().equals(_myHash))
throw new IllegalArgumentException("don't send to ourselves");
if (repliable) {
I2PDatagramMaker dgMaker = new I2PDatagramMaker(_session);
payload = dgMaker.makeI2PDatagram(payload);
if (payload == null) {
if (_log.shouldWarn())
_log.warn("DGM fail");
return false;
}
}
SendMessageOptions opts = new SendMessageOptions();
opts.setDate(_context.clock().now() + 60*1000);
opts.setTagsToSend(SEND_CRYPTO_TAGS);
opts.setTagThreshold(LOW_CRYPTO_TAGS);
if (!repliable)
opts.setSendLeaseSet(false);
try {
boolean success = _session.sendMessage(dest, payload, 0, payload.length,
repliable ? I2PSession.PROTO_DATAGRAM : I2PSession.PROTO_DATAGRAM_RAW,
_rPort, toPort, opts);
if (success) {
// ...
} else {
if (_log.shouldWarn())
_log.warn("sendMessage fail");
}
return success;
} catch (I2PSessionException ise) {
if (_log.shouldWarn())
_log.warn("sendMessage fail", ise);
return false;
}
}
///// Reception.....
/**
* @param from dest or null if it didn't come in on signed port
*/
private void receiveMessage(Destination from, int fromPort, byte[] payload) {
if (payload.length < 8) {
if (_log.shouldInfo())
_log.info("Got short message: " + payload.length + " bytes");
return;
}
int action = (int) DataHelper.fromLong(payload, 0, 4);
int tid = (int) DataHelper.fromLong(payload, 4, 4);
ReplyWaiter waiter = _sentQueries.remove(Integer.valueOf(tid));
if (waiter == null) {
if (_log.shouldInfo())
_log.info("Rcvd msg with no one waiting: " + tid);
return;
}
if (action == ACTION_ANNOUNCE) {
receiveAnnounce(waiter, payload);
} else if (action == ACTION_ERROR) {
receiveError(waiter, payload);
} else {
// includes ACTION_CONNECT
if (_log.shouldInfo())
_log.info("Rcvd msg with unknown action: " + action + " for: " + waiter);
waiter.gotReply(false);
Tracker tr = waiter.getSentTo();
tr.gotError();
}
}
private void receiveAnnounce(ReplyWaiter waiter, byte[] payload) {
Tracker tr = waiter.getSentTo();
if (payload.length >= 22) {
int interval = Math.min(MAX_INTERVAL, Math.max(MIN_INTERVAL,
(int) DataHelper.fromLong(payload, 8, 4)));
int leeches = (int) DataHelper.fromLong(payload, 12, 4);
int seeds = (int) DataHelper.fromLong(payload, 16, 4);
int peers = (int) DataHelper.fromLong(payload, 20, 2);
if (22 + (peers * Hash.HASH_LENGTH) > payload.length) {
if (_log.shouldWarn())
_log.warn("Short reply");
waiter.gotReply(false);
tr.gotError();
return;
}
if (_log.shouldInfo())
_log.info("Rcvd " + peers + " peers from " + tr);
Set<Hash> hashes;
if (peers > 0) {
hashes = new HashSet<Hash>(peers);
for (int off = 20; off < payload.length; off += Hash.HASH_LENGTH) {
hashes.add(Hash.create(payload, off));
}
} else {
hashes = Collections.emptySet();
}
TrackerResponse resp = new TrackerResponse(interval, seeds, leeches, hashes);
waiter.gotResponse(resp);
tr.setInterval(interval);
} else {
waiter.gotReply(false);
tr.gotError();
}
}
private void receiveError(ReplyWaiter waiter, byte[] payload) {
String msg;
if (payload.length > 8) {
msg = DataHelper.getUTF8(payload, 8, payload.length - 8);
} else {
msg = "";
}
TrackerResponse resp = new TrackerResponse(msg);
waiter.gotResponse(resp);
Tracker tr = waiter.getSentTo();
tr.gotError();
}
// I2PSessionMuxedListener interface ----------------
/**
* Instruct the client that the given session has received a message
*
* Will be called only if you register via addMuxedSessionListener().
* Will be called only for the proto(s) and toPort(s) you register for.
*
* @param session session to notify
* @param msgId message number available
* @param size size of the message - why it's a long and not an int is a mystery
* @param proto 1-254 or 0 for unspecified
* @param fromPort 1-65535 or 0 for unspecified
* @param toPort 1-65535 or 0 for unspecified
*/
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromPort, int toPort) {
// TODO throttle
try {
byte[] payload = session.receiveMessage(msgId);
if (payload == null)
return;
if (toPort == _rPort) {
// raw
receiveMessage(null, fromPort, payload);
} else {
if (_log.shouldWarn())
_log.warn("msg on bad port");
}
} catch (I2PSessionException e) {
if (_log.shouldWarn())
_log.warn("bad msg");
}
}
/** for non-muxed */
public void messageAvailable(I2PSession session, int msgId, long size) {}
public void reportAbuse(I2PSession session, int severity) {}
public void disconnected(I2PSession session) {
if (_log.shouldWarn())
_log.warn("UDPTC disconnected");
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
if (_log.shouldWarn())
_log.warn("UDPTC got error msg: ", error);
}
public static class TrackerResponse {
private final int interval, complete, incomplete;
private final String error;
private final Set<Hash> peers;
/** success */
public TrackerResponse(int interval, int seeds, int leeches, Set<Hash> peers) {
this.interval = interval;
complete = seeds;
incomplete = leeches;
this.peers = peers;
error = null;
}
/** failure */
public TrackerResponse(String errorMsg) {
interval = DEFAULT_INTERVAL;
complete = 0;
incomplete = 0;
peers = null;
error = errorMsg;
}
public Set<Hash> getPeers() {
return peers;
}
public int getPeerCount() {
int pc = peers == null ? 0 : peers.size();
return Math.max(pc, complete + incomplete - 1);
}
public int getSeedCount() {
return complete;
}
public int getLeechCount() {
return incomplete;
}
public String getFailureReason() {
return error;
}
/** in seconds */
public int getInterval() {
return interval;
}
}
private static class HostPort {
protected final String host;
protected final int port;
/**
* @param port the announce port
*/
public HostPort(String host, int port) {
this.host = host;
this.port = port;
}
/**
* @return the announce port
*/
public int getPort() {
return port;
}
@Override
public int hashCode() {
return host.hashCode() ^ port;
}
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof HostPort))
return false;
HostPort dp = (HostPort) o;
return port == dp.port && host.equals(dp.host);
}
@Override
public String toString() {
return "UDP Tracker " + host + ':' + port;
}
}
private class Tracker extends HostPort {
private final Object destLock = new Object();
private Destination dest;
private long expires;
private long lastHeardFrom;
private long lastFailed;
private int consecFails;
private int interval = DEFAULT_INTERVAL;
private static final long DELAY = 15*1000;
public Tracker(String host, int port) {
super(host, port);
}
/**
* @param fast if true, do not lookup
* @return dest or null
*/
public Destination getDest(boolean fast) {
synchronized(destLock) {
if (dest == null && !fast)
dest = _util.getDestination(host);
return dest;
}
}
/** does not change state */
public synchronized void replyTimeout() {
consecFails++;
lastFailed = _context.clock().now();
}
public synchronized int getInterval() {
return interval;
}
/** sets heardFrom; calls notify */
public synchronized void setInterval(int interval) {
long now = _context.clock().now();
lastHeardFrom = now;
consecFails = 0;
this.interval = interval;
this.notifyAll();
}
/** sets heardFrom; calls notify */
public synchronized void gotError() {
long now = _context.clock().now();
lastHeardFrom = now;
consecFails = 0;
this.notifyAll();
}
/** doubled for each consecutive failure */
public synchronized long getTimeout() {
return DEFAULT_TIMEOUT << Math.min(consecFails, 3);
}
@Override
public String toString() {
return "UDP Tracker " + host + ':' + port + " hasDest? " + (dest != null);
}
}
/**
* Callback for replies
*/
private class ReplyWaiter extends SimpleTimer2.TimedEvent {
private final int tid;
private final Tracker sentTo;
private final byte[] data;
private TrackerResponse replyObject;
private WaitState state = WaitState.INIT;
/**
* Either wait on this object with a timeout, or use non-null Runnables.
* Any sent data to be remembered may be stored by setSentObject().
* Reply object may be in getReplyObject().
*/
public ReplyWaiter(int tid, Tracker tracker, byte[] payload, long toWait) {
super(SimpleTimer2.getInstance(), toWait);
this.tid = tid;
sentTo = tracker;
data = payload;
}
public int getID() {
return tid;
}
public Tracker getSentTo() {
return sentTo;
}
public byte[] getPayload() {
return data;
}
/**
* @return may be null depending on what happened. Cast to expected type.
*/
public synchronized TrackerResponse getReplyObject() {
return replyObject;
}
/**
* If true, we got a reply, and getReplyObject() may contain something.
*/
public synchronized WaitState getState() {
return state;
}
/**
* Will notify this.
* Also removes from _sentQueries and calls heardFrom().
* Sets state to SUCCESS or FAIL.
*/
public synchronized void gotReply(boolean success) {
cancel();
_sentQueries.remove(Integer.valueOf(tid));
setState(success ? WaitState.SUCCESS : WaitState.FAIL);
}
/**
* Will notify this and run onReply.
* Also removes from _sentQueries and calls heardFrom().
*/
private synchronized void setState(WaitState state) {
this.state = state;
this.notifyAll();
}
/**
* Will notify this.
* Also removes from _sentQueries and calls heardFrom().
* Sets state to SUCCESS.
*/
public synchronized void gotResponse(TrackerResponse resp) {
replyObject = resp;
gotReply(true);
}
/**
* Sets state to INIT.
*/
@Override
public synchronized void schedule(long toWait) {
state = WaitState.INIT;
super.schedule(toWait);
}
/** timer callback on timeout */
public synchronized void timeReached() {
// don't trump success or failure
if (state != WaitState.INIT)
return;
//if (action == ACTION_CONNECT)
// sentTo.connFailed();
//else
sentTo.replyTimeout();
setState(WaitState.TIMEOUT);
if (_log.shouldWarn())
_log.warn("timeout waiting for reply from " + sentTo);
}
@Override
public String toString() {
return "Waiting for ID: " + tid + " to: " + sentTo + " state: " + state;
}
}
}

View File

@ -32,14 +32,15 @@ public class ConfigUIHelper {
{ "cs", "cz", "Čeština", null },
{ "zh", "cn", "Chinese 中文", null },
//{ "zh_TW", "tw", "Chinese 中文", "Taiwan" },
//{ "da", "dk", "Dansk", null },
{ "da", "dk", "Dansk", null },
{ "de", "de", "Deutsch", null },
//{ "et", "ee", "Eesti", null },
{ "en", "us", "English", null },
{ "es", "es", "Español", null },
{ "fa", "ir", "Persian فارسی", null },
{ "fr", "fr", "Français", null },
//{ "gl", "lang_gl", "Galego", null },
//{ "el", "gr", "Greek Ελληνικά", null },
{ "el", "gr", "Greek Ελληνικά", null },
{ "in", "id", "bahasa Indonesia", null },
{ "it", "it", "Italiano", null },
{ "ja", "jp", "Japanese 日本語", null },

View File

@ -4,9 +4,12 @@ import java.io.File;
import java.io.IOException;
import java.util.Properties;
import org.eclipse.jetty.util.log.Log;
import net.i2p.I2PAppContext;
import net.i2p.apps.systray.UrlLauncher;
import net.i2p.data.DataHelper;
import net.i2p.jetty.I2PLogger;
import net.i2p.jetty.JettyStart;
/**
@ -29,6 +32,13 @@ public class RunStandalone {
} catch (IOException ioe) {}
}
_context = new I2PAppContext(p);
// Do this after we have a context
// To take effect, must be set before any Jetty classes are loaded
try {
Log.setLog(new I2PLogger(_context));
} catch (Throwable t) {
System.err.println("INFO: I2P Jetty logging class not found, logging to stdout");
}
File base = _context.getBaseDir();
File xml = new File(base, "jetty-i2psnark.xml");
_jettyStart = new JettyStart(_context, null, new String[] { xml.getAbsolutePath() } );

View File

@ -3,12 +3,14 @@ package org.klomp.snark.web;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.Collator;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -22,6 +24,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@ -35,8 +38,10 @@ import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.servlet.util.ServletUtil;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.SystemVersion;
import net.i2p.util.Translate;
import net.i2p.util.UIMessages;
@ -251,11 +256,12 @@ public class I2PSnarkServlet extends BasicServlet {
}
} else {
String base = addPaths(req.getRequestURI(), "/");
boolean showEdit = req.getParameter("showEdit") != null;
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null,
req.getParameter("sort"));
req.getParameter("sort"), showEdit);
if (method.equals("POST")) {
// P-R-G
sendRedirect(req, resp, "");
sendRedirect(req, resp, showEdit ? "?showEdit" : "");
} else if (listing != null) {
setHTMLHeaders(resp, cspNonce, true);
resp.getWriter().write(listing);
@ -343,25 +349,24 @@ public class I2PSnarkServlet extends BasicServlet {
out.write("</head>\n" +
"<body>" +
"<center>");
List<Tracker> sortedTrackers = null;
if (isConfigure) {
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + "/\" title=\"");
out.write(_t("Torrents"));
out.write("\" class=\"snarkNav nav_main\">");
if (_contextName.equals(DEFAULT_NAME))
out.write(_t("I2PSnark"));
else
out.write(_contextName);
out.write("</a>");
} else {
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + '/' + peerString + "\" title=\"");
out.write(_t("Refresh page"));
out.write("\" class=\"snarkNav nav_main\">");
if (_contextName.equals(DEFAULT_NAME))
out.write(_t("I2PSnark"));
else
out.write(_contextName);
out.write("</a>\n");
}
out.write("\" class=\"snarkNav nav_main\">");
if (_contextName.equals(DEFAULT_NAME))
out.write(_t("I2PSnark"));
else
out.write(_contextName);
if (!_context.isRouterContext()) {
out.write(' ' + CoreVersion.VERSION);
}
out.write("</a>");
List<Tracker> sortedTrackers = null;
if (!isConfigure) {
sortedTrackers = _manager.getSortedTrackers();
if (_context.isRouterContext() && _manager.hasModifiedTrackers()) {
for (Tracker t : sortedTrackers) {
@ -2511,12 +2516,18 @@ public class I2PSnarkServlet extends BasicServlet {
out.write("<select name='theme'>");
String theme = _manager.getTheme();
String[] themes = _manager.getThemes();
Arrays.sort(themes);
// translated sort
Map<String, String> tmap = new TreeMap<String, String>(Collator.getInstance());
for (int i = 0; i < themes.length; i++) {
if(themes[i].equals(theme))
out.write("\n<OPTION value=\"" + themes[i] + "\" SELECTED>" + themes[i]);
tmap.put(_t(themes[i]), themes[i]);
}
for (Map.Entry<String, String> e : tmap.entrySet()) {
String tr = e.getKey();
String opt = e.getValue();
if(opt.equals(theme))
out.write("\n<option value=\"" + opt + "\" SELECTED>" + tr + "</option>");
else
out.write("\n<OPTION value=\"" + themes[i] + "\">" + themes[i]);
out.write("\n<option value=\"" + opt + "\">" + tr + "</option>");
}
out.write("</select>\n");
}
@ -2984,8 +2995,8 @@ public class I2PSnarkServlet extends BasicServlet {
* @return String of HTML or null if postParams != null
* @since 0.7.14
*/
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams, String sortParam)
throws IOException
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams,
String sortParam, boolean showEdit) throws IOException
{
String decodedBase = decodePath(base);
String title = decodedBase;
@ -3027,6 +3038,10 @@ public class I2PSnarkServlet extends BasicServlet {
_manager.startTorrent(snark);
} else if (postParams.get("recheck") != null) {
_manager.recheckTorrent(snark);
} else if (postParams.get("editTorrent") != null) {
saveTorrentEdit(snark, postParams);
} else if (postParams.get("showEdit") != null) {
// P-R-G only
} else {
_manager.addMessage("Unknown command");
}
@ -3055,7 +3070,7 @@ public class I2PSnarkServlet extends BasicServlet {
r = new File("");
}
boolean showStopStart = snark != null;
boolean showStopStart = snark != null && !showEdit;
Storage storage = snark != null ? snark.getStorage() : null;
boolean showPriority = storage != null && !storage.complete() &&
r.isDirectory();
@ -3093,7 +3108,7 @@ public class I2PSnarkServlet extends BasicServlet {
final boolean includeForm = showStopStart || showPriority || er || ec;
if (includeForm) {
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
"<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\">\n");
if (sortParam != null) {
buf.append("<input type=\"hidden\" name=\"sort\" value=\"")
.append(DataHelper.stripHTML(sortParam)).append("\" >\n");
@ -3103,7 +3118,7 @@ public class I2PSnarkServlet extends BasicServlet {
// first table - torrent info
buf.append("<table class=\"snarkTorrentInfo\">\n" +
"<tr><th></th><th><b>")
.append(_t("Torrent"))
.append(showEdit ? _t("Edit Torrent") : _t("Torrent"))
.append("</b></th><th>")
.append(DataHelper.escapeHTML(snark.getBaseName()))
.append("</th></tr>\n");
@ -3117,7 +3132,7 @@ public class I2PSnarkServlet extends BasicServlet {
.append("</b></td><td><a href=\"").append(_contextPath).append('/').append(baseName).append("\">")
.append(DataHelper.escapeHTML(fullPath))
.append("</a></td></tr>\n");
if (storage != null) {
if (storage != null && !showEdit) {
buf.append("<tr><td>");
toThemeImg(buf, "file");
buf.append("</td><td><b>")
@ -3127,17 +3142,19 @@ public class I2PSnarkServlet extends BasicServlet {
.append("</td></tr>\n");
}
String hex = I2PSnarkUtil.toHex(snark.getInfoHash());
buf.append("<tr><td>");
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Info hash"))
.append("</b></td><td><span id=\"infohash\">")
.append(hex.toUpperCase(Locale.US))
.append("</span></td></tr>\n");
if (!showEdit) {
buf.append("<tr><td>");
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Info hash"))
.append("</b></td><td><span id=\"infohash\">")
.append(hex.toUpperCase(Locale.US))
.append("</span></td></tr>\n");
}
String announce = null;
MetaInfo meta = snark.getMetaInfo();
if (meta != null) {
if (meta != null && !showEdit) {
announce = meta.getAnnounce();
if (announce == null)
announce = snark.getTrackerURL();
@ -3218,7 +3235,7 @@ public class I2PSnarkServlet extends BasicServlet {
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Comment")).append("</b></td><td>")
.append(DataHelper.stripHTML(com))
.append(DataHelper.escapeHTML(com).replace("\r\n", "<br>").replace("\n", "<br>"))
.append("</td></tr>\n");
}
long dat = meta.getCreationDate();
@ -3239,7 +3256,7 @@ public class I2PSnarkServlet extends BasicServlet {
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Created By")).append("</b></td><td>")
.append(DataHelper.stripHTML(cby))
.append(DataHelper.escapeHTML(cby))
.append("</td></tr>\n");
}
long[] dates = _manager.getSavedAddedAndCompleted(snark);
@ -3275,6 +3292,7 @@ public class I2PSnarkServlet extends BasicServlet {
}
}
if (!showEdit) { // don't bother to reindent
if (meta == null || !meta.isPrivate()) {
buf.append("<tr><td><a href=\"")
.append(MagnetURI.MAGNET_FULL).append(hex);
@ -3377,6 +3395,7 @@ public class I2PSnarkServlet extends BasicServlet {
.append(":</b> ")
.append(formatSize(snark.getPieceLength(0)))
.append("</span></td></tr>\n");
} // !showEdit
// buttons
if (showStopStart) {
@ -3390,7 +3409,7 @@ public class I2PSnarkServlet extends BasicServlet {
buf.append("<b>").append(_t("Starting")).append("&hellip;</b>");
} else if (snark.isAllocating()) {
buf.append("<b>").append(_t("Allocating")).append("&hellip;</b>");
} else {
} else if (isTopLevel && !showEdit) {
boolean isRunning = !snark.isStopped();
buf.append("<input type=\"submit\" value=\"");
if (isRunning)
@ -3398,14 +3417,23 @@ public class I2PSnarkServlet extends BasicServlet {
else
buf.append(_t("Start")).append("\" name=\"start\" class=\"starttorrent\">\n");
buf.append("<input type=\"submit\" name=\"recheck\" value=\"").append(_t("Force Recheck"));
if (isRunning)
if (isRunning) {
buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"")
.append(_t("Stop the torrent in order to check file integrity"))
.append("\">\n");
else
.append(_t("Torrent must be stopped"));
} else {
buf.append("\" class=\"reload\" title=\"")
.append(_t("Check integrity of the downloaded files"))
.append("\">\n");
.append(_t("Check integrity of the downloaded files"));
}
buf.append("\">\n" +
"<input type=\"submit\" name=\"showEdit\" value=\"").append(_t("Edit Torrent"));
if (isRunning) {
buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"")
.append(_t("Torrent must be stopped"));
} else {
buf.append("\" class=\"reload\" title=\"")
.append(_t("Add or remove trackers"));
}
buf.append("\">\n");
}
boolean showInOrder = storage != null && !storage.complete() &&
meta != null;
@ -3439,6 +3467,13 @@ public class I2PSnarkServlet extends BasicServlet {
}
buf.append("</table>\n");
if (snark != null && isTopLevel && showEdit) {
// Edit torrent. Show edit section only.
displayTorrentEdit(snark, base, buf);
buf.append("</form></div></div></center></body></html>");
return buf.toString();
}
if (snark != null && !r.exists()) {
// fixup TODO
buf.append("<table class=\"resourceError\" id=\"DoesNotExist\"><tr><th colspan=\"2\">")
@ -3481,7 +3516,7 @@ public class I2PSnarkServlet extends BasicServlet {
displayComments(snark, er, ec, esc, buf);
if (includeForm)
buf.append("</form>");
buf.append("</div></div></body></html>");
buf.append("</div></div></center></body></html>");
return buf.toString();
}
@ -3778,7 +3813,7 @@ public class I2PSnarkServlet extends BasicServlet {
// for stop/start/check
if (includeForm)
buf.append("</form>");
buf.append("</div></div></body></html>");
buf.append("</div></div></center></body></html>");
return buf.toString();
}
@ -4150,6 +4185,136 @@ public class I2PSnarkServlet extends BasicServlet {
buf.append("</div>");
}
/**
* @param snark non-null
* @since 0.9.53
*/
private void displayTorrentEdit(Snark snark, String base, StringBuilder buf) {
MetaInfo meta = snark.getMetaInfo();
if (meta == null)
return;
buf.append("<div id=\"snarkCommentSection\"><table class=\"snarkTorrentInfo\">\n");
//.append("<tr><th colspan=\"5\">")
//.append(_t("Edit Torrent"))
//.append("</th>")
//.append("</tr>");
boolean isRunning = !snark.isStopped();
if (isRunning) {
// shouldn't happen
buf.append("<tr><td colspan=\"5\">")
.append(_t("Torrent must be stopped"))
.append("</td></tr></table></div></form>");
return;
}
String announce = meta.getAnnounce();
if (announce == null)
announce = snark.getTrackerURL();
if (announce != null) {
// strip non-i2p trackers
if (!isI2PTracker(announce))
announce = null;
}
List<List<String>> alist = meta.getAnnounceList();
Set<String> annlist = new TreeSet<String>();
if (alist != null && !alist.isEmpty()) {
// strip non-i2p trackers
for (List<String> alist2 : alist) {
for (String s : alist2) {
if (isI2PTracker(s))
annlist.add(s);
}
}
}
if (announce != null)
annlist.add(announce);
if (!annlist.isEmpty()) {
buf.append("<tr><td colspan=\"3\"></td><td>").append("Primary").append("</td><td>")
.append("Delete").append("</td></tr>");
for (String s : annlist) {
int hc = s.hashCode();
buf.append("<tr><td>");
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Tracker")).append("</b></td><td>");
s = DataHelper.stripHTML(s);
buf.append("<span class=\"info_tracker\">");
buf.append(getShortTrackerLink(s, snark.getInfoHash()));
buf.append("</span> ");
//buf.append(s);
buf.append("</td><td>");
buf.append("<input type=\"radio\" class=\"optbox\" name=\"primary\" ");
if (s.equals(announce))
buf.append("checked=\"checked\" ");
buf.append("value=\"").append(hc);
buf.append("\"></td><td>");
buf.append("<input type=\"checkbox\" class=\"optbox\" name=\"trdelete.")
.append(hc).append("\" title=\"").append(_t("Mark for deletion")).append("\">");
buf.append("</td></tr>\n");
}
}
List<Tracker> newTrackers = _manager.getSortedTrackers();
for (Iterator<Tracker> iter = newTrackers.iterator(); iter.hasNext(); ) {
Tracker t = iter.next();
String announceURL = t.announceURL.replace("&#61;", "=");
if (announceURL.equals(announce) || annlist.contains(announceURL))
iter.remove();
}
if (!newTrackers.isEmpty()) {
buf.append("<tr><td colspan=\"3\"></td><td>").append("Primary").append("</td><td>")
.append("Add").append("</td></tr>");
for (Tracker t : newTrackers) {
String name = t.name;
int hc = t.announceURL.hashCode();
String announceURL = t.announceURL.replace("&#61;", "=");
buf.append("<tr><td>");
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Add Tracker")).append("</b></td><td>");
buf.append(name);
buf.append("</td><td><input type=\"radio\" class=\"optbox\" name=\"primary\" value=\"");
buf.append(hc);
buf.append("\"></td><td>");
buf.append("<input type=\"checkbox\" class=\"optbox\" id=\"").append(name).append("\" name=\"tradd.")
.append(hc).append("\" title=\"").append(_t("Add tracker")).append("\"> ")
.append("</td><td></td></tr>\n");
}
}
String com = meta.getComment();
if (com == null) {
com = "";
} else if (com.length() > 0) {
com = DataHelper.escapeHTML(com);
}
buf.append("<tr><td>");
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Comment")).append("</b></td>");
buf.append("<td colspan=\"2\" id=\"addCommentText\"><textarea name=\"nofilter_newTorrentComment\" cols=\"88\" rows=\"4\">")
.append(com).append("</textarea></td><td></td>");
buf.append("</tr>\n");
String cb = meta.getCreatedBy();
if (cb == null) {
cb = "";
} else if (cb.length() > 0) {
cb = DataHelper.escapeHTML(cb);
}
buf.append("<tr><td>");
toThemeImg(buf, "details");
buf.append("</td><td><b>")
.append(_t("Created By")).append("</b></td>");
buf.append("<td id=\"editTorrentCreatedBy\"><input type=\"text\" name=\"nofilter_newTorrentCreatedBy\" cols=\"44\" rows=\"1\" value=\"")
.append(cb).append("\"></td></tr>");
buf.append("<tr id=\"torrentInfoControl\"><td colspan=\"5\">");
buf.append("<input type=\"submit\" name=\"editTorrent\" value=\"");
buf.append(_t("Save Changes"));
buf.append("\" class=\"accept\"></td></tr>\n");
buf.append("</table></div>");
}
/**
* @param so null ok
* @return query string or ""
@ -4370,6 +4535,147 @@ public class I2PSnarkServlet extends BasicServlet {
_manager.setSavedCommentsEnabled(snark, yes);
}
/**
* @since 0.9.53
*/
private void saveTorrentEdit(Snark snark, Map<String, String[]> postParams) {
if (!snark.isStopped()) {
// shouldn't happen
_manager.addMessage(_t("Torrent must be stopped"));
return;
}
List<Integer> toAdd = new ArrayList<Integer>();
List<Integer> toDel = new ArrayList<Integer>();
Integer primary = null;
String newComment = "";
String newCreatedBy = "";
for (Map.Entry<String, String[]> entry : postParams.entrySet()) {
String key = entry.getKey();
String val = entry.getValue()[0]; // jetty arrays
if (key.startsWith("tradd.")) {
try {
toAdd.add(Integer.parseInt(key.substring(6)));
} catch (NumberFormatException nfe) {}
} else if (key.startsWith("trdelete.")) {
try {
toDel.add(Integer.parseInt(key.substring(9)));
} catch (NumberFormatException nfe) {}
} else if (key.equals("primary")) {
try {
primary = Integer.parseInt(val);
} catch (NumberFormatException nfe) {}
} else if (key.equals("nofilter_newTorrentComment")) {
newComment = val.trim();
} else if (key.equals("nofilter_newTorrentCreatedBy")) {
newCreatedBy = val.trim();
}
}
MetaInfo meta = snark.getMetaInfo();
if (meta == null) {
// shouldn't happen
_manager.addMessage("Can't edit magnet");
return;
}
String oldPrimary = meta.getAnnounce();
String oldComment = meta.getComment();
if (oldComment == null)
oldComment = "";
String oldCreatedBy = meta.getCreatedBy();
if (oldCreatedBy == null)
oldCreatedBy = "";
if (toAdd.isEmpty() && toDel.isEmpty() &&
(primary == null || primary.equals(oldPrimary)) &&
oldComment.equals(newComment) &&
oldCreatedBy.equals(newCreatedBy)) {
_manager.addMessage("No changes to torrent, not saved");
return;
}
List<List<String>> alist = meta.getAnnounceList();
Set<String> annlist = new TreeSet<String>();
if (alist != null && !alist.isEmpty()) {
// strip non-i2p trackers
for (List<String> alist2 : alist) {
for (String s : alist2) {
if (isI2PTracker(s))
annlist.add(s);
}
}
}
if (oldPrimary != null)
annlist.add(oldPrimary);
List<Tracker> newTrackers = _manager.getSortedTrackers();
for (Integer i : toDel) {
int hc = i.intValue();
for (Iterator<String> iter = annlist.iterator(); iter.hasNext(); ) {
String s = iter.next();
if (s.hashCode() == hc)
iter.remove();
}
}
for (Integer i : toAdd) {
int hc = i.intValue();
for (Tracker t : newTrackers) {
if (t.announceURL.hashCode() == hc) {
annlist.add(t.announceURL);
break;
}
}
}
String thePrimary = oldPrimary;
if (primary != null) {
int hc = primary.intValue();
for (String s : annlist) {
if (s.hashCode() == hc) {
thePrimary = s;
break;
}
}
}
List<List<String>> newAnnList;
if (annlist.isEmpty()) {
newAnnList = null;
thePrimary = null;
} else {
List<String> aalist = new ArrayList<String>(annlist);
newAnnList = Collections.singletonList(aalist);
if (!aalist.contains(thePrimary))
thePrimary = aalist.get(0);
}
if (newComment.equals(""))
newComment = null;
if (newCreatedBy.equals(""))
newCreatedBy = null;
MetaInfo newMeta = new MetaInfo(thePrimary, meta.getName(), null, meta.getFiles(), meta.getLengths(),
meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.isPrivate(),
newAnnList, newCreatedBy, meta.getWebSeedURLs(), newComment);
if (!DataHelper.eq(meta.getInfoHash(), newMeta.getInfoHash())) {
// shouldn't happen
_manager.addMessage("Torrent edit failed, infohash mismatch");
return;
}
File f = new File(_manager.util().getTempDir(), "edit-" + _manager.util().getContext().random().nextLong() + ".torrent");
OutputStream out = null;
try {
out = new SecureFileOutputStream(f);
out.write(newMeta.getTorrentData());
out.close();
boolean ok = FileUtil.rename(f, new File(snark.getName()));
if (!ok) {
_manager.addMessage("Save edit changes failed");
return;
}
} catch (IOException ioe) {
try { if (out != null) out.close(); } catch (IOException ioe2) {}
_manager.addMessage("Save edit changes failed: " + ioe);
return;
} finally {
f.delete();
}
snark.replaceMetaInfo(newMeta);
_manager.addMessage("Torrent changes saved");
}
/** @since 0.9.32 */
private static boolean noCollapsePanels(HttpServletRequest req) {
// check for user agents that can't toggle the collapsible panels...

View File

@ -4,5 +4,33 @@
# The file jetty-i2psnark.xml must be present in the current directory.
# i2psnark will be accessed at http://127.0.0.1:8002/
#
# Raise the soft open files soft ulimit to this value, if able
OPEN_FILES_ULIMIT=2048
raiseopenfilesulimit() {
OPEN_FILES_SOFT=`ulimit -S -n` 2> /dev/null || return
if [ "$OPEN_FILES_SOFT" != "unlimited" ]
then
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_SOFT" ]
then
OPEN_FILES_HARD=`ulimit -H -n` 2> /dev/null || return
if [ "$OPEN_FILES_HARD" != "unlimited" ]
then
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_HARD" ]
then
OPEN_FILES_ULIMIT="$OPEN_FILES_HARD"
fi
fi
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_SOFT" ]
then
ulimit -S -n "$OPEN_FILES_ULIMIT" > /dev/null 2>&1
fi
fi
fi
}
raiseopenfilesulimit
I2P="."
java -jar "$I2P/i2psnark.jar"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
3gpp = video/3gpp
7z = application/x-7z-compressed
ape = audio/x-monkeys-audio
avif = image/avif
bz2 = application/x-bzip2
cue = application/x-cue
dff = audio/x-dsd

View File

@ -4,6 +4,10 @@ java -jar i2psnark.jar
I2PSnark web ui will be at http://127.0.0.1:8002/i2psnark/
To change or disable browser launch at startup, edit i2psnark-appctx.config.
To change the port, edit jetty-i2psnark.xml.
I2PSnark is GPL'ed software, based on Snark (http://www.klomp.org/) to run on top of I2P
(https://geti2p.net/) within a webserver (such as the bundled Jetty from
https://www.eclipse.org/jetty/). For more information about I2PSnark, get in touch
@ -19,7 +23,7 @@ To add RPC support:
to the webapps/ directory in your standalone install.
2b) If you do not have the i2psnark-rpc plugin installed, get the i2p.plugins.i2psnark-rpc
branch out of monotone, build with 'ant war', and copy the file src/build/transmission.war.jar
branch out of git, build with 'ant war', and copy the file src/build/transmission.war.jar
to the file webapps/transmission.war in your standalone install.
3) Start i2psnark standalone as usual. The transmission web interface will be at

View File

@ -354,8 +354,9 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* @return A copy, unmodifiable, non-null
* @since public since 0.9.53 for advanced plugin usage, was package private
*/
List<I2PSession> getSessions() {
public List<I2PSession> getSessions() {
if (_sessions.isEmpty())
return Collections.emptyList();
return new ArrayList<I2PSession>(_sessions);

View File

@ -148,6 +148,9 @@ public class TunnelController implements Logging {
private static final String OPT_ENCTYPE = PFX_OPTION + "i2cp.leaseSetEncType";
/** @since 0.9.53 */
private static final String OPT_PRIORITY = PFX_OPTION + "outbound.priority";
/** all of these @since 0.9.14 */
public static final String TYPE_CONNECT = "connectclient";
public static final String TYPE_HTTP_BIDIR_SERVER = "httpbidirserver";
@ -231,6 +234,15 @@ public class TunnelController implements Logging {
_state = keyOK && getStartOnLoad() ? TunnelState.START_ON_LOAD : TunnelState.STOPPED;
}
/**
* The I2PTunnel
*
* @since 0.9.53 for advanced plugin usage
*/
public I2PTunnel getTunnel() {
return _tunnel;
}
/**
* @return success
*/
@ -902,6 +914,12 @@ public class TunnelController implements Logging {
if (!_config.containsKey(OPT_SIG_TYPE))
_config.setProperty(OPT_SIG_TYPE, PREFERRED_SIGTYPE.name());
}
if (type.equals(TYPE_IRC_CLIENT) || type.equals(TYPE_STD_CLIENT) ||
type.equals(TYPE_IRC_SERVER) || type.equals(TYPE_STD_SERVER) ||
type.equals(TYPE_SOCKS_IRC)) {
if (!_config.containsKey(OPT_PRIORITY))
_config.setProperty(OPT_PRIORITY, "10");
}
if (!isClient(type)) {
_tunnel.filterDefinition = _config.getProperty(PROP_FILTER);

View File

@ -122,10 +122,11 @@ class AccessFilter implements StatefulConnectionFilter {
synchronized(knownDests) {
knownDests.keySet().retainAll(tmp.keySet());
for (Hash newHash : tmp.keySet()) {
for (Map.Entry<Hash, DestTracker> e : tmp.entrySet()) {
Hash newHash = e.getKey();
if (knownDests.containsKey(newHash))
continue;
knownDests.put(newHash, tmp.get(newHash));
knownDests.put(newHash, e.getValue());
}
}

View File

@ -1,5 +1,6 @@
package net.i2p.i2ptunnel.access;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
@ -18,6 +19,7 @@ import net.i2p.data.Hash;
class FileFilterDefinitionElement extends FilterDefinitionElement {
private final File file;
private final Map<Hash, DestTracker> lastLoaded = new HashMap<>();
private volatile long lastLoading;
/**
@ -31,18 +33,36 @@ class FileFilterDefinitionElement extends FilterDefinitionElement {
@Override
public void update(Map<Hash, DestTracker> map) throws IOException {
if (!(file.exists() && file.isFile() && file.lastModified() > lastLoading))
if (!(file.exists() && file.isFile()))
return;
if (file.lastModified() <= lastLoading) {
synchronized (lastLoaded) {
for (Map.Entry<Hash, DestTracker> entry : lastLoaded.entrySet()) {
if (!map.containsKey(entry.getKey()))
map.put(entry.getKey(),entry.getValue());
}
}
return;
}
lastLoading = System.currentTimeMillis();
BufferedReader reader = null;
synchronized (lastLoaded) {
lastLoaded.clear();
}
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
reader = new BufferedReader(new FileReader(file));
String b32;
while((b32 = reader.readLine()) != null) {
Hash hash = fromBase32(b32);
if (map.containsKey(hash))
continue;
map.put(hash, new DestTracker(hash, threshold));
DestTracker newTracker = new DestTracker(hash, threshold);
map.put(hash, newTracker);
synchronized (lastLoaded) {
lastLoaded.put(hash, newTracker);
}
}
} catch (InvalidDefinitionException bad32) {
throw new IOException("invalid access list entry", bad32);

View File

@ -59,13 +59,17 @@ abstract class IRCFilter {
*/
public static String inboundFilter(String s, StringBuffer expectedPong, DCCHelper helper) {
String field[] = DataHelper.split(s, " ", 4);
String field[] = DataHelper.split(s, " ", 5);
String command;
int idx=0;
try {
if (field[0].charAt(0) == ':')
// https://www.unrealircd.org/docs/Message_tags
// https://ircv3.net/specs/extensions/message-tags.html
if (field[0].charAt(0) == '@')
idx++;
if (field[idx].charAt(0) == ':')
idx++;
command = field[idx++].toUpperCase(Locale.US);
} catch (IndexOutOfBoundsException ioobe) {
@ -279,7 +283,7 @@ abstract class IRCFilter {
*/
public static String outboundFilter(String s, StringBuffer expectedPong, DCCHelper helper) {
String field[] = DataHelper.split(s, " ",3);
String field[] = DataHelper.split(s, " ", 4);
if(field[0].length()==0)
return null; // W T F?
@ -288,7 +292,12 @@ abstract class IRCFilter {
if(field[0].charAt(0)==':')
return null; // ???
String command = field[0].toUpperCase(Locale.US);
int idx = 0;
// https://www.unrealircd.org/docs/Message_tags
// https://ircv3.net/specs/extensions/message-tags.html
if (field[0].charAt(0) == '@')
idx++;
String command = field[idx++].toUpperCase(Locale.US);
if ("PING".equals(command)) {
// Most clients just send a PING and are happy with any old PONG. Others,
@ -304,17 +313,17 @@ abstract class IRCFilter {
String rv = null;
expectedPong.setLength(0);
if (field.length == 1) { // PING
if (field.length == idx) { // PING
rv = "PING";
// If we aren't rewriting the PING don't rewrite the PONG
// expectedPong.append("PONG 127.0.0.1");
} else if (field.length == 2) { // PING nonce
rv = "PING " + field[1];
} else if (field.length == idx + 1) { // PING nonce
rv = "PING " + field[idx];
// If we aren't rewriting the PING don't rewrite the PONG
// expectedPong.append("PONG ").append(field[1]);
} else if (field.length == 3) { // PING nonce serverLocation
rv = "PING " + field[1];
expectedPong.append("PONG ").append(field[2]).append(" :").append(field[1]); // PONG serverLocation nonce
} else if (field.length == idx + 2) { // PING nonce serverLocation
rv = "PING " + field[idx];
expectedPong.append("PONG ").append(field[idx + 1]).append(" :").append(field[idx]); // PONG serverLocation nonce
} else {
//if (_log.shouldLog(Log.ERROR))
// _log.error("IRC client sent a PING we don't understand, filtering it (\"" + s + "\")");
@ -335,20 +344,20 @@ abstract class IRCFilter {
// in addition to the CTCP version
if("NOTICE".equals(command))
{
if (field.length < 3)
if (field.length < idx + 2)
return s; // invalid, allow server response
String msg = field[2];
String msg = field[idx + 1];
if(msg.startsWith(":DCC "))
return filterDCCOut(field[0] + ' ' + field[1] + " :DCC ", msg.substring(5), helper);
return filterDCCOut(field[idx - 1] + ' ' + field[idx] + " :DCC ", msg.substring(5), helper);
// fall through
}
// Allow PRIVMSG, but block CTCP (except ACTION).
if("PRIVMSG".equals(command) || "NOTICE".equals(command))
{
if (field.length < 3)
if (field.length < idx + 2)
return s; // invalid, allow server response
String msg = field[2];
String msg = field[idx + 1];
if(msg.indexOf(0x01) >= 0) // CTCP marker ^A can be anywhere, not just immediately after the ':'
{
@ -369,7 +378,7 @@ abstract class IRCFilter {
return s;
}
if (msg.startsWith("DCC "))
return filterDCCOut(field[0] + ' ' + field[1] + " :\001DCC ", msg.substring(4), helper);
return filterDCCOut(field[idx - 1] + ' ' + field[idx] + " :\001DCC ", msg.substring(4), helper);
// XDCC looks safe, ip/port happens over regular DCC
// http://en.wikipedia.org/wiki/XDCC
if (msg.toUpperCase(Locale.US).startsWith("XDCC ") && helper != null && helper.isEnabled())
@ -382,19 +391,19 @@ abstract class IRCFilter {
}
if("USER".equals(command)) {
if (field.length < 3)
if (field.length < idx + 2)
return s; // invalid, allow server response
int idx = field[2].lastIndexOf(':');
if(idx<0)
int cidx = field[idx + 1].lastIndexOf(':');
if (cidx < 0)
return "USER user hostname localhost :realname";
String realname = field[2].substring(idx+1);
String ret = "USER "+field[1]+" hostname localhost :"+realname;
String realname = field[idx + 1].substring(cidx + 1);
String ret = "USER " + field[idx] + " hostname localhost :" + realname;
return ret;
}
if ("PART".equals(command)) {
// hide client message
return "PART " + field[1] + " :leaving";
return "PART " + field[idx] + " :leaving";
}
if ("QUIT".equals(command)) {

View File

@ -8,12 +8,17 @@ import net.i2p.i2ptunnel.udp.*;
import net.i2p.util.Log;
/**
* Sends to one of many Sinks
* Sends to one of many Sinks based on the toPort
*
* @author zzz modded from streamr/MultiSource
*/
public class MultiSink<S extends Sink> implements Source, Sink {
private final Map<Integer, S> cache;
public MultiSink(Map<Destination, S> cache) {
/**
* @param cache map of toPort to Sink
*/
public MultiSink(Map<Integer, S> cache) {
this.cache = cache;
}
@ -23,18 +28,32 @@ public class MultiSink<S extends Sink> implements Source, Sink {
public void start() {}
/**
* Send to a single sink looked up by toPort
*
* May throw RuntimeException from underlying sinks
*
* @param from passed along
* @param fromPort passed along
* @param toPort passed along
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException
*/
public void send(Destination from, byte[] data) {
Sink s = this.cache.get(from);
public void send(Destination from, int fromPort, int toPort, byte[] data) {
Sink s = cache.get(toPort);
if (s == null && toPort == 0 && cache.size() == 1) {
// for now, do the server a favor if the toPort isn't specified
for (Sink ss : cache.values()) {
s = ss;
break;
}
}
if (s == null) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(MultiSink.class);
log.error("No where to go for " + from.calculateHash().toBase64().substring(0, 6));
String frm = (from != null) ? from.toBase32() : "raw";
if (log.shouldWarn())
log.warn("No where to go for " + frm + " port " + fromPort + " to port " + toPort);
return;
}
s.send(from, data);
s.send(from, fromPort, toPort, data);
}
private Map<Destination, S> cache;
}

View File

@ -1,38 +0,0 @@
package net.i2p.i2ptunnel.socks;
import java.util.Map;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.udp.*;
import net.i2p.util.Log;
/**
* Track who the reply goes to
* @author zzz
*/
public class ReplyTracker<S extends Sink> implements Source, Sink {
public ReplyTracker(S reply, Map<Destination, S> cache) {
this.reply = reply;
this.cache = cache;
}
public void setSink(Sink sink) {
this.sink = sink;
}
public void start() {}
/**
* May throw RuntimeException from underlying sink
* @throws RuntimeException
*/
public void send(Destination to, byte[] data) {
this.cache.put(to, this.reply);
this.sink.send(to, data);
}
private S reply;
private Map<Destination, S> cache;
private Sink sink;
}

View File

@ -1,8 +1,12 @@
package net.i2p.i2ptunnel.socks;
import java.util.Arrays;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.socks.SOCKS5Constants.AddressType;
import net.i2p.util.Addresses;
/**
* Save the SOCKS header from a datagram
@ -11,9 +15,12 @@ import net.i2p.data.Destination;
* @author zzz
*/
public class SOCKSHeader {
private byte[] header;
private static final byte[] beg = {0,0,0,3,60};
/**
* @param data the whole packet
* @throws IllegalArgumentException on bad socks format
*/
public SOCKSHeader(byte[] data) {
if (data.length <= 8)
@ -24,14 +31,12 @@ public class SOCKSHeader {
throw new IllegalArgumentException("We can't handle fragments!");
int headerlen = 0;
int addressType = data[3];
if (addressType == 1) {
// this will fail in getDestination()
if (addressType == AddressType.IPV4) {
headerlen = 6 + 4;
} else if (addressType == 3) {
} else if (addressType == AddressType.DOMAINNAME) {
headerlen = 6 + 1 + (data[4] & 0xff);
} else if (addressType == 4) {
// this will fail in getDestination()
// but future garlicat partial hash lookup possible?
} else if (addressType == AddressType.IPV6) {
// future garlicat partial hash lookup possible?
headerlen = 6 + 16;
} else {
throw new IllegalArgumentException("Unknown address type: " + addressType);
@ -42,33 +47,62 @@ public class SOCKSHeader {
this.header = new byte[headerlen];
System.arraycopy(data, 0, this.header, 0, headerlen);
}
private static final byte[] beg = {0,0,0,3,60};
private static final byte[] end = {0,0};
/**
* Make a dummy header from a dest,
* for those cases where we want to receive unsolicited datagrams.
* Unused for now.
*
* @param port I2CP port 0-65535
* @since 0.9.53 add port param
*/
public SOCKSHeader(Destination dest) {
this.header = new byte[beg.length + 60 + end.length];
public SOCKSHeader(Destination dest, int port) {
this.header = new byte[beg.length + 60 + 2];
System.arraycopy(beg, 0, this.header, 0, beg.length);
String b32 = dest.toBase32();
System.arraycopy(DataHelper.getASCII(b32), 0, this.header, beg.length, 60);
System.arraycopy(end, 0, this.header, beg.length + 60, end.length);
DataHelper.toLong(header, beg.length + 60, 2, port);
}
/**
* As of 0.9.53, returns IP address as a string for address types 1 and 4.
*
* @return hostname or null for unknown address type
*/
public String getHost() {
int addressType = this.header[3];
if (addressType != 3)
return null;
int namelen = (this.header[4] & 0xff);
byte[] nameBytes = new byte[namelen];
System.arraycopy(this.header, 5, nameBytes, 0, namelen);
return DataHelper.getUTF8(nameBytes);
if (addressType == AddressType.DOMAINNAME) {
int namelen = (this.header[4] & 0xff);
return DataHelper.getUTF8(header, 5, namelen);
}
if (addressType == AddressType.IPV4)
return Addresses.toString(Arrays.copyOfRange(header, 4, 4));
if (addressType == AddressType.IPV6)
return Addresses.toString(Arrays.copyOfRange(header, 4, 16));
return null;
}
/**
* @return 0 - 65535
* @since 0.9.53
*/
public int getPort() {
int namelen;
int addressType = header[3];
if (addressType == 3)
namelen = 1 + (header[4] & 0xff);
else if (addressType == 1)
namelen = 4;
else if (addressType == 4)
namelen = 16;
else
return 0;
return (int) DataHelper.fromLong(header, 4 + namelen, 2);
}
/**
* @return destination or null
*/
public Destination getDestination() {
String name = getHost();
if (name == null)
@ -80,6 +114,4 @@ public class SOCKSHeader {
public byte[] getBytes() {
return header;
}
private byte[] header;
}

View File

@ -5,6 +5,7 @@ import java.net.InetAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.udp.*;
@ -14,22 +15,26 @@ import net.i2p.i2ptunnel.udp.*;
* ports, it happens outside of here.
*
* TX:
* UDPSource -&gt; SOCKSUDPUnwrapper -&gt; ReplyTracker ( -&gt; I2PSink in SOCKSUDPTunnel)
* UDPSource -&gt; SOCKSUDPUnwrapper -&gt; (I2PSink in SOCKSUDPTunnel)
*
* RX:
* UDPSink &lt;- SOCKSUDPWrapper ( &lt;- MultiSink &lt;- I2PSource in SOCKSUDPTunnel)
*
* The Unwrapper passes headers to the Wrapper through a cache.
* The ReplyTracker passes sinks to MultiSink through a cache.
* MultiSink routes packets based on toPort.
*
* @author zzz
*/
public class SOCKSUDPPort implements Source, Sink {
private final UDPSink udpsink;
private final UDPSource udpsource;
private final SOCKSUDPWrapper wrapper;
private final SOCKSUDPUnwrapper unwrapper;
public SOCKSUDPPort(InetAddress host, int port, Map<Destination, SOCKSUDPPort> replyMap) {
public SOCKSUDPPort(InetAddress host, int port, Map<Integer, SOCKSUDPPort> replyMap) {
// this passes the host and port from UDPUnwrapper to UDPWrapper
Map<Destination, SOCKSHeader> cache = new ConcurrentHashMap<Destination, SOCKSHeader>(4);
Map<I2PSocketAddress, SOCKSHeader> cache = new ConcurrentHashMap<I2PSocketAddress, SOCKSHeader>(4);
// rcv from I2P and send to a port
this.wrapper = new SOCKSUDPWrapper(cache);
@ -41,8 +46,6 @@ public class SOCKSUDPPort implements Source, Sink {
this.udpsource = new UDPSource(sock);
this.unwrapper = new SOCKSUDPUnwrapper(cache);
this.udpsource.setSink(this.unwrapper);
this.udptracker = new ReplyTracker<SOCKSUDPPort>(this, replyMap);
this.unwrapper.setSink(this.udptracker);
}
/** Socks passes this back to the client on the TCP connection */
@ -51,7 +54,7 @@ public class SOCKSUDPPort implements Source, Sink {
}
public void setSink(Sink sink) {
this.udptracker.setSink(sink);
this.unwrapper.setSink(sink);
}
public void start() {
@ -66,16 +69,14 @@ public class SOCKSUDPPort implements Source, Sink {
/**
* May throw RuntimeException from underlying sink
* @param from will be passed along
* @param fromPort will be passed along
* @param toPort will be passed along
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException
*/
public void send(Destination from, byte[] data) {
this.wrapper.send(from, data);
public void send(Destination from, int fromPort, int toPort, byte[] data) {
this.wrapper.send(from, fromPort, toPort, data);
}
private UDPSink udpsink;
private UDPSource udpsource;
private SOCKSUDPWrapper wrapper;
private SOCKSUDPUnwrapper unwrapper;
private ReplyTracker<SOCKSUDPPort> udptracker;
}

View File

@ -4,6 +4,7 @@ import java.net.InetAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Iterator;
import java.util.Map;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPClientBase;
@ -12,18 +13,24 @@ import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPClientBase;
* A Datagram Tunnel that can have multiple bidirectional ports on the UDP side.
*
* TX:
* (ReplyTracker in multiple SOCKSUDPPorts -&gt; ) I2PSink
* (multiple SOCKSUDPPorts -&gt; ) I2PSink
*
* RX:
* (SOCKSUDPWrapper in multiple SOCKSUDPPorts &lt;- ) MultiSink &lt;- I2PSource
*
* The reply from a dest goes to the last SOCKSUDPPort that sent to that dest.
* If multiple ports are talking to a dest at the same time, this isn't
* going to work very well.
* The replies must be to the same I2CP toPort as the outbound fromPort.
* If the server does not honor that, the replies will be dropped.
*
* The replies must be repliable. Raw datagrams are not supported, and would
* require a unique source port for each target.
*
* Preliminary, untested, possibly incomplete.
*
* @author zzz modded from streamr/StreamrConsumer
*/
public class SOCKSUDPTunnel extends I2PTunnelUDPClientBase {
private final Map<Integer, SOCKSUDPPort> ports;
private final MultiSink<SOCKSUDPPort> demuxer;
/**
* Set up a tunnel with no UDP side yet.
@ -33,15 +40,14 @@ public class SOCKSUDPTunnel extends I2PTunnelUDPClientBase {
super(null, tunnel, tunnel, tunnel);
this.ports = new ConcurrentHashMap<Integer, SOCKSUDPPort>(1);
this.cache = new ConcurrentHashMap<Destination, SOCKSUDPPort>(1);
this.demuxer = new MultiSink<SOCKSUDPPort>(this.cache);
this.demuxer = new MultiSink<SOCKSUDPPort>(ports);
setSink(this.demuxer);
}
/** @return the UDP port number */
public int add(InetAddress host, int port) {
SOCKSUDPPort sup = new SOCKSUDPPort(host, port, this.cache);
SOCKSUDPPort sup = new SOCKSUDPPort(host, port, ports);
this.ports.put(Integer.valueOf(sup.getPort()), sup);
sup.setSink(this);
sup.start();
@ -52,11 +58,6 @@ public class SOCKSUDPTunnel extends I2PTunnelUDPClientBase {
SOCKSUDPPort sup = this.ports.remove(port);
if (sup != null)
sup.stop();
for (Iterator<Map.Entry<Destination, SOCKSUDPPort>> iter = cache.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Destination, SOCKSUDPPort> e = iter.next();
if (e.getValue() == sup)
iter.remove();
}
}
@Override
@ -81,12 +82,5 @@ public class SOCKSUDPTunnel extends I2PTunnelUDPClientBase {
sup.stop();
}
this.ports.clear();
this.cache.clear();
}
private Map<Integer, SOCKSUDPPort> ports;
private Map<Destination, SOCKSUDPPort> cache;
private MultiSink<SOCKSUDPPort> demuxer;
}

View File

@ -5,20 +5,23 @@ import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.udp.*;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.util.Log;
/**
* Strip a SOCKS header off a datagram, convert it to a Destination
* Strip a SOCKS header off a datagram, convert it to a Destination and port
* Ref: RFC 1928
*
* @author zzz
*/
public class SOCKSUDPUnwrapper implements Source, Sink {
private Sink sink;
private final Map<I2PSocketAddress, SOCKSHeader> cache;
/**
* @param cache put headers here to pass to SOCKSUDPWrapper
*/
public SOCKSUDPUnwrapper(Map<Destination, SOCKSHeader> cache) {
public SOCKSUDPUnwrapper(Map<I2PSocketAddress, SOCKSHeader> cache) {
this.cache = cache;
}
@ -31,9 +34,13 @@ public class SOCKSUDPUnwrapper implements Source, Sink {
/**
*
* May throw RuntimeException from underlying sink
* @param ignored_from ignored
* @param fromPort will be passed along
* @param toPort ignored
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException
*/
public void send(Destination ignored_from, byte[] data) {
public void send(Destination ignored_from, int fromPort, int toPort, byte[] data) {
SOCKSHeader h;
try {
h = new SOCKSHeader(data);
@ -50,14 +57,14 @@ public class SOCKSUDPUnwrapper implements Source, Sink {
return;
}
cache.put(dest, h);
cache.put(new I2PSocketAddress(dest, toPort), h);
int headerlen = h.getBytes().length;
byte unwrapped[] = new byte[data.length - headerlen];
System.arraycopy(data, headerlen, unwrapped, 0, unwrapped.length);
this.sink.send(dest, unwrapped);
// We pass the local DatagramSocket's port through as the I2CP from port,
// so that it will come back as the toPort in the reply,
// and MultiSink will send it to the right SOCKSUDPWrapper/SOCKSUDPPort
this.sink.send(dest, fromPort, h.getPort(), unwrapped);
}
private Sink sink;
private Map<Destination, SOCKSHeader> cache;
}

View File

@ -2,8 +2,11 @@ package net.i2p.i2ptunnel.socks;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.udp.*;
import net.i2p.util.Log;
/**
* Put a SOCKS header on a datagram
@ -12,7 +15,10 @@ import net.i2p.i2ptunnel.udp.*;
* @author zzz
*/
public class SOCKSUDPWrapper implements Source, Sink {
public SOCKSUDPWrapper(Map<Destination, SOCKSHeader> cache) {
private Sink sink;
private final Map<I2PSocketAddress, SOCKSHeader> cache;
public SOCKSUDPWrapper(Map<I2PSocketAddress, SOCKSHeader> cache) {
this.cache = cache;
}
@ -26,13 +32,23 @@ public class SOCKSUDPWrapper implements Source, Sink {
* Use the cached header, which should have the host string and port
*
* May throw RuntimeException from underlying sink
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException
*/
public void send(Destination from, byte[] data) {
public void send(Destination from, int fromPort, int toPort, byte[] data) {
if (this.sink == null)
return;
if (from == null) {
// TODO to handle raw replies, SOCKSUDPWrapper would have to use a unique
// fromPort for every target or request, and we would lookup the
// destination by toPort
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SOCKSUDPWrapper.class);
if (log.shouldWarn())
log.warn("No support for raw datagrams, from port " + fromPort + " to port " + toPort);
return;
}
SOCKSHeader h = cache.get(from);
SOCKSHeader h = cache.get(new I2PSocketAddress(from, fromPort));
if (h == null) {
// RFC 1928 says drop
// h = new SOCKSHeader(from);
@ -43,9 +59,6 @@ public class SOCKSUDPWrapper implements Source, Sink {
byte wrapped[] = new byte[header.length + data.length];
System.arraycopy(header, 0, wrapped, 0, header.length);
System.arraycopy(data, 0, wrapped, header.length, data.length);
this.sink.send(from, wrapped);
this.sink.send(from, fromPort, toPort, wrapped);
}
private Sink sink;
private Map<Destination, SOCKSHeader> cache;
}

View File

@ -8,6 +8,7 @@ import java.net.Socket;
import java.nio.channels.SelectableChannel;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;

View File

@ -14,10 +14,12 @@ import net.i2p.util.Log;
* @author zzz modded for I2PTunnel
*/
public class MultiSource implements Source, Sink {
private Sink sink;
private final List<MSink> sinks;
private final Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
public MultiSource() {
this.sinks = new CopyOnWriteArrayList<Destination>();
this.sinks = new CopyOnWriteArrayList<MSink>();
}
public void setSink(Sink sink) {
@ -32,9 +34,10 @@ public class MultiSource implements Source, Sink {
/**
* May throw RuntimeException from underlying sinks
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException
*/
public void send(Destination ignored_from, byte[] data) {
public void send(Destination ignored_from, int ignored_fromPort, int ignored_toPort, byte[] data) {
if (sinks.isEmpty()) {
if (log.shouldDebug())
log.debug("No subscribers to send " + data.length + " bytes to");
@ -43,19 +46,53 @@ public class MultiSource implements Source, Sink {
if (log.shouldDebug())
log.debug("Sending " + data.length + " bytes to " + sinks.size() + " subscribers");
for(Destination dest : this.sinks) {
this.sink.send(dest, data);
for(MSink ms : this.sinks) {
this.sink.send(ms.dest, ms.fromPort, ms.toPort, data);
}
}
public void add(Destination sink) {
this.sinks.add(sink);
/**
* @since 0.9.53 changed to MSink parameter
*/
public void add(MSink ms) {
sinks.add(ms);
}
public void remove(Destination sink) {
this.sinks.remove(sink);
/**
* @since 0.9.53 changed to MSink parameter
*/
public void remove(MSink ms) {
sinks.remove(ms);
}
/**
* @since 0.9.53
*/
static class MSink {
public final Destination dest;
public final int fromPort, toPort;
public MSink(Destination dest, int fromPort, int toPort) {
this.dest = dest; this.fromPort = fromPort; this.toPort = toPort;
}
@Override
public int hashCode() {
return dest.hashCode() | fromPort | (toPort << 16);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof MSink))
return false;
MSink s = (MSink) o;
return dest.equals(s.dest) && fromPort == s.fromPort && toPort == s.toPort;
}
@Override
public String toString() {
return "from port " + fromPort + " to " + dest.toBase32() + ':' + toPort;
}
}
private Sink sink;
private final List<Destination> sinks;
}

View File

@ -10,10 +10,21 @@ import net.i2p.util.Log;
* @author welterde/zzz
*/
public class Pinger implements Source, Runnable {
private final Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
protected Sink sink;
protected final Thread thread;
private final Object waitlock = new Object();
protected volatile boolean running;
private final Log log;
private final int fromPort;
public Pinger() {
/**
* @param fromPort the I2CP from port
* @since 0.9.53 added ctx and fromPort params
*/
public Pinger(I2PAppContext ctx, int fromPort) {
this.thread = new I2PAppThread(this);
log = ctx.logManager().getLog(getClass());
this.fromPort = fromPort;
}
public void setSink(Sink sink) {
@ -22,7 +33,6 @@ public class Pinger implements Source, Runnable {
public void start() {
this.running = true;
//this.waitlock = new Object();
this.thread.start();
}
@ -35,9 +45,9 @@ public class Pinger implements Source, Runnable {
byte[] data = new byte[1];
data[0] = 1;
try {
this.sink.send(null, data);
this.sink.send(null, fromPort, 0, data);
if (log.shouldDebug())
log.debug("Sent unsubscribe");
log.debug("Sent unsubscribe from port " + fromPort);
} catch (RuntimeException re) {}
}
@ -47,11 +57,10 @@ public class Pinger implements Source, Runnable {
data[0] = 0;
int i = 0;
while(this.running) {
//System.out.print("p");
try {
this.sink.send(null, data);
this.sink.send(null, fromPort, 0, data);
if (log.shouldDebug())
log.debug("Sent subscribe");
log.debug("Sent subscribe from port " + fromPort);
} catch (RuntimeException re) {
if (log.shouldWarn())
log.warn("error sending", re);
@ -71,9 +80,4 @@ public class Pinger implements Source, Runnable {
}
}
}
protected Sink sink;
protected final Thread thread;
private final Object waitlock = new Object();
protected volatile boolean running;
}

View File

@ -24,11 +24,13 @@ public class StreamrConsumer extends I2PTunnelUDPClientBase {
super(destination, l, notifyThis, tunnel);
// create udp-destination
this.sink = new UDPSink(host, port);
UDPSink udps = new UDPSink(host, port);
int localPort = udps.getPort();
this.sink = udps;
setSink(this.sink);
// create pinger
this.pinger = new Pinger();
this.pinger = new Pinger(_context, localPort);
this.pinger.setSink(this);
}

View File

@ -21,8 +21,7 @@ public class StreamrProducer extends I2PTunnelUDPServerBase {
public StreamrProducer(int port,
File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis, I2PTunnel tunnel) {
// verify subscription requests
super(true, privkey, privkeyname, l, notifyThis, tunnel);
super(privkey, privkeyname, l, notifyThis, tunnel);
// The broadcaster
this.multi = new MultiSource();

View File

@ -20,7 +20,7 @@ public class Subscriber implements Sink {
private final I2PAppContext ctx = I2PAppContext.getGlobalContext();
private final Log log = ctx.logManager().getLog(getClass());
private final Map<Destination, Long> subscriptions;
private final Map<MultiSource.MSink, Long> subscriptions;
private final MultiSource multi;
private final SimpleTimer2.TimedEvent timer;
private volatile boolean timerRunning;
@ -31,7 +31,7 @@ public class Subscriber implements Sink {
public Subscriber(MultiSource multi) {
this.multi = multi;
// subscriptions
this.subscriptions = new ConcurrentHashMap<Destination, Long>();
this.subscriptions = new ConcurrentHashMap<MultiSource.MSink, Long>();
timer = new Expire();
}
@ -40,44 +40,47 @@ public class Subscriber implements Sink {
*
* @param dest to subscribe or unsubscribe
* @param data must be a single byte, 0 to subscribe, 1 to unsubscribe
* @since 0.9.53 added fromPort and toPort parameters
*/
public void send(Destination dest, byte[] data) {
public void send(Destination dest, int fromPort, int toPort, byte[] data) {
if(dest == null || data.length < 1) {
// invalid packet
if (log.shouldWarn())
log.warn("bad subscription from " + dest);
log.warn("bad subscription from " + dest.toBase32() + ':' + fromPort);
} else {
byte ctrl = data[0];
// swap fromPort and toPort for the replies
MultiSource.MSink ms = new MultiSource.MSink(dest, toPort, fromPort);
int ctrl = data[0] & 0xff;
if(ctrl == 0) {
if (this.subscriptions.put(dest, Long.valueOf(ctx.clock().now())) == null) {
if (this.subscriptions.put(ms, Long.valueOf(ctx.clock().now())) == null) {
if (subscriptions.size() > MAX_SUBSCRIPTIONS) {
subscriptions.remove(dest);
if (log.shouldWarn())
log.warn("Too many subscriptions, denying: " + dest.toBase32());
log.warn("Too many subscriptions, denying: " + ms);
return;
}
// subscribe
if (log.shouldWarn())
log.warn("Add subscription: " + dest.toBase32());
this.multi.add(dest);
log.warn("Add subscription: " + ms);
this.multi.add(ms);
if (!timerRunning) {
timer.reschedule(EXPIRATION);
timerRunning = true;
}
} else {
if (log.shouldInfo())
log.info("Continue subscription: " + dest.toBase32());
log.info("Continue subscription: " + ms);
}
} else if(ctrl == 1) {
// unsubscribe
if (log.shouldWarn())
log.warn("Remove subscription: " + dest.toBase32());
if (subscriptions.remove(dest) != null)
multi.remove(dest);
log.warn("Remove subscription: " + ms);
if (subscriptions.remove(ms) != null)
multi.remove(ms);
} else {
// invalid packet
if (log.shouldWarn())
log.warn("bad subscription from " + dest);
log.warn("bad subscription flag " + ctrl + " from " + ms);
}
}
}
@ -95,15 +98,15 @@ public class Subscriber implements Sink {
return;
}
long exp = ctx.clock().now() - EXPIRATION;
for (Iterator<Map.Entry<Destination, Long>> iter = subscriptions.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<Destination, Long> e = iter.next();
for (Iterator<Map.Entry<MultiSource.MSink, Long>> iter = subscriptions.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<MultiSource.MSink, Long> e = iter.next();
long then = e.getValue().longValue();
if (then < exp) {
Destination dest = e.getKey();
MultiSource.MSink ms = e.getKey();
iter.remove();
multi.remove(dest);
multi.remove(ms);
if (log.shouldWarn())
log.warn("Expired subscription: " + dest.toBase32());
log.warn("Expired subscription: " + ms);
}
}
if (!subscriptions.isEmpty()) {

View File

@ -14,14 +14,39 @@ import net.i2p.client.datagram.I2PDatagramMaker;
*/
public class I2PSink implements Sink {
protected final boolean raw;
protected final I2PSession sess;
protected final Destination dest;
protected final I2PDatagramMaker maker;
/**
* @since 0.9.53
*/
protected final int toPort;
/**
* repliable (not raw)
*/
public I2PSink(I2PSession sess, Destination dest) {
this(sess, dest, false);
}
/**
* @param raw false for repliable
*/
public I2PSink(I2PSession sess, Destination dest, boolean raw) {
this(sess, dest, raw, I2PSession.PORT_UNSPECIFIED);
}
/**
* @param raw false for repliable
* @param toPort I2CP destination port, 0-65535
* @since 0.9.53
*/
public I2PSink(I2PSession sess, Destination dest, boolean raw, int toPort) {
this.sess = sess;
this.dest = dest;
this.raw = raw;
this.toPort = toPort;
// create maker
if (raw) {
@ -34,31 +59,30 @@ public class I2PSink implements Sink {
/**
* @param src ignored
* @param fromPort I2CP port
* @param ign_toPort ignored
* @since 0.9.53 added fromPort and toPort parameters, breaking change, sorry
* @throws RuntimeException if session is closed
*/
public synchronized void send(Destination src, byte[] data) {
public synchronized void send(Destination src, int fromPort, int ign_toPort, byte[] data) {
//System.out.print("w");
// create payload
byte[] payload;
if(!this.raw) {
if (!this.raw) {
synchronized(this.maker) {
payload = this.maker.makeI2PDatagram(data);
}
} else
} else {
payload = data;
}
// send message
try {
this.sess.sendMessage(this.dest, payload,
(this.raw ? I2PSession.PROTO_DATAGRAM_RAW : I2PSession.PROTO_DATAGRAM),
I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
fromPort, toPort);
} catch (I2PSessionException ise) {
throw new RuntimeException("failed to send data", ise);
}
}
protected final boolean raw;
protected final I2PSession sess;
protected final Destination dest;
protected final I2PDatagramMaker maker;
}

View File

@ -14,6 +14,10 @@ import net.i2p.client.datagram.I2PDatagramMaker;
*/
public class I2PSinkAnywhere implements Sink {
protected final boolean raw;
protected final I2PSession sess;
protected final I2PDatagramMaker maker;
public I2PSinkAnywhere(I2PSession sess) {
this(sess, false);
}
@ -35,27 +39,35 @@ public class I2PSinkAnywhere implements Sink {
* @param to - where it's going
* @throws RuntimeException if session is closed
*/
public synchronized void send(Destination to, byte[] data) {
public void send(Destination to, byte[] data) {
send(to, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED, data);
}
/**
* @param to - where it's going
* @param fromPort I2CP port 0 - 65535
* @param toPort I2CP port 0 - 65535
* @since 0.9.53
* @throws RuntimeException if session is closed
*/
public synchronized void send(Destination to, int fromPort, int toPort, byte[] data) {
// create payload
byte[] payload;
if(!this.raw) {
synchronized(this.maker) {
payload = this.maker.makeI2PDatagram(data);
}
} else
} else {
payload = data;
}
// send message
try {
this.sess.sendMessage(to, payload,
(this.raw ? I2PSession.PROTO_DATAGRAM_RAW : I2PSession.PROTO_DATAGRAM),
I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
fromPort, toPort);
} catch (I2PSessionException ise) {
throw new RuntimeException("failed to send data", ise);
}
}
protected final boolean raw;
protected final I2PSession sess;
protected final I2PDatagramMaker maker;
}

View File

@ -1,94 +1,105 @@
package net.i2p.i2ptunnel.udp;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionListener;
import net.i2p.client.I2PSessionMuxedListener;
import net.i2p.client.datagram.I2PDatagramDissector;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
/**
* Refactored in 0.9.53 to support I2CP protocols and ports
*
* @author welterde
*/
public class I2PSource implements Source, Runnable {
public class I2PSource implements Source {
protected final I2PSession sess;
protected Sink sink;
private final Protocol protocol;
private final int port;
private final I2PDatagramDissector diss;
private final Log log;
/**
* @since 0.9.53
*/
public enum Protocol { REPLIABLE, RAW, BOTH }
/**
* Handles both REPLIABLE and RAW on any port
*/
public I2PSource(I2PSession sess) {
this(sess, true, false);
this(sess, Protocol.BOTH);
}
public I2PSource(I2PSession sess, boolean verify) {
this(sess, verify, false);
/**
* Listen on all I2CP ports.
* No support for arbitrary protocol numbers.
*
* @param protocol REPLIABLE, RAW, or BOTH
* @since 0.9.53
*/
public I2PSource(I2PSession sess, Protocol protocol) {
this(sess, protocol, I2PSession.PORT_ANY);
}
public I2PSource(I2PSession sess, boolean verify, boolean raw) {
/**
* @param port I2CP port or I2PSession.PORT_ANY
* @param protocol REPLIABLE, RAW, or BOTH
* @since 0.9.53
*/
public I2PSource(I2PSession sess, Protocol protocol, int port) {
this.sess = sess;
this.verify = verify;
this.raw = raw;
// create queue
this.queue = new ArrayBlockingQueue<Integer>(256);
// create listener
this.sess.setSessionListener(new Listener());
// create thread
this.thread = new I2PAppThread(this);
this.protocol = protocol;
this.port = port;
diss = (protocol != Protocol.RAW) ? new I2PDatagramDissector() : null;
log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
}
public void setSink(Sink sink) {
this.sink = sink;
}
public void start() {
this.thread.start();
// create listener
Listener l = new Listener();
if (protocol != Protocol.RAW)
sess.addMuxedSessionListener(l, I2PSession.PROTO_DATAGRAM, port);
if (protocol != Protocol.REPLIABLE)
sess.addMuxedSessionListener(l, I2PSession.PROTO_DATAGRAM_RAW, port);
}
public void run() {
// create dissector
I2PDatagramDissector diss = new I2PDatagramDissector();
_running = true;
while (_running) {
protected class Listener implements I2PSessionMuxedListener {
public void messageAvailable(I2PSession sess, int id, long size) {
throw new IllegalStateException("muxed");
}
/**
* @since 0.9.53
*/
public void messageAvailable(I2PSession session, int id, long size, int proto, int fromPort, int toPort) {
if (log.shouldDebug())
log.debug("Got " + size + " bytes, proto: " + proto + " from port: " + fromPort + " to port: " + toPort);
try {
// get id
int id = this.queue.take();
// receive message
byte[] msg = this.sess.receiveMessage(id);
if(!this.raw) {
byte[] msg = session.receiveMessage(id);
if (proto == I2PSession.PROTO_DATAGRAM) {
// load datagram into it
diss.loadI2PDatagram(msg);
// now call sink
if(this.verify)
this.sink.send(diss.getSender(), diss.getPayload());
else
this.sink.send(diss.extractSender(), diss.extractPayload());
sink.send(diss.getSender(), fromPort, toPort, diss.getPayload());
} else if (proto == I2PSession.PROTO_DATAGRAM_RAW) {
sink.send(null, fromPort, toPort, msg);
} else {
// verify is ignored
this.sink.send(null, msg);
if (log.shouldWarn())
log.warn("dropping message with unknown protocol " + proto);
}
//System.out.print("r");
} catch(Exception e) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
if (log.shouldWarn())
log.warn("error sending", e);
break;
}
}
}
protected class Listener implements I2PSessionListener {
public void messageAvailable(I2PSession sess, int id, long size) {
try {
queue.put(id);
} catch(Exception e) {
// ignore
log.warn("error receiving datagram", e);
}
}
@ -97,24 +108,11 @@ public class I2PSource implements Source, Runnable {
}
public void disconnected(I2PSession arg0) {
_running = false;
thread.interrupt();
}
public void errorOccurred(I2PSession arg0, String arg1, Throwable arg2) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
log.error(arg1, arg2);
_running = false;
thread.interrupt();
}
}
protected final I2PSession sess;
protected final BlockingQueue<Integer> queue;
protected Sink sink;
protected final Thread thread;
protected final boolean verify;
protected final boolean raw;
private volatile boolean _running;
}

View File

@ -8,8 +8,11 @@ import net.i2p.data.Destination;
*/
public interface Sink {
/**
* @param src some implementations may ignore
* @param fromPort I2CP source port, 0-65535
* @param toPort I2CP destination port, 0-65535
* @param src some implementations may ignore, may be null in some implementations
* @since 0.9.53 added fromPort and toPort parameters, breaking change, sorry
* @throws RuntimeException in some implementations
*/
public void send(Destination src, byte[] data);
public void send(Destination src, int fromPort, int toPort, byte[] data);
}

View File

@ -13,7 +13,13 @@ import net.i2p.data.Destination;
*/
public class UDPSink implements Sink {
protected final DatagramSocket sock;
protected final InetAddress remoteHost;
protected final int remotePort;
/**
* @param host where to send
* @param port where to send
* @throws IllegalArgumentException on DatagramSocket IOException
*/
public UDPSink(InetAddress host, int port) {
@ -23,18 +29,31 @@ public class UDPSink implements Sink {
} catch (IOException e) {
throw new IllegalArgumentException("failed to open udp-socket", e);
}
this.remoteHost = host;
this.remotePort = port;
}
// remote port
/**
* @param socket existing socket
* @param host where to send
* @param port where to send
* @since 0.9.53
*/
public UDPSink(DatagramSocket socket, InetAddress host, int port) {
sock = socket;
this.remoteHost = host;
this.remotePort = port;
}
/**
* @param src ignored
* @param fromPort ignored
* @param toPort ignored
* @since 0.9.53 added fromPort and toPort parameters, breaking change, sorry
* @throws RuntimeException on DatagramSocket IOException
*/
public void send(Destination src, byte[] data) {
public void send(Destination src, int fromPort, int toPort, byte[] data) {
// if data.length > this.sock.getSendBufferSize() ...
// create packet
@ -48,6 +67,9 @@ public class UDPSink implements Sink {
}
}
/**
* @return the local port of the DatagramSocket we are sending from
*/
public int getPort() {
return this.sock.getLocalPort();
}
@ -60,9 +82,4 @@ public class UDPSink implements Sink {
public void stop() {
this.sock.close();
}
protected final DatagramSocket sock;
protected final InetAddress remoteHost;
protected final int remotePort;
}

View File

@ -13,6 +13,10 @@ import net.i2p.util.Log;
* @author welterde
*/
public class UDPSource implements Source, Runnable {
protected final DatagramSocket sock;
protected Sink sink;
protected final Thread thread;
private final int port;
public static final int MAX_SIZE = 15360;
/**
@ -25,7 +29,7 @@ public class UDPSource implements Source, Runnable {
} catch (IOException e) {
throw new RuntimeException("failed to listen...", e);
}
this.port = port;
// create thread
this.thread = new I2PAppThread(this);
}
@ -33,6 +37,7 @@ public class UDPSource implements Source, Runnable {
/** use socket from UDPSink */
public UDPSource(DatagramSocket sock) {
this.sock = sock;
port = sock.getLocalPort();
this.thread = new I2PAppThread(this);
}
@ -60,7 +65,7 @@ public class UDPSource implements Source, Runnable {
System.arraycopy(pack.getData(), 0, nbuf, 0, nbuf.length);
// transfer to sink
this.sink.send(null, nbuf);
this.sink.send(null, port, 0, nbuf);
//System.out.print("i");
} catch(Exception e) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
@ -71,11 +76,15 @@ public class UDPSource implements Source, Runnable {
}
}
/**
* @return the local port of the DatagramSocket we are receiving on
* @since 0.9.53
*/
public int getPort() {
return port;
}
public void stop() {
this.sock.close();
}
protected final DatagramSocket sock;
protected Sink sink;
protected final Thread thread;
}

View File

@ -12,6 +12,7 @@ import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.crypto.SigType;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
@ -57,7 +58,6 @@ import net.i2p.util.EventDispatcher;
private final I2PSession _session;
private final Source _i2pSource;
private final Sink _i2pSink;
private final Destination _otherDest;
/**
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
@ -103,19 +103,19 @@ import net.i2p.util.EventDispatcher;
throw new RuntimeException("failed to create session", exc);
}
// Setup the source. Always expect raw unverified datagrams.
_i2pSource = new I2PSource(_session, false, true);
// Setup the source. Handle both repliable and raw datagrams, on all ports.
_i2pSource = new I2PSource(_session, I2PSource.Protocol.BOTH);
// Setup the sink. Always send repliable datagrams.
if (destination != null && destination.length() > 0) {
_otherDest = _context.namingService().lookup(destination);
if (_otherDest == null) {
I2PSocketAddress addr = new I2PSocketAddress(destination);
if (addr.isUnresolved()) {
// unlike in I2PTunnelClient, we don't defer and retry resolution later
l.log("Could not resolve " + destination);
throw new RuntimeException("failed to create session - could not resolve " + destination);
}
_i2pSink = new I2PSink(_session, _otherDest, false);
}
_i2pSink = new I2PSink(_session, addr.getAddress(), false, addr.getPort());
} else {
_otherDest = null;
_i2pSink = new I2PSinkAnywhere(_session, false);
}
}
@ -178,10 +178,11 @@ import net.i2p.util.EventDispatcher;
* Sink Methods
*
* @param to - ignored if configured for a single destination
* (we use the dest specified in the constructor)
* (we use the dest specified in the constructor)
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException if session is closed
*/
public void send(Destination to, byte[] data) {
_i2pSink.send(to, data);
public void send(Destination to, int fromPort, int toPort, byte[] data) {
_i2pSink.send(to, fromPort, toPort, data);
}
}

View File

@ -67,15 +67,14 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin
* badly that we cant create a socketManager
*
*/
public I2PTunnelUDPServerBase(boolean verify, File privkey, String privkeyname, Logging l,
public I2PTunnelUDPServerBase(File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis, I2PTunnel tunnel) {
super("UDPServer <- " + privkeyname, notifyThis, tunnel);
_log = tunnel.getContext().logManager().getLog(I2PTunnelUDPServerBase.class);
FileInputStream fis = null;
try {
fis = new FileInputStream(privkey);
init(verify, fis, privkeyname, l);
init(fis, privkeyname, l);
} catch (IOException ioe) {
_log.error("Error starting server", ioe);
notifyEvent("openServerResult", "error");
@ -85,7 +84,7 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin
}
}
private void init(boolean verify, InputStream privData, String privkeyname, Logging l) {
private void init(InputStream privData, String privkeyname, Logging l) {
this.l = l;
// create i2pclient
@ -99,8 +98,8 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin
throw new RuntimeException("failed to create session", exc);
}
// Setup the source. Always expect repliable datagrams, optionally verify
_i2pSource = new I2PSource(_session, verify, false);
// Setup the source. Always expect repliable datagrams, listen on all ports.
_i2pSource = new I2PSource(_session, I2PSource.Protocol.REPLIABLE);
// Setup the sink. Always send raw datagrams.
_i2pSink = new I2PSinkAnywhere(_session, true);
@ -188,11 +187,12 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin
* Sink Methods
*
* @param to
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException if session is closed
*
*/
public void send(Destination to, byte[] data) {
_i2pSink.send(to, data);
public void send(Destination to, int fromPort, int toPort, byte[] data) {
_i2pSink.send(to, fromPort, toPort, data);
}
}

View File

@ -2,7 +2,9 @@ package net.i2p.i2ptunnel.ui;
import java.io.File;
import java.io.IOException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -14,6 +16,7 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.crypto.SigType;
import net.i2p.data.Base32;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
@ -855,8 +858,35 @@ public class GeneralHelper {
return 0;
}
/**
* @return entries sorted, converted to b32, separated by newlines, or ""
*/
public String getAccessList(int tunnel) {
return getProperty(tunnel, "i2cp.accessList", "").replace(",", "\n");
String val = getProperty(tunnel, "i2cp.accessList", "");
if (val.length() > 0) {
// Convert B64 to B32 for display
String[] vals = DataHelper.split(val, ",");
for (int i = 0; i < vals.length; i++) {
String v = vals[i];
if (v.length() == 44) {
byte[] b = Base64.decode(v);
if (b != null)
vals[i] = Base32.encode(b) + ".b32.i2p";
}
}
Arrays.sort(vals, Collator.getInstance());
StringBuilder buf = new StringBuilder(val.length() * 3 / 2);
for (int i = 0; i < vals.length; i++) {
String v = vals[i];
if (v.length() == 0)
continue;
buf.append(vals[i]);
if (i != vals.length - 1)
buf.append('\n');
}
val = buf.toString();
}
return val;
}
/**

View File

@ -19,6 +19,7 @@ import net.i2p.crypto.EncType;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.KeyPair;
import net.i2p.crypto.SigType;
import net.i2p.data.Base32;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
@ -466,8 +467,27 @@ public class TunnelConfig {
}
public void setAccessList(String val) {
if (val != null)
_otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
if (val != null) {
val = val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ",");
// Convert to B64 to save space
String[] vals = DataHelper.split(val, ",");
StringBuilder buf = new StringBuilder(val.length());
for (int i = 0; i < vals.length; i++) {
String v = vals[i];
int len = v.length();
if (len == 0)
continue;
if (len == 60 && v.endsWith(".b32.i2p")) {
byte[] b = Base32.decode(v.substring(0, 52));
if (b != null)
v = Base64.encode(b);
}
buf.append(v);
if (i != vals.length - 1)
buf.append(',');
}
_otherOptions.put("i2cp.accessList", buf.toString());
}
}
public void setJumpList(String val) {

View File

@ -609,12 +609,12 @@
</tr><tr>
<td colspan="2">
<span class="multiOption" <%=ehdisabled%>>
<label><input value="0" type="radio" id="startOnLoad" name="encType" <%=(has0 ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
<label><input value="0" type="radio" id="startOnLoad" name="encType" <%=((has0 && !has4) ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
ElGamal-2048</label>
</span>
<span class="multiOption" <%=ehdisabled%>>
<label><input value="4" type="radio" id="startOnLoad" name="encType" <%=(has4 ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
ECIES-X25519 (<%=intl._t("Experts only!")%>)</label>
<label><input value="4" type="radio" id="startOnLoad" name="encType" <%=((has4 && !has0) ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
ECIES-X25519</label>
</span>
<span class="multiOption" <%=ehdisabled%>>
<label><input value="4,0" type="radio" id="startOnLoad" name="encType" <%=((has0 && has4) ? " checked=\"checked\"" : edisabled)%> class="tickbox" />

View File

@ -701,12 +701,12 @@
</tr><tr>
<td colspan="2">
<span class="multiOption" <%=ehdisabled%>>
<label><input value="0" type="radio" id="startOnLoad" name="encType" <%=(has0 ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
<label><input value="0" type="radio" id="startOnLoad" name="encType" <%=((has0 && !has4) ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
ElGamal-2048</label>
</span>
<span class="multiOption" <%=ehdisabled%>>
<label><input value="4" type="radio" id="startOnLoad" name="encType" <%=(has4 ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
ECIES-X25519 (<%=intl._t("Experts only!")%>)</label>
<label><input value="4" type="radio" id="startOnLoad" name="encType" <%=((has4 && !has0) ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
ECIES-X25519</label>
</span>
<span class="multiOption" <%=ehdisabled%>>
<label><input value="4,0" type="radio" id="startOnLoad" name="encType" <%=((has0 && has4) ? " checked=\"checked\"" : edisabled)%> class="tickbox" />
@ -911,7 +911,7 @@
</th>
</tr><tr>
<th colspan="5">
<%=intl._t("Inbound connection limits (0=unlimited)")%>
<%=intl._t("Inbound connection limits")%>
</th>
</tr><tr>
<td></td>
@ -921,27 +921,30 @@
<td class="blankColumn"></td>
</tr><tr>
<td><b><%=intl._t("Per Client")%></b></td>
<%
String unlimited = " (0 = " + intl._t("unlimited") + ')';
%>
<td>
<input type="text" name="limitMinute" title="<%=intl._t("Maximum number of web page requests per minute for a unique client before access to the server is blocked")%>" value="<%=editBean.getLimitMinute(curTunnel)%>" class="freetext" />
<input type="text" name="limitMinute" title="<%=intl._t("Maximum number of web page requests per minute for a unique client before access to the server is blocked") + unlimited %>" value="<%=editBean.getLimitMinute(curTunnel)%>" class="freetext" />
</td><td>
<input type="text" name="limitHour" title="<%=intl._t("Maximum number of web page requests per hour for a unique client before access to the server is blocked")%>" value="<%=editBean.getLimitHour(curTunnel)%>" class="freetext" />
<input type="text" name="limitHour" title="<%=intl._t("Maximum number of web page requests per hour for a unique client before access to the server is blocked") + unlimited %>" value="<%=editBean.getLimitHour(curTunnel)%>" class="freetext" />
</td><td>
<input type="text" name="limitDay" title="<%=intl._t("Maximum number of web page requests per day for a unique client before access to the server is blocked")%>" value="<%=editBean.getLimitDay(curTunnel)%>" class="freetext" />
<input type="text" name="limitDay" title="<%=intl._t("Maximum number of web page requests per day for a unique client before access to the server is blocked") + unlimited %>" value="<%=editBean.getLimitDay(curTunnel)%>" class="freetext" />
</td><td class="blankColumn"></td>
</tr><tr>
<td><b><%=intl._t("Total")%></b></td>
<td>
<input type="text" name="totalMinute" title="<%=intl._t("Total number of web page requests per minute before access to the server is blocked")%>" value="<%=editBean.getTotalMinute(curTunnel)%>" class="freetext" />
<input type="text" name="totalMinute" title="<%=intl._t("Total number of web page requests per minute before access to the server is blocked") + unlimited %>" value="<%=editBean.getTotalMinute(curTunnel)%>" class="freetext" />
</td><td>
<input type="text" name="totalHour" title="<%=intl._t("Total number of web page requests per hour before access to the server is blocked")%>" value="<%=editBean.getTotalHour(curTunnel)%>" class="freetext" />
<input type="text" name="totalHour" title="<%=intl._t("Total number of web page requests per hour before access to the server is blocked") + unlimited %>" value="<%=editBean.getTotalHour(curTunnel)%>" class="freetext" />
</td><td>
<input type="text" name="totalDay" title="<%=intl._t("Total number of web page requests per day before access to the server is blocked")%>" value="<%=editBean.getTotalDay(curTunnel)%>" class="freetext" />
<input type="text" name="totalDay" title="<%=intl._t("Total number of web page requests per day before access to the server is blocked") + unlimited %>" value="<%=editBean.getTotalDay(curTunnel)%>" class="freetext" />
</td><td class="blankColumn"></td>
</tr><tr>
<th colspan="5"><%=intl._t("Max concurrent connections (0=unlimited)")%></th>
<th colspan="5"><%=intl._t("Max concurrent connections")%></th>
</tr><tr>
<td></td><td>
<input type="text" name="maxStreams" title="<%=intl._t("Maximum number of simultaneous client connections")%>" value="<%=editBean.getMaxStreams(curTunnel)%>" class="freetext" />
<input type="text" name="maxStreams" title="<%=intl._t("Maximum number of simultaneous client connections") + unlimited %>" value="<%=editBean.getMaxStreams(curTunnel)%>" class="freetext" />
</td><td></td><td></td><td class="blankColumn"></td>
</tr>
<%
@ -949,7 +952,7 @@
%>
<tr>
<th colspan="5">
<%=intl._t("POST limits (0=unlimited)")%>
<%=intl._t("POST limits")%>
</th>
</tr><tr>
<td></td><td>
@ -962,7 +965,7 @@
<b><%=intl._t("Per Client")%>
</b>
</td><td>
<input type="text" name="postMax" title="<%=intl._t("Maximum number of post requests permitted for a unique client for the configured time span")%>" value="<%=editBean.getPostMax(curTunnel)%>" class="freetext quantity"/>
<input type="text" name="postMax" title="<%=intl._t("Maximum number of post requests permitted for a unique client for the configured time span") + unlimited %>" value="<%=editBean.getPostMax(curTunnel)%>" class="freetext quantity"/>
</td><td colspan="2">
<input type="text" name="postBanTime" title="<%=intl._t("If a client exceeds the maximum number of post requests per allocated period, enforce a ban for this number of minutes")%>" value="<%=editBean.getPostBanTime(curTunnel)%>" class="freetext period"/>
<%=intl._t("minutes")%>
@ -972,7 +975,7 @@
<b><%=intl._t("Total")%>
</b>
</td><td>
<input type="text" name="postTotalMax" title="<%=intl._t("Total number of post requests permitted for the configured time span")%>" value="<%=editBean.getPostTotalMax(curTunnel)%>" class="freetext quantity"/>
<input type="text" name="postTotalMax" title="<%=intl._t("Total number of post requests permitted for the configured time span") + unlimited %>" value="<%=editBean.getPostTotalMax(curTunnel)%>" class="freetext quantity"/>
</td><td colspan="2">
<input type="text" name="postTotalBanTime" title="<%=intl._t("If the maximum number of post requests per allocated period is exceeded, enforce a global access ban for this number of minutes")%>" value="<%=editBean.getPostTotalBanTime(curTunnel)%>" class="freetext period"/>
<%=intl._t("minutes")%>

View File

@ -47,7 +47,7 @@ form {
}
input[type="checkbox"], input[type="radio"] {
margin: 5px 3px 5px 5px;
margin: 4px 5px 6px;
background: none;
vertical-align: sub;
min-width: 16px;
@ -216,7 +216,7 @@ hr {
box-sizing: border-box;
margin: 2px 4px !important;
min-width: 70px !important;
padding: 7px 8px 3px;
padding: 5px 8px 5px;
}
.control:hover, .control:focus {

View File

@ -4,16 +4,17 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# タカハシ, 2022
# Masayuki Hatta <mhatta@mhatta.org>, 2018
# XMPPはいいぞ, 2021
# XMPPはいいぞ, 2021
# daingewuvzeevisiddfddd, 2021
# daingewuvzeevisiddfddd, 2021
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-05 14:31+0000\n"
"PO-Revision-Date: 2021-05-14 01:18+0000\n"
"Last-Translator: XMPPはいいぞ\n"
"PO-Revision-Date: 2022-02-14 11:11+0000\n"
"Last-Translator: タカハシ\n"
"Language-Team: Japanese (http://www.transifex.com/otf/I2P/language/ja/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -554,7 +555,7 @@ msgid ""
"This address will be saved to your Local address book. Select this option "
"for addresses you wish to keep separate from the main router address book, "
"but don't mind publishing."
msgstr ""
msgstr "このアドレスはローカルのアドレス帳に保存されます。メインのルーターアドレス帳とは別に保管したいが、公開しても構わないアドレスに対してこのオプションを選択してください。"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1478
#, java-format
@ -569,7 +570,7 @@ msgstr "このアドレスはプライベートなアドレス帳へ保存され
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1507
msgid "Base32 address requires lookup password"
msgstr ""
msgstr "Base32のアドレスにはルックアップパスワードが必要です"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1509
msgid "Base32 address requires encryption key"
@ -577,7 +578,7 @@ msgstr "Base32 アドレスは暗号化鍵を要求します"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1511
msgid "Base32 address requires encryption key and lookup password"
msgstr ""
msgstr "Base32のアドレスには暗号化キーとルックアップパスワードが必要です"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1513
msgid "Base32 address decryption failure, check encryption key"
@ -611,7 +612,7 @@ msgstr "新しいDH暗号化鍵を生成"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1541
msgid "Lookup password"
msgstr ""
msgstr "ルックアップパスワード"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1542
msgid "You must enter the password provided by the server operator."
@ -628,7 +629,7 @@ msgstr "アドレスヘルパー経由で追加"
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:235
msgid "Missing lookup password"
msgstr ""
msgstr "ルックアップパスワードがありません"
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:249
msgid "Missing private key"

View File

@ -9,7 +9,7 @@
# cacapo <handelsehorisont@gmail.com>, 2015-2016
# hottuna <i2p@robertfoss.se>, 2013
# hottuna <i2p@robertfoss.se>, 2012
# Jonatan Nyberg <jonatan@autistici.org>, 2016-2017,2021
# Jonatan Nyberg <jonatan@autistici.org>, 2016-2017,2021-2022
# efef6ec5b435a041fce803c7f8af77d2_2341d43, 2019-2020
# efef6ec5b435a041fce803c7f8af77d2_2341d43, 2017
# WinterFairy <winterfairy@riseup.net>, 2014
@ -18,7 +18,7 @@ msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-05 14:31+0000\n"
"PO-Revision-Date: 2021-06-23 09:38+0000\n"
"PO-Revision-Date: 2022-01-14 17:57+0000\n"
"Last-Translator: Jonatan Nyberg <jonatan@autistici.org>\n"
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
"MIME-Version: 1.0\n"
@ -116,7 +116,7 @@ msgstr "Webbplatsen var inte nåbar."
msgid ""
"The website is offline, there is network congestion, or your router is not "
"yet well-integrated with peers."
msgstr "Webbplatsen är frånkopplad, nätverket är under stor belastning eller så är din router inte välintegrerad med noder."
msgstr "Webbplatsen är frånkopplad, nätverket är under stor belastning, eller din router är ännu inte välintegrerad med jämlikar."
#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:58
#: ../java/build/Proxy.java:109 ../java/build/Proxy.java:129
@ -130,7 +130,7 @@ msgstr "Du kanske vill {0}försöka igen{1}."
#: ../java/build/Proxy.java:118 ../java/build/Proxy.java:130
#: ../java/build/Proxy.java:171 ../java/build/Proxy.java:196
msgid "Could not find the following destination:"
msgstr "Kunde inte hitta följande mål:"
msgstr "Det gick inte att hitta följande destination:"
#: ../java/build/Proxy.java:16 ../java/build/Proxy.java:22
#: ../java/build/Proxy.java:160 ../java/build/Proxy.java:166
@ -205,7 +205,7 @@ msgstr "Varning: Ogiltigt Mål"
#: ../java/build/Proxy.java:47
msgid "The Base32 address is invalid."
msgstr ""
msgstr "Base32-adressen är ogiltig."
#: ../java/build/Proxy.java:56
msgid "The website was not reachable, because its lease set was not found."
@ -315,7 +315,7 @@ msgstr ""
msgid ""
"The website destination specified was not valid, or was otherwise "
"unreachable."
msgstr "Webbplatsens angivna destination var inte giltig, eller på annat sätt onåbar. "
msgstr "Den angivna webbplatsdestinationen var inte giltig eller på annat sätt onåbar."
#: ../java/build/Proxy.java:127
msgid ""
@ -335,7 +335,7 @@ msgstr "Varning: Ingen utproxy inställd"
msgid ""
"Your request was for a site outside of I2P, but you have no HTTP outproxy "
"configured."
msgstr "Din förfrågan var för en sida utanför I2P, men du har ingen HTTP utproxy inställd."
msgstr "Din förfrågan gällde en webbplats utanför I2P, men du har ingen HTTP-utproxy konfigurerad."
#: ../java/build/Proxy.java:139
msgid "Please configure an outproxy in I2PTunnel."
@ -362,7 +362,7 @@ msgid ""
"Resolve the conflict by deciding which key you trust, and then either ignore"
" the address helper link, or delete the host entry from your address book "
"and click the address helper link again."
msgstr "Lös konflikten genom att välja vilken nyckel du litar på. Sedan kan du antingen ignorera adressens hjälplänk eller radera posten från din adressbok och klicka på adressens hjälplänk igen. "
msgstr "Lös konflikten genom att bestämma vilken nyckel du litar på och ignorera sedan länken för adresshjälp eller ta bort värdposten från din adressbok och klicka på länken för adresshjälp igen."
#: ../java/build/Proxy.java:150 ../java/build/Proxy.java:156
msgid "Warning: Bad Address Helper"
@ -391,7 +391,7 @@ msgstr "HTTP-utproxyn hittades inte."
msgid ""
"It is offline, there is network congestion, or your router is not yet well-"
"integrated with peers."
msgstr "Den är antingen frånkopplad, nätverket är under stor belastning eller så är din router ännu inte väl integrerad med noder."
msgstr "Den är antingen frånkopplad, nätverket är under stor belastning eller så är din router ännu inte väl integrerad med jämlikar."
#: ../java/build/Proxy.java:172 ../java/build/Proxy.java:178
msgid "Warning: Request Denied"
@ -399,7 +399,7 @@ msgstr "Varning: Förfrågan Nekad"
#: ../java/build/Proxy.java:179
msgid "You attempted to connect to a non-I2P website or location."
msgstr "Du försökte ansluta till en icke-I2P webbplats eller plats."
msgstr "Du försökte ansluta till en icke-I2P-webbplats eller plats."
#: ../java/build/Proxy.java:180
msgid "Proxy Authorization Required"
@ -467,7 +467,7 @@ msgstr "HTTP-proxyn kunde inte nås eftersom den använder krypteringsinställni
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:692
msgid "This seems to be a bad destination:"
msgstr "Detta verkar vara ett felaktigt mål"
msgstr "Det här verkar vara en dåligt destination:"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:693
msgid "i2paddresshelper cannot help you with a destination like that!"
@ -529,7 +529,7 @@ msgstr "Fortsätt utan att spara"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1460
#, java-format
msgid "Save {0} to router address book and continue to website"
msgstr "Spara {0} till routerns adressbok och fortsätt till webbplatsen."
msgstr "Spara {0} i routerns adressbok och fortsätt till webbplatsen"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1461
msgid ""
@ -565,7 +565,7 @@ msgstr ""
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1478
#, java-format
msgid "Save {0} to private address book and continue to website"
msgstr "Spara {0} till privata adressboken och fortsätt till webbplatsen."
msgstr "Spara {0} i den privata adressboken och fortsätt till webbplatsen"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1479
msgid ""
@ -626,7 +626,7 @@ msgstr ""
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
#, java-format
msgid "Added via address helper from {0}"
msgstr "Tillagd via adresshjälpare från {0}"
msgstr "Tillagd via adresshjälp från {0}"
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:198
msgid "Added via address helper"
@ -701,7 +701,7 @@ msgstr ""
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:379
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:411
msgid "Click here if you are not redirected automatically."
msgstr "Klicka här om du inte omdirigeras automatiskt "
msgstr "Klicka här om du inte omdirigeras automatiskt."
#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:409
#, java-format

View File

@ -4,7 +4,7 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# Kaya Zeren <kayazeren@gmail.com>, 2016-2021
# Kaya Zeren <kayazeren@gmail.com>, 2016-2022
# Ozancan Karataş <ozancankaratas96@outlook.com>, 2015
# zzzi2p, 2018
msgid ""
@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-05 14:31+0000\n"
"PO-Revision-Date: 2021-02-15 04:27+0000\n"
"PO-Revision-Date: 2022-02-03 06:12+0000\n"
"Last-Translator: Kaya Zeren <kayazeren@gmail.com>\n"
"Language-Team: Turkish (Turkey) (http://www.transifex.com/otf/I2P/language/tr_TR/)\n"
"MIME-Version: 1.0\n"
@ -443,7 +443,7 @@ msgstr "İstek kötü bir iletişim kuralı kullanıyor."
#: ../java/build/Proxy.java:205
msgid "The I2P HTTP Proxy supports HTTP and HTTPS requests only."
msgstr "I2P HTTP Vekil Sunucusu yalnız HTTP ve HTTPS isteklerini destekler."
msgstr "I2P HTTP Vekil Sunucusu yalnızca HTTP ve HTTPS isteklerini destekler."
#: ../java/build/Proxy.java:206
msgid "Other protocols such as FTP are not allowed."

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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