Compare commits

...

839 Commits

Author SHA1 Message Date
zzz
f16e83f21b 0.9.2 2012-09-21 14:24:53 +00:00
ee66747def Fix after review and install on fresh osx (lession learned: test clean installs) 2012-09-17 20:33:03 +00:00
zzz
ccb8483766 * Build: Fix unpack problem on Java 5: http://forum.i2p/viewtopic.php?t=7334 2012-09-15 13:12:00 +00:00
zzz
b317eca5e3 * SSU: Fix shutdown NPE (ticket #709) 2012-09-14 13:50:06 +00:00
zzz
5ffacccdd7 update geoip license 2012-09-14 13:49:32 +00:00
zzz
a41936af94 typo 2012-09-14 13:49:09 +00:00
0991adc291 GeoIPdb update based on Maxmind GeoLite Country database from 2012-09-05 2012-09-13 16:15:09 +00:00
7820cef60a Czech language translation updates from transifex
(and updated en po files)
2012-09-12 12:04:43 +00:00
cb39006f6c minor corrections after review
The newly added OSX bits by meeh (thanks!) do not require Izpack to perform
substitutions, so I'm removing those files from the <parseable> tags.
2012-09-10 10:51:17 +00:00
52447096f2 Added a fix for a startup issue in newer versions of launchd. Setting Disabled
in configfile will only work on older launchd version. Ref manpage:
  (-w Overrides the Disabled key and sets it to false. In previous ver-
      sions, this option would modify the configuration file. Now the
      state of the Disabled key is stored elsewhere on-disk.)
2012-09-10 03:14:09 +00:00
5056706742 Added command scripts for osx to install i2p as a daemon in launchd.
.command extension make it possible to double click on it like bat files
in windows. Daemon is runned as the user who executes the command file.
2012-09-05 21:36:24 +00:00
zzz
9fd2f1e6a7 SSU: Fix some issues with queueing outbound establishments 2012-09-04 13:51:55 +00:00
zzz
b98474880d OutNetMessage: Speedup after profiling (ticket #707 - thx dg, kytv, zab) 2012-09-04 13:50:24 +00:00
zzz
5347d296dc log tweak 2012-09-04 13:46:52 +00:00
zzz
666a387d1b log fix 2012-09-04 13:46:31 +00:00
zzz
bb66e16b69 I2PTunnelServer: Clean shutdown after session exception 2012-09-04 13:46:10 +00:00
0ba3aad666 Disable i2jump.i2p from the tunnel wizard since it's been disabled elsewhere 2012-09-01 15:02:38 +00:00
zzz
a5e4b15349 add irc.killyourtv.i2p 2012-08-31 14:47:14 +00:00
zzz
7cc353ab04 javadoc 2012-08-31 14:36:53 +00:00
zzz
506626d6b1 i2psnark: Remove * from magnet and download names 2012-08-31 13:58:37 +00:00
zzz
26898f38ad Startup: Don't complain about clients.config missing on Android 2012-08-31 13:57:24 +00:00
zzz
4fdff1bf13 Router: Lengthen shutdown spinner life 2012-08-31 13:56:24 +00:00
7d4a6e74d2 Report no bugs in Android BitSet 2012-08-31 03:53:19 +00:00
b33a01cf26 use index instead of upper and lower, it's the same thing, but makes more sense. 2012-08-31 02:37:15 +00:00
a4511ca2ab typo fix 2012-08-29 19:17:40 +00:00
zzz
17b4ab6151 message cleanup 2012-08-29 14:16:19 +00:00
zzz
d2a7af2884 refactor trimmers to their own files 2012-08-29 14:05:02 +00:00
zzz
d05f1ca2c8 RandomIterator: Workaround for Android bug (ticket #703)
Include test case
2012-08-29 13:59:44 +00:00
zzz
832d66bfb9 NTCP: Reduce lock contention (ticket #697) 2012-08-29 13:47:05 +00:00
zzz
c8a46dac5d i2psnark:
- Add new flood-resistant KBucket trim policy
   - Limit received MsgID size
2012-08-29 13:45:29 +00:00
zzz
7005376061 - Fix NPE on destroy() if init() failed 2012-08-29 13:29:13 +00:00
zzz
ab213f45e2 cleanups 2012-08-29 13:28:03 +00:00
zzz
fa504ae8a3 - Fix cases where we weren't using the session for b32 lookup 2012-08-27 21:36:39 +00:00
zzz
d305eb6a9c * SSU:
- Limit UDPSender queue size
   - Increase UDPSender max packet lifetime
   - Clear UDPSender queue before sending destroys to all
   - Increase PeerState queue size so large streaming windows
     don't get dropped right away, especially at slow start
   - Various improvements on iterating over pending outbound
     messages in PeerState
2012-08-27 20:39:00 +00:00
zzz
f8bc6f8612 * Streaming: Limit amount of slow-start exponential growth 2012-08-27 20:36:24 +00:00
zzz
9099937119 * Reseed: Remove forum.i2p2.de 2012-08-27 20:35:02 +00:00
zzz
b827468e2f * i2psnark: Notify threads awaiting DHT replies at shutdown 2012-08-27 20:34:19 +00:00
zzz
587795552e Wrapper files for armv7.
Compiled on trimslice:
    gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) 
    java version "1.6.0_18"
    OpenJDK Runtime Environment (IcedTea6 1.8.13) (6b18-1.8.13-0ubuntu1~11.04.1)
    OpenJDK Zero VM (build 14.0-b16, mixed mode)
    Wrapper 3.5.15 GPLv2
    All binaries stripped.
2012-08-27 17:43:04 +00:00
0a1ff9b6bd Update Java Service Wrapper to v3.5.15.
- Windows: Self-compiled with VS2010 in Windows 7. The icon has been
   changed from Tanuki's default to Itoopie.
 - FreeBSD: Self-compiled in FreeBSD 7.4 to eliminate the dependency on the
   compat6x port.
 - Linux ARMv5 & PPC32: Self-compiled in Debian Squeeze
 - Linux x86, Linux x64, MacOSX & Solaris: Binares are from the "community
   edition" deltapack offered by Tanuki. The x86 and x64 binaries for Linux
   have been stripped.
2012-08-27 13:49:18 +00:00
zzz
b01cf32321 * SendMessageOptions: Increase tag fields to 4 bits and use
table lookup for more flexibility
 * Streaming: Use packet type and current window size to adjust
              number of tags sent and tag threshold, to improve
              efficiency and reliability
2012-08-26 13:02:11 +00:00
zzz
9ba6c293ed * DataHelper: Trim trailing whitespace when loading properties 2012-08-26 12:54:49 +00:00
zzz
99681e1d1e * NetDB: Increase floodfills, decrease flood redundancy 2012-08-26 12:47:31 +00:00
96775acf5a Dutch and German translation updates from TX 2012-08-25 19:43:25 +00:00
ba992067ad typo fixes (ticket #701), thanks vz 2012-08-25 19:05:45 +00:00
zzz
2552d99308 * Other object churn cleanups (ticket #699) 2012-08-25 14:48:39 +00:00
zzz
e99e25b3b9 minor cleanup 2012-08-25 14:45:31 +00:00
zzz
70820d7be6 * SDSCache: Reduce min and increase max size
* SimpleByteCache: Change from LBQ to ABQ to reduce object churn
2012-08-25 14:44:52 +00:00
zzz
38fda46d44 javadoc 2012-08-25 14:42:04 +00:00
zzz
9d383d6aef * i2psnark:
- Use extended I2CP options for datagrams
   - Fix timeout for sent datagrams
   - Reduce token timeout
   - Check token age before use
   - Limit incoming token size
2012-08-24 22:13:08 +00:00
zzz
ba0408a741 * I2CP:
- Add methods for sending a message with extended options
   - Fix cases where the efficient sendNoEffort() wasn't being used
 * OCMOSJ:
   - Implement per-message overrides for tag threshold,
     tags to send, and bundle leaseset
   - Fix bug adjusting timeouts
   - Warn on client expiration time bugs
2012-08-24 22:11:02 +00:00
zzz
07c21c3bfd - Add link for local torrent file on details page
- Show totals line even if only one torrent
2012-08-24 22:07:00 +00:00
zzz
5ffefd2a19 * Crypto: Rename bouncycastle HMAC libs so they don't conflict
with older Android versions which bundle them
2012-08-23 19:11:55 +00:00
zzz
e3e15850bb * SSU:
- Don't relay or introduce to/from privileged ports
   - Various spoof detections
2012-08-23 19:10:36 +00:00
zzz
54b367b153 * NTCP: Reduce lock contention 2012-08-23 19:08:45 +00:00
zzz
b61127270e * SSU:
- Fail establishment immediately on SessionCreated
     validation fail
   - Defer outbound DH generation until required
   - Validate address/port in RelayIntro messages
   - Throttle hole punches
   - More cleanups
2012-08-22 17:43:09 +00:00
zzz
1d41c2fd19 SSU: Workaround for Android ICS bug 2012-08-22 17:41:43 +00:00
zzz
7c7e131dc0 * SimpleTimer2: Synchronization improvements (ticket #653) 2012-08-22 17:40:47 +00:00
zzz
85fbbf8980 * NetDB: Add hash collision detection 2012-08-22 17:40:25 +00:00
zzz
612fab1b2a * SSU:
- Use external, not internal port to sign SessionCreated message.
     Together with previous fix to allow external port change, this
     should fix session establish fails when NAT changes our port
   - Track outbound establishments by both Hash and IP/port,
     to improve lookups of establishments in progress
   - Fix expiration of outbound establishments
   - Validate address/port in RelayResponse messages
   - Change RemoteHostID to store Hash instead of byte[] for the peer hash
   - Log tweaks
2012-08-21 19:53:08 +00:00
zzz
fbd8c69eea * NetDB: Decrease stat publish probability 2012-08-21 19:49:43 +00:00
zzz
8fcac04aad javadoc 2012-08-21 19:48:18 +00:00
zzz
7d902cca1e log tweak 2012-08-21 19:48:04 +00:00
ddc1d7c6bc disapproval of revision 'acc7942148f44d32fc600d2f5784d1a43496eada' 2012-08-20 21:26:12 +00:00
5bb90c6185 * Fixed the FIXME in createInstance where the method failed to ensures that there will be only one naming service instance. 2012-08-20 18:28:20 +00:00
zzz
9452547204 * SSU: Allow port change if firewalled
* UPnP:
   - Prep for UPnP returning different external port
   - Better logging of errors
2012-08-20 12:22:00 +00:00
zzz
34c09583b4 do not use 8887 default in UI 2012-08-20 12:14:43 +00:00
zzz
38b0927d01 * I2CP: MessageStatus cleanup 2012-08-20 12:13:26 +00:00
zzz
715bde5ecf * Streaming: Increase max connection timeout 2012-08-20 12:12:48 +00:00
zzz
6c2eb317fe * I2PTunnelRunner: Remove unnecessary lock (ticket #690) 2012-08-20 12:10:10 +00:00
zzz
05516f3260 * i2psnark: Add minimum tracker and DHT announce intervals 2012-08-20 12:09:20 +00:00
264df83943 fix hang during uninstallation (#656) 2012-08-18 13:06:07 +00:00
zzz
3a546612d9 * SSU:
- Use remote MTU when published (ticket #687)
   - Queue outbound msgs during inbound establish
   - IntroManager cleanups
   - More synchronization
   - More log tweaks
2012-08-17 14:15:01 +00:00
zzz
3cac01ff27 * i2psnark:
- Adjust DHT timeouts
   - Add max peers per-torrent in tracker
   - Remove duplicate clean task for nodes
   - Fix another DHT warning message
2012-08-17 14:09:49 +00:00
e01521618f BOB: just some cleanup 2012-08-17 05:39:02 +00:00
zzz
ee63f3b86d minor NTCP cleanup 2012-08-16 19:02:38 +00:00
zzz
a900511d5e * Utils: Drop unused BufferedRandomSource, PooledRandomSource,
EepGetScheduler, EepPost and HTTPSendData, moved to i2p.scripts
2012-08-16 18:25:49 +00:00
zzz
3fe092d788 tab cleanup 2012-08-16 18:24:59 +00:00
zzz
e2fe5004e7 javadoc fixes 2012-08-15 14:39:52 +00:00
zzz
442af031eb propagate from branch 'i2p.i2p.zzz.upnp' (head fbd68f812db1e891f96e212b3a5938beec0233b5)
to branch 'i2p.i2p' (head a8d4956565f7c58736c2a3001f2b08ecff59ab57)
2012-08-15 14:29:30 +00:00
zzz
e22882bd02 - More fixups from merge
- Remove local address from thread names for thread dump anonymity
2012-08-15 14:24:40 +00:00
zzz
523d39b3bb * i2psnark:
- Fix bug preventing completion announcement, broken in 0.9.1
   - Fix setting short retry time after initial announce failure
   - Fix DHT announce and getPeers
   - Fix DHT warning message
   - log tweaks
2012-08-15 12:44:46 +00:00
zzz
65efefb094 propagate from branch 'i2p.i2p' (head 51b3351f42e7ff6e2f2bd8512e4b4402e08631f4)
to branch 'i2p.i2p.zzz.upnp' (head d28cfe73c2741ea264f73a7317f8a9919e108170)
2012-08-14 13:50:15 +00:00
zzz
44edf70842 * SSU EstablishmentManager: Fix bug with OB establishment via introducers
- log tweaks
2012-08-13 23:06:07 +00:00
zzz
16a46b3211 * SSU EstablishmentManager:
- Remove use of outbound timers in EstablishmentManager; drive all events in Establisher thread
   - Don't change nonces when retransmitting intro packets
   - More synchronization in EstablishmentManager
   - Increase establishment timeouts and implement timeouts for individual phases (ticket #669)
   - Fix bug where InboundEstablishState.createdPacketSent() wasn't being called,
     so SessionCreated packets weren't retransmitted
   - Increase retransmission timeout for SessionCreated and implement backoff
   - Send destroy if establishment times out in the middle
   - Fix code that pulls outbound states off a deferred queue
   - Improve UDPPacket.toString() for debugging
   - More logging of packets dropped in EstablishmentManager
   - Change establish states to enums
2012-08-13 15:12:33 +00:00
zzz
e9cc85141c comparator cleanups 2012-08-13 15:08:06 +00:00
zzz
cfcafd2ba3 * SSU:
- Reject some packet types if they came in via fallback introKey
   - Increase retransmission timeout for SessionRequest, SessionConfirm,
     and RelayRequest; implement backoff
   - Move UDPFlooder to test
   - More volatiles, finals, cleanups, stat removals, log tweaks
2012-08-12 11:24:15 +00:00
zzz
e67dd15308 * PeerManager: Fix NPE on Android (ticket #687) 2012-08-12 11:19:49 +00:00
zzz
a76f840ff8 remove finalize 2012-08-12 11:18:24 +00:00
zzz
269a36c549 * Jetty: Don't use direct byte buffers that may be leaking (ticket #679) 2012-08-12 11:11:45 +00:00
36bf248385 Removed unnecessary <p></p> around <img> on /graph 2012-08-12 00:35:09 +00:00
046135f8e3 merge of '8027b8544962ebd34af3edfe73bbc8195f8c1e90'
and '871249c3be5c8d8ce83a539ba8c5409876ef3a44'
2012-08-11 14:47:43 +00:00
97e469da7b command safari does not exist. using command "open" instead. 2012-08-11 14:34:20 +00:00
zzz
01beb015dc merge of '52c0538bb3404f46ac4fde538794a547852d5d44'
and 'e40ee84836983c85a8985c0a76e9e5a7635002cd'
2012-08-11 13:55:55 +00:00
zzz
50d5692884 snark build fix sorry 2012-08-11 13:51:52 +00:00
0ea6513e9c Changed my reseed url to contain a subpath, /netDb/. also updated the certificate (forgot to copy out of conf folder when i changed webserver and deleted config). 2012-08-11 13:43:48 +00:00
zzz
e2b683556b merge of '15095538e7b5c72468863e969541571ade1796f9'
and '39af5d31a13c5d5b71107e2caa0c234b84827c03'
2012-08-11 12:26:32 +00:00
zzz
14587ebb59 dash six 2012-08-11 12:16:43 +00:00
zzz
be3cf44608 get rid of one more UnsupportedOperationException 2012-08-11 11:41:29 +00:00
zzz
1538cd84a9 * DataHelper: toString(byte[]) cleanup and javadoc 2012-08-11 11:40:41 +00:00
f5b808b997 OSX: Fixed a bug in OS_VER variable.
the old version (grep -o '[0-9]*\.[0-9]*\.[0-9]*') won't detect versions as 10.8, or 10.7
added cut -d: -f2 | sed -e 's/[^0-9]*//' , matching both 10.8, 10.7.4, etc.
2012-08-11 11:40:05 +00:00
zzz
f92d8aed3d make context list concurrent 2012-08-11 11:34:32 +00:00
zzz
f6c769187e fix 2012-08-11 11:33:43 +00:00
zzz
c70e3727be * SSU MTU (ticket #682):
- Use local MTU detection
   - i2np.udp.mtu now sets max MTU, not initial MTU
   - Put local MTU in netDb 
   - Fix receive MTU calculations
   - Track remote MTU based on actual received packet size
   - Display local MTU on peers page
2012-08-11 11:27:28 +00:00
zzz
a6a0228ef8 * i2psnark: Fix DHT nodes not being saved at shutdown
Log infohashes in hex
             Don't write out nodes if we don't have any
2012-08-11 11:23:27 +00:00
d2a5595df2 Added new reseed host, and a self signed certificate for that host. 2012-08-11 01:14:58 +00:00
e9c07a123a German and Greek translation updates from Transifex 2012-08-11 00:25:43 +00:00
1e8e2a197b A fix for ticket #684 2012-08-10 23:55:14 +00:00
zzz
39d9d3f5b6 * SAM: Don't use direct byte buffers for streams (ticket #679)
DatagramServer one stays as it is a singleton.
2012-08-09 15:12:17 +00:00
zzz
8bada7f882 merge of '01c1a5c0e70a460534e66dd487aa8676d666168d'
and '441b352d1cef1c64ee96f55692be67cfc4b4abf1'
2012-08-09 15:03:01 +00:00
a940062255 add ')' missing from str4d's checkins 2012-08-09 11:35:41 +00:00
93efd31a5b merge of '62c3f266fd9443e9d7b3f71be8f4b36b4952f96b'
and 'd71795fa9a3d5fa0349f46eef4062670755b2cff'
2012-08-09 02:37:19 +00:00
2e9fdc6d9f Updated history.txt 2012-08-09 02:36:39 +00:00
zzz
b9f5f230a2 stub out local MTU detection (ticket 682) 2012-08-08 23:36:11 +00:00
zzz
0a751a303f post-0.9 cleanup 2012-08-08 17:09:28 +00:00
zzz
b2da629034 * Datagrams: Remove static logs 2012-08-08 17:07:28 +00:00
zzz
37a542c009 NTCP stat cleanup 2012-08-08 17:06:33 +00:00
zzz
0451ee7f08 * SSU: More cleanups; don't send a packet that exceeds the MTU 2012-08-08 17:05:42 +00:00
zzz
d8dd76c6e0 * SAM: Volatiles and finals 2012-08-08 17:01:59 +00:00
zzz
9cee0ee504 * i2psnark:
- Remove static SnarkManager instance
   - Allow DHT-only torrents
   - DHT debugging
2012-08-08 17:00:33 +00:00
58a545d30c Snark: explicitly check if universal theme is "classic", and use "light" if so 2012-08-07 23:08:37 +00:00
dfb0b7801d Updates to classic console theme from dr|z3d 2012-08-07 23:03:34 +00:00
a21175d903 propagate from branch 'i2p.i2p' (head b1fe8f8037e6dd8a1f6be6e30151ad0ca92e6689)
to branch 'i2p.i2p.str4d.fux' (head 723929af49930ca764fe4befb3621a036a3f99b8)
2012-08-07 12:28:30 +00:00
9c7f4cc604 merge of '6014a9321bb2362ffc628a351c1db19922384f76'
and 'd68b6ad6b4308d8dbe27d1faac089cb15358bfa2'
2012-08-07 12:24:33 +00:00
3017e4f51a Fixed .mtn-ignore to ignore build dirs properly 2012-08-07 12:18:16 +00:00
5ed1ec681f Updates to classic console theme from dr|z3d 2012-08-07 11:42:22 +00:00
0a4031cd7b Updates to midnight console theme from dr|z3d 2012-08-07 06:03:35 +00:00
31ea4a7093 Changed universal theming prop key (so not mistaken as a theme name by routerconsole) 2012-08-07 04:29:50 +00:00
0ca2d33ee1 New midnight theme for susidns from dr|z3d 2012-08-07 04:24:41 +00:00
48bcd3a8c2 Updates to console and snark themes from dr|z3d 2012-08-07 04:22:40 +00:00
zzz
1ab8200c7f * Clone System properties before iterating to avoid
ConcurrentModificationException (ticket #680)
2012-08-06 14:45:37 +00:00
zzz
91e61dbd5c fix flag links so language selection returns to the same page 2012-08-06 14:13:15 +00:00
zzz
fb4ef57148 propagate from branch 'i2p.i2p.zzz.dhtsnark' (head d4f16babae7cb0156609b211f5bb0310b03aaf57)
to branch 'i2p.i2p' (head 7bcd2f192b0f571374cc9882eca407095eb97c17)
2012-08-06 14:05:09 +00:00
zzz
ced0129e03 * libjbigi.so for ARMv6
GMP 4.3.2
    Compiled on Raspberry Pi with gcc version: gcc (Debian 4.6.3-8+rpi1) 4.6.3
    java version "1.6.0_24"
    OpenJDK Runtime Environment (IcedTea6 1.11.3) (6b24-1.11.3-2+rpi1)
    OpenJDK Zero VM (build 20.0-b12, mixed mode)
    Stripped.
    Had a report that the ARMv5 jbigi worked on the RPi but it didn't for me.
    See NativeBigInteger for more info.
    /proc/cpuinfo:

Processor	: ARMv6-compatible processor rev 7 (v6l)
BogoMIPS	: 697.95
Features	: swp half thumb fastmult vfp edsp java tls 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xb76
CPU revision	: 7

Hardware	: BCM2708
Revision	: 0002
2012-08-06 14:04:32 +00:00
740b6501cd Updated history.txt 2012-08-06 12:38:00 +00:00
zzz
67f16b0de4 javadocs 2012-08-05 16:55:39 +00:00
zzz
fd3d92d3b2 merge of '1045fe48c576267959eae499a22776d9f9acafc3'
and 'bed1572eff55282ffcb5a2b92d02813eb04c0548'
2012-08-05 15:16:45 +00:00
zzz
5ba5d537b5 * UDP:
- Limit PacketHandler threads to 1 (ticket #660)
   - Limit queue sizes between UDPReceiver and PacketHandler,
     and between PacketHandler and MessageReceiver, to prevent OOMs
     and/or excessive queue delays
   - Increase UDPPacket cache size based on max mem
   - Remove more stats
2012-08-05 14:24:14 +00:00
zzz
4efa87d6bf * i2ptunnel, I2CP, EepGet: Buffer socket input streams (ticket #666)
* I2PSessionImpl: One more volatile (ticket #659)
2012-08-05 13:33:28 +00:00
442897ba5b merge of '48ea7675889a36bfb253833a66c22275abcee355'
and '925b1411e60b0e76e2421becd84d6c63832b69bb'
2012-08-05 07:40:05 +00:00
2b79da5c35 Debian: update patch to compensate for last commit
At the same time I'm making more Debian-specific changes, such as
 - not checking for Gentoo (this is obviously never going to be true :P)
 - not mentioning IzPack replacing variables (also not applicable)
 - inform about dpkg-reconfigure i2p in the event someone tries to remove or
   install an initscript with "i2prouter install|remove|uninstall"
 - remove the manual wrapper instructions
2012-08-04 17:44:09 +00:00
cc3a8e5d62 have izpack expand %USER_HOME in i2prouter
This will solve the issue of I2P_CONFIG_DIR being set incorrectly if another
user runs i2prouter once, such as when installing the initscript.
2012-08-04 17:32:11 +00:00
zzz
280a708afe - Change secure Node ID requirements again
- Protect against null DHT races
- Add message about restarting tunnel when DHT config changes
- Add DHT size to table totals
2012-08-04 17:11:11 +00:00
f5a348a863 propagate from branch 'i2p.i2p.str4d.test' (head 190d9be59620f8c6f80e0cb2fc4d9fa839edbb4f)
to branch 'i2p.i2p' (head c884db74f90a9d1c33deca6e2fd2e29f6c1ac8fa)
2012-08-03 21:30:29 +00:00
85a4e9cb5c Clarified purpose of the default IRC tunnel (it's not a "proxy") 2012-08-03 21:26:09 +00:00
zzz
4715dbdbd0 fixup after prop 2012-08-03 20:40:31 +00:00
zzz
afad77af19 propagate from branch 'i2p.i2p' (head d2198c4bc21a9d06194cdb2dce24945ebc9d1542)
to branch 'i2p.i2p.zzz.dhtsnark' (head 59fc0206608a5d1323a0acfbcb151d862fe95f95)
2012-08-03 20:29:31 +00:00
zzz
b4a50ed03a replace SimpleScheduler.getInstance() calls 2012-08-03 17:13:17 +00:00
zzz
00f9fea98c replace SimpleTimer2.getInstance() calls 2012-08-03 16:23:31 +00:00
zzz
501651125f * UDP:
- Catch some cases where UDPPackets were not returned to the cache (ticket #675)
   - Fix inverted logging logic (ticket #663)
   - Remove check in UDPPacket.getLifetime() (ticket #664)
   - RemoteHostID cleanup, cache hashcode
   - Remove udp.fetchRemoteSlow stat
   - Remove some time stamping in UDPPacket
   - Other cleanups - see http://zzz.i2p/topics/1198
2012-08-03 14:25:32 +00:00
zzz
18e8d35910 * LogManager: Add logger.logBufferSize and logger.dropOnOverflow options (ticket #662) 2012-08-03 13:42:40 +00:00
zzz
9e4d231285 final 2012-08-03 11:23:09 +00:00
zzz
2972e79f9e * OutNetMessage: Fix NPE when log level = INFO (ticket #676) 2012-08-03 11:22:48 +00:00
zzz
4d32eaa036 * JobQueue: Synch fix (ticket #670) 2012-08-03 11:21:28 +00:00
zzz
e4f141b94c * build.xml: Add buildI2PTunnelJar target for Android 2012-08-03 11:20:28 +00:00
zzz
ccf36abd30 * i2psnark: Finish migration to I2P logging to reduce object churn (ticket #673) 2012-08-03 11:19:52 +00:00
d147db3382 Only call SaveConfig once in ConfigUIHandler 2012-08-03 03:49:46 +00:00
9d29dc6b68 Fixed bugs introduced while reverting 2012-08-01 05:54:07 +00:00
6562b33bbc Removed readConfigFile and writeConfigFile from I2PAppContext (unnecessary now) 2012-08-01 03:05:18 +00:00
f58f297cdb Reverted susimail to storing theme itself, but checking routerconsole for universal theming 2012-08-01 03:00:23 +00:00
376b991b63 Reverted i2psnark to storing theme itself, but checking routerconsole for universal theming 2012-08-01 02:44:18 +00:00
120d31244e Reverted i2ptunnel to reading theme from routerconsole 2012-08-01 02:26:26 +00:00
679549cbf2 Reverted susidns to storing theme itself, but checking routerconsole for universal theming 2012-08-01 02:04:00 +00:00
a623d924fa Reverted routerconsole to storing theme itself, also store universal theming boolean 2012-08-01 01:50:59 +00:00
95fb141ad9 0.9.1 debian changelog 2012-08-01 00:34:17 +00:00
fad6f54794 propagate from branch 'i2p.i2p.unittests' (head 0c5ea65761d9127f160bccb3d1d157f8947ca050)
to branch 'i2p.i2p' (head e36d5669f32ad1a0f66ab84f7f9ff8fa2937680b)
2012-07-31 21:49:31 +00:00
e1525d98cd Fixed paths to JUnit tests in javadoc targets 2012-07-31 21:26:46 +00:00
3d69d2bf63 If the theme set for susidns doesn't exist, use the default instead 2012-07-31 12:56:23 +00:00
cb2dd03e77 Refactored writeConfigFile to use DataHelper.storeProps, tidied up logging 2012-07-31 12:52:31 +00:00
3253f82900 Added ScalaTest support to router build.xml 2012-07-31 00:06:28 +00:00
zzz
33a00efd82 * RoutingKeyGenerator: Cleanups (ticket #672) 2012-07-30 22:39:47 +00:00
zzz
8bcbf24713 propagate from branch 'i2p.i2p.zzz.test' (head 5474e1a513fc8144a3d855e9c85d8b235f7f9816)
to branch 'i2p.i2p' (head 5932d3923108572b22a8a7a600f0f9e62ecac347)
2012-07-30 22:29:02 +00:00
52ba727664 line ending fixes
These CSS files improperly contain a mixture of Windows *and* UNIX line
endings. Some lines had ^M after them but most didn't.
2012-07-30 17:01:12 +00:00
a1cfacd8da merge of '3347c631d86a2ccf634321b74e24b9a58e490a15'
and '95d885a94cdde7ad40bbef11ec76f86d1ea7b798'
2012-07-30 16:51:05 +00:00
5b6e7ba91d Added some skeleton Spec tests for various I2P data types in net.i2p.data 2012-07-30 14:50:51 +00:00
zzz
77a19a0b17 0.9.1 2012-07-30 13:28:56 +00:00
zzz
7ecb90640c fixes after review 2012-07-30 13:15:58 +00:00
691ce6fec7 The first ScalaTest specification: a Hash must be 32 bytes long 2012-07-30 12:36:00 +00:00
618f214a4f Added ScalaTest support to core build.xml
The old JUnit tests are still present, but "ant test" and "ant fulltest" default
to the (pending) ScalaTest ones. To run the ScalaTest tests with Cobertura, execute
the following:

ant -Dscalatest.libs=./lib -Dwith.cobertura=/usr/share/java/cobertura.jar fulltest

The scalatest.libs property must point to a directory containing scala-compiler.jar,
scala-library.jar and scalatest.jar.
2012-07-30 12:26:35 +00:00
48df91f796 Moved existing JUnit tests to junittest/ in preparation for ScalaTest 2012-07-30 04:04:07 +00:00
d27d0bd2e4 A couple more specific excludes for JUnit tests 2012-07-30 04:03:02 +00:00
39d954d56a merge of '00209f1054786667de66adbd6038fccf8825260f'
and '180794b6e2aad94450e102b4ac346f8172811ba1'
2012-07-28 22:22:07 +00:00
78b1922dd7 "11th hour" de & es translation updates from TX 2012-07-28 21:18:56 +00:00
639253e9bb Fixed a bug in a console_big.css override in light and dark themes 2012-07-28 13:45:12 +00:00
f8fe2a295f Fixed icon column width being too large in snark filemanager 2012-07-28 13:38:40 +00:00
9d2831f520 Updated history and bumped build number 2012-07-28 12:47:42 +00:00
c2438a7508 Fixed an i2ptunnel button regression in the dark console theme 2012-07-28 12:33:36 +00:00
zzz
4298958952 comment out tests 2012-07-27 15:49:46 +00:00
zzz
54a80d6bdc javadoc 2012-07-27 15:31:20 +00:00
zzz
aba655a9c7 move default properties from build.xml to build.properties 2012-07-27 15:30:11 +00:00
b6eef94383 Tweaked console themes so console error pages look alright in iframe
This fixes the issue where, when an iframed app was not running, the summary
bar would be displayed inside the iframe as well as outside it.
2012-07-27 12:41:45 +00:00
7526db9e6c minor update to reflect the other systems that we support 2012-07-26 21:11:42 +00:00
c853337d41 merge of '3b6726af78088f84c156c7d5cb40aa0b73946465'
and '5b76774cf78d7a3537006860fc78c00c191c9cb0'
2012-07-26 20:50:39 +00:00
00dd72e284 July 2012 GeoIP db update 2012-07-26 20:32:04 +00:00
05850371a6 Finnish, French, German, Spanish and Swedish translation updates from TX 2012-07-26 20:31:01 +00:00
c285cb84bd BOB Fix static references to Log 2012-07-26 20:30:42 +00:00
a4a0e1def3 Replaced a few more /home icons 2012-07-25 23:38:25 +00:00
fea7a42ece Reset spin flag in BOB 2012-07-24 18:38:36 +00:00
72f74b7f6e Disabled the iframe fallback for the summary bar
HTML compliance wins out over summary bar refresh for users with JS disabled,
for now - get user feedback after release.
2012-07-24 13:15:11 +00:00
a92456e144 Updated history.txt 2012-07-24 12:47:21 +00:00
7f7a82802d Added CC attribution for Creative Commons Cat 2012-07-24 12:07:09 +00:00
93097ab630 Moved RestartStatus back above LocalDestinations in default /home summary bar 2012-07-24 12:05:34 +00:00
d3d22a8f4b Added license for new /home icons 2012-07-24 05:32:40 +00:00
7a1b082216 Updated /home icon for git.repo.i2p 2012-07-24 05:18:20 +00:00
0e907c5ad0 Replaced 16x16 /home icons with 32x32 ones 2012-07-24 05:17:13 +00:00
59b8dc4f41 Update to dark console.css - more space efficient on /home, "0px" -> "0" 2012-07-24 03:23:51 +00:00
299109433c merge of '03b8e025ba8c54efe24644ee68500ae2ff0ea2de'
and 'b45f7b28764f452bbd5f58ebd598f465565cab5c'
2012-07-23 23:30:39 +00:00
zzz
9823d761d9 dont create router.ping file on Android to reduce flash wear 2012-07-23 23:21:20 +00:00
db6b8d3b6b debian: minor improvements to the build script
- Tighten setting of Debian version in I2P
- Ensure the clean target will remove .so and .o files
2012-07-23 23:20:23 +00:00
c61a18545e Debian: Use our Jetty/Tomcat libs instead of the ones from Debian/Ubuntu (for now at least).
Tomcat6 & Jetty6 are going to be dropped from the next release of Debian stable
(Wheezy) in favor of Tomcat7 and Jetty8.
2012-07-23 23:13:28 +00:00
zzz
c1181f855a remove unused dirs 2012-07-23 23:11:50 +00:00
zzz
e2aa2affd7 * LookaheadInputStream: Fix bug causing gunzip fails, esp. on Android
* SSLEepGet: Fix on Android (ticket #668)
2012-07-23 23:10:26 +00:00
7f18d25d0d Re-add alt="" to some icons in snark to prevent double text in text browsers 2012-07-23 21:49:30 +00:00
314817242b If the theme set for susimail doesn't exist, use the default instead 2012-07-23 11:01:39 +00:00
945a0f30aa If the theme set for snark doesn't exist, use the default instead 2012-07-23 10:39:11 +00:00
a7c8a7201a When fetching a theme, if theme config key is not found, write out the default
This is required in order to get the theme config keys of the various apps into
themes.config; this way, the routerconsole requires no knowledge of what apps
support universal theming, and can just blanket apply themes to all known keys.
2012-07-22 13:13:43 +00:00
6be94658a7 Use router console theme setting from themes.config in i2ptunnel 2012-07-22 11:33:20 +00:00
490dcc5020 Bugfix: update SnarkManager._theme each time getTheme() is called
FIXME: ensure that _theme is only read from the config file once per page load.
2012-07-22 06:59:43 +00:00
8e6bade42b Added checkbox to console to set theme universally across all apps 2012-07-22 06:53:43 +00:00
c145e4267c Fixes to light/console_ar.css 2012-07-22 00:18:07 +00:00
a4064190dd merge of '5aab7ebb6f496024fff1cc4b87e39475a7277db0'
and 'b2e508dbe3e9c7c67bb38d3f573481ae3fb8c9b3'
2012-07-21 20:59:01 +00:00
f97213630c Reverted susimail and snark links in /home and summarybar to point directly at app
The iframing pages remain accessible in the console, so this may be reverted in future,
or made configurable. The /webmail link buried in the text of the several readme.html
files has been left.
2012-07-21 20:57:46 +00:00
6a21e22bf1 Moved dark snark theme out of bundled themes dir due to an image issue
dr|z3d is happy for the problem image to be replaced and the theme reinstated
when universal theming support is added (likely 0.9.2)
2012-07-21 20:43:33 +00:00
zzz
77f8729257 new cowpuncher cert 2012-07-21 19:26:29 +00:00
zzz
39d4e1be72 SSU: Remove excessive exception creation (ticket #665) 2012-07-21 13:04:50 +00:00
zzz
ebe55aba61 remove dark as requested 2012-07-21 13:03:27 +00:00
3c4f1b7814 Store susimail theme setting in themes.config 2012-07-21 12:33:42 +00:00
ce024ff006 Store susidns theme setting in themes.config 2012-07-21 11:39:13 +00:00
e603b120c3 Store snark theme setting in themes.config 2012-07-21 11:06:24 +00:00
b17af505c2 Replaced a hard-coded string that was missed in previous commit 2012-07-21 10:22:12 +00:00
5d5a3b80e5 Store router console theme setting in themes.config 2012-07-21 10:11:32 +00:00
c8a73b63fd Added methods to read and write properties in arbitrary config files 2012-07-21 10:07:04 +00:00
ce7a46bbed Open Previewed http dests with target="_parent" in i2ptunnel
Before iframing i2ptunnel, clicking the Preview button would open the site in the parent
window. This change will cause the sites to open in the parent window from
within the iframed page.
2012-07-20 19:20:18 +00:00
eee67f09e1 en PO files updated and pushed to tx 2012-07-20 17:59:39 +00:00
ab7246565c Reverted alt tags in status column of snark 2012-07-20 04:16:53 +00:00
096d067d6c Updated history and bumped build version 2012-07-20 01:18:24 +00:00
9d2709be19 Update to light theme to increase top margin of iframes 2012-07-20 01:13:01 +00:00
3cce978e26 New light snark theme from dr|z3d (integrates well with light console theme) 2012-07-20 00:59:13 +00:00
8f30a74c7d Split up status and show/hide peers headings into separate columns 2012-07-20 00:57:53 +00:00
a86a2ba04a Moved text from headings in snark filemanager into tooltips (in line with main page)
Directory column could do with a different icon.
2012-07-20 00:36:43 +00:00
f4ffb30153 Added iframed fixes to default.css in classic, light and midnight console themes 2012-07-19 23:05:25 +00:00
ecdaa6f2b3 Removed text headings and status text from main snark page to reduce visual clutter
The header icons are self-explanatory (but tooltip also has header text); the
status text is available from the tooltip.
2012-07-19 21:39:17 +00:00
2b8b406f9d Added title and alt tags to Status and Torrent columns in snark main view 2012-07-19 13:06:45 +00:00
212a794c65 Give div.app fixed height for classic and midnight console themes
This stops app icons getting "stuck" on the right-hand side of /home
2012-07-19 00:41:52 +00:00
0e2dede168 Replaced itoopie with a dedicated icon for eepsite links on /home with no favicon 2012-07-19 00:08:29 +00:00
c1f3fa6004 Updates to dark susi themes and new light newsbullet from dr|z3d 2012-07-18 23:44:55 +00:00
e2be19039f Added missing git.repo.i2p and id3nt.i2p entries to hosts.txt 2012-07-18 12:33:30 +00:00
zzz
846f6f2190 * Home page: Add colombo-bt.i2p, remove keys.i2p
Icon created and contributed by colombo-bt.i2p
2012-07-18 12:00:42 +00:00
zzz
37716f34de * i2psnark: Clear rate stats when restarting torrent 2012-07-18 11:52:20 +00:00
f01ccf9797 Update to dark susimail theme from dr|z3d 2012-07-18 03:56:35 +00:00
074baa63f5 Fixed /home tooltip to not break page 2012-07-18 02:50:13 +00:00
763eb08dad Added git.repo.i2p and id3nt.i2p to /home eepsite list 2012-07-17 21:27:22 +00:00
1d40a88166 Fix snark Ajax JS (wrong level of escaping of quotes) 2012-07-17 20:55:36 +00:00
5766b36b33 Give the summary bar sections string names (some already translated) 2012-07-17 20:36:57 +00:00
4cea0b6099 US spelling 2012-07-17 19:44:44 +00:00
43fd5caf30 Fixed snark initAjax method so it runs (I'm SURE it did before...) 2012-07-17 19:32:22 +00:00
5c1a1b13f4 Update to dark susimail theme from dr|z3d 2012-07-17 12:22:52 +00:00
109e1a75bf Improved alignment of news headings in classic and midnight themes 2012-07-17 11:40:44 +00:00
99f8384129 merge of '9fdbbb81553e767812b7a8634edd867cb1ca8438'
and 'cc04809f72ea9e8f0e0427484d65f613838f65d7'
2012-07-17 10:56:51 +00:00
f3cb399605 Update to dark susidns theme from dr|z3d 2012-07-17 10:56:20 +00:00
c94ce79e68 Added links inside <iframe> tags on iframed pages to corresponding apps 2012-07-17 01:20:08 +00:00
744930a090 Consolidated Ajax calls for summary.jsi into a single summaryajax.jsi 2012-07-17 00:47:08 +00:00
be7aa991d7 Consolidated the common iframing JS into /js/iframed.js 2012-07-16 23:39:44 +00:00
c815bc2996 Removed spurious UTF8 chars from dark/console_ar.css 2012-07-16 21:36:48 +00:00
924520955e merge of '369340479baf15d01ba95519145edddba22634ca'
and '3b7088110cf55db67e72b7e9bfbf8cd832daac06'
2012-07-16 21:34:41 +00:00
0bff0a4998 Removed backup favicon.ico from css.jsi (all current themes have favicons) 2012-07-16 21:32:47 +00:00
691c003e95 merge of '762f90d57bd57a4e0b122dff85484774b0105d93'
and 'd02235b886c4e596e3c980dcb0b8df9d3547d103'
2012-07-16 21:29:04 +00:00
a28dab9bdc Bumped build number to -22 2012-07-16 21:04:11 +00:00
c33c0259a7 Only set extraversion in Unix since Windows doesn't have awk available
I need to investigate a cross-platform method.
2012-07-16 17:47:48 +00:00
77d40f8d31 propagate from branch 'i2p.i2p.str4d.fux' (head 48cafeb29fb3408078a8b93c0bab0fc9d766a8bc)
to branch 'i2p.i2p' (head 47f04ff21e8edd00134a0fd68219f86fd3caba36)
2012-07-16 16:17:19 +00:00
619b766c85 Removed CSS hiding susimail Cancel button (it might be hiding other buttons) 2012-07-16 16:13:03 +00:00
693ffed9be Updated history.txt 2012-07-16 16:11:03 +00:00
c175d5470f Updates to dark console theme from dr|z3d 2012-07-16 14:50:56 +00:00
42e6d06559 Whitespace fixes to dark susimail theme 2012-07-16 14:04:58 +00:00
25da127d02 Whitespace fixes for light susimail theme 2012-07-16 13:58:22 +00:00
00b88675ea Whitespace fixes in light susidns theme 2012-07-16 13:56:31 +00:00
c552db59e4 Whitespace fixes in dark susidns theme 2012-07-16 13:53:54 +00:00
763116fb24 Updates to dark susidns theme from dr|z3d 2012-07-16 13:50:21 +00:00
f3531f1c2c Updates to dark console theme from dr|z3d 2012-07-16 13:47:03 +00:00
a2a67d82ab Some updates to ieshim.css in classic theme to fix some IE console regressions 2012-07-16 13:44:58 +00:00
zzz
69cdcc8226 * XORComparator: Reduce object churn (Ticket #658) 2012-07-15 15:04:58 +00:00
5e11e51f6a Added "theme" to list of susidns config options 2012-07-15 13:13:57 +00:00
a419347eba Added preliminary dark susimail and susidns themes
Add "theme=dark" to susidns config page, or "susimail.theme=dark" to
susimail.config file, to enable the dark themes.
2012-07-15 11:03:51 +00:00
ab42e47385 Added iframing pages for susimail and susidns 2012-07-14 21:56:38 +00:00
50cfd52c23 Moved susidns CSS into themes dir (required moving loadConfig into BaseBean.java) 2012-07-14 06:27:23 +00:00
e0ff0c63c8 Moved susimail CSS into themes dir 2012-07-14 03:46:23 +00:00
a123def967 Inter-browser fixes for dark snark theme from dr|z3d 2012-07-14 01:38:14 +00:00
8360a2f4e7 Console theme hacks from dr|z3d to target specific Opera oddity 2012-07-14 01:29:04 +00:00
f13a1b2aed Tweak to snark dark theme from dr|z3d 2012-07-14 00:54:05 +00:00
cec5838649 Added newsbullets from dr|z3d to the other themes 2012-07-14 00:48:28 +00:00
2c0de05e9d Updates to dark console and snark themes from dr|z3d 2012-07-14 00:18:16 +00:00
f782afef8d Add scrollbar width to iframe height, so vertical scrollbar doesn't appear
FIXME: add horizontal scrollbar detection so only adding the extra height
when it is actually needed.
2012-07-14 00:13:33 +00:00
a45688867d Add README.Debian to the package
Previously, README.Debian use to be installed to /u/s/d/i2p. A while ago I
split I2P into smaller packages and symlinked /usr/share/doc/libjbigi-jni and
/usr/share/doc/i2p to /usr/share/doc/i2p-router. I didn't noticed that
README.Debian wasn't being included after the split, so now I'm explicitly
adding it.
2012-07-13 00:41:44 +00:00
6104cfa56a el & nl after poupdate
I nearly always run ant poupdate before checking in language files; I neglected
to do it for the last check-in.
2012-07-13 00:35:25 +00:00
6869ed937b Translation updates from tx
- Add Greek language
- Update Dutch translation
- Adjust flag rows
2012-07-13 00:28:06 +00:00
945cc55b54 move check for binary down since osx binaries are no longer 'fat' 2012-07-12 22:43:12 +00:00
7e7cabfdc2 Added getElementsByClassName shim for IE 2012-07-12 21:47:09 +00:00
cbd61e2fce Tweak to get rid of iframe scrollbars in dark snark theme 2012-07-12 14:21:37 +00:00
ffdac3ce2c Added some non-breaking spaces to error.jsp where JSP was eating the spaces 2012-07-12 13:40:54 +00:00
eaa64cb02f Removed superfluous "Updating:" from update status (section has a heading now) 2012-07-12 13:39:32 +00:00
b36a418dff More changes to dark snark theme from dr|z3d - close to RTM 2012-07-12 13:37:51 +00:00
46ca3ab51d Used JS changes to fix iframed i2ptunnel scrollbar issue in CSS
Also added comments to the JS to point out that the issue is that
offsetHeight doesn't include the very top or bottom margins.
2012-07-12 13:34:19 +00:00
0deaab7c1a Improvements to resizing JS in iframed app container pages 2012-07-12 13:07:48 +00:00
c6d45b22b6 Added links to iframed pages to open them in new tabs (as per old console) 2012-07-12 10:32:00 +00:00
69bcc9d012 Dark snark theme updates from dr|z3d 2012-07-11 21:47:50 +00:00
182409ce3a Corrected titles of iframed pages 2012-07-11 21:44:02 +00:00
c45dc0c838 Moved add and delete buttons into sidebar config table, and removed unnecessary ones 2012-07-11 21:20:35 +00:00
7d175678ab CSS for the sidebar config table from dr|z3d 2012-07-11 12:06:59 +00:00
dd86515d2e Tweaks to dark snark theme from dr|z3d 2012-07-11 12:02:58 +00:00
b1a4b8bfed Rearanged columns of summary bar config table 2012-07-10 23:35:54 +00:00
ac9bdab78e Added class to summary bar config table 2012-07-10 22:48:59 +00:00
177b6e2d48 Link both icon and text for tracker and magnet entries in snark torrent info 2012-07-10 22:48:07 +00:00
b48014f8e6 Use favicon from theme in I2PTunnel if it exists 2012-07-10 21:29:49 +00:00
f1881352c8 Further tweaks to ubergine and vanilla themes by dr|z3d for new layouts 2012-07-10 21:10:58 +00:00
d6572fd027 Give update status section a heading to draw attention to it, display total size 2012-07-10 21:07:36 +00:00
7e5edc2f6e Changes to snark themes from dr|z3d to work with new layouts
(and a tweak to dark console theme)
2012-07-10 19:30:42 +00:00
709c75c517 Reordered torrent info section in snark filemanager - reduced to 4 info lines 2012-07-10 19:03:48 +00:00
ebc4d53fa9 Corrected output of message to notice instead of error on summary bar config page 2012-07-10 12:05:07 +00:00
dbd95c5c64 Added extra column to keep ordering buttons in line 2012-07-10 11:57:40 +00:00
42565f19fc Use image buttons for ordering summary bar sections (images courtesy of dr|z3d) 2012-07-10 11:42:20 +00:00
8d9909acfb To improve themability, make torrent info table in snark filemanager a single column 2012-07-09 21:17:02 +00:00
9c7f9a935b Only add new summary bar sections to the bottom 2012-07-09 20:35:58 +00:00
c9fc3f11a6 Replaced text fields for ordering summary bar with movement buttons (CSS styling needed) 2012-07-09 07:59:41 +00:00
75046d11fb Separated disabling of iframe refresh from the refresh time 2012-07-09 00:39:52 +00:00
bb39d9ddcf Integrated summary bar preset buttons into main edit form as restore buttons 2012-07-08 22:11:05 +00:00
fb629404c6 Split torrent info and dir info on snark filemanager into two separate tables 2012-07-08 21:35:49 +00:00
78691ba344 Updates to dark console theme from dr|z3d 2012-07-08 12:07:54 +00:00
f41fde8471 Updates to dark snark theme from dr|z3d 2012-07-08 12:05:17 +00:00
319d217dc1 Slight simplification of class injection for iframed app pages 2012-07-05 13:04:45 +00:00
ec80501977 Remaining size in snark filemanager reads GB instead of Gbyte etc (consistency) 2012-07-05 13:01:00 +00:00
4a0319389b A few more newlines to improve HTML readability 2012-07-05 12:22:56 +00:00
ebcc304642 Fixed a few colspans that were missed when adding icon/thumbnail td 2012-07-05 11:49:36 +00:00
c695a51883 Tidy up layout of rendered HTML for one row of snark filemanager 2012-07-05 11:47:41 +00:00
3cc447c5f2 Put icon/thumbnail for snark filemanager in separate td 2012-07-05 11:36:10 +00:00
52742ceeca Moved "View or change bandwidth" link inline to tidy up vertical alignment 2012-07-05 02:55:28 +00:00
08d86019e4 Uncommented input.default CSS (to hide extra Add button) in dark snark theme 2012-07-05 02:50:01 +00:00
582a62d75b Added classes to and removed hard-coded layouts from snark config page (stubbed in CSS) 2012-07-05 02:48:45 +00:00
8ebadf5236 propagate from branch 'i2p.i2p' (head 5b24a07e8a843d03ea45e664c59b93937c5efc42)
to branch 'i2p.i2p.str4d.fux' (head 0bfff6086d6f72df836909ae379a95ebbe4b6933)
2012-07-05 00:48:41 +00:00
814f5ca194 Arabic-specific console theming for dark from dr|z3d 2012-07-05 00:36:32 +00:00
3da63182cd Updates to dark snark theme from dr|z3d 2012-07-05 00:35:09 +00:00
029a903d79 Expanded resizeFrame() to prepare for more intelligent iframe-sizing 2012-07-04 12:49:54 +00:00
zzz
e2588a5379 move DecayingBloomFilter, DecayingHashSet, and xlattice filters from core to router 2012-07-02 19:22:33 +00:00
zzz
0d8bcd5dad * i2psnark:
- Don't send a keepalive to a peer we are going to disconnect
   - Disconnect peer when idle a long time
   - PeerCheckerTask cleanup
   - Static ref cleanup
   - Don't show a downloaded torrent file as "seeding"
   - Better torrent file download icon (from Silk, same license as the others)
2012-07-01 16:16:08 +00:00
zzz
63f22a54e1 fix unsafe initialization of super constructor calling override 2012-06-29 17:21:57 +00:00
zzz
ab18550711 * Update: Increase eepget timeouts to reduce retries 2012-06-29 16:25:22 +00:00
zzz
4092f61898 * Streaming:
- Allow at least 3 packets and up to half the window to be active resends
     instead of just 1, to reduce stall time after a packet drop
   - Increase fast retransmit threshold back to 3 to reduce retransmissions
   - Don't fast retransmit if we recently retransmitted it already
   - Allow double the window as long as gaps are less than the window
   - Don't set the MSS in a resent packet (saves 2 bytes)
   - Remove redundant calls to updateAcks()
   - Update activity timer when resending a packet
   - Reset unackedPacketsReceived counter at all places where acks are sent
     so it isn't wrong
   - Fix some places where the activeResends count could become wrong
   - Prevent storm of CLOSE packets
   - Never resend the whole packet in ackImmediately(), just send an ack
   - Cancel flusher timer in MessageOutputStream when closed
   - Move some createRateStats to ConnectionManager to reduce repeated calls
   - Cleanups, javadocs, logging, volatile, finals
2012-06-29 14:53:53 +00:00
zzz
ebb6609a2b fix SimpleTimer logging 2012-06-29 14:05:39 +00:00
820345f84d Changes to dark theme from dr|z3d to improve look of tunnel wizard 2012-06-29 13:49:37 +00:00
zzz
5a1d52d82c * HTTP Proxy: Change the error code for unknown host from 404 to 500. 2012-06-29 13:30:24 +00:00
9adb97d300 Tweak to dark console theme .iframed .panel margins 2012-06-28 03:36:31 +00:00
eae4d704a1 Copied body.iframed CSS from dark i2ptunnel.css to dark snark.css 2012-06-27 03:26:22 +00:00
84cc6711b4 Dark theme for i2psnark from dr|z3d - for better integration with router console 2012-06-27 03:19:30 +00:00
5be02b1592 Fixed JS for iframed apps so the CSS class is injected on page change 2012-06-27 02:16:21 +00:00
255894e241 Embed /i2psnark/ in an iframe like /i2ptunnel/ 2012-06-27 02:00:30 +00:00
zzz
6c8c87b2dd javadocs 2012-06-25 19:29:51 +00:00
zzz
dba3fee477 - Concurrent PeerCoordinatorSet
- final infoHash in Snark
2012-06-25 18:20:18 +00:00
zzz
50fba8fc8d propagate from branch 'i2p.i2p' (head db152f1a9e08e80c7bd3b87735b51800e8f4c46f)
to branch 'i2p.i2p.zzz.dhtsnark' (head 9b08b2f47961167d0fee52b6481895c494d410d6)
2012-06-24 19:53:20 +00:00
zzz
5eab417134 propagate from branch 'i2p.i2p' (head db152f1a9e08e80c7bd3b87735b51800e8f4c46f)
to branch 'i2p.i2p.zzz.test' (head 5fd7a423338073ff81f5118cde74317b567846a6)
2012-06-24 19:53:08 +00:00
zzz
ff0bfb9f12 * i2psnark: Don't create a new PeerCoordinator after restart, as the
TrackerClient holds on to the old one and that causes it
             to not get peers. Possibly fixes ticket #563.
2012-06-24 19:26:23 +00:00
zzz
1671e3b126 Fix bad size estimate when tags are included in the AES block,
resulting in trailing zeros after the random padding in the unencrypted AES data block.
The number of zeros equaled the number of tags included (typ. 6 or 40).
As the data size is rounded up to the next multiple of 16,
this increased the size of the data by 0, 16, 32, or 48 bytes when tags were included.
Bug introduced 2004-10-30.
2012-06-24 19:23:05 +00:00
zzz
fe53501990 * GarlicMessage:
- Put data clove last to speed acks and leaseset store on far end
  - Change release target
  - Javadocs and cleanups
2012-06-24 14:41:50 +00:00
zzz
e497859587 * ElGamal/AES/SessionTag:
- Increase TX expire from 10 to 12 min, while keeping RX expire at 15 min.
    3 minutes should be plenty of clock skew + delay.
  - Move tags-to-send and low-threshold values to be per-SKM
  - New session config options crypto.tagsToSend and crypto.lowTagThreshold
  - Prep for per-packet override of tags and thresholds
  - Cleanups and Javadocs
* I2PTunnel: Add some defaults for the new session config options
* OCMOSJ:
  - Don't bundle LeaseSet just because we're requesting an ACK
  - Changed session config option shouldBundleReplyInfo to default to true
    and be used to disable bundling altogether when set to false.
    Was previously an undocumented option to force bundling with a certain probability.
  - Don't send tags unless we've already generated a reply token (race)
  - Cleanups and Javadocs
2012-06-24 13:17:52 +00:00
zzz
97b05b1dbf * I2PTunnel: Fix NPE on shared client creation, thx kytv
* Transport: Add Ethiopia to hidden mode list
 * Log and javadoc tweaks
2012-06-24 11:38:37 +00:00
588799a2ff Update to dark theme from dr|z3d 2012-06-24 01:15:06 +00:00
zzz
d5a1e0b1c6 - Add kad lib, from i2p.zzz.kademlia branch (without the history),
which is a rewrite of the netdb kad
- Drop now-unused SHA1Comparator
- Efficiency tweak to NodeInfoComparator
2012-06-22 17:39:41 +00:00
zzz
5883b7344e propagate from branch 'i2p.i2p' (head 80aed456e1c6e4b17906153c9ee6dc9bc45e0eec)
to branch 'i2p.i2p.zzz.dhtsnark' (head dbf88ff4c1429f26656ad34fe0b9ba94305d726a)
2012-06-22 15:13:04 +00:00
zzz
8522779df1 - Switch to real kad with lib from i2p.zzz.kademlia (not checked in yet)
- Bootstrap only once in explore thread
- Add exploring to explore thread
- Don't store default DHT setting in config file, so we can switch default to true later
- Add new enforce-protocol streaming config, sorry locks out < 0.7.1 peers
- Log tweaks
2012-06-22 15:12:43 +00:00
7976ba1dff Only display summary bar "Show news" link on /home (/console already has one) 2012-06-22 01:32:07 +00:00
e88ca3048c Removed unnecessary extra sentences from summary bar tooltips 2012-06-22 00:08:43 +00:00
8412bafc5c Use JS injection to allow iframe-specific I2PTunnel styling 2012-06-21 23:47:49 +00:00
zzz
2a8adcb89a * IRC Client: Don't flush output unless out of input, so the
streaming messages don't get split up unnecessarily
2012-06-21 21:16:54 +00:00
zzz
829e3f47ff Store context in the PeerSelector so we don't have to pass it around 2012-06-21 20:52:39 +00:00
zzz
4e4634496a * TunnelPool: Fix bug where a tunnel was marked as reused when it wasn't 2012-06-21 20:26:29 +00:00
zzz
d148efd458 * TunnelPoolManager: Use one ClientPeerSelector for all pools 2012-06-21 20:08:02 +00:00
zzz
f7656b0401 * TunnelInfo: Change msg counter from long to int 2012-06-21 19:59:29 +00:00
zzz
6635448bda * I2CP: Make separate message ID counters per-destination, use atomic,
increase max (could have caused "local loopback" problems)
2012-06-21 19:52:00 +00:00
zzz
baa89c5bbf * OCMOSJ, ElG, Streaming: log tweaks 2012-06-21 19:10:14 +00:00
ab1144865f propagate from branch 'i2p.i2p' (head 204c786941e233e9ed0e447499049f5c21103f40)
to branch 'i2p.i2p.str4d.fux' (head 5bc2b4f4713087086e44eca6678d880651fa717a)
2012-06-21 04:05:48 +00:00
4348ff2689 Added custom bullet to newsheadings li 2012-06-21 01:54:28 +00:00
33c4b321db Fix up alignment of news headings in other themes 2012-06-20 12:56:00 +00:00
zzz
39d9a25e19 log tweak 2012-06-20 12:22:43 +00:00
zzz
b5dad73f6f * I2PSession:
- Greatly simplify the VerifyUsage timers
   - Constructor cleanup
2012-06-20 12:18:57 +00:00
99eb49e347 Added favicons to resident themes (dr|z3d's dark favicon for dark and midnight themes) 2012-06-20 02:55:44 +00:00
11f111790e Use a theme-specific favicon.ico if it exists 2012-06-20 02:48:24 +00:00
zzz
f8e470c7f4 propagate from branch 'i2p.i2p' (head a38c8874bc61e9bf11c4d43666ad72cd5eecbf8a)
to branch 'i2p.i2p.zzz.dhtsnark' (head 028e93195ce28c8b6fbe573e6c660d5c329df42a)
2012-06-20 01:19:25 +00:00
zzz
d8a2e39006 from last checkin - fix enforce proto default, fix http client delayed start 2012-06-20 01:19:05 +00:00
zzz
c6d1c552f8 propagate from branch 'i2p.i2p' (head 9ca94e960929c6af5dea1085105278d2f33217f2)
to branch 'i2p.i2p.zzz.dhtsnark' (head 1f23a71b0fa5169c220f3f21dd705e1fcfbb1b5d)
2012-06-19 23:31:53 +00:00
zzz
e383477b01 * Socks: Pass remote port through
* I2PTunnel: More javadoc warnings on default options
2012-06-19 21:48:31 +00:00
zzz
129b16d93d * Streaming:
- Listen only on local port if set
   - Listen only for streaming protocol if configured (new option)
   - Javadocs re: ports
2012-06-19 20:26:46 +00:00
zzz
48f29ff1b8 - Hide buttons while stopping all 2012-06-19 20:24:30 +00:00
zzz
d368937bce dash sixteen 2012-06-18 21:40:05 +00:00
zzz
4b3ccabb44 - Thread task to open tunnel and improve UI feedback while open is pending 2012-06-18 21:07:34 +00:00
zzz
4dcfe3e434 * i2psnark:
- Improve torrent shutdown handling to maximize chance of
     announces getting to tracker
   - Clean up delete-torrent messages
   - Remove redundant shutdown hook
   - Avoid NPE in PEX message handling
   - Log tweaks
2012-06-18 18:06:47 +00:00
zzz
273d7399a0 jump table tweak 2012-06-15 01:30:58 +00:00
zzz
5ce0479268 tweaks 2012-06-15 01:29:29 +00:00
zzz
de3ce6cdb7 handle jump servers with ports 2012-06-14 22:08:40 +00:00
zzz
3e192cc57e remove text about restart 2012-06-14 22:07:28 +00:00
zzz
6c5902837c * NetDB: Only publish stats every so often, to improve
anonymity while preserving the ability to monitor
          the network (effective next release)
2012-06-14 19:48:12 +00:00
zzz
e522ffad4e * I2PTunnel:
- More client options cleanups
   - Options changes now propagate to running
     socket managers and sessions, and through to the router
 * SocketManager:
   - Simplify factory, use 4-arg constructor,
     make fields final, deprecate 0-arg constructor
   - Improve how options are updated
   - Javadocs
2012-06-14 19:44:47 +00:00
zzz
64221fb3fb * I2PSocketEepGet: Use specified port
* I2PTunnel:
   - Don't strip port from an I2P URL
2012-06-14 19:42:07 +00:00
zzz
c73044b6b4 * Streaming:
- Channel cleanups and comments
   - New I2PSocketAddress
2012-06-13 19:08:49 +00:00
zzz
ad1b356879 * i2psnark: Possible fix for piece-after-choke 2012-06-13 19:04:11 +00:00
zzz
07caf2e316 * I2PSocketEepGet: Set port to 80
* I2PTunnel:
   - Pass port through HTTP client proxy
   - HTTP server proxy sets host header to
     the value of "spoofedhost.xx" option for port xx
   - Set client options more efficiently
2012-06-13 19:02:09 +00:00
zzz
c2137a2a80 - Add explore thread
- More checks for stopping
- Add xor of port to secure NID
2012-06-12 21:38:25 +00:00
zzz
44da37f009 - Timeout if can't find b32
- Refactor ReplyWaiter
2012-06-12 19:22:31 +00:00
zzz
d0b967388a rework DHTNodes to hide the CHM implementation, in prep for real Kad 2012-06-12 18:30:58 +00:00
zzz
41096c7f23 - Add heardAbout() and call for receive peers
- Move last-seen tracking from NodeInfo to NID, add fail tracking
- Make NodeInfo fields final
- Remove nodes on consecutive failures
- Only persist nodes heard from recently
- Implement NID verification for security
2012-06-12 18:09:42 +00:00
4f6fb6993d Pass requestURI through to xhr1.jsp so forms work and correct summary bar is chosen 2012-06-12 00:12:51 +00:00
fa3e3e0764 Enforcing minimum refresh rate better, and reducing it to 3 seconds
This does mean that the iframe don't-refresh state (= refresh rate of 0 seconds)
doesn't hold if the page is changed (in fact, the refresh rate becomes the
minimum) - so maybe better to have a different config var for that?
2012-06-11 23:33:33 +00:00
zzz
fe2b97c941 propagate from branch 'i2p.i2p' (head 27fc588723d201c76ea9c18a6c715b11efcb5b0e)
to branch 'i2p.i2p.zzz.dhtsnark' (head cae6d265415ba9ed4242b3fc888ffcf2a1c1b2f2)
2012-06-11 20:29:19 +00:00
zzz
6e52ae307c * Router: Don't let shutdown tasks hang the shutdown 2012-06-11 20:09:31 +00:00
zzz
6e077ee621 * i2psnark:
- Reduce TrackerClient threads
   - Reduce delay between peer adds for faster startup
   - Thread the announces and reduce timeout when stopping
2012-06-11 19:38:33 +00:00
zzz
30e2f73d5f * i2psnark:
- Display torrent file downloads in torrent area
   - Sort magnets and downloads first
   - Fix sorting problem when torrent dir is a symlink
   - Reduce max file idle time
   - arrow_down icon copied from console css
2012-06-11 12:04:40 +00:00
zzz
7469e9c63d * NativeBigInteger: Workaround for Raspberry Pi to load the correct lib 2012-06-11 11:48:34 +00:00
296ddbe930 Removed some unnecessary <hr>s 2012-06-11 05:32:53 +00:00
e20f2d0bf6 Summary bar for /home defaults to reduced preset, everywhere else to full preset 2012-06-11 05:29:27 +00:00
cc61f4eb61 Some changes to enable per-page sidebar configuration from the config file 2012-06-09 13:48:26 +00:00
0a61b8052c propagate from branch 'i2p.i2p' (head 44d553e8644f01d5e5af3c3145210bdb0a923d3c)
to branch 'i2p.i2p.str4d.fux' (head 51022349e906bd393602b558861bcaaac4d56c89)
2012-06-09 06:11:40 +00:00
cbcbfea6e8 Shorten /i2ptunnelmanager to /i2ptunnelmgr (/i2ptunnel is surplanted by /i2ptunnel/) 2012-06-09 06:08:44 +00:00
57abfe7653 Set position: absolute for div.routersummaryouter so /home app icons don't clear past it 2012-06-09 04:33:29 +00:00
zzz
e0313814b8 disable private config for default trackers 2012-06-08 16:39:10 +00:00
zzz
59df524a91 * i2psnark:
- Move private tracker config from create box to torrent config
   - Refactor private and open tracker configuration
   - Add private indication on details page
2012-06-08 16:11:55 +00:00
zzz
b304393bc3 * netdb.jsp: Don't show our info on summary page since there's a tab for it now 2012-06-08 16:10:26 +00:00
f6304ccd4d Commented out "All times are UTC" blurb because all graphs are labeled UTC 2012-06-08 12:58:50 +00:00
9d241cc0d4 Added tooltips to the various sections of the summary bar 2012-06-07 14:14:13 +00:00
a46ca210f5 Added some spaces to notification messages on /configsidebar 2012-06-07 11:11:37 +00:00
328857f97f Tweaks to config nav bar in dark theme from dr|z3d 2012-06-07 10:39:14 +00:00
b00fbfa23d Centre box for order of new section, and add default value 2012-06-07 10:26:44 +00:00
3a75f8d7d1 Modifying section addition to show an option list
Also adding form handler class that I forgot to commit earlier.
2012-06-07 04:42:52 +00:00
84344b6789 Added a config page for summary bar 2012-06-07 02:51:22 +00:00
b75d28fd0d Added /i2ptunnelmanager which wraps /i2ptunnel/ in an IFrame
/i2ptunnelmanager redirects to /i2ptunnel/ if the browser doesn't support IFrames.
2012-06-05 15:19:40 +00:00
a8424e59b0 propagate from branch 'i2p.i2p' (head ab5f37b28e499d49e108e8e6869164d107c7049e)
to branch 'i2p.i2p.str4d.fux' (head fb4425cd3e38762f211f73d91c7a173972e7145c)
2012-06-05 13:38:02 +00:00
420bf851b5 Added Ajax script to every routerconsole page that includes summary.jsi 2012-06-05 13:37:10 +00:00
83c8233812 Insert routerconsole.summaryRefresh config value into Ajax script
This way, both Ajax and IFrame get their refresh time from config (though
IFrame one can still be modified live).
2012-06-05 13:00:40 +00:00
52a3860717 Rearrange summary bar code to consolidate Ajax and IFrame, and /home and /console
Now, Ajax will be used first, and will fall back to IFrame if JS is disabled,
and a separate page if on a text or mobile browser.
Also, /home and /console (and everywhere) now all have the same summary bar
content, which currently defaults to the original full listing.
2012-06-05 12:44:17 +00:00
531c6c0f4c Implementation of customisable summary bar via routerconsole.summaryBar property
The initial attempt using mapping of strings to methods is shorter and neater,
but also doesn't work so is commented out.
2012-06-05 07:36:24 +00:00
5699b4515b Preparations for making the summary bar customisable 2012-06-05 02:12:30 +00:00
zzz
6a1b90f8f8 hash caching 2012-06-05 01:03:39 +00:00
ceedc9c645 Moved rendering of news headings into SummaryBarRenderer
An instance of NewsHelper needs to be passed in to get output.
2012-06-04 23:46:03 +00:00
zzz
3f40487c99 - Add persistent local DHT storage
- Shutdown now closes tunnel
- Delay after sending stop announces at shutdown
- Stub out using Hash cache
- Implement stop for all cleaners
- Log tweaks
2012-06-04 22:34:56 +00:00
zzz
a6f7761544 propagate from branch 'i2p.i2p' (head ab5f37b28e499d49e108e8e6869164d107c7049e)
to branch 'i2p.i2p.zzz.dhtsnark' (head afa1bbfb0882c9c1946ec32b87300e127c9928b2)
2012-06-04 14:26:10 +00:00
zzz
e1c9cd6cdc * i2psnark: Take tracker out of opentracker list when removed 2012-06-04 14:25:53 +00:00
zzz
d5cb443925 - Switch back from storing NID to full NodeInfo for outgoing tokens so they don't get expired early
- Announce only to the single closest DHT peer
- Increase random port range
- Decrease max local tracker and DHT size
2012-06-04 14:15:38 +00:00
9333cd56f9 Moved calls to external renderers into separate methods to match the rest 2012-06-04 13:28:52 +00:00
910001e3a1 Split HTML rendering of summary bar into separate methods
This solidifies the summary bar design pattern of modular sections separated by
<hr>s which in future could be reordered or hidden without affecting the theme.
2012-06-04 13:10:12 +00:00
zzz
121491a3be - B32 lookup if required for non-announce queries only
- Token timeout tweaks
- Most classes package private
2012-06-03 16:05:38 +00:00
zzz
152b2152cb - Fix node ID / node info confusion
- Fix updating node ID when receiving pong
- Fix getting DHT enable setting from config file
- Fix handling of get_peers replies
- Fix sending and receiving announces without signing
- Fix incoming/outgoing token handling
- Set cleanup timer for all queries
- More debug logging
2012-06-03 15:25:51 +00:00
0abbe45a6d correct flag country (I copied the line and changed all but the flag) 2012-06-03 15:02:27 +00:00
403d6a322a Italian language updates from tx & debconf updates
-10
2012-06-03 14:34:39 +00:00
4ec20ef796 typo fix (*facepalm*) 2012-06-03 14:09:41 +00:00
3b7eaa107e s/$ENGLISHNAME/$NATIVENAME/g 2012-06-03 13:52:27 +00:00
8375e9129d enable Hungarian in another spot 2012-06-03 13:46:33 +00:00
53fbece6b5 Hungarian language translation by AdminLMH from TX
Thanks!
2012-06-03 13:46:07 +00:00
69d909d3eb Added <hr>s to separate the various logs on /logs 2012-06-03 00:22:03 +00:00
zzz
4346c90aa2 lower log level 2012-06-02 21:45:00 +00:00
zzz
f8c185d09f prep for merging 2012-06-02 21:44:23 +00:00
zzz
558bb2f4f3 select proto on UDP send 2012-06-02 18:56:10 +00:00
zzz
7b07eb89a3 - Uncomment DHT
- Change DHT from option bit to extension message
- Add DHT start/stop code
- Add UI for DHT enabling
- Add raw datagram protocol type and use for response port
2012-06-02 18:52:46 +00:00
zzz
bec33cad87 propagate from branch 'i2p.i2p' (head f005cd64cce03cf3a301359f94380bc20eaa7c61)
to branch 'i2p.i2p.zzz.dhtsnark' (head 0562e4f429dcebf3f623d0975bd3a63d7645c0b7)
2012-06-02 15:16:14 +00:00
zzz
eb6217add9 add failsafe check for job queue sort order 2012-06-02 13:49:38 +00:00
zzz
7d94f9fb19 improve opentracker configuration 2012-06-02 13:49:14 +00:00
zzz
324e9c960d fix partially-complete torrent not announcing 2012-06-01 14:12:32 +00:00
zzz
96575e61f2 * Console: Tab the netdb and profile pages 2012-06-01 13:30:38 +00:00
zzz
8d57cba762 * NetDB: Reduce flood redundancy from 8x to 6x 2012-06-01 13:27:53 +00:00
zzz
e1823ece68 whitespace fix 2012-06-01 13:27:01 +00:00
b23414eab1 Reordered /home summarybar, added <hr> after update download button 2012-06-01 01:21:57 +00:00
zzz
38a4f05000 * i2psnark:
- Close connection immediately if bad protocol,
     this makes blacklist work better too
   - Stop adding peers when we hit the limit
   - Lower limit for outbound connections so we give
     new peers a better chance in large swarms
2012-05-31 15:20:50 +00:00
zzz
041c87a2c9 * i2psnark:
- Add per-hour conn limit
   - Blacklist peer after two bad handshakes
   - Reduce connect timeout
2012-05-31 12:19:27 +00:00
ef06fc758c Show news headings in the /home sidebar when the main news div is hidden
Note: this now refreshes along with the rest of the sidebar, so the news.xml
file is read once every 15s - it may be better to cache the headings somewhere,
though it's debatable whether reading the cache file is better than reading the
news.xml file...
2012-05-31 09:30:30 +00:00
9f1c95c829 rework patch to deal with my earlier changes to jcpuid's build.sh 2012-05-30 20:42:06 +00:00
zzz
f14ff31a20 * Timestamper:
- Move from core to router, leave stub in core
     so it doesn't break compatibility. This removes a
     thread in app context and prevents any app context from
     running NTP; external clients must use the time
     received from the router.
   - Increase query interval
2012-05-30 20:03:30 +00:00
zzz
ddc329e8f1 Handle URI encoding, UTF-8, and multiple trackers in magnet links 2012-05-30 19:45:45 +00:00
zzz
8453c34bfc Improve rarest-first behavior by not favoring a partial piece
held by multiple peers when requesting from a seed
2012-05-30 15:21:37 +00:00
zzz
c6fcdf967c increase per-minute conn limit from 6 to 8 2012-05-30 14:10:03 +00:00
zzz
1427c502c0 Reduce log level to warn for normal EofException when generating graphs 2012-05-30 14:09:16 +00:00
zzz
4e84370128 Increase max upstream form field to 4 digits (9999) ticket #645 2012-05-30 14:06:45 +00:00
zzz
5314d886be merge of '539c9ff2992eb584e2c311924370437f167f0b99'
and 'b355f1862601a6a192c42f1f50743f2a4f0b4c22'
2012-05-28 22:20:19 +00:00
zzz
829af21c49 javadoc fix 2012-05-28 18:04:11 +00:00
c9406b8f96 document changes for Arch 2012-05-28 17:01:31 +00:00
af398632f3 Add initscript support to i2prouter for ArchLinux
With this commit, ArchLinux users can run "i2prouter install" to install an
initscript to /etc/rc.d.
2012-05-28 16:59:47 +00:00
e574b5e61a minor updates to jbigi/jcpuid scripts
- use my find-java-home stub script when building jcupid
- move find-java-home to core/c
- add support for ArchLinux to find-java-home
2012-05-28 16:56:08 +00:00
d946fda859 Replace reseed host i2pbote.net with euve5653.vserver.de (http://trac.i2p2.i2p/ticket/445) 2012-05-26 07:25:44 +00:00
df3771e791 merge of '60890b0ffbf109b297bd6dd66a28d8179322331a'
and '7e63908db8b78229f8f0c963050b062ce0f6dd96'
2012-05-25 21:09:25 +00:00
zzz
df00725077 compile fixup after merge 2012-05-25 20:30:06 +00:00
zzz
26846d592c merge of '10bd7656ab9a474e3bb2d405d50261f147690ce4'
and 'c3f96ef85962a5e9a3896e117036cc83b37fe3a8'
2012-05-25 20:11:28 +00:00
zzz
21466e017f explicit merge of 'dd3f93f7ec59e6a7f967945a75c5d4d7b53539ed'
and 'e99d32aef6da4112890ebe09c3f7d8fcb8b647e5'
2012-05-25 19:55:02 +00:00
zzz
b033db969c Revert all changes to the org/cybergarage library
in the 2009-08-11 whitespace cleanup at ef1c23821d433903124f7612cbc46ac096fc985b
to make merging with the newer library easier.
2012-05-25 19:52:39 +00:00
zzz
d18e4d430c explicit merge of '59eae97dbb470d8c4a1e4dba3a9763e134bb0c53'
and 'aeec86a504a5fd67dff12d6775411a9c865d42ad'


Merge to aeec86a504a5fd67dff12d6775411a9c865d42ad
which is just before the 2009-08-11 whitespace cleanup at ef1c23821d433903124f7612cbc46ac096fc985b
2012-05-25 19:36:22 +00:00
zzz
14ac5ac03e Cyberlink for Java v2.1 (2011-09-16) from SVN,
with extra parsers in xml/parsers removed.
Diverging from original v1.7 checkin in prep for merging.
2012-05-25 17:47:18 +00:00
464279ca1c German, Spanish, and Swedish translations from Transifex 2012-05-25 12:40:12 +00:00
6014de9cd5 Italian translation updates from Transifex (thanks colombo-bt!) 2012-05-25 11:05:22 +00:00
e7c3e07626 A few more CSS changes to finish tweaking /home.jsp for the other themes 2012-05-25 05:27:46 +00:00
c4057bb5a0 A few trivial CSS changes to get /home.jsp looking better with the other themes 2012-05-25 01:27:48 +00:00
34f0420753 Changes to dark theme
- improve /home.jsp
- general theme tweaks and fixes
2012-05-25 00:58:29 +00:00
10bd1343c3 Changes to structure of /home.jsp to bring it back in line with /console.jsp 2012-05-25 00:43:29 +00:00
zzz
4979f8dace - Custom tracker map AIOOBE fix, thx kytv 2012-05-23 22:40:03 +00:00
zzz
b2846de94f propagate from branch 'i2p.i2p' (head 8066e0ff00b526c6971e77de44ff2d322f25069a)
to branch 'i2p.i2p.zzz.dhtsnark' (head f857dd921a7c806c85eb80419f4f9fdd3b6428a2)
2012-05-23 16:56:13 +00:00
zzz
501f2f85d5 * jobs.jsp: Add more queue diagnostics 2012-05-23 16:37:43 +00:00
zzz
580bb5a6fe * i2psnark:
- Fixes when complete except for skipped files
     (ticket #447) status in UI, don't connect outbound,
     disconnect seeds when done
   - More classes pkg private
2012-05-23 16:36:37 +00:00
zzz
e27df771aa - Fix deadlock when changing file priorities 2012-05-22 19:26:37 +00:00
zzz
0f321f1597 - Refactor tracker map 2012-05-22 18:19:52 +00:00
zzz
10872f751e - Prevent torrent shutdown when changing file priority to skip 2012-05-22 18:18:30 +00:00
zzz
20567ae75e * RoutingKeyModifier: Update after large clock shift 2012-05-22 18:16:51 +00:00
zzz
f06d99480d javadoc fixes 2012-05-21 12:53:56 +00:00
zzz
c2e39687e6 * RoutingKeyModifier: Several changes to ensure the routing key
is correctly changed just after midnight.
2012-05-20 18:20:48 +00:00
zzz
6972d9d02b change stat name, log tweak 2012-05-20 18:18:58 +00:00
zzz
d8b3d2c508 * i2psnark:
- Create sparse files at torrent creation and delay
     "ballooning" until first write (ticket #641)
   - Redo clear messages button
   - Concurrent message queue
2012-05-20 18:15:36 +00:00
zzz
1da1dce981 * Profiles: reduce same-country bonus 2012-05-20 18:13:11 +00:00
zzz
c4f9485e13 * Console: Add full file path to thread dump message 2012-05-20 18:12:41 +00:00
zzz
9cff4d5a42 final 2012-05-20 18:12:20 +00:00
zzz
6ca4b519bf * i2psnark:
- Store received chunks in temp files
   - Don't allocate from heap for unneeded chunks
   - Remove peer count restriction for torrents with large pieces
   - Use priorities and rarest calculations to sort partials
   - Preserve p parameter in clear messages link
2012-05-19 13:27:02 +00:00
zzz
3685bf04d0 add X-Frame-Options to console headers 2012-05-13 13:05:17 +00:00
zzz
fc5e30e6ae better throttle log msgs 2012-05-09 21:07:33 +00:00
zzz
047c668ee1 dont include wrapper .txt files in installer 2012-05-09 21:06:59 +00:00
zzz
e55a1f608a comment out unused text summaries 2012-05-09 21:05:34 +00:00
cbbf82a4ae Stricter match by matching 'Version:' 2012-05-09 02:18:20 +00:00
81d9e2f164 update changelog 2012-05-06 10:36:22 +00:00
8397296286 Add 'graceful' / comment out stanza not needed on Ubuntu 2012-05-06 10:35:59 +00:00
06d0412558 Spanish & German translation updates from transifex
(These were sent to transifex too late to make it into 0.9)
2012-05-05 20:30:08 +00:00
ffde067c5e s/Reunion/Réunion/ 2012-05-05 16:00:37 +00:00
zzz
c4a05ec49e add missing wrapper.jar to installer-nowindows 2012-05-02 13:14:44 +00:00
zzz
ed92411df2 installer target fixes 2012-05-01 23:41:04 +00:00
zzz
7a690b245f rename windows installer; 0.9 2012-05-01 21:06:17 +00:00
7af65f4346 Update russian translation 2012-04-29 17:31:24 +00:00
c89e127d8a Added missing test classes to net.i2p.data.i2cp 2012-04-27 03:00:45 +00:00
104bfa8784 Exclude TestSuite classes from testing 2012-04-27 02:45:00 +00:00
3013b56d16 Fixed server tunnel wizard bug - pass a value for privKeyFile 2012-04-26 23:01:12 +00:00
188316132e refer to translation updates 2012-04-26 18:37:18 +00:00
53c7f7d602 use our jstl.jar instead of glassfish 2012-04-26 18:19:48 +00:00
3ccc102412 German, Spanish, and Swedish translation updates from Transifex 2012-04-26 18:19:07 +00:00
e99749097a Moved tests for net.i2p.data.i2cp into the correct subdirectory 2012-04-26 12:08:14 +00:00
zzz
9d1995125a xml fix 2012-04-21 14:43:51 +00:00
b18f654e37 look for ^# so there aren't surprises later (thanks for pointing it out, darrob!) 2012-04-20 20:14:59 +00:00
a70d9394da pushed to tx after fixing the bundle-messages script 2012-04-20 19:11:58 +00:00
6eff7be49a Avoid catching comments in countries.txt 2012-04-20 18:09:40 +00:00
zzz
01efcd3156 merge of '4f47546a7fcac5e20d0d9ac04bcae904bb155cc9'
and '71ed1d74f02291ae56a495e97fae65970bfbdd30'
2012-04-20 13:55:39 +00:00
zzz
112b88a7f9 * Fix i2psnark dir when started from Windows no-wrapper (ticket #627) 2012-04-20 13:44:41 +00:00
c61b73c281 use <os family="unix"> instead of just <os = 'unix'> 2012-04-20 13:26:23 +00:00
zzz
79a7c14fdf remove search box 2012-04-20 13:25:29 +00:00
e4513d18b9 manpage updates 2012-04-19 23:25:32 +00:00
7870ededcb Updated en locale files pushed to tx 2012-04-19 23:24:20 +00:00
195a4e971d merge of '06de0d416fefcfbd6ce12716ce5fed4eefe8a711'
and 'e78c33c88a31edac9e53dd6cdf973886ecabd996'
2012-04-18 18:48:26 +00:00
aa02358b1b disapproval of revision '78927d1f7087f93dca9a96ac375d05d01596fb74' 2012-04-18 18:48:20 +00:00
18fe05b9ef merge of '06b8a711c60dbb6a9d628c4c5ffd8b205176271a'
and 'c1edfeadb75181e65ad8d4c0005b861f3563dd5b'
2012-04-18 18:47:53 +00:00
5b5b3b203c s/unix|mac/unix/ 2012-04-18 18:47:43 +00:00
49cee5bf58 disable the 'convenience links' in /etc/i2p. 2012-04-18 01:45:08 +00:00
ac1b51c9ac Fix installer-windows build target
Izpack tries to parse the 'unix-only' files in Windows. If we don't copy them,
the installation will fail in Windows. Therefore, moving the files that were in
the preppkg-unix target to the preppkg-base target. They're so small I don't
see how this will be a problem.
2012-04-18 01:44:17 +00:00
746b91f257 Since it was suggested, I'm adding my eepsite 2012-04-18 01:41:38 +00:00
77d970fd5a debian: initscript: reorder, change some exit statuses 2012-04-17 12:33:30 +00:00
678c87d0f4 bump version to -27 (debian package fix and people can get the new wrapper & geoip) 2012-04-17 11:49:45 +00:00
f352a38836 revert back to linking to glassfish-javaee.jar (fixes susidns and i2pbote in Debian) 2012-04-15 22:19:44 +00:00
d402f0c93d history update 2012-04-14 17:46:17 +00:00
7093a57a03 update GeoIP using Maxmind's April 2012 db 2012-04-14 17:44:30 +00:00
6a9432d62e Update the Tanuki Java wrapper to v3.5.14
All are from the "delta-pack" with the following exceptions:

* binaries for FreeBSD have been compiled in FreeBSD 7.4 to eliminate the
  dependency on FBSD v6 compatibility libs. They've also been stripped.
* Tanuki doesn't offer win64 binaries. Since the x64 binaries had to be
  compiled anyway, I've added an itoopie icon to the binary and both x86 and
  x64 binaries are compiled by me.
* Linux PPC binaries have been compiled by me since upstream mistakenly ships a
  64bit PPC binary as a 32bit binary.
* Linux ARMv5 have been compiled by me.
* The Linux x64 and x86 binaries have been stripped.
2012-04-14 17:38:08 +00:00
7440e64eb6 dropping FAT osx wrapper before adding new 'non-FAT' one 2012-04-14 17:25:49 +00:00
zzz
97436e8357 Wrapper files for armv7.
Compiled on trimslice:
    gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) 
    java version "1.6.0_18"
    OpenJDK Runtime Environment (IcedTea6 1.8.13) (6b18-1.8.13-0ubuntu1~11.04.1)
    OpenJDK Zero VM (build 14.0-b16, mixed mode)
    Wrapper 3.5.14 GPLv2
    All binaries stripped.
2012-04-14 14:16:36 +00:00
f9ff90eb72 update comments 2012-04-12 21:05:19 +00:00
6acece85a2 German, Spanish, and Swedish translation updates from Transifex 2012-04-12 20:25:52 +00:00
zzz
2d24f01831 - Non-default theme cleanups
- Theme and translate home page logo
2012-04-12 17:54:44 +00:00
zzz
f2b228561f strip DOS line endings to make diffing to other themese easier 2012-04-12 15:29:26 +00:00
zzz
ce9cae4ff8 fix whitespace issues 2012-04-12 15:21:21 +00:00
zzz
c3a387d646 Try again to fix console on Windows w/o IPv6 by starting connectors individually 2012-04-12 14:54:50 +00:00
zzz
b24085bbe5 prevent very early NPE 2012-04-12 14:48:18 +00:00
41419738c5 add keys.i2p 2012-04-09 15:27:07 +00:00
dd65f174ef Debian: Explicitly create the home directory in the postinst
Older versions of adduser create it automatically. The version in Debian unstable apparently does not...
2012-04-08 20:35:30 +00:00
d888d4834d Debian: update symlinks 2012-04-03 21:17:14 +00:00
ce8cd91d72 propagate from branch 'i2p.i2p' (head 3002b47d5d20180f84fb6a4f161823bc751989be)
to branch 'i2p.i2p.unittests' (head 97dc8de19916c3d0c7ec42790800c1e23f9ce9e8)
2012-03-30 05:10:53 +00:00
0cefaba925 More excess whitespace removed 2012-03-30 04:40:56 +00:00
zzz
565807126c * RouterInfo: Fix fatal NPE in last checkin 2012-03-28 19:17:39 +00:00
zzz
bed548d5af 24 2012-03-27 20:34:40 +00:00
zzz
fbd230f21e * RouterInfo: Add method to validate sig during read-in for speed 2012-03-27 20:25:26 +00:00
zzz
dc402acd4a * FloodfillVerify:
- Fix verifies stuck on one peer by blamimg the verify peer on failure
    - Follow DSRM in response to RI verifies, to help integration
    - Increase floodfill verify timeout
2012-03-27 20:10:30 +00:00
zzz
e3dab56e79 Delay router down message until two consecutive fails 2012-03-27 20:08:11 +00:00
zzz
d3578e2a2e graphs CSS tweak 2012-03-27 20:07:30 +00:00
zzz
7a6e7baf18 generics and final 2012-03-27 20:07:12 +00:00
97f23286d3 allow SHOUTcast/icecast to work over the http proxy + cleanups 2012-03-27 17:41:02 +00:00
zzz
11ff89fef2 - Try again to fix console on Windows w/o IPv6 (ticket # 621) 2012-03-26 14:07:38 +00:00
zzz
764a7f2e13 remove unused private items (eclipse) 2012-03-26 00:52:06 +00:00
zzz
1db58dee89 remove unused local variables (eclipse) 2012-03-25 22:16:03 +00:00
zzz
f13956d380 remove unused imports (eclipse) 2012-03-25 21:45:18 +00:00
zzz
522ae4eb41 fix fallthrough 2012-03-25 20:52:57 +00:00
zzz
3e889d2747 remove unnecessary casts (eclipse) 2012-03-25 20:42:41 +00:00
zzz
ed13424913 - Move oldconsole rendering from Router to OldConsoleHelper 2012-03-24 23:04:05 +00:00
zzz
bdfca07626 - Fix dup torrent msg with magnets (tickets #433 and #504)
- Fix state when storage dies during transition out of magnet mode
    - Fix NPE in magnet mode
    - Error logging improvements
    - Support add-torrent with infohash alone
    - CSS tweaks
2012-03-24 12:58:13 +00:00
ba3bc9e2ed Uncommented two Datagram tests that currently throw NullPointerExceptions 2012-03-24 11:18:37 +00:00
f164951848 Actually test something in DatagramTest.testBadagram, remove excess whitespace 2012-03-24 08:04:49 +00:00
bfaf72a547 Added a couple of tests for net.i2p.client.naming 2012-03-24 05:09:31 +00:00
zzz
008c79e743 comment out unused GarlicConfig methods 2012-03-23 17:02:32 +00:00
zzz
0d565818d4 final 2012-03-23 17:01:34 +00:00
zzz
c1a7f90860 test cleanup 2012-03-23 17:01:24 +00:00
zzz
c2e36453b8 undeprecate for syndie 2012-03-23 17:00:51 +00:00
zzz
675e8a91a4 * RetransmissionTimer: Instantiate per-destination 2012-03-22 20:40:35 +00:00
zzz
cae94320b5 remove FlushTimer 2012-03-22 19:55:40 +00:00
zzz
bafef846d9 * SimpleScheduler, SimpleTimer, SimpleTimer2: Replace static instances
with I2PAppContext-rooted references
2012-03-22 19:53:05 +00:00
zzz
db42a46c71 home page CSS 2012-03-22 19:49:48 +00:00
zzz
adcd1e8d9a * Reseeder: Get rid of static instance, root in netDB,
don't use system properties for status
2012-03-22 19:47:44 +00:00
zzz
ca57b71266 * Router: When removing a config setting, remove from context also 2012-03-22 19:40:17 +00:00
zzz
e3da181cea * NetDB:
- Don't reread RI if netdb date is recent
    - Prevent LS/RI overwrites
    - Disallow hash mismatches in RI files
    - Reseed won't fetch our own RI
    - Reseed won't overwrite recent RIs
2012-03-20 18:38:28 +00:00
zzz
3da6ccfbbe make runRouter() public 2012-03-20 14:52:22 +00:00
zzz
444ba47463 * i2psnark: Message area tweaks and clear link 2012-03-20 14:09:17 +00:00
377aa9bca1 In core build.xml, tell junit to look for the hamcrest libraries
hamcrest-core.jar, hamcrest-library.jar and hamcrest-integration.jar should be
present in $ANT_HOME/lib/ (real or symlinked).
2012-03-20 00:29:14 +00:00
4ffbfce51e Plugins: Order and reverse order plugin names for start/stop all cases. 2012-03-19 12:37:39 +00:00
d1ed30e79c Plugins: less confusing message, fix CNFE by catch and ignore on delete. 2012-03-19 01:46:47 +00:00
zzz
8ca5591933 fix check aliases override 2012-03-18 14:05:14 +00:00
zzz
7d9db79619 fix build 2012-03-18 00:07:46 +00:00
zzz
508533b83e -17 2012-03-17 22:07:51 +00:00
zzz
0a521b7456 * BuildHandler: Implement restart and shutdown to stop the thread 2012-03-17 21:58:41 +00:00
zzz
0c348ec17e * FileUtil: Better logging of weird unpack200 errors 2012-03-17 21:56:20 +00:00
zzz
df8bab6b72 * Streaming: Scheduler cleanup 2012-03-17 21:55:05 +00:00
zzz
f6fb4b4be3 news header 2012-03-17 21:53:14 +00:00
zzz
0f74ccd446 add servlet version 2012-03-17 21:52:48 +00:00
zzz
02bde80725 * Jetty: Don't extract wars 2012-03-17 21:52:17 +00:00
zzz
e2a39a3eb5 * I2PTunnel: Make CLI-only methods private, and static where possible 2012-03-17 21:49:11 +00:00
zzz
3a2fe5e860 * OCMOSJ: Refactor cache to its own class, make non-static 2012-03-16 12:20:29 +00:00
zzz
fb8244ee1a javadoc 2012-03-16 12:18:04 +00:00
zzz
045627a583 throw ISE on attempt to change peer value 2012-03-16 12:17:07 +00:00
zzz
e7898b5b8f * FragmentHandler: Zero-copy read of unfragmented messages
for speed and to reduce object churn
  * FragmentedMessage cleanup
2012-03-16 12:16:05 +00:00
zzz
080f435708 * TransportManager: Fix fatal exception on soft restart caused by DHSKB refactoring 2012-03-16 12:13:20 +00:00
zzz
f2a3d597dd * Jetty: Add I2P mime types to default eepsite config 2012-03-16 12:11:31 +00:00
zzz
74743357d0 * TrustedUpdate: Preserve default key names even when keys are set
in advanced config
2012-03-16 12:09:49 +00:00
zzz
1de88909e9 * Home page: Tag tooltip; CSS tweaks; news tweak 2012-03-16 12:08:43 +00:00
zzz
f7dc55f087 * HTTP Proxy: Jump and addresshelper page tweaks 2012-03-16 12:07:13 +00:00
67da35ab35 Commented out test for net.i2p.data.Lease.getNumSuccess() / getNumFailure 2012-03-16 05:08:31 +00:00
136d77a8aa Fix YKGenerator test - methods no longer static 2012-03-16 04:51:55 +00:00
bf0b59b3b3 Remove DHSessionKeyBuilderTest from CryptoTestSuite 2012-03-16 03:18:53 +00:00
f19bc6a4b0 Moved DHSessionKeyBuilderTest to net.i2p.router.transport.crypto 2012-03-16 03:08:10 +00:00
79ab065500 propagate from branch 'i2p.i2p' (head e1c79060790ae24b2a96aff2857477d6528ae7c6)
to branch 'i2p.i2p.unittests' (head 357abd51672dd9950cbac6dd30ded117fe5695e0)
2012-03-16 02:30:42 +00:00
9d07bc241c Replace hostname included by junit in reports with "i2ptester"
The fake hostname can be overridden by setting the host.fakename property.
2012-03-16 01:41:23 +00:00
d9ba62aa2c Update paths, add new files 2012-03-15 14:51:34 +00:00
1f407bc34f Plugins 2012-03-15 00:39:11 +00:00
zzz
1aa6367517 orb->org 2012-03-14 12:45:27 +00:00
zzz
2436d86000 * DHSessionKeyBuilder:
- Move from core to router/transport
    - Make non-static, instantiate in TransportManager
    - Generate keypair in constructor and make final
      to move more processing to the precalc thread
      and eliminate races
    - Synchronize getSessionKey() to eliminate races
    - Comment out unused methods
  * UDPTransport:
    - Make key builder final in InboundEstablishState to
      eliminate rare NPE (ticket #406)
    - Remove unused static instance
2012-03-14 12:09:02 +00:00
zzz
17f9280b3f * Jetty:
- Set default cache-control for webapps and eepsite
    - Disable dir listing for console webapps
  * WebAppStarter: Remove static log
2012-03-14 12:04:20 +00:00
zzz
326b9998fd make YKGenerator non-static; control from ElGamalEngine 2012-03-14 11:59:24 +00:00
zzz
3b64ce7408 remove static logs 2012-03-14 11:57:06 +00:00
zzz
7c88180b83 remove static logs 2012-03-14 11:55:13 +00:00
7d0741bd7e Add plugin site 2012-03-13 21:48:02 +00:00
65612a1c86 merge of '7bb8bf1ecf9084d263f4d9a764330dd19faf6102'
and 'bd10ddbf34b82a92bfbca38cefbba707a709c251'
2012-03-13 21:38:56 +00:00
a408a43d96 Add plugin site host key 2012-03-13 21:38:25 +00:00
bc0a38b405 Fix grammar error, thanx user 2012-03-13 19:54:07 +00:00
913994f312 Add sponge.i2p :-D bump version. 2012-03-13 11:11:41 +00:00
0fabbc9039 Plugins: bugfix and defer update. 2012-03-13 09:49:15 +00:00
92c3d33ce5 Plugins: Handle 'file://' URLs for installation and updates. 2012-03-13 05:49:02 +00:00
zzz
d29da5597f merge of '4db3befef542b29ac21bba7ed51b609b6e9223ea'
and 'fe5447c647ccadee0d67aa5db36f5ed785520a1b'
2012-03-12 15:02:49 +00:00
zzz
2415091a2f * Console:
- Better IPv6 test, hopefully will work on Windows
    - Hide home page flags once language is selected
    - Home page shrinkage and other CSS tweaks
2012-03-12 14:30:38 +00:00
05537ba1b2 Slackware package script updates 2012-03-11 23:54:02 +00:00
zzz
703f28e87d * Build:
- Include old commons logging classes in commons-logging.jar
    - Preserve manifests in Jetty/Tomcat jars
  * Jetty Logger: Promote warns to erros when a Throwable is the second arg
2012-03-11 18:30:43 +00:00
f91f83faef fix broken comment in jetty.xml 2012-03-11 16:46:45 +00:00
d598396bb2 Add 'unsignedbuild' id 2012-03-11 13:02:51 +00:00
zzz
fdb1f9dd39 IPv4 check 2012-03-10 22:07:15 +00:00
zzz
9e3b49d67f -10 2012-03-10 21:59:26 +00:00
zzz
f6bda355b9 * Build: Add new jetty jars to router.jar classpath so it works
on very old installs with individual jars specified in the
    wrapper.config classpath
2012-03-10 21:55:45 +00:00
zzz
a34b674f7d more comment updates 2012-03-10 21:54:55 +00:00
zzz
307826c4d3 move the update-all button 2012-03-10 21:54:38 +00:00
zzz
9faba9fd30 * Console: Test if IPv6 addresses will work before instantiating
connectors, so Jetty will still start without them
2012-03-10 21:53:29 +00:00
zzz
87c04bf00b * configloggging: Fix clearing log overrides 2012-03-10 21:51:50 +00:00
zzz
7bd83f83ed * Jetty Logger: Put a note in wrapper log saying where the logs went 2012-03-10 21:51:20 +00:00
zzz
528ef4dd1a * HTTP Proxy: Fix generation of jump links (ticket #616),
broken by refactoring in -1
2012-03-10 21:50:45 +00:00
zzz
5ab17da73d * Simplify default eepsite base and CGI contexts to use a Context
instead of a ContextHolder, ServletHandler, and ServletHolder.
    This also fixes / which returned a zero-length file,
    broken in the last checkin.
2012-03-09 20:00:18 +00:00
0c55af2622 new i2p-projekt.de reseed SSL certificate. Sorry. 2012-03-09 16:52:55 +00:00
zzz
e331800898 * Update default eepsite base context to use DefaultServlet
instead of ResourceHandler, to provide resume, directory
    indexes, caching, etc.
2012-03-09 16:03:27 +00:00
e4257f28c9 Add note about 32bit libs 2012-03-09 02:00:07 +00:00
zzz
97f402be0b * SessionKeyManager:
- Don't use unacked tagsets after consecutive ack failures
      and revert to full ElGamal if necessary (ticket #574)
    - Synchronize creation of new sessions to prevent dups
    - Don't remove an unacked session until it's really out of tags
    - Failsafe removal of old unacked tagsets
    - Cleanups, final, comments, log tweaks, debug.jsp tweaks, synchronization tweaks
2012-03-08 17:48:19 +00:00
zzz
1e978ea435 javadoc 2012-03-08 17:46:56 +00:00
zzz
629c7862ca log tweak 2012-03-08 17:46:15 +00:00
zzz
7006ffb75d remove calls re-setting defaults 2012-03-08 17:45:29 +00:00
zzz
b42993b495 * GarlicConfig: Remove unused reply block methods 2012-03-08 17:44:41 +00:00
a9935cb609 Spanish and German translation updates from TX.
Updated EN po files to push to TX.
2012-03-07 01:39:39 +00:00
05be39d286 minor updates 2012-03-06 19:25:19 +00:00
0c77f7a82d update comments to reflect that we're now using jetty6 2012-03-06 19:25:03 +00:00
18f113d8e7 add pkg200 target, fix dist200 target 2012-03-06 19:23:28 +00:00
zzz
175e464c17 merge of '6719c390f50bba07ed4913fa1578249050506422'
and 'd766ba05d9b2db6d6f7c0ea14b5ad0252c274c2d'
2012-03-06 14:16:04 +00:00
zzz
0cea3e03ae * Webapp class loader: Fix dup classes in classpath
caused by last checkin (symptom: i2psnark in wrong directory)
2012-03-06 14:04:04 +00:00
zzz
de2b204646 * viewstat.jsp: Properly close the ImageOutputStream to fix
NPEs in the finalizer, probably was the root cause of
    what we blamed on Jetty
2012-03-06 14:02:12 +00:00
zzz
e1c3979af7 * Jetty logger: Fix stack trace logging 2012-03-06 14:01:13 +00:00
zzz
46438b7412 * i2psnark: Fix NPE on magnet link addition 2012-03-06 13:59:32 +00:00
6a103a1492 minor changes to some targets/descriptions 2012-03-05 23:58:56 +00:00
a24937b7fd improve display of dark, classic, and midnight on the new homepage and center the search box under the news 2012-03-05 23:57:51 +00:00
zzz
d7c1e9724b merge of 'c09b40fe67ffc403c484ac4b14d896de48d37d9c'
and 'c269d35a60d164027bb1d1fbb6d30c06ffd2292d'
2012-03-05 15:24:21 +00:00
224405d737 Allow EXEs to be built on AMD64
Enabling this should be safe since we will ship separate installers for
Win/non-Win. Those that don't want to build the EXEs can set noEXE in build.properties,
Even better might be to run one of
* ant installer-freebsd
* ant installer-linux
* ant installer-nowindows
* ant installer-osx
2012-03-05 15:07:04 +00:00
zzz
6c2d4ded1c add time interval to graph legends 2012-03-05 15:00:52 +00:00
zzz
ee22244b53 merge of '166d63ea25334cf53075e0d418ececcb1d8bc6ca'
and 'e42352d45c2c9304562eed404c9c2dd8120becc5'
2012-03-05 15:00:02 +00:00
5376858175 Add WithJavadoc targets that also include Jetty 2012-03-05 13:39:54 +00:00
910f60031a html fix 2012-03-05 01:46:41 +00:00
zzz
3240dacfe8 remove dup utility jar building, build win and non-win installers for release 2012-03-05 00:52:21 +00:00
zzz
76c0f56be8 * Plugins: Fix setting webapp classpath on Jetty 6 2012-03-05 00:41:36 +00:00
zzz
d85ae48f4e specify action so that the user will go to the top of the page to see the result even if an anchor was specified 2012-03-05 00:40:41 +00:00
zzz
e99b2064e7 log tweaks 2012-03-05 00:37:48 +00:00
zzz
57f4ede102 increase floodfills to 250 2012-03-05 00:37:20 +00:00
zzz
0c55de7fcc * viewstat.jsp: Send Connection: Close to work around Jetty bug
http://jira.codehaus.org/browse/JETTY-1346
2012-03-05 00:36:54 +00:00
af1a49e5f4 s|update\.killyourtv\.i2p/javadoc|i2p-javadocs.i2p| for homepage
..and add to hosts.txt
2012-03-04 23:20:26 +00:00
28e3ab9132 Minor tweaks so that debs can be built again
Work is needed so that I2P is built using jetty packages from
Debian/Ubuntu.
2012-03-04 18:40:01 +00:00
zzz
60f35bdd5d history for prop, -4 2012-03-04 17:01:17 +00:00
zzz
44db814ded remove old jetty 5 classes in case you forgot to distclean after updating 2012-03-04 14:28:50 +00:00
zzz
349a01265f backwhacks 2012-03-04 04:30:16 +00:00
zzz
a0a9c23bcd fixups after prop 2012-03-04 02:48:47 +00:00
zzz
2b81cee653 propagate from branch 'i2p.i2p.zzz.jetty6' (head b2ad0d72311d5ec26270cfcbbc79d128b268869b)
to branch 'i2p.i2p' (head b05b73d4740740f306a665e4b354d412eab2f328)
2012-03-04 00:34:00 +00:00
zzz
140ffc5c5e - /confighome validation fixes
- AJAX fixes
  - Translate 'router is down' message
2012-03-03 22:55:24 +00:00
zzz
8cd9fb80ba history for prop, -2 2012-03-03 20:25:49 +00:00
zzz
21ce36db9c merge of '20d8216d0354a2fa3dbd14fa595ebe0ee080a9d8'
and 'b97f3cabaa528502fd5728f44345cc09348b74a6'
2012-03-03 20:20:30 +00:00
zzz
f010b27b14 Home page:
- Convert ajax to use shared script
  - CSS tweaks
  - Add missing icons
    Silk icons: See licenses/LICENSE-SilkIcons.txt
2012-03-03 20:17:02 +00:00
zzz
b0a682f606 propagate from branch 'i2p.i2p.zzz.homepage' (head 27c8ea684056ce34ea81acdfd18571776ca63641)
to branch 'i2p.i2p' (head 83f37b19742045fa42ed71a0abd8e90d080c2c05)
2012-03-03 19:05:15 +00:00
zzz
e2acc9fdd2 * i2psnark:
- CSS tweaks
    - Ajax fixes
    - Move js to console in prep for merging w/ homepage
2012-03-03 18:58:31 +00:00
zzz
0e8d3d1862 remove bw stats from netdb effective next release 2012-03-03 18:21:00 +00:00
zzz
a0f714097a css tweak 2012-03-03 18:20:06 +00:00
zzz
15e182809d html fix 2012-03-03 18:19:50 +00:00
7673d2ba04 Susimail: HTML fixes 2012-03-03 18:10:45 +00:00
7a765757dd Proxy pages: HTML validation fixes for the proxy pages 2012-03-03 16:31:05 +00:00
2db086ee32 Add Updater200WithJetty target 2012-03-03 13:46:54 +00:00
366d79ddad Adding missing HTML tags 2012-03-03 01:00:31 +00:00
2bff0d6bcc HTML validation fixes 2012-03-02 22:32:45 +00:00
1aa24a38a4 Various HTML fixes 2012-03-02 18:46:34 +00:00
zzz
f62c3047b5 history for prop, -1 2012-03-02 01:34:21 +00:00
zzz
0cdff150f5 propagate from branch 'i2p.i2p.zzz.test' (head 44ecb300f6f5655b9b8699049d2fba046822deed)
to branch 'i2p.i2p' (head 5120d6c371d7cba9f201b48a142b0e0ead028cdd)
2012-03-02 00:18:54 +00:00
zzz
75eda7e1b1 log tweak 2012-03-01 23:52:56 +00:00
zzz
f22ac28e4d search param encoding 2012-03-01 23:46:06 +00:00
zzz
33964fac45 Fixes for:
IPv6 addresses
    Reject all hostnames w/o dots except IPv6
    http://i2p/b64dest
Log tweaks
Add nicer ahelper-notfound error page
2012-03-01 23:38:22 +00:00
5e5e4f6f2c HTML fix 2012-03-01 19:16:44 +00:00
4a74bd0fe7 HTML fixes 2012-03-01 19:02:32 +00:00
zzz
e7bcff5e71 - Refactoring to use Jave URI parser to better handle
escapes, IPv6 addresses, ports
    - Rewrite i2paddresshelper scanning/removal

intermediate checkin, bug fixes to follow
2012-03-01 18:39:07 +00:00
zzz
cadedeb06c * Build: Add built-by to jars; check for corrupt jars on debug page 2012-03-01 16:04:17 +00:00
zzz
5af6c97bee log tweaks, final 2012-03-01 14:21:54 +00:00
zzz
cf41068fef SDK message 2012-03-01 13:41:24 +00:00
81bd0fcbf4 propagate from branch 'i2p.i2p.str4d.i2ptunnel' (head 237102a8f647caf98bf0491af5de6f636e254722)
to branch 'i2p.i2p' (head 3d9f6d9555224cc3277ca10fd9409c83422758eb)
2012-03-01 05:07:47 +00:00
b6f7321497 Fix the UTF8 POSTing issue 2012-03-01 05:03:29 +00:00
zzz
538427c269 - Synchronize StoreJob.sendNext() to avoid dups
- StoreState finals
2012-02-29 18:49:49 +00:00
zzz
f61183d2d8 * DataStructures:
- Remove static logs
    - Sort addresses in RouterInfo at initialization only;
      change from Set to List to save space
    - Remove unused counters in Lease to save space
    - Increase max leases to 16
2012-02-29 18:09:16 +00:00
zzz
48551f0617 NetDB: Reenable RI store verifies. Was disabled in 0.7.9;
checkin comments claim it was reenabled in 0.7.10
but it didn't actually happen, probably due to bad merge.
2012-02-29 17:50:54 +00:00
zzz
6f682c1e71 change console link 2012-02-29 15:30:28 +00:00
zzz
f747febd0a home page updates 2012-02-29 15:29:29 +00:00
zzz
1f9ab5d880 propagate from branch 'i2p.i2p' (head 3d9f6d9555224cc3277ca10fd9409c83422758eb)
to branch 'i2p.i2p.zzz.homepage' (head 0657f1e52c859e06b78e55fe04656567bfba3bf0)
2012-02-29 15:27:37 +00:00
zzz
f43b0be5b8 Check in the 840 KB of jars we need from Jetty 6.1.26
(Apache 2.0 license) instead of downloading a 25 MB zip
2012-02-29 15:12:25 +00:00
zzz
bb46535e71 propagate from branch 'i2p.i2p' (head 3d9f6d9555224cc3277ca10fd9409c83422758eb)
to branch 'i2p.i2p.zzz.jetty6' (head 0212d710e5a4eb30249201015f045d43f44650cb)
2012-02-29 14:37:02 +00:00
zzz
2bc70b53c1 debug log 2012-02-29 13:45:02 +00:00
786a261a70 Fixed usage of assertEquals - should be assertEquals(expected, got) 2012-02-29 05:34:13 +00:00
a226d25dc6 Test getters etc. in RateStat 2012-02-29 05:01:21 +00:00
zzz
ede1b1954c remove static logs 2012-02-28 15:58:02 +00:00
zzz
17f7264863 remove unused counters and methods 2012-02-28 15:26:31 +00:00
zzz
d6d8c0d119 remove static logs 2012-02-28 14:50:39 +00:00
zzz
4f6ed70044 remove static logs 2012-02-28 14:35:32 +00:00
zzz
cf5d7d2f08 remove dtg from updater 2012-02-28 13:28:25 +00:00
zzz
d0dca206f7 release script tweaks 2012-02-28 13:20:34 +00:00
382dd3405a merge of '19c723e9710a494c2745c7a6e9476faf11f19fe1'
and '9f02425e8331315552436375df61b3dd03e6ef56'
2012-02-27 23:16:51 +00:00
a655a7848b debian: changelog update, policy update 2012-02-27 22:53:10 +00:00
9ac0a91f17 html fix (selected=true isn't valid) 2012-02-27 21:31:17 +00:00
49d7dace3b s/mbuild/build/
mbuild_jbigi.sh was into build_jbigi.sh
2012-02-27 21:25:40 +00:00
zzz
bda3fbbe63 add icon to installer exe 2012-02-27 21:15:56 +00:00
e2e578a66d merge of '228c3961aed59e47a5cbc291817fc7c41ffeb54e'
and '55bddb9107328a5d5a3fd697e0599dda7d3ff2e3'
2012-02-27 19:06:21 +00:00
d436c846ac Temporarily exclude slow core tests for dev purposes 2012-02-27 03:29:13 +00:00
24268c5130 Exclude classes from code coverage report that aren't in net.i2p.*
The classes are specifically excluded rather than only including net.i2p.*
so that only classes we know we want to exclude get excluded.
2012-02-27 03:26:58 +00:00
zzz
6ebd1f121a installer build fixups 2012-02-26 23:05:21 +00:00
zzz
394943c36f debug output of LS encryption key to correlate with SKM debug page 2012-02-26 21:15:31 +00:00
zzz
6ee9b79e45 generics, final 2012-02-26 21:13:06 +00:00
zzz
da482c373e new single graph page 2012-02-26 17:34:22 +00:00
22900a0d91 Fixed RouterAddressTest.testToString to actually test the returned String 2012-02-26 09:30:27 +00:00
e7922c4ded Added some tests of setOptions to RouterAddressTest 2012-02-26 04:57:31 +00:00
f19ef3e486 Fix test that would always pass 2012-02-26 03:01:48 +00:00
5b6a23b13a propagate from branch 'i2p.i2p.str4d.i2ptunnel' (head 382a93d72f9ae55f3e251a6f3cbf6e35df2e560d)
to branch 'i2p.i2p' (head 36eae7cbb8817fb68bb042e8207c6c955264f393)
2012-02-25 21:14:34 +00:00
ce1e055848 Removed WizardBean - it does nothing extra over EditBean 2012-02-25 19:37:54 +00:00
f72e16f571 Close line break tags properly 2012-02-25 19:28:22 +00:00
zzz
fc3343270a fix LS count 2012-02-25 18:40:27 +00:00
zzz
e1c9402d3f propagate from branch 'i2p.i2p' (head 36eae7cbb8817fb68bb042e8207c6c955264f393)
to branch 'i2p.i2p.zzz.test' (head 33f78a361bfef38b120fdf55d9e9535f3be96d6e)
2012-02-25 17:49:09 +00:00
4148aa54f3 Need to run clean and prepareTest before test in router build.xml 2012-02-25 01:48:31 +00:00
204440b06b Run full test reports with fulltest target, not just junit report 2012-02-25 01:46:46 +00:00
6a26c0b621 Edited router build.xml to match core build.xml (inc. clover, cobertura stuff) 2012-02-25 01:43:15 +00:00
c955adf7f6 Set up report targets properly in main build.xml 2012-02-25 01:29:08 +00:00
c68d53faf3 Removed fullclovertest target from main build.xml
Clover usage is triggered by setting with.clover instead.
2012-02-25 01:23:17 +00:00
ceda7c9ca0 Fix jbigi.jar path in test target of router build.xml 2012-02-25 01:21:31 +00:00
zzz
54f0cae2ff refactor proxy.i2p server to its own class 2012-02-24 17:58:54 +00:00
zzz
eeb9ee0852 snark conn limits 2012-02-23 19:31:25 +00:00
zzz
87da4b78ab add negative cache to BFNS 2012-02-23 19:06:21 +00:00
zzz
141ad67650 ajaxify the refresh 2012-02-23 17:41:06 +00:00
zzz
a288fc52e0 Move icon URL from _icons to .icons 2012-02-23 15:40:31 +00:00
zzz
667bd46643 hide stat log config unless already enabled 2012-02-22 15:41:04 +00:00
849c407712 Added clover targets to core build.xml as well, so either will work
Pass in the absolute location of clover.jar as the with.clover property
to activate the clover targets.
2012-02-21 04:05:27 +00:00
zzz
3131e65b66 fix redirection; change header 2012-02-20 14:32:48 +00:00
a2217b2b36 Fiddle with cobertura definitions a bit in core build.xml
To run with cobertura, execute the following in "core/java/":
ant -Dwith.cobertura=/usr/share/java/cobertura.jar cobertura.report
2012-02-20 03:44:23 +00:00
zzz
13731e7b35 add b64 hash 2012-02-19 20:57:44 +00:00
zzz
3fbd8b8d14 - Fix custom tracker list
- Add tracker config form
- Remove custom tracker from create form
- More icons in buttons
2012-02-19 19:52:16 +00:00
zzz
3876f74f6c tooltip 2012-02-18 19:19:42 +00:00
zzz
b47aa34d6a add private option in UI 2012-02-18 18:50:57 +00:00
zzz
295242316b Disable PEX/metadata extensions and open trackers for private torrents
Handle announce URLs with parameters correctly
2012-02-18 17:58:54 +00:00
zzz
274e37b284 propagate from branch 'i2p.i2p' (head 0a3db5b65f8fdc3e91000c9dff9e679401b52f72)
to branch 'i2p.i2p.zzz.jetty6' (head da78bc5726f3715ec8083aa86bcaa6c05896b8d6)
2012-02-18 15:28:34 +00:00
zzz
1997be371c propagate from branch 'i2p.i2p' (head 0a3db5b65f8fdc3e91000c9dff9e679401b52f72)
to branch 'i2p.i2p.zzz.homepage' (head 73d71bde13883d204e50f497efe264d8643fb8fa)
2012-02-18 15:28:19 +00:00
zzz
c957577e72 propagate from branch 'i2p.i2p' (head 0a3db5b65f8fdc3e91000c9dff9e679401b52f72)
to branch 'i2p.i2p.zzz.test' (head 8648a7d67ffff19124cafdd14648c13dedccd2ba)
2012-02-18 15:23:24 +00:00
zzz
cf463100cd stub out private mode 2012-02-18 15:22:40 +00:00
c7d473a7eb Shuffle round mkdirs and deletes in router build.xml to match core build.xml
batchtest has a todir config option that makes manual copying unnecessary.
2012-02-18 02:13:46 +00:00
d1a03f500f Moved mkdir above delete in core build.xml and removed mkdir from base build.xml
mkdir does nothing if the directory exists, whereas delete fails by default
if the directory doesn't exist. Also, mkdir creates parent dirs as needed.
2012-02-16 02:58:25 +00:00
8a32aad6c3 Shuffled build targets around to group together ones related to unit tests 2012-02-16 00:08:27 +00:00
zzz
45ba9e1bd4 fixup after prop 2012-02-04 15:45:03 +00:00
zzz
6107e38e56 propagate from branch 'i2p.i2p' (head 0f4af64e64536c467b623286bce659dd2dfe9342)
to branch 'i2p.i2p.zzz.jetty6' (head d525108d8e9ecb842a99bb81c1138c43b5e39653)
2012-02-04 15:42:23 +00:00
zzz
3af2577c11 propagate from branch 'i2p.i2p' (head 0f4af64e64536c467b623286bce659dd2dfe9342)
to branch 'i2p.i2p.zzz.homepage' (head dde6a3d7db54894a2ac30a2af6cbb04cbb92296a)
2012-02-04 15:40:22 +00:00
zzz
f7dee01609 fix form default on chrome 2012-01-25 16:40:55 +00:00
zzz
6e44710b94 - escape html for adds and queries
- change config separator char
- fix enter in add boxes
- fix alt and title tags
2012-01-25 16:22:04 +00:00
zzz
0d494c50af add firewall and reseed status on /home 2012-01-25 15:01:19 +00:00
zzz
00b8e14adf /confighome:
- config search engines
- add icons
- restore defaults button
2012-01-25 14:34:54 +00:00
zzz
61290dfbcf * Old console to /console, new redirect script at /index 2012-01-25 02:50:42 +00:00
zzz
fbca6ac1fd * New home page, including basic AJAX, home page config and search 2012-01-25 02:38:49 +00:00
zzz
061f96ad89 Use ${ant.home}/lib/ant.jar instead of pulling ant.jar from Jetty 2012-01-23 16:44:08 +00:00
zzz
221499c1a8 propagate from branch 'i2p.i2p' (head 156f8e6137be3c25aa70176fe0a78218b898a684)
to branch 'i2p.i2p.zzz.jetty6' (head 960f416b20e26662b1b5b30468a85dbb25f09ffd)
2012-01-22 18:30:41 +00:00
zzz
ba01451038 propagate from branch 'i2p.i2p' (head 7e9829897ed454bcb4a8e64b029060f7e90cbbfa)
to branch 'i2p.i2p.zzz.jetty6' (head b9984717e63b03b3b5fcf3a9729d55411aa65e89)
2012-01-18 19:16:53 +00:00
zzz
6198739f7a propagate from branch 'i2p.i2p' (head 899a1923586f3d7d622bd02e03c87717566617c4)
to branch 'i2p.i2p.zzz.jetty6' (head cb3e33be282b6002f6790609f4a3eef7f42069d3)
2012-01-16 14:10:32 +00:00
zzz
404578515b bundle jmx 2012-01-14 22:20:39 +00:00
zzz
fd6fcda781 fixup after prop 2012-01-14 18:08:26 +00:00
zzz
23ca49ea8e propagate from branch 'i2p.i2p' (head b3d611a1fe034bc89963c54179d5bef3a3147950)
to branch 'i2p.i2p.zzz.jetty6' (head c83bf7bd62d0e07be0d965f062f01b01864be4d2)
2012-01-14 18:04:39 +00:00
zzz
edf5ef588d remove dup docs from tomcat jars 2012-01-10 20:17:35 +00:00
zzz
405c24b0e3 Add JSP 2.1 trim white space directive to all jsps to save a few KB 2012-01-10 13:21:35 +00:00
zzz
1b85c22ffc escape quotes 2012-01-10 04:10:14 +00:00
zzz
8e20e7a5b5 propagate from branch 'i2p.i2p' (head 96b4e09e85e0947d0b9df188f4861664073f07a6)
to branch 'i2p.i2p.zzz.jetty6' (head 4024ef4f6e6c5e4ca6a7803614dc769ca654ac5f)
2012-01-10 04:06:23 +00:00
zzz
3024b3fd3b * Move from Servlet 2.4/JSP 2.0 to Servlet 2.5/JSP 2.1
- Replace Jasper 2.0 from Jetty 6 with Apache Tomcat 6.0.35 (CDDL 1.0 + GPLv2)
  - Replace JSTL 1.1.2 with JSTL 1.2 (CDDL 1.0 + GPLv2)
2012-01-10 04:03:30 +00:00
zzz
51f7f3a378 log all jetty warns as errors 2012-01-10 03:40:53 +00:00
zzz
7feaadbd7d fixup after prop 2012-01-08 15:01:35 +00:00
zzz
69bbb88407 propagate from branch 'i2p.i2p' (head b7ee04ecc7a594239e977b25a52ebdabadce558e)
to branch 'i2p.i2p.zzz.jetty6' (head 2cd4a4dae8b87b9ed2128d83aff1b39e3a818556)
2012-01-08 14:55:10 +00:00
zzz
43ec87e412 - i2ptunnel: Move bean classes from the jar to the war.
External usage was deprecated about a year ago.
  This will break old seedless plugins.
2012-01-04 15:22:01 +00:00
zzz
a14643f710 fix eepsite migration in packages 2012-01-04 15:20:35 +00:00
0ad4789ff2 Add jetty-rewrite-handler.jar to the Debian packaging 2012-01-02 02:04:51 +00:00
dd9cae57a8 merge of '83079d347b764886d77d9d9de902011f09f5dcce'
and 'c76215859aa3b76a04c4a83a1ded20548a8773a6'
2012-01-02 00:59:33 +00:00
84e4558d7d Updated packaging scripts to work with Debian's (and Ubuntu's) Jetty packages. 2012-01-01 23:31:41 +00:00
zzz
33b25b5780 - Fix jetty.xml migrate quote handling
- Fix I2PRequestLog javadoc
- Rename jetty-threadpool.jar to jetty-java5-threadpool.jar to match ubuntu symlink
- Bundle jetty-rewrite-handler.jar (20KB) and .xml config to
  make it easier to do rewrites (unused by I2P)
2012-01-01 17:57:59 +00:00
zzz
b5f97d0883 - Fix jetty.xml copy bug
- Fix NPE logging error when Router.saveConfig() fails early
- Log readConfig() and saveConfig() errors to wrapper log if
  router log not yet available
- Handle non-split configuration in MigrateJetty
- Better logging of migration errors
- Handle existing backup files during migration better
2011-12-31 14:38:37 +00:00
zzz
beb6d1f43f I2PLogger fix; better log init 2011-12-31 00:49:39 +00:00
zzz
c0662bc111 license update 2011-12-31 00:06:23 +00:00
zzz
327f38b535 fix JettyFixes target 2011-12-30 23:34:18 +00:00
zzz
4d1736eaf6 jetty logging tweaks 2011-12-30 21:58:16 +00:00
zzz
03e86fcb24 make jetty use I2P logging 2011-12-30 19:44:57 +00:00
zzz
be7623a462 - Start webapps after console for faster startup
- Add delay in systray port checker to ensure console is up
- Move I2PRequestLog to net.i2p.jetty package
2011-12-29 23:25:27 +00:00
07162b56c8 escape quotation marks in jsp files
The routerconsole's precompilejsp target failed when using the jetty packages
from Debian. These escaped jsp files also work with 'our' jetty6 (and jetty6).
2011-12-25 22:37:30 +00:00
zzz
ec7ec564be - Fix Console concurrent ThreadPool
- Uncomment eepsite concurrent ThreadPool and fix arguments
- Reduce eepsite acceptors to 1
- Add jetty-ssl.xml example
- jetty.xml cleanup
2011-12-24 17:09:01 +00:00
zzz
0fb9096096 - Fix help.jsp mapping to language
- Fix nowebapp.jsp for non-started webapps
- Fix nowebapp.jsp for url /foo for non-started webapp foo
2011-12-24 05:26:56 +00:00
zzz
5a4becba68 updater200WithJetty target 2011-12-23 22:00:16 +00:00
zzz
2083d8c6a6 basic jetty.xml migration tool 2011-12-23 21:27:38 +00:00
zzz
8ea587accb fix up-to-date check 2011-12-23 15:41:05 +00:00
zzz
d976b59732 fix getWebApp() 2011-12-23 15:36:44 +00:00
zzz
41ea29209f eepsite:
- fix cgi-bin
  - enable WebAppDeplyer
  - add webdefault.xml
2011-12-23 15:17:34 +00:00
zzz
fbbab0d819 mods to compile with Jetty 6 2011-12-23 01:01:40 +00:00
zzz
56901e5ff7 base files from Jetty 5.1.15 for reference 2011-12-23 01:00:45 +00:00
zzz
04cbcf2759 reapply I2P mods 2011-12-23 00:58:13 +00:00
zzz
99ad70e80a 6.1.26 base for reference 2011-12-23 00:57:46 +00:00
zzz
92b9d0a996 First cut at migrating to Jetty 6 and prep for using an external
Jetty 6 package.

- Add several jars from the Jetty 6 distribution
- Update jetty.xml
- Add context XML files
- Update WorkingDir to migrate the content XML files
- Update RouterConsoleRunner and LocaleWebAppHandler
- Remove all old Jetty 5.1.15 local mods;
  this will break Seedless using a custom Server() constructor
- Update I2PRequestLog to be a mod of NCSARequestLog from 6.1.26
- Put I2PRequestLog in its own jar
- Copy MultiPartRequest and other required classes from Jetty 5.1.15
  and add it to susimail, as the replacement MultiPartFilter in
  Jetty 6 is difficult to migrate to, and does not support content-type
- Update i2psnark for Jetty 6
- Disable i2psnark RunStandalone, unused and instantiated Jetty 5
- Fix up all webapp build.xml to reference new jars

Not yet working: Plugin/webapp run detection and stopping, eepsite CGI
Not well tested: Plugins, classpaths, webapps
2011-12-23 00:56:48 +00:00
zzz
d8080278b3 initial DHT code, needs work 2011-02-06 00:12:54 +00:00
1223 changed files with 132412 additions and 73400 deletions

View File

@ -17,10 +17,10 @@ _jsp\.java$
\.war$
\.zip$
^\.
^build/
^build
^pkg-temp/
~$
/build/
/build
/classes/
^debian/copyright
override.properties

View File

@ -7,6 +7,7 @@ trans.da = apps/i2ptunnel/locale/messages_da.po
trans.de = apps/i2ptunnel/locale/messages_de.po
trans.es = apps/i2ptunnel/locale/messages_es.po
trans.fr = apps/i2ptunnel/locale/messages_fr.po
trans.hu = apps/i2ptunnel/locale/messages_hu.po
trans.it = apps/i2ptunnel/locale/messages_it.po
trans.nl = apps/i2ptunnel/locale/messages_nl.po
trans.ru = apps/i2ptunnel/locale/messages_ru.po
@ -22,10 +23,12 @@ trans.ar = apps/routerconsole/locale/messages_ar.po
trans.cs = apps/routerconsole/locale/messages_cs.po
trans.da = apps/routerconsole/locale/messages_da.po
trans.de = apps/routerconsole/locale/messages_de.po
trans.et_EE = apps/routerconsole/locale/messages_ee.po
trans.el = apps/routerconsole/locale/messages_el.po
trans.es = apps/routerconsole/locale/messages_es.po
trans.et_EE = apps/routerconsole/locale/messages_ee.po
trans.fi = apps/routerconsole/locale/messages_fi.po
trans.fr = apps/routerconsole/locale/messages_fr.po
trans.hu = apps/routerconsole/locale/messages_hu.po
trans.it = apps/routerconsole/locale/messages_it.po
trans.nl = apps/routerconsole/locale/messages_nl.po
trans.pl = apps/routerconsole/locale/messages_pl.po
@ -43,6 +46,7 @@ trans.cs = apps/i2psnark/locale/messages_cs.po
trans.de = apps/i2psnark/locale/messages_de.po
trans.es = apps/i2psnark/locale/messages_es.po
trans.fr = apps/i2psnark/locale/messages_fr.po
trans.hu = apps/i2psnark/locale/messages_hu.po
trans.it = apps/i2psnark/locale/messages_it.po
trans.nl = apps/i2psnark/locale/messages_nl.po
trans.pl = apps/i2psnark/locale/messages_pl.po
@ -59,8 +63,10 @@ trans.ar = apps/susidns/locale/messages_ar.po
trans.cs = apps/susidns/locale/messages_cs.po
trans.da = apps/susidns/locale/messages_da.po
trans.de = apps/susidns/locale/messages_de.po
trans.el = apps/susidns/locale/messages_el.po
trans.es = apps/susidns/locale/messages_es.po
trans.fr = apps/susidns/locale/messages_fr.po
trans.hu = apps/susidns/locale/messages_hu.po
trans.it = apps/susidns/locale/messages_it.po
trans.nl = apps/susidns/locale/messages_nl.po
trans.pl = apps/susidns/locale/messages_pl.po
@ -77,8 +83,10 @@ trans.ar = apps/desktopgui/locale/messages_ar.po
trans.cs = apps/desktopgui/locale/messages_cs.po
trans.da = apps/desktopgui/locale/messages_da.po
trans.de = apps/desktopgui/locale/messages_de.po
trans.el = apps/desktopgui/locale/messages_el.po
trans.es = apps/desktopgui/locale/messages_es.po
trans.fr = apps/desktopgui/locale/messages_fr.po
trans.hu = apps/desktopgui/locale/messages_hu.po
trans.it = apps/desktopgui/locale/messages_it.po
trans.nl = apps/desktopgui/locale/messages_nl.po
trans.pl = apps/desktopgui/locale/messages_pl.po
@ -95,6 +103,7 @@ trans.cs = apps/susimail/locale/messages_cs.po
trans.de = apps/susimail/locale/messages_de.po
trans.es = apps/susimail/locale/messages_es.po
trans.fr = apps/susimail/locale/messages_fr.po
trans.hu = apps/susimail/locale/messages_hu.po
trans.it = apps/susimail/locale/messages_it.po
trans.nl = apps/susimail/locale/messages_nl.po
trans.ru = apps/susimail/locale/messages_ru.po
@ -109,7 +118,10 @@ source_file = debian/po/templates.pot
source_lang = en
trans.cs = debian/po/cs.po
trans.de = debian/po/de.po
trans.el = debian/po/el.po
trans.es = debian/po/es.po
trans.it = debian/po/it.po
trans.hu = debian/po/hu.po
trans.pl = debian/po/pl.po
trans.ru = debian/po/ru.po
trans.sv_SE = debian/po/sv.po

View File

@ -79,11 +79,13 @@ Public domain except as listed below:
From freenet
See licenses/LICENSE-GPLv2.txt
UPnP subsystem 1.7:
Copyright (C) 2003-2006 Satoshi Konno
UPnP subsystem (CyberLink) 2.1:
Copyright (C) 2003-2010 Satoshi Konno
See licenses/LICENSE-UPnP.txt
GeoIP data free to use, courtesy http://www.maxmind.com/
GeoIP: GeoLite databases are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
http://creativecommons.org/licenses/by-sa/3.0/
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
Installer:
@ -172,8 +174,9 @@ Applications:
By welterde.
See licenses/LICENSE-GPLv2.txt
Jetty 5.1.15:
Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
Jetty 6.1.26:
Copyright 1995-2009 Mort Bay Consulting Pty Ltd
See licenses/LICENSE-Jetty.txt
See licenses/LICENSE-Apache2.0.txt
See licenses/NOTICE-Commons-Logging.txt
@ -196,6 +199,7 @@ Applications:
- Guernsey and Isle of Man flags from the Open Clip Art Library, released into the public domain
- All other flag icons: public domain, courtesy mjames@gmail.com http://www.famfamfam.com/
Silk icons: See licenses/LICENSE-SilkIcons.txt
FatCow icons: See licenses/LICENSE-FatCowIcons.txt
GeoIP Data:
Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
@ -205,6 +209,10 @@ Applications:
"Man with hat over face" & related images licensed under a Creative Commons 2.0 license.
Original photos by Florian Kuhlmann. http://www.flickr.com/photos/floriankuhlmann/3117758155
I2PSnark light theme:
"Creative Commons Cat" licensed under a Creative Commons Attribution 3.0 Unported License.
Original photo by Boaz Arad. http://www.luxphile.com/2011/01/creative-commons-cat.html
SAM:
Public domain.
@ -215,8 +223,10 @@ Applications:
Copyright (C) 2005 <susi23@mail.i2p>
GPLv2 (or any later version)
See licenses/LICENSE-GPLv2.txt
Uses Apache Jakarta Standard Tag Library 1.1.2:
See licenses/LICENSE-Apache2.0.txt
Uses Glassfish Standard Tag Library (JSTL) 1.2:
Common Development and Distribution License (CDDL) version 1.0 + GNU General Public License (GPL) version 2
See https://glassfish.dev.java.net/public/CDDL+GPL.html
See licenses/LICENSE-GPLv2.txt
SusiMail:
Copyright (C) 2004-2005 <susi23@mail.i2p>
@ -228,6 +238,10 @@ Applications:
Bundles systray4j-2.4.1:
See licenses/LICENSE-LGPLv2.1.txt
Tomcat 6.0.35:
Copyright 1999-2011 The Apache Software Foundation
See licenses/LICENSE-Apache2.0.txt
See licenses/NOTICE-Tomcat.txt
Other Applications and Libraries

View File

@ -26,9 +26,9 @@ for i in *.config ; {
( cd $INST_DIR/eepsite
if [ -f jetty.xml ] ; then
rm jetty.xml.new
echo "Please check ${INST_DIR}/eepsite, as there are new files."
else
mv jetty.xml.new jetty.xml
find $PKG/$INSTALL_DIR/i2p -name "*.xml.new" -exec sh -c 'mv "$0" "${0/.new}"' {} \;
fi
)

View File

@ -18,6 +18,7 @@
#
BUILD=1sponge
# INSTALL_DIR is referenced from /, don't prefix it with a '/'
INSTALL_DIR=opt
NAME=i2p
ARCH=noarch
@ -104,7 +105,10 @@ sed "s|%INSTALL_PATH|/$INSTALL_DIR/i2p|g" runplain.sh > a
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" a > runplain.sh
# i2prouter %INSTALL_PATH and %SYSTEM_java_io_tmpdir
sed "s|%INSTALL_PATH|/$INSTALL_DIR/i2p|g" i2prouter > a
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" a > i2prouter
rm i2prouter
mv a i2prouter
sed "s|%SYSTEM_java_io_tmpdir|/$INSTALL_DIR/i2p|g" i2prouter > a
sed "s|#ALLOW_ROOT=true|ALLOW_ROOT=true|g" a > i2prouter
chmod 744 ./i2prouter
chmod 744 ./osid
@ -116,7 +120,7 @@ rm -Rf ./lib/*.dll ./*.bat ./*.exe ./installer ./icons ./a postinstall.sh
mv $PKG/$INSTALL_DIR/i2p/*.config $PKG/install
mv $PKG/$INSTALL_DIR/i2p/blocklist.txt $PKG/$INSTALL_DIR/i2p/blocklist.txt.new
mv $PKG/$INSTALL_DIR/i2p/eepsite/jetty.xml $PKG/$INSTALL_DIR/i2p/eepsite/jetty.xml.new
find $PKG/$INSTALL_DIR/i2p -name "*.xml" -exec mv {} {}.new \;
mv $PKG/$INSTALL_DIR/i2p/eepsite/docroot/index.html $PKG/$INSTALL_DIR/i2p/eepsite/docroot/index.html.new
mv $PKG/$INSTALL_DIR/i2p/eepsite/docroot/favicon.ico $PKG/$INSTALL_DIR/i2p/eepsite/docroot/favicon.ico.new
sed "s|directory|/$INSTALL_DIR/i2p/|g" $CWD/doinst.sh > $PKG/install/doinst.sh

View File

@ -38,7 +38,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClient;
import net.i2p.client.streaming.RetransmissionTimer;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer2;
@ -116,7 +115,6 @@ import net.i2p.util.SimpleTimer2;
*/
public class BOB {
private final static Log _log = new Log(BOB.class);
public final static String PROP_CONFIG_LOCATION = "BOB.config";
public final static String PROP_BOB_PORT = "BOB.port";
public final static String PROP_BOB_HOST = "BOB.host";
@ -138,7 +136,7 @@ public class BOB {
*/
public static void info(String arg) {
System.out.println("INFO:" + arg);
_log.info(arg);
(new Log(BOB.class)).info(arg);
}
/**
@ -148,7 +146,7 @@ public class BOB {
*/
public static void warn(String arg) {
System.out.println("WARNING:" + arg);
_log.warn(arg);
(new Log(BOB.class)).warn(arg);
}
/**
@ -158,7 +156,7 @@ public class BOB {
*/
public static void error(String arg) {
System.out.println("ERROR: " + arg);
_log.error(arg);
(new Log(BOB.class)).error(arg);
}
/**
@ -182,12 +180,11 @@ public class BOB {
// Re-reading the config file in each thread is pretty damn stupid.
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
// This is here just to ensure there is no interference with our threadgroups.
RetransmissionTimer Y = RetransmissionTimer.getInstance();
SimpleScheduler Y1 = SimpleScheduler.getInstance();
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
i = Y.hashCode();
i = Y1.hashCode();
i = Y2.hashCode();
Log _log = new Log(BOB.class);
try {
{
File cfg = new File(configLocation);
@ -263,6 +260,7 @@ public class BOB {
i = 0;
boolean g = false;
spin.set(true);
try {
info("BOB is now running.");
listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));

View File

@ -54,7 +54,7 @@ public class DoCMDS implements Runnable {
// FIX ME
// I need a better way to do versioning, but this will do for now.
public static final String BMAJ = "00", BMIN = "00", BREV = "0F", BEXT = "";
public static final String BMAJ = "00", BMIN = "00", BREV = "10", BEXT = "";
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
private Socket server;
private Properties props;
@ -400,7 +400,7 @@ public class DoCMDS implements Runnable {
*/
private boolean is64ok(String data) {
try {
Destination x = new Destination(data);
new Destination(data);
return true;
} catch (Exception e) {
return false;

View File

@ -41,7 +41,6 @@ public class I2Plistener implements Runnable {
private NamedDB info, database;
private Log _log;
// private int tgwatch;
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
private AtomicBoolean lives;
@ -103,7 +102,7 @@ public class I2Plistener implements Runnable {
serverSocket.close();
} catch (I2PException ex) {
}
// System.out.println("I2Plistener: Close");
// System.out.println("I2Plistener: Close");
}
}
}

View File

@ -108,16 +108,6 @@ public class MUXlisten implements Runnable {
this.listener = new ServerSocket(port, backlog, host);
}
socketManager = I2PSocketManagerFactory.createManager(prikey, Q);
// I2PException, IOException, RuntimeException
// To bad we can't just catch and enumerate....
// } catch (I2PException e) {
// Something went bad.
// this.database.getWriteLock();
// this.info.getWriteLock();
// this.info.add("STARTING", new Boolean(false));
// this.info.releaseWriteLock();
// this.database.releaseWriteLock();
// throw new I2PException(e);
} catch (IOException e) {
// Something went bad.
this.database.getWriteLock();
@ -194,7 +184,6 @@ public class MUXlisten implements Runnable {
lock.set(false);
return;
}
// socketManager.addDisconnectListener(new DisconnectListener());
lives.set(true);
lock.set(false);
quit:
@ -347,7 +336,6 @@ public class MUXlisten implements Runnable {
String boner = tg.getName();
// System.out.println("BOB: MUXlisten: Starting thread collection for: " + boner);
_log.warn("BOB: MUXlisten: Starting thread collection for: " + boner);
// tg.interrupt(); // give my stuff a small smack again.
if (tg.activeCount() + tg.activeGroupCount() != 0) {
// visit(tg, 0, boner);
int foo = tg.activeCount() + tg.activeGroupCount();

View File

@ -23,7 +23,6 @@
*/
package net.i2p.BOB;
import net.i2p.client.streaming.RetransmissionTimer;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer2;
@ -40,7 +39,6 @@ public class Main {
*/
public static void main(String[] args) {
// THINK THINK THINK THINK THINK THINK
RetransmissionTimer Y = RetransmissionTimer.getInstance();
SimpleScheduler Y1 = SimpleScheduler.getInstance();
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
@ -48,6 +46,5 @@ public class Main {
Y2.stop();
Y1.stop();
Y.stop();
}
}

View File

@ -95,14 +95,9 @@ public class TCPio implements Runnable {
if (b > 0) {
Aout.write(a, 0, b);
} else if (b == 0) {
// Will this die? We'll see.
while(Ain.available() == 0) {
Thread.sleep(20);
}
// Thread.yield(); // this should act like a mini sleep.
// if (Ain.available() == 0) {
// Thread.sleep(10);
// }
} else {
/* according to the specs:
*

View File

@ -27,8 +27,6 @@ import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
// import net.i2p.client.I2PSession;
// import net.i2p.client.I2PSessionException;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;

View File

@ -108,20 +108,18 @@ public class TCPtoI2P implements Runnable {
* @throws java.io.IOException
*/
private void Emsg(String e, OutputStream out) throws IOException {
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
out.write("ERROR ".concat(e).getBytes());
out.write(13);
out.write(10);
out.flush();
}
// private void rlock() throws Exception {
private void rlock() {
database.getReadLock();
info.getReadLock();
}
// private void runlock() throws Exception {
private void runlock() {
info.releaseReadLock();
database.releaseReadLock();

View File

@ -111,7 +111,7 @@ public class UDPIOthread implements I2PSessionListener, Runnable {
* @param size
*/
public void messageAvailable(I2PSession session, int msgId, long size) {
// _log.debug("Message available: id = " + msgId + " size = " + size);
// _log.debug("Message available: id = " + msgId + " size = " + size);
try {
byte msg[] = session.receiveMessage(msgId);
out.write(msg);

View File

@ -56,6 +56,7 @@
<manifest>
<attribute name="Main-Class" value="addressbook.Daemon"/>
<attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes}" />
@ -75,6 +76,7 @@
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}">
<manifest>
<attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@ -84,7 +84,7 @@ public class Servlet extends HttpServlet {
this.thread.setDaemon(true);
this.thread.setName("Addressbook");
this.thread.start();
System.out.println("INFO: Starting Addressbook " + Daemon.VERSION);
//System.out.println("INFO: Starting Addressbook " + Daemon.VERSION);
//System.out.println("INFO: config root under " + args[0]);
}

View File

@ -26,7 +26,6 @@ import java.util.Iterator;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper; // debug
/**
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator

View File

@ -0,0 +1,56 @@
# 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:
# <lixtetrax@grhack.net>, 2012.
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
"PO-Revision-Date: 2012-07-02 11:28+0000\n"
"Last-Translator: lixtetrax <lixtetrax@grhack.net>\n"
"Language-Team: Greek (http://www.transifex.com/projects/p/I2P/language/el/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: el\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
msgid "Start I2P"
msgstr "Έναρξη Ι2Ρ"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
msgid "I2P is starting!"
msgstr "Το Ι2Ρ ξεκίνησε!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
msgid "Starting"
msgstr "Έναρξη"
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
msgid "Launch I2P Browser"
msgstr "Έναρξη φυλλομετρητή Ι2Ρ"
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
msgid "Configure desktopgui"
msgstr "Παραμετροποίηση desktopgui"
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
msgid "Restart I2P"
msgstr "Επανεκκίνηση Ι2Ρ"
#: src/net/i2p/desktopgui/InternalTrayManager.java:85
msgid "Stop I2P"
msgstr "Τερματισμός Ι2Ρ"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
msgid "Tray icon configuration"
msgstr "Παραμετροποίηση εικονιδίου"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
msgid "Should tray icon be enabled?"
msgstr "Ενεργοποίηση εικονιδίου;"

View File

@ -0,0 +1,55 @@
# 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:
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
"PO-Revision-Date: 2012-06-01 16:28+0000\n"
"Last-Translator: AdminLMH <lehetmashogy@i2pmail.org>\n"
"Language-Team: Hungarian (http://www.transifex.net/projects/p/I2P/language/hu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:23
msgid "Start I2P"
msgstr "I2P indítása"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
msgid "I2P is starting!"
msgstr "I2P indul!"
#: src/net/i2p/desktopgui/ExternalTrayManager.java:38
msgid "Starting"
msgstr "indítás"
#: src/net/i2p/desktopgui/InternalTrayManager.java:26
msgid "Launch I2P Browser"
msgstr "I2P Böngésző Indítása"
#: src/net/i2p/desktopgui/InternalTrayManager.java:50
msgid "Configure desktopgui"
msgstr "Asztali Grafikus Felület Beállítása"
#: src/net/i2p/desktopgui/InternalTrayManager.java:67
msgid "Restart I2P"
msgstr "I2P Újraindítása"
#: src/net/i2p/desktopgui/InternalTrayManager.java:85
msgid "Stop I2P"
msgstr "I2P Leállítása"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
msgid "Tray icon configuration"
msgstr "Tálcaikon beállítása"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
msgid "Should tray icon be enabled?"
msgstr "Tálcaikon engedélyezve legyen?"

View File

@ -3,15 +3,17 @@
# This file is distributed under the same license as the desktopgui package.
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# <bovas85@gmail.com>, 2012.
# <jokjok@hotmail.it>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
"PO-Revision-Date: 2011-06-09 17:09+0000\n"
"Last-Translator: mkkid <jokjok@hotmail.it>\n"
"Language-Team: Italian (http://www.transifex.net/projects/p/I2P/team/it/)\n"
"PO-Revision-Date: 2012-06-01 12:21+0000\n"
"Last-Translator: Leelium <bovas85@gmail.com>\n"
"Language-Team: Italian (http://www.transifex.net/projects/p/I2P/language/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -48,10 +50,8 @@ msgstr "Ferma I2P"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:44
msgid "Tray icon configuration"
msgstr "Configurazione dell'icona nell'Area di notifica"
msgstr "Configurazione dell'icona nell'area di notifica"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
msgid "Should tray icon be enabled?"
msgstr "Vuoi che l'icona nell'Area di notifica venga abilitata?"
msgstr "Vuoi che l'icona nelll'rea di notifica venga abilitata?"

View File

@ -3,7 +3,8 @@
# This file is distributed under the same license as the desktopgui package.
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# 123hund123 <M8R-ra4r1r@mailinator.com>, 2011
# Translators:
# 123hund123 <M8R-ra4r1r@mailinator.com>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: I2P\n"
@ -11,7 +12,7 @@ msgstr ""
"POT-Creation-Date: 2011-03-03 18:29+0000\n"
"PO-Revision-Date: 2011-03-22 15:49+0000\n"
"Last-Translator: 123hund123 <M8R-ra4r1r@mailinator.com>\n"
"Language-Team: Swedish (Sweden) (http://www.transifex.net/projects/p/I2P/team/sv_SE/)\n"
"Language-Team: Swedish (Sweden) (http://www.transifex.net/projects/p/I2P/language/sv_SE/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -53,5 +54,3 @@ msgstr "Ikonpanelskonfiguration"
#: src/net/i2p/desktopgui/gui/DesktopguiConfigurationFrame.java:47
msgid "Should tray icon be enabled?"
msgstr "Ska ikonpanelen vara aktiverad?"

View File

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

View File

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 587 B

View File

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

View File

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

View File

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 889 B

View File

Before

Width:  |  Height:  |  Size: 766 B

After

Width:  |  Height:  |  Size: 766 B

View File

Before

Width:  |  Height:  |  Size: 653 B

After

Width:  |  Height:  |  Size: 653 B

View File

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

View File

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 578 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

View File

Before

Width:  |  Height:  |  Size: 853 B

After

Width:  |  Height:  |  Size: 853 B

View File

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 635 B

View File

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 294 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

View File

@ -19,6 +19,7 @@
<pathelement location="../../ministreaming/java/build/obj" />
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
</classpath>
</depend>
</target>
@ -34,7 +35,7 @@
debug="true" deprecation="on" source="1.5" target="1.5"
destdir="./build/obj"
includeAntRuntime="false"
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" >
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../jetty/jettylib/jetty-util.jar:../../ministreaming/java/build/mstreaming.jar" >
<compilerarg line="${javac.compilerargs}" />
</javac>
</target>
@ -56,11 +57,12 @@
<target name="jar" depends="builddep, compile, jarUpToDate, listChangedFiles" unless="jar.uptodate" >
<!-- set if unset -->
<property name="workspace.changes.tr" value="" />
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/messages_*.class">
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/FetchAndAdd*.class **/messages_*.class">
<manifest>
<attribute name="Main-Class" value="org.klomp.snark.Snark" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
<attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
@ -70,7 +72,7 @@
<target name="jarUpToDate">
<uptodate property="jar.uptodate" targetfile="build/i2psnark.jar" >
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/messages_*.class" />
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/FetchAndAdd*.class **/messages_*.class" />
</uptodate>
<condition property="shouldListChanges" >
<and>
@ -95,11 +97,16 @@
<target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" >
<!-- set if unset -->
<property name="workspace.changes.tr" value="" />
<war destfile="../i2psnark.war" webxml="../web.xml" basedir="../" includes="_icons/*" >
<copy todir="build/icons/.icons" >
<fileset dir="../icons/" />
</copy>
<war destfile="../i2psnark.war" webxml="../web.xml" >
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
<classes dir="./build/obj" includes="**/web/*.class" />
<fileset dir="build/icons/" />
<manifest>
<attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
@ -109,7 +116,7 @@
<target name="warUpToDate">
<uptodate property="war.uptodate" targetfile="../i2psnark.war" >
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../_icons/* ../web.xml" />
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../icons/* ../web.xml" />
</uptodate>
</target>

View File

@ -0,0 +1,76 @@
package net.i2p.kademlia;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.Set;
import net.i2p.data.SimpleDataStructure;
/**
* Group, without inherent ordering, a set of keys a certain distance away from
* a local key, using XOR as the distance metric
*
* Refactored from net.i2p.router.networkdb.kademlia
* @since 0.9.2
*/
public interface KBucket<T extends SimpleDataStructure> {
/**
* Lowest order high bit for difference keys.
* The lower-bounds distance of this bucket is 2**begin.
* If begin == 0, this is the closest bucket.
*/
public int getRangeBegin();
/**
* Highest high bit for the difference keys.
* The upper-bounds distance of this bucket is (2**(end+1)) - 1.
* If begin == end, the bucket cannot be split further.
* If end == (numbits - 1), this is the furthest bucket.
*/
public int getRangeEnd();
/**
* Number of keys already contained in this kbucket
*/
public int getKeyCount();
/**
* Add the peer to the bucket
*
* @return true if added
*/
public boolean add(T key);
/**
* Remove the key from the bucket
* @return true if the key existed in the bucket before removing it, else false
*/
public boolean remove(T key);
/**
* Update the last-changed timestamp to now.
*/
public void setLastChanged();
/**
* The last-changed timestamp
*/
public long getLastChanged();
/**
* Retrieve all routing table entries stored in the bucket
* @return set of Hash structures
*/
public Set<T> getEntries();
public void getEntries(SelectionCollector<T> collector);
public void clear();
}

View File

@ -0,0 +1,150 @@
package net.i2p.kademlia;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.Collections;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.ConcurrentHashSet;
/**
* A concurrent implementation using ConcurrentHashSet.
* The max size (K) may be temporarily exceeded due to concurrency,
* a pending split, or the behavior of the supplied trimmer,
* as explained below.
* The creator is responsible for splits.
*
* This class has no knowledge of the DHT base used for XORing,
* and thus there are no validity checks in add/remove.
*
* The begin and end values are immutable.
* All entries in this bucket will have at least one bit different
* from us in the range [begin, end] inclusive.
* Splits must be implemented by creating two new buckets
* and discarding this one.
*
* The keys are kept in a Set and are NOT sorted by last-seen.
* Per-key last-seen-time, failures, etc. must be tracked elsewhere.
*
* If this bucket is full (i.e. begin == end && size == max)
* then add() will call KBucketTrimmer.trim() do
* (possibly) remove older entries, and indicate whether
* to add the new entry. If the trimmer returns true without
* removing entries, this KBucket will exceed the max size.
*
* Refactored from net.i2p.router.networkdb.kademlia
* @since 0.9.2
*/
class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> {
/**
* set of Hash objects for the peers in the kbucket
*/
private final Set<T> _entries;
/** include if any bits equal or higher to this bit (in big endian order) */
private final int _begin;
/** include if no bits higher than this bit (inclusive) are set */
private final int _end;
private final int _max;
private final KBucketTrimmer _trimmer;
/** when did we last shake things up */
private long _lastChanged;
private final I2PAppContext _context;
/**
* All entries in this bucket will have at least one bit different
* from us in the range [begin, end] inclusive.
*/
public KBucketImpl(I2PAppContext context, int begin, int end, int max, KBucketTrimmer trimmer) {
if (begin > end)
throw new IllegalArgumentException(begin + " > " + end);
_context = context;
_entries = new ConcurrentHashSet(max + 4);
_begin = begin;
_end = end;
_max = max;
_trimmer = trimmer;
}
public int getRangeBegin() { return _begin; }
public int getRangeEnd() { return _end; }
public int getKeyCount() {
return _entries.size();
}
/**
* @return an unmodifiable view; not a copy
*/
public Set<T> getEntries() {
return Collections.unmodifiableSet(_entries);
}
public void getEntries(SelectionCollector<T> collector) {
for (T h : _entries) {
collector.add(h);
}
}
public void clear() {
_entries.clear();
}
/**
* Sets last-changed if rv is true OR if the peer is already present.
* Calls the trimmer if begin == end and we are full.
* If begin != end then add it and caller must do bucket splitting.
* @return true if added
*/
public boolean add(T peer) {
if (_begin != _end || _entries.size() < _max ||
_entries.contains(peer) || _trimmer.trim(this, peer)) {
// do this even if already contains, to call setLastChanged()
boolean rv = _entries.add(peer);
setLastChanged();
return rv;
}
return false;
}
/**
* @return if removed. Does NOT set lastChanged.
*/
public boolean remove(T peer) {
boolean rv = _entries.remove(peer);
//if (rv)
// setLastChanged();
return rv;
}
/**
* Update the last-changed timestamp to now.
*/
public void setLastChanged() {
_lastChanged = _context.clock().now();
}
/**
* The last-changed timestamp, which actually indicates last-added or last-seen.
*/
public long getLastChanged() {
return _lastChanged;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(1024);
buf.append(_entries.size());
buf.append(" entries in (").append(_begin).append(',').append(_end);
buf.append(") : ").append(_entries.toString());
return buf.toString();
}
}

View File

@ -0,0 +1,790 @@
package net.i2p.kademlia;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.Log;
/**
* In-memory storage of buckets sorted by the XOR metric from the base (us)
* passed in via the constructor.
* This starts with one bucket covering the whole key space, and
* may eventually be split to a max of the number of bits in the data type
* (160 for SHA1Hash or 256 for Hash),
* times 2**(B-1) for Kademlia value B.
*
* Refactored from net.i2p.router.networkdb.kademlia
* @since 0.9.2
*/
public class KBucketSet<T extends SimpleDataStructure> {
private final Log _log;
private final I2PAppContext _context;
private final T _us;
/**
* The bucket list is locked by _bucketsLock, however the individual
* buckets are not locked. Users may see buckets that have more than
* the maximum k entries, or may have adds and removes silently fail
* when they appear to succeed.
*
* Closest values are in bucket 0, furthest are in the last bucket.
*/
private final List<KBucket> _buckets;
private final Range<T> _rangeCalc;
private final KBucketTrimmer _trimmer;
/**
* Locked for reading only when traversing all the buckets.
* Locked for writing only when splitting a bucket.
* Adds/removes/gets from individual buckets are not locked.
*/
private final ReentrantReadWriteLock _bucketsLock = new ReentrantReadWriteLock(false);
private final int KEYSIZE_BITS;
private final int NUM_BUCKETS;
private final int BUCKET_SIZE;
private final int B_VALUE;
private final int B_FACTOR;
/**
* Use the default trim strategy, which removes a random entry.
* @param us the local identity (typically a SHA1Hash or Hash)
* The class must have a zero-argument constructor.
* @param max the Kademlia value "k", the max per bucket, k >= 4
* @param b the Kademlia value "b", split buckets an extra 2**(b-1) times,
* b > 0, use 1 for bittorrent, Kademlia paper recommends 5
*/
public KBucketSet(I2PAppContext context, T us, int max, int b) {
this(context, us, max, b, new RandomTrimmer(context, max));
}
/**
* Use the supplied trim strategy.
*/
public KBucketSet(I2PAppContext context, T us, int max, int b, KBucketTrimmer trimmer) {
_us = us;
_context = context;
_log = context.logManager().getLog(KBucketSet.class);
_trimmer = trimmer;
if (max <= 4 || b <= 0 || b > 8)
throw new IllegalArgumentException();
KEYSIZE_BITS = us.length() * 8;
B_VALUE = b;
B_FACTOR = 1 << (b - 1);
NUM_BUCKETS = KEYSIZE_BITS * B_FACTOR;
BUCKET_SIZE = max;
_buckets = createBuckets();
_rangeCalc = new Range(us, B_VALUE);
// this verifies the zero-argument constructor
makeKey(new byte[us.length()]);
}
private void getReadLock() {
_bucketsLock.readLock().lock();
}
/**
* Get the lock if we can. Non-blocking.
* @return true if the lock was acquired
*/
private boolean tryReadLock() {
return _bucketsLock.readLock().tryLock();
}
private void releaseReadLock() {
_bucketsLock.readLock().unlock();
}
/** @return true if the lock was acquired */
private boolean getWriteLock() {
try {
boolean rv = _bucketsLock.writeLock().tryLock(3000, TimeUnit.MILLISECONDS);
if ((!rv) && _log.shouldLog(Log.WARN))
_log.warn("no lock, size is: " + _bucketsLock.getQueueLength(), new Exception("rats"));
return rv;
} catch (InterruptedException ie) {}
return false;
}
private void releaseWriteLock() {
_bucketsLock.writeLock().unlock();
}
/**
* @return true if the peer is new to the bucket it goes in, or false if it was
* already in it. Always returns false on an attempt to add ourselves.
*
*/
public boolean add(T peer) {
KBucket bucket;
getReadLock();
try {
bucket = getBucket(peer);
} finally { releaseReadLock(); }
if (bucket != null) {
if (bucket.add(peer)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer " + peer + " added to bucket " + bucket);
if (shouldSplit(bucket)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Splitting bucket " + bucket);
split(bucket.getRangeBegin());
//testAudit(this, _log);
}
return true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer " + peer + " NOT added to bucket " + bucket);
return false;
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to add, probably us: " + peer);
return false;
}
}
/**
* No lock required.
* FIXME will split the closest buckets too far if B > 1 and K < 2**B
* Won't ever really happen and if it does it still works.
*/
private boolean shouldSplit(KBucket b) {
return
b.getRangeBegin() != b.getRangeEnd() &&
b.getKeyCount() > BUCKET_SIZE;
}
/**
* Grabs the write lock.
* Caller must NOT have the read lock.
* The bucket should be splittable (range start != range end).
* @param r the range start of the bucket to be split
*/
private void split(int r) {
if (!getWriteLock())
return;
try {
locked_split(r);
} finally { releaseWriteLock(); }
}
/**
* Creates two or more new buckets. The old bucket is replaced and discarded.
*
* Caller must hold write lock
* The bucket should be splittable (range start != range end).
* @param r the range start of the bucket to be split
*/
private void locked_split(int r) {
int b = pickBucket(r);
while (shouldSplit(_buckets.get(b))) {
KBucket<T> b0 = _buckets.get(b);
// Each bucket gets half the keyspace.
// When B_VALUE = 1, or the bucket is larger than B_FACTOR, then
// e.g. 0-159 => 0-158, 159-159
// When B_VALUE > 1, and the bucket is smaller than B_FACTOR, then
// e.g. 1020-1023 => 1020-1021, 1022-1023
int s1, e1, s2, e2;
s1 = b0.getRangeBegin();
e2 = b0.getRangeEnd();
if (B_FACTOR > 1 &&
(s1 & (B_FACTOR - 1)) == 0 &&
((e2 + 1) & (B_FACTOR - 1)) == 0 &&
e2 > s1 + B_FACTOR) {
// The bucket is a "whole" kbucket with a range > B_FACTOR,
// so it should be split into two "whole" kbuckets each with
// a range >= B_FACTOR.
// Log split
s2 = e2 + 1 - B_FACTOR;
} else {
// The bucket is the smallest "whole" kbucket with a range == B_FACTOR,
// or B_VALUE > 1 and the bucket has already been split.
// Start or continue splitting down to a depth B_VALUE.
// Linear split
s2 = s1 + ((1 + e2 - s1) / 2);
}
e1 = s2 - 1;
if (_log.shouldLog(Log.INFO))
_log.info("Splitting (" + s1 + ',' + e2 + ") -> (" + s1 + ',' + e1 + ") (" + s2 + ',' + e2 + ')');
KBucket<T> b1 = createBucket(s1, e1);
KBucket<T> b2 = createBucket(s2, e2);
for (T key : b0.getEntries()) {
if (getRange(key) < s2)
b1.add(key);
else
b2.add(key);
}
_buckets.set(b, b1);
_buckets.add(b + 1, b2);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Split bucket at idx " + b +
":\n" + b0 +
"\ninto: " + b1 +
"\nand: " + b2);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("State is now: " + toString());
if (b2.getKeyCount() > BUCKET_SIZE) {
// should be rare... too hard to call _trimmer from here
// (and definitely not from inside the write lock)
if (_log.shouldLog(Log.INFO))
_log.info("All went into 2nd bucket after split");
}
// loop if all the entries went in the first bucket
}
}
/**
* The current number of entries.
*/
public int size() {
int rv = 0;
getReadLock();
try {
for (KBucket b : _buckets) {
rv += b.getKeyCount();
}
} finally { releaseReadLock(); }
return rv;
}
public boolean remove(T entry) {
KBucket kbucket;
getReadLock();
try {
kbucket = getBucket(entry);
} finally { releaseReadLock(); }
boolean removed = kbucket.remove(entry);
return removed;
}
/** @since 0.8.8 */
public void clear() {
getReadLock();
try {
for (KBucket b : _buckets) {
b.clear();
}
} finally { releaseReadLock(); }
_rangeCalc.clear();
}
/**
* @return a copy in a new set
*/
public Set<T> getAll() {
Set<T> all = new HashSet(256);
getReadLock();
try {
for (KBucket b : _buckets) {
all.addAll(b.getEntries());
}
} finally { releaseReadLock(); }
return all;
}
/**
* @return a copy in a new set
*/
public Set<T> getAll(Set<T> toIgnore) {
Set<T> all = getAll();
all.removeAll(toIgnore);
return all;
}
public void getAll(SelectionCollector<T> collector) {
getReadLock();
try {
for (KBucket b : _buckets) {
b.getEntries(collector);
}
} finally { releaseReadLock(); }
}
/**
* The keys closest to us.
* Returned list will never contain us.
* @return non-null, closest first
*/
public List<T> getClosest(int max) {
return getClosest(max, Collections.EMPTY_SET);
}
/**
* The keys closest to us.
* Returned list will never contain us.
* @return non-null, closest first
*/
public List<T> getClosest(int max, Collection<T> toIgnore) {
List<T> rv = new ArrayList(max);
int count = 0;
getReadLock();
try {
// start at first (closest) bucket
for (int i = 0; i < _buckets.size() && count < max; i++) {
Set<T> entries = _buckets.get(i).getEntries();
// add the whole bucket except for ignores,
// extras will be trimmed after sorting
for (T e : entries) {
if (!toIgnore.contains(e)) {
rv.add(e);
count++;
}
}
}
} finally { releaseReadLock(); }
Comparator comp = new XORComparator(_us);
Collections.sort(rv, comp);
int sz = rv.size();
for (int i = sz - 1; i >= max; i--) {
rv.remove(i);
}
return rv;
}
/**
* The keys closest to the key.
* Returned list will never contain us.
* @return non-null, closest first
*/
public List<T> getClosest(T key, int max) {
return getClosest(key, max, Collections.EMPTY_SET);
}
/**
* The keys closest to the key.
* Returned list will never contain us.
* @return non-null, closest first
*/
public List<T> getClosest(T key, int max, Collection<T> toIgnore) {
if (key.equals(_us))
return getClosest(max, toIgnore);
List<T> rv = new ArrayList(max);
int count = 0;
getReadLock();
try {
int start = pickBucket(key);
// start at closest bucket, then to the smaller (closer to us) buckets
for (int i = start; i >= 0 && count < max; i--) {
Set<T> entries = _buckets.get(i).getEntries();
for (T e : entries) {
if (!toIgnore.contains(e)) {
rv.add(e);
count++;
}
}
}
// then the farther from us buckets if necessary
for (int i = start + 1; i < _buckets.size() && count < max; i++) {
Set<T> entries = _buckets.get(i).getEntries();
for (T e : entries) {
if (!toIgnore.contains(e)) {
rv.add(e);
count++;
}
}
}
} finally { releaseReadLock(); }
Comparator comp = new XORComparator(key);
Collections.sort(rv, comp);
int sz = rv.size();
for (int i = sz - 1; i >= max; i--) {
rv.remove(i);
}
return rv;
}
/**
* The bucket number (NOT the range number) that the xor of the key goes in
* Caller must hold read lock
* @return 0 to max-1 or -1 for us
*/
private int pickBucket(T key) {
int range = getRange(key);
if (range < 0)
return -1;
int rv = pickBucket(range);
if (rv >= 0) {
return rv;
}
_log.error("Key does not fit in any bucket?! WTF!\nKey : ["
+ DataHelper.toHexString(key.getData()) + "]"
+ "\nUs : " + _us
+ "\nDelta: ["
+ DataHelper.toHexString(DataHelper.xor(_us.getData(), key.getData()))
+ "]", new Exception("WTF"));
_log.error(toString());
throw new IllegalStateException("pickBucket returned " + rv);
//return -1;
}
/**
* Returned list is a copy of the bucket list, closest first,
* with the actual buckets (not a copy).
*
* Primarily for testing. You shouldn't ever need to get all the buckets.
* Use getClosest() or getAll() instead to get the keys.
*
* @return non-null
*/
List<KBucket<T>> getBuckets() {
getReadLock();
try {
return new ArrayList(_buckets);
} finally { releaseReadLock(); }
}
/**
* The bucket that the xor of the key goes in
* Caller must hold read lock
* @return null if key is us
*/
private KBucket getBucket(T key) {
int bucket = pickBucket(key);
if (bucket < 0)
return null;
return _buckets.get(bucket);
}
/**
* The bucket number that contains this range number
* Caller must hold read lock or write lock
* @return 0 to max-1 or -1 for us
*/
private int pickBucket(int range) {
// If B is small, a linear search from back to front
// is most efficient since most of the keys are at the end...
// If B is larger, there's a lot of sub-buckets
// of equal size to be checked so a binary search is better
if (B_VALUE <= 3) {
for (int i = _buckets.size() - 1; i >= 0; i--) {
KBucket b = _buckets.get(i);
if (range >= b.getRangeBegin() && range <= b.getRangeEnd())
return i;
}
return -1;
} else {
KBucket dummy = new DummyBucket(range);
return Collections.binarySearch(_buckets, dummy, new BucketComparator());
}
}
private List<KBucket> createBuckets() {
// just an initial size
List<KBucket> buckets = new ArrayList(4 * B_FACTOR);
buckets.add(createBucket(0, NUM_BUCKETS -1));
return buckets;
}
private KBucket createBucket(int start, int end) {
if (end - start >= B_FACTOR &&
(((end + 1) & B_FACTOR - 1) != 0 ||
(start & B_FACTOR - 1) != 0))
throw new IllegalArgumentException("Sub-bkt crosses K-bkt boundary: " + start + '-' + end);
KBucket bucket = new KBucketImpl(_context, start, end, BUCKET_SIZE, _trimmer);
return bucket;
}
/**
* The number of bits minus 1 (range number) for the xor of the key.
* Package private for testing only. Others shouldn't need this.
* @return 0 to max-1 or -1 for us
*/
int getRange(T key) {
return _rangeCalc.getRange(key);
}
/**
* For every bucket that hasn't been updated in this long,
* generate a random key that would be a member of that bucket.
* The returned keys may be searched for to "refresh" the buckets.
* @return non-null, closest first
*/
public List<T> getExploreKeys(long age) {
List<T> rv = new ArrayList(_buckets.size());
long old = _context.clock().now() - age;
getReadLock();
try {
for (KBucket b : _buckets) {
if (b.getLastChanged() < old)
rv.add(generateRandomKey(b));
}
} finally { releaseReadLock(); }
return rv;
}
/**
* Generate a random key to go within this bucket
* Package private for testing only. Others shouldn't need this.
*/
T generateRandomKey(KBucket bucket) {
int begin = bucket.getRangeBegin();
int end = bucket.getRangeEnd();
// number of fixed bits, out of B_VALUE - 1 bits
int fixed = 0;
int bsz = 1 + end - begin;
// compute fixed = B_VALUE - log2(bsz)
// e.g for B=4, B_FACTOR=8, sz 4-> fixed 1, sz 2->fixed 2, sz 1 -> fixed 3
while (bsz < B_FACTOR) {
fixed++;
bsz <<= 1;
}
int fixedBits = 0;
if (fixed > 0) {
// 0x01, 03, 07, 0f, ...
int mask = (1 << fixed) - 1;
// fixed bits masked from begin
fixedBits = (begin >> (B_VALUE - (fixed + 1))) & mask;
}
int obegin = begin;
int oend = end;
begin >>= (B_VALUE - 1);
end >>= (B_VALUE - 1);
// we need randomness for [0, begin) bits
BigInteger variance;
// 00000000rrrr
if (begin > 0)
variance = new BigInteger(begin - fixed, _context.random());
else
variance = BigInteger.ZERO;
// we need nonzero randomness for [begin, end] bits
int numNonZero = 1 + end - begin;
if (numNonZero == 1) {
// 00001000rrrr
variance = variance.setBit(begin);
// fixed bits as the 'main' bucket is split
// 00001fffrrrr
if (fixed > 0)
variance = variance.or(BigInteger.valueOf(fixedBits).shiftLeft(begin - fixed));
} else {
// dont span main bucket boundaries with depth > 1
if (fixed > 0)
throw new IllegalStateException("WTF " + bucket);
BigInteger nonz;
if (numNonZero <= 62) {
// add one to ensure nonzero
long nz = 1 + _context.random().nextLong((1l << numNonZero) - 1);
nonz = BigInteger.valueOf(nz);
} else {
// loop to ensure nonzero
do {
nonz = new BigInteger(numNonZero, _context.random());
} while (nonz.equals(BigInteger.ZERO));
}
// shift left and or-in the nonzero randomness
if (begin > 0)
nonz = nonz.shiftLeft(begin);
// 0000nnnnrrrr
variance = variance.or(nonz);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("SB(" + obegin + ',' + oend + ") KB(" + begin + ',' + end + ") fixed=" + fixed + " fixedBits=" + fixedBits + " numNonZ=" + numNonZero);
byte data[] = variance.toByteArray();
T key = makeKey(data);
byte[] hash = DataHelper.xor(key.getData(), _us.getData());
T rv = makeKey(hash);
// DEBUG
//int range = getRange(rv);
//if (range < obegin || range > oend) {
// throw new IllegalStateException("Generate random key failed range=" + range + " for " + rv + " meant for bucket " + bucket);
//}
return rv;
}
/**
* Make a new SimpleDataStrucure from the data
* @param data size <= SDS length, else throws IAE
* Can be 1 bigger if top byte is zero
*/
private T makeKey(byte[] data) {
int len = _us.length();
int dlen = data.length;
if (dlen > len + 1 ||
(dlen == len + 1 && data[0] != 0))
throw new IllegalArgumentException("bad length " + dlen + " > " + len);
T rv;
try {
rv = (T) _us.getClass().newInstance();
} catch (Exception e) {
_log.error("fail", e);
throw new RuntimeException(e);
}
if (dlen == len) {
rv.setData(data);
} else {
byte[] ndata = new byte[len];
if (dlen == len + 1) {
// one bigger
System.arraycopy(data, 1, ndata, 0, len);
} else {
// smaller
System.arraycopy(data, 0, ndata, len - dlen, dlen);
}
rv.setData(ndata);
}
return rv;
}
private static class Range<T extends SimpleDataStructure> {
private final int _bValue;
private final BigInteger _bigUs;
private final Map<T, Integer> _distanceCache;
public Range(T us, int bValue) {
_bValue = bValue;
_bigUs = new BigInteger(1, us.getData());
_distanceCache = new LHM(256);
}
/** @return 0 to max-1 or -1 for us */
public int getRange(T key) {
Integer rv;
synchronized (_distanceCache) {
rv = _distanceCache.get(key);
if (rv == null) {
// easy way when _bValue == 1
//rv = Integer.valueOf(_bigUs.xor(new BigInteger(1, key.getData())).bitLength() - 1);
BigInteger xor = _bigUs.xor(new BigInteger(1, key.getData()));
int range = xor.bitLength() - 1;
if (_bValue > 1) {
int toShift = range + 1 - _bValue;
int highbit = range;
range <<= _bValue - 1;
if (toShift >= 0) {
int extra = xor.clearBit(highbit).shiftRight(toShift).intValue();
range += extra;
//Log log = I2PAppContext.getGlobalContext().logManager().getLog(KBucketSet.class);
//if (log.shouldLog(Log.DEBUG))
// log.debug("highbit " + highbit + " toshift " + toShift + " extra " + extra + " new " + range);
}
}
rv = Integer.valueOf(range);
_distanceCache.put(key, rv);
}
}
return rv.intValue();
}
public void clear() {
synchronized (_distanceCache) {
_distanceCache.clear();
}
}
}
private static class LHM<K, V> extends LinkedHashMap<K, V> {
private final int _max;
public LHM(int max) {
super(max, 0.75f, true);
_max = max;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > _max;
}
}
/**
* For Collections.binarySearch.
* getRangeBegin == getRangeEnd.
*/
private static class DummyBucket<T extends SimpleDataStructure> implements KBucket<T> {
private final int r;
public DummyBucket(int range) {
r = range;
}
public int getRangeBegin() { return r; }
public int getRangeEnd() { return r; }
public int getKeyCount() {
return 0;
}
public Set<T> getEntries() {
throw new UnsupportedOperationException();
}
public void getEntries(SelectionCollector<T> collector) {
throw new UnsupportedOperationException();
}
public void clear() {}
public boolean add(T peer) {
throw new UnsupportedOperationException();
}
public boolean remove(T peer) {
return false;
}
public void setLastChanged() {}
public long getLastChanged() {
return 0;
}
}
/**
* For Collections.binarySearch.
* Returns equal for any overlap.
*/
private static class BucketComparator implements Comparator<KBucket> {
public int compare(KBucket l, KBucket r) {
if (l.getRangeEnd() < r.getRangeBegin())
return -1;
if (l.getRangeBegin() > r.getRangeEnd())
return 1;
return 0;
}
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(1024);
buf.append("Bucket set rooted on: ").append(_us.toString())
.append(" K= ").append(BUCKET_SIZE)
.append(" B= ").append(B_VALUE)
.append(" with ").append(size())
.append(" keys in ").append(_buckets.size()).append(" buckets:\n");
getReadLock();
try {
int len = _buckets.size();
for (int i = 0; i < len; i++) {
KBucket b = _buckets.get(i);
buf.append("* Bucket ").append(i).append("/").append(len).append(": ");
buf.append(b.toString()).append("\n");
}
} finally { releaseReadLock(); }
return buf.toString();
}
}

View File

@ -0,0 +1,20 @@
package net.i2p.kademlia;
import net.i2p.data.SimpleDataStructure;
/**
* Called when a kbucket can no longer be split and is too big
* @since 0.9.2
*/
public interface KBucketTrimmer<K extends SimpleDataStructure> {
/**
* Called from add() just before adding the entry.
* You may call getEntries() and/or remove() from here.
* Do NOT call add().
* To always discard a newer entry, always return false.
*
* @param kbucket the kbucket that is now too big
* @return true to actually add the entry.
*/
public boolean trim(KBucket<K> kbucket, K toAdd);
}

View File

@ -0,0 +1,21 @@
package net.i2p.kademlia;
import net.i2p.I2PAppContext;
import net.i2p.data.SimpleDataStructure;
/**
* Removes a random element, but only if the bucket hasn't changed in 5 minutes.
* @since 0.9.2
*/
public class RandomIfOldTrimmer<T extends SimpleDataStructure> extends RandomTrimmer<T> {
public RandomIfOldTrimmer(I2PAppContext ctx, int max) {
super(ctx, max);
}
public boolean trim(KBucket<T> kbucket, T toAdd) {
if (kbucket.getLastChanged() > _ctx.clock().now() - 5*60*1000)
return false;
return super.trim(kbucket, toAdd);
}
}

View File

@ -0,0 +1,31 @@
package net.i2p.kademlia;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.SimpleDataStructure;
/**
* Removes a random element. Not resistant to flooding.
* @since 0.9.2
*/
public class RandomTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> {
protected final I2PAppContext _ctx;
private final int _max;
public RandomTrimmer(I2PAppContext ctx, int max) {
_ctx = ctx;
_max = max;
}
public boolean trim(KBucket<T> kbucket, T toAdd) {
List<T> e = new ArrayList(kbucket.getEntries());
int sz = e.size();
// concurrency
if (sz < _max)
return true;
T toRemove = e.get(_ctx.random().nextInt(sz));
return kbucket.remove(toRemove);
}
}

View File

@ -0,0 +1,13 @@
package net.i2p.kademlia;
import net.i2p.data.SimpleDataStructure;
/**
* Removes nothing and always rejects the add. Flood resistant..
* @since 0.9.2
*/
public class RejectTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> {
public boolean trim(KBucket<T> kbucket, T toAdd) {
return false;
}
}

View File

@ -0,0 +1,11 @@
package net.i2p.kademlia;
import net.i2p.data.SimpleDataStructure;
/**
* Visit kbuckets, gathering matches
* @since 0.9.2
*/
public interface SelectionCollector<T extends SimpleDataStructure> {
public void add(T entry);
}

View File

@ -0,0 +1,28 @@
package net.i2p.kademlia;
import java.util.Comparator;
import net.i2p.data.DataHelper;
import net.i2p.data.SimpleDataStructure;
/**
* Help sort Hashes in relation to a base key using the XOR metric
*
* @since 0.9.2
*/
class XORComparator<T extends SimpleDataStructure> implements Comparator<T> {
private final byte[] _base;
/**
* @param target key to compare distances with
*/
public XORComparator(T target) {
_base = target.getData();
}
public int compare(T lhs, T rhs) {
byte lhsDelta[] = DataHelper.xor(lhs.getData(), _base);
byte rhsDelta[] = DataHelper.xor(rhs.getData(), _base);
return DataHelper.compareTo(lhsDelta, rhsDelta);
}
}

View File

@ -0,0 +1,6 @@
<html><body><p>
This is a major rewrite of KBucket, KBucketSet, and KBucketImpl from net.i2p.router.networkdb.kademlia.
The classes are now generic to support SHA1. SHA256, or other key lengths.
The long-term goal is to prove out this new implementation in i2psnark,
then move it to core, then convert the network database to use it.
</p></body></html>

View File

@ -29,8 +29,12 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.Hash;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.ObjectCounter;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
/**
* Accepts connections on a TCP port and routes them to sub-acceptors.
@ -41,11 +45,15 @@ public class ConnectionAcceptor implements Runnable
private I2PServerSocket serverSocket;
private PeerAcceptor peeracceptor;
private Thread thread;
private I2PSnarkUtil _util;
private final I2PSnarkUtil _util;
private final ObjectCounter<Hash> _badCounter = new ObjectCounter();
private boolean stop;
private boolean socketChanged;
private static final int MAX_BAD = 2;
private static final long BAD_CLEAN_INTERVAL = 30*60*1000;
public ConnectionAcceptor(I2PSnarkUtil util) { _util = util; }
public synchronized void startAccepting(PeerCoordinatorSet set, I2PServerSocket socket) {
@ -59,6 +67,7 @@ public class ConnectionAcceptor implements Runnable
thread = new I2PAppThread(this, "I2PSnark acceptor");
thread.setDaemon(true);
thread.start();
_util.getContext().simpleScheduler().addPeriodicEvent(new Cleaner(), BAD_CLEAN_INTERVAL);
}
}
}
@ -70,11 +79,10 @@ public class ConnectionAcceptor implements Runnable
this.peeracceptor = peeracceptor;
_util = util;
socketChanged = false;
stop = false;
thread = new I2PAppThread(this, "I2PSnark acceptor");
thread.setDaemon(true);
thread.start();
_util.getContext().simpleScheduler().addPeriodicEvent(new Cleaner(), BAD_CLEAN_INTERVAL);
}
public void halt()
@ -138,7 +146,13 @@ public class ConnectionAcceptor implements Runnable
}
} else {
if (socket.getPeerDestination().equals(_util.getMyDestination())) {
_util.debug("Incoming connection from myself", Snark.ERROR);
_log.error("Incoming connection from myself");
try { socket.close(); } catch (IOException ioe) {}
continue;
}
if (_badCounter.count(socket.getPeerDestination().calculateHash()) >= MAX_BAD) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting connection from " + socket.getPeerDestination().calculateHash() + " after " + MAX_BAD + " failures");
try { socket.close(); } catch (IOException ioe) {}
continue;
}
@ -149,13 +163,13 @@ public class ConnectionAcceptor implements Runnable
catch (I2PException ioe)
{
if (!socketChanged) {
_util.debug("Error while accepting: " + ioe, Snark.ERROR);
_log.error("Error while accepting", ioe);
stop = true;
}
}
catch (IOException ioe)
{
_util.debug("Error while accepting: " + ioe, Snark.ERROR);
_log.error("Error while accepting", ioe);
stop = true;
}
// catch oom?
@ -171,10 +185,12 @@ public class ConnectionAcceptor implements Runnable
}
private class Handler implements Runnable {
private I2PSocket _socket;
private final I2PSocket _socket;
public Handler(I2PSocket socket) {
_socket = socket;
}
public void run() {
try {
InputStream in = _socket.getInputStream();
@ -182,13 +198,23 @@ public class ConnectionAcceptor implements Runnable
// this is for the readahead in PeerAcceptor.connection()
in = new BufferedInputStream(in);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handling socket from " + _socket.getPeerDestination().calculateHash().toBase64());
_log.debug("Handling socket from " + _socket.getPeerDestination().calculateHash());
peeracceptor.connection(_socket, in, out);
} catch (PeerAcceptor.ProtocolException ihe) {
_badCounter.increment(_socket.getPeerDestination().calculateHash());
if (_log.shouldLog(Log.INFO))
_log.info("Protocol error from " + _socket.getPeerDestination().calculateHash(), ihe);
try { _socket.close(); } catch (IOException ignored) { }
} catch (IOException ioe) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Error handling connection from " + _socket.getPeerDestination().calculateHash().toBase64(), ioe);
_log.debug("Error handling connection from " + _socket.getPeerDestination().calculateHash(), ioe);
try { _socket.close(); } catch (IOException ignored) { }
}
}
}
/** @since 0.9.1 */
private class Cleaner implements SimpleTimer.TimedEvent {
public void timeReached() { _badCounter.clear(); }
}
}

View File

@ -24,7 +24,7 @@ package org.klomp.snark;
/**
* Callback used when some peer changes state.
*/
public interface CoordinatorListener
interface CoordinatorListener
{
/**
* Called when the PeerCoordinator notices a change in the state of a peer.
@ -40,4 +40,5 @@ public interface CoordinatorListener
public boolean overUploadLimit(int uploaders);
public boolean overUpBWLimit();
public boolean overUpBWLimit(long total);
public void addMessage(String message);
}

View File

@ -1,7 +1,6 @@
package org.klomp.snark;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
@ -15,7 +14,6 @@ import net.i2p.util.Log;
import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEncoder;
import org.klomp.snark.bencode.BEValue;
import org.klomp.snark.bencode.InvalidBEncodingException;
/**
* REF: BEP 10 Extension Protocol
@ -24,14 +22,15 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
*/
abstract class ExtensionHandler {
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ExtensionHandler.class);
public static final int ID_HANDSHAKE = 0;
public static final int ID_METADATA = 1;
public static final String TYPE_METADATA = "ut_metadata";
public static final int ID_PEX = 2;
/** not ut_pex since the compact format is different */
public static final String TYPE_PEX = "i2p_pex";
public static final int ID_DHT = 3;
/** not using the option bit since the compact format is different */
public static final String TYPE_DHT = "i2p_dht";
/** Pieces * SHA1 Hash length, + 25% extra for file names, benconding overhead, etc */
private static final int MAX_METADATA_SIZE = Storage.MAX_PIECES * 20 * 5 / 4;
private static final int PARALLEL_REQUESTS = 3;
@ -39,15 +38,23 @@ abstract class ExtensionHandler {
/**
* @param metasize -1 if unknown
* @param pexAndMetadata advertise these capabilities
* @param dht advertise DHT capability
* @return bencoded outgoing handshake message
*/
public static byte[] getHandshake(int metasize) {
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht) {
Map<String, Object> handshake = new HashMap();
Map<String, Integer> m = new HashMap();
m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA));
m.put(TYPE_PEX, Integer.valueOf(ID_PEX));
if (metasize >= 0)
handshake.put("metadata_size", Integer.valueOf(metasize));
if (pexAndMetadata) {
m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA));
m.put(TYPE_PEX, Integer.valueOf(ID_PEX));
if (metasize >= 0)
handshake.put("metadata_size", Integer.valueOf(metasize));
}
if (dht) {
m.put(TYPE_DHT, Integer.valueOf(ID_DHT));
}
// include the map even if empty so the far-end doesn't NPE
handshake.put("m", m);
handshake.put("p", Integer.valueOf(6881));
handshake.put("v", "I2PSnark");
@ -56,21 +63,24 @@ abstract class ExtensionHandler {
}
public static void handleMessage(Peer peer, PeerListener listener, int id, byte[] bs) {
if (_log.shouldLog(Log.INFO))
_log.info("Got extension msg " + id + " length " + bs.length + " from " + peer);
Log log = I2PAppContext.getGlobalContext().logManager().getLog(ExtensionHandler.class);
if (log.shouldLog(Log.INFO))
log.info("Got extension msg " + id + " length " + bs.length + " from " + peer);
if (id == ID_HANDSHAKE)
handleHandshake(peer, listener, bs);
handleHandshake(peer, listener, bs, log);
else if (id == ID_METADATA)
handleMetadata(peer, listener, bs);
handleMetadata(peer, listener, bs, log);
else if (id == ID_PEX)
handlePEX(peer, listener, bs);
else if (_log.shouldLog(Log.INFO))
_log.info("Unknown extension msg " + id + " from " + peer);
handlePEX(peer, listener, bs, log);
else if (id == ID_DHT)
handleDHT(peer, listener, bs, log);
else if (log.shouldLog(Log.INFO))
log.info("Unknown extension msg " + id + " from " + peer);
}
private static void handleHandshake(Peer peer, PeerListener listener, byte[] bs) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got handshake msg from " + peer);
private static void handleHandshake(Peer peer, PeerListener listener, byte[] bs, Log log) {
if (log.shouldLog(Log.DEBUG))
log.debug("Got handshake msg from " + peer);
try {
// this throws NPE on missing keys
InputStream is = new ByteArrayInputStream(bs);
@ -81,20 +91,26 @@ abstract class ExtensionHandler {
Map<String, BEValue> msgmap = map.get("m").getMap();
if (msgmap.get(TYPE_PEX) != null) {
if (_log.shouldLog(Log.WARN))
_log.debug("Peer supports PEX extension: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Peer supports PEX extension: " + peer);
// peer state calls peer listener calls sendPEX()
}
if (msgmap.get(TYPE_DHT) != null) {
if (log.shouldLog(Log.DEBUG))
log.debug("Peer supports DHT extension: " + peer);
// peer state calls peer listener calls sendDHT()
}
MagnetState state = peer.getMagnetState();
if (msgmap.get(TYPE_METADATA) == null) {
if (_log.shouldLog(Log.WARN))
_log.debug("Peer does not support metadata extension: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Peer does not support metadata extension: " + peer);
// drop if we need metainfo and we haven't found anybody yet
synchronized(state) {
if (!state.isInitialized()) {
_log.debug("Dropping peer, we need metadata! " + peer);
log.debug("Dropping peer, we need metadata! " + peer);
peer.disconnect();
}
}
@ -103,20 +119,20 @@ abstract class ExtensionHandler {
BEValue msize = map.get("metadata_size");
if (msize == null) {
if (_log.shouldLog(Log.WARN))
_log.debug("Peer does not have the metainfo size yet: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Peer does not have the metainfo size yet: " + peer);
// drop if we need metainfo and we haven't found anybody yet
synchronized(state) {
if (!state.isInitialized()) {
_log.debug("Dropping peer, we need metadata! " + peer);
log.debug("Dropping peer, we need metadata! " + peer);
peer.disconnect();
}
}
return;
}
int metaSize = msize.getInt();
if (_log.shouldLog(Log.WARN))
_log.debug("Got the metainfo size: " + metaSize);
if (log.shouldLog(Log.DEBUG))
log.debug("Got the metainfo size: " + metaSize);
int remaining;
synchronized(state) {
@ -125,21 +141,21 @@ abstract class ExtensionHandler {
if (state.isInitialized()) {
if (state.getSize() != metaSize) {
if (_log.shouldLog(Log.WARN))
_log.debug("Wrong metainfo size " + metaSize + " from: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Wrong metainfo size " + metaSize + " from: " + peer);
peer.disconnect();
return;
}
} else {
// initialize it
if (metaSize > MAX_METADATA_SIZE) {
if (_log.shouldLog(Log.WARN))
_log.debug("Huge metainfo size " + metaSize + " from: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Huge metainfo size " + metaSize + " from: " + peer);
peer.disconnect(false);
return;
}
if (_log.shouldLog(Log.INFO))
_log.info("Initialized state, metadata size = " + metaSize + " from " + peer);
if (log.shouldLog(Log.INFO))
log.info("Initialized state, metadata size = " + metaSize + " from " + peer);
state.initialize(metaSize);
}
remaining = state.chunksRemaining();
@ -152,13 +168,13 @@ abstract class ExtensionHandler {
synchronized(state) {
chk = state.getNextRequest();
}
if (_log.shouldLog(Log.INFO))
_log.info("Request chunk " + chk + " from " + peer);
if (log.shouldLog(Log.INFO))
log.info("Request chunk " + chk + " from " + peer);
sendRequest(peer, chk);
}
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Handshake exception from " + peer, e);
if (log.shouldLog(Log.WARN))
log.warn("Handshake exception from " + peer, e);
}
}
@ -166,15 +182,13 @@ abstract class ExtensionHandler {
private static final int TYPE_DATA = 1;
private static final int TYPE_REJECT = 2;
private static final int CHUNK_SIZE = 16*1024;
/**
* REF: BEP 9
* @since 0.8.4
*/
private static void handleMetadata(Peer peer, PeerListener listener, byte[] bs) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got metadata msg from " + peer);
private static void handleMetadata(Peer peer, PeerListener listener, byte[] bs, Log log) {
if (log.shouldLog(Log.DEBUG))
log.debug("Got metadata msg from " + peer);
try {
InputStream is = new ByteArrayInputStream(bs);
BDecoder dec = new BDecoder(is);
@ -185,8 +199,8 @@ abstract class ExtensionHandler {
MagnetState state = peer.getMagnetState();
if (type == TYPE_REQUEST) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got request for " + piece + " from: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Got request for " + piece + " from: " + peer);
byte[] pc;
synchronized(state) {
pc = state.getChunk(piece);
@ -197,8 +211,8 @@ abstract class ExtensionHandler {
listener.uploaded(peer, pc.length);
} else if (type == TYPE_DATA) {
int size = map.get("total_size").getInt();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got data for " + piece + " length " + size + " from: " + peer);
if (log.shouldLog(Log.DEBUG))
log.debug("Got data for " + piece + " length " + size + " from: " + peer);
boolean done;
int chk = -1;
synchronized(state) {
@ -207,14 +221,14 @@ abstract class ExtensionHandler {
int len = is.available();
if (len != size) {
// probably fatal
if (_log.shouldLog(Log.WARN))
_log.warn("total_size " + size + " but avail data " + len);
if (log.shouldLog(Log.WARN))
log.warn("total_size " + size + " but avail data " + len);
}
peer.downloaded(len);
listener.downloaded(peer, len);
done = state.saveChunk(piece, bs, bs.length - len, len);
if (_log.shouldLog(Log.INFO))
_log.info("Got chunk " + piece + " from " + peer);
if (log.shouldLog(Log.INFO))
log.info("Got chunk " + piece + " from " + peer);
if (!done)
chk = state.getNextRequest();
}
@ -223,26 +237,26 @@ abstract class ExtensionHandler {
// Done!
// PeerState will call the listener (peer coord), who will
// check to see if the MagnetState has it
if (_log.shouldLog(Log.WARN))
_log.warn("Got last chunk from " + peer);
if (log.shouldLog(Log.WARN))
log.warn("Got last chunk from " + peer);
} else {
// get the next chunk
if (_log.shouldLog(Log.INFO))
_log.info("Request chunk " + chk + " from " + peer);
if (log.shouldLog(Log.INFO))
log.info("Request chunk " + chk + " from " + peer);
sendRequest(peer, chk);
}
} else if (type == TYPE_REJECT) {
if (_log.shouldLog(Log.WARN))
_log.warn("Got reject msg from " + peer);
if (log.shouldLog(Log.WARN))
log.warn("Got reject msg from " + peer);
peer.disconnect(false);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Got unknown metadata msg from " + peer);
if (log.shouldLog(Log.WARN))
log.warn("Got unknown metadata msg from " + peer);
peer.disconnect(false);
}
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.info("Metadata ext. msg. exception from " + peer, e);
if (log.shouldLog(Log.INFO))
log.info("Metadata ext. msg. exception from " + peer, e);
// fatal ?
peer.disconnect(false);
}
@ -252,9 +266,11 @@ abstract class ExtensionHandler {
sendMessage(peer, TYPE_REQUEST, piece);
}
/****
private static void sendReject(Peer peer, int piece) {
sendMessage(peer, TYPE_REJECT, piece);
}
****/
/** REQUEST and REJECT are the same except for message type */
private static void sendMessage(Peer peer, int type, int piece) {
@ -267,8 +283,8 @@ abstract class ExtensionHandler {
peer.sendExtension(hisMsgCode, payload);
} catch (Exception e) {
// NPE, no metadata capability
if (_log.shouldLog(Log.WARN))
_log.info("Metadata send req msg exception to " + peer, e);
//if (log.shouldLog(Log.INFO))
// log.info("Metadata send req msg exception to " + peer, e);
}
}
@ -286,8 +302,8 @@ abstract class ExtensionHandler {
peer.sendExtension(hisMsgCode, payload);
} catch (Exception e) {
// NPE, no metadata caps
if (_log.shouldLog(Log.WARN))
_log.info("Metadata send piece msg exception to " + peer, e);
//if (log.shouldLog(Log.INFO))
// log.info("Metadata send piece msg exception to " + peer, e);
}
}
@ -301,15 +317,18 @@ abstract class ExtensionHandler {
* added.f and dropped unsupported
* @since 0.8.4
*/
private static void handlePEX(Peer peer, PeerListener listener, byte[] bs) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got PEX msg from " + peer);
private static void handlePEX(Peer peer, PeerListener listener, byte[] bs, Log log) {
if (log.shouldLog(Log.DEBUG))
log.debug("Got PEX msg from " + peer);
try {
InputStream is = new ByteArrayInputStream(bs);
BDecoder dec = new BDecoder(is);
BEValue bev = dec.bdecodeMap();
Map<String, BEValue> map = bev.getMap();
byte[] ids = map.get("added").getBytes();
bev = map.get("added");
if (bev == null)
return;
byte[] ids = bev.getBytes();
if (ids.length < HASH_LENGTH)
return;
int len = Math.min(ids.length, (I2PSnarkUtil.MAX_CONNECTIONS - 1) * HASH_LENGTH);
@ -319,14 +338,36 @@ abstract class ExtensionHandler {
System.arraycopy(ids, off, hash, 0, HASH_LENGTH);
if (DataHelper.eq(hash, peer.getPeerID().getDestHash()))
continue;
PeerID pID = new PeerID(hash);
PeerID pID = new PeerID(hash, listener.getUtil());
peers.add(pID);
}
// could include ourselves, listener must remove
listener.gotPeers(peer, peers);
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.info("PEX msg exception from " + peer, e);
if (log.shouldLog(Log.INFO))
log.info("PEX msg exception from " + peer, e);
//peer.disconnect(false);
}
}
/**
* Receive the DHT port numbers
* @since DHT
*/
private static void handleDHT(Peer peer, PeerListener listener, byte[] bs, Log log) {
if (log.shouldLog(Log.DEBUG))
log.debug("Got DHT msg from " + peer);
try {
InputStream is = new ByteArrayInputStream(bs);
BDecoder dec = new BDecoder(is);
BEValue bev = dec.bdecodeMap();
Map<String, BEValue> map = bev.getMap();
int qport = map.get("port").getInt();
int rport = map.get("rport").getInt();
listener.gotPort(peer, qport, rport);
} catch (Exception e) {
if (log.shouldLog(Log.INFO))
log.info("DHT msg exception from " + peer, e);
//peer.disconnect(false);
}
}
@ -353,9 +394,27 @@ abstract class ExtensionHandler {
peer.sendExtension(hisMsgCode, payload);
} catch (Exception e) {
// NPE, no PEX caps
if (_log.shouldLog(Log.WARN))
_log.info("PEX msg exception to " + peer, e);
//if (log.shouldLog(Log.INFO))
// log.info("PEX msg exception to " + peer, e);
}
}
/**
* Send the DHT port numbers
* @since DHT
*/
public static void sendDHT(Peer peer, int qport, int rport) {
Map<String, Object> map = new HashMap();
map.put("port", Integer.valueOf(qport));
map.put("rport", Integer.valueOf(rport));
byte[] payload = BEncoder.bencode(map);
try {
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_DHT).getInt();
peer.sendExtension(hisMsgCode, payload);
} catch (Exception e) {
// NPE, no DHT caps
//if (log.shouldLog(Log.INFO))
// log.info("DHT msg exception to " + peer, e);
}
}
}

View File

@ -3,6 +3,7 @@ package org.klomp.snark;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -20,6 +21,7 @@ import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketEepGet;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Base32;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
@ -35,7 +37,7 @@ import net.i2p.util.SimpleTimer;
import net.i2p.util.Translate;
import org.klomp.snark.dht.DHT;
//import org.klomp.snark.dht.KRPC;
import org.klomp.snark.dht.KRPC;
/**
* I2P specific helpers for I2PSnark
@ -53,8 +55,9 @@ public class I2PSnarkUtil {
private String _i2cpHost;
private int _i2cpPort;
private final Map<String, String> _opts;
private I2PSocketManager _manager;
private volatile I2PSocketManager _manager;
private boolean _configured;
private volatile boolean _connecting;
private final Set<Hash> _shitlist;
private int _maxUploaders;
private int _maxUpBW;
@ -62,17 +65,20 @@ public class I2PSnarkUtil {
private final File _tmpDir;
private int _startupDelay;
private boolean _shouldUseOT;
private boolean _shouldUseDHT;
private boolean _areFilesPublic;
private String _openTrackerString;
private List<String> _openTrackers;
private DHT _dht;
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
public static final int DEFAULT_STARTUP_DELAY = 3;
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
public static final int DEFAULT_MAX_UP_BW = 8; //KBps
public static final int MAX_CONNECTIONS = 16; // per torrent
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
//private static final boolean ENABLE_DHT = true;
public static final boolean DEFAULT_USE_DHT = false;
public I2PSnarkUtil(I2PAppContext ctx) {
_context = ctx;
@ -81,12 +87,14 @@ public class I2PSnarkUtil {
//setProxy("127.0.0.1", 4444);
setI2CPConfig("127.0.0.1", 7654, null);
_shitlist = new ConcurrentHashSet();
_configured = false;
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
_maxUpBW = DEFAULT_MAX_UP_BW;
_maxConnections = MAX_CONNECTIONS;
_startupDelay = DEFAULT_STARTUP_DELAY;
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
// FIXME split if default has more than one
_openTrackers = Collections.singletonList(DEFAULT_OPENTRACKERS);
_shouldUseDHT = DEFAULT_USE_DHT;
// This is used for both announce replies and .torrent file downloads,
// so it must be available even if not connected to I2CP.
// so much for multiple instances
@ -115,6 +123,9 @@ public class I2PSnarkUtil {
}
******/
/** @since 0.9.1 */
public I2PAppContext getContext() { return _context; }
public boolean configured() { return _configured; }
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
@ -184,11 +195,15 @@ public class I2PSnarkUtil {
/** @since 0.8.9 */
public void setFilesPublic(boolean yes) { _areFilesPublic = yes; }
/** @since 0.9.1 */
public File getTempDir() { return _tmpDir; }
/**
* Connect to the router, if we aren't already
*/
synchronized public boolean connect() {
if (_manager == null) {
_connecting = true;
// try to find why reconnecting after stop
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connecting to I2P", new Exception("I did it"));
@ -207,6 +222,8 @@ public class I2PSnarkUtil {
// we don't need fast handshake for peer connections.
//if (opts.getProperty("i2p.streaming.connectDelay") == null)
// opts.setProperty("i2p.streaming.connectDelay", "500");
if (opts.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)
opts.setProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT, "75000");
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
@ -219,11 +236,19 @@ public class I2PSnarkUtil {
// opts.setProperty("i2p.streaming.writeTimeout", "90000");
//if (opts.getProperty("i2p.streaming.readTimeout") == null)
// opts.setProperty("i2p.streaming.readTimeout", "120000");
if (opts.getProperty("i2p.streaming.maxConnsPerMinute") == null)
opts.setProperty("i2p.streaming.maxConnsPerMinute", "2");
if (opts.getProperty("i2p.streaming.maxTotalConnsPerMinute") == null)
opts.setProperty("i2p.streaming.maxTotalConnsPerMinute", "8");
if (opts.getProperty("i2p.streaming.maxConnsPerHour") == null)
opts.setProperty("i2p.streaming.maxConnsPerHour", "20");
if (opts.getProperty("i2p.streaming.enforceProtocol") == null)
opts.setProperty("i2p.streaming.enforceProtocol", "true");
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
_connecting = false;
}
// FIXME this only instantiates krpc once, left stuck with old manager
//if (ENABLE_DHT && _manager != null && _dht == null)
// _dht = new KRPC(_context, _manager.getSession());
if (_shouldUseDHT && _manager != null && _dht == null)
_dht = new KRPC(_context, _manager.getSession());
return (_manager != null);
}
@ -235,15 +260,35 @@ public class I2PSnarkUtil {
public boolean connected() { return _manager != null; }
/** @since 0.9.1 */
public boolean isConnecting() { return _manager == null && _connecting; }
/**
* For FetchAndAdd
* @return null if not connected
* @since 0.9.1
*/
public I2PSocketManager getSocketManager() {
return _manager;
}
/**
* Destroy the destination itself
*/
public void disconnect() {
public synchronized void disconnect() {
if (_dht != null) {
_dht.stop();
_dht = null;
}
I2PSocketManager mgr = _manager;
// FIXME this can cause race NPEs elsewhere
_manager = null;
_shitlist.clear();
mgr.destroySocketManager();
if (mgr != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Disconnecting from I2P", new Exception("I did it"));
mgr.destroySocketManager();
}
// this will delete a .torrent file d/l in progress so don't do that...
FileUtil.rmdir(_tmpDir, false);
// in case the user will d/l a .torrent file next...
@ -270,7 +315,7 @@ public class I2PSnarkUtil {
return rv;
} catch (I2PException ie) {
_shitlist.add(dest);
SimpleScheduler.getInstance().addEvent(new Unshitlist(dest), 10*60*1000);
_context.simpleScheduler().addEvent(new Unshitlist(dest), 10*60*1000);
throw new IOException("Unable to reach the peer " + peer + ": " + ie.getMessage());
}
}
@ -282,11 +327,24 @@ public class I2PSnarkUtil {
}
/**
* fetch the given URL, returning the file it is stored in, or null on error
* Fetch the given URL, returning the file it is stored in, or null on error.
* No retries.
*/
public File get(String url) { return get(url, true, 0); }
/**
* @param rewrite if true, convert http://KEY.i2p/foo/announce to http://i2p/KEY/foo/announce
*/
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
/**
* @param retries if < 0, set timeout to a few seconds
*/
public File get(String url, int retries) { return get(url, true, retries); }
/**
* @param retries if < 0, set timeout to a few seconds
*/
public File get(String url, boolean rewrite, int retries) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
@ -295,7 +353,7 @@ public class I2PSnarkUtil {
// we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms...
out = SecureFile.createTempFile("i2psnark", null, _tmpDir);
} catch (IOException ioe) {
ioe.printStackTrace();
_log.error("temp file error", ioe);
if (out != null)
out.delete();
return null;
@ -307,12 +365,21 @@ public class I2PSnarkUtil {
//_log.debug("Rewritten url [" + fetchURL + "]");
//EepGet get = new EepGet(_context, _shouldProxy, _proxyHost, _proxyPort, retries, out.getAbsolutePath(), fetchURL);
// Use our tunnel for announces and .torrent fetches too! Make sure we're connected first...
if (!connected()) {
if (!connect())
int timeout;
if (retries < 0) {
if (!connected())
return null;
timeout = EEPGET_CONNECT_TIMEOUT_SHORT;
retries = 0;
} else {
timeout = EEPGET_CONNECT_TIMEOUT;
if (!connected()) {
if (!connect())
return null;
}
}
EepGet get = new I2PSocketEepGet(_context, _manager, retries, out.getAbsolutePath(), fetchURL);
if (get.fetch()) {
if (get.fetch(timeout)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
return out;
@ -386,7 +453,8 @@ public class I2PSnarkUtil {
if (sess != null) {
byte[] b = Base32.decode(ip.substring(0, BASE32_HASH_LENGTH));
if (b != null) {
Hash h = new Hash(b);
//Hash h = new Hash(b);
Hash h = Hash.create(b);
if (_log.shouldLog(Log.INFO))
_log.info("Using existing session for lookup of " + ip);
try {
@ -441,30 +509,17 @@ public class I2PSnarkUtil {
}
/** @param ot non-null */
public void setOpenTrackerString(String ot) {
_openTrackerString = ot;
public void setOpenTrackers(List<String> ot) {
_openTrackers = ot;
}
public String getOpenTrackerString() {
if (_openTrackerString == null)
return DEFAULT_OPENTRACKERS;
return _openTrackerString;
}
/** comma delimited list open trackers to use as backups */
/** sorted map of name to announceURL=baseURL */
/** List of open trackers to use as backups
* @return non-null, possibly unmodifiable, empty if disabled
*/
public List<String> getOpenTrackers() {
if (!shouldUseOpenTrackers())
return null;
List<String> rv = new ArrayList(1);
String trackers = getOpenTrackerString();
StringTokenizer tok = new StringTokenizer(trackers, ", ");
while (tok.hasMoreTokens())
rv.add(tok.nextToken());
if (rv.isEmpty())
return null;
return rv;
return Collections.EMPTY_LIST;
return _openTrackers;
}
public void setUseOpenTrackers(boolean yes) {
@ -474,6 +529,22 @@ public class I2PSnarkUtil {
public boolean shouldUseOpenTrackers() {
return _shouldUseOT;
}
/** @since DHT */
public synchronized void setUseDHT(boolean yes) {
_shouldUseDHT = yes;
if (yes && _manager != null && _dht == null) {
_dht = new KRPC(_context, _manager.getSession());
} else if (!yes && _dht != null) {
_dht.stop();
_dht = null;
}
}
/** @since DHT */
public boolean shouldUseDHT() {
return _shouldUseDHT;
}
/**
* Like DataHelper.toHexString but ensures no loss of leading zero bytes
@ -490,40 +561,6 @@ public class I2PSnarkUtil {
return buf.toString();
}
/** hook between snark's logger and an i2p log */
void debug(String msg, int snarkDebugLevel) {
debug(msg, snarkDebugLevel, null);
}
void debug(String msg, int snarkDebugLevel, Throwable t) {
if (t instanceof OutOfMemoryError) {
try { Thread.sleep(100); } catch (InterruptedException ie) {}
try {
t.printStackTrace();
} catch (Throwable tt) {}
try {
System.out.println("OOM thread: " + Thread.currentThread().getName());
} catch (Throwable tt) {}
}
switch (snarkDebugLevel) {
case 0:
case 1:
_log.error(msg, t);
break;
case 2:
_log.warn(msg, t);
break;
case 3:
case 4:
_log.info(msg, t);
break;
case 5:
case 6:
default:
_log.debug(msg, t);
break;
}
}
private static final String BUNDLE_NAME = "org.klomp.snark.web.messages";
/** lang in routerconsole.lang property, else current locale */

View File

@ -5,10 +5,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.RandomSource;
import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue;
@ -27,7 +27,6 @@ import org.klomp.snark.bencode.BEValue;
*/
class MagnetState {
public static final int CHUNK_SIZE = 16*1024;
private static final Random random = I2PAppContext.getGlobalContext().random();
private final byte[] infohash;
private boolean complete;
@ -129,7 +128,7 @@ class MagnetState {
throw new IllegalArgumentException("not initialized");
if (complete)
throw new IllegalArgumentException("complete");
int rand = random.nextInt(totalChunks);
int rand = RandomSource.getInstance().nextInt(totalChunks);
for (int i = 0; i < totalChunks; i++) {
int chk = (i + rand) % totalChunks;
if (!(have.get(chk) || requested.get(chk))) {

View File

@ -23,8 +23,6 @@ package org.klomp.snark;
import java.io.DataOutputStream;
import java.io.IOException;
import net.i2p.util.SimpleTimer;
// Used to queue outgoing connections
// sendMessage() should be used to translate them to wire format.
class Message

View File

@ -33,7 +33,6 @@ import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA1;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
@ -61,6 +60,7 @@ public class MetaInfo
private final int piece_length;
private final byte[] piece_hashes;
private final long length;
private final boolean privateTorrent;
private Map<String, BEValue> infoMap;
/**
@ -71,7 +71,7 @@ public class MetaInfo
* @param lengths null for single-file torrent
*/
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
int piece_length, byte[] piece_hashes, long length)
int piece_length, byte[] piece_hashes, long length, boolean privateTorrent)
{
this.announce = announce;
this.name = name;
@ -82,6 +82,7 @@ public class MetaInfo
this.piece_length = piece_length;
this.piece_hashes = piece_hashes;
this.length = length;
this.privateTorrent = privateTorrent;
// TODO if we add a parameter for other keys
//if (other != null) {
@ -160,6 +161,10 @@ public class MetaInfo
else
name_utf8 = null;
// BEP 27
val = info.get("private");
privateTorrent = val != null && val.getString().equals("1");
val = info.get("piece length");
if (val == null)
throw new InvalidBEncodingException("Missing piece length number");
@ -212,7 +217,7 @@ public class MetaInfo
if (l < oldTotal)
throw new InvalidBEncodingException("Huge total length");
val = (BEValue)desc.get("path");
val = desc.get("path");
if (val == null)
throw new InvalidBEncodingException("Missing path list");
List<BEValue> path_list = val.getList();
@ -238,7 +243,7 @@ public class MetaInfo
m_files.add(Collections.unmodifiableList(file));
val = (BEValue)desc.get("path.utf-8");
val = desc.get("path.utf-8");
if (val != null) {
path_list = val.getList();
path_length = path_list.size();
@ -318,6 +323,14 @@ public class MetaInfo
return name;
}
/**
* Is it a private torrent?
* @since 0.9
*/
public boolean isPrivate() {
return privateTorrent;
}
/**
* Returns a list of lists of file name hierarchies or null if it is
* a single name. It has the same size as the list returned by
@ -409,6 +422,29 @@ public class MetaInfo
return false;
return true;
}
/**
* @return good
* @since 0.9.1
*/
boolean checkPiece(PartialPiece pp) {
MessageDigest sha1 = SHA1.getInstance();
int piece = pp.getPiece();
byte[] hash;
try {
hash = pp.getHash();
} catch (IOException ioe) {
// Could be caused by closing a peer connnection
// we don't want the exception to propagate through
// to Storage.putPiece()
_log.warn("Error checking", ioe);
return false;
}
for (int i = 0; i < 20; i++)
if (hash[i] != piece_hashes[20 * piece + i])
return false;
return true;
}
/**
* Returns the total length of the torrent in bytes.
@ -439,7 +475,7 @@ public class MetaInfo
{
return new MetaInfo(announce, name, name_utf8, files,
lengths, piece_length,
piece_hashes, length);
piece_hashes, length, privateTorrent);
}
/**
@ -475,6 +511,10 @@ public class MetaInfo
info.put("name", name);
if (name_utf8 != null)
info.put("name.utf-8", name_utf8);
// BEP 27
if (privateTorrent)
info.put("private", "1");
info.put("piece length", Integer.valueOf(piece_length));
info.put("pieces", piece_hashes);
if (files == null)

View File

@ -1,6 +1,22 @@
package org.klomp.snark;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.MessageDigest;
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA1;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
/**
* Store the received data either on the heap or in a temp file.
* The third option, to write chunks directly to the destination file,
* is unimplemented.
*
* This is the class passed from PeerCoordinator to PeerState so
* PeerState may start requests.
*
@ -8,45 +24,81 @@ package org.klomp.snark;
* a piece is not completely downloaded, for example
* when the Peer disconnects or chokes.
*
* New objects for the same piece are created during the end game -
* this object should not be shared among multiple peers.
*
* @since 0.8.2
*/
class PartialPiece implements Comparable {
private final int piece;
// we store the piece so we can use it in compareTo()
private final Piece piece;
// null if using temp file
private final byte[] bs;
private final int off;
private final long createdTime;
private int off;
//private final long createdTime;
private File tempfile;
private RandomAccessFile raf;
private final int pclen;
private final File tempDir;
// Any bigger than this, use temp file instead of heap
private static final int MAX_IN_MEM = 128 * 1024;
// May be reduced on OOM
private static int _max_in_mem = MAX_IN_MEM;
/**
* Used by PeerCoordinator.
* Creates a new PartialPiece, with no chunks yet downloaded.
* Allocates the data.
* Allocates the data storage area, either on the heap or in the
* temp directory, depending on size.
*
* @param piece Piece number requested.
* @param len must be equal to the piece length
*/
public PartialPiece (int piece, int len) throws OutOfMemoryError {
public PartialPiece (Piece piece, int len, File tempDir) {
this.piece = piece;
this.bs = new byte[len];
this.off = 0;
this.createdTime = 0;
this.pclen = len;
//this.createdTime = 0;
this.tempDir = tempDir;
// temps for finals
byte[] tbs = null;
try {
if (len <= MAX_IN_MEM) {
try {
tbs = new byte[len];
return;
} catch (OutOfMemoryError oom) {
if (_max_in_mem > PeerState.PARTSIZE)
_max_in_mem /= 2;
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PartialPiece.class);
log.logAlways(Log.WARN, "OOM creating new partial piece");
// fall through to use temp file
}
}
// delay creating temp file until required in read()
} finally {
// finals
this.bs = tbs;
}
}
/**
* Used by PeerState.
* Creates a new PartialPiece, with chunks up to but not including
* firstOutstandingRequest already downloaded and stored in the Request byte array.
* Caller must synchronize
*
* Note that this cannot handle gaps; chunks after a missing chunk cannot be saved.
* That would be harder.
*
* @param firstOutstandingRequest the first request not fulfilled for the piece
* @since 0.9.1
*/
public PartialPiece (Request firstOutstandingRequest) {
this.piece = firstOutstandingRequest.piece;
this.bs = firstOutstandingRequest.bs;
this.off = firstOutstandingRequest.off;
this.createdTime = System.currentTimeMillis();
private void createTemp() throws IOException {
//tfile = SecureFile.createTempFile("piece", null, tempDir);
// debug
tempfile = SecureFile.createTempFile("piece_" + piece.getId() + '_', null, tempDir);
//I2PAppContext.getGlobalContext().logManager().getLog(PartialPiece.class).warn("Created " + tempfile);
// tfile.deleteOnExit() ???
raf = new RandomAccessFile(tempfile, "rw");
// Do not preallocate the file space.
// Not necessary to call setLength(), file is extended when written
//traf.setLength(len);
}
/**
@ -55,33 +107,168 @@ class PartialPiece implements Comparable {
*/
public Request getRequest() {
return new Request(this.piece, this.bs, this.off, Math.min(this.bs.length - this.off, PeerState.PARTSIZE));
return new Request(this, this.off, Math.min(this.pclen - this.off, PeerState.PARTSIZE));
}
/** piece number */
public int getPiece() {
return this.piece;
return this.piece.getId();
}
/** how many bytes are good */
/**
* @since 0.9.1
*/
public int getLength() {
return this.pclen;
}
/**
* How many bytes are good - only valid by setDownloaded()
*/
public int getDownloaded() {
return this.off;
}
/**
* Call this before returning a PartialPiece to the PeerCoordinator
* @since 0.9.1
*/
public void setDownloaded(int offset) {
this.off = offset;
}
/****
public long getCreated() {
return this.createdTime;
}
****/
/**
* Highest downloaded first
* Piece must be complete.
* The SHA1 hash of the completely read data.
* @since 0.9.1
*/
public byte[] getHash() throws IOException {
MessageDigest sha1 = SHA1.getInstance();
if (bs != null) {
sha1.update(bs);
} else {
int read = 0;
byte[] buf = new byte[Math.min(pclen, 16384)];
synchronized (this) {
if (raf == null)
throw new IOException();
raf.seek(0);
while (read < pclen) {
int rd = raf.read(buf, 0, Math.min(buf.length, pclen - read));
if (rd < 0)
break;
read += rd;
sha1.update(buf, 0, rd);
}
}
if (read < pclen)
throw new IOException();
}
return sha1.digest();
}
/**
* Blocking.
* @since 0.9.1
*/
public void read(DataInputStream din, int off, int len) throws IOException {
if (bs != null) {
din.readFully(bs, off, len);
} else {
// read in fully before synching on raf
byte[] tmp = new byte[len];
din.readFully(tmp);
synchronized (this) {
if (raf == null)
createTemp();
raf.seek(off);
raf.write(tmp);
}
}
}
/**
* Piece must be complete.
* Caller must synchronize on out and seek to starting point.
* Caller must call release() when done with the whole piece.
*
* @param out stream to write to
* @param offset offset in the piece
* @param len length to write
* @since 0.9.1
*/
public void write(DataOutput out, int offset, int len) throws IOException {
if (bs != null) {
out.write(bs, offset, len);
} else {
int read = 0;
byte[] buf = new byte[Math.min(len, 16384)];
synchronized (this) {
if (raf == null)
throw new IOException();
raf.seek(offset);
while (read < len) {
int rd = Math.min(buf.length, len - read);
raf.readFully(buf, 0, rd);
read += rd;
out.write(buf, 0, rd);
}
}
}
}
/**
* Release all resources.
*
* @since 0.9.1
*/
public void release() {
if (bs == null) {
synchronized (this) {
if (raf != null)
locked_release();
}
//if (raf != null)
// I2PAppContext.getGlobalContext().logManager().getLog(PartialPiece.class).warn("Released " + tempfile);
}
}
/**
* Caller must synchronize
*
* @since 0.9.1
*/
private void locked_release() {
try {
raf.close();
} catch (IOException ioe) {
I2PAppContext.getGlobalContext().logManager().getLog(PartialPiece.class).warn("Error closing " + raf, ioe);
}
tempfile.delete();
}
/*
* Highest priority first,
* then rarest first,
* then highest downloaded first
*/
public int compareTo(Object o) throws ClassCastException {
return ((PartialPiece)o).off - this.off; // reverse
PartialPiece opp = (PartialPiece)o;
int d = this.piece.compareTo(opp.piece);
if (d != 0)
return d;
return opp.off - this.off; // reverse
}
@Override
public int hashCode() {
return piece * 7777;
return piece.getId() * 7777;
}
/**
@ -92,13 +279,13 @@ class PartialPiece implements Comparable {
public boolean equals(Object o) {
if (o instanceof PartialPiece) {
PartialPiece pp = (PartialPiece)o;
return pp.piece == this.piece;
return pp.piece.getId() == this.piece.getId();
}
return false;
}
@Override
public String toString() {
return "Partial(" + piece + ',' + off + ',' + bs.length + ')';
return "Partial(" + piece.getId() + ',' + off + ',' + pclen + ')';
}
}

View File

@ -80,7 +80,9 @@ public class Peer implements Comparable
static final long OPTION_FAST = 0x0000000000000004l;
static final long OPTION_DHT = 0x0000000000000001l;
/** we use a different bit since the compact format is different */
/* no, let's use an extension message
static final long OPTION_I2P_DHT = 0x0000000040000000l;
*/
static final long OPTION_AZMP = 0x1000000000000000l;
private long options;
@ -268,15 +270,18 @@ public class Peer implements Comparable
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer supports extensions, sending reply message");
int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1;
out.sendExtension(0, ExtensionHandler.getHandshake(metasize));
boolean pexAndMetadata = metainfo == null || !metainfo.isPrivate();
boolean dht = util.getDHT() != null;
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht));
}
if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer supports DHT, sending PORT message");
int port = util.getDHT().getPort();
out.sendPort(port);
}
// Old DHT PORT message
//if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Peer supports DHT, sending PORT message");
// int port = util.getDHT().getPort();
// out.sendPort(port);
//}
// Send our bitmap
if (bitfield != null)
@ -459,7 +464,7 @@ public class Peer implements Comparable
if (this.deregister) {
PeerListener p = s.listener;
if (p != null) {
List<PartialPiece> pcs = s.returnPartialPieces();
List<Request> pcs = s.returnPartialPieces();
if (!pcs.isEmpty())
p.savePartialPieces(this, pcs);
// now covered by savePartialPieces

View File

@ -46,6 +46,10 @@ public class PeerAcceptor
private final PeerCoordinator coordinator;
final PeerCoordinatorSet coordinators;
/** shorten timeout while reading handshake */
private static final long HASH_READ_TIMEOUT = 45*1000;
public PeerAcceptor(PeerCoordinator coordinator)
{
this.coordinator = coordinator;
@ -69,11 +73,20 @@ public class PeerAcceptor
// talk about, and we can just look for that in our list of active torrents.
byte peerInfoHash[] = null;
if (in instanceof BufferedInputStream) {
// multitorrent
in.mark(LOOKAHEAD_SIZE);
peerInfoHash = readHash(in);
long timeout = socket.getReadTimeout();
socket.setReadTimeout(HASH_READ_TIMEOUT);
try {
peerInfoHash = readHash(in);
} catch (IOException ioe) {
// unique exception so ConnectionAcceptor can blame the peer
throw new ProtocolException(ioe.toString());
}
socket.setReadTimeout(timeout);
in.reset();
} else {
// is this working right?
// Single torrent - is this working right?
try {
peerInfoHash = readHash(in);
if (_log.shouldLog(Log.INFO))
@ -104,9 +117,8 @@ public class PeerAcceptor
}
} else {
// multitorrent capable, so lets see what we can handle
for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) {
PeerCoordinator cur = (PeerCoordinator)iter.next();
PeerCoordinator cur = coordinators.get(peerInfoHash);
if (cur != null) {
if (DataHelper.eq(cur.getInfoHash(), peerInfoHash)) {
if (cur.needPeers())
{
@ -130,22 +142,50 @@ public class PeerAcceptor
}
}
private static final String PROTO_STR = "BitTorrent protocol";
private static final int PROTO_STR_LEN = PROTO_STR.length();
private static final int PROTO_LEN = PROTO_STR_LEN + 1;
private static final int[] PROTO = new int[PROTO_LEN];
static {
PROTO[0] = PROTO_STR_LEN;
for (int i = 0; i < PROTO_STR_LEN; i++) {
PROTO[i+1] = PROTO_STR.charAt(i);
}
}
/** 48 */
private static final int LOOKAHEAD_SIZE = 1 + // chr(19)
"BitTorrent protocol".length() +
private static final int LOOKAHEAD_SIZE = PROTO_LEN +
8 + // blank, reserved
20; // infohash
/**
* Read ahead to the infohash, throwing an exception if there isn't enough data
* Read ahead to the infohash, throwing an exception if there isn't enough data.
* Also check the first 20 bytes for the correct protocol here and throw IOE if bad,
* so we don't hang waiting for 48 bytes if it's not a bittorrent client.
* The 20 bytes are checked again in Peer.handshake().
*/
private byte[] readHash(InputStream in) throws IOException {
byte buf[] = new byte[LOOKAHEAD_SIZE];
private static byte[] readHash(InputStream in) throws IOException {
for (int i = 0; i < PROTO_LEN; i++) {
int b = in.read();
if (b != PROTO[i])
throw new IOException("Bad protocol 0x" + Integer.toHexString(b) + " at byte " + i);
}
if (in.skip(8) != 8)
throw new IOException("EOF before hash");
byte buf[] = new byte[20];
int read = DataHelper.read(in, buf);
if (read != buf.length)
throw new IOException("Unable to read the hash (read " + read + ")");
byte rv[] = new byte[20];
System.arraycopy(buf, buf.length-rv.length, rv, 0, rv.length);
return rv;
return buf;
}
/**
* A unique exception so we can tell the ConnectionAcceptor about non-BT connections
* @since 0.9.1
*/
public static class ProtocolException extends IOException {
public ProtocolException(String s) {
super(s);
}
}
}

View File

@ -21,11 +21,14 @@
package org.klomp.snark;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import org.klomp.snark.dht.DHT;
/**
* TimerTask that checks for good/bad up/downloader. Works together
@ -37,16 +40,18 @@ class PeerCheckerTask implements Runnable
private final PeerCoordinator coordinator;
private final I2PSnarkUtil _util;
private final Log _log;
private final Random random;
private int _runCount;
PeerCheckerTask(I2PSnarkUtil util, PeerCoordinator coordinator)
{
_util = util;
_log = util.getContext().logManager().getLog(PeerCheckerTask.class);
random = util.getContext().random();
this.coordinator = coordinator;
}
private static final Random random = I2PAppContext.getGlobalContext().random();
public void run()
{
_runCount++;
@ -60,9 +65,7 @@ class PeerCheckerTask implements Runnable
long worstdownload = Long.MAX_VALUE;
Peer worstDownloader = null;
int peers = 0;
int uploaders = 0;
int downloaders = 0;
int removedCount = 0;
long uploaded = 0;
@ -73,6 +76,7 @@ class PeerCheckerTask implements Runnable
List<Peer> removed = new ArrayList();
int uploadLimit = coordinator.allowedUploaders();
boolean overBWLimit = coordinator.overUpBWLimit();
DHT dht = _util.getDHT();
for (Peer peer : peerList) {
// Remove dying peers
@ -85,12 +89,16 @@ class PeerCheckerTask implements Runnable
continue;
}
peers++;
if (peer.getInactiveTime() > PeerCoordinator.MAX_INACTIVE) {
if (_log.shouldLog(Log.WARN))
_log.warn("Disconnecting peer idle " +
DataHelper.formatDuration(peer.getInactiveTime()) + ": " + peer);
peer.disconnect();
continue;
}
if (!peer.isChoking())
uploaders++;
if (!peer.isChoked() && peer.isInteresting())
downloaders++;
long upload = peer.getUploaded();
uploaded += upload;
@ -99,14 +107,15 @@ class PeerCheckerTask implements Runnable
peer.setRateHistory(upload, download);
peer.resetCounters();
_util.debug(peer + ":", Snark.DEBUG);
_util.debug(" ul: " + upload*1024/KILOPERSECOND
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(peer + ":"
+ " ul: " + upload*1024/KILOPERSECOND
+ " dl: " + download*1024/KILOPERSECOND
+ " i: " + peer.isInterested()
+ " I: " + peer.isInteresting()
+ " c: " + peer.isChoking()
+ " C: " + peer.isChoked(),
Snark.DEBUG);
+ " C: " + peer.isChoked());
}
// Choke a percentage of them rather than all so it isn't so drastic...
// unless this torrent is over the limit all by itself.
@ -127,8 +136,8 @@ class PeerCheckerTask implements Runnable
// Check if it still wants pieces from us.
if (!peer.isInterested())
{
_util.debug("Choke uninterested peer: " + peer,
Snark.INFO);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke uninterested peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
@ -138,8 +147,8 @@ class PeerCheckerTask implements Runnable
}
else if (overBWLimitChoke)
{
_util.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer,
Snark.INFO);
if (_log.shouldLog(Log.DEBUG))
_log.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
@ -151,7 +160,8 @@ class PeerCheckerTask implements Runnable
else if (peer.isInteresting() && peer.isChoked())
{
// If they are choking us make someone else a downloader
_util.debug("Choke choking peer: " + peer, Snark.DEBUG);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke choking peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
@ -163,7 +173,8 @@ class PeerCheckerTask implements Runnable
else if (!peer.isInteresting() && !coordinator.completed())
{
// If they aren't interesting make someone else a downloader
_util.debug("Choke uninteresting peer: " + peer, Snark.DEBUG);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke uninteresting peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
@ -177,8 +188,8 @@ class PeerCheckerTask implements Runnable
&& download == 0)
{
// We are downloading but didn't receive anything...
_util.debug("Choke downloader that doesn't deliver:"
+ peer, Snark.DEBUG);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke downloader that doesn't deliver: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
@ -205,10 +216,13 @@ class PeerCheckerTask implements Runnable
// send PEX
if ((_runCount % 17) == 0 && !peer.isCompleted())
coordinator.sendPeers(peer);
peer.keepAlive();
// cheap failsafe for seeds connected to seeds, stop pinging and hopefully
// the inactive checker (above) will eventually disconnect it
if (coordinator.getNeededLength() > 0 || !peer.isCompleted())
peer.keepAlive();
// announce them to local tracker (TrackerClient does this too)
if (_util.getDHT() != null && (_runCount % 5) == 0) {
_util.getDHT().announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
if (dht != null && (_runCount % 5) == 0) {
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
}
}
@ -222,8 +236,8 @@ class PeerCheckerTask implements Runnable
|| uploaders > uploadLimit)
&& worstDownloader != null)
{
_util.debug("Choke worst downloader: " + worstDownloader,
Snark.DEBUG);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke worst downloader: " + worstDownloader);
worstDownloader.setChoking(true);
coordinator.uploaders--;
@ -256,8 +270,8 @@ class PeerCheckerTask implements Runnable
}
// announce ourselves to local tracker (TrackerClient does this too)
if (_util.getDHT() != null && (_runCount % 16) == 0) {
_util.getDHT().announce(coordinator.getInfoHash());
if (dht != null && (_runCount % 16) == 0) {
dht.announce(coordinator.getInfoHash());
}
}
}

View File

@ -148,11 +148,9 @@ class PeerConnectionIn implements Runnable
begin = din.readInt();
len = i-9;
Request req = ps.getOutstandingRequest(piece, begin, len);
byte[] piece_bytes;
if (req != null)
{
piece_bytes = req.bs;
din.readFully(piece_bytes, begin, len);
req.read(din);
ps.pieceMessage(req);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received data(" + piece + "," + begin + ") from " + peer);
@ -160,8 +158,9 @@ class PeerConnectionIn implements Runnable
else
{
// XXX - Consume but throw away afterwards.
piece_bytes = new byte[len];
din.readFully(piece_bytes);
int rcvd = din.skipBytes(len);
if (rcvd != len)
throw new IOException("EOF reading unwanted data");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received UNWANTED data(" + piece + "," + begin + ") from " + peer);
}

View File

@ -143,7 +143,7 @@ class PeerConnectionOut implements Runnable
}
}
if (m == null && !sendQueue.isEmpty()) {
m = (Message)sendQueue.remove(0);
m = sendQueue.remove(0);
//SimpleTimer.getInstance().removeEvent(m.expireEvent);
}
}
@ -395,7 +395,7 @@ class PeerConnectionOut implements Runnable
while (it.hasNext())
{
Message m = (Message)it.next();
if (m.type == Message.REQUEST && m.piece == req.piece &&
if (m.type == Message.REQUEST && m.piece == req.getPiece() &&
m.begin == req.off && m.length == req.len)
{
if (_log.shouldLog(Log.DEBUG))
@ -406,7 +406,7 @@ class PeerConnectionOut implements Runnable
}
Message m = new Message();
m.type = Message.REQUEST;
m.piece = req.piece;
m.piece = req.getPiece();
m.begin = req.off;
m.length = req.len;
addMessage(m);
@ -492,7 +492,7 @@ class PeerConnectionOut implements Runnable
{
Message m = (Message)it.next();
if (m.type == Message.REQUEST
&& m.piece == req.piece
&& m.piece == req.getPiece()
&& m.begin == req.off
&& m.length == req.len)
it.remove();
@ -502,7 +502,7 @@ class PeerConnectionOut implements Runnable
// Always send, just to be sure it it is really canceled.
Message m = new Message();
m.type = Message.CANCEL;
m.piece = req.piece;
m.piece = req.getPiece();
m.begin = req.off;
m.length = req.len;
addMessage(m);

View File

@ -22,6 +22,7 @@ package org.klomp.snark;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -48,7 +49,7 @@ import org.klomp.snark.dht.DHT;
/**
* Coordinates what peer does what.
*/
public class PeerCoordinator implements PeerListener
class PeerCoordinator implements PeerListener
{
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerCoordinator.class);
@ -68,6 +69,7 @@ public class PeerCoordinator implements PeerListener
// package local for access by CheckDownLoadersTask
final static long CHECK_PERIOD = 40*1000; // 40 seconds
final static int MAX_UPLOADERS = 6;
public static final long MAX_INACTIVE = 8*60*1000;
/**
* Approximation of the number of current uploaders.
@ -116,15 +118,21 @@ public class PeerCoordinator implements PeerListener
*/
private final List<Piece> wantedPieces;
/** partial pieces - lock by synching on wantedPieces */
/** The total number of bytes in wantedPieces, or -1 if not yet known.
* Sync on wantedPieces.
* @since 0.9.1
*/
private long wantedBytes;
/** partial pieces - lock by synching on wantedPieces - TODO store Requests, not PartialPieces */
private final List<PartialPiece> partialPieces;
private boolean halted = false;
private volatile boolean halted;
private final MagnetState magnetState;
private final CoordinatorListener listener;
private final I2PSnarkUtil _util;
private static final Random _random = I2PAppContext.getGlobalContext().random();
private final Random _random;
/**
* @param metainfo null if in magnet mode
@ -134,6 +142,7 @@ public class PeerCoordinator implements PeerListener
CoordinatorListener listener, Snark torrent)
{
_util = util;
_random = util.getContext().random();
this.id = id;
this.infohash = infohash;
this.metainfo = metainfo;
@ -151,7 +160,7 @@ public class PeerCoordinator implements PeerListener
// Install a timer to check the uploaders.
// Randomize the first start time so multiple tasks are spread out,
// this will help the behavior with global limits
timer = new CheckEvent(new PeerCheckerTask(_util, this));
timer = new CheckEvent(_util.getContext(), new PeerCheckerTask(_util, this));
timer.schedule((CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD));
}
@ -161,8 +170,8 @@ public class PeerCoordinator implements PeerListener
*/
private static class CheckEvent extends SimpleTimer2.TimedEvent {
private final PeerCheckerTask _task;
public CheckEvent(PeerCheckerTask task) {
super(SimpleTimer2.getInstance());
public CheckEvent(I2PAppContext ctx, PeerCheckerTask task) {
super(ctx.simpleTimer2());
_task = task;
}
public void timeReached() {
@ -171,16 +180,22 @@ public class PeerCoordinator implements PeerListener
}
}
// only called externally from Storage after the double-check fails
/**
* Only called externally from Storage after the double-check fails.
* Sets wantedBytes too.
*/
public void setWantedPieces()
{
if (metainfo == null || storage == null)
if (metainfo == null || storage == null) {
wantedBytes = -1;
return;
}
// Make a list of pieces
synchronized(wantedPieces) {
wantedPieces.clear();
BitField bitfield = storage.getBitField();
int[] pri = storage.getPiecePriorities();
long count = 0;
for (int i = 0; i < metainfo.getPieces(); i++) {
// only add if we don't have and the priority is >= 0
if ((!bitfield.get(i)) &&
@ -189,8 +204,10 @@ public class PeerCoordinator implements PeerListener
if (pri != null)
p.setPriority(pri[i]);
wantedPieces.add(p);
count += metainfo.getPieceLength(i);
}
}
wantedBytes = count;
Collections.shuffle(wantedPieces, _random);
}
}
@ -233,7 +250,9 @@ public class PeerCoordinator implements PeerListener
}
/**
* Returns how many bytes are still needed to get the complete file.
* Bytes not yet in storage. Does NOT account for skipped files.
* Not exact (does not adjust for last piece size).
* Returns how many bytes are still needed to get the complete torrent.
* @return -1 if in magnet mode
*/
public long getLeft()
@ -244,6 +263,15 @@ public class PeerCoordinator implements PeerListener
return ((long) storage.needed()) * metainfo.getPieceLength(0);
}
/**
* Bytes still wanted. DOES account for skipped files.
* @return exact value. or -1 if no storage yet.
* @since 0.9.1
*/
public long getNeededLength() {
return wantedBytes;
}
/**
* Returns the total number of uploaded bytes of all peers.
*/
@ -330,14 +358,32 @@ public class PeerCoordinator implements PeerListener
return infohash;
}
/**
* Inbound.
* Not halted, peers < max.
* @since 0.9.1
*/
public boolean needPeers()
{
return !halted && peers.size() < getMaxConnections();
}
/**
* Outbound.
* Not halted, peers < max, and need pieces.
* @since 0.9.1
*/
public boolean needOutboundPeers() {
//return wantedBytes != 0 && needPeers();
// minus one to make it a little easier for new peers to get in on large swarms
return wantedBytes != 0 && !halted && peers.size() < getMaxConnections() - 1;
}
/**
* Reduce max if huge pieces to keep from ooming when leeching
* @return 512K: 16; 1M: 11; 2M: 6
* Formerly used to
* reduce max if huge pieces to keep from ooming when leeching
* but now we don't
* @return usually 16
*/
private int getMaxConnections() {
if (metainfo == null)
@ -347,13 +393,14 @@ public class PeerCoordinator implements PeerListener
return 4;
if (pieces <= 5)
return 6;
int size = metainfo.getPieceLength(0);
//int size = metainfo.getPieceLength(0);
int max = _util.getMaxConnections();
if (size <= 512*1024 || completed())
// Now that we use temp files, no memory concern
//if (size <= 512*1024 || completed())
return max;
if (size <= 1024*1024)
return (max + max + 2) / 3;
return (max + 2) / 3;
//if (size <= 1024*1024)
// return (max + max + 2) / 3;
//return (max + 2) / 3;
}
public boolean halted() { return halted; }
@ -380,10 +427,27 @@ public class PeerCoordinator implements PeerListener
}
// delete any saved orphan partial piece
synchronized (partialPieces) {
for (PartialPiece pp : partialPieces) {
pp.release();
}
partialPieces.clear();
}
}
/**
* @since 0.9.1
*/
public void restart() {
halted = false;
synchronized (uploaded_old) {
Arrays.fill(uploaded_old, 0);
}
synchronized (downloaded_old) {
Arrays.fill(downloaded_old, 0);
}
timer.schedule((CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD));
}
public void connected(Peer peer)
{
if (halted)
@ -396,7 +460,7 @@ public class PeerCoordinator implements PeerListener
synchronized(peers)
{
Peer old = peerIDInList(peer.getPeerID(), peers);
if ( (old != null) && (old.getInactiveTime() > 8*60*1000) ) {
if ( (old != null) && (old.getInactiveTime() > MAX_INACTIVE) ) {
// idle for 8 minutes, kill the old con (32KB/8min = 68B/sec minimum for one block)
if (_log.shouldLog(Log.WARN))
_log.warn("Remomving old peer: " + peer + ": " + old + ", inactive for " + old.getInactiveTime());
@ -468,7 +532,10 @@ public class PeerCoordinator implements PeerListener
return null;
}
// returns true if actual attempt to add peer occurs
/**
* Add peer (inbound or outbound)
* @return true if actual attempt to add peer occurs
*/
public boolean addPeer(final Peer peer)
{
if (halted)
@ -487,7 +554,7 @@ public class PeerCoordinator implements PeerListener
need_more = (!peer.isConnected()) && peersize < getMaxConnections();
// Check if we already have this peer before we build the connection
Peer old = peerIDInList(peer.getPeerID(), peers);
need_more = need_more && ((old == null) || (old.getInactiveTime() > 8*60*1000));
need_more = need_more && ((old == null) || (old.getInactiveTime() > MAX_INACTIVE));
}
if (need_more)
@ -630,22 +697,23 @@ public class PeerCoordinator implements PeerListener
* -1 if none of the given pieces are wanted.
*/
public int wantPiece(Peer peer, BitField havePieces) {
return wantPiece(peer, havePieces, true);
Piece pc = wantPiece(peer, havePieces, true);
return pc != null ? pc.getId() : -1;
}
/**
* Returns one of pieces in the given BitField that is still wanted or
* -1 if none of the given pieces are wanted.
* null if none of the given pieces are wanted.
*
* @param record if true, actually record in our data structures that we gave the
* request to this peer. If false, do not update the data structures.
* @since 0.8.2
*/
private int wantPiece(Peer peer, BitField havePieces, boolean record) {
private Piece wantPiece(Peer peer, BitField havePieces, boolean record) {
if (halted) {
if (_log.shouldLog(Log.WARN))
_log.warn("We don't want anything from the peer, as we are halted! peer=" + peer);
return -1;
return null;
}
Piece piece = null;
@ -680,7 +748,7 @@ public class PeerCoordinator implements PeerListener
// If we do end game all the time, we generate lots of extra traffic
// when the seeder is super-slow and all the peers are "caught up"
if (wantedSize > END_GAME_THRESHOLD)
return -1; // nothing to request and not in end game
return null; // nothing to request and not in end game
// let's not all get on the same piece
// Even better would be to sort by number of requests
if (record)
@ -704,7 +772,7 @@ public class PeerCoordinator implements PeerListener
_log.warn("nothing to even rerequest from " + peer + ": requested = " + requested);
// _log.warn("nothing to even rerequest from " + peer + ": requested = " + requested
// + " wanted = " + wantedPieces + " peerHas = " + havePieces);
return -1; //If we still can't find a piece we want, so be it.
return null; //If we still can't find a piece we want, so be it.
} else {
// Should be a lot smarter here -
// share blocks rather than starting from 0 with each peer.
@ -719,7 +787,7 @@ public class PeerCoordinator implements PeerListener
_log.info(peer + " is now requesting: piece " + piece + " priority " + piece.getPriority());
piece.setRequested(peer, true);
}
return piece.getId();
return piece;
} // synch
}
@ -736,6 +804,7 @@ public class PeerCoordinator implements PeerListener
_log.debug("Updated piece priorities called but no priorities to set?");
return;
}
List<Piece> toCancel = new ArrayList();
synchronized(wantedPieces) {
// Add incomplete and previously unwanted pieces to the list
// Temp to avoid O(n**2)
@ -749,6 +818,7 @@ public class PeerCoordinator implements PeerListener
if (!want.get(i)) {
Piece piece = new Piece(i);
wantedPieces.add(piece);
wantedBytes += metainfo.getPieceLength(i);
// As connections are already up, new Pieces will
// not have their PeerID list populated, so do that.
for (Peer p : peers) {
@ -770,23 +840,32 @@ public class PeerCoordinator implements PeerListener
p.setPriority(priority);
} else {
iter.remove();
// cancel all peers
for (Peer peer : peers) {
peer.cancel(p.getId());
}
toCancel.add(p);
wantedBytes -= metainfo.getPieceLength(p.getId());
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Updated piece priorities, now wanted: " + wantedPieces);
// if we added pieces, they will be in-order unless we shuffle
Collections.shuffle(wantedPieces, _random);
}
// update request queues, in case we added wanted pieces
// and we were previously uninterested
for (Peer peer : peers) {
peer.request();
// cancel outside of wantedPieces lock to avoid deadlocks
if (!toCancel.isEmpty()) {
// cancel all peers
for (Peer peer : peers) {
for (Piece p : toCancel) {
peer.cancel(p.getId());
}
}
}
// ditto, avoid deadlocks
// update request queues, in case we added wanted pieces
// and we were previously uninterested
for (Peer peer : peers) {
peer.request();
}
}
/**
@ -811,8 +890,10 @@ public class PeerCoordinator implements PeerListener
snark.stopTorrent();
String msg = "Error reading the storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
_log.error(msg, ioe);
SnarkManager.instance().addMessage(msg);
SnarkManager.instance().addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
if (listener != null) {
listener.addMessage(msg);
listener.addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
}
throw new RuntimeException(msg, ioe);
}
}
@ -846,10 +927,11 @@ public class PeerCoordinator implements PeerListener
*
* @throws RuntimeException on IOE saving the piece
*/
public boolean gotPiece(Peer peer, int piece, byte[] bs)
public boolean gotPiece(Peer peer, PartialPiece pp)
{
if (metainfo == null || storage == null)
return true;
int piece = pp.getPiece();
if (halted) {
_log.info("Got while-halted piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
return true; // We don't actually care anymore.
@ -872,7 +954,7 @@ public class PeerCoordinator implements PeerListener
try
{
if (storage.putPiece(piece, bs))
if (storage.putPiece(pp))
{
if (_log.shouldLog(Log.INFO))
_log.info("Got valid piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
@ -890,38 +972,51 @@ public class PeerCoordinator implements PeerListener
snark.stopTorrent();
String msg = "Error writing storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
_log.error(msg, ioe);
SnarkManager.instance().addMessage(msg);
SnarkManager.instance().addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
if (listener != null) {
listener.addMessage(msg);
listener.addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
}
throw new RuntimeException(msg, ioe);
}
wantedPieces.remove(p);
wantedBytes -= metainfo.getPieceLength(p.getId());
}
// just in case
removePartialPiece(piece);
boolean done = wantedBytes <= 0;
// Announce to the world we have it!
// Disconnect from other seeders when we get the last piece
List<Peer> toDisconnect = new ArrayList();
Iterator<Peer> it = peers.iterator();
while (it.hasNext())
{
Peer p = it.next();
List<Peer> toDisconnect = done ? new ArrayList() : null;
for (Peer p : peers) {
if (p.isConnected())
{
if (completed() && p.isCompleted())
if (done && p.isCompleted())
toDisconnect.add(p);
else
p.have(piece);
}
}
it = toDisconnect.iterator();
while (it.hasNext())
{
Peer p = it.next();
}
if (done) {
for (Peer p : toDisconnect) {
p.disconnect(true);
}
}
// put msg on the console if partial, since Storage won't do it
if (!completed())
snark.storageCompleted(storage);
synchronized (partialPieces) {
for (PartialPiece ppp : partialPieces) {
ppp.release();
}
partialPieces.clear();
}
}
return true;
}
@ -998,17 +1093,24 @@ public class PeerCoordinator implements PeerListener
* Also mark the piece unrequested if this peer was the only one.
*
* @param peer partials, must include the zero-offset (empty) ones too
* No dup pieces, piece.setDownloaded() must be set
* @since 0.8.2
*/
public void savePartialPieces(Peer peer, List<PartialPiece> partials)
public void savePartialPieces(Peer peer, List<Request> partials)
{
if (halted)
return;
if (_log.shouldLog(Log.INFO))
_log.info("Partials received from " + peer + ": " + partials);
if (halted || completed()) {
for (Request req : partials) {
PartialPiece pp = req.getPartialPiece();
pp.release();
}
return;
}
synchronized(wantedPieces) {
for (PartialPiece pp : partials) {
if (pp.getDownloaded() > 0) {
for (Request req : partials) {
PartialPiece pp = req.getPartialPiece();
if (req.off > 0) {
// PartialPiece.equals() only compares piece number, which is what we want
int idx = partialPieces.indexOf(pp);
if (idx < 0) {
@ -1017,10 +1119,12 @@ public class PeerCoordinator implements PeerListener
_log.info("Saving orphaned partial piece (new) " + pp);
} else if (idx >= 0 && pp.getDownloaded() > partialPieces.get(idx).getDownloaded()) {
// replace what's there now
partialPieces.get(idx).release();
partialPieces.set(idx, pp);
if (_log.shouldLog(Log.INFO))
_log.info("Saving orphaned partial piece (bigger) " + pp);
} else {
pp.release();
if (_log.shouldLog(Log.INFO))
_log.info("Discarding partial piece (not bigger)" + pp);
}
@ -1029,10 +1133,14 @@ public class PeerCoordinator implements PeerListener
// sorts by remaining bytes, least first
Collections.sort(partialPieces);
PartialPiece gone = partialPieces.remove(max);
gone.release();
if (_log.shouldLog(Log.INFO))
_log.info("Discarding orphaned partial piece (list full)" + gone);
}
} // else drop the empty partial piece
} else {
// drop the empty partial piece
pp.release();
}
// synchs on wantedPieces...
markUnrequested(peer, pp.getPiece());
}
@ -1058,10 +1166,18 @@ public class PeerCoordinator implements PeerListener
PartialPiece pp = iter.next();
int savedPiece = pp.getPiece();
if (havePieces.get(savedPiece)) {
iter.remove();
// this is just a double-check, it should be in there
boolean skipped = false;
for(Piece piece : wantedPieces) {
if (piece.getId() == savedPiece) {
if (peer.isCompleted() && piece.getPeerCount() > 1) {
// Try to preserve rarest-first when we have only one seeder
// by not preferring a partial piece that others have too
// from a seeder
skipped = true;
break;
}
iter.remove();
piece.setRequested(peer, true);
if (_log.shouldLog(Log.INFO)) {
_log.info("Restoring orphaned partial piece " + pp +
@ -1070,8 +1186,12 @@ public class PeerCoordinator implements PeerListener
return pp;
}
}
if (_log.shouldLog(Log.WARN))
_log.warn("Partial piece " + pp + " NOT in wantedPieces??");
if (_log.shouldLog(Log.WARN)) {
if (skipped)
_log.warn("Partial piece " + pp + " with multiple peers skipped for seeder");
else
_log.warn("Partial piece " + pp + " NOT in wantedPieces??");
}
}
}
if (_log.shouldLog(Log.WARN) && !partialPieces.isEmpty())
@ -1079,14 +1199,9 @@ public class PeerCoordinator implements PeerListener
}
// ...and this section turns this into the general move-requests-around code!
// Temporary? So PeerState never calls wantPiece() directly for now...
int piece = wantPiece(peer, havePieces);
if (piece >= 0) {
try {
return new PartialPiece(piece, metainfo.getPieceLength(piece));
} catch (OutOfMemoryError oom) {
if (_log.shouldLog(Log.WARN))
_log.warn("OOM creating new partial piece");
}
Piece piece = wantPiece(peer, havePieces, true);
if (piece != null) {
return new PartialPiece(piece, metainfo.getPieceLength(piece.getId()), _util.getTempDir());
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("We have no partial piece to return");
@ -1121,7 +1236,7 @@ public class PeerCoordinator implements PeerListener
}
}
}
return wantPiece(peer, havePieces, false) >= 0;
return wantPiece(peer, havePieces, false) != null;
}
/**
@ -1176,6 +1291,7 @@ public class PeerCoordinator implements PeerListener
}
} else if (id == ExtensionHandler.ID_HANDSHAKE) {
sendPeers(peer);
sendDHT(peer);
}
}
@ -1186,6 +1302,8 @@ public class PeerCoordinator implements PeerListener
* @since 0.8.4
*/
void sendPeers(Peer peer) {
if (metainfo != null && metainfo.isPrivate())
return;
Map<String, BEValue> handshake = peer.getHandshakeMap();
if (handshake == null)
return;
@ -1202,6 +1320,26 @@ public class PeerCoordinator implements PeerListener
} catch (InvalidBEncodingException ibee) {}
}
/**
* Send a DHT message to the peer, if we both support DHT.
* @since DHT
*/
void sendDHT(Peer peer) {
DHT dht = _util.getDHT();
if (dht == null)
return;
Map<String, BEValue> handshake = peer.getHandshakeMap();
if (handshake == null)
return;
BEValue bev = handshake.get("m");
if (bev == null)
return;
try {
if (bev.getMap().get(ExtensionHandler.TYPE_DHT) != null)
ExtensionHandler.sendDHT(peer, dht.getPort(), dht.getRPort());
} catch (InvalidBEncodingException ibee) {}
}
/**
* Sets the storage after transition out of magnet mode
* Snark calls this after we call gotMetaInfo()
@ -1219,20 +1357,23 @@ public class PeerCoordinator implements PeerListener
/**
* PeerListener callback
* Tell the DHT to ping it, this will get back the node info
* @param rport must be port + 1
* @since 0.8.4
*/
public void gotPort(Peer peer, int port) {
public void gotPort(Peer peer, int port, int rport) {
DHT dht = _util.getDHT();
if (dht != null)
if (dht != null &&
port > 0 && port < 65535 && rport == port + 1)
dht.ping(peer.getDestination(), port);
}
/**
* Get peers from PEX -
* PeerListener callback
* @since 0.8.4
*/
public void gotPeers(Peer peer, List<PeerID> peers) {
if (completed() || !needPeers())
if (!needOutboundPeers())
return;
Destination myDest = _util.getMyDestination();
if (myDest == null)
@ -1294,5 +1435,13 @@ public class PeerCoordinator implements PeerListener
return listener.overUpBWLimit(total * 1000 / CHECK_PERIOD);
return false;
}
/**
* Convenience
* @since 0.9.2
*/
public I2PSnarkUtil getUtil() {
return _util;
}
}

View File

@ -1,9 +1,10 @@
package org.klomp.snark;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.SHA1Hash;
/**
* Hmm, any guesses as to what this is? Used by the multitorrent functionality
@ -12,26 +13,28 @@ import java.util.Set;
* from it there too)
*/
public class PeerCoordinatorSet {
private final Set _coordinators;
private final Map<SHA1Hash, PeerCoordinator> _coordinators;
public PeerCoordinatorSet() {
_coordinators = new HashSet();
_coordinators = new ConcurrentHashMap();
}
public Iterator iterator() {
synchronized (_coordinators) {
return new ArrayList(_coordinators).iterator();
}
public Iterator<PeerCoordinator> iterator() {
return _coordinators.values().iterator();
}
public void add(PeerCoordinator coordinator) {
synchronized (_coordinators) {
_coordinators.add(coordinator);
}
_coordinators.put(new SHA1Hash(coordinator.getInfoHash()), coordinator);
}
public void remove(PeerCoordinator coordinator) {
synchronized (_coordinators) {
_coordinators.remove(coordinator);
}
_coordinators.remove(new SHA1Hash(coordinator.getInfoHash()));
}
/**
* @since 0.9.2
*/
public PeerCoordinator get(byte[] infoHash) {
return _coordinators.get(new SHA1Hash(infoHash));
}
}

View File

@ -52,6 +52,7 @@ public class PeerID implements Comparable
/** whether we have tried to get the dest from the hash - only do once */
private boolean triedDestLookup;
private final int hash;
private final I2PSnarkUtil util;
public PeerID(byte[] id, Destination address)
{
@ -60,6 +61,7 @@ public class PeerID implements Comparable
this.port = 6881;
this.destHash = address.calculateHash().getData();
hash = calculateHash();
util = null;
}
/**
@ -93,13 +95,15 @@ public class PeerID implements Comparable
port = 6881;
this.destHash = address.calculateHash().getData();
hash = calculateHash();
util = null;
}
/**
* Creates a PeerID from a destHash
* @param util for eventual destination lookup
* @since 0.8.1
*/
public PeerID(byte[] dest_hash) throws InvalidBEncodingException
public PeerID(byte[] dest_hash, I2PSnarkUtil util) throws InvalidBEncodingException
{
// id and address remain null
port = 6881;
@ -107,6 +111,7 @@ public class PeerID implements Comparable
throw new InvalidBEncodingException("bad hash length");
destHash = dest_hash;
hash = DataHelper.hashCode(dest_hash);
this.util = util;
}
public byte[] getID()
@ -131,7 +136,7 @@ public class PeerID implements Comparable
{
if (address == null && destHash != null && !triedDestLookup) {
String b32 = Base32.encode(destHash) + ".b32.i2p";
address = I2PAppContext.getGlobalContext().namingService().lookup(b32);
address = util.getDestination(b32);
triedDestLookup = true;
}
return address;

View File

@ -95,12 +95,11 @@ interface PeerListener
* will be closed.
*
* @param peer the Peer that got the piece.
* @param piece the piece number received.
* @param bs the byte array containing the piece.
* @param piece the piece received.
*
* @return true when the bytes represent the piece, false otherwise.
*/
boolean gotPiece(Peer peer, int piece, byte[] bs);
boolean gotPiece(Peer peer, PartialPiece piece);
/**
* Called when the peer wants (part of) a piece from us. Only called
@ -167,7 +166,7 @@ interface PeerListener
* @param peer the peer
* @since 0.8.2
*/
void savePartialPieces(Peer peer, List<PartialPiece> pcs);
void savePartialPieces(Peer peer, List<Request> pcs);
/**
* Called when a peer has connected and there may be a partially
@ -191,13 +190,14 @@ interface PeerListener
void gotExtension(Peer peer, int id, byte[] bs);
/**
* Called when a port message is received.
* Called when a DHT port message is received.
*
* @param peer the Peer that got the message.
* @param port the port
* @param port the query port
* @param rport the response port
* @since 0.8.4
*/
void gotPort(Peer peer, int port);
void gotPort(Peer peer, int port, int rport);
/**
* Called when peers are received via PEX
@ -207,4 +207,10 @@ interface PeerListener
* @since 0.8.4
*/
void gotPeers(Peer peer, List<PeerID> pIDList);
/**
* Convenience
* @since 0.9.2
*/
public I2PSnarkUtil getUtil();
}

View File

@ -20,13 +20,10 @@
package org.klomp.snark;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.i2p.I2PAppContext;
@ -42,13 +39,13 @@ class PeerState implements DataLoader
// Interesting and choking describes whether we are interested in or
// are choking the other side.
boolean interesting = false;
boolean choking = true;
volatile boolean interesting;
volatile boolean choking = true;
// Interested and choked describes whether the other side is
// interested in us or choked us.
boolean interested = false;
boolean choked = true;
volatile boolean interested;
volatile boolean choked = true;
/** the pieces the peer has */
BitField bitfield;
@ -110,7 +107,7 @@ class PeerState implements DataLoader
// The only problem with returning the partials to the coordinator
// is that chunks above a missing request are lost.
// Future enhancements to PartialPiece could keep track of the holes.
List<PartialPiece> pcs = returnPartialPieces();
List<Request> pcs = returnPartialPieces();
if (!pcs.isEmpty()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " got choked, returning partial pieces to the PeerCoordinator: " + pcs);
@ -234,8 +231,8 @@ class PeerState implements DataLoader
return;
}
if (_log.shouldLog(Log.INFO))
_log.info("Queueing (" + piece + ", " + begin + ", "
if (_log.shouldLog(Log.DEBUG))
_log.debug("Queueing (" + piece + ", " + begin + ", "
+ length + ")" + " to " + peer);
// don't load the data into mem now, let PeerConnectionOut do it
@ -270,8 +267,8 @@ class PeerState implements DataLoader
return null;
}
if (_log.shouldLog(Log.INFO))
_log.info("Sending (" + piece + ", " + begin + ", "
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending (" + piece + ", " + begin + ", "
+ length + ")" + " to " + peer);
return pieceBytes;
}
@ -307,22 +304,23 @@ class PeerState implements DataLoader
if (_log.shouldLog(Log.DEBUG))
_log.debug("got end of Chunk("
+ req.piece + "," + req.off + "," + req.len + ") from "
+ req.getPiece() + "," + req.off + "," + req.len + ") from "
+ peer);
// Last chunk needed for this piece?
if (getFirstOutstandingRequest(req.piece) == -1)
// FIXME if priority changed to skip, we will think we're done when we aren't
if (getFirstOutstandingRequest(req.getPiece()) == -1)
{
// warning - may block here for a while
if (listener.gotPiece(peer, req.piece, req.bs))
if (listener.gotPiece(peer, req.getPartialPiece()))
{
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got " + req.piece + ": " + peer);
_log.debug("Got " + req.getPiece() + ": " + peer);
}
else
{
if (_log.shouldLog(Log.WARN))
_log.warn("Got BAD " + req.piece + " from " + peer);
_log.warn("Got BAD " + req.getPiece() + " from " + peer);
}
}
@ -338,7 +336,7 @@ class PeerState implements DataLoader
synchronized private int getFirstOutstandingRequest(int piece)
{
for (int i = 0; i < outstandingRequests.size(); i++)
if (outstandingRequests.get(i).piece == piece)
if (outstandingRequests.get(i).getPiece() == piece)
return i;
return -1;
}
@ -374,7 +372,7 @@ class PeerState implements DataLoader
synchronized(this)
{
req = outstandingRequests.get(r);
while (req.piece == piece && req.off != begin
while (req.getPiece() == piece && req.off != begin
&& r < outstandingRequests.size() - 1)
{
r++;
@ -382,7 +380,7 @@ class PeerState implements DataLoader
}
// Something wrong?
if (req.piece != piece || req.off != begin || req.len != length)
if (req.getPiece() != piece || req.off != begin || req.len != length)
{
if (_log.shouldLog(Log.INFO))
_log.info("Unrequested or unneeded 'piece: "
@ -430,13 +428,13 @@ class PeerState implements DataLoader
Request rv = null;
int lowest = Integer.MAX_VALUE;
for (Request r : outstandingRequests) {
if (r.piece == piece && r.off < lowest) {
if (r.getPiece() == piece && r.off < lowest) {
lowest = r.off;
rv = r;
}
}
if (pendingRequest != null &&
pendingRequest.piece == piece && pendingRequest.off < lowest)
pendingRequest.getPiece() == piece && pendingRequest.off < lowest)
rv = pendingRequest;
if (_log.shouldLog(Log.DEBUG))
@ -450,14 +448,16 @@ class PeerState implements DataLoader
* @return List of PartialPieces, even those with an offset == 0, or empty list
* @since 0.8.2
*/
synchronized List<PartialPiece> returnPartialPieces()
synchronized List<Request> returnPartialPieces()
{
Set<Integer> pcs = getRequestedPieces();
List<PartialPiece> rv = new ArrayList(pcs.size());
List<Request> rv = new ArrayList(pcs.size());
for (Integer p : pcs) {
Request req = getLowestOutstandingRequest(p.intValue());
if (req != null)
rv.add(new PartialPiece(req));
if (req != null) {
req.getPartialPiece().setDownloaded(req.off);
rv.add(req);
}
}
outstandingRequests.clear();
pendingRequest = null;
@ -471,9 +471,9 @@ class PeerState implements DataLoader
synchronized private Set<Integer> getRequestedPieces() {
Set<Integer> rv = new HashSet(outstandingRequests.size() + 1);
for (Request req : outstandingRequests) {
rv.add(Integer.valueOf(req.piece));
rv.add(Integer.valueOf(req.getPiece()));
if (pendingRequest != null)
rv.add(Integer.valueOf(pendingRequest.piece));
rv.add(Integer.valueOf(pendingRequest.getPiece()));
}
return rv;
}
@ -489,6 +489,13 @@ class PeerState implements DataLoader
/** @since 0.8.2 */
void extensionMessage(int id, byte[] bs)
{
if (metainfo != null && metainfo.isPrivate() &&
(id == ExtensionHandler.ID_METADATA || id == ExtensionHandler.ID_PEX)) {
// shouldn't get this since we didn't advertise it but they could send it anyway
if (_log.shouldLog(Log.WARN))
_log.warn("Private torrent, ignoring ext msg " + id);
return;
}
ExtensionHandler.handleMessage(peer, listener, id, bs);
// Peer coord will get metadata from MagnetState,
// verify, and then call gotMetaInfo()
@ -519,10 +526,14 @@ class PeerState implements DataLoader
setInteresting(true);
}
/** @since 0.8.4 */
/**
* Unused
* @since 0.8.4
*/
void portMessage(int port)
{
listener.gotPort(peer, port);
// for compatibility with old DHT PORT message
listener.gotPort(peer, port, port + 1);
}
void unknownMessage(int type, byte[] bs)
@ -567,14 +578,14 @@ class PeerState implements DataLoader
* @since 0.8.1
*/
synchronized void cancelPiece(int piece) {
if (lastRequest != null && lastRequest.piece == piece)
if (lastRequest != null && lastRequest.getPiece() == piece)
lastRequest = null;
Iterator<Request> it = outstandingRequests.iterator();
while (it.hasNext())
{
Request req = it.next();
if (req.piece == piece)
if (req.getPiece() == piece)
{
it.remove();
// Send cancel even when we are choked to make sure that it is
@ -590,10 +601,10 @@ class PeerState implements DataLoader
* @since 0.8.1
*/
synchronized boolean isRequesting(int piece) {
if (pendingRequest != null && pendingRequest.piece == piece)
if (pendingRequest != null && pendingRequest.getPiece() == piece)
return true;
for (Request req : outstandingRequests) {
if (req.piece == piece)
if (req.getPiece() == piece)
return true;
}
return false;
@ -675,7 +686,7 @@ class PeerState implements DataLoader
{
int pieceLength;
boolean isLastChunk;
pieceLength = metainfo.getPieceLength(lastRequest.piece);
pieceLength = metainfo.getPieceLength(lastRequest.getPiece());
isLastChunk = lastRequest.off + lastRequest.len == pieceLength;
// Last part of a piece?
@ -683,14 +694,13 @@ class PeerState implements DataLoader
more_pieces = requestNextPiece();
else
{
int nextPiece = lastRequest.piece;
PartialPiece nextPiece = lastRequest.getPartialPiece();
int nextBegin = lastRequest.off + PARTSIZE;
byte[] bs = lastRequest.bs;
int maxLength = pieceLength - nextBegin;
int nextLength = maxLength > PARTSIZE ? PARTSIZE
: maxLength;
Request req
= new Request(nextPiece, bs, nextBegin, nextLength);
= new Request(nextPiece,nextBegin, nextLength);
outstandingRequests.add(req);
if (!choked)
out.sendRequest(req);
@ -736,7 +746,7 @@ class PeerState implements DataLoader
// what piece to give us next.
int nextPiece = listener.wantPiece(peer, bitfield);
if (nextPiece != -1
&& (lastRequest == null || lastRequest.piece != nextPiece)) {
&& (lastRequest == null || lastRequest.getPiece() != nextPiece)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " want piece " + nextPiece);
// Fail safe to make sure we are interested

View File

@ -57,6 +57,15 @@ class Piece implements Comparable {
/** caller must synchronize */
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
/**
* How many peers have this piece?
* Caller must synchronize
* @since 0.9.1
*/
public int getPeerCount() {
return this.peers.size();
}
/** caller must synchronize */
public boolean isRequested() {
return this.requests != null && !this.requests.isEmpty();

View File

@ -20,14 +20,16 @@
package org.klomp.snark;
import java.io.DataInputStream;
import java.io.IOException;
/**
* Holds all information needed for a partial piece request.
* This class should be used only by PeerState, PeerConnectionIn, and PeerConnectionOut.
*/
class Request
{
final int piece;
final byte[] bs;
private final PartialPiece piece;
final int off;
final int len;
long sendTime;
@ -36,26 +38,49 @@ class Request
* Creates a new Request.
*
* @param piece Piece number requested.
* @param bs byte array where response should be stored.
* @param off the offset in the array.
* @param len the number of bytes requested.
*/
Request(int piece, byte[] bs, int off, int len)
Request(PartialPiece piece, int off, int len)
{
// Sanity check
if (off < 0 || len <= 0 || off + len > piece.getLength())
throw new IndexOutOfBoundsException("Illegal Request " + toString());
this.piece = piece;
this.bs = bs;
this.off = off;
this.len = len;
}
// Sanity check
if (piece < 0 || off < 0 || len <= 0 || off + len > bs.length)
throw new IndexOutOfBoundsException("Illegal Request " + toString());
/**
* @since 0.9.1
*/
public void read(DataInputStream din) throws IOException {
piece.read(din, off, len);
}
/**
* The piece number this Request is for
*
* @since 0.9.1
*/
public int getPiece() {
return piece.getPiece();
}
/**
* The PartialPiece this Request is for
*
* @since 0.9.1
*/
public PartialPiece getPartialPiece() {
return piece;
}
@Override
public int hashCode()
{
return piece ^ off ^ len;
return piece.getPiece() ^ off ^ len;
}
@Override
@ -64,7 +89,7 @@ class Request
if (o instanceof Request)
{
Request req = (Request)o;
return req.piece == piece && req.off == off && req.len == len;
return req.piece.equals(piece) && req.off == off && req.len == len;
}
return false;
@ -73,6 +98,6 @@ class Request
@Override
public String toString()
{
return "(" + piece + "," + off + "," + len + ")";
return "(" + piece.getPiece() + "," + off + "," + len + ")";
}
}

View File

@ -20,12 +20,10 @@
package org.klomp.snark;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -37,6 +35,7 @@ import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.data.Destination;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Main Snark program startup class.
@ -49,34 +48,12 @@ public class Snark
private final static int MIN_PORT = 6881;
private final static int MAX_PORT = 6889;
// Error messages (non-fatal)
public final static int ERROR = 1;
// Warning messages
public final static int WARNING = 2;
// Notices (peer level)
public final static int NOTICE = 3;
// Info messages (protocol policy level)
public final static int INFO = 4;
// Debug info (protocol level)
public final static int DEBUG = 5;
// Very low level stuff (network level)
public final static int ALL = 6;
/**
* What level of debug info to show.
*/
//public static int debug = NOTICE;
// Whether or not to ask the user for commands while sharing
private static boolean command_interpreter = true;
//private static boolean command_interpreter = true;
private static final String newline = System.getProperty("line.separator");
/****
private static final String copyright =
"The Hunting of the Snark Project - Copyright (C) 2003 Mark J. Wielaard"
+ newline + newline
@ -92,10 +69,12 @@ public class Snark
"Press return for help. Type \"quit\" and return to stop.";
private static final String help =
"Commands: 'info', 'list', 'quit'.";
****/
// String indicating main activity
String activity = "Not started";
/****
private static class OOMListener implements I2PThread.OOMEventListener {
public void outOfMemory(OutOfMemoryError err) {
try {
@ -108,6 +87,7 @@ public class Snark
}
}
****/
/******** No, not maintaining a command-line client
@ -247,11 +227,13 @@ public class Snark
private TrackerClient trackerclient;
private String rootDataDir = ".";
private final CompleteListener completeListener;
private boolean stopped;
private volatile boolean stopped;
private volatile boolean starting;
private byte[] id;
private byte[] infoHash;
private final byte[] infoHash;
private String additionalTrackerURL;
private final I2PSnarkUtil _util;
private final Log _log;
private final PeerCoordinatorSet _peerCoordinatorSet;
private String trackerProblems;
private int trackerSeenPeers;
@ -305,6 +287,7 @@ public class Snark
completeListener = complistener;
_util = util;
_log = util.getContext().logManager().getLog(Snark.class);
_peerCoordinatorSet = peerCoordinatorSet;
acceptor = connectionAcceptor;
@ -315,10 +298,9 @@ public class Snark
activity = "Network setup";
id = generateID();
debug("My peer id: " + PeerID.idencode(id), Snark.INFO);
if (_log.shouldLog(Log.INFO))
_log.info("My peer id: " + PeerID.idencode(id));
int port;
IOException lastException = null;
/*
* Don't start a tunnel if the torrent isn't going to be started.
* If we are starting,
@ -339,6 +321,7 @@ public class Snark
meta = null;
File f = null;
InputStream in = null;
byte[] x_infoHash = null;
try
{
f = new File(torrent);
@ -346,6 +329,8 @@ public class Snark
in = new FileInputStream(f);
else
{
/**** No, we don't ever fetch a torrent file this way
and we don't want to block in the constructor
activity = "Getting torrent";
File torrentFile = _util.get(torrent, 3);
if (torrentFile == null) {
@ -355,9 +340,11 @@ public class Snark
torrentFile.deleteOnExit();
in = new FileInputStream(torrentFile);
}
*****/
throw new IOException("not found");
}
meta = new MetaInfo(in);
infoHash = meta.getInfoHash();
x_infoHash = meta.getInfoHash();
}
catch(IOException ioe)
{
@ -398,7 +385,9 @@ public class Snark
try { in.close(); } catch (IOException ioe) {}
}
debug(meta.toString(), INFO);
infoHash = x_infoHash; // final
if (_log.shouldLog(Log.INFO))
_log.info(meta.toString());
// When the metainfo torrent was created from an existing file/dir
// it already exists.
@ -459,6 +448,7 @@ public class Snark
{
completeListener = complistener;
_util = util;
_log = util.getContext().logManager().getLog(Snark.class);
_peerCoordinatorSet = peerCoordinatorSet;
acceptor = connectionAcceptor;
this.torrent = torrent;
@ -505,9 +495,19 @@ public class Snark
}
/**
* Start up contacting peers and querying the tracker
* Start up contacting peers and querying the tracker.
* Blocks if tunnel is not yet open.
*/
public void startTorrent() {
public synchronized void startTorrent() {
starting = true;
try {
x_startTorrent();
} finally {
starting = false;
}
}
private void x_startTorrent() {
boolean ok = _util.connect();
if (!ok) fatal("Unable to connect to I2P");
if (coordinator == null) {
@ -516,9 +516,11 @@ public class Snark
fatal("Unable to listen for I2P connections");
else {
Destination d = serversocket.getManager().getSession().getMyDestination();
debug("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64(), NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Listening on I2P destination " + d.toBase64() + " / " + d.calculateHash().toBase64());
}
debug("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient", NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient");
activity = "Collecting pieces";
coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
if (_peerCoordinatorSet != null) {
@ -538,21 +540,14 @@ public class Snark
}
stopped = false;
boolean coordinatorChanged = false;
if (coordinator.halted()) {
// ok, we have already started and stopped, but the coordinator seems a bit annoying to
// restart safely, so lets build a new one to replace the old
coordinator.restart();
if (_peerCoordinatorSet != null)
_peerCoordinatorSet.remove(coordinator);
PeerCoordinator newCoord = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
if (_peerCoordinatorSet != null)
_peerCoordinatorSet.add(newCoord);
coordinator = newCoord;
coordinatorChanged = true;
_peerCoordinatorSet.add(coordinator);
}
if (!trackerclient.started() && !coordinatorChanged) {
if (!trackerclient.started()) {
trackerclient.start();
} else if (trackerclient.halted() || coordinatorChanged) {
} else if (trackerclient.halted()) {
if (storage != null) {
try {
storage.reopen(rootDataDir);
@ -563,23 +558,30 @@ public class Snark
fatal("Could not reopen storage", ioe);
}
}
TrackerClient newClient = new TrackerClient(_util, meta, additionalTrackerURL, coordinator, this);
if (!trackerclient.halted())
trackerclient.halt();
trackerclient = newClient;
trackerclient.start();
} else {
debug("NOT starting TrackerClient???", NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("NOT starting TrackerClient???");
}
}
/**
* Stop contacting the tracker and talking with peers
*/
public void stopTorrent() {
stopTorrent(false);
}
/**
* Stop contacting the tracker and talking with peers
* @param fast if true, limit the life of the unannounce threads
* @since 0.9.1
*/
public synchronized void stopTorrent(boolean fast) {
stopped = true;
TrackerClient tc = trackerclient;
if (tc != null)
tc.halt();
tc.halt(fast);
PeerCoordinator pc = coordinator;
if (pc != null)
pc.halt();
@ -601,10 +603,12 @@ public class Snark
_util.disconnect();
}
/****
private static Snark parseArguments(String[] args)
{
return parseArguments(args, null, null);
}
****/
// Accessors
@ -668,6 +672,22 @@ public class Snark
return stopped;
}
/**
* Startup in progress.
* @since 0.9.1
*/
public boolean isStarting() {
return starting && stopped;
}
/**
* Set startup in progress.
* @since 0.9.1
*/
public void setStarting() {
starting = true;
}
/**
* @since 0.8.4
*/
@ -780,6 +800,7 @@ public class Snark
}
/**
* Bytes not yet in storage. Does NOT account for skipped files.
* @return exact value. or -1 if no storage yet.
* getNeeded() * pieceLength(0) isn't accurate if last piece
* is still needed.
@ -800,6 +821,20 @@ public class Snark
}
/**
* Bytes still wanted. DOES account for skipped files.
* FIXME -1 when not running.
* @return exact value. or -1 if no storage yet or when not running.
* @since 0.9.1
*/
public long getNeededLength() {
PeerCoordinator coord = coordinator;
if (coord != null)
return coord.getNeededLength();
return -1;
}
/**
* Does not account for skipped files.
* @return number of pieces still needed (magnet mode or not), or -1 if unknown
* @since 0.8.4
*/
@ -911,7 +946,7 @@ public class Snark
}
else if (args[i].equals("--no-commands"))
{
command_interpreter = false;
//command_interpreter = false;
i++;
}
//else if (args[i].equals("--eepproxy"))
@ -970,22 +1005,13 @@ public class Snark
private static void usage()
{
System.out.println
("Usage: snark [--debug [level]] [--no-commands] [--port <port>]");
("Usage: snark [--no-commands] [--port <port>]");
System.out.println
(" [--eepproxy hostname portnum]");
System.out.println
(" [--i2cp routerHost routerPort ['name=val name=val name=val']]");
System.out.println
(" (<url>|<file>)");
System.out.println
(" --debug\tShows some extra info and stacktraces");
System.out.println
(" level\tHow much debug details to show");
System.out.println
(" \t(defaults to "
+ NOTICE + ", with --debug to "
+ INFO + ", highest level is "
+ ALL + ").");
System.out.println
(" --no-commands\tDon't read interactive commands or show usage info.");
System.out.println
@ -1024,22 +1050,18 @@ public class Snark
*/
private void fatal(String s, Throwable t)
{
_util.debug(s, ERROR, t);
_log.error(s, t);
//System.err.println("snark: " + s + ((t == null) ? "" : (": " + t)));
//if (debug >= INFO && t != null)
// t.printStackTrace();
stopTorrent();
if (t != null)
s += ": " + t;
if (completeListener != null)
completeListener.fatal(this, s);
throw new RuntimeException(s, t);
}
/**
* Show debug info if debug is true.
*/
private void debug(String s, int level)
{
_util.debug(s, level, null);
}
/** CoordinatorListener - this does nothing */
public void peerChange(PeerCoordinator coordinator, Peer peer)
{
@ -1056,10 +1078,12 @@ public class Snark
* @since 0.8.4
*/
public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) {
meta = metainfo;
try {
storage = new Storage(_util, meta, this);
// The following two may throw IOE...
storage = new Storage(_util, metainfo, this);
storage.check(rootDataDir);
// ... so don't set meta until here
meta = metainfo;
if (completeListener != null) {
String newName = completeListener.gotMetaInfo(this);
if (newName != null)
@ -1075,7 +1099,7 @@ public class Snark
}
}
private boolean allocating = false;
//private boolean allocating = false;
public void storageCreateFile(Storage storage, String name, long length)
{
//if (allocating)
@ -1083,7 +1107,7 @@ public class Snark
//System.out.print("Creating file '" + name
// + "' of length " + length + ": ");
allocating = true;
//allocating = true;
}
// How much storage space has been allocated
@ -1091,9 +1115,9 @@ public class Snark
public void storageAllocated(Storage storage, long length)
{
allocating = true;
//allocating = true;
//System.out.print(".");
allocated += length;
//allocated += length;
//if (allocated == meta.getTotalLength())
// System.out.println(); // We have all the disk space we need.
}
@ -1103,7 +1127,7 @@ public class Snark
private boolean prechecking = true;
public void storageChecked(Storage storage, int num, boolean checked)
{
allocating = false;
//allocating = false;
if (!allChecked && !checking)
{
// Use the MetaInfo from the storage since our own might not
@ -1115,9 +1139,10 @@ public class Snark
// + " pieces: ");
checking = true;
}
if (!checking)
debug("Got " + (checked ? "" : "BAD ") + "piece: " + num,
Snark.INFO);
if (!checking) {
if (_log.shouldLog(Log.INFO))
_log.info("Got " + (checked ? "" : "BAD ") + "piece: " + num);
}
}
public void storageAllChecked(Storage storage)
@ -1133,7 +1158,8 @@ public class Snark
public void storageCompleted(Storage storage)
{
debug("Completely received " + torrent, Snark.INFO);
if (_log.shouldLog(Log.INFO))
_log.info("Completely received " + torrent);
//storage.close();
//System.out.println("Completely received: " + torrent);
if (completeListener != null)
@ -1153,6 +1179,15 @@ public class Snark
//System.exit(0);
}
/**
* StorageListener and CoordinatorListener callback
* @since 0.9.2
*/
public void addMessage(String message) {
if (completeListener != null)
completeListener.addMessage(this, message);
}
public interface CompleteListener {
public void torrentComplete(Snark snark);
public void updateStatus(Snark snark);
@ -1167,6 +1202,16 @@ public class Snark
*/
public String gotMetaInfo(Snark snark);
/**
* @since 0.9
*/
public void fatal(Snark snark, String error);
/**
* @since 0.9.2
*/
public void addMessage(Snark snark, String message);
// not really listeners but the easiest way to get back to an optional SnarkManager
public long getSavedTorrentTime(Snark snark);
public BitField getSavedTorrentBitField(Snark snark);
@ -1181,8 +1226,8 @@ public class Snark
if (_peerCoordinatorSet == null || uploaders <= 0)
return false;
int totalUploaders = 0;
for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
PeerCoordinator c = (PeerCoordinator)iter.next();
for (Iterator<PeerCoordinator> iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
PeerCoordinator c = iter.next();
if (!c.halted())
totalUploaders += c.uploaders;
}
@ -1195,13 +1240,14 @@ public class Snark
if (_peerCoordinatorSet == null)
return false;
long total = 0;
for (Iterator iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
PeerCoordinator c = (PeerCoordinator)iter.next();
for (Iterator<PeerCoordinator> iter = _peerCoordinatorSet.iterator(); iter.hasNext(); ) {
PeerCoordinator c = iter.next();
if (!c.halted())
total += c.getCurrentUploadRate();
}
long limit = 1024l * _util.getMaxUpBW();
debug("Total up bw: " + total + " Limit: " + limit, Snark.WARNING);
if (_log.shouldLog(Log.INFO))
_log.info("Total up bw: " + total + " Limit: " + limit);
return total > limit;
}

View File

@ -8,6 +8,10 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -16,9 +20,9 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
@ -30,13 +34,15 @@ import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
import org.klomp.snark.dht.DHT;
/**
* Manage multiple snarks
*/
public class SnarkManager implements Snark.CompleteListener {
private static SnarkManager _instance = new SnarkManager();
public static SnarkManager instance() { return _instance; }
/**
* Map of (canonical) filename of the .torrent file to Snark instance.
@ -51,12 +57,14 @@ public class SnarkManager implements Snark.CompleteListener {
private Properties _config;
private final I2PAppContext _context;
private final Log _log;
private final List _messages;
private final Queue<String> _messages;
private final I2PSnarkUtil _util;
private PeerCoordinatorSet _peerCoordinatorSet;
private ConnectionAcceptor _connectionAcceptor;
private Thread _monitor;
private volatile boolean _running;
private volatile boolean _stopping;
private final Map<String, Tracker> _trackerMap;
public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost";
public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort";
@ -79,27 +87,56 @@ public class SnarkManager implements Snark.CompleteListener {
//public static final String DEFAULT_LINK_PREFIX = "file:///";
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
public static final String PROP_REFRESH_DELAY = "i2psnark.refreshSeconds";
public static final String RC_PROP_THEME = "routerconsole.theme";
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 PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers";
private static final String PROP_USE_DHT = "i2psnark.enableDHT";
public static final int MIN_UP_BW = 2;
public static final int DEFAULT_MAX_UP_BW = 10;
public static final int DEFAULT_STARTUP_DELAY = 3;
public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
private SnarkManager() {
/**
* "name", "announceURL=websiteURL" pairs
* '=' in announceURL must be escaped as &#44;
*/
public static final String DEFAULT_TRACKERS[] = {
// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/"
// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/"
// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/"
// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/"
// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php"
// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/"
// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/"
"Postman", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/"
,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
,"Diftracker", "http://n--XWjHjUPjnMNrSwXA2OYXpMIUL~u4FNXnrt2HtjK3y6j~4SOClyyeKzd0zRPlixxkCe2wfBIYye3bZsaqAD8bd0QMmowxbq91WpjsPfKMiphJbePKXtYAVARiy0cqyvh1d2LyDE-6wkvgaw45hknmS0U-Dg3YTJZbAQRU2SKXgIlAbWCv4R0kDFBLEVpReDiJef3rzAWHiW8yjmJuJilkYjMwlfRjw8xx1nl2s~yhlljk1pl13jGYb0nfawQnuOWeP-ASQWvAAyVgKvZRJE2O43S7iveu9piuv7plXWbt36ef7ndu2GNoNyPOBdpo9KUZ-NOXm4Kgh659YtEibL15dEPAOdxprY0sYUurVw8OIWqrpX7yn08nbi6qHVGqQwTpxH35vkL8qrCbm-ym7oQJQnNmSDrNTyWYRFSq5s5~7DAdFDzqRPW-pX~g0zEivWj5tzkhvG9rVFgFo0bpQX3X0PUAV9Xbyf8u~v8Zbr9K1pCPqBq9XEr4TqaLHw~bfAAAA.i2p/announce.php=http://diftracker.i2p/"
// , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
// ,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
};
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
public static final String PROP_TRACKERS = "i2psnark.trackers";
public SnarkManager(I2PAppContext ctx) {
_snarks = new ConcurrentHashMap();
_magnets = new ConcurrentHashSet();
_addSnarkLock = new Object();
_context = I2PAppContext.getGlobalContext();
_context = ctx;
_log = _context.logManager().getLog(SnarkManager.class);
_messages = new ArrayList(16);
_messages = new LinkedBlockingQueue();
_util = new I2PSnarkUtil(_context);
_configFile = new File(CONFIG_FILE);
if (!_configFile.isAbsolute())
_configFile = new File(_context.getConfigDir(), CONFIG_FILE);
_trackerMap = new ConcurrentHashMap(4);
loadConfig(null);
}
@ -112,35 +149,50 @@ public class SnarkManager implements Snark.CompleteListener {
_connectionAcceptor = new ConnectionAcceptor(_util);
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
_monitor.start();
_context.addShutdownTask(new SnarkManagerShutdown());
// Not required, Jetty has a shutdown hook
//_context.addShutdownTask(new SnarkManagerShutdown());
}
/*
* Called by the webapp at Jetty shutdown.
* Stops all torrents. Does not close the tunnel, so the announces have a chance.
* Fix this so an individual webaapp stop will close the tunnel.
* Runs inline.
*/
public void stop() {
_running = false;
_monitor.interrupt();
_connectionAcceptor.halt();
(new SnarkManagerShutdown()).run();
stopAllTorrents(true);
}
/** @since 0.9.1 */
public boolean isStopping() { return _stopping; }
/** hook to I2PSnarkUtil for the servlet */
public I2PSnarkUtil util() { return _util; }
private static final int MAX_MESSAGES = 5;
private static final int MAX_MESSAGES = 100;
public void addMessage(String message) {
synchronized (_messages) {
_messages.add(message);
while (_messages.size() > MAX_MESSAGES)
_messages.remove(0);
_messages.offer(message);
while (_messages.size() > MAX_MESSAGES) {
_messages.poll();
}
if (_log.shouldLog(Log.INFO))
_log.info("MSG: " + message);
}
/** newest last */
public List getMessages() {
synchronized (_messages) {
return new ArrayList(_messages);
}
public List<String> getMessages() {
if (_messages.isEmpty())
return Collections.EMPTY_LIST;
return new ArrayList(_messages);
}
/** @since 0.9 */
public void clearMessages() {
_messages.clear();
}
/**
@ -237,6 +289,9 @@ public class SnarkManager implements Snark.CompleteListener {
_config.setProperty(PROP_STARTUP_DELAY, Integer.toString(DEFAULT_STARTUP_DELAY));
if (!_config.containsKey(PROP_THEME))
_config.setProperty(PROP_THEME, DEFAULT_THEME);
// no, so we can switch default to true later
//if (!_config.containsKey(PROP_USE_DHT))
// _config.setProperty(PROP_USE_DHT, Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT));
updateConfig();
}
/**
@ -245,6 +300,26 @@ public class SnarkManager implements Snark.CompleteListener {
*/
public String getTheme() {
String theme = _config.getProperty(PROP_THEME);
boolean universalTheming = _context.getBooleanProperty(RC_PROP_UNIVERSAL_THEMING);
if (universalTheming) {
// Fetch routerconsole theme (or use our default if it doesn't exist)
theme = _context.getProperty(RC_PROP_THEME, DEFAULT_THEME);
// Ensure that theme exists
String[] themes = getThemes();
boolean themeExists = false;
for (int i = 0; i < themes.length; i++) {
if (themes[i].equals(theme))
themeExists = true;
}
if (!themeExists) {
// Since the default is not "light", explicitly check if universal theme is "classic"
if (theme.equals("classic"))
theme = "light";
else
theme = DEFAULT_THEME;
_config.setProperty(PROP_THEME, DEFAULT_THEME);
}
}
return theme;
}
@ -307,11 +382,15 @@ public class SnarkManager implements Snark.CompleteListener {
_util.setFilesPublic(areFilesPublic());
String ot = _config.getProperty(PROP_OPENTRACKERS);
if (ot != null)
_util.setOpenTrackerString(ot);
_util.setOpenTrackers(getOpenTrackers());
String useOT = _config.getProperty(PROP_USE_OPENTRACKERS);
boolean bOT = useOT == null || Boolean.valueOf(useOT).booleanValue();
_util.setUseOpenTrackers(bOT);
// careful, so we can switch default to true later
_util.setUseDHT(Boolean.valueOf(_config.getProperty(PROP_USE_DHT,
Boolean.toString(I2PSnarkUtil.DEFAULT_USE_DHT))).booleanValue());
getDataDir().mkdirs();
initTrackerMap();
}
private int getInt(String prop, int defaultVal) {
@ -328,7 +407,7 @@ public class SnarkManager implements Snark.CompleteListener {
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
String startDelay, String seedPct, String eepHost,
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
String upLimit, String upBW, boolean useOpenTrackers, String openTrackers, String theme) {
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
boolean changed = false;
//if (eepHost != null) {
// // unused, we use socket eepget
@ -512,13 +591,16 @@ public class SnarkManager implements Snark.CompleteListener {
_util.setUseOpenTrackers(useOpenTrackers);
changed = true;
}
if (openTrackers != null) {
if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(_util.getOpenTrackerString())) {
_config.setProperty(PROP_OPENTRACKERS, openTrackers.trim());
_util.setOpenTrackerString(openTrackers);
addMessage(_("Open Tracker list changed - torrent restart required to take effect."));
changed = true;
}
if (_util.shouldUseDHT() != useDHT) {
_config.setProperty(PROP_USE_DHT, Boolean.toString(useDHT));
if (useDHT)
addMessage(_("Enabled DHT."));
else
addMessage(_("Disabled DHT."));
if (_util.connected())
addMessage(_("DHT change requires tunnel shutdown and reopen"));
_util.setUseDHT(useDHT);
changed = true;
}
if (theme != null) {
if(!theme.equals(_config.getProperty(PROP_THEME))) {
@ -534,6 +616,84 @@ public class SnarkManager implements Snark.CompleteListener {
}
}
/**
* Others should use the version in I2PSnarkUtil
* @return non-null, empty if disabled
* @since 0.9.1
*/
private List<String> getOpenTrackers() {
if (!_util.shouldUseOpenTrackers())
return Collections.EMPTY_LIST;
return getListConfig(PROP_OPENTRACKERS, I2PSnarkUtil.DEFAULT_OPENTRACKERS);
}
/**
* @return non-null, fixed size, may be empty or unmodifiable
* @since 0.9.1
*/
public List<String> getPrivateTrackers() {
return getListConfig(PROP_PRIVATETRACKERS, null);
}
/**
* @param ot null to restore default
* @since 0.9.1
*/
public void saveOpenTrackers(List<String> ot) {
String val = setListConfig(PROP_OPENTRACKERS, ot);
if (ot == null)
ot = Collections.singletonList(I2PSnarkUtil.DEFAULT_OPENTRACKERS);
_util.setOpenTrackers(ot);
addMessage(_("Open Tracker list changed - torrent restart required to take effect."));
saveConfig();
}
/**
* @param pt null ok, default is none
* @since 0.9.1
*/
public void savePrivateTrackers(List<String> pt) {
setListConfig(PROP_PRIVATETRACKERS, pt);
addMessage(_("Private tracker list changed - affects newly created torrents only."));
saveConfig();
}
/**
* @param dflt default or null
* @return non-null, fixed size
* @since 0.9.1
*/
private List<String> getListConfig(String prop, String dflt) {
String val = _config.getProperty(prop);
if (val == null)
val = dflt;
if (val == null)
return Collections.EMPTY_LIST;
return Arrays.asList(val.split(","));
}
/**
* Sets the config, does NOT save it
* @param values may be null or empty
* @return the comma-separated config string, non-null
* @since 0.9.1
*/
private String setListConfig(String prop, List<String> values) {
if (values == null || values.isEmpty()) {
_config.remove(prop);
return "";
}
StringBuilder buf = new StringBuilder(64);
for (String s : values) {
if (buf.length() > 0)
buf.append(',');
buf.append(s);
}
String rv = buf.toString();
_config.setProperty(prop, rv);
return rv;
}
public void saveConfig() {
try {
synchronized (_configFile) {
@ -623,7 +783,7 @@ public class SnarkManager implements Snark.CompleteListener {
File dataDir = getDataDir();
Snark torrent = null;
synchronized (_snarks) {
torrent = (Snark)_snarks.get(filename);
torrent = _snarks.get(filename);
}
// don't hold the _snarks lock while verifying the torrent
if (torrent == null) {
@ -663,14 +823,16 @@ public class SnarkManager implements Snark.CompleteListener {
}
if (!TrackerClient.isValidAnnounce(info.getAnnounce())) {
if (_util.shouldUseOpenTrackers() && _util.getOpenTrackers() != null) {
//addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers and DHT only.", info.getName()));
addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers only.", info.getName()));
//} else if (_util.getDHT() != null) {
// addMessage(_("Warning - No I2P trackers in \"{0}\", and open trackers are disabled, will announce to DHT only.", info.getName()));
if (info.isPrivate()) {
addMessage(_("ERROR - No I2P trackers in private torrent \"{0}\"", info.getName()));
} else if (!_util.getOpenTrackers().isEmpty()) {
addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers and DHT only.", info.getName()));
//addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers only.", info.getName()));
} else if (_util.shouldUseDHT()) {
addMessage(_("Warning - No I2P trackers in \"{0}\", and open trackers are disabled, will announce to DHT only.", info.getName()));
} else {
//addMessage(_("Warning - No I2P trackers in \"{0}\", and DHT and open trackers are disabled, you should enable open trackers or DHT before starting the torrent.", info.getName()));
addMessage(_("Warning - No I2P Trackers found in \"{0}\". Make sure Open Tracker is enabled before starting this torrent.", info.getName()));
addMessage(_("Warning - No I2P trackers in \"{0}\", and DHT and open trackers are disabled, you should enable open trackers or DHT before starting the torrent.", info.getName()));
//addMessage(_("Warning - No I2P Trackers found in \"{0}\". Make sure Open Tracker is enabled before starting this torrent.", info.getName()));
dontAutoStart = true;
}
}
@ -746,10 +908,13 @@ public class SnarkManager implements Snark.CompleteListener {
if (shouldAutoStart()) {
torrent.startTorrent();
addMessage(_("Fetching {0}", name));
boolean haveSavedPeers = false;
if ((!util().connected()) && !haveSavedPeers) {
addMessage(_("We have no saved peers and no other torrents are running. " +
"Fetch of {0} will not succeed until you start another torrent.", name));
DHT dht = _util.getDHT();
boolean shouldWarn = _util.connected() &&
_util.getOpenTrackers().isEmpty() &&
((!_util.shouldUseDHT()) || dht == null || dht.size() <= 0);
if (shouldWarn) {
addMessage(_("Open trackers are disabled and we have no DHT peers. " +
"Fetch of {0} may not succeed until you start another torrent, enable open trackers, or enable DHT.", name));
}
} else {
addMessage(_("Adding {0}", name));
@ -770,6 +935,29 @@ public class SnarkManager implements Snark.CompleteListener {
_magnets.remove(snark.getName());
removeMagnetStatus(snark.getInfoHash());
}
/**
* Add and start a FetchAndAdd task.
* Remove it with deleteMagnet().
*
* @param torrent must be instanceof FetchAndAdd
* @throws RuntimeException via Snark.fatal()?
* @since 0.9.1
*/
public void addDownloader(Snark torrent) {
synchronized (_snarks) {
Snark snark = getTorrentByInfoHash(torrent.getInfoHash());
if (snark != null) {
addMessage(_("Download already running: {0}", snark.getBaseName()));
return;
}
String name = torrent.getName();
// Tell the dir monitor not to delete us
_magnets.add(name);
_snarks.put(name, torrent);
}
torrent.startTorrent();
}
/**
* Add a torrent from a MetaInfo. Save the MetaInfo data to filename.
@ -1091,9 +1279,9 @@ public class SnarkManager implements Snark.CompleteListener {
Snark torrent = null;
synchronized (_snarks) {
if (shouldRemove)
torrent = (Snark)_snarks.remove(filename);
torrent = _snarks.remove(filename);
else
torrent = (Snark)_snarks.get(filename);
torrent = _snarks.get(filename);
remaining = _snarks.size();
}
if (torrent != null) {
@ -1152,13 +1340,11 @@ public class SnarkManager implements Snark.CompleteListener {
// don't bother delaying if auto start is false
long delay = 60 * 1000 * getStartupDelayMinutes();
if (delay > 0 && shouldAutoStart()) {
_messages.add(_("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
addMessage(_("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
// the first message was a "We are starting up in 1m"
synchronized (_messages) {
if (_messages.size() == 1)
_messages.remove(0);
}
// Remove that first message
if (_messages.size() == 1)
_messages.poll();
}
// here because we need to delay until I2CP is up
@ -1239,8 +1425,11 @@ public class SnarkManager implements Snark.CompleteListener {
return null;
}
saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities
String name = (new File(getDataDir(), storage.getBaseName() + ".torrent")).getAbsolutePath();
// temp for addMessage() in case canonical throws
String name = storage.getBaseName();
try {
// _snarks must use canonical
name = (new File(getDataDir(), storage.getBaseName() + ".torrent")).getCanonicalPath();
// put the announce URL in the file
String announce = snark.getTrackerURL();
if (announce != null)
@ -1264,6 +1453,22 @@ public class SnarkManager implements Snark.CompleteListener {
return null;
}
/**
* A Snark.CompleteListener method.
* @since 0.9
*/
public void fatal(Snark snark, String error) {
addMessage(_("Error on torrent {0}", snark.getName()) + ": " + error);
}
/**
* A Snark.CompleteListener method.
* @since 0.9.2
*/
public void addMessage(Snark snark, String message) {
addMessage(message);
}
// End Snark.CompleteListeners
/**
@ -1279,7 +1484,7 @@ public class SnarkManager implements Snark.CompleteListener {
byte[] ih = Base64.decode(b64);
// ignore value - TODO put tracker URL in value
if (ih != null && ih.length == 20)
addMagnet("Magnet: " + I2PSnarkUtil.toHex(ih), ih, null, false);
addMagnet(_("Magnet") + ' ' + I2PSnarkUtil.toHex(ih), ih, null, false);
// else remove from config?
}
}
@ -1356,57 +1561,89 @@ public class SnarkManager implements Snark.CompleteListener {
}
/**
* "name", "announceURL=websiteURL" pairs
* Unsorted map of name to Tracker object
* Modifiable, not a copy
* @since 0.9.1
*/
private static final String DEFAULT_TRACKERS[] = {
// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/"
// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/"
// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/"
// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/"
// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php"
// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/"
// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/"
"Postman", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/"
,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
,"Diftracker", "http://n--XWjHjUPjnMNrSwXA2OYXpMIUL~u4FNXnrt2HtjK3y6j~4SOClyyeKzd0zRPlixxkCe2wfBIYye3bZsaqAD8bd0QMmowxbq91WpjsPfKMiphJbePKXtYAVARiy0cqyvh1d2LyDE-6wkvgaw45hknmS0U-Dg3YTJZbAQRU2SKXgIlAbWCv4R0kDFBLEVpReDiJef3rzAWHiW8yjmJuJilkYjMwlfRjw8xx1nl2s~yhlljk1pl13jGYb0nfawQnuOWeP-ASQWvAAyVgKvZRJE2O43S7iveu9piuv7plXWbt36ef7ndu2GNoNyPOBdpo9KUZ-NOXm4Kgh659YtEibL15dEPAOdxprY0sYUurVw8OIWqrpX7yn08nbi6qHVGqQwTpxH35vkL8qrCbm-ym7oQJQnNmSDrNTyWYRFSq5s5~7DAdFDzqRPW-pX~g0zEivWj5tzkhvG9rVFgFo0bpQX3X0PUAV9Xbyf8u~v8Zbr9K1pCPqBq9XEr4TqaLHw~bfAAAA.i2p/announce.php=http://diftracker.i2p/"
// , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
// ,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
};
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
public static final String PROP_TRACKERS = "i2psnark.trackers";
private static Map<String, String> trackerMap = null;
/** sorted map of name to announceURL=baseURL */
public Map<String, String> getTrackers() {
if (trackerMap != null) // only do this once, can't be updated while running
return trackerMap;
Map<String, String> rv = new TreeMap();
public Map<String, Tracker> getTrackerMap() {
return _trackerMap;
}
/**
* Unsorted, do not modify
*/
public Collection<Tracker> getTrackers() {
return _trackerMap.values();
}
/**
* Sorted copy
* @since 0.9.1
*/
public List<Tracker> getSortedTrackers() {
List<Tracker> rv = new ArrayList(_trackerMap.values());
Collections.sort(rv, new IgnoreCaseComparator());
return rv;
}
/** @since 0.9 */
private void initTrackerMap() {
String trackers = _config.getProperty(PROP_TRACKERS);
if ( (trackers == null) || (trackers.trim().length() <= 0) )
trackers = _context.getProperty(PROP_TRACKERS);
if ( (trackers == null) || (trackers.trim().length() <= 0) ) {
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2)
rv.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]);
setDefaultTrackerMap(true);
} else {
StringTokenizer tok = new StringTokenizer(trackers, ",");
while (tok.hasMoreTokens()) {
String pair = tok.nextToken();
int split = pair.indexOf('=');
if (split <= 0)
continue;
String name = pair.substring(0, split).trim();
String url = pair.substring(split+1).trim();
if ( (name.length() > 0) && (url.length() > 0) )
rv.put(name, url);
String[] toks = trackers.split(",");
for (int i = 0; i < toks.length; i += 2) {
String name = toks[i].trim().replace("&#44;", ",");
String url = toks[i+1].trim().replace("&#44;", ",");
if ( (name.length() > 0) && (url.length() > 0) ) {
String urls[] = url.split("=", 2);
String url2 = urls.length > 1 ? urls[1] : "";
_trackerMap.put(name, new Tracker(name, urls[0], url2));
}
}
}
trackerMap = rv;
return trackerMap;
}
/** @since 0.9 */
public void setDefaultTrackerMap() {
setDefaultTrackerMap(true);
}
/** @since 0.9.1 */
private void setDefaultTrackerMap(boolean save) {
_trackerMap.clear();
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) {
String name = DEFAULT_TRACKERS[i];
String urls[] = DEFAULT_TRACKERS[i+1].split("=", 2);
String url2 = urls.length > 1 ? urls[1] : null;
_trackerMap.put(name, new Tracker(name, urls[0], url2));
}
if (save && _config.remove(PROP_TRACKERS) != null) {
saveConfig();
}
}
/** @since 0.9 */
public void saveTrackerMap() {
StringBuilder buf = new StringBuilder(2048);
boolean comma = false;
for (Map.Entry<String, Tracker> e : _trackerMap.entrySet()) {
if (comma)
buf.append(',');
else
comma = true;
Tracker t = e.getValue();
buf.append(e.getKey().replace(",", "&#44;")).append(',').append(t.announceURL.replace(",", "&#44;"));
if (t.baseURL != null)
buf.append('=').append(t.baseURL);
}
_config.setProperty(PROP_TRACKERS, buf.toString());
saveConfig();
}
private static class TorrentFilenameFilter implements FilenameFilter {
private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter();
public static TorrentFilenameFilter instance() { return _filter; }
@ -1415,15 +1652,147 @@ public class SnarkManager implements Snark.CompleteListener {
}
}
public class SnarkManagerShutdown extends I2PAppThread {
@Override
/**
* If not connected, thread it, otherwise inline
* @since 0.9.1
*/
public void startTorrent(byte[] infoHash) {
for (Snark snark : _snarks.values()) {
if (DataHelper.eq(infoHash, snark.getInfoHash())) {
if (snark.isStarting() || !snark.isStopped()) {
addMessage("Torrent already started");
return;
}
boolean connected = _util.connected();
if ((!connected) && !_util.isConnecting())
addMessage(_("Opening the I2P tunnel"));
addMessage(_("Starting up torrent {0}", snark.getBaseName()));
if (connected) {
snark.startTorrent();
} else {
// mark it for the UI
snark.setStarting();
(new I2PAppThread(new ThreadedStarter(snark), "TorrentStarter", true)).start();
try { Thread.sleep(200); } catch (InterruptedException ie) {}
}
return;
}
}
addMessage("Torrent not found?");
}
/**
* If not connected, thread it, otherwise inline
* @since 0.9.1
*/
public void startAllTorrents() {
if (_util.connected()) {
startAll();
} else {
addMessage(_("Opening the I2P tunnel and starting all torrents."));
for (Snark snark : _snarks.values()) {
// mark it for the UI
snark.setStarting();
}
(new I2PAppThread(new ThreadedStarter(null), "TorrentStarterAll", true)).start();
try { Thread.sleep(200); } catch (InterruptedException ie) {}
}
}
/**
* Use null constructor param for all
* @since 0.9.1
*/
private class ThreadedStarter implements Runnable {
private final Snark snark;
public ThreadedStarter(Snark s) { snark = s; }
public void run() {
Set names = listTorrentFiles();
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
Snark snark = getTorrent((String)iter.next());
if ( (snark != null) && (!snark.isStopped()) )
snark.stopTorrent();
if (snark != null) {
if (snark.isStopped())
snark.startTorrent();
} else {
startAll();
}
}
}
/**
* Inline
* @since 0.9.1
*/
private void startAll() {
for (Snark snark : _snarks.values()) {
if (snark.isStopped())
snark.startTorrent();
}
}
/**
* Stop all running torrents, and close the tunnel after a delay
* to allow for announces.
* If called at router shutdown via Jetty shutdown hook -> webapp destroy() -> stop(),
* the tunnel won't actually be closed as the SimpleScheduler is already shutdown
* or will be soon, so we delay a few seconds inline.
* @param finalShutdown if true, sleep at the end if any torrents were running
* @since 0.9.1
*/
public void stopAllTorrents(boolean finalShutdown) {
_stopping = true;
if (finalShutdown && _log.shouldLog(Log.WARN))
_log.warn("SnarkManager final shutdown");
int count = 0;
for (Snark snark : _snarks.values()) {
if (!snark.isStopped()) {
if (count == 0)
addMessage(_("Stopping all torrents and closing the I2P tunnel."));
count++;
if (finalShutdown)
snark.stopTorrent(true);
else
stopTorrent(snark, false);
// Throttle since every unannounce is now threaded.
// How to do this without creating a ton of threads?
try { Thread.sleep(20); } catch (InterruptedException ie) {}
}
}
if (_util.connected()) {
if (count > 0) {
DHT dht = _util.getDHT();
if (dht != null)
dht.stop();
// Schedule this even for final shutdown, as there's a chance
// that it's just this webapp that is stopping.
_context.simpleScheduler().addEvent(new Disconnector(), 60*1000);
addMessage(_("Closing I2P tunnel after notifying trackers."));
if (finalShutdown) {
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
}
} else {
_util.disconnect();
_stopping = false;
addMessage(_("I2P tunnel closed."));
}
}
}
/** @since 0.9.1 */
private class Disconnector implements SimpleTimer.TimedEvent {
public void timeReached() {
if (_util.connected()) {
_util.disconnect();
_stopping = false;
addMessage(_("I2P tunnel closed."));
}
}
}
/**
* ignore case, current locale
* @since 0.9
*/
private static class IgnoreCaseComparator implements Comparator<Tracker> {
public int compare(Tracker l, Tracker r) {
return l.name.toLowerCase().compareTo(r.name.toLowerCase());
}
}
}

View File

@ -26,6 +26,7 @@ import net.i2p.util.I2PAppThread;
/**
* Makes sure everything ends correctly when shutting down.
* @deprecated unused
*/
public class SnarkShutdown extends I2PAppThread
{
@ -61,7 +62,7 @@ public class SnarkShutdown extends I2PAppThread
//Snark.debug("Halting TrackerClient...", Snark.INFO);
if (trackerclient != null)
trackerclient.halt();
trackerclient.halt(true);
//Snark.debug("Halting PeerCoordinator...", Snark.INFO);
if (coordinator != null)

View File

@ -34,6 +34,7 @@ import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.SHA1;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
/**
@ -50,9 +51,12 @@ public class Storage
private File[] RAFfile; // File to make it easier to reopen
/** priorities by file; default 0; may be null. @since 0.8.1 */
private int[] priorities;
/** is the file empty and sparse? */
private boolean[] isSparse;
private final StorageListener listener;
private final I2PSnarkUtil _util;
private final Log _log;
private /* FIXME final FIXME */ BitField bitfield; // BitField to represent the pieces
private int needed; // Number of pieces needed
@ -73,6 +77,8 @@ public class Storage
private static final Map<String, String> _filterNameCache = new ConcurrentHashMap();
private static final boolean _isWindows = System.getProperty("os.name").startsWith("Win");
/**
* Creates a new storage based on the supplied MetaInfo. This will
* try to create and/or check all needed files in the MetaInfo.
@ -83,6 +89,7 @@ public class Storage
throws IOException
{
_util = util;
_log = util.getContext().logManager().getLog(Storage.class);
this.metainfo = metainfo;
this.listener = listener;
needed = metainfo.getPieces();
@ -101,10 +108,12 @@ public class Storage
* @param announce may be null
* @param listener may be null
*/
public Storage(I2PSnarkUtil util, File baseFile, String announce, StorageListener listener)
public Storage(I2PSnarkUtil util, File baseFile, String announce,
boolean privateTorrent, StorageListener listener)
throws IOException
{
_util = util;
_log = util.getContext().logManager().getLog(Storage.class);
this.listener = listener;
// Create names, rafs and lengths arrays.
getFiles(baseFile);
@ -157,7 +166,7 @@ public class Storage
byte[] piece_hashes = fast_digestCreate();
metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
lengthsList, piece_size, piece_hashes, total);
lengthsList, piece_size, piece_hashes, total, privateTorrent);
}
@ -201,7 +210,7 @@ public class Storage
RAFtime = new long[size];
RAFfile = new File[size];
priorities = new int[size];
isSparse = new boolean[size];
int i = 0;
Iterator it = files.iterator();
@ -227,8 +236,9 @@ public class Storage
File[] files = f.listFiles();
if (files == null)
{
_util.debug("WARNING: Skipping '" + f
+ "' not a normal file.", Snark.WARNING);
if (_log.shouldLog(Log.WARN))
_log.warn("WARNING: Skipping '" + f
+ "' not a normal file.");
return;
}
for (int i = 0; i < files.length; i++)
@ -333,7 +343,8 @@ public class Storage
}
/**
* Must call setPiecePriorities() after calling this
* Must call Snark.updatePiecePriorities()
* (which calls getPiecePriorities()) after calling this.
* @param file canonical path (non-directory)
* @param pri default 0; <0 to disable
* @since 0.8.1
@ -451,7 +462,8 @@ public class Storage
if (files == null)
{
// Create base as file.
_util.debug("Creating/Checking file: " + base, Snark.NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Creating/Checking file: " + base);
if (!base.createNewFile() && !base.exists())
throw new IOException("Could not create file " + base);
@ -461,6 +473,7 @@ public class Storage
RAFlock = new Object[1];
RAFtime = new long[1];
RAFfile = new File[1];
isSparse = new boolean[1];
lengths[0] = metainfo.getTotalLength();
RAFlock[0] = new Object();
RAFfile[0] = base;
@ -474,7 +487,8 @@ public class Storage
else
{
// Create base as dir.
_util.debug("Creating/Checking directory: " + base, Snark.NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Creating/Checking directory: " + base);
if (!base.mkdir() && !base.isDirectory())
throw new IOException("Could not create directory " + base);
@ -487,6 +501,7 @@ public class Storage
RAFlock = new Object[size];
RAFtime = new long[size];
RAFfile = new File[size];
isSparse = new boolean[size];
for (int i = 0; i < size; i++)
{
List<String> path = files.get(i);
@ -532,19 +547,22 @@ public class Storage
bitfield = savedBitField;
needed = metainfo.getPieces() - bitfield.count();
_probablyComplete = complete();
_util.debug("Found saved state and files unchanged, skipping check", Snark.NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Found saved state and files unchanged, skipping check");
} else {
// the following sets the needed variable
changed = true;
checkCreateFiles(false);
}
if (complete()) {
_util.debug("Torrent is complete", Snark.NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Torrent is complete");
} else {
// fixme saved priorities
if (files != null)
priorities = new int[files.size()];
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
if (_log.shouldLog(Log.INFO))
_log.info("Still need " + needed + " out of " + metainfo.getPieces() + " pieces");
}
}
@ -700,6 +718,7 @@ public class Storage
}
// Make sure all files are available and of correct length
// The files should all exist as they have been created with zero length by createFilesFromNames()
for (int i = 0; i < rafs.length; i++)
{
long length = RAFfile[i].length();
@ -721,9 +740,11 @@ public class Storage
} else {
String msg = "File '" + names[i] + "' exists, but has wrong length (expected " +
lengths[i] + " but found " + length + ") - repairing corruption";
SnarkManager.instance().addMessage(msg);
_util.debug(msg, Snark.ERROR);
if (listener != null)
listener.addMessage(msg);
_log.error(msg);
changed = true;
resume = true;
_probablyComplete = false; // to force RW
synchronized(RAFlock[i]) {
checkRAF(i);
@ -797,26 +818,55 @@ public class Storage
}
}
/** this calls openRAF(); caller must synnchronize and call closeRAF() */
/**
* This creates a (presumably) sparse file so that reads won't fail with IOE.
* Sets isSparse[nr] = true. balloonFile(nr) should be called later to
* defrag the file.
*
* This calls openRAF(); caller must synchronize and call closeRAF().
*/
private void allocateFile(int nr) throws IOException
{
// caller synchronized
openRAF(nr, false); // RW
// XXX - Is this the best way to make sure we have enough space for
// the whole file?
long remaining = lengths[nr];
if (listener != null)
listener.storageCreateFile(this, names[nr], remaining);
rafs[nr].setLength(remaining);
// don't bother ballooning later on Windows since there is no sparse file support
// until JDK7 using the JSR-203 interface.
// RAF seeks/writes do not create sparse files.
// Windows will zero-fill up to the point of the write, which
// will make the file fairly unfragmented, on average, at least until
// near the end where it will get exponentially more fragmented.
if (!_isWindows)
isSparse[nr] = true;
// caller will close rafs[nr]
if (listener != null)
listener.storageAllocated(this, lengths[nr]);
}
/**
* This "balloons" the file with zeros to eliminate disk fragmentation.,
* Overwrites the entire file with zeros. Sets isSparse[nr] = false.
*
* Caller must synchronize and call checkRAF() or openRAF().
* @since 0.9.1
*/
private void balloonFile(int nr) throws IOException
{
if (_log.shouldLog(Log.INFO))
_log.info("Ballooning " + nr + ": " + RAFfile[nr]);
long remaining = lengths[nr];
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
byte[] zeros = new byte[ZEROBLOCKSIZE];
rafs[nr].seek(0);
while (remaining > 0) {
int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
rafs[nr].write(zeros, 0, size);
remaining -= size;
}
// caller will close rafs[nr]
if (listener != null)
listener.storageAllocated(this, lengths[nr]);
isSparse[nr] = false;
}
@ -837,7 +887,7 @@ public class Storage
closeRAF(i);
}
} catch (IOException ioe) {
_util.debug("Error closing " + RAFfile[i], Snark.ERROR, ioe);
_log.error("Error closing " + RAFfile[i], ioe);
// gobble gobble
}
}
@ -858,7 +908,8 @@ public class Storage
try {
bs = new byte[len];
} catch (OutOfMemoryError oom) {
_util.debug("Out of memory, can't honor request for piece " + piece, Snark.WARNING, oom);
if (_log.shouldLog(Log.WARN))
_log.warn("Out of memory, can't honor request for piece " + piece, oom);
return null;
}
getUncheckedPiece(piece, bs, off, len);
@ -872,54 +923,66 @@ public class Storage
* matches), otherwise false.
* @exception IOException when some storage related error occurs.
*/
public boolean putPiece(int piece, byte[] ba) throws IOException
public boolean putPiece(PartialPiece pp) throws IOException
{
// First check if the piece is correct.
// Copy the array first to be paranoid.
byte[] bs = (byte[]) ba.clone();
int length = bs.length;
boolean correctHash = metainfo.checkPiece(piece, bs, 0, length);
if (listener != null)
listener.storageChecked(this, piece, correctHash);
if (!correctHash)
return false;
int piece = pp.getPiece();
try {
synchronized(bitfield) {
if (bitfield.get(piece))
return true; // No need to store twice.
}
synchronized(bitfield)
{
if (bitfield.get(piece))
return true; // No need to store twice.
}
// TODO alternative - check hash on the fly as we write to the file,
// to save another I/O pass
boolean correctHash = metainfo.checkPiece(pp);
if (listener != null)
listener.storageChecked(this, piece, correctHash);
if (!correctHash) {
return false;
}
// Early typecast, avoid possibly overflowing a temp integer
long start = (long) piece * (long) piece_size;
int i = 0;
long raflen = lengths[i];
while (start > raflen)
{
i++;
start -= raflen;
raflen = lengths[i];
}
// Early typecast, avoid possibly overflowing a temp integer
long start = (long) piece * (long) piece_size;
int i = 0;
long raflen = lengths[i];
while (start > raflen) {
i++;
start -= raflen;
raflen = lengths[i];
}
int written = 0;
int off = 0;
while (written < length)
{
int need = length - written;
int len = (start + need < raflen) ? need : (int)(raflen - start);
synchronized(RAFlock[i])
{
checkRAF(i);
rafs[i].seek(start);
rafs[i].write(bs, off + written, len);
}
written += len;
if (need - len > 0)
{
i++;
raflen = lengths[i];
start = 0;
int written = 0;
int off = 0;
int length = metainfo.getPieceLength(piece);
while (written < length) {
int need = length - written;
int len = (start + need < raflen) ? need : (int)(raflen - start);
synchronized(RAFlock[i]) {
checkRAF(i);
if (isSparse[i]) {
// If the file is a newly created sparse file,
// AND we aren't skipping it, balloon it with all
// zeros to un-sparse it by allocating the space.
// Obviously this could take a while.
// Once we have written to it, it isn't empty/sparse any more.
if (priorities == null || priorities[i] >= 0)
balloonFile(i);
else
isSparse[i] = false;
}
rafs[i].seek(start);
//rafs[i].write(bs, off + written, len);
pp.write(rafs[i], off + written, len);
}
written += len;
if (need - len > 0) {
i++;
raflen = lengths[i];
start = 0;
}
}
} finally {
pp.release();
}
changed = true;
@ -950,13 +1013,14 @@ public class Storage
if (needed > 0) {
if (listener != null)
listener.setWantedPieces(this);
_util.debug("WARNING: Not really done, missing " + needed
+ " pieces", Snark.WARNING);
if (_log.shouldLog(Log.WARN))
_log.warn("WARNING: Not really done, missing " + needed
+ " pieces");
}
}
return true;
}
}
/**
* This is a dup of MetaInfo.getPieceLength() but we need it
@ -1021,7 +1085,7 @@ public class Storage
/**
* Close unused RAFs - call periodically
*/
private static final long RAFCloseDelay = 7*60*1000;
private static final long RAFCloseDelay = 4*60*1000;
public void cleanRAFs() {
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
for (int i = 0; i < RAFlock.length; i++) {

View File

@ -23,7 +23,7 @@ package org.klomp.snark;
/**
* Callback used when Storage changes.
*/
public interface StorageListener
interface StorageListener
{
/**
* Called when the storage creates a new file of a given length.
@ -61,4 +61,6 @@ public interface StorageListener
*
*/
void setWantedPieces(Storage storage);
void addMessage(String message);
}

View File

@ -0,0 +1,28 @@
/*
* Released into the public domain
* with no warranty of any kind, either expressed or implied.
*/
package org.klomp.snark;
/**
* A structure for known trackers
*
* @since 0.9.1
*/
public class Tracker {
public final String name;
public final String announceURL;
public final String baseURL;
public final boolean supportsDetails;
/**
* @param baseURL The web site, may be null
*/
public Tracker(String name, String announceURL, String baseURL) {
this.name = name;
this.announceURL = announceURL;
this.baseURL = baseURL;
this.supportsDetails = name.contains("tracker2.postman.i2p");
}
}

View File

@ -27,7 +27,9 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@ -35,9 +37,11 @@ import java.util.Random;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
import org.klomp.snark.dht.DHT;
@ -45,11 +49,22 @@ import org.klomp.snark.dht.DHT;
* Informs metainfo tracker of events and gets new peers for peer
* coordinator.
*
* start() creates a thread and starts it.
* At the end of each run, a TimedEvent is queued on the SimpleTimer2 queue.
* The TimedEvent creates a new thread and starts it, so it does not
* clog SimpleTimer2.
*
* The thread runs one pass through the trackers, the PEX, and the DHT,
* then queues a new TimedEvent and exits.
*
* Thus there are only threads that are actively announcing, not one thread per torrent forever.
*
* start() may be called again after halt().
*
* @author Mark Wielaard (mark@klomp.org)
*/
public class TrackerClient extends I2PAppThread
{
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(TrackerClient.class);
public class TrackerClient implements Runnable {
private final Log _log;
private static final String NO_EVENT = "";
private static final String STARTED_EVENT = "started";
private static final String COMPLETED_EVENT = "completed";
@ -58,25 +73,43 @@ public class TrackerClient extends I2PAppThread
private final static int SLEEP = 5; // 5 minutes.
private final static int DELAY_MIN = 2000; // 2 secs.
private final static int DELAY_MUL = 1500; // 1.5 secs.
private final static int DELAY_RAND = 6*1000;
private final static int MAX_REGISTER_FAILS = 10; // * INITIAL_SLEEP = 15m to register
private final static int INITIAL_SLEEP = 90*1000;
private final static int MAX_CONSEC_FAILS = 5; // slow down after this
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 10*60*1000;
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
private I2PSnarkUtil _util;
private final I2PSnarkUtil _util;
private final MetaInfo meta;
private final String infoHash;
private final String peerID;
private final String additionalTrackerURL;
private final PeerCoordinator coordinator;
private final Snark snark;
private final int port;
private final String _threadName;
private boolean stop;
private boolean started;
private List trackers;
private volatile boolean stop = true;
private volatile boolean started;
private volatile boolean _initialized;
private volatile int _runCount;
// running thread so it can be interrupted
private volatile Thread _thread;
// queued event so it can be cancelled
private volatile SimpleTimer2.TimedEvent _event;
// these 2 used in loop()
private volatile boolean runStarted;
private volatile int consecutiveFails;
private boolean completed;
private volatile boolean _fastUnannounce;
private long lastDHTAnnounce;
private final List<Tracker> trackers;
/**
* Call start() to start it.
*
* @param meta null if in magnet mode
* @param additionalTrackerURL may be null, from the ?tr= param in magnet mode, otherwise ignored
*/
@ -86,20 +119,32 @@ public class TrackerClient extends I2PAppThread
super();
// Set unique name.
String id = urlencode(snark.getID());
setName("TrackerClient " + id.substring(id.length() - 12));
_threadName = "TrackerClient " + id.substring(id.length() - 12);
_util = util;
_log = util.getContext().logManager().getLog(TrackerClient.class);
this.meta = meta;
this.additionalTrackerURL = additionalTrackerURL;
this.coordinator = coordinator;
this.snark = snark;
this.port = 6881; //(port == -1) ? 9 : port;
this.infoHash = urlencode(snark.getInfoHash());
this.peerID = urlencode(snark.getID());
this.trackers = new ArrayList(2);
}
@Override
public void start() {
if (stop) throw new RuntimeException("Dont rerun me, create a copy");
super.start();
public synchronized void start() {
if (!stop) {
if (_log.shouldLog(Log.WARN))
_log.warn("Already started: " + _threadName);
return;
}
stop = false;
consecutiveFails = 0;
runStarted = false;
_fastUnannounce = false;
_thread = new I2PAppThread(this, _threadName + " #" + (++_runCount), true);
_thread.start();
started = true;
}
@ -108,11 +153,50 @@ public class TrackerClient extends I2PAppThread
/**
* Interrupts this Thread to stop it.
* @param fast if true, limit the life of the unannounce threads
*/
public void halt()
{
stop = true;
this.interrupt();
public synchronized void halt(boolean fast) {
boolean wasStopped = stop;
if (wasStopped) {
if (_log.shouldLog(Log.WARN))
_log.warn("Already stopped: " + _threadName);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Stopping: " + _threadName);
stop = true;
}
SimpleTimer2.TimedEvent e = _event;
if (e != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Cancelling next announce " + _threadName);
e.cancel();
_event = null;
}
Thread t = _thread;
if (t != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Interrupting " + t.getName());
t.interrupt();
}
_fastUnannounce = true;
if (!wasStopped)
unannounce();
}
private void queueLoop(long delay) {
_event = new Runner(delay);
}
private class Runner extends SimpleTimer2.TimedEvent {
public Runner(long delay) {
super(_util.getContext().simpleTimer2(), delay);
}
public void timeReached() {
_event = null;
_thread = new I2PAppThread(TrackerClient.this, _threadName + " #" + (++_runCount), true);
_thread.start();
}
}
private boolean verifyConnected() {
@ -125,20 +209,56 @@ public class TrackerClient extends I2PAppThread
return !stop && _util.connected();
}
@Override
public void run()
{
String infoHash = urlencode(snark.getInfoHash());
String peerID = urlencode(snark.getID());
/**
* Setup the first time only,
* then one pass (usually) through the trackers, PEX, and DHT.
* This will take several seconds to several minutes.
*/
public void run() {
long begin = _util.getContext().clock().now();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Start " + Thread.currentThread().getName());
try {
if (!_initialized) {
setup();
}
if (trackers.isEmpty() && _util.getDHT() == null) {
stop = true;
this.snark.addMessage(_util.getString("No valid trackers for {0} - enable opentrackers or DHT?",
this.snark.getBaseName()));
_log.error("No valid trackers for " + this.snark.getBaseName());
this.snark.stopTorrent();
return;
}
if (!_initialized) {
_initialized = true;
// FIXME only when starting everybody at once, not for a single torrent
long delay = I2PAppContext.getGlobalContext().random().nextInt(30*1000);
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {}
}
loop();
} finally {
// don't hold ref
_thread = null;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Finish " + Thread.currentThread().getName() +
" after " + DataHelper.formatDuration(_util.getContext().clock().now() - begin));
}
}
/**
* Do this one time only (not every time it is started).
* @since 0.9.1
*/
private void setup() {
// Construct the list of trackers for this torrent,
// starting with the primary one listed in the metainfo,
// followed by the secondary open trackers
// It's painful, but try to make sure if an open tracker is also
// the primary tracker, that we don't add it twice.
// todo: check for b32 matches as well
trackers = new ArrayList(2);
String primary = null;
if (meta != null)
primary = meta.getAnnounce();
@ -156,7 +276,7 @@ public class TrackerClient extends I2PAppThread
primary = "";
}
List tlist = _util.getOpenTrackers();
if (tlist != null) {
if (tlist != null && (meta == null || !meta.isPrivate())) {
for (int i = 0; i < tlist.size(); i++) {
String url = (String)tlist.get(i);
if (!isValidAnnounce(url)) {
@ -184,70 +304,33 @@ public class TrackerClient extends I2PAppThread
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
}
}
this.completed = coordinator.getLeft() == 0;
}
if (trackers.isEmpty()) {
stop = true;
// FIXME translate
SnarkManager.instance().addMessage("No valid trackers for " + this.snark.getBaseName() + " - enable opentrackers?");
_log.error("No valid trackers for " + this.snark.getBaseName());
// FIXME keep going if DHT enabled
this.snark.stopTorrent();
return;
}
long uploaded = coordinator.getUploaded();
long downloaded = coordinator.getDownloaded();
long left = coordinator.getLeft();
boolean completed = (left == 0);
int sleptTime = 0;
/**
* Announce to all the trackers, get peers from PEX and DHT, then queue up a SimpleTimer2 event.
* This will take several seconds to several minutes.
* @since 0.9.1
*/
private void loop() {
try
{
if (!verifyConnected()) return;
boolean runStarted = false;
boolean firstTime = true;
int consecutiveFails = 0;
Random r = I2PAppContext.getGlobalContext().random();
while(!stop)
{
if (!verifyConnected()) {
stop = true;
return;
}
// Local DHT tracker announce
if (_util.getDHT() != null)
_util.getDHT().announce(snark.getInfoHash());
try
{
// Sleep some minutes...
// Sleep the minimum interval for all the trackers, but 60s minimum
// except for the first time...
int delay;
int random = r.nextInt(120*1000);
if (firstTime) {
delay = r.nextInt(30*1000);
firstTime = false;
} else if (completed && runStarted)
delay = 3*SLEEP*60*1000 + random;
else if (snark.getTrackerProblems() != null && ++consecutiveFails < MAX_CONSEC_FAILS)
delay = INITIAL_SLEEP;
else
// sleep a while, when we wake up we will contact only the trackers whose intervals have passed
delay = SLEEP*60*1000 + random;
DHT dht = _util.getDHT();
if (dht != null)
dht.announce(snark.getInfoHash());
if (delay > 0)
Thread.sleep(delay);
}
catch(InterruptedException interrupt)
{
// ignore
}
if (stop)
break;
if (!verifyConnected()) return;
uploaded = coordinator.getUploaded();
downloaded = coordinator.getDownloaded();
left = coordinator.getLeft(); // -1 in magnet mode
long uploaded = coordinator.getUploaded();
long downloaded = coordinator.getDownloaded();
long left = coordinator.getLeft(); // -1 in magnet mode
// First time we got a complete download?
String event;
@ -260,13 +343,10 @@ public class TrackerClient extends I2PAppThread
event = NO_EVENT;
// *** loop once for each tracker
// Only do a request when necessary.
sleptTime = 0;
int maxSeenPeers = 0;
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
Tracker tr = (Tracker)iter.next();
for (Tracker tr : trackers) {
if ((!stop) && (!tr.stop) &&
(completed || coordinator.needPeers()) &&
(completed || coordinator.needOutboundPeers() || !tr.started) &&
(event.equals(COMPLETED_EVENT) || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
{
try
@ -292,25 +372,25 @@ public class TrackerClient extends I2PAppThread
snark.setTrackerSeenPeers(tr.seenPeers);
// pass everybody over to our tracker
if (_util.getDHT() != null) {
dht = _util.getDHT();
if (dht != null) {
for (Peer peer : peers) {
_util.getDHT().announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
}
}
if ( (left != 0) && (!completed) ) {
if (coordinator.needOutboundPeers()) {
// we only want to talk to new people if we need things
// from them (duh)
List<Peer> ordered = new ArrayList(peers);
Collections.shuffle(ordered, r);
Iterator<Peer> it = ordered.iterator();
while ((!stop) && it.hasNext()) {
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
Peer cur = it.next();
// FIXME if id == us || dest == us continue;
// only delay if we actually make an attempt to add peer
if(coordinator.addPeer(cur) && it.hasNext()) {
int delay = (DELAY_MUL * r.nextInt(10)) + DELAY_MIN;
sleptTime += delay;
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
}
}
@ -319,9 +399,10 @@ public class TrackerClient extends I2PAppThread
catch (IOException ioe)
{
// Probably not fatal (if it doesn't last to long...)
_util.debug
if (_log.shouldLog(Log.WARN))
_log.warn
("WARNING: Could not contact tracker at '"
+ tr.announce + "': " + ioe, Snark.WARNING);
+ tr.announce + "': " + ioe);
tr.trackerProblems = ioe.getMessage();
// don't show secondary tracker problems to the user
if (tr.isPrimary)
@ -342,101 +423,184 @@ public class TrackerClient extends I2PAppThread
tr.interval = LONG_SLEEP; // slow down
}
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Not announcing to " + tr.announce + " last announce was " +
new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval));
}
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
maxSeenPeers = tr.seenPeers;
} // *** end of trackers loop here
// Get peers from PEX
if (left > 0 && coordinator.needPeers() && !stop) {
if (coordinator.needOutboundPeers() && (meta == null || !meta.isPrivate()) && !stop) {
Set<PeerID> pids = coordinator.getPEXPeers();
if (!pids.isEmpty()) {
_util.debug("Got " + pids.size() + " from PEX", Snark.INFO);
if (_log.shouldLog(Log.INFO))
_log.info("Got " + pids.size() + " from PEX");
List<Peer> peers = new ArrayList(pids.size());
for (PeerID pID : pids) {
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
}
Collections.shuffle(peers, r);
Iterator<Peer> it = peers.iterator();
while ((!stop) && it.hasNext()) {
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
Peer cur = it.next();
if (coordinator.addPeer(cur) && it.hasNext()) {
int delay = (DELAY_MUL * r.nextInt(10)) + DELAY_MIN;
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
}
}
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Not getting PEX peers");
}
// Get peers from DHT
// FIXME this needs to be in its own thread
if (_util.getDHT() != null && !stop) {
dht = _util.getDHT();
if (dht != null && (meta == null || !meta.isPrivate()) && (!stop) &&
_util.getContext().clock().now() > lastDHTAnnounce + MIN_DHT_ANNOUNCE_INTERVAL) {
int numwant;
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
if (event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
numwant = 1;
else
numwant = _util.getMaxConnections();
List<Hash> hashes = _util.getDHT().getPeers(snark.getInfoHash(), numwant, 2*60*1000);
_util.debug("Got " + hashes + " from DHT", Snark.INFO);
Collection<Hash> hashes = dht.getPeers(snark.getInfoHash(), numwant, 2*60*1000);
if (!hashes.isEmpty()) {
runStarted = true;
lastDHTAnnounce = _util.getContext().clock().now();
}
if (_log.shouldLog(Log.INFO))
_log.info("Got " + hashes + " from DHT");
// announce ourselves while the token is still good
// FIXME this needs to be in its own thread
if (!stop) {
int good = _util.getDHT().announce(snark.getInfoHash(), 8, 5*60*1000);
_util.debug("Sent " + good + " good announces to DHT", Snark.INFO);
// announce only to the 1 closest
int good = dht.announce(snark.getInfoHash(), 1, 5*60*1000);
if (_log.shouldLog(Log.INFO))
_log.info("Sent " + good + " good announces to DHT");
}
// now try these peers
if ((!stop) && !hashes.isEmpty()) {
List<Peer> peers = new ArrayList(hashes.size());
for (Hash h : hashes) {
PeerID pID = new PeerID(h.getData());
PeerID pID = new PeerID(h.getData(), _util);
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
}
Collections.shuffle(peers, r);
Iterator<Peer> it = peers.iterator();
while ((!stop) && it.hasNext()) {
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
Peer cur = it.next();
if (coordinator.addPeer(cur) && it.hasNext()) {
int delay = (DELAY_MUL * r.nextInt(10)) + DELAY_MIN;
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
}
}
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Not getting DHT peers");
}
// we could try and total the unique peers but that's too hard for now
snark.setTrackerSeenPeers(maxSeenPeers);
if (!runStarted)
_util.debug(" Retrying in one minute...", Snark.DEBUG);
if (stop)
return;
try {
// Sleep some minutes...
// Sleep the minimum interval for all the trackers, but 60s minimum
int delay;
int random = r.nextInt(120*1000);
if (completed && runStarted)
delay = 3*SLEEP*60*1000 + random;
else if (snark.getTrackerProblems() != null && ++consecutiveFails < MAX_CONSEC_FAILS)
delay = INITIAL_SLEEP;
else if ((!runStarted) && _runCount < MAX_CONSEC_FAILS)
delay = INITIAL_SLEEP;
else
// sleep a while, when we wake up we will contact only the trackers whose intervals have passed
delay = SLEEP*60*1000 + random;
if (delay > 20*1000) {
// put ourselves on SimpleTimer2
if (_log.shouldLog(Log.DEBUG))
_log.debug("Requeueing in " + DataHelper.formatDuration(delay) + ": " + Thread.currentThread().getName());
queueLoop(delay);
return;
} else if (delay > 0) {
Thread.sleep(delay);
}
} catch(InterruptedException interrupt) {}
} // *** end of while loop
} // try
catch (Throwable t)
{
_util.debug("TrackerClient: " + t, Snark.ERROR, t);
_log.error("TrackerClient: " + t, t);
if (t instanceof OutOfMemoryError)
throw (OutOfMemoryError)t;
}
finally
{
// Local DHT tracker unannounce
if (_util.getDHT() != null)
_util.getDHT().unannounce(snark.getInfoHash());
}
/**
* Creates a thread for each tracker in parallel if tunnel is still open
* @since 0.9.1
*/
private void unannounce() {
// Local DHT tracker unannounce
DHT dht = _util.getDHT();
if (dht != null)
dht.unannounce(snark.getInfoHash());
int i = 0;
for (Tracker tr : trackers) {
if (_util.connected() &&
tr.started && (!tr.stop) && tr.trackerProblems == null) {
try {
(new I2PAppThread(new Unannouncer(tr), _threadName + " U" + (++i), true)).start();
} catch (OutOfMemoryError oom) {
// probably ran out of threads, ignore
tr.reset();
}
} else {
tr.reset();
}
}
}
/**
* Send "stopped" to a single tracker
* @since 0.9.1
*/
private class Unannouncer implements Runnable {
private final Tracker tr;
public Unannouncer(Tracker tr) {
this.tr = tr;
}
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Running unannounce " + _threadName + " to " + tr.announce);
long uploaded = coordinator.getUploaded();
long downloaded = coordinator.getDownloaded();
long left = coordinator.getLeft();
try
{
// try to contact everybody we can
// Don't try to restart I2CP connection just to say goodbye
for (Iterator iter = trackers.iterator(); iter.hasNext(); ) {
if (!_util.connected()) return;
Tracker tr = (Tracker)iter.next();
if (tr.started && (!tr.stop) && tr.trackerProblems == null)
doRequest(tr, infoHash, peerID, uploaded,
if (_util.connected()) {
if (tr.started && (!tr.stop) && tr.trackerProblems == null)
doRequest(tr, infoHash, peerID, uploaded,
downloaded, left, STOPPED_EVENT);
}
}
}
catch(IOException ioe) { /* ignored */ }
}
tr.reset();
}
}
private TrackerInfo doRequest(Tracker tr, String infoHash,
@ -444,26 +608,40 @@ public class TrackerClient extends I2PAppThread
long downloaded, long left, String event)
throws IOException
{
// What do we send for left in magnet mode? Can we omit it?
long tleft = left >= 0 ? left : 1;
String s = tr.announce
+ "?info_hash=" + infoHash
+ "&peer_id=" + peerID
+ "&port=" + port
+ "&ip=" + _util.getOurIPString() + ".i2p"
+ "&uploaded=" + uploaded
+ "&downloaded=" + downloaded
+ "&left=" + tleft
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
s += "&numwant=0";
StringBuilder buf = new StringBuilder(512);
buf.append(tr.announce);
if (tr.announce.contains("?"))
buf.append('&');
else
s += "&numwant=" + _util.getMaxConnections();
_util.debug("Sending TrackerClient request: " + s, Snark.INFO);
buf.append('?');
buf.append("info_hash=").append(infoHash)
.append("&peer_id=").append(peerID)
.append("&port=").append(port)
.append("&ip=" ).append(_util.getOurIPString()).append(".i2p")
.append("&uploaded=").append(uploaded)
.append("&downloaded=").append(downloaded)
.append("&left=");
// What do we send for left in magnet mode? Can we omit it?
if (left >= 0)
buf.append(left);
else
buf.append('1');
buf.append("&compact=1"); // NOTE: opentracker will return 400 for &compact alone
if (! event.equals(NO_EVENT))
buf.append("&event=").append(event);
buf.append("&numwant=");
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
buf.append('0');
else
buf.append(_util.getMaxConnections());
String s = buf.toString();
if (_log.shouldLog(Log.INFO))
_log.info("Sending TrackerClient request: " + s);
tr.lastRequestTime = System.currentTimeMillis();
File fetched = _util.get(s);
// Don't wait for a response to stopped when shutting down
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
File fetched = _util.get(s, true, fast ? -1 : 0);
if (fetched == null) {
throw new IOException("Error fetching " + s);
}
@ -473,14 +651,15 @@ public class TrackerClient extends I2PAppThread
in = new FileInputStream(fetched);
TrackerInfo info = new TrackerInfo(in, snark.getID(),
snark.getInfoHash(), snark.getMetaInfo());
_util.debug("TrackerClient response: " + info, Snark.INFO);
snark.getInfoHash(), snark.getMetaInfo(), _util);
if (_log.shouldLog(Log.INFO))
_log.info("TrackerClient response: " + info);
String failure = info.getFailureReason();
if (failure != null)
throw new IOException(failure);
tr.interval = info.getInterval() * 1000;
tr.interval = Math.max(MIN_TRACKER_ANNOUNCE_INTERVAL, info.getInterval() * 1000l);
return info;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
@ -522,7 +701,7 @@ public class TrackerClient extends I2PAppThread
* @return true for i2p hosts only
* @since 0.7.12
*/
static boolean isValidAnnounce(String ann) {
public static boolean isValidAnnounce(String ann) {
URL url;
try {
url = new URL(ann);
@ -552,6 +731,13 @@ public class TrackerClient extends I2PAppThread
announce = a;
isPrimary = p;
interval = INITIAL_SLEEP;
}
/**
* Call before restarting
* @since 0.9.1
*/
public void reset() {
lastRequestTime = 0;
trackerProblems = null;
stop = false;

View File

@ -38,7 +38,7 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
* Compact format 1 - a list of hashes - early format for testing
* Compact format 2 - One big string of concatenated hashes - official format
*/
public class TrackerInfo
class TrackerInfo
{
private final String failure_reason;
private final int interval;
@ -47,19 +47,19 @@ public class TrackerInfo
private int incomplete;
/** @param metainfo may be null */
public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo)
public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
throws IOException
{
this(new BDecoder(in), my_id, infohash, metainfo);
this(new BDecoder(in), my_id, infohash, metainfo, util);
}
private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo)
private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
throws IOException
{
this(be.bdecodeMap().getMap(), my_id, infohash, metainfo);
this(be.bdecodeMap().getMap(), my_id, infohash, metainfo, util);
}
private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo)
private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
throws IOException
{
BEValue reason = (BEValue)m.get("failure reason");
@ -85,10 +85,10 @@ public class TrackerInfo
Set<Peer> p;
try {
// One big string (the official compact format)
p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo);
p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo, util);
} catch (InvalidBEncodingException ibe) {
// List of Dictionaries or List of Strings
p = getPeers(bePeers.getList(), my_id, infohash, metainfo);
p = getPeers(bePeers.getList(), my_id, infohash, metainfo, util);
}
peers = p;
}
@ -124,7 +124,7 @@ public class TrackerInfo
******/
/** List of Dictionaries or List of Strings */
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo)
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
throws IOException
{
Set<Peer> peers = new HashSet(l.size());
@ -138,7 +138,7 @@ public class TrackerInfo
try {
// Case 2 - compact - A list of 32-byte binary strings (hashes)
// This was just for testing and is not the official format
peerID = new PeerID(bev.getBytes());
peerID = new PeerID(bev.getBytes(), util);
} catch (InvalidBEncodingException ibe2) {
// don't let one bad entry spoil the whole list
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
@ -157,7 +157,7 @@ public class TrackerInfo
* One big string of concatenated 32-byte hashes
* @since 0.8.1
*/
private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo)
private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util)
throws IOException
{
int count = l.length / HASH_LENGTH;
@ -168,7 +168,7 @@ public class TrackerInfo
byte[] hash = new byte[HASH_LENGTH];
System.arraycopy(l, i * HASH_LENGTH, hash, 0, HASH_LENGTH);
try {
peerID = new PeerID(hash);
peerID = new PeerID(hash, util);
} catch (InvalidBEncodingException ibe) {
// won't happen
continue;
@ -195,6 +195,7 @@ public class TrackerInfo
return failure_reason;
}
/** in seconds */
public int getInterval()
{
return interval;

View File

@ -4,7 +4,7 @@ package org.klomp.snark.dht;
* GPLv2
*/
import java.util.List;
import java.util.Collection;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
@ -12,15 +12,21 @@ import net.i2p.data.Hash;
/**
* Stub for KRPC
* @since 0.8.4
*/
public interface DHT {
/**
* @return The UDP port that should be included in a PORT message.
* @return The UDP query port
*/
public int getPort();
/**
* @return The UDP response port
*/
public int getRPort();
/**
* Ping. We don't have a NID yet so the node is presumed
* to be absent from our DHT.
@ -37,9 +43,9 @@ public interface DHT {
* @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
* @return list or empty list (never null)
* @return possibly empty (never null)
*/
public List<Hash> getPeers(byte[] ih, int max, long maxWait);
public Collection<Hash> getPeers(byte[] ih, int max, long maxWait);
/**
* Announce to ourselves.
@ -79,4 +85,19 @@ public interface DHT {
* @return the number of successful announces, not counting ourselves.
*/
public int announce(byte[] ih, int max, long maxWait);
/**
* Stop everything.
*/
public void stop();
/**
* Known nodes, not estimated total network size.
*/
public int size();
/**
* Debug info, HTML formatted
*/
public String renderStatusHTML();
}

View File

@ -0,0 +1,166 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA1Hash;
import net.i2p.data.DataHelper;
import net.i2p.kademlia.KBucketSet;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
/**
* All the nodes we know about, stored as a mapping from
* node ID to a Destination and Port.
*
* And a real Kademlia routing table, which stores node IDs only.
*
* @since 0.9.2
* @author zzz
*/
class DHTNodes {
private final I2PAppContext _context;
private long _expireTime;
private final Log _log;
private final ConcurrentHashMap<NID, NodeInfo> _nodeMap;
private final KBucketSet<NID> _kad;
private volatile boolean _isRunning;
/** stagger with other cleaners */
private static final long CLEAN_TIME = 187*1000;
/** how long since last heard from do we delete - BEP 5 says 15 minutes */
private static final long MAX_EXPIRE_TIME = 30*60*1000;
private static final long MIN_EXPIRE_TIME = 10*60*1000;
private static final long DELTA_EXPIRE_TIME = 3*60*1000;
private static final int MAX_PEERS = 799;
/** Buckets older than this are refreshed - BEP 5 says 15 minutes */
private static final long MAX_BUCKET_AGE = 15*60*1000;
private static final int KAD_K = 8;
private static final int KAD_B = 1;
public DHTNodes(I2PAppContext ctx, NID me) {
_context = ctx;
_expireTime = MAX_EXPIRE_TIME;
_log = _context.logManager().getLog(DHTNodes.class);
_nodeMap = new ConcurrentHashMap();
_kad = new KBucketSet(ctx, me, KAD_K, KAD_B, new KBTrimmer(ctx, KAD_K));
}
public void start() {
_isRunning = true;
new Cleaner();
}
public void stop() {
clear();
_isRunning = false;
}
// begin ConcurrentHashMap methods
public int size() {
return _nodeMap.size();
}
public void clear() {
_kad.clear();
_nodeMap.clear();
}
public NodeInfo get(NID nid) {
return _nodeMap.get(nid);
}
/**
* @return the old value if present, else null
*/
public NodeInfo putIfAbsent(NodeInfo nInfo) {
_kad.add(nInfo.getNID());
return _nodeMap.putIfAbsent(nInfo.getNID(), nInfo);
}
public NodeInfo remove(NID nid) {
_kad.remove(nid);
return _nodeMap.remove(nid);
}
public Collection<NodeInfo> values() {
return _nodeMap.values();
}
// end ConcurrentHashMap methods
/**
* DHT
* @param h either a InfoHash or a NID
*/
public List<NodeInfo> findClosest(SHA1Hash h, int numWant) {
NID key;
if (h instanceof NID)
key = (NID) h;
else
key = new NID(h.getData());
List<NID> keys = _kad.getClosest(key, numWant);
List<NodeInfo> rv = new ArrayList(keys.size());
for (NID nid : keys) {
NodeInfo ninfo = _nodeMap.get(nid);
if (ninfo != null)
rv.add(ninfo);
}
return rv;
}
/**
* DHT - get random keys to explore
*/
public List<NID> getExploreKeys() {
return _kad.getExploreKeys(MAX_BUCKET_AGE);
}
/** */
private class Cleaner extends SimpleTimer2.TimedEvent {
public Cleaner() {
super(SimpleTimer2.getInstance(), CLEAN_TIME);
}
public void timeReached() {
if (!_isRunning)
return;
long now = _context.clock().now();
int peerCount = 0;
for (Iterator<NodeInfo> iter = DHTNodes.this.values().iterator(); iter.hasNext(); ) {
NodeInfo peer = iter.next();
if (peer.lastSeen() < now - _expireTime) {
iter.remove();
_kad.remove(peer.getNID());
} else {
peerCount++;
}
}
if (peerCount > MAX_PEERS)
_expireTime = Math.max(_expireTime - DELTA_EXPIRE_TIME, MIN_EXPIRE_TIME);
else
_expireTime = Math.min(_expireTime + DELTA_EXPIRE_TIME, MAX_EXPIRE_TIME);
if (_log.shouldLog(Log.DEBUG))
_log.debug("DHT storage cleaner done, now with " +
peerCount + " peers, " +
DataHelper.formatDuration(_expireTime) + " expiration");
schedule(CLEAN_TIME);
}
}
}

View File

@ -0,0 +1,168 @@
package org.klomp.snark.dht;
/*
* From zzzot, relicensed to GPLv2
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
/**
* The tracker stores peers, i.e. Dest hashes (not nodes).
*
* @since 0.9.2
* @author zzz
*/
class DHTTracker {
private final I2PAppContext _context;
private final Torrents _torrents;
private long _expireTime;
private final Log _log;
private volatile boolean _isRunning;
/** not current, updated by cleaner */
private int _peerCount;
/** not current, updated by cleaner */
private int _torrentCount;
/** stagger with other cleaners */
private static final long CLEAN_TIME = 199*1000;
private static final long MAX_EXPIRE_TIME = 45*60*1000;
private static final long MIN_EXPIRE_TIME = 15*60*1000;
private static final long DELTA_EXPIRE_TIME = 3*60*1000;
private static final int MAX_PEERS = 2000;
private static final int MAX_PEERS_PER_TORRENT = 150;
DHTTracker(I2PAppContext ctx) {
_context = ctx;
_torrents = new Torrents();
_expireTime = MAX_EXPIRE_TIME;
_log = _context.logManager().getLog(DHTTracker.class);
}
public void start() {
_isRunning = true;
new Cleaner();
}
void stop() {
_torrents.clear();
_isRunning = false;
}
void announce(InfoHash ih, Hash hash) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Announce " + hash + " for " + ih);
Peers peers = _torrents.get(ih);
if (peers == null) {
peers = new Peers();
Peers peers2 = _torrents.putIfAbsent(ih, peers);
if (peers2 != null)
peers = peers2;
}
Peer peer = new Peer(hash.getData());
Peer peer2 = peers.putIfAbsent(peer, peer);
if (peer2 != null)
peer = peer2;
peer.setLastSeen(_context.clock().now());
}
void unannounce(InfoHash ih, Hash hash) {
Peers peers = _torrents.get(ih);
if (peers == null)
return;
Peer peer = new Peer(hash.getData());
peers.remove(peer);
}
/**
* Caller's responsibility to remove himself from the list
* @return list or empty list (never null)
*/
List<Hash> getPeers(InfoHash ih, int max) {
Peers peers = _torrents.get(ih);
if (peers == null)
return Collections.EMPTY_LIST;
int size = peers.size();
List<Hash> rv = new ArrayList(peers.values());
if (max < size) {
Collections.shuffle(rv, _context.random());
rv = rv.subList(0, max);
}
return rv;
}
/**
* Debug info, HTML formatted
*/
public void renderStatusHTML(StringBuilder buf) {
buf.append("DHT tracker: ").append(_torrentCount).append(" torrents ")
.append(_peerCount).append(" peers ")
.append(DataHelper.formatDuration(_expireTime)).append(" expiration<br>");
}
private class Cleaner extends SimpleTimer2.TimedEvent {
public Cleaner() {
super(SimpleTimer2.getInstance(), CLEAN_TIME);
}
public void timeReached() {
if (!_isRunning)
return;
long now = _context.clock().now();
int torrentCount = 0;
int peerCount = 0;
for (Iterator<Peers> iter = _torrents.values().iterator(); iter.hasNext(); ) {
Peers p = iter.next();
int recent = 0;
for (Iterator<Peer> iterp = p.values().iterator(); iterp.hasNext(); ) {
Peer peer = iterp.next();
if (peer.lastSeen() < now - _expireTime)
iterp.remove();
else {
recent++;
peerCount++;
}
}
if (recent > MAX_PEERS_PER_TORRENT) {
// too many, delete at random
// TODO per-torrent adjustable expiration?
for (Iterator<Peer> iterp = p.values().iterator(); iterp.hasNext() && p.size() > MAX_PEERS_PER_TORRENT; ) {
iterp.next();
iterp.remove();
peerCount--;
}
torrentCount++;
} else if (recent <= 0) {
iter.remove();
} else {
torrentCount++;
}
}
if (peerCount > MAX_PEERS)
_expireTime = Math.max(_expireTime - DELTA_EXPIRE_TIME, MIN_EXPIRE_TIME);
else
_expireTime = Math.min(_expireTime + DELTA_EXPIRE_TIME, MAX_EXPIRE_TIME);
if (_log.shouldLog(Log.DEBUG))
_log.debug("DHT tracker cleaner done, now with " +
torrentCount + " torrents, " +
peerCount + " peers, " +
DataHelper.formatDuration(_expireTime) + " expiration");
_peerCount = peerCount;
_torrentCount = torrentCount;
schedule(CLEAN_TIME);
}
}
}

View File

@ -0,0 +1,29 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import net.i2p.crypto.SHA1Hash;
import org.klomp.snark.I2PSnarkUtil;
/**
* A 20-byte SHA1 info hash
*
* @since 0.9.2
* @author zzz
*/
class InfoHash extends SHA1Hash {
public InfoHash(byte[] data) {
super(data);
}
@Override
public String toString() {
if (_data == null) {
return super.toString();
} else {
return "[InfoHash: " + I2PSnarkUtil.toHex(_data) + ']';
}
}
}

View File

@ -0,0 +1,38 @@
package org.klomp.snark.dht;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.kademlia.KBucket;
import net.i2p.kademlia.KBucketTrimmer;
/**
* Removes an element older than 15 minutes, but only if the bucket hasn't changed in 5 minutes.
* @since 0.9.2
*/
class KBTrimmer implements KBucketTrimmer<NID> {
private final I2PAppContext _ctx;
private final int _max;
private static final long MIN_BUCKET_AGE = 5*60*1000;
private static final long MAX_NODE_AGE = 15*60*1000;
public KBTrimmer(I2PAppContext ctx, int max) {
_ctx = ctx;
_max = max;
}
public boolean trim(KBucket<NID> kbucket, NID toAdd) {
long now = _ctx.clock().now();
if (kbucket.getLastChanged() > now - MIN_BUCKET_AGE)
return false;
Set<NID> entries = kbucket.getEntries();
for (NID nid : entries) {
if (nid.lastSeen() < now - MAX_NODE_AGE) {
if (kbucket.remove(nid))
return true;
}
}
return entries.size() < _max;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
package org.klomp.snark.dht;
/*
* GPLv2
*/
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
/**
* Used for both incoming and outgoing message IDs
*
* @since 0.9.2
* @author zzz
*/
class MsgID extends ByteArray {
/** BEP 5: 2 bytes, incremented */
private static final int MY_TOK_LEN = 8;
private static final int MAX_TOK_LEN = 16;
/** outgoing - generate a random ID */
public MsgID(I2PAppContext ctx) {
super(null);
byte[] data = new byte[MY_TOK_LEN];
ctx.random().nextBytes(data);
setData(data);
setValid(MY_TOK_LEN);
}
/** incoming - save the ID (arbitrary length) */
public MsgID(byte[] data) {
super(data);
// lets not get carried away
if (data.length > MAX_TOK_LEN)
throw new IllegalArgumentException();
}
}

View File

@ -0,0 +1,46 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import net.i2p.crypto.SHA1Hash;
import net.i2p.util.Clock;
/**
* A 20-byte peer ID, used as a Map key in lots of places.
* Must be public for constructor in KBucketSet.generateRandomKey()
*
* @since 0.9.2
* @author zzz
*/
public class NID extends SHA1Hash {
private long lastSeen;
private int fails;
private static final int MAX_FAILS = 3;
public NID() {
super(null);
}
public NID(byte[] data) {
super(data);
}
public long lastSeen() {
return lastSeen;
}
public void setLastSeen() {
lastSeen = Clock.getInstance().now();
fails = 0;
}
/**
* @return if more than max timeouts
*/
public boolean timeout() {
return fails++ > MAX_FAILS;
}
}

View File

@ -0,0 +1,253 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.RandomSource;
/*
* A Node ID, Hash, and port, and an optional Destination.
* This is what DHTNodes remembers. The DHT tracker just stores Hashes.
* getData() returns the 54 byte compact info (NID, Hash, port).
*
* Things are a little tricky in KRPC since we exchange Hashes and don't
* always have the Destination.
* The conpact info is immutable. The Destination may be added later.
*
* @since 0.9.2
* @author zzz
*/
class NodeInfo extends SimpleDataStructure {
private final NID nID;
private final Hash hash;
private Destination dest;
private final int port;
public static final int LENGTH = NID.HASH_LENGTH + Hash.HASH_LENGTH + 2;
/**
* With a fake NID used for pings
*/
public NodeInfo(Destination dest, int port) {
super();
this.nID = KRPC.FAKE_NID;
this.dest = dest;
this.hash = dest.calculateHash();
this.port = port;
initialize();
}
/**
* Use this if we have the full destination
* @throws IllegalArgumentException
*/
public NodeInfo(NID nID, Destination dest, int port) {
super();
this.nID = nID;
this.dest = dest;
this.hash = dest.calculateHash();
this.port = port;
initialize();
verify();
}
/**
* No Destination yet available
* @throws IllegalArgumentException
*/
public NodeInfo(NID nID, Hash hash, int port) {
super();
this.nID = nID;
this.hash = hash;
this.port = port;
initialize();
verify();
}
/**
* No Destination yet available
* @param compactInfo 20 byte node ID, 32 byte destHash, 2 byte port
* @param offset starting at this offset in compactInfo
* @throws IllegalArgumentException
* @throws AIOOBE
*/
public NodeInfo(byte[] compactInfo, int offset) {
super();
byte[] d = new byte[LENGTH];
System.arraycopy(compactInfo, offset, d, 0, LENGTH);
setData(d);
byte[] ndata = new byte[NID.HASH_LENGTH];
System.arraycopy(d, 0, ndata, 0, NID.HASH_LENGTH);
this.nID = new NID(ndata);
this.hash = Hash.create(d, NID.HASH_LENGTH);
this.port = (int) DataHelper.fromLong(d, NID.HASH_LENGTH + Hash.HASH_LENGTH, 2);
if (port <= 0 || port >= 65535)
throw new IllegalArgumentException("Bad port");
verify();
}
/**
* Create from persistent storage string.
* Format: NID:Hash:Destination:port
* First 3 in base 64; Destination may be empty string
* @throws IllegalArgumentException
*/
public NodeInfo(String s) throws DataFormatException {
super();
String[] parts = s.split(":", 4);
if (parts.length != 4)
throw new DataFormatException("Bad format");
byte[] nid = Base64.decode(parts[0]);
if (nid == null)
throw new DataFormatException("Bad NID");
nID = new NID(nid);
byte[] h = Base64.decode(parts[1]);
if (h == null)
throw new DataFormatException("Bad hash");
//hash = new Hash(h);
hash = Hash.create(h);
if (parts[2].length() > 0)
dest = new Destination(parts[2]);
try {
port = Integer.parseInt(parts[3]);
} catch (NumberFormatException nfe) {
throw new DataFormatException("Bad port", nfe);
}
initialize();
}
/**
* Creates 54-byte compact info
* @throws IllegalArgumentException
*/
private void initialize() {
if (port <= 0 || port >= 65535)
throw new IllegalArgumentException("Bad port");
byte[] compactInfo = new byte[LENGTH];
System.arraycopy(nID.getData(), 0, compactInfo, 0, NID.HASH_LENGTH);
System.arraycopy(hash.getData(), 0, compactInfo, NID.HASH_LENGTH, Hash.HASH_LENGTH);
DataHelper.toLong(compactInfo, NID.HASH_LENGTH + Hash.HASH_LENGTH, 2, port);
setData(compactInfo);
}
/**
* Generate a secure NID that matches the Hash and port.
* Rules: First 4 bytes must match Hash.
* Next 2 bytes must match Hash ^ port.
* Remaining bytes may be random.
*
* @throws IllegalArgumentException
*/
public static NID generateNID(Hash h, int p, RandomSource random) {
byte[] n = new byte[NID.HASH_LENGTH];
System.arraycopy(h.getData(), 0, n, 0, 6);
n[4] ^= (byte) (p >> 8);
n[5] ^= (byte) p;
random.nextBytes(n, 6, NID.HASH_LENGTH - 6);
return new NID(n);
}
/**
* Verify the NID matches the Hash.
* See generateNID() for requirements.
* @throws IllegalArgumentException on mismatch
*/
private void verify() {
if (!KRPC.SECURE_NID)
return;
byte[] nb = nID.getData();
byte[] hb = hash.getData();
if ((!DataHelper.eq(nb, 0, hb, 0, 4)) ||
((nb[4] ^ (port >> 8)) & 0xff) != (hb[4] & 0xff) ||
((nb[5] ^ port) & 0xff) != (hb[5] & 0xff))
throw new IllegalArgumentException("NID/Hash mismatch");
}
public int length() {
return LENGTH;
}
public NID getNID() {
return this.nID;
}
/** @return may be null if we don't have it */
public Destination getDestination() {
return this.dest;
}
public Hash getHash() {
return this.hash;
}
@Override
public Hash calculateHash() {
return this.hash;
}
/**
* This can come in later but the hash must match.
* @throws IllegalArgumentException if hash of dest doesn't match previous hash
*/
public void setDestination(Destination dest) throws IllegalArgumentException {
if (this.dest != null)
return;
if (!dest.calculateHash().equals(this.hash))
throw new IllegalArgumentException("Hash mismatch, was: " + this.hash + " new: " + dest.calculateHash());
this.dest = dest;
}
public int getPort() {
return this.port;
}
public long lastSeen() {
return nID.lastSeen();
}
@Override
public int hashCode() {
return super.hashCode() ^ nID.hashCode() ^ port;
}
@Override
public boolean equals(Object o) {
try {
NodeInfo ni = (NodeInfo) o;
// assume dest matches, ignore it
return this.hash.equals(ni.hash) && nID.equals(ni.nID) && port == ni.port;
} catch (Exception e) {
return false;
}
}
@Override
public String toString() {
return "NodeInfo: " + nID + ' ' + hash + " port: " + port + (dest != null ? " known dest" : " null dest");
}
/**
* To persistent storage string.
* Format: NID:Hash:Destination:port
* First 3 in base 64; Destination may be empty string
*/
public String toPersistentString() {
StringBuilder buf = new StringBuilder(650);
buf.append(nID.toBase64()).append(':');
buf.append(hash.toBase64()).append(':');
if (dest != null)
buf.append(dest.toBase64());
buf.append(':').append(port);
return buf.toString();
}
}

View File

@ -0,0 +1,31 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import java.util.Comparator;
import net.i2p.crypto.SHA1Hash;
import net.i2p.data.DataHelper;
/**
* Closest to a InfoHash or NID key.
* Use for NodeInfos.
*
* @since 0.9.2
* @author zzz
*/
class NodeInfoComparator implements Comparator<NodeInfo> {
private final byte[] _base;
public NodeInfoComparator(SHA1Hash h) {
_base = h.getData();
}
public int compare(NodeInfo lhs, NodeInfo rhs) {
byte lhsDelta[] = DataHelper.xor(lhs.getNID().getData(), _base);
byte rhsDelta[] = DataHelper.xor(rhs.getNID().getData(), _base);
return DataHelper.compareTo(lhsDelta, rhsDelta);
}
}

View File

@ -0,0 +1,30 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import net.i2p.data.Hash;
/**
* A single peer for a single torrent.
* This is what the DHT tracker remembers.
*
* @since 0.9.2
* @author zzz
*/
class Peer extends Hash {
private long lastSeen;
public Peer(byte[] data) {
super(data);
}
public long lastSeen() {
return lastSeen;
}
public void setLastSeen(long now) {
lastSeen = now;
}
}

View File

@ -0,0 +1,21 @@
package org.klomp.snark.dht;
/*
* From zzzot, modded and relicensed to GPLv2
*/
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.data.Hash;
/**
* All the peers for a single torrent
*
* @since 0.9.2
* @author zzz
*/
class Peers extends ConcurrentHashMap<Hash, Peer> {
public Peers() {
super(8);
}
}

View File

@ -0,0 +1,85 @@
package org.klomp.snark.dht;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
/**
* Retrieve / Store the local DHT in a file
*
* @since 0.9.2
*/
abstract class PersistDHT {
private static final long MAX_AGE = 60*60*1000;
public static synchronized void loadDHT(KRPC krpc, File file) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
int count = 0;
FileInputStream in = null;
try {
in = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
String line = null;
while ( (line = br.readLine()) != null) {
if (line.startsWith("#"))
continue;
try {
krpc.heardAbout(new NodeInfo(line));
count++;
// TODO limit number? this will flush the router's SDS caches
} catch (IllegalArgumentException iae) {
if (log.shouldLog(Log.WARN))
log.warn("Error reading DHT entry", iae);
} catch (DataFormatException dfe) {
if (log.shouldLog(Log.WARN))
log.warn("Error reading DHT entry", dfe);
}
}
} catch (IOException ioe) {
if (log.shouldLog(Log.WARN) && file.exists())
log.warn("Error reading the DHT File", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
if (log.shouldLog(Log.INFO))
log.info("Loaded " + count + " nodes from " + file);
}
public static synchronized void saveDHT(DHTNodes nodes, File file) {
if (nodes.size() <= 0)
return;
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
int count = 0;
long maxAge = I2PAppContext.getGlobalContext().clock().now() - MAX_AGE;
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "ISO-8859-1")));
out.println("# DHT nodes, format is NID:Hash:Destination:port");
for (NodeInfo ni : nodes.values()) {
if (ni.lastSeen() < maxAge)
continue;
// DHTNodes shouldn't contain us, if that changes check here
out.println(ni.toPersistentString());
count++;
}
} catch (IOException ioe) {
if (log.shouldLog(Log.WARN))
log.warn("Error writing the DHT File", ioe);
} finally {
if (out != null) out.close();
}
if (log.shouldLog(Log.INFO))
log.info("Stored " + count + " nodes to " + file);
}
}

View File

@ -0,0 +1,75 @@
package org.klomp.snark.dht;
/*
* GPLv2
*/
import java.util.Date;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
/**
* Used for Both outgoing and incoming tokens
*
* @since 0.9.2
* @author zzz
*/
class Token extends ByteArray {
private static final int MY_TOK_LEN = 8;
private static final int MAX_TOK_LEN = 64;
private final long lastSeen;
/** outgoing - generate a random token */
public Token(I2PAppContext ctx) {
super(null);
byte[] data = new byte[MY_TOK_LEN];
ctx.random().nextBytes(data);
setData(data);
setValid(MY_TOK_LEN);
lastSeen = ctx.clock().now();
}
/** incoming - save the token (arbitrary length) */
public Token(I2PAppContext ctx, byte[] data) {
super(data);
// lets not get carried away
if (data.length > MAX_TOK_LEN)
throw new IllegalArgumentException();
lastSeen = ctx.clock().now();
}
/** incoming - for lookup only, not storage, lastSeen is 0 */
public Token(byte[] data) {
super(data);
lastSeen = 0;
}
public long lastSeen() {
return lastSeen;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[Token: ");
byte[] bs = getData();
if (bs.length == 0) {
buf.append("0 bytes");
} else {
buf.append(bs.length).append(" bytes: 0x");
// backwards, but the same way BEValue does it
for (int i = 0; i < bs.length; i++) {
int b = bs[i] & 0xff;
if (b < 16)
buf.append('0');
buf.append(Integer.toHexString(b));
}
}
if (lastSeen > 0)
buf.append(" created ").append((new Date(lastSeen)).toString());
buf.append(']');
return buf.toString();
}
}

View File

@ -0,0 +1,20 @@
package org.klomp.snark.dht;
/*
* GPLv2
*/
import net.i2p.crypto.SHA1Hash;
import net.i2p.data.DataHelper;
/**
* Used to index incoming Tokens
*
* @since 0.9.2
* @author zzz
*/
class TokenKey extends SHA1Hash {
public TokenKey(NID nID, InfoHash ih) {
super(DataHelper.xor(nID.getData(), ih.getData()));
}
}

View File

@ -0,0 +1,19 @@
package org.klomp.snark.dht;
/*
* From zzzot, relicensed to GPLv2
*/
import java.util.concurrent.ConcurrentHashMap;
/**
* All the torrents
*
* @since 0.9.2
* @author zzz
*/
class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
public Torrents() {
super();
}
}

View File

@ -0,0 +1,347 @@
package org.klomp.snark.web;
/*
* Released into the public domain
* with no warranty of any kind, either expressed or implied.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocketEepGet;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.crypto.SHA1;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
import org.klomp.snark.MetaInfo;
import org.klomp.snark.Snark;
import org.klomp.snark.SnarkManager;
import org.klomp.snark.Storage;
/**
* A cancellable torrent file downloader.
* We extend Snark so its status may be easily listed in the
* web table without adding a lot of code there.
*
* Upon successful download, this Snark will be deleted and
* a "real" Snark created.
*
* The methods return values similar to a Snark in magnet mode.
* A fake info hash, which is the SHA1 of the URL, is returned
* to prevent duplicates.
*
* This Snark may be stopped and restarted, although a partially
* downloaded file is discarded.
*
* @since 0.9.1 Moved from I2PSnarkUtil
*/
public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnable {
private final I2PAppContext _ctx;
private final Log _log;
private final SnarkManager _mgr;
private final String _url;
private final byte[] _fakeHash;
private final String _name;
private volatile long _remaining = -1;
private volatile long _total = -1;
private volatile long _transferred;
private volatile boolean _isRunning;
private volatile boolean _active;
private volatile long _started;
private String _failCause;
private Thread _thread;
private EepGet _eepGet;
private static final int RETRIES = 3;
/**
* Caller should call _mgr.addDownloader(this), which
* will start things off.
*/
public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url) {
// magnet constructor
super(mgr.util(), "Torrent download",
null, null, null, null, null, false, null);
_ctx = ctx;
_log = ctx.logManager().getLog(FetchAndAdd.class);
_mgr = mgr;
_url = url;
_name = _("Download torrent file from {0}", url);
byte[] fake = null;
try {
fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1"));
} catch (IOException ioe) {}
_fakeHash = fake;
}
/**
* Set off by startTorrent()
*/
public void run() {
_mgr.addMessage(_("Fetching {0}", urlify(_url)));
File file = get();
if (!_isRunning) // stopped?
return;
_isRunning = false;
if (file != null && file.exists() && file.length() > 0) {
// remove this in snarks
_mgr.deleteMagnet(this);
add(file);
} else {
_mgr.addMessage(_("Torrent was not retrieved from {0}", urlify(_url)) +
((_failCause != null) ? (": " + _failCause) : ""));
}
if (file != null)
file.delete();
}
/**
* Copied from I2PSnarkUtil so we may add ourselves as a status listener
* @return null on failure
*/
private File get() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetching [" + _url + "]");
File out = null;
try {
out = SecureFile.createTempFile("torrentFile", null, _mgr.util().getTempDir());
} catch (IOException ioe) {
_log.error("temp file error", ioe);
_mgr.addMessage("Temp file error: " + ioe);
if (out != null)
out.delete();
return null;
}
out.deleteOnExit();
if (!_mgr.util().connected()) {
_mgr.addMessage(_("Opening the I2P tunnel"));
if (!_mgr.util().connect())
return null;
}
I2PSocketManager manager = _mgr.util().getSocketManager();
if (manager == null)
return null;
_eepGet = new I2PSocketEepGet(_ctx, manager, RETRIES, out.getAbsolutePath(), _url);
_eepGet.addStatusListener(this);
if (_eepGet.fetch()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetch successful [" + _url + "]: size=" + out.length());
return out;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetch failed [" + _url + ']');
out.delete();
return null;
}
}
/**
* Tell SnarkManager to copy the torrent file over and add it to the Snarks list.
* This Snark may then be deleted.
*/
private void add(File file) {
_mgr.addMessage(_("Torrent fetched from {0}", urlify(_url)));
FileInputStream in = null;
try {
in = new FileInputStream(file);
byte[] fileInfoHash = new byte[20];
String name = MetaInfo.getNameAndInfoHash(in, fileInfoHash);
try { in.close(); } catch (IOException ioe) {}
Snark snark = _mgr.getTorrentByInfoHash(fileInfoHash);
if (snark != null) {
_mgr.addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
return;
}
name = Storage.filterName(name);
name = name + ".torrent";
File torrentFile = new File(_mgr.getDataDir(), name);
String canonical = torrentFile.getCanonicalPath();
if (torrentFile.exists()) {
if (_mgr.getTorrent(canonical) != null)
_mgr.addMessage(_("Torrent already running: {0}", name));
else
_mgr.addMessage(_("Torrent already in the queue: {0}", name));
} else {
// This may take a LONG time to create the storage.
_mgr.copyAndAddTorrent(file, canonical);
}
} catch (IOException ioe) {
_mgr.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
} catch (OutOfMemoryError oom) {
_mgr.addMessage(_("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + oom.getMessage());
} finally {
try { if (in != null) in.close(); } catch (IOException ioe) {}
}
}
// Snark overrides so all the buttons and stats on the web page work
@Override
public synchronized void startTorrent() {
if (_isRunning)
return;
// reset counters in case starting a second time
_remaining = -1;
// leave the total if we knew it before
//_total = -1;
_transferred = 0;
_failCause = null;
_started = _ctx.clock().now();
_isRunning = true;
_active = false;
_thread = new I2PAppThread(this, "Torrent File EepGet", true);
_thread.start();
}
@Override
public synchronized void stopTorrent() {
if (_thread != null && _isRunning) {
if (_eepGet != null)
_eepGet.stopFetching();
_thread.interrupt();
}
_isRunning = false;
_active = false;
}
@Override
public boolean isStopped() {
return !_isRunning;
}
@Override
public String getName() {
return _name;
}
@Override
public String getBaseName() {
return _name;
}
@Override
public byte[] getInfoHash() {
return _fakeHash;
}
/**
* @return torrent file size or -1
*/
@Override
public long getTotalLength() {
return _total;
}
/**
* @return -1 when done so the web will list us as "complete" instead of "seeding"
*/
@Override
public long getRemainingLength() {
long rv = _remaining;
return rv > 0 ? rv : -1;
}
/**
* @return torrent file bytes remaining or -1
*/
@Override
public long getNeededLength() {
return _remaining;
}
@Override
public long getDownloadRate() {
if (_isRunning && _active) {
long time = _ctx.clock().now() - _started;
if (time > 1000) {
long rv = (_transferred * 1000) / time;
if (rv >= 100)
return rv;
}
}
return 0;
}
@Override
public long getDownloaded() {
return _total - _remaining;
}
@Override
public int getPeerCount() {
return (_isRunning && _active && _transferred > 0) ? 1 : 0;
}
@Override
public int getTrackerSeenPeers() {
return (_transferred > 0) ? 1 : 0;
}
// End Snark overrides
// EepGet status listeners to maintain the state for the web page
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
}
_transferred = bytesTransferred;
if (cause != null)
_failCause = cause.toString();
_active = false;
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
_total = bytesRemaining + currentWrite + alreadyTransferred;
}
_transferred = bytesTransferred;
_active = true;
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
_total = bytesRemaining + alreadyTransferred;
}
_transferred = bytesTransferred;
_active = false;
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
if (bytesRemaining >= 0) {
_remaining = bytesRemaining;
}
_transferred = bytesTransferred;
_active = false;
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
// End of EepGet status listeners
private String _(String s) {
return _mgr.util().getString(s);
}
private String _(String s, String o) {
return _mgr.util().getString(s, o);
}
private static String urlify(String s) {
return I2PSnarkServlet.urlify(s);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,6 @@ import net.i2p.util.FileUtil;
import org.mortbay.jetty.Server;
public class RunStandalone {
private Server _server;
static {
System.setProperty("org.mortbay.http.Version.paranoid", "true");
System.setProperty("org.mortbay.xml.XmlParser.NotValidating", "true");
@ -31,6 +29,8 @@ public class RunStandalone {
if (!workDirCreated)
System.err.println("ERROR: Unable to create Jetty temporary work directory");
throw new RuntimeException("unsupported");
/****
try {
_server = new Server("jetty-i2psnark.xml");
// just blow up NPE if we don't have a context
@ -39,13 +39,17 @@ public class RunStandalone {
} catch (Exception e) {
e.printStackTrace();
}
****/
}
public void stop() {
throw new RuntimeException("unsupported");
/****
try {
_server.stop();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
****/
}
}

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