Compare commits

...

114 Commits

Author SHA1 Message Date
4ce6b308b3 * 2005-08-03 0.6.0.1 released
2005-08-03  jrandom
    * Backed out an inadvertant change to the netDb store redundancy factor.
    * Verify tunnel participant caching.
    * Logging cleanup
2005-08-03 18:58:12 +00:00
72c6e7d1c5 2005-08-01 duck
* Update IzPack to 3.7.2 (build 2005.04.22). This fixes bug #82.
2005-08-02 03:26:51 +00:00
7ca3f22e77 2005-08-01 duck
* Update IzPack to 3.7.2 (build 2005.04.22)
      This fixes bug #82
2005-08-02 03:25:51 +00:00
59790dafef 2005-08-01 duck
* Fix an addressbook NPE when a new hostname from the master addressbook
      didn't exist in the router addressbook.
    * Fix an addressbook bug which caused subscriptions not to be parsed at
      all. (Oops!)
2005-08-01 13:35:11 +00:00
7227cae6ef 2005-08-01 duck
* Fix an addressbook NPE when a new hostname from the master addressbook
      didn't exist in the router addressbook.
    * Fix an addressbook bug which caused subscriptions not to be parsed at
      all. (Oops!)
2005-08-01 13:34:10 +00:00
03bba51c1e * Fixed some issues with the merge logic that caused addressbooks to be written to disk even when unmodified.
* Fixed a bug that could result in a downloaded remote addressbook not being deleted, halting the update process.
2005-08-01 03:32:37 +00:00
0637050cbc No real reason for eepget to retry, addressbook will try again in an hour, and it makes updates take an absurdly long time. 2005-08-01 00:10:54 +00:00
7f58a68c5a Whoops! Forgot the new build file. I broke cvs! 2005-07-31 22:49:25 +00:00
8120b0397c Move addressbook off URL and on to EepGet. Should no longer leak dns lookups, but now only supports conditional GET with HTTP 1.1. If that's a big problem, it can be fixed in future. 2005-07-31 22:19:10 +00:00
fbe42b7dce Added HTTP 1.1 conditional GET support to EepGet. 2005-07-31 22:17:10 +00:00
def24e34ad 2005-07-31 jrandom
* Adjust the netDb search and store per peer timeouts to match the average
      measured per peer success times, rather than huge fixed values.
    * Optimized and reverified the netDb peer selection / retrieval process
      within the kbuckets.
    * Drop TCP connections that don't have any useful activity in 10 minutes.
    * If i2np.udp.fixedPort=true, never change the externally published port,
      even if we are autodetecting the IP address.
(also includes most of the new peer/NAT testing, but thats not used atm)
2005-07-31 21:35:26 +00:00
593253e6a3 update compilation target 2005-07-31 01:11:12 +00:00
56dd4cb8b5 * added luckypunk.i2p to hosts.txt 2005-07-30 02:27:12 +00:00
10c6f67500 oops 2005-07-28 20:33:27 +00:00
5c1f968afa no message 2005-07-27 20:16:44 +00:00
aaaf437d62 skip properly (DataHelper.read confusion) 2005-07-27 20:15:35 +00:00
a8a866b5f6 * 2005-07-27 0.6 released
2005-07-27  jrandom
    * Enabled SSU as the default top priority transport, adjusting the
      config.jsp page accordingly.
    * Add verification fields to the SSU and TCP connection negotiation (not
      compatible with previous builds)
    * Enable the backwards incompatible tunnel crypto change as documented in
      tunnel-alt.html (have each hop encrypt the received IV before using it,
      then encrypt it again before sending it on)
    * Disable the I2CP encryption, leaving in place the end to end garlic
      encryption (another backwards incompatible change)
    * Adjust the protocol versions on the TCP and SSU transports so that they
      won't talk to older routers.
    * Fix up the config stats handling again
    * Fix a rare off-by-one in the SSU fragmentation
    * Reduce some unnecessary netDb resending by inluding the peers queried
      successfully in the store redundancy count.
2005-07-27 19:03:43 +00:00
aeb8f02269 2005-07-22 jrandom
* Use the small thread pool for I2PTunnelHTTPServer (already used for
      I2PTunnelServer)
    * Minor memory churn reduction in I2CP
    * Small stats update
2005-07-23 00:15:56 +00:00
45767360ab 2005-07-21 jrandom
* Fix in the SDK for a bug which would manifest itself as misrouted
      streaming packets when a destination has many concurrent streaming
      connections (thanks duck!)
    * No more "Graceful shutdown in -18140121441141s"
2005-07-21 22:37:14 +00:00
3563aa2e4d 2005-07-20 jrandom
* Allow the user to specify an external port # for SSU even if the external
      host isn't specified (thanks duck!)
2005-07-20 19:24:47 +00:00
843d5b625a 2005-07-19 jrandom
* Further preparation for removing I2CP crypto
    * Added some validation to the DH key agreement (thanks $anon)
    * Validate tunnel data message expirations (though not really a problem,
      since tunnels expire)
    * Minor PRNG threading cleanup
2005-07-19 21:00:25 +00:00
0f8ede85ca 2005-07-15 cervantes
* Added workaround for an odd win32 bug in the stats configuration
	  console page which meant only the first checkbox selection was saved.

2005-07-15  Romster
	* Added per group selection toggles in the stats configuration console
	  page.
2005-07-16 12:52:35 +00:00
9267d7cae2 more n3ws 2005-07-13 21:59:01 +00:00
dade5a981b 2005-07-13 jrandom
* Fixed a recently injected bug in the multitransport bidding which had
      allowed an essentially arbitrary choice of transports, rather than the
      properly ordered choice.
(getLatency() != getLatencyMs().  duh)
2005-07-13 20:07:31 +00:00
f873cba27e 2005-07-13 jrandom
* Fixed a long standing bug where we weren't properly comparing session
      tags but instead largely depending upon comparing their hashCode,
      causing intermittent decryption errors.
2005-07-13 18:20:43 +00:00
108dec53a5 * mixing a revert and some logging updates... (crosses fingers) 2005-07-12 22:30:13 +00:00
e9592ed400 2005-07-12 jrandom
* Add some data duplication to avoid a recently injected concurrency problem
      in the session tag manager (thanks redzara and romster).
2005-07-12 21:26:07 +00:00
4c230522a2 typo *ahem* 2005-07-12 03:56:42 +00:00
16bd19c6dc added bash.i2p, stats.i2p 2005-07-11 23:23:22 +00:00
b4b6d49d34 ssu testing 2005-07-11 23:16:41 +00:00
9d5f16a889 2005-07-11 jrandom
* Reduced the growth factor on the slow start and congestion avoidance for
      the streaming lib.
    * Adjusted some of the I2PTunnelServer threading to use a small pool of
      handlers, rather than launching off new threads which then immediately
      launch off an I2PTunnelRunner instance (which launches 3 more threads..)
    * Don't persist session keys / session tags (not worth it, for now)
    * Added some detection and handling code for duplicate session tags being
      delivered (root cause still not addressed)
    * Make the PRNG's buffer size configurable (via the config property
      "i2p.prng.totalBufferSizeKB=4096")
    * Disable SSU flooding by default (duh)
    * Updates to the StreamSink apps for better throttling tests.
2005-07-11 23:06:23 +00:00
51c492b842 no message 2005-07-09 23:02:19 +00:00
d3380228ac * you mean 3f != 0x3f? [duh]
* minor cleanups
2005-07-09 22:58:22 +00:00
ad47bf5da3 * moved the inbound partial messages to the PeerState itself, reducing lock contention in the InboundMessageFragments and transparently dropping failed messages when we drop old peer states 2005-07-07 22:27:44 +00:00
76e8631e31 included IV tagging info 2005-07-07 21:16:57 +00:00
f688b9112d 2005-07-05
* Use a buffered PRNG, pulling the PRNG data off a larger precalculated
      buffer, rather than the underlying PRNG's (likely small) one, which in
      turn reduces the frequency of recalcing.
    * More tuning to reduce temporary allocation churn
2005-07-05 22:08:56 +00:00
18d3f5d25d 2005-07-04 jrandom
* Within the tunnel, use xor(IV, msg[0:16]) as the flag to detect dups,
      rather than the IV by itself, preventing an attack that would let
      colluding internal adversaries tag a message to determine that they are
      in the same tunnel.  Thanks dvorak for the catch!
    * Drop long inactive profiles on startup and shutdown
    * /configstats.jsp: web interface to pick what stats to log
    * Deliver more session tags to account for wider window sizes
    * Cache some intermediate values in our HMACSHA256 and BC's HMAC
    * Track the client send rate (stream.sendBps and client.sendBpsRaw)
    * UrlLauncher: adjust the browser selection order
    * I2PAppContext: hooks for dummy HMACSHA256 and a weak PRNG
    * StreamSinkClient: add support for sending an unlimited amount of data
    * Migrate the tests out of the default build jars

2005-06-22  Comwiz
    * Migrate the core tests to junit
2005-07-04 20:44:17 +00:00
440cf2c983 2005-03-23 Comwiz
* Phase 1 of the unit test bounty completed. (The router build script was modified not to build the router
 tests because of a broken dependancy on the core tests. This should be fixed in
 phase 3 of the unit test bounty.)
2005-06-23 02:11:04 +00:00
adeb09576a util/PooledRandomSource.java 2005-06-03 20:23:32 +00:00
fd52bcf8cd added archive.i2p, www.fr.i2p, romster.i2p, marshmallow.i2p, openforums.i2p 2005-05-26 04:51:24 +00:00
c2696bba00 2005-05-25 duck
* Fixed PRNG bug (bugzilla #107)
2005-05-25 21:32:38 +00:00
fef9d57483 removed duplicate manveru.i2p 2005-05-10 05:53:18 +00:00
c250692ef0 added bittorrent.i2p - new home for brittanytracker 2005-05-04 05:52:55 +00:00
2a6024e196 end of first round of ssu testing 2005-05-03 00:53:53 +00:00
835662b3c9 2005-05-01 jrandom
* Added a substantial optimization to the AES engine by caching the
      prepared session keys (duh).
2005-05-02 02:35:16 +00:00
6b5b880ab6 * replaced explicit NACKs and numACKs with ACK bitfields for high congestion links
* increased the maximum number of fragments allowed in a message from 31 to 127,
  reducing the maximum fragment size to 8KB and moving around some bits in the fragment
  info.  This is not backwards compatible.
* removed the old (hokey) congestion control description, replacing it with the TCP-esque
  algorithm implemented
note: the code for the ACK bitfields and fragment info changes have not yet been
implemented, so the old version of this document describes whats going on in the live net.
the new bitfields / fragment info should be deployed in the next day or so (hopefully :)
2005-05-01 20:08:08 +00:00
3de23d4206 2005-05-01 jrandom
* Cleaned up the peers page a bit more.
more udp stuff:
* add new config option: i2np.udp.alwaysPreferred=true to adjust the bidding
  so that UDP is picked first, even if a TCP connection exists
* fixed the initial clock skew problem (duh)
* reduced the MTU to 576 (largest nearly-universally-safe, and allows a
  tunnel message in 2 fragments)
* handle some races @ connection establishment (thanks duck!)
* if there are more ACKs than we can send in a packet, reschedule another
  ACK immediately
2005-05-01 17:21:48 +00:00
ea82f2a8cc oops (thanks newkid!) 2005-05-01 01:35:23 +00:00
b5ad7642bc 2005-04-30 jrandom
* Added a small new page to the web console (/peers.jsp) which contains
      the peer connection information.  This will be cleaned up a lot more
      before 0.6 is out, but its a start.
2005-05-01 00:48:15 +00:00
0fbe84e9f0 2005-04-30 jrandom
* Reduced some SimpleTimer churn
* add hooks for per-peer choking in the outbound message queue - if/when a
  peer reaches their cwin, no further messages will enter the 'active' pool
  until there are more bytes available.  other messages waiting (either later
  on in the same priority queue, or in the queues for other priorities) may
  take that slot.
* when we have a message acked, release the acked size to the congestion
  window (duh), rather than waiting for the second to expire and refill the
  capacity.
* send packets in a volley explicitly, waiting until we can allocate the full
  cwin size for that message
2005-04-30 23:26:18 +00:00
8063889d23 udp updates:
* more stats. including per-peer KBps (updated every second)
* improved blocking/timeout situations on the send queue
* added drop simulation hook
* provide logical RTO limits
2005-04-30 03:14:09 +00:00
6e1ac8e173 added elf.i2p, de-ebooks.i2p, i2pchan.i2p, longhorn.i2p 2005-04-29 22:26:12 +00:00
1b0bb5ea19 2005-04-29 jrandom
* Reduce the peer profile stat coallesce overhead by inlining it with the
      reorganize.
    * Limit each transport to at most one address (any transport that requires
      multiple entry points can include those alternatives in the address).
udp stuff:
* change the UDP transport's style from "udp" to "SSUv1"
* keep track of each peer's skew
* properly handle session reestablishment over an existing session, rather
  than requiring both sides to expire first
2005-04-29 06:24:12 +00:00
4ce51261f1 2005-04-28 jrandom
* More fixes for the I2PTunnel "other" interface handling (thanks nelgin!)
    * Add back the code to handle bids from multiple transports (though there
      is still only one transport enabled by default)
    * Adjust the router's queueing of outbound client messages when under
      heavy load by running the preparatory job in the client's I2CP handler
      thread, thereby blocking additional outbound messages when the router is
      hosed.
    * No need to validate or persist a netDb entry if we already have it
And for some udp stuff:
* only bid on what we know (duh)
* reduceed the queue size in the UDPSender itself, so that ACKs go
  through more quickly, leaving the payload messages to queue up in
  the outbound fragment scheduler
* rather than /= 2 on congestion, /= 2/3 (still AIMD, but less drastic)
* adjust the fragment selector so a wsiz throttle won't force extra
  volleys
* mark congestion when it occurs, not after the message has been
  ACKed
* when doing a round robin over the active messages, move on to the
  next after a full volley, not after each packet (causing less "fair"
  performance but better latency)
* reduced the lock contention in the inboundMessageFragments by
  moving the ack and complete queues to the ACKSender and
  MessageReceiver respectively (each of which have their own
  threads)
* prefer new and existing UDP sessions to new TCP sessions, but
  prefer existing TCP sessions to new UDP sessions
2005-04-28 21:54:27 +00:00
6e34d9b73e added amobius.i2p 2005-04-28 02:11:02 +00:00
6e01637400 added google.i2p 2005-04-27 21:30:53 +00:00
9a96798f9f added mrplod.i2p 2005-04-27 03:58:00 +00:00
c9db6f87d1 2005-04-25 smeghead
* Added button to router console for manual update checks.
    * Fixed bug in configupdate.jsp that caused the proxy port to be updated
      every time the form was submitted even if it hadn't changed.
2005-04-26 02:59:23 +00:00
567ce84e1e * randomized the shitlist duration (still with exponential backoff though)
* fail UDP sessions after two consecutive failed messages in different minutes
* honor UDP reconnections
2005-04-25 16:29:48 +00:00
cde7ac7e52 2005-04-24 jrandom
* Added a pool of PRNGs using a different synchronization technique,
      hopefully sufficient to work around IBM's PRNG bugs until we get our
      own Fortuna.
    * In the streaming lib, don't jack up the RTT on NACK, and have the window
      size bound the not-yet-ready messages to the peer, not the unacked
      message count (not sure yet whether this is worthwile).
    * Many additions to the messageHistory log.
    * Handle out of order tunnel fragment delivery (not an issue on the live
      net with TCP, but critical with UDP).
2005-04-24 18:44:59 +00:00
b2f0d17e94 2005-04-24 jrandom
* Added a pool of PRNGs using a different synchronization technique,
      hopefully sufficient to work around IBM's PRNG bugs until we get our
      own Fortuna.
    * In the streaming lib, don't jack up the RTT on NACK, and have the window
      size bound the not-yet-ready messages to the peer, not the unacked
      message count (not sure yet whether this is worthwile).
    * Many additions to the messageHistory log.
    * Handle out of order tunnel fragment delivery (not an issue on the live
      net with TCP, but critical with UDP).
and for udp stuff:
* implemented tcp-esque rto code in the udp transport
* make sure we don't ACK too many messages at once
* transmit fragments in a simple (nonrandom) order so that we can more easily
  adjust timeouts/etc.
* let the active outbound pool grow dynamically if there are outbound slots to
  spare
* use a simple decaying bloom filter at the UDP level to drop duplicate resent
  packets.
2005-04-24 18:42:02 +00:00
dae6be14b7 I removed those dumb platform specific makefiles. They weren't doing what they ought anyway. If there are platform specific issues, someone please tell me and I'll provide support for it here. Or patch it yourself.
And this is the big "Fix the Parser" patch.  It turns the sam_parse function in src/parse.c into something that actually works.  Generating the argument list from an incoming SAM thingy is a bit memory churn-y; perhaps when I have time I'll replace all those strdups with structures that simply track the (start,end) indices.
Oh and also I moved i2p-ping to the new system.  Which required 0 change in code.  All I did was fix the Makefile, and add shared library libtool support.  Anyway, so enjoy folks.  It's rare I'm this productive
- polecat
2005-04-23 03:28:40 +00:00
20cec857d2 signed with the latest 2005-04-21 16:26:46 +00:00
aum
739f694cfe Node shutdown now uses halt() 2005-04-21 03:10:16 +00:00
aum
84779002fb now builds a working Q console 2005-04-20 21:35:05 +00:00
df926fb60d * 2005-04-20 0.5.0.7 released 2005-04-20 20:14:17 +00:00
a2c7c5a516 2005-04-20 jrandom
* In the SDK, we don't actually need to block when we're sending a message
      as BestEffort (and these days, we're always sending BestEffort).
    * Pass out client messages in fewer (larger) steps.
    * Have the InNetMessagePool short circuit dispatch requests.
    * Have the message validator take into account expiration to cut down on
      false positives at high transfer rates.
    * Allow configuration of the probabalistic window size growth rate in the
      streaming lib's slow start and congestion avoidance phases, and default
      them to a more conservative value (2), rather than the previous value
      (1).
    * Reduce the ack delay in the streaming lib to 500ms
    * Honor choke requests in the streaming lib (only affects those getting
      insanely high transfer rates)
    * Let the user specify an interface besides 127.0.0.1 or 0.0.0.0 on the
      I2PTunnel client page (thanks maestro^!)
(plus minor udp tweaks)
2005-04-20 19:15:25 +00:00
aum
1861379d43 needed for QConsole 2005-04-20 18:54:39 +00:00
aum
408a344aae added QConsole 2005-04-20 18:53:08 +00:00
e9c1ed70d0 added sirup.i2p 2005-04-18 23:27:31 +00:00
916dcca2b0 * build with reference to the i2p.jar/mstreaming.jar/i2ptunnel.jar inline (building as necessary)
* removed unnecessary references to i2ptunnel (though i2ptunnelxmlobject still references i2ptunnelxmlwrapper)
2005-04-18 18:47:22 +00:00
aum
31e81bab17 fixed build failures 2005-04-18 18:12:32 +00:00
aum
6a5170c341 oops, forgot to add earlier 2005-04-18 18:05:50 +00:00
aum
42bff8093c removed obsolete ref to MiniHttpRequestHandlerBase, changed to MiniHttpRequestHandler 2005-04-18 18:04:12 +00:00
aum
d1df94f284 added needed html template files 2005-04-18 18:02:07 +00:00
aum
9cf1744291 restored images in binary mode 2005-04-18 17:24:33 +00:00
aum
f0545c8c9a removed images which were not checked in as binary 2005-04-18 17:22:27 +00:00
aum
ef9ed87d30 binary mode this time 2005-04-18 17:20:10 +00:00
aum
58ffd92a34 dammit, forgot binary mode 2005-04-18 17:19:25 +00:00
aum
418facc7e0 Added apps/q - the Q distributed file store framework, by aum 2005-04-18 17:03:21 +00:00
7f3c953e14 2005-04-17 sirup
* Added the possibility for i2ptunnel client and httpclient instances to
      have their own i2p session (and hence, destination and tunnels).  By
      default, tunnels are shared, but that can be changed on the web
      interface or with the sharedClient config option in i2ptunnel.config.
2005-04-17  jrandom
    * Marked the net.i2p.i2ptunnel.TunnelManager as deprecated.  Anyone use
      this?  If not, I want to drop it (lots of tiny details with lots of
      duplicated semantics).
2005-04-18 02:07:57 +00:00
addab1fa2a 2005-04-17 zzz
* Added new user-editable eepproxy error page templates.
2005-04-17  jrandom
    * Revamp the tunnel building throttles, fixing a situation where the
      rebuild may not recover, and defaulting it to unthrottled (users with
      slow CPUs may want to set "router.tunnel.shouldThrottle=true" in their
      advanced router config)
2005-04-17 23:23:20 +00:00
39343ce957 2005-04-16 jrandom
* Migrated to Bouncycastle's SHA256 and HMAC implementations for efficiency
2005-04-17 01:04:06 +00:00
7389cec78f 2005-04-16 jrandom
* Migrated to Bouncycastle's SHA256 and HMAC implementations for efficiency
(also lots of udp fixes)
2005-04-17 00:59:48 +00:00
9e5fe7d2b6 * fixed some stupid threading issues in the packet handler (duh)
* use the new raw i2np message format (the previous corruptions were due to above)
* add a new test component (UDPFlooder) which floods all peers at the rate desired
* packet munging fix for highly fragmented messages
* include basic slow start code
* fixed the UDP peer rate refilling
* cleaned up some nextSend scheduling
2005-04-16 15:18:09 +00:00
a7dfaee5ac added connelly.i2p 2005-04-13 02:29:59 +00:00
7beb92b1cc First pass of the UDP transport. No where near ready for use, but it does
the basics (negotiate a session and send I2NP messages back and forth).  Lots,
lots more left.
2005-04-12 16:48:43 +00:00
5b56d22da9 2005-04-12 jrandom
* Make sure we don't get cached updates (thanks smeghead!)
    * Clear out the callback for the TestJob after it passes (only affects the
      job timing accounting)
2005-04-12 15:22:11 +00:00
e6b343070a removed copy/paste error 2005-04-09 23:15:53 +00:00
8496b88518 2005-04-08 smeghead
* Added NativeBigInteger benchmark to scripts/i2pbench.sh.
2005-04-09 03:16:05 +00:00
aa542b7876 for implementation simplicity, include fragment size in the SessionConfirmed packets 2005-04-08 23:20:45 +00:00
3f7d46378b * specify exactly what gets in the DSA signatures for the connection establishment
* include a new signedOnTime so that we can prepare the packet at a different moment from
  when we encrypt & send it (also allowing us to reuse that signature on resends for the same
  establishment)
2005-04-08 14:21:26 +00:00
b36def1f72 2005-04-08 smeghead
* Security improvements to TrustedUpdate: signing and verification of the
      version string along with the data payload for signed update files
      (consequently the positions of the DSA signature and version string fields
      have been swapped in the spec for the update file's header); router will
      no longer perform a trusted update if the signed update's version is lower
      than or equal to the currently running router's version.
    * Added two new CLI commands to TrustedUpdate: showversion, verifyupdate.
    * Extended TrustedUpdate public API for use by third party applications.
2005-04-08 12:39:20 +00:00
5a6a3a5e8d oops, forgot to add new eepget script to build 2005-04-08 01:56:02 +00:00
c3bd26d9b4 added wspucktracker.i2p 2005-04-07 20:40:30 +00:00
aum
967e106ee7 fixed one last javadoc err 2005-04-07 04:36:06 +00:00
aum
7c73e59482 Fixed more javadoc errors 2005-04-07 04:26:55 +00:00
aum
03dfa913d1 Removed erroneous @author tag from methods 2005-04-07 04:05:13 +00:00
348e845793 *cough* thanks cervantes 2005-04-06 16:38:38 +00:00
80827c3aad * 2005-04-06 0.5.0.6 released 2005-04-06 15:43:25 +00:00
3b4cf0a024 added 55cancri.i2p 2005-04-06 15:14:00 +00:00
941252fd80 2005-04-05 jrandom
* Retry I2PTunnel startup if we are unable to build a socketManager for a
      client or httpclient tunnel.
    * Add some basic sanity checking on the I2CP settings (thanks duck!)
2005-04-05 22:24:32 +00:00
bc626ece2d 2005-04-05 jrandom
* After a successfull netDb search for a leaseSet, republish it to all of
      the peers we have tried so far who did not give us the key (up to 10),
      rather than the old K closest (which may include peers who had given us
      the key)
    * Don't wait 5 minutes to publish a leaseSet (duh!), and rather than
      republish it every 5 minutes, republish it every 3.  In addition, always
      republish as soon as the leaseSet changes (duh^2).
    * Minor fix for oddball startup race (thanks travis_bickle!)
    * Minor AES update to allow in-place decryption.
2005-04-05 16:06:14 +00:00
400feb3ba7 clarify crypto/hmac usage for simpler implementation 2005-04-05 15:28:54 +00:00
756a4e3995 added a section for congestion control describing what I hope to implement. what
/actually/ gets implemented will be documented further once its, er, implemented
2005-04-04 17:21:30 +00:00
aum
578301240e Added constructors to PrivateKey, PublicKey, SigningPrivateKey and
SigningPublicKey, which take a single String argument and construct
the object from the Base64 data in that string (where this data is
the product of a .toBase64() call on a prior instance).
2005-04-04 06:13:50 +00:00
aum
9b8f91c7f9 Added 'toPublic()' methods to PrivateKey and SigningPrivateKey, such
that these return PublicKey and SigningPublicKey objects, respectively.
2005-04-04 06:01:13 +00:00
c7c389d4fb added eepget wrapper script for *nix 2005-04-03 13:35:52 +00:00
68f7adfa0b *** keyword substitution change *** 2005-04-03 13:33:29 +00:00
c4ac5170c7 2005-04-03 jrandom
* EepGet fix for open-ended HTTP fetches (such as the news.xml
      feeding the NewsFetcher)
2005-04-03 12:50:11 +00:00
32e0c8ac71 updated status blurb 2005-04-03 07:22:28 +00:00
c9c1eae32f 2005-04-01 jrandom
* Allow editing I2PTunnel server instances with five digit ports
      (thanks nickless_head!)
    * More NewsFetcher debugging for reported weirdness
2005-04-01 13:29:26 +00:00
33366cc291 2005-04-01 jrandom
* Fix to check for missing news file (thanks smeghead!)
    * Added destination display CLI:
      java -cp lib/i2p.jar net.i2p.data.Destination privKeyFilename
    * Added destination display to the web interface (thanks pnspns)
    * Installed CIA backdoor
2005-04-01 11:28:06 +00:00
083ac1f125 n3wz0rz 2005-03-31 02:04:18 +00:00
438 changed files with 37664 additions and 5409 deletions

View File

@ -6,8 +6,7 @@
<property name="dist" location="dist"/>
<property name="jar" value="addressbook.jar"/>
<property name="war" value="addressbook.war"/>
<property name="servlet" value="../jetty/jettylib/javax.servlet.jar"/>
<target name="init">
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
@ -22,7 +21,12 @@
<target name="compile" depends="init">
<javac debug="true" deprecation="on" source="1.3" target="1.3"
srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
srcdir="${src}" destdir="${build}">
<classpath>
<pathelement location="../../core/java/build/i2p.jar" />
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
</classpath>
</javac>
</target>
<target name="jar" depends="compile">

View File

@ -24,11 +24,12 @@ package addressbook;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.File;
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.util.EepGet;
/**
* An address book for storing human readable names mapped to base64 i2p
* destinations. AddressBooks can be created from local and remote files, merged
@ -65,14 +66,18 @@ public class AddressBook {
* where key is a human readable name, and value is a base64 i2p
* destination.
*/
public AddressBook(URL url) {
this.location = url.getHost();
public AddressBook(String url, String proxyHost, int proxyPort) {
this.location = url;
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
proxyHost, proxyPort, 0, "addressbook.tmp", url, true,
null);
get.fetch();
try {
this.addresses = ConfigParser.parse(url);
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
} catch (IOException exp) {
this.addresses = new HashMap();
}
new File("addressbook.tmp").delete();
}
/**
@ -83,43 +88,19 @@ public class AddressBook {
* @param subscription
* A Subscription instance pointing at a remote address book.
*/
public AddressBook(Subscription subscription) {
public AddressBook(Subscription subscription, String proxyHost, int proxyPort) {
this.location = subscription.getLocation();
try {
URL url = new URL(subscription.getLocation());
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
if (subscription.getEtag() != null) {
connection.addRequestProperty("If-None-Match", subscription
.getEtag());
}
if (subscription.getLastModified() != null) {
connection.addRequestProperty("If-Modified-Since", subscription
.getLastModified());
}
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
connection.disconnect();
this.addresses = new HashMap();
return;
}
if (connection.getHeaderField("ETag") != null) {
subscription.setEtag(connection.getHeaderField("ETag"));
}
if (connection.getHeaderField("Last-Modified") != null) {
subscription.setLastModified(connection
.getHeaderField("Last-Modified"));
}
} catch (IOException exp) {
}
try {
this.addresses = ConfigParser.parse(new URL(subscription
.getLocation()));
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
proxyHost, proxyPort, 0, "addressbook.tmp",
subscription.getLocation(), true, subscription.getEtag());
get.fetch();
subscription.setEtag(get.getETag());
try {
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
} catch (IOException exp) {
this.addresses = new HashMap();
}
new File("addressbook.tmp").delete();
}
/**
@ -181,7 +162,7 @@ public class AddressBook {
* @param log
* The log to write messages about new addresses or conflicts to.
*/
public void merge(AddressBook other, Log log) {
public void merge(AddressBook other, boolean overwrite, Log log) {
Iterator otherIter = other.addresses.keySet().iterator();
while (otherIter.hasNext()) {
@ -189,7 +170,7 @@ public class AddressBook {
String otherValue = (String) other.addresses.get(otherKey);
if (otherKey.endsWith(".i2p") && otherValue.length() >= 516) {
if (this.addresses.containsKey(otherKey)) {
if (this.addresses.containsKey(otherKey) && !overwrite) {
if (!this.addresses.get(otherKey).equals(otherValue)
&& log != null) {
log.append("Conflict for " + otherKey + " from "
@ -197,28 +178,19 @@ public class AddressBook {
+ ". Destination in remote address book is "
+ otherValue);
}
} else {
} else if (!this.addresses.containsKey(otherKey)
|| !this.addresses.get(otherKey).equals(otherValue)) {
this.addresses.put(otherKey, otherValue);
this.modified = true;
if (log != null) {
log.append("New address " + otherKey
+ " added to address book.");
+ " added to address book.");
}
}
}
}
}
/**
* Merge this AddressBook with other, without logging.
*
* @param other
* An AddressBook to merge with.
*/
public void merge(AddressBook other) {
this.merge(other, null);
}
/**
* Write the contents of this AddressBook out to the File file. If the file
* cannot be writen to, this method will silently fail.
@ -243,4 +215,4 @@ public class AddressBook {
public void write() {
this.write(new File(this.location));
}
}
}

View File

@ -27,7 +27,6 @@ import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.io.*;
import java.net.URL;
/**
* Utility class providing methods to parse and write files in config file
@ -86,24 +85,6 @@ public class ConfigParser {
return result;
}
/**
* Return a Map using the contents of the file at url. See
* parseBufferedReader for details of the input format.
*
* @param url
* A url pointing to a file to parse.
* @return A Map containing the key, value pairs from url.
* @throws IOException
* if url cannot be read.
*/
public static Map parse(URL url) throws IOException {
InputStream urlStream;
urlStream = url.openConnection().getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(
urlStream));
return ConfigParser.parse(input);
}
/**
* Return a Map using the contents of the File file. See parseBufferedReader
* for details of the input format.

View File

@ -58,15 +58,14 @@ public class Daemon {
*/
public static void update(AddressBook master, AddressBook router,
File published, SubscriptionList subscriptions, Log log) {
String routerLocation = router.getLocation();
master.merge(router);
router.merge(master, true, null);
Iterator iter = subscriptions.iterator();
while (iter.hasNext()) {
master.merge((AddressBook) iter.next(), log);
router.merge((AddressBook) iter.next(), false, log);
}
master.write(new File(routerLocation));
router.write();
if (published != null)
master.write(published);
router.write(published);
subscriptions.write();
}
@ -101,7 +100,8 @@ public class Daemon {
defaultSubs.add("http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/hosts.txt");
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
etagsFile, lastModifiedFile, defaultSubs);
etagsFile, lastModifiedFile, defaultSubs, (String) settings
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
Log log = new Log(logFile);
Daemon.update(master, router, published, subscriptions, log);
@ -154,11 +154,6 @@ public class Daemon {
while (true) {
settings = ConfigParser.parse(settingsFile, defaultSettings);
System.setProperty("proxySet", "true");
System.setProperty("http.proxyHost", (String) settings
.get("proxy_host"));
System.setProperty("http.proxyPort", (String) settings
.get("proxy_port"));
long delay = Long.parseLong((String) settings.get("update_delay"));
if (delay < 1) {
delay = 1;

View File

@ -33,6 +33,8 @@ import java.util.List;
public class SubscriptionIterator implements Iterator {
private Iterator subIterator;
private String proxyHost;
private int proxyPort;
/**
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
@ -40,8 +42,10 @@ public class SubscriptionIterator implements Iterator {
* @param subscriptions
* List of Subscription objects that represent address books.
*/
public SubscriptionIterator(List subscriptions) {
public SubscriptionIterator(List subscriptions, String proxyHost, int proxyPort) {
this.subIterator = subscriptions.iterator();
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
@ -49,15 +53,15 @@ public class SubscriptionIterator implements Iterator {
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return subIterator.hasNext();
return this.subIterator.hasNext();
}
/* (non-Javadoc)
* @see java.util.Iterator#next()
*/
public Object next() {
Subscription sub = (Subscription) subIterator.next();
return new AddressBook(sub);
Subscription sub = (Subscription) this.subIterator.next();
return new AddressBook(sub, this.proxyHost, this.proxyPort);
}
/* (non-Javadoc)

View File

@ -42,6 +42,10 @@ public class SubscriptionList {
private File etagsFile;
private File lastModifiedFile;
private String proxyHost;
private int proxyPort;
/**
* Construct a SubscriptionList using the urls from locationsFile and, if
@ -58,15 +62,18 @@ public class SubscriptionList {
* GET. The file is in the format "url=leastmodified".
*/
public SubscriptionList(File locationsFile, File etagsFile,
File lastModifiedFile, List defaultSubs) {
File lastModifiedFile, List defaultSubs, String proxyHost,
int proxyPort) {
this.subscriptions = new LinkedList();
this.etagsFile = etagsFile;
this.lastModifiedFile = lastModifiedFile;
List locations;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
Map etags;
Map lastModified;
String location;
locations = ConfigParser.parseSubscriptions(locationsFile, defaultSubs);
List locations = ConfigParser.parseSubscriptions(locationsFile,
defaultSubs);
try {
etags = ConfigParser.parse(etagsFile);
} catch (IOException exp) {
@ -80,11 +87,9 @@ public class SubscriptionList {
Iterator iter = locations.iterator();
while (iter.hasNext()) {
location = (String) iter.next();
subscriptions.add(new Subscription(location, (String) etags
this.subscriptions.add(new Subscription(location, (String) etags
.get(location), (String) lastModified.get(location)));
}
iter = this.iterator();
}
/**
@ -94,7 +99,8 @@ public class SubscriptionList {
* @return A SubscriptionIterator.
*/
public SubscriptionIterator iterator() {
return new SubscriptionIterator(this.subscriptions);
return new SubscriptionIterator(this.subscriptions, this.proxyHost,
this.proxyPort);
}
/**

View File

@ -27,6 +27,7 @@
* not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
@ -288,8 +289,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
l.log("textserver <host> <port> <privkey>");
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
l.log("gentextkeys");
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>");
l.log("httpclient <port>");
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
l.log("lookup <name>");
l.log("quit");
l.log("close [forced] <jobnumber>|all");
@ -486,12 +487,16 @@ public class I2PTunnel implements Logging, EventDispatcher {
* Also sets the event "openClientResult" = "error" or "ok" (before setting the value to "ok" it also
* adds "Ready! Port #" to the logger as well). In addition, it will also set "clientLocalPort" =
* Integer port number if the client is listening
* sharedClient parameter is a String "true" or "false"
*
* @param args {portNumber, destinationBase64 or "file:filename"}
* @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient]}
* @param l logger to receive events and output
*/
public void runClient(String args[], Logging l) {
if (args.length == 2) {
boolean isShared = true;
if (args.length == 3)
isShared = Boolean.valueOf(args[2].trim()).booleanValue();
if ( (args.length == 2) || (args.length == 3) ) {
int portNum = -1;
try {
portNum = Integer.parseInt(args[0]);
@ -502,6 +507,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
return;
}
I2PTunnelTask task;
ownDest = !isShared;
try {
task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this);
addtask(task);
@ -512,11 +518,12 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("clientTaskId", new Integer(-1));
}
} else {
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>");
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>[ <sharedClient>]");
l.log(" creates a client that forwards port to the pubkey.\n"
+ " use 0 as port to get a free port assigned. If you specify\n"
+ " a comma delimited list of pubkeys, it will rotate among them\n"
+ " randomlyl");
+ " randomlyl. sharedClient indicates if this client shares \n"
+ " with other clients (true of false)");
notifyEvent("clientTaskId", new Integer(-1));
}
}
@ -526,12 +533,13 @@ public class I2PTunnel implements Logging, EventDispatcher {
*
* Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
* Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has started.
* parameter sharedClient is a String, either "true" or "false"
*
* @param args {portNumber and (optionally) proxy to be used for the WWW}
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
* @param l logger to receive events and output
*/
public void runHttpClient(String args[], Logging l) {
if (args.length >= 1 && args.length <= 2) {
if (args.length >= 1 && args.length <= 3) {
int port = -1;
try {
port = Integer.parseInt(args[0]);
@ -541,12 +549,32 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("httpclientTaskId", new Integer(-1));
return;
}
String proxy = "squid.i2p";
if (args.length == 2) {
proxy = args[1];
boolean isShared = true;
if (args.length > 1) {
if ("true".equalsIgnoreCase(args[1].trim())) {
isShared = true;
if (args.length == 3)
proxy = args[2];
} else if ("false".equalsIgnoreCase(args[1].trim())) {
_log.warn("args[1] == [" + args[1] + "] and rejected explicitly");
isShared = false;
if (args.length == 3)
proxy = args[2];
} else if (args.length == 3) {
isShared = false; // not "true"
proxy = args[2];
_log.warn("args[1] == [" + args[1] + "] but rejected");
} else {
// isShared not specified, default to true
isShared = true;
proxy = args[1];
}
}
I2PTunnelTask task;
ownDest = !isShared;
try {
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
addtask(task);
@ -557,8 +585,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("httpclientTaskId", new Integer(-1));
}
} else {
l.log("httpclient <port> [<proxy>]");
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
l.log(" creates a client that distributes HTTP requests.");
l.log(" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true of false)");
l.log(" <proxy> (optional) indicates a proxy server to be used");
l.log(" when trying to access an address out of the .i2p domain");
l.log(" (the default proxy is squid.i2p).");
@ -1117,6 +1146,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
private String getPrefix() { return '[' + _tunnelId + "]: "; }
public I2PAppContext getContext() { return _context; }
/**
* Call this whenever we lose touch with the router involuntarily (aka the router

View File

@ -110,7 +110,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
_log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
@ -209,8 +209,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
props.putAll(System.getProperties());
else
props.putAll(tunnel.getClientOptions());
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(tunnel.host, Integer.parseInt(tunnel.port), props);
if (sockManager == null) return null;
int portNum = 7654;
if (tunnel.port != null) {
try {
portNum = Integer.parseInt(tunnel.port);
} catch (NumberFormatException nfe) {
_log.log(Log.CRIT, "Invalid port specified [" + tunnel.port + "], reverting to " + portNum);
}
}
I2PSocketManager sockManager = null;
while (sockManager == null) {
sockManager = I2PSocketManagerFactory.createManager(tunnel.host, portNum, props);
if (sockManager == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
sockManager.setName("Client");
return sockManager;
}

View File

@ -23,6 +23,7 @@ import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
/**
@ -70,7 +71,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"wrong BASE64 I2P Destination or the link you are following is "+
"bad. The host (or the WWW proxy, if you're using one) could also "+
"be temporarily offline. You may want to <b>retry</b>. "+
"Could not find the following Destination:<BR><BR>")
"Could not find the following Destination:<BR><BR><div>")
.getBytes();
private final static byte[] ERR_TIMEOUT =
@ -190,6 +191,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "ISO-8859-1"));
String line, method = null, protocol = null, host = null, destination = null;
StringBuffer newRequest = new StringBuffer();
int ahelper = 0;
while ((line = br.readLine()) != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
@ -282,6 +284,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (addressHelper != null) {
destination = addressHelper;
host = getHostName(destination);
ahelper = 1;
}
}
line = method + " " + request.substring(pos);
@ -403,7 +406,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
l.log("Could not resolve " + destination + ".");
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, destination);
String str;
byte[] header;
if (usingWWWProxy)
str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true);
else if(ahelper != 0)
str = FileUtil.readTextFile("docs/dnfb-header.ht", 100, true);
else
str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true);
if (str != null)
header = str.getBytes();
else
header = ERR_DESTINATION_UNKNOWN;
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
s.close();
return;
}
@ -476,10 +491,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (out != null) {
out.write(errMessage);
if (targetRequest != null) {
out.write(targetRequest.getBytes());
int protopos = targetRequest.indexOf(" ");
String uri = targetRequest.substring(0, protopos);
out.write("<a href=\"http://".getBytes());
out.write(uri.getBytes());
out.write("\">http://".getBytes());
out.write(uri.getBytes());
out.write("</a>".getBytes());
if (usingWWWProxy) out.write(("<br>WWW proxy: " + wwwProxy).getBytes());
}
out.write("<p /><i>Generated on: ".getBytes());
out.write("</div><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
@ -493,7 +514,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
_log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
if (out != null) {
try {
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, wwwProxy);
String str;
byte[] header;
if (usingWWWProxy)
str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true);
else
str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true);
if (str != null)
header = str.getBytes();
else
header = ERR_DESTINATION_UNKNOWN;
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {
_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe);
}

View File

@ -35,85 +35,66 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, l, notifyThis, tunnel);
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
* Called by the thread pool of I2PSocket handlers
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
protected void blockingHandle(I2PSocket socket) {
long afterAccept = getTunnel().getContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
String modifiedHeader = getModifiedHeader(socket);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
Socket s = new Socket(remoteHost, remotePort);
afterSocket = getTunnel().getContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
} catch (SocketException ex) {
try {
_handleSocket.setReadTimeout(readTimeout);
String modifiedHeader = getModifiedHeader();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null, modifiedHeader.getBytes(), null);
} catch (SocketException ex) {
try {
_handleSocket.close();
} catch (IOException ioe) {
socket.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: "
+ (afterSocket-afterAccept) + "]");
}
private String getModifiedHeader() throws IOException {
InputStream in = _handleSocket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
return formatHeaders(headers, command);
} catch (IOException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error while receiving the new HTTP request", ex);
}
long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0);
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
private String getModifiedHeader(I2PSocket handleSocket) throws IOException {
InputStream in = handleSocket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
return formatHeaders(headers, command);
}
private String formatHeaders(Properties headers, StringBuffer command) {

View File

@ -75,9 +75,18 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
I2PClient client = I2PClientFactory.createClient();
Properties props = new Properties();
props.putAll(getTunnel().getClientOptions());
int portNum = 7654;
if (getTunnel().port != null) {
try {
portNum = Integer.parseInt(getTunnel().port);
} catch (NumberFormatException nfe) {
_log.log(Log.CRIT, "Invalid port specified [" + getTunnel().port + "], reverting to " + portNum);
}
}
while (sockMgr == null) {
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, portNum,
props);
}
@ -150,58 +159,91 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
}
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
private static final int DEFAULT_HANDLER_COUNT = 10;
protected int getHandlerCount() {
int rv = DEFAULT_HANDLER_COUNT;
String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);
if (cnt != null) {
try {
rv = Integer.parseInt(cnt);
if (rv <= 0)
rv = DEFAULT_HANDLER_COUNT;
} catch (NumberFormatException nfe) {
rv = DEFAULT_HANDLER_COUNT;
}
}
return rv;
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
int handlers = getHandlerCount();
for (int i = 0; i < handlers; i++) {
I2PThread handler = new I2PThread(new Handler(i2pss), "Handle Server " + i);
handler.start();
}
/*
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
*/
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
* minor thread pool to pull off the accept() concurrently. there are still lots
* (and lots) of wasted threads within the I2PTunnelRunner, but its a start
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
private I2PServerSocket _serverSocket;
public Handler(I2PServerSocket serverSocket) {
_serverSocket = serverSocket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
_handleSocket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
} catch (SocketException ex) {
while (open) {
try {
_handleSocket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
blockingHandle(_serverSocket.accept());
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}
protected void blockingHandle(I2PSocket socket) {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, null);
} catch (SocketException ex) {
try {
socket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}

View File

@ -153,10 +153,11 @@ public class TunnelController implements Logging {
setListenOn();
String listenPort = getListenPort();
String proxyList = getProxyList();
String sharedClient = getSharedClient();
if (proxyList == null)
_tunnel.runHttpClient(new String[] { listenPort }, this);
_tunnel.runHttpClient(new String[] { listenPort, sharedClient }, this);
else
_tunnel.runHttpClient(new String[] { listenPort, proxyList }, this);
_tunnel.runHttpClient(new String[] { listenPort, sharedClient, proxyList }, this);
acquire();
_running = true;
}
@ -199,7 +200,8 @@ public class TunnelController implements Logging {
setListenOn();
String listenPort = getListenPort();
String dest = getTargetDestination();
_tunnel.runClient(new String[] { listenPort, dest }, this);
String sharedClient = getSharedClient();
_tunnel.runClient(new String[] { listenPort, dest, sharedClient }, this);
acquire();
_running = true;
}
@ -258,8 +260,16 @@ public class TunnelController implements Logging {
if ("localhost".equals(_tunnel.host))
_tunnel.host = "127.0.0.1";
String port = getI2CPPort();
if ( (port != null) && (port.length() > 0) )
_tunnel.port = port;
if ( (port != null) && (port.length() > 0) ) {
try {
int portNum = Integer.parseInt(port);
_tunnel.port = String.valueOf(portNum);
} catch (NumberFormatException nfe) {
_tunnel.port = "7654";
}
} else {
_tunnel.port = "7654";
}
}
public void stopTunnel() {
@ -323,7 +333,20 @@ public class TunnelController implements Logging {
public String getListenPort() { return _config.getProperty("listenPort"); }
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
public String getProxyList() { return _config.getProperty("proxyList"); }
public String getSharedClient() { return _config.getProperty("sharedClient", "true"); }
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
public String getMyDestination() {
if (_tunnel != null) {
List sessions = _tunnel.getSessions();
for (int i = 0; i < sessions.size(); i++) {
I2PSession session = (I2PSession)sessions.get(i);
Destination dest = session.getMyDestination();
if (dest != null)
return dest.toBase64();
}
}
return null;
}
public boolean getIsRunning() { return _running; }
public boolean getIsStarting() { return _starting; }

View File

@ -1,435 +0,0 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Quick and dirty socket listener to control an I2PTunnel.
* Basically run this class as TunnelManager [listenHost] [listenPort] and
* then send it commands on that port. Commands are one shot deals -
* Send a command + newline, get a response plus newline, then get disconnected.
* <p />
* <b>Implemented commands:</b>
* <pre>
* -------------------------------------------------
* lookup &lt;name&gt;\n
* --
* &lt;base64 of the destination&gt;\n
* or
* &lt;error message, usually 'Unknown host'&gt;\n
*
* Lookup the public key of a named destination (i.e. listed in hosts.txt)
* -------------------------------------------------
* genkey\n
* --
* &lt;base64 of the destination&gt;\t&lt;base64 of private data&gt;\n
*
* Generates a new public and private key pair
* -------------------------------------------------
* convertprivate &lt;base64 of privkey&gt;
* --
* &lt;base64 of destination&gt;\n
* or
* &lt;error message&gt;\n
*
* Returns the destination (pubkey) of a given private key.
* -------------------------------------------------
* listen_on &lt;ip&gt;\n
* --
* ok\n
* or
* error\n
*
* Sets the ip address clients will listen on. By default this is the
* localhost (127.0.0.1)
* -------------------------------------------------
* openclient &lt;listenPort&gt; &lt;peer&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open a tunnel on the given &lt;listenport&gt; to the destination specified
* by &lt;peer&gt;. If &lt;listenPort&gt; is 0 a free port is picked and returned in
* the reply message. Otherwise the short reply message is used.
* Peer can be the base64 of the destination, a file with the public key
* specified as 'file:&lt;filename&gt;' or the name of a destination listed in
* hosts.txt. The &lt;jobId&gt; returned together with "ok" and &lt;listenport&gt; can
* later be used as argument for the "close" command.
* -------------------------------------------------
* openhttpclient &lt;listenPort&gt; [&lt;proxy&gt;]\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open an HTTP proxy through the I2P on the given
* &lt;listenport&gt;. &lt;proxy&gt; (optional) specifies a
* destination to be used as an outbound proxy, to access normal WWW
* sites out of the .i2p domain. If &lt;listenPort&gt; is 0 a free
* port is picked and returned in the reply message. Otherwise the
* short reply message is used. &lt;proxy&gt; can be the base64 of the
* destination, a file with the public key specified as
* 'file:&lt;filename&gt;' or the name of a destination listed in
* hosts.txt. The &lt;jobId&gt; returned together with "ok" and
* &lt;listenport&gt; can later be used as argument for the "close"
* command.
* -------------------------------------------------
* opensockstunnel &lt;listenPort&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open an SOCKS tunnel through the I2P on the given
* &lt;listenport&gt;. If &lt;listenPort&gt; is 0 a free port is
* picked and returned in the reply message. Otherwise the short
* reply message is used. The &lt;jobId&gt; returned together with
* "ok" and &lt;listenport&gt; can later be used as argument for the
* "close" command.
* -------------------------------------------------
* openserver &lt;serverHost&gt; &lt;serverPort&gt; &lt;serverKeys&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* error\n
*
* Starts receiving traffic for the destination specified by &lt;serverKeys&gt;
* and forwards it to the &lt;serverPort&gt; of &lt;serverHost&gt;.
* &lt;serverKeys&gt; is the base 64 encoded private key set of the local
* destination. The &lt;joId&gt; returned together with "ok" can later be used
* as argument for the "close" command.
* -------------------------------------------------
* close [forced] &lt;jobId&gt;\n
* or
* close [forced] all\n
* --
* ok\n
* or
* error\n
*
* Closes the job specified by &lt;jobId&gt; or all jobs. Use the list command
* for a list of running jobs.
* Normally a connection job is not closed when it still has an active
* connection. Use the optional 'forced' keyword to close connections
* regardless of their use.
* -------------------------------------------------
* list\n
* --
* Example output:
*
* [0] i2p.dnsalias.net/69.55.226.145:5555 &lt;- C:\i2pKeys\squidPriv
* [1] 8767 -&gt; HTTPClient
* [2] 7575 -&gt; file:C:\i2pKeys\squidPub
* [3] 5252 -&gt; sCcSANIO~f4AQtCNI1BvDp3ZBS~9Ag5O0k0Msm7XBWWz5eOnZWL3MQ-2rxlesucb9XnpASGhWzyYNBpWAfaIB3pux1J1xujQLOwscMIhm7T8BP76Ly5jx6BLZCYrrPj0BI0uV90XJyT~4UyQgUlC1jzFQdZ9HDgBPJDf1UI4-YjIwEHuJgdZynYlQ1oUFhgno~HhcDByXO~PDaO~1JDMDbBEfIh~v6MgmHp-Xchod1OfKFrxFrzHgcJbn7E8edTFjZA6JCi~DtFxFelQz1lSBd-QB1qJnA0g-pVL5qngNUojXJCXs4qWcQ7ICLpvIc-Fpfj-0F1gkVlGDSGkb1yLH3~8p4czYgR3W5D7OpwXzezz6clpV8kmbd~x2SotdWsXBPRhqpewO38coU4dJG3OEUbuYmdN~nJMfWbmlcM1lXzz2vBsys4sZzW6dV3hZnbvbfxNTqbdqOh-KXi1iAzXv7CVTun0ubw~CfeGpcAqutC5loRUq7Mq62ngOukyv8Z9AAAA
*
* Lists descriptions of all running jobs. The exact format of the
* description depends on the type of job.
* -------------------------------------------------
* </pre>
*/
public class TunnelManager implements Runnable {
private final static Log _log = new Log(TunnelManager.class);
private I2PTunnel _tunnel;
private ServerSocket _socket;
private boolean _keepAccepting;
public TunnelManager(int listenPort) {
this(null, listenPort);
}
public TunnelManager(String listenHost, int listenPort) {
_tunnel = new I2PTunnel();
_keepAccepting = true;
try {
if (listenHost != null) {
_socket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost));
_log.info("Listening for tunnel management clients on " + listenHost + ":" + listenPort);
} else {
_socket = new ServerSocket(listenPort);
_log.info("Listening for tunnel management clients on localhost:" + listenPort);
}
} catch (Exception e) {
_log.error("Error starting up tunnel management listener on " + listenPort, e);
}
}
public static void main(String args[]) {
int port = 7676;
String host = null;
if (args.length == 1) {
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
_log.error("Usage: TunnelManager [host] [port]");
return;
}
} else if (args.length == 2) {
host = args[0];
try {
port = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
_log.error("Usage: TunnelManager [host] [port]");
return;
}
}
TunnelManager mgr = new TunnelManager(host, port);
Thread t = new I2PThread(mgr, "Listener");
t.start();
}
public void run() {
if (_socket == null) {
_log.error("Unable to start listening, since the socket was not bound. Already running?");
return;
}
_log.debug("Running");
try {
while (_keepAccepting) {
Socket socket = _socket.accept();
_log.debug("Client accepted");
if (socket != null) {
Thread t = new I2PThread(new TunnelManagerClientRunner(this, socket));
t.setName("TunnelManager Client");
t.setPriority(I2PThread.MIN_PRIORITY);
t.start();
}
}
} catch (IOException ioe) {
_log.error("Error accepting connections", ioe);
} catch (Exception e) {
_log.error("Other error?!", e);
} finally {
if (_socket != null) try {
_socket.close();
} catch (IOException ioe) {
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
}
public void error(String msg, OutputStream out) throws IOException {
out.write(msg.getBytes());
out.write('\n');
}
public void processQuit(OutputStream out) throws IOException {
out.write("Nice try".getBytes());
out.write('\n');
}
public void processList(OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
long startCommand = Clock.getInstance().now();
_tunnel.runCommand("list", buf);
Object obj = _tunnel.waitEventValue("listDone");
long endCommand = Clock.getInstance().now();
String str = buf.getBuffer();
_log.debug("ListDone complete after " + (endCommand - startCommand) + "ms: [" + str + "]");
out.write(str.getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processListenOn(String ip, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("listen_on " + ip, buf);
String status = (String) _tunnel.waitEventValue("listen_onResult");
out.write((status + "\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* "lookup <name>" returns with the result in base64, else "Unknown host" [or something like that],
* then a newline.
*
*/
public void processLookup(String name, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("lookup " + name, buf);
String rv = (String) _tunnel.waitEventValue("lookupResult");
out.write(rv.getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processTestDestination(String destKey, OutputStream out) throws IOException {
try {
Destination d = new Destination();
d.fromBase64(destKey);
out.write("valid\n".getBytes());
} catch (DataFormatException dfe) {
out.write("invalid\n".getBytes());
}
out.flush();
}
public void processConvertPrivate(String priv, OutputStream out) throws IOException {
try {
Destination dest = new Destination();
dest.fromBase64(priv);
String str = dest.toBase64();
out.write(str.getBytes());
out.write('\n');
} catch (DataFormatException dfe) {
_log.error("Error converting private data", dfe);
out.write("Error converting private key\n".getBytes());
}
}
public void processClose(String which, boolean forced, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand((forced ? "close forced " : "close ") + which, buf);
String str = (String) _tunnel.waitEventValue("closeResult");
out.write((str + "\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* "genkey" returns with the base64 of the destination, followed by a tab, then the base64 of that
* destination's private keys, then a newline.
*
*/
public void processGenKey(OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("gentextkeys", buf);
String priv = (String) _tunnel.waitEventValue("privateKey");
String pub = (String) _tunnel.waitEventValue("publicDestination");
out.write((pub + "\t" + priv).getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processOpenClient(int listenPort, String peer, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("client " + listenPort + " " + peer, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("clientTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openClientResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenHTTPClient(int listenPort, String proxy, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("httpclient " + listenPort + " " + proxy, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("httpclientTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openHTTPClientResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenSOCKSTunnel(int listenPort, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("sockstunnel " + listenPort, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("sockstunnelTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openSOCKSTunnelResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenServer(String serverHost, int serverPort, String privateKeys, OutputStream out)
throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("textserver " + serverHost + " " + serverPort + " " + privateKeys, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("serverTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openServerResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* Frisbee.
*
*/
public void unknownCommand(String command, OutputStream out) throws IOException {
out.write("Unknown command: ".getBytes());
out.write(command.getBytes());
out.write("\n".getBytes());
}
}

View File

@ -1,194 +0,0 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.StringTokenizer;
import net.i2p.util.Log;
/**
* Runner thread that reads commands from the socket and fires off commands to
* the TunnelManager
*
*/
class TunnelManagerClientRunner implements Runnable {
private final static Log _log = new Log(TunnelManagerClientRunner.class);
private TunnelManager _mgr;
private Socket _clientSocket;
public TunnelManagerClientRunner(TunnelManager mgr, Socket socket) {
_clientSocket = socket;
_mgr = mgr;
}
public void run() {
_log.debug("Client running");
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(_clientSocket.getInputStream()));
OutputStream out = _clientSocket.getOutputStream();
String cmd = reader.readLine();
if (cmd != null) processCommand(cmd, out);
} catch (IOException ioe) {
_log.error("Error processing client commands", ioe);
} finally {
if (_clientSocket != null) try {
_clientSocket.close();
} catch (IOException ioe) {
}
}
_log.debug("Client closed");
}
/**
* Parse the command string and fire off the appropriate tunnelManager method,
* sending the results to the output stream
*/
private void processCommand(String command, OutputStream out) throws IOException {
_log.debug("Processing [" + command + "]");
StringTokenizer tok = new StringTokenizer(command);
if (!tok.hasMoreTokens()) {
_mgr.unknownCommand(command, out);
} else {
String cmd = tok.nextToken();
if ("quit".equalsIgnoreCase(cmd)) {
_mgr.processQuit(out);
} else if ("lookup".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processLookup(tok.nextToken(), out);
else
_mgr.error("Usage: lookup <hostname>", out);
} else if ("testdestination".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processTestDestination(tok.nextToken(), out);
else
_mgr.error("Usage: testdestination <publicDestination>", out);
} else if ("convertprivate".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processConvertPrivate(tok.nextToken(), out);
else
_mgr.error("Usage: convertprivate <privateData>", out);
} else if ("close".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens()) {
String closeArg;
if ((closeArg = tok.nextToken()).equals("forced")) {
if (tok.hasMoreTokens()) {
_mgr.processClose(tok.nextToken(), true, out);
} else {
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
}
} else {
_mgr.processClose(closeArg, false, out);
}
} else {
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
}
} else if ("genkey".equalsIgnoreCase(cmd)) {
_mgr.processGenKey(out);
} else if ("list".equalsIgnoreCase(cmd)) {
_mgr.processList(out);
} else if ("listen_on".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens()) {
_mgr.processListenOn(tok.nextToken(), out);
} else {
_mgr.error("Usage: listen_on <ip>", out);
}
} else if ("openclient".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String peer = null;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openclient <listenPort> <peer>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openclient <listenport> <peer>", out);
return;
}
peer = tok.nextToken();
_mgr.processOpenClient(listenPort, peer, out);
} else if ("openhttpclient".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String proxy = "squid.i2p";
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openhttpclient <listenPort> [<proxy>]", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (tok.hasMoreTokens()) {
proxy = tok.nextToken();
}
if (tok.hasMoreTokens()) {
_mgr.error("Usage: openclient <listenport> [<proxy>]", out);
return;
}
_mgr.processOpenHTTPClient(listenPort, proxy, out);
} else if ("opensockstunnel".equalsIgnoreCase(cmd)) {
int listenPort = 0;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: opensockstunnel <listenPort>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (tok.hasMoreTokens()) {
_mgr.error("Usage: opensockstunnel <listenport>", out);
return;
}
_mgr.processOpenSOCKSTunnel(listenPort, out);
} else if ("openserver".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String serverHost = null;
String serverKeys = null;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
serverHost = tok.nextToken();
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
serverKeys = tok.nextToken();
_mgr.processOpenServer(serverHost, listenPort, serverKeys, out);
} else {
_mgr.unknownCommand(command, out);
}
}
}
}

View File

@ -36,14 +36,6 @@ public class EditBean extends IndexBean {
}
}
public String getInternalType(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getType();
else
return "";
}
public String getTargetHost(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
@ -81,6 +73,14 @@ public class EditBean extends IndexBean {
return false;
}
public boolean isSharedClient(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return "true".equalsIgnoreCase(tun.getSharedClient());
else
return true;
}
public boolean shouldDelay(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {

View File

@ -52,6 +52,7 @@ public class IndexBean {
private String _privKeyFile;
private String _profile;
private boolean _startOnLoad;
private boolean _sharedClient;
private boolean _privKeyGenerate;
private boolean _removeConfirmed;
@ -204,7 +205,8 @@ public class IndexBean {
for (int i = 0; i < controllers.size(); i++) {
TunnelController c = (TunnelController)controllers.get(i);
if (c == cur) continue;
if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) {
//only change when they really are declared of beeing a sharedClient
if (("httpclient".equals(c.getType()) || "client".equals(c.getType())) && "true".equalsIgnoreCase(c.getSharedClient())) {
Properties cOpt = c.getConfig("");
if (_tunnelCount != null) {
cOpt.setProperty("option.inbound.quantity", _tunnelCount);
@ -311,6 +313,14 @@ public class IndexBean {
else return internalType;
}
public String getInternalType(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getType();
else
return "";
}
public String getClientInterface(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
@ -335,6 +345,14 @@ public class IndexBean {
return "";
}
public String getSharedClient(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getSharedClient();
else
return "";
}
public String getClientDestination(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun == null) return "";
@ -350,6 +368,19 @@ public class IndexBean {
return "";
}
public String getDestinationBase64(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {
String rv = tun.getMyDestination();
if (rv != null)
return rv;
else
return "";
} else {
return "";
}
}
///
/// bean props for form submission
///
@ -448,6 +479,9 @@ public class IndexBean {
public void setStartOnLoad(String moo) {
_startOnLoad = true;
}
public void setSharedClient(String moo) {
_sharedClient=true;
}
public void setConnectDelay(String moo) {
_connectDelay = true;
}
@ -475,8 +509,14 @@ public class IndexBean {
if (_proxyList != null)
config.setProperty("proxyList", _proxyList);
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
if (_name != null && !_sharedClient) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
}
config.setProperty("sharedClient", _sharedClient + "");
} else if ("client".equals(_type)) {
if (_port != null)
config.setProperty("listenPort", _port);
@ -489,6 +529,11 @@ public class IndexBean {
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
if (_name != null && !_sharedClient) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
}
config.setProperty("sharedClient", _sharedClient + "");
} else if ("server".equals(_type)) {
if (_targetHost != null)
config.setProperty("targetHost", _targetHost);
@ -519,8 +564,10 @@ public class IndexBean {
config.setProperty("description", _description);
if (_i2cpHost != null)
config.setProperty("i2cpHost", _i2cpHost);
if (_i2cpPort != null)
if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) )
config.setProperty("i2cpPort", _i2cpPort);
else
config.setProperty("i2cpPort", "7654");
if (_customOptions != null) {
StringTokenizer tok = new StringTokenizer(_customOptions);
@ -544,7 +591,7 @@ public class IndexBean {
}
config.setProperty("startOnLoad", _startOnLoad + "");
if (_tunnelCount != null) {
config.setProperty("option.inbound.quantity", _tunnelCount);
config.setProperty("option.outbound.quantity", _tunnelCount);
@ -558,7 +605,7 @@ public class IndexBean {
else
config.setProperty("option.i2p.streaming.connectDelay", "0");
if (_name != null) {
if ( (!"client".equals(_type)) && (!"httpclient".equals(_type)) ) {
if ( ((!"client".equals(_type)) && (!"httpclient".equals(_type))) || (!_sharedClient) ) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
} else {

View File

@ -93,7 +93,7 @@ if (curTunnel >= 0) {
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachablyByOther" size="20" />
<input type="text" name="reachableByOther" size="20" />
<% } else if ("0.0.0.0".equals(clientInterface)) { %>
<option value="127.0.0.1">Locally (127.0.0.1)</option>
<option value="0.0.0.0" selected="true">Everyone (0.0.0.0)</option>
@ -102,7 +102,7 @@ if (curTunnel >= 0) {
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachablyByOther" size="20" />
<input type="text" name="reachableByOther" size="20" />
<% } else { %>
<option value="127.0.0.1">Locally (127.0.0.1)</option>
<option value="0.0.0.0">Everyone (0.0.0.0)</option>
@ -111,7 +111,7 @@ if (curTunnel >= 0) {
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachablyByOther" size="20" value="<%=clientInterface%>" />
<input type="text" name="reachableByOther" size="20" value="<%=clientInterface%>" />
<% } %>
</td>
@ -161,10 +161,23 @@ if (curTunnel >= 0) {
</td>
</tr>
<tr>
<td>
<b>Shared Client</b>
</td>
<td>
<% if (editBean.isSharedClient(curTunnel)) { %>
<input type="checkbox" value="true" name="sharedClient" checked="true" />
<% } else { %>
<input type="checkbox" value="true" name="sharedClient" />
<% } %>
<i>(Share tunnels with other clients and httpclients? Change requires restart of client proxy)</i>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<b><hr size="1">
Advanced networking options<br />
<span style="color:#dd0000;">(Those are shared between ALL your Client proxies!)</span></b>
<span style="color:#dd0000;">(NOTE: when this client proxy is configured to share tunnels, then these options are for all the shared proxy clients!)</span></b>
</td>
</tr>
<tr>

View File

@ -77,7 +77,7 @@ if (curTunnel >= 0) {
</td>
<td>
Host: <input type="text" size="20" name="targetHost" value="<%=editBean.getTargetHost(curTunnel)%>" />
Port: <input type="text" size="4" maxlength="4" name="targetPort" value="<%=editBean.getTargetPort(curTunnel)%>" />
Port: <input type="text" size="6" maxlength="5" name="targetPort" value="<%=editBean.getTargetPort(curTunnel)%>" />
</td>
</tr>
<% String curType = editBean.getInternalType(curTunnel);
@ -110,6 +110,10 @@ Port: <input type="text" size="4" maxlength="4" name="targetPort" value="<%=edit
</td>
</tr>
<tr>
<td valign="top" align="left"><b>Local destination:</b><br /><i>(if known)</i></td>
<td valign="top" align="left"><input type="text" size="60" value="<%=editBean.getDestinationBase64(curTunnel)%>" /></td>
</tr>
<tr>
<td colspan="2" align="center">
<b><hr size="1">
Advanced networking options<br />

View File

@ -118,6 +118,9 @@
case IndexBean.RUNNING:
%><b><span style="color:#00dd00">Running</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curServer%>">[STOP]</a><%
if ("httpserver".equals(indexBean.getInternalType(curServer))) {
%> (<a href="http://<%=(new java.util.Random()).nextLong()%>.i2p/?i2paddresshelper=<%=indexBean.getDestinationBase64(curServer)%>">preview</a>)<%
}
break;
case IndexBean.NOT_RUNNING:
%><b><span style="color:#dd0000">Not Running</span></b>

View File

@ -15,6 +15,7 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.data.DataFormatException;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
@ -74,61 +75,67 @@ public class StreamSinkClient {
} finally {
if (fis == null) try { fis.close(); } catch (IOException ioe) {}
}
System.out.println("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
try {
I2PSocket sock = mgr.connect(peer);
byte buf[] = new byte[32*1024];
Random rand = new Random();
OutputStream out = sock.getOutputStream();
long beforeSending = System.currentTimeMillis();
for (int i = 0; i < _sendSize; i+= 32) {
rand.nextBytes(buf);
out.write(buf);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
while (true) {
try {
I2PSocket sock = mgr.connect(peer);
byte buf[] = new byte[Math.min(32*1024, _sendSize*1024)];
Random rand = new Random();
OutputStream out = sock.getOutputStream();
long beforeSending = System.currentTimeMillis();
for (int i = 0; (_sendSize < 0) || (i < _sendSize); i+= buf.length/1024) {
rand.nextBytes(buf);
out.write(buf);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote " + ((1+i*buf.length)/1024) + "/" + _sendSize + "KB");
if (_writeDelay > 0) {
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
}
}
sock.close();
long afterSending = System.currentTimeMillis();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote " + (i+32) + "/" + _sendSize + "KB");
if (_writeDelay > 0) {
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
}
}
sock.close();
long afterSending = System.currentTimeMillis();
System.out.println("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
} catch (InterruptedIOException iie) {
_log.error("Timeout connecting to the peer", iie);
return;
} catch (NoRouteToHostException nrthe) {
_log.error("Unable to connect to the peer", nrthe);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
} catch (I2PException ie) {
_log.error("Error connecting to the peer", ie);
return;
} catch (IOException ioe) {
_log.error("IO error sending", ioe);
return;
_log.debug("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
} catch (InterruptedIOException iie) {
_log.error("Timeout connecting to the peer", iie);
//return;
} catch (NoRouteToHostException nrthe) {
_log.error("Unable to connect to the peer", nrthe);
//return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
//return;
} catch (I2PException ie) {
_log.error("Error connecting to the peer", ie);
return;
} catch (IOException ioe) {
_log.error("IO error sending", ioe);
return;
}
}
}
/**
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile</code> <br />
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile [concurrentSends]</code> <br />
* <ul>
* <li><b>sendSizeKB</b>: how many KB to send</li>
* <li><b>sendSizeKB</b>: how many KB to send, or -1 for unlimited</li>
* <li><b>writeDelayMs</b>: how long to wait between each .write (0 for no delay)</li>
* <li><b>serverDestFile</b>: file containing the StreamSinkServer's binary Destination</li>
* <li><b>concurrentSends</b>: how many concurrent threads should send to the server at once</li>
* </ul>
*/
public static void main(String args[]) {
StreamSinkClient client = null;
int sendSizeKB = -1;
int writeDelayMs = -1;
int concurrent = 1;
switch (args.length) {
case 3:
case 3: // fall through
case 4:
try {
sendSizeKB = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
@ -141,9 +148,13 @@ public class StreamSinkClient {
System.err.println("Write delay ms invalid [" + args[1] + "]");
return;
}
if (args.length == 4) {
try { concurrent = Integer.parseInt(args[3]); } catch (NumberFormatException nfe) {}
}
client = new StreamSinkClient(sendSizeKB, writeDelayMs, args[2]);
break;
case 5:
case 5: // fall through
case 6:
try {
int port = Integer.parseInt(args[1]);
sendSizeKB = Integer.parseInt(args[2]);
@ -152,11 +163,26 @@ public class StreamSinkClient {
} catch (NumberFormatException nfe) {
System.err.println("arg error");
}
if (args.length == 6) {
try { concurrent = Integer.parseInt(args[5]); } catch (NumberFormatException nfe) {}
}
break;
default:
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile");
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile [concurrentSends]");
}
if (client != null) {
for (int i = 0; i < concurrent; i++)
new I2PThread(new Runner(client), "Client " + i).start();
}
}
private static class Runner implements Runnable {
private StreamSinkClient _client;
public Runner(StreamSinkClient client) {
_client = client;
}
public void run() {
_client.runClient();
}
if (client != null)
client.runClient();
}
}

View File

@ -6,6 +6,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import net.i2p.I2PAppContext;
@ -26,6 +28,7 @@ public class StreamSinkServer {
private String _destFile;
private String _i2cpHost;
private int _i2cpPort;
private int _handlers;
/**
* Create but do not start the streaming server.
@ -34,13 +37,14 @@ public class StreamSinkServer {
* @param ourDestFile filename to write our binary destination to
*/
public StreamSinkServer(String sinkDir, String ourDestFile) {
this(sinkDir, ourDestFile, null, -1);
this(sinkDir, ourDestFile, null, -1, 3);
}
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort) {
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort, int handlers) {
_sinkDir = sinkDir;
_destFile = ourDestFile;
_i2cpHost = i2cpHost;
_i2cpPort = i2cpPort;
_handlers = handlers;
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
}
@ -56,7 +60,8 @@ public class StreamSinkServer {
else
mgr = I2PSocketManagerFactory.createManager();
Destination dest = mgr.getSession().getMyDestination();
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
if (_log.shouldLog(Log.INFO))
_log.info("Listening for connections on: " + dest.calculateHash().toBase64());
FileOutputStream fos = null;
try {
fos = new FileOutputStream(_destFile);
@ -72,24 +77,16 @@ public class StreamSinkServer {
}
I2PServerSocket sock = mgr.getServerSocket();
while (true) {
try {
I2PSocket curSock = sock.accept();
handle(curSock);
} catch (I2PException ie) {
_log.error("Error accepting connection", ie);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
}
}
startup(sock);
}
private void handle(I2PSocket socket) {
I2PThread t = new I2PThread(new ClientRunner(socket));
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
t.start();
public void startup(I2PServerSocket sock) {
for (int i = 0; i < _handlers; i++) {
I2PThread t = new I2PThread(new ClientRunner(sock));
t.setName("Handler " + i);
t.setDaemon(false);
t.start();
}
}
/**
@ -97,27 +94,44 @@ public class StreamSinkServer {
*
*/
private class ClientRunner implements Runnable {
private I2PSocket _sock;
private FileOutputStream _fos;
public ClientRunner(I2PSocket socket) {
_sock = socket;
private I2PServerSocket _socket;
public ClientRunner(I2PServerSocket socket) {
_socket = socket;
}
public void run() {
while (true) {
try {
I2PSocket socket = _socket.accept();
if (socket != null)
handle(socket);
} catch (I2PException ie) {
_log.error("Error accepting connection", ie);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
}
}
}
private void handle(I2PSocket sock) {
FileOutputStream fos = null;
try {
File sink = new File(_sinkDir);
if (!sink.exists())
sink.mkdirs();
File cur = File.createTempFile("clientSink", ".dat", sink);
_fos = new FileOutputStream(cur);
System.out.println("Writing to " + cur.getAbsolutePath());
fos = new FileOutputStream(cur);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing to " + cur.getAbsolutePath());
} catch (IOException ioe) {
_log.error("Error creating sink", ioe);
_fos = null;
return;
}
}
public void run() {
if (_fos == null) return;
long start = System.currentTimeMillis();
try {
InputStream in = _sock.getInputStream();
InputStream in = sock.getInputStream();
byte buf[] = new byte[4096];
long written = 0;
int read = 0;
@ -125,47 +139,55 @@ public class StreamSinkServer {
//_fos.write(buf, 0, read);
written += read;
if (_log.shouldLog(Log.DEBUG))
_log.debug("read and wrote " + read);
_log.debug("read and wrote " + read + " (" + written + ")");
}
_fos.write(("written: [" + written + "]\n").getBytes());
fos.write(("written: [" + written + "]\n").getBytes());
long lifetime = System.currentTimeMillis() - start;
_log.error("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
_log.info("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
} catch (IOException ioe) {
_log.error("Error writing the sink", ioe);
} finally {
if (_fos != null) try { _fos.close(); } catch (IOException ioe) {}
if (_sock != null) try { _sock.close(); } catch (IOException ioe) {}
_log.error("Client socket closed");
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
if (sock != null) try { sock.close(); } catch (IOException ioe) {}
_log.debug("Client socket closed");
}
}
}
/**
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile</code><br />
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [numHandlers]</code><br />
* <ul>
* <li><b>sinkDir</b>: Directory to store received files in</li>
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
* <li><b>numHandlers</b>: how many concurrent connections to handle</li>
* </ul>
*/
public static void main(String args[]) {
StreamSinkServer server = null;
switch (args.length) {
case 0:
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654);
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654, 3);
break;
case 2:
server = new StreamSinkServer(args[0], args[1]);
break;
case 4:
case 5:
int handlers = 3;
if (args.length == 5) {
try {
handlers = Integer.parseInt(args[4]);
} catch (NumberFormatException nfe) {}
}
try {
int port = Integer.parseInt(args[1]);
server = new StreamSinkServer(args[2], args[3], args[0], port);
server = new StreamSinkServer(args[2], args[3], args[0], port, handlers);
} catch (NumberFormatException nfe) {
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
}
break;
default:
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
}
if (server != null)
server.runServer();

View File

@ -149,7 +149,8 @@ public class TestSwarm {
public void run() {
_started = _context.clock().now();
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
byte data[] = new byte[32*1024];
byte data[] = new byte[4*1024];
_context.random().nextBytes(data);
long value = 0;
long lastSend = _context.clock().now();
if (_socket == null) {
@ -167,15 +168,19 @@ public class TestSwarm {
try {
OutputStream out = _socket.getOutputStream();
while (!_closed) {
out.write(data);
// out.flush();
_totalSent += data.length;
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
//try { Thread.sleep(100); } catch (InterruptedException ie) {}
long now = _context.clock().now();
_log.debug("Sending " + _connectionId + " after " + (now-lastSend));
lastSend = now;
try { Thread.sleep(20); } catch (InterruptedException ie) {}
if (shouldSend()) {
out.write(data);
// out.flush();
_totalSent += data.length;
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
//try { Thread.sleep(100); } catch (InterruptedException ie) {}
long now = _context.clock().now();
//_log.debug("Sending " + _connectionId + " after " + (now-lastSend));
lastSend = now;
//try { Thread.sleep(20); } catch (InterruptedException ie) {}
} else {
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
}
}
} catch (Exception e) {
_log.error("Error sending", e);
@ -188,13 +193,13 @@ public class TestSwarm {
long now = lastRead;
try {
InputStream in = _socket.getInputStream();
byte buf[] = new byte[32*1024];
byte buf[] = new byte[8*1024];
int read = 0;
while ( (read = in.read(buf)) != -1) {
now = System.currentTimeMillis();
_totalReceived += read;
_context.statManager().addRateData("swarm." + getConnectionId() + ".totalReceived", _totalReceived, 0);
_log.debug("Receiving " + _connectionId + " with " + read + " after " + (now-lastRead));
//_log.debug("Receiving " + _connectionId + " with " + read + " after " + (now-lastRead));
lastRead = now;
}
} catch (Exception e) {
@ -203,4 +208,8 @@ public class TestSwarm {
}
}
}
private boolean shouldSend() {
return Boolean.valueOf(_context.getProperty("shouldSend", "false")).booleanValue();
}
}

BIN
apps/q/doc/client.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

26
apps/q/doc/diagrams.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q System Diagrams</title>
</head>
<body>
<h1>Q Diagrams</h1>
Informal system diagrams of Q network, hubs and clients.
<center>
<hr>
<img src="overall.jpg">
<hr>
<img src="client.jpg">
<hr>
<img src="hub.jpg">
</center>
<hr>
<address><a href="mailto:aum@mail.i2p">aum</a></address>
<!-- Created: Sat Apr 16 17:24:02 NZST 2005 -->
<!-- hhmts start -->
Last modified: Mon Apr 18 14:06:02 NZST 2005
<!-- hhmts end -->
</body>
</html>

BIN
apps/q/doc/hub.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

80
apps/q/doc/index.html Normal file
View File

@ -0,0 +1,80 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Quartermaster - I2P Distributed File Store</title>
</head>
<body>
<center>
<h1>Quartermaster<br>an I2P Distributed File Store</h1>
<h3>STATUS<h3>
<i>Whole new (incompatible) version currently in development;
ETA for release approx 4-7 days;
view screenshots <a href="screenshots.html">here</a>
</i>
<br>
<hr>
<small>
<a href="manual/index.html">User Manual</a> |
<a href="spec/index.html">Protocol Spec</a> |
<a href="metadata.html">Metadata Spec</a> |
<a href="diagrams.html">Q Pr0n (diagrams)</a> |
<a href="api/index.html">API Spec</a> |
<a href="qnoderefs.txt">qnoderefs.txt</a> |
Full Download |
Updated jar
</small>
</center>
<hr>
<h2>Intro</h2>
Quartermaster, or Q for short, is a distributed file storage framework for I2P.
<h2>Features</h2>
<ul>
<li>Now features 'QSites' - the Q equivalent of Freenet freesites,
static websites which are retrievable even if author is offline</li>
<li>Easy web interface - interact with Q (and view/insert QSites)
from your web browser</li>
<li>Maximum expectations of content retrievability</li>
<li>Content security akin to Freenet CHK and SSK keys</li>
<li>Powerful, flexible search engine</li>
<li>Comfortably accommodates both permanent and transient
nodes without significant network disruption (for instance,
no flooding of the I2P network with futile
calls to offline nodes)</li>
<li>Rapid query resolution, due to distributed catalogue
mirroring which eliminates all in-network query traffic</li>
<li>Modular, extensible architecture</li>
<li>Simple interfaces for 3rd-party app developers</li>
<li>Is custom-designed and built around I2P, so no duplication of
I2P's encryption/anonymity features</li>
<li>Simple XML-RPC interface for all inter-node communication, makes it easy to
implement user-level clients in any language; also allows alternative
implementations of core server and/or client nodes.</li>
</ul>
<hr>
<h2>Status</h2>
Q is presently under development, and a test release is expected soon.
<hr>
<h2>Architecture</h2>
Refer to the <a href="spec/index.html">Protocol Specification</a> for more information.
<hr>
<!-- Created: Sat Mar 26 11:09:12 NZST 2005 -->
<!-- hhmts start -->
Last modified: Mon Apr 18 18:55:19 NZST 2005
<!-- hhmts end -->
</body>
</html>

View File

@ -0,0 +1,805 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q User/Programmer Manual</title>
<style type="text/css">
<!--
td { vertical-align: top; }
code { font-family: courier, monospace; font-weight: bold }
-->
</style>
</head>
<body style="font-family: arial, helvetica, sans-serif">
<center>
<h1>Q User/Programmer Manual</h1>
<i>A brief but hopefully easy guide to installing and using the Q distributed file
store within the I2P network</i>
<br><br>
<i>(Return to <a href="../index.html">Q Homepage</a>)</i>
<br>
<br>
<small>
<a href="#intro">Introduction</a> |
<a href="#checklist">Checklist</a> |
<a href="#serverorclient">Server?orClient?</a> |
<a href="#walkthrough">Walkthrough</a> |
<a href="#server">Server Nodes</a> |
<a href="#qmgr">About QMgr</a> |
<a href="#contact">Contact us</a>
</small>
</center>
<a name="intro"/>
<hr>
<h2>1. Introduction</h2>
<blockquote>
Q is a distributed Peer2Peer file storage/retrieval network that aims to deliver optimal
performance by respecting the properties of the I2P network.<br>
<br>
This manual serves as a 'walkthrough' guide, to take you through the steps from initial
download, to everyday usage. It also provides information for the benefit of higher-level
client application authors.
</blockquote>
<a name="checklist"/>
<hr>
<h2>2. Preliminary Checklist</h2>
<blockquote>
OK, we assume here that you've already cracked the tarball, and are looking at
the distribution files.<br>
<br>
In order to get Q set up and running, you'll need:
<ol>
<li>An I2P router installed, set up and (permanently or transiently) running</li>
<li>Your system shell set up with at the environment variables:
<ul>
<li><b>CLASSPATH</b> - this should include:
<ul>
<li>The regular I2P jar files and 3rd party support jar files (eg <b>i2p.jar</b>,
<b>i2ptunnel.jar</b>, <b>streaming.jar</b>,
<b>mstreaming.jar</b>, <b>jbigi.jar</b>)</li>
<li>Apache's XML-RPC support jarfile - included in this Q distro as
<b>xmlrpc.jar</b></li>
<li>Aum's jarfile <b>aum.jar</b>, which includes Q and all needed support code</li>
</ul>
</li>
<li><b>PATH</b> - your execution search path <b><i>must</i></b> include the directory
in which your main java VM execution program (<b>java</b>, or on windows systems,
<b>java.exe</b>) resides.<br>
<b>NOTE</b> - if <b>java[.exe]</b> is not on your <b>PATH</b>, then Q <i>will
not run</i>.</li>
</ul>
</ol>
</blockquote>
<a name="serverorclient"/>
<hr>
<h2>3. Q Server or Q Client?</h2>
<blockquote>
Nearly everyone will want to run a <b>Q Client Node</b>.<br>
<Br>
It is only client nodes which provide users with full access to the Q network.<br>
<br>
However, if you have a (near-) permanently running I2P Router, and you're a kind and
generous soul, you might <i>also</i> be willing to run a <b>Q Server Node</b> in addition
to your <b>Q Client Node</b>.<br>
<br>
If you do choose to run a server node, you'll be expected to keep it running as near as
possible to 24/7. While transience of client nodes - frequent entering and leaving the
Q network - causes little or no disruption, transience of server nodes can significantly
impair Q's usability for everyone, particularly if this transience occurs frequently amongst
more than the smallest percentage of the server node pool.<br>
<br>
Until you're feeling well "settled in" with Q, your best approach is to just run a
client node for now, and add a server node later when you feel ready.<br>
</blockquote>
<a name="walkthrough"/>
<hr>
<h2>4. Q Walkthrough</h2>
<h3>4.1. Introduction</h3>
<blockquote>
This chapter discusses the deployment and usage of a Q Client Node, and will take you
through the steps of:
<ol>
<li>Double-checking that you've met the installation requirements</li>
<li>Launching a Q Client Node</li>
<li>Verifying that your Q Client Node is running</li>
<li>If your node fails to launch, figuring out why</li>
<li>Importing one or more noderefs into your node</li>
<li>Observing that your node is discovering other nodes on the network</li>
<li>Observing that your node is discovering content on the network</li>
<li>Searching for items of content that match chosen criteria</li>
<li>Retrieving stuff from the network</li>
<li>Inserting stuff to the network</li>
<li>Shutting down your client node</li>
</ol>
Setup and running of Q Server Nodes will be discussed in a later chapter.
</blockquote>
<hr>
<h3>4.2. Verify Your Q Installation Is Correct</h3>
<blockquote>
Ensure that all the needed I2P jarfiles, as well as <b>xmlrpc.jar</b> and
Q's very own <b>aum.jar</b> are correctly listed in your <b>CLASSPATH</b> environment
varaible, and your main java launcher is correctly listed in your <b>PATH</b> environment
variable.<br>
<br>
Typically, you will likely copy the jarfiles <b>aum.jar</b> and <b>xmlrpc.jar</b>
into the <b>lib/</b> subdirectory of your I2P router installation, along with all
the other I2P jar files. Wherever you choose to put these files, make sure they're
correctly listed in your <b>CLASSPATH</b>.
<br>
Also, you'll want to add execute permission to your <b>qmgr</b> (or <b>qmgr.bat</b>)
wrapper script, and copy it into one of the directories listed in your <b>PATH</b>
environment variable.<br>
</blockquote>
<hr>
<h3>4.3. Get Familiar With qmgr</h3>
<blockquote>
<b>qmgr</b> (or <b>qmgr.bat</b>) is a convenience wrapper script to save your
sore fingers from needless typing. It's just a wrapper which passes arguments
to the java command <b><code>java&nbsp;net.i2p.aum.q.QMgr</code></b><br>
<br>
You can verify you've set up qmgr correctly with the command:
<blockquote><code><pre>
qmgr help</pre></code></blockquote>
This displays a brief summary of qmgr commands. On the other hand, the command:
<blockquote><code><pre>
qmgr help verbose</pre></code></blockquote>
floods your terminal window with a detailed explanation of all the qmgr commands
and their arguments.<br>
</blockquote>
<hr>
<h3>4.4. Running A Q Client Node For The First Time</h3>
<blockquote>
Provided you've successfully completed the preliminaries, you can launch your
Q Client Node with the command:
<blockquote><code><pre>
qmgr start</pre></code></blockquote>
All going well, you should have a Q Client Node now running in background.
</blockquote>
<hr>
<h3>4.5. Verify that your Q Client Node is actually Running</h3>
<blockquote>
After typed the <b>qmgr start</b> command, you will see little or no
evidence that Q is actually running.<br>
<br>
You can test if the node is actually up by typing the command:
<blockquote><code><pre>
qmgr status</pre></code></blockquote>
If your Q Client Node is running, this <b>status</b> command should produce
something like:
<blockquote><code><pre>
Pinging node at '/home/myusername/.quartermaster_client'...
Node Ping:
status=ok
numPeers=0
dest=-3LQaE215uIYwl-DsirnzXGQBI31EZQj9u~xx45E823WqjN5i2Umi37GPFTWc8KyislDjF37J7jy5newLUp-qrDpY7BZum3bRyTXo3Udl8a3sUjuu4qR5oBEWFfoghQiqDGYDQyJV9Rtz7DEGaKHGlhtoGsAYRXGXEa8a43T2llqZx2fqaXs~836g8t6sLZjryA5A9fpq98nE5lT0hcTalPieFpluJVairZREXpUiAUmGHG7wAIjF6iszXLEHSZ8Qc622Xgwy0d1yrPojL2yhZ64o05aueYcr~xNCiFxYoHyEJO3XYmkx~q-W-mzS3nn6pRevRda74MnX1~3fFDZ0u~OG6cLZoFkWgnxrwrWGFUUVMR87Yz251xMCKJAX6zErcoGjGFpqGZsWxl4~yq7yfkjPnq3GuTxp2cB75bRAOZRIAieqBOVJDEodFYW5amCinu4AxYE7G1ezz4ghqHFe~0yaAdO74Q1XoUny138YT6P33oNOOlISO1cAAAA
uptime=4952
load=0.0
id=6LVZ9-~GgJJ52WUF1fLHt3UnH50TnXSoPQXy7WZ4GA=
numLocalItems=47
numRemoteItems=2173</pre></code></blockquote>
If you see something like this, then smile, because Q is now up on your system.<br>
<br>
If the node launch failed, you might see something like:
<blockquote><code><pre>
Pinging node at '/home/myusername/.quartermaster_client'...
java.io.IOException: Connection refused
at org.apache.xmlrpc.XmlRpcClient$Worker.execute(Unknown Source)
at org.apache.xmlrpc.XmlRpcClient.execute(Unknown Source)
at net.i2p.aum.q.QMgr.doStatus(QMgr.java:310)
at net.i2p.aum.q.QMgr.execute(QMgr.java:813)
at net.i2p.aum.q.QMgr.main(QMgr.java:869)
Failed to ping node</pre></code></blockquote>
This indicates that your Q client node has either crashed, or failed to launch in the
first place.<br>
<br>
If you're having trouble like this, you might like to try running your Q client node
in foreground, instead of spawning it off into background.<br>
<br>
The command to run a Q client node in foreground is:
<blockquote><code><pre>
qmgr foreground</pre></code></blockquote>
You should see some meaningless startup messages, and no return to your shell prompt.<br>
</blockquote>
<hr>
<h3>4.6. Diversion - Q Storage Directories</h3>
<blockquote>
By default, when you run a Q Client Node, it creates a datastore directory tree
at <b>~/.quartermaster_client</b>. (Windows users note - you'll find this directory
wherever your user home directory is - this depends on what version of Windows
you have installed).<br>
<br>
Within this directory tree, you should see a file called <b>node.log</b>, which
will contain various debug log messages, and can help you to rectify any problems
with your Q installation. If you hit a wall and can't rectify the problems
yourself, you should send this file to the Q author (aum).<br>
<br>
It's possible to run your Q node from another directory, by passing that directory
as a <b>-dir &lt;path&gt;</b> argument to the
<b>qmgr</b> <b>start</b>, <b>foreground</b> and <b>stop</b>
commands. See <b>qmgr help verbose</b> for more information.
</blockquote>
<hr>
<h3>4.7. Importing a Noderef</h3>
<blockquote>
Note from the prior <b>qmgr status</b> command the line:
<blockquote><code><pre>
numPeers=0</pre></code></blockquote>
This means that your Q client node is running standalone, and doesn't have any contact
with any Q network. As such, your node is effectively useless. We need to hook up
your node with other nodes in the Q network.<br>
<br>
Q doesn't ship with any means for new client nodes to automatically connect to any Q
server nodes. This is deliberate.<br>
<br>
In all likelihood, there will be one 'main' Q network running within I2P, largely
based around the author's own Q server node, and most people will likely want to
use this Q network. But the author doesn't want to stop other people running their
own private Q networks, for whatever purpose has meaning for them.
<blockquote><i><small>
<hr>
This is especially relevant for Q as opposed to Freenet. With Freenet, there's
no way for a user to know of the existence of any item of content without
first being given its 'key'. However, since Q works with published catalogs,
any user can know everything that's available on a Q network, which might
not be desirable to those wishing to share content in a private situation.<br>
<Br>
The Q author anticipates, and warmly supports, people running their own
private Q networks within I2P, in addition to accessing the mainstream
'official' Q network.<br>
<br>
The way Q is designed and implemented, there is no way for anyone, including
Q's author, to know of the existence of anyone else's private Q network.
It is beyond the author's control, (and thus arguably the author's
legal responsibility), what private Q networks people set up, and what
kind of content is trafficked on these networks. This claim of plausible
deniability on the part of Q's author parallels that of a hardware retailer
denying responsibility for what people do with tools that they purchase.
<hr>
</small>
</i></blockquote>
Ok, getting back on topic - your brand new virgin Q client node is useless and lonely,
and desperately needs some Q server nodes to talk to. So let's hook up your node to
the mainstream Q network.<br>
<br>
You'll need to get one or more 'noderefs' for Q server nodes.<br>
<br>
There's nothing fancy about a Q noderef. It's just a regular I2P 'destination', with
which your Q Client Node can connect with a Q Server Node.<br>
<br>
A 'semi-official' list of noderefs for the mainstream Q network can be downloaded
from the url: <a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a>.<br>
<br>
Download this file, save it as (say) <b>qnoderefs.txt</b>. (Alternatively, if you're
wanting to subscribe into a private Q network, then get a noderef for at least one
of that network's server nodes from someone on that network who trusts you).<br>
<br>
Import these noderefs into your Q client node via the command:
<blockquote><code><pre>
qmgr addref qnoderefs.txt</pre></code></blockquote>
If all goes well, you should see no output from this command, or (possibly) a brief
line or two suggesting success.<br>
<br>
Your client node is now subscribed into the Q network of your choice. Verify this
with the command:
<blockquote><code><pre>
qmgr status</pre></code></blockquote>
In the output from that command, you should see the <b>numPeers=</b> line showing at least
1 peer.<br>
<br>
If there is more than one Q Server Node on the Q network you've just subscribed to,
then your local node should sooner or later discover all these server nodes, and
the <b>numPeers</b> value should increase over time.<br>
<br>
<blockquote>
<hr>
While Q is in its early development and testing stages, the author may abdicate
the mainstream Q network, and publish nodrefs for a whole new mainstream Q network.
This will especially happen if the author makes any substantial changes to the
inter-node protocol, and/or releases incompatible new versions of Q client/server
nodes. Remember that
<a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a> will
serve as the authoritative source for noderefs for the mainstream Q network within
the mainstream I2P network.
<hr>
</blockquote>
When your client node gets its noderefs to a Q network, it will periodically,
from then on, retrieve differential peer list and catalog updates from servers
it knows about.<br>
<br>
Even if you only feed your client just one ref for a single server node, it will
in time discover all other operating server nodes on that Q network, and will
build up a full local catalog of everything that's available on that Q network.<br>
<br>
Provided that your client is running ok, and has been fed with at least one
ref for a live Q network that contains content, then over time, successive:
<blockquote><code><pre>
qmgr status</pre></code></blockquote>
commands should report increasing values in the fields:
<ul>
<li><b>numPeers</b> - number of peers this client node knows about</li>
<li><b>numLocalItems</b> - number of locally stored content items, ie items
which you have either inserted to, or retrieved from, your client node</li>
<li><b>numRemoteItems</b> - number of unique data items which are available
on remote server nodes in the Q network, and which can be retrieved through
your local client node.</li>
</ul>
<blockquote>
<hr>
<h4>4.7.1. One Big Warning</h4>
If you are participating in more than one distinct Q network, then <b>do not</b>
insert noderefs for different networks into the same running instance of a
local Q client, unless you don't plan on inserting content via that client.<br>
<Br>
For instance, let's say you are participating in two different Q networks:
<ul>
<li>The 'mainstream' Q netowrk</li>
<li>A secret Q network - "My friends' teen angst diaries"</li>
</ul>
If you get a noderef for both these networks, and insert both of these into the
same running Q client node, then this local client node will be transparently
connected to both networks.<br>
<br>
If you only ever plan on retrieving content, and never inserting content, this
won't be a problem, except that you won't be able to tell which content
resides on the mainstream Q network, and which resides in the secret Q network.<br>
<Br>
The big problem arises from inserting content. Whenever you insert data through this
'contaminated'
Q client node, this node picks 3 different servers to which upload a copy of this
data. You won't have any control over whether the data gets inserted to the mainstream
Q network, or your secret Q network. You might insert something sensitive, intending it
to go only into the secret Q network, where in fact it also ends up in the mainstream
network, with consequences you might not want.
</blockquote>
</blockquote>
<hr>
<h3>4.8. Content Data and Metadata</h3>
<blockquote>
Whenever content gets stored on Q, it is actually stored as two separate items:
<ul>
<li>The <b>raw data</b> - whether a text file, or the raw bytes of image files,
audio files etc</li>
<li>The <b>metadata</b>, which contains human-readable and machine-readable
descriptions of the data</li>
</ul>
Metadata consists of a set of <b>category=value</b> pairs.<br>
<br>
Confused yet? Don't worry, I'm confused as well. Let's illustrate this with an
example of metadata for an MP3 audio recording:
<ul>
<li>title=Fight_Last_Thursday.mp3</li>
<li>type=audio</li>
<li>mimetype=audio/mpeg</li>
<li>abstract=upcoming single recorded in our garage last April</li>
<li>keywords=grunge,country,indie</li>
<li>artist=Ring of Fire</li>
<li>size=4379443</li>
<li>contact=ring-of-fire@mail.i2p</li>
<li>key=blah37blah24-yada23hfhyada</li>
</ul>
All metadata categories are optional. In fact, you can insert content with no metadata
at all.<br>
<br>
If you fail to provide metadata when inserting an item, a blank set of metadata will
be created with at least the following categories:
<ul>
<li><b>key</b> - the derived key, under which the item will later be retrievable
by yourself and others</li>
<li><b>title</b> - if not provided at insert time, this will be set to the key</li>
<li><b>size</b> - size of the item's raw data, in bytes</li>
</ul>
Within Q, there is a convention to supply a minimal amount of metadata. While this
is not expected or enforced, including all these categories is most strongly
recommended. These core categories are:
<ul>
<li><b>title</b> - a meaningful title for the data item, consisting only of characters
which are legal in filenames on all platforms, and which ends with a file extension.</li>
<li><b>type</b> - one of a superset of eMule classifiers, such as:
<ul>
<li><b>text</b> - plain text</li>
<li><b>html</b> - HTML content</li>
<li><b>image</b> - content is in an image format, such as .png, .jpg, .gif etc</li>
<li><b>audio</b> - content is an audio sample, such as .ogg, .mp3, .wav etc</li>
<li><b>video</b> - due to the sheer size of video files, and Q's present design,
it's unlikely people will be inserting video content anytime soon (unless it's
very short)</li>
<li><b>archive</b> - packed file collections, such as .tar.gz, .zip, .rar etc</li>
<li><b>misc</b> - content does not fit into any of the above categories</li>
</ul>
</li>
<li><b>mimetype</b> - not as important as the <b>type</b> category, but providing
this category in your metadata is still strongly encouraged. Value for this category
should be one of the standard mimetypes, eg <b>text/html</b>, <b>audio/ogg</b> etc.</li>
<li><b>abstract</b> - a short description (<255 characters), intended for human reading</li>
<li><b>keywords</b> - a comma-separated list of keywords, intended for
machine-readability, should be all lowercase, no spaces</li>
</ul>
Note that you can supply extra metadata categories in addition to the above, and that
people searching for content can search on these extra categories if they know about
them.
</blockquote>
<hr>
<h3>4.9. Searching For Content</h3>
<blockquote>
As mentioned earlier - in constrast with Freenet, local Q nodes build up a complete
catalog of all available content on whatever Q network they are connected to.<br>
<br>
This is a design decision, based on the choice to eliminate query traffic.<br>
<br>
The author hopes that this will result in a distributed storage network with a
high retrievability guarantee, in contrast with freenet which offers no such
guarantee.<br>
<br>
With Freenet, you only ever know of the existence of something if someone tells
you about it.<br>
<br>
But with Q, your local client node builds up a global catalog of everything that's
available within the whole network.<br>
<br>
The QMgr client has a command for searching your Q client node:
<blockquote><code><pre>
qmgr search -m category1=pattern1 category2=pattern2 ...</pre></code></blockquote>
For example:
<blockquote><code><pre>
qmgr search -m type=audio artist=Mozart keywords=symphony</pre></code></blockquote>
or:
<blockquote><code><pre>
qmgr search -m type=text title="bible|biblical|(Nag Hammadi)" keywords="apocrypha|Magdalene"</pre></code></blockquote>
As implied in the latter example, search patterns are regular expressions. This example will
locate all text items, whose <b>title</b> metadata category contains one of <b>bible</b>, <b>biblical</b> or <b>Nag&nbsp;Hammadi</b>, <i>and</i> whose <b>keywords</b> category contains either
or both the words <b>apocrypha</b> or <b>Magdalene</b>.<br>
<br>
Please use the search function carefully, otherwise (if and when Q usage grows) you
could be inundated with thousands or even millions of entries.<br>
<br>
If a search turns up nothing, qmgr will simply exit. But if it turns up one or more items,
it will the items out one at a time, with the key first, then each metadata entry
on an indented line following.
</blockquote>
<hr>
<h3>4.10. Retrieving Content</h3>
<blockquote>
Now, we're actually going to retrieve something.<br>
<br>
Presumably, after following the previous section, you will have seen one or more search
results come up, with the 'keys' under which the items can be accessed.<br>
<br>
Now, choose one of the keys, preferably for a short text item. Try either of the following
commands:
<blockquote><code><pre>
qmgr get &lt;keystring&gt; something.txt</pre></code></blockquote>
<i>or</i>:
<blockquote><code><pre>
qmgr get &lt;keystring&gt; &gt; something.txt</pre></code></blockquote>
(both have the same effect - the first one explicitly writes to the named file, the second
one dumps the raw data to stdout, which we shell-redirect into the file.<br>
<br>
<b><i>Note - redirection of fetched data to a file via shell is not working at present. Use only
the first form till we fix the bug.</i></b>
</blockquote>
<hr>
<h3>4.11. Inserting Content</h3>
<blockquote>
Our last example in this walkthrough relates to inserting content.<br>
<br>
Firstly, create a small text file with 2-3 lines of text, and save it as (say)
myqinsert.txt.<br>
<br>
Now, think of some metadata to insert along with the file. Or, you can just use
the set:
<blockquote><code><pre>
type=text
keywords=test
abstract=My simple test of inserting into Q
title=myqinsert.txt</pre></code></blockquote>
Now, let's insert the file. Ensure your Q client node is running, then type:
<blockquote><code><pre>
qmgr put myqinsert.txt -m type=text keywords=test title="myqinsert.txt" \
abstract="My simple test of inserting into Q"</pre></code></blockquote>
If all went well, this command should produce half a line of gibberish, followed
immediately by your shell prompt, eg:
<blockquote><code><pre>
aRoFC~9MU~pM2C-uCTDBp5B7j79spFD8gUeu~BNkUf0=<b>$</b>
</pre></code></blockquote>
The '$' at the end is your shell prompt, and all the characters before it are the 'key'
which was derived from the content you just inserted.<br>
<br>
To avoid the hassle of copying/pasting the key, you could just add output redirection
to the above command, eg:
<blockquote><code><pre>
qmgr put myqinsert.txt -m type=text keywords=test title="myqinsert.txt" \
abstract="My simple test of inserting into Q" \
> myqinsert.key</pre></code></blockquote>
This will cause the generated key to be written safe and sound into the file
<b>myqinsert.key</b>.<br>
<br>
You can verify that this insert worked by a 'get' command, as in:
<blockquote><code><pre>
qmgr get `cat myqinsert.key` somefilename.ext</pre></code></blockquote>
(Note that this won't work on windows because the DOS shell is irredeemably brain-damaged. If
you're using Windows, you <b>will</b> have to cut/paste the key.
</blockquote>
<hr>
<h3>4.12. Shutting Down your Node</h3>
<blockquote>
If you've worked through to here, then congratulations! You've got your Q Client Node set up
and working, and ready to meet all your distributed file storage and retrieval needs.<br>
<br>
You can leave your client node running 24/7 if you want. In fact, we recommend you keep your
client node running as much of the time as possible, so that you get prompt catalog updates,
and can more quickly stay in touch with new content.<br>
<br>
However, if you need to shut down your node, the command for doing this is:
<blockquote><code><pre>
qmgr stop</pre></code></blockquote>
This command will take a while to complete (since the node has to wait for the I2P
java shutdown hooks to complete before it can rest in peace). But once your node is
shut down, you can start it up again at any time and pick up where you left off.
</blockquote>
<a name="server"/>
<hr>
<h2>5. Running a Q Server Node</h2>
<h3>5.1. Introduction</h3>
<blockquote>
This section describes the requirements for, and procedures involved with, running
a Q Server Node.<br>
<br>
We'll use a similar 'walkthrough' style to that which we used in the previous section
on client nodes.
</blockquote>
<hr>
<h3>5.2. Requirements and Choices</h3>
<blockquote>
Running a Q server is a generous thing to do, and helps substantially with making
Q work at its best for everyone. However, please do make sure you can meet some
basic requirements:
<ul>
<li>You are running a permanent (24/7) I2P Router, on a box with at least (say)
98% uptime.</li>
<li>You have a little bandwidth to spare, and don't mind the extra memory, disk and
CPU-usage footprint of running a fulltime Q server node</li>
<li>You have already been able to successfully run a Q client node.</li>
</ul>
Also, please decide whether you want your server node to contribute to the mainstream
Q network, or whether you want to create your own private Q network, or join someone
else's private network. Your contribution will be most appreciated, though, if you
can run a server within the mainstream Q network.
</blockquote>
<hr>
<h3>5.3. Starting Your Server Node</h3>
<blockquote>
Starting up a Q Server node is very similar to starting up a Q client node, except
that with the qmgr command line, you must put the keyword arg <b>server</b> before the
command word. So the command is:
<blockquote><code><pre>
qmgr server start</pre></code></blockquote>
Similar to Q client nodes, you can check the status of a running Q server node with
the command:
<blockquote><code><pre>
qmgr server status</pre></code></blockquote>
(Note that this command will take longer to complete than with client nodes, because
the communication passes through a multi-hop I2P tunnel, rather than just through
localhost TCP).<br>
<br>
If the status command succeeds, then you'll know your new Q Server Node is happily
running in background.
</blockquote>
<hr>
<h3>5.4. Joining A Q Network</h3>
<blockquote>
When a Q Server node starts up for the first time, it is in a private network
all by itself.<br>
<br>
If you want to link your server into an existing Q network, you'll have to add a
noderef for at least one other server on that network. The command to do this
is similar to that for subscribing a client node to a network:
<blockquote><code><pre>
qmgr server addref &lt;noderef-file&gt;</pre></code></blockquote>
where &lt;noderef-file&gt; is a file into which you've saved the noderef for
the network you want to join.
<blockquote>
<hr><i><small>
Recall from the section on client nodes that the authoritative noderefs
for the mainstream Q network can be downloaded from
<a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a>.
</small></i><hr>
</blockquote>
After you've added the noderef, subsequent <b>qmgr server status</b> commands
should show <b>numPeers</b> having a value of at least 1 (and growing, as more
server nodes come online in the mainstream Q network.)
</blockquote>
<hr>
<h3>5.5. Private Networks - Exporting Your Server's Noderef</h3>
<blockquote>
If you're planning to start your own private Q network, and want to include other
server operators in this network, then you'll have to export your server's noderef
and make it available to the others you want to invite into your network.<br>
<br>
The command to export your Q Server noderef is:
<blockquote><code><pre>
qmgr server getref &lt;noderef-file&gt;</pre></code></blockquote>
This will extract the <i>I2P Destination</i> of your running server node, and
write it into &lt;noderef-file&gt;. You can then privately share this file with
others who you want to invite into your private network. Each recipient of
this file will do a <b>qmgr server addref &lt;noderef-file&gt;</b> command
to import your ref into their servers.<br>
<br>
Don't forget that if you're running, or participating in, a private Q network, then
you'll need to run a separate client for accessing this network, separate from any
mainstream Q network client you may already be running.<br>
<br>
To start this extra client, you'll have to choose a directory where you want this
client to reside, a port number you want your client to listen on locally for
user commands, and run the command:
<blockquote><code><pre>
qmgr -dir /path/to/my/new/client -port &lt;portnum&gt; start</pre></code></blockquote>
You need the <b>-port &lt;portnum&gt;</b> command, because otherwise it'll fail
to launch (if you already have a client node running off the mainstream Q network).<br>
<br>
This will create, and launch, a new instance of a Q client, accessing your private
Q network. Don't forget to import your server's noderef into this client. Also,
note that you'll have to use this same <b>-port &lt;portnum&gt;</b> argument when
doing any operation on this client instance, such as get, put, status, search.
</blockquote>
<a name="qmgr"/>
<hr>
<h2>6. About the qmgr Utility</h2>
qmgr (or, to people fluent in Java, <b>net.i2p.aum.q.QMgr</b>), is just one simple
Q client application, that happens to be bundled in with the Q distro.<br>
<br>
It is by no means the only, or even main facility for accessing the Q network. We
anticipate that folks will write all manner of client apps, including fancy GUI
apps.<br>
<br>
Anyway, qmgr does give you a rudimentary yet workable client for basic access
to the Q network. Until fancy apps get written, qmgr will have to do.<br>
<br>
Don't forget that qmgr has very detailed inbuilt help. Run:
<blockquote><code><pre>
qmgr help</pre></code></blockquote>
for a quick help summary, or:
<blockquote><code><pre>
qmgr help verbose</pre></code></blockquote>
for the 'War and Peace' treatise.<br>
<br>
<blockquote><hr>
One crucial concept to remember with qmgr is that client and server node instances
are uniquely identified by the directories at which they reside. If you are running
multiple server and/or client instances, you can specify an instance with the
<b>-dir &lt;dirpath&gt;</b> option - see the help for details.
<hr></blockquote>
<hr>
One last note - we strongly discourage any writing of client apps that spawn a qmgr
process, pass it arguments and parse its results. This is most definitely a path to
pain, since qmgr's shell interface is subject to radical change at any time without
notice.<br>
<br>
qmgr is for human usage, or at most, inclusion in init/at/cron scripts. Please respect
this.<br>
<br>
If you want to write higher-level clients, your best course of action is to use the
official client api library, which we anticipate will have versions available in
Java, Python, Perl and C++. If you want to write in another language, such as
OCaml, Scheme etc, then the existing api lib implementations should serve as an excellent
reference to support you in writing a native port for your own language.
<a name="contact"/>
<hr>
<h2>8. Contacting the Author</h2>
I am <b>aum</b>, and can be reached as <b>aum</b> on in-I2P IRC networks, and also
at the in-I2P email address of <b>aum@mail.i2p</b>.<br>
<br>
<hr>
<center>
Return to <a href="../index.html">Q Homepage</a><br>
<br>
<small>
<a href="#intro">Introduction</a> |
<a href="#checklist">Checklist</a> |
<a href="#serverorclient">Server?orClient?</a> |
<a href="#walkthrough">Walkthrough</a> |
<a href="#server">Server Nodes</a> |
<a href="#qmgr">About QMgr</a> |
<a href="#contact">Contact us</a>
</small>
</center>
<hr>
<!-- Created: Fri Apr 1 11:03:27 NZST 2005 -->
<!-- hhmts start -->
Last modified: Sun Apr 3 20:06:53 NZST 2005
<!-- hhmts end -->
</body>
</html>

23
apps/q/doc/manual/notes Normal file
View File

@ -0,0 +1,23 @@
rise on each hit:
dy = (1 - y) / kRise
fall after each time unit:
dy = y / kFall
fall after time dt:
dy = - y ** - (dt / kFall)
after the next hit:
y = y - y ** (- dt / kFall) + (1 - y) / kRise
first attempt at a load measurement algorithm:
- kFall is an arbitrary constant which dictates decay rate of load
in the absence of hits
- kRise is another constant which dictates rise of load with each hit
- dt is the time between each hit

372
apps/q/doc/metadata.html Normal file
View File

@ -0,0 +1,372 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q Metadata Specification</title>
<style type="text/css">
<!--
td { vertical-align: top; }
code { font-family: courier, monospace; font-weight: bolder; font-size:smaller }
-->
</style>
</head>
<body>
<h1>Q Metadata Specification</h1>
<h2>1. Introduction</h2>
This document lists the standard metadata keys for Q data items,
discussing the rules of metadata insertion, processing and validation.<br>
<hr>
<h3>1.1. Definitions</h3>
To avoid confusions in terminology, this document will strictly abide the following definitions:
<br>
<br>
<table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
<tr style="font-weight: bold">
<td>Term</td>
<td>Definition</td>
</tr>
<tr>
<td><code>key</code></td>
<td>A metadata category name, technically a <code>key</code> as the word is used with
Java <code>Hashtable</code> and Python <code>dict</code> objects.</td>
</tr>
<tr>
<td><code>uri</code></td>
<td>A Uniform Resource Indicator for an item of content stored within the Q network.<br>
Q URIs have the form: <code>Q:&lt;basename&gt;[,&lt;cryptoKey&gt;][&lt;path&gt;]</code>
<br>
<br>
Some examples:
<ul>
<li><code>Q:fhvnr3HFSK234khsf90sdh42fsh</code> (a plain hash uri, no cryptoKey)</li>
<li><code>Q:e54fhjeo39schr2kcy4osEH478D/files/johnny.mp3</code> (a secure space URI,
no cryptoKey)</li>
<li><code>Q:vhfh4se987WwfkhwWFEwkh3234S,47fhh2dkhseiyu</code> (a plain hash URI, with
a cryptoKey)</li>
</td>
</tr>
<tr>
<td><code>basename</code></td>
<td>The basic element of a Q uri. This will be a base64-encoded hash - refer below to
URI calculation procedures</td>
</tr>
<tr>
<td><code>cryptoKey</code></td>
<td>An optional session encryption key for the stored data, encoded as base64.
This affords some protection to server node operators, and gives them a level
of plausible deniability for whatever gets stored in their server's
datastore without their direct human awareness.</td>
</tr>
<tr>
<td><code>path</code></td>
<td>Whever an item of content is inserted in <code>secure space</code> mode, this path
serves as a pseudo-pathname, and is conceptually similar to the <code>path</code>
component in (for example) standard HTTP URLs
<code>http://&lt;domainname&gt;[:&lt;port&gt;][&lt;path&gt;]</code>, such as
<code>http://slashdot.org/faq/editorial.shtml</code> (whose <code>path</code>
is <code>/faq/editorial.shtml</code>).<br>
<br>
Paths, if not empty, should contain a leading slash ("/").
If an application specifies a non-empty <code>path</code> that doesn't begin with a
leading '/', a '/' will be automatically prepended by the receiving node.
</td>
</tr>
<tr>
<td><code>plain hash</code></td>
<td>A mode of inserting items, whereby the security of the resulting URI comes from
computing the URI from a hash of the item's data and metadata (and imposing a
mathematical barrier against spoofing content under a given URI). Corresponds to
Freenet's <code>CHK@</code> keys.</td>
</tr>
<tr>
<td><code>secure space</code></td>
<td>A mode of inserting items where the security of the URI is based not on a hash of the
item's data and metadata (as with <code>plain hash</code> mode),
but on the <code>privateKey</code> provided by the
application, and a content signature created from that private key.
Corresponds to Freenet's <code>SSK@</code> keys. Within a secure space, you
can insert any number of items under different pseudo-pathnames (as is the case
with Freenet SSK keys).
</li>
</table>
<br><br>
<hr>
<h3>2.1. Keys Inserted By Application Before sending <code>putItem</code> RPCs</h3>
As the heading suggests, this is a list of metadata keys which should be inserted by a
Q application prior to invoking a <code>putItem</code> RPC on the local Q client node.<br>
<br>
<table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
<tr style="font-weight: bold">
<td>Key</td>
<td>Data Type</td>
<td>Description</td>
</tr>
<tr>
<td><code>title</code></td>
<td>String</td>
<td>Optional but strongly recommended. A free-text short description of the item,
should be less than 80 characters. The idea is that applications should
support a 'view' of catalogue data that shows item titles. (Prior Q convention of
titles expressed as valid filename syntax has been abandoned).
</td>
</tr>
<tr>
<td><code>path</code></td>
<td>String</td>
<td>Optional but strongly recommended.
A virtual 'pathname' for the item, which should be in valid *nix
absolute pathname syntax (beginning with '/', containing no '//', consisting
only of alphanumerics, '-', '_', '.' and '/'.<br>
<br>
In Q web interfaces, the <code>filename</code> component of this path will
serve as the recommended filename when downloading/saving the item.<br>
<br>
If the application also provides a
<code>privateKey</code> key, the path
is used in conjunction with the private key to generate <code>publicKey</code>
and <code>signature</code> keys (see below), and ultimately the final <code>uri</code>
under which the item can be retrieved by others.<br>
<br>
Refer also to <code>mimetype</code> below.
</td>
</tr>
<tr>
<td><code>encrypt</code></td>
<td>String</td>
<td>Optional. If this key is present, and has a value "1", "yes" or "true",
this indicates that the application wishes the data to be stored on servers in
encrypted form.<br>
<br>
If this key is present and set to a positive value, the Q node, on receiving the
<code>putItem</code> RPC, will:
<ol>
<li>Generate a random symmetric encryption key</li>
<li>Encrypt the item's data using this encryption key</li>
<li>Delete the <code>encrypt</code> key from the metadata</li>
<li>Enclose a base64 representation of this encryption key in the RPC response
it sends back to the application (embedded in the <code>uri</code></li>
</ol>
</td>
</tr>
<tr>
<td><code>type</code></td>
<td>String</td>
<td>Optional but strongly recommended. A standard ed2k specifier, one of <code>text html image
audio video archive other</code></td>
</tr>
<tr>
<td><code>mimetype</code></td>
<td>String</td>
<td>Optional but moderately recommended. Mimetype designation of data, eg <code>text/html</code>,
<code>image/jpeg</code> etc. If not specified, an attempt will be made to guess
a mometype from the value of the <code>path</code> key. If this attempt fails, then
this key will be set to <code>application/x-octet-stream</code> by the node receiving
the <code>putItem</code> RPC.</td>
</tr>
<tr>
<td><code>keywords</code></td>
<td>String</td>
<td>Optional but moderately recommended.
A set of keywords, under which the inserting app would like this item to be
discoverable. Keywords should be entirely lower case and comma-separated. Content
inserts should consider keywords carefully, and only use space characters inside
keywords when necessary (eg, for flagging a distinctive phrase containing very
common words).</td>
<tr>
<td><code>privateKey</code></td>
<td>String</td>
<td>Optional. A Base64-encoded signing private key, in cases where the application wishes
to insert an item in <code>signed space</code> mode. This can be accompanied by another key,
<code>path</code>, indicating a 'path' within the signed space. If 'path'
is not given, it will default to '/'.<br>
<br>
Either way, when a node receives a
<code>putItem</code> RPC containing a <code>privateKey</code> in its metadata,
it removes this key and replaces it with <code>publicKey</code> and
<code>signature</code>.
</td>
</tr>
<tr>
<td><code>path</code></td>
<td>String</td>
<td>Optional. The virtual pathname, within signed space, under which to store the item.
This gets ignored and deleted unless the application also provides a
<code>privateKey</code> as well. But if the private key is given, the path
is used in conjunction with the private key to generate <code>publicKey</code>
and <code>signature</code> keys (see below).<br>
<code>path</code> should be a 'unix-style pathname', ie, containing only slashes
as (pseudo) directory delimiters, and alphanumeric, '-', '_' and '.' characters,
and preferably ending in a meaningful file extension such as <code>.html</code>
</td>
</tr>
<tr>
<td><code>expiry</code></td>
<td>int</td>
<td>Unixtime at which the inserted item should expire. When this expiry time
is reached, the item won't necessarily be deleted straight away, but may
be deleted whenever a node's data store is full.<br>
<br>
If this is not provided, it will default to a given duration according to
the client node's configuration.<br>
<br>
If it is provided, by an application, then the client node will transparently
generate the required 'rent payment' before caching the data item and uploading
it to servers.
</td>
</tr>
</table>
<br><br>
<hr>
<h3>2.2. Keys Inserted By Node Upon Receipt Of <code>putItem</code> RPC</h3>
<table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
<tr style="font-weight: bold">
<td>Key</td>
<td>Data Type</td>
<td>Description</td>
</tr>
<tr>
<td><code>size</code></td>
<td>Integer</td>
<td>Size of the data to be inserted, in bytes.</td>
</tr>
<tr>
<td><code>dataHash</code></td>
<td>String</td>
<td>base64-encoded SHA256 hash of data.</td>
</tr>
<tr>
<td><code>uri</code></td>
<td>String</td>
<td>This depends on whether the item is being inserted in <i>plain</i> or
<i>signed space</i> mode.<br>
<br>
If inserting in <i>plain</i> mode, then the uri is in the form
<code>Q:somebase64hash</code>, where the hash is computed according to
the <a href="#plainhash">plain hash calculation procedure</a>.<br>
<br>
If inserting in <i>signed space</i> mode, then the uri will be in the form
<code>Q:somebase64hash/path.ext</code>, where the hash is computed as per
the <a href="#signedhash">signed space hash calculation procedure</a>, and
the <code>/path.ext</code> is the verbatim value of the app-supplied
<code>path</code> key.
</td>
</tr>
<tr>
<td><code>publicKey</code></td>
<td>String</td>
<td>Base64-encoded signing public key. In cases where app provides
<code>privateKey</code>,
a node will derive the signing public key from the private key,
delete the private key from the metadata, and replace it with its corresponding
public key
key.</td>
</tr>
<tr>
<td><code>signature</code></td>
<td>String</td>
<td>Base64-encoded signature of <code>path+dataHash</code>, created using
the app-provided <code>privateKey</code>.</td>
</tr>
<tr>
<td><code>rent</code></td>
<td>String</td>
<td>A rent payment for the data's accommodation on the server.<br>
Intention is to support a variety of payment tokens. Initially, the
only acceptable form of payment will be a hashcash-like token,
in the form <code>hashcash:base64string</code>. The <code>hashcash:</code>
prefix indicates that this payment is in hashcash currency, in which case
the <code>base64String</code> should decode to a 16-byte string whose
SHA256 hash partially collides with <code>dataHash</code>.
The greater the number of bits in the collision,
the longer the data's accommodation will be 'paid up for'.<br>
<br>
If this key is already present, a Q node will verify the hashcash,
and adjust the <code>expiry</code> key value to the time the item's accommodation
is paid up till.<br>
<br>
If the key is not present:
<ul>
<li>A client node will generate a value for this key with enough collision bits
to pay the accommodation up till the given app-specified <code>expiry</code> date.</li>
<li>A server node will grant temporary free accommodation, and adjust the <code>expiry</code>
key to the end of the free accommodation period.</li>
</ul>
</td>
</tr>
</table>
<br><br>
<a name="plainhash"/>
<hr>
<h2>3. URI Determination Procedures</h2>
<h3>3.1. Plain Hash URI Calculation Procedure</h3>
When items are inserted in <code>plain</code> mode, the final URI is determined from
a hash of the data and metadata. Security of the item is based on the mathematical difficulty
of creating an arbitrary data+metadata set whose hash collides with the target URI.<br>
<br>
Specifically, the recipe for calculating plain hash URIs is:
<ol>
<li>If the key <code>size</code> is missing, set this to the size of the data,
in bytes</li>
<li>If the key <code>dataHash</code> is missing, set this to the base64-encoded
SHA256(data)</li>
<li>If the key <code>title</code> is missing, set this to the value of <code>dataHash</code></li>
<li>From the metadata, create a set of strings, each in the form <code>key=value</code>,
where each line contains a metadata <code>key</code> and its <code>value</code>, and
is terminated by an ASCII linefeed (\n, 0x10).</li>
<li>Ensure that key <code>uri</code> is omitted</li>
<li>Sort the strings into ascending ASCII sort order</li>
<li>Concatenate the strings together into one big string</li>
<li>Calculate the SHA256 hash of this string</li>
<li>Encode the hash into Base64</li>
<li>Prepend the string <code>Q:</code> to this</li>
</ol>
<a name="signedhash"/>
<hr>
<h3>3.2. Signed Space URI Calculation Procedure</h3>
This is much simpler than determining plain hash URI, since the security of the URI
is based not on hashes of data and metadata, but on the cryptographic <code>privateKey</code>
given by the application.<br>
<br>
Calculation recipe for Signed Space URIs is:
<ol>
<li>Calculate the SHA256 hash of the private key's binary data (not its base64 representation)</li>
<li>Encode this hash into base64, dropping any trailing '=' characters</li>
<li>Append to this the value of metadata item <code>path</code> (recall that <code>path</code>,
if not empty, must begin with a '/')</li>
<li>Prepend the string <code>Q:</code> to this</li>
</ol>
The resulting URI then is in the form <code>Q:pubkeyHash/path.ext</code>
<hr>
<!-- Created: Tue Apr 5 00:56:45 NZST 2005 -->
<!-- hhmts start -->
Last modified: Wed Apr 6 00:36:37 NZST 2005
<!-- hhmts end -->
</body>
</html>

BIN
apps/q/doc/overall.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

1
apps/q/doc/qnoderefs.txt Normal file
View File

@ -0,0 +1 @@
rxvXpHKfWGWsql4PJaHglAERSUYyrdKKAzK6jPHT4QXRf9jgcVd4mInq0j6H4inVOzT9dG4L6c9GrlQwe4ysUm5jSTyZemxiZpQDCAazsoRzNDv6gevA40J6uGl10JtVtOjqXW8Ej0JUKubz88g~ogPb1h4Xibc-RrtqrvsJebg5xYFkLlnr7DxDtiWzIMRSZ9Ri2P~eq0SwZzd81tvASPj5fb3nySHeABAuY8HrNu0gqRLjeayDpd3OK1ogrxf1lMvfutn5pnLrlVcvKHa~6rNWWGSulsuEYWtpUd4Itj9aKqIgF9ES7RF77Z73W1f6NRTHO48ZLyLLaKVLjDIsHQP-0mOevszcPjFWtheqRKvT2D28WEMpVC-mPtfw91BkdgBa3pwWhwG~7KIhvWhGs8bj2NOKkqrwYU7xhNVaHdDDkzv4gsweCutHNiiCF~4yL54WzCIfSKDjcHjQxxVkh2NKeaItzgw9E~mPAKNZD22X~2oAuuL9i~0lldEV1ddUAAAA

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
apps/q/doc/screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
apps/q/doc/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -0,0 +1,23 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q Screenshots</title>
</head>
<body>
<h1>Q Screenshots</h1>
<ul>
<li><a href="screenshot-search.jpg">Search Screen</li>
<li><a href="screenshot-qsite.jpg">QSite Insertion Form</li>
<li><a href="screenshot-iewarn.jpg">Q Security Features</li>
</ul>
<hr>
<address><a href="mailto:aum@mail.i2p">aum</a></address>
<!-- Created: Sat Apr 16 17:24:02 NZST 2005 -->
<!-- hhmts start -->
Last modified: Mon Apr 18 14:06:02 NZST 2005
<!-- hhmts end -->
</body>
</html>

1460
apps/q/doc/spec/index.html Normal file

File diff suppressed because it is too large Load Diff

90
apps/q/java/build.xml Normal file
View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="aum">
<!-- Written to assume that classpath is rooted in the current directory. -->
<!-- So this should be OK if you make this script in the root of a filesystem. -->
<!-- If not, just change src.dir to be the root of your sources' package tree -->
<!-- and use e.g. View over a Filesystem to mount that subdirectory with all capabilities. -->
<!-- The idea is that both Ant and NetBeans have to know what the package root is -->
<!-- for the classes in your application. -->
<!-- Don't worry if you don't know the Ant syntax completely or need help on some tasks! -->
<!-- The standard Ant documentation can be downloaded from AutoUpdate and -->
<!-- and then you can access the Ant manual in the online help. -->
<target name="init">
<property location="build" name="classes.dir"/>
<property location="src" name="src.dir"/>
<property location="doc/q/api" name="javadoc.dir"/>
<property name="project.name" value="${ant.project.name}"/>
<property location="${project.name}.jar" name="jar"/>
<property location="q.war" name="war"/>
</target>
<target name="builddep">
<ant dir="../../i2ptunnel/java/" target="build" />
<!-- i2ptunnel builds ministreaming and core -->
</target>
<target depends="init,builddep" name="compile">
<!-- Both srcdir and destdir should be package roots. -->
<mkdir dir="${classes.dir}"/>
<javac debug="true"
deprecation="true"
destdir="${classes.dir}"
srcdir="${src.dir}"
classpath="xmlrpc.jar:../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar:../../i2ptunnel/java/build/i2ptunnel.jar" >
<!-- To add something to the classpath: -->
<!-- <classpath><pathelement location="${mylib}"/></classpath> -->
<!-- To exclude some files: -->
<!-- <exclude name="com/foo/SomeFile.java"/><exclude name="com/foo/somepackage/"/> -->
</javac>
</target>
<target depends="init,compile" name="jar">
<!-- To make a standalone app, insert into <jar>: -->
<!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
<!-- <jar basedir="${classes.dir}" compress="true" jarfile="${jar}"> -->
<jar compress="true" jarfile="${jar}">
<!-- <jar basedir="." compress="true" jarfile="${jar}" includes="**/*.class,doc/**/*.html"> -->
<fileset dir="${classes.dir}"/>
<fileset dir="." includes="qresources/**"/>
<manifest>
<attribute name="Main-Class" value="net.i2p.aum.q.QMgr"/>
<attribute name="Class-Path" value="i2p.jar xmlrpc.jar mstreaming.jar streaming.jar jbigi.jar"/>
</manifest>
</jar>
</target>
<target depends="init,compile" name="war">
<!-- To make a standalone app, insert into <jar>: -->
<!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
<war compress="true" jarfile="${war}" webxml="web.xml">
<!-- <fileset file="build/net/i2p/aum/q/QConsole.class"/> -->
<classes dir="build" includes="**/QConsole.class"/>
<classes dir="build" includes="**/HTML/**"/>
<!-- <fileset includes="**/HTML/*.class"/> -->
<lib file="xmlrpc.jar"/>
</war>
</target>
<target depends="init,jar,war" description="Build everything." name="all"/>
<target depends="init" description="Javadoc for my API." name="javadoc">
<mkdir dir="${javadoc.dir}"/>
<javadoc destdir="${javadoc.dir}" packagenames="*">
<sourcepath>
<pathelement location="${src.dir}"/>
</sourcepath>
<sourcepath>
<pathelement location="/java/xmlrpc-1.2-b1/src/java"/>
</sourcepath>
</javadoc>
</target>
<target depends="init" description="Clean all build products." name="clean">
<delete dir="${classes.dir}"/>
<delete dir="${javadoc.dir}"/>
<delete file="${jar}"/>
</target>
</project>

View File

@ -0,0 +1,10 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Item Not Found</td>
</tr>
<tr>
<td align=center>Failed to retrieve item:<br>
<code><tmpl_var 404_uri></code>
</td>
</tr>
</table>

View File

@ -0,0 +1,27 @@
<TABLE align="center" class="mainpaneitem">
<TR>
<TD class="formHeading">About Q</TD>
</tr>
<tr class="formBody">
<TD align=center>
<p>Version
<tmpl_if version>
<tmpl_var version></p>
<tmpl_else>
0.0.1
</tmpl_if>
<p>Designed and engineered by <a href="mailto:aum@mail.i2p">aum</a></p>
<p>Copyright &copy; 2005 by aum.
<br>Released under the
<br>GNU Lesser General Public License</p>
<p style="font-size: smaller; font-style: italic">
Many thanks to jrandom, smeghead and frosk<br>
for their patient and knowledgeable support<br>
in helping this python programmer<br>
get partly proficient in java.</p>
</TD>
</TR>
</TABLE>

View File

@ -0,0 +1,40 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading">Add Hub Noderef</td>
</tr>
<form action="/tools" method="POST" class="formBody">
<input type="hidden" name="cmd" value="addref">
<tr>
<td align="center">
<table width=70%>
<tr>
<td>
In order for your node to join a Q network, it must have a ref to at
least one of the running Q Hubs. You need to get a Q Hub noderef and
paste it here.
</td>
</tr>
<tr>
<td align=center>
<textarea name="noderef" cols=40 rows=6></textarea>
</td>
</tr>
<tr>
<td align="center">
<input type="submit" name="submit" value="Add Hub Noderef!">
</td>
</tr>
</table>
</td>
</tr>
</form>
</table>

View File

@ -0,0 +1,29 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td>
<p>You are attempting to view a QSite via the Internet Explorer web browser.
</p>
<p>We have blocked the connection to protect your anonymity.
</p>
<p>As a matter of policy, Q does not support QSite browsing via Microsoft
Internet Explorer (MSIE), because of that browser's abysmal track record with
regard to security. If we did allow you to browse Q via MSIE, it would
be easy for a malicious QSite author to embed hostile content which
undermines your computer's security and compromises your anonymity.
</p>
<p>If you want to surf I2P QSites, you'll need to use a more secure web
browser such as <a href="http://www.mozilla.org">Mozilla or Firefox</a>.
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,52 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td>
<p>You are attempting to view a QSite via an unprotected link.
</p>
<p>We have blocked the connection to protect your anonymity. If we don't
do this, then a malicious QSite author can insert content into the QSite
which triggers oubound hits to arbitrary servers on the mainstream web,
which in turn can easily reveal a lot of personal information about you
and the QSite you are accessing.
</p>
<p>If you want to browse QSites with your web browser with greater safety,
you'll have to follow these simple steps:
</p>
<ol>
<li>Edit your I2P <code>hosts.txt</code> file and add the following entry
(all on one line):
<blockquote><code><textarea rows=5 cols=50>q.i2p=<tmpl_var dest></textarea></code><blockquote>
(and make sure you do NOT reveal this entry to anyone else)
</li>
<li>Configure one of your web browsers to use the proxy server:
<blockquote><code>localhost:4444</code></blockquote>
(or whatever address you have configured your I2P EEProxy to listen on,
if you've changed it)<br><br>
</li>
<li>Start up the browser you have just configured, and enter the web address:
<blockquote><code>http://q.i2p</code></blockquote>
</li>
</ol>
<p>Even if you do this, you still won't have a 100% guarantee of anonymity, since
a malicious QSite author can send code to your browser which exploits a vulnerability
in the browser to compromise your anonymity (eg, accessing third party cookies, executing
arbitrary code, accessing your local filesystem, uploading compromising information
about you to hostile I2P EEPsites etc). But you can relax (somewhat) in the knowledge
that such attacks are much more difficult, particularly if you use a decent web browser.
</p>
<p>Thank you for your co-operation. We wish you happy and safe browsing.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,9 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Downloads</td>
</tr>
<tr>
<td>Not implemented yet</td>
</tr>
</table>

View File

@ -0,0 +1,28 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Generate New Keypair</td>
</tr>
<tr>
<td align="center">
<table>
<tr>
<td>
Click the button to generate a new keypair for
future inserts of signed space keys.
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center">
<form action="/tools" method="POST" class="formBody">
<input type="hidden" name="cmd" value="genkeys">
<input type="submit" name="submit" value="Generate Keys!">
</form>
</td>
</tr>
</table>

View File

@ -0,0 +1,32 @@
<table align="center" class="mainpaneitem">
<tr>
<td colspan=2 class="formHeading">Your New Keys</td>
</tr>
<tr>
<td align="center" colspan=2>
<table>
<tr>
<td>
Please save these keys in a safe place,
preferably on an encrypted partition:
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>Public Key: </td>
<td>
<input readonly type="text" size=32 style="font-family: courier, monospace" value="<tmpl_var publickey>">
</td>
</tr>
<tr>
<td>Private Key: </td>
<td><input readonly type="text" size=32 style="font-family: courier, monospace" value="<tmpl_var privatekey>"></td>
</tr>
</table>

View File

@ -0,0 +1,15 @@
<table align="center" class="mainpaneitem" border=0>
<TR>
<TD class="formHeading" colspan="3">Retrieve Content</TD>
</tr>
<tr class="formBody">
<form action="/home" method="POST">
<input type="hidden" name="cmd" value="get">
<TD>URI:</TD>
<td><INPUT type="text" name="uri" value="Q:" size="60" maxlength="128"></td>
<td><INPUT type="submit" name="submit" value="Retrieve it"></td>
</form>
</TR>
</table>

View File

@ -0,0 +1,11 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Help</td>
</tr>
<tr>
<td>
Not written yet
</td>
</tr>
</table>

View File

@ -0,0 +1,34 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading" colspan=2>Current Background Jobs</td>
</tr>
<tr>
<td>
<tmpl_if items>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td><b>Secs From Now</b></td>
<td><b>Description</b></td>
</tr>
<tmpl_loop items>
<tr>
<td><tmpl_var future></td>
<td><tmpl_var description></td>
</tr>
</tmpl_loop>
</table>
<tmpl_else>
<tr><td colspan=2 align=center><i>(No current jobs)</i></td></tr>
</tmpl_if>
</td>
</tr>
</table>

View File

@ -0,0 +1,122 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<TITLE>Q Web Interface</TITLE>
<style type="text/css">
<!--
body { font-family: helvetica, arial, sans-serif; background-color: #000080 }
td { }
code { font-family: courier, monospace; font-weight: bolder; font-size:smaller }
.logocell {
border-color: #ffe000;
border-width: 1px;
border-top-style:none;
border-bottom-style:solid;
border-left-style:none;
border-right-style:none;
text-align: right;
font-weight: bold;
color: #f0f0ff;
}
.tabcell {
font-weight: bold;
background-color: #fffff0;
padding:5px;
border-color:#ffe000;
border-width: 1px;
border-top-style:solid;
border-bottom-style:none;
border-left-style:solid;
border-right-style:solid;
}
.tabcell_inactive {
background-color: #f0f0ff;
padding:5px;
border-color:#ffe000;
border-width: 1px;
border-top-style:solid;
border-bottom-style:solid;
border-left-style:solid;
border-right-style:solid;
}
.tablink {
font-size: smaller;
text-decoration: none;
}
.mainpane { border-color:#ffe000;
border-width: 1px;
border-top-style:none;
border-bottom-style:solid;
border-left-style:solid;
border-right-style:solid;
}
.mainpaneitem1 { border-style:solid; border-color:#0000ff; border-width: thin; }
.mainpaneitem { border-style:solid;
border-color:#0000ff;
border-width: thin;
background-color: #f0f0ff;
}
.formHeading { font-size:larger; font-weight: bolder; text-align:center; }
.btn1 { font-weight: bold;
}
input1 { background-color: #fffff0;
color: #000080;
}
-->
</style>
</head>
<body bgcolor="#ffffff">
<TABLE width="99%" height=99% align="center" cellspacing="0" cellpadding="0" border=0>
<TR>
<TD>
<table width="100%" cellspacing=0 cellpadding=0>
<tr>
<td>
<table align=right cellspacing=0 cellpadding=0 border=0>
<tr>
<tmpl_loop tabs>
<td class=<tmpl_if active>"tabcell"<tmpl_else>"tabcell_inactive"</tmpl_if>>
<a class="tablink" href="/<tmpl_var name>"><tmpl_var label></a>
</td>
</tmpl_loop>
<tmpl_if _ignore>
<TD class="tabcell_inactive">
<a class="tablink" href="/status">Status</a>
</TD>
<TD class="tabcell">
<a class="tablink" href="/settings">Settings</a>
</TD>
</tmpl_if>
</tr>
</table>
</td>
<TD width=100% class="logocell">
Q <tmpl_var nodeType> Node
</TD>
</TR>
</table>
</TD> </TR>
<tr height=99% bgcolor="#fffff0">
<TD class="mainpane">
<br>
<center>
<tmpl_loop items>
<br>
<tmpl_var item>
<br>
</tmpl_loop>
</center>
</TD>
</tr>
</TABLE>
</body>
</html>

View File

@ -0,0 +1,29 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td>
<p>You are attempting to view a QSite via the Internet Explorer web browser.
</p>
<p>We have blocked the connection to protect your anonymity.
</p>
<p>As a matter of policy, Q does not support QSite browsing via Microsoft
Internet Explorer (MSIE), because of that browser's abysmal track record with
regard to security. If we did allow you to browse Q via MSIE, it would
be easy for a malicious QSite author to embed hostile content which
undermines your computer's security and compromises your anonymity.
</p>
<p>If you want to surf I2P QSites, you'll need to use a more secure web
browser such as <a href="http://www.mozilla.org">Mozilla or Firefox</a>.
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,10 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Error Inserting <tmpl_if is_site>QSite<tmpl_else>Item</tmpl_if></td>
</tr>
<tr>
<td align=center>Your insert failed because:<br>
<code><tmpl_var error></code>
</td>
</tr>
</table>

View File

@ -0,0 +1,139 @@
<TABLE align="center" class="mainpaneitem" width=90% >
<TR>
<TD class="formHeading" colspan="2">Insert Content</TD>
</tr>
<TR>
<TD colspan="2" align=center><a href="/insert?mode=site">Insert A QSite Instead</a></TD>
</tr>
<tr><td colspan=2><hr></td></tr>
<form method="POST" ENCTYPE="multipart/form-data" action="/insert" class="formBody">
<!-- <form method="POST" action="/insert" class="formBody"> -->
<input type=hidden name="cmd" value="put">
<tr>
<td valign=top>Type: </td>
<td style="font-size:smaller">
<input type="radio" name="type" value="text">Text
<input type="radio" name="type" value="html">HTML
<input type="radio" name="type" value="image">Image
<input type="radio" name="type" value="audio">Audio
<input type="radio" name="type" value="video">Video
<input type="radio" name="type" value="archive">Archive
<input type="radio" name="type" value="other" checked>Other
</td>
</tr>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Title: </TD>
<td>
<input type="text" name="title" size="60" maxlength="58">
<div style="font-style: italic; font-size: smaller">
(A short descriptive title for this item)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Path: </TD>
<td>
<input type="text" name="path" size="60" maxlength="128">
<div style="font-style: italic; font-size: smaller">
(If you're inserting in plain-hash mode, this should be a suggested
filename for people to save the item as when downloading it; If you're
inserting in signed-space mode, this can be a longer pathname)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Mimetype: </TD>
<td>
<input type="text" name="mimetype" size="40" maxlength="40">
<div style="font-style: italic; font-size: smaller">
(Leave blank unless you know exactly what this means)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Keywords: </TD>
<td>
<input type="text" name="keywords" size="60" maxlength="80">
<div style="font-style: italic; font-size: smaller">
(Comma-separated list of short descriptive keywords)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Abstract: </TD>
<td>
<textarea name="abstract" cols="50" rows="2"></textarea>
<div style="font-style: italic; font-size: smaller">
(A short descriptive summary for the item, preferably no
longer than 256 characters)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>File: </TD>
<td>
<INPUT type="file" name="data" size="40" maxlength="128">
<div style="font-style: italic; font-size: smaller">
(Leave this blank if you are entering the raw data in the text box below)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Private Key: </TD>
<td>
<INPUT type="text" name="privkey" size="30" maxlength="60">
<div style="font-size: smaller; font-style: italic">
(Only if inserting in signed space mode)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD colspan=2 align=center>
<INPUT class="btn" type="submit" name="submit" value="Insert it">
</TD>
</tr>
<tr><td colspan=2><hr></td></tr>
<tr>
<td valign=top>Raw Data:</td>
<td>
<div style="font-size: smaller; font-style:italic">
(You may enter the raw data here as text instead of selecting a
file above)
</div>
<textarea name="rawdata" cols=60 rows=16></textarea>
</td>
</tr>
</form>
</TABLE>

View File

@ -0,0 +1,10 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading"><tmpl_if is_site>QSite<tmpl_else>Item</tmpl_if> Inserted Successfully</td>
</tr>
<tr>
<td align=center>Item URI:<br>
<code><input type="text" size=60 readonly value="<tmpl_var uri>"></code>
</td>
</tr>
</table>

View File

@ -0,0 +1,101 @@
<table align="center" class="mainpaneitem" width=90% >
<TR>
<TD class="formHeading" colspan="2">Insert A QSite</TD>
</tr>
<TR>
<TD colspan="2" align=center><a href="/insert?mode=file">Insert A Single File Instead</a></TD>
</tr>
<tr><td colspan=2><hr></td></tr>
<form method="POST" action="/insert" class="formBody">
<!-- <form method="POST" action="/insert" class="formBody"> -->
<input type=hidden name="cmd" value="putsite">
<input type=hidden name="type" value="qsite">
<tr>
<TD valign=top>Name: </TD>
<td>
<input type="text" name="name" size="32" maxlength="50">
<div style="font-style: italic; font-size: smaller">
(A short name for the QSite - should contain only alphanumerics, '-', and '_';
should definitely <i>not</i> contain '/', ':', '\', ' ' etc)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Private Key: </TD>
<td>
<INPUT type="text" name="privkey" size="30" maxlength="60">
<div style="font-size: smaller; font-style: italic">
(Mandatory - if you don't have a signed-space keypair, get one by
clicking on <b>Tools</b>)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Directory: </TD>
<td>
<INPUT type="text" name="dir" size="60" maxlength="128">
<div style="font-size: smaller; font-style: italic">
(Absolute directory path where the QSite's files reside)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Title: </TD>
<td>
<input type="text" name="title" size="60" maxlength="58">
<div style="font-style: italic; font-size: smaller">
(A short descriptive title for this QSite)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Keywords: </TD>
<td>
<input type="text" name="keywords" size="60" maxlength="80">
<div style="font-style: italic; font-size: smaller">
(Comma-separated list of short descriptive keywords)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Abstract: </TD>
<td>
<textarea name="abstract" cols="50" rows="2"></textarea>
<div style="font-style: italic; font-size: smaller">
(A short descriptive summary for the QSite, preferably no
longer than 256 characters)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD colspan=2 align=center>
<INPUT class="btn" type="submit" name="submit" value="Insert it">
</TD>
</tr>
</form>
</table>

View File

@ -0,0 +1,76 @@
<TABLE align="center" class="mainpaneitem">
<TR>
<TD class="formHeading" colspan="3">Search For Content</TD>
</tr>
<form method="GET" action="/search" class="formBody">
<input type=hidden name=cmd value="search">
<tr>
<td valign=top>Type: </td>
<td style="font-size:smaller" colspan=2>
<input type="radio" name="type" value="any" checked>ANY
<input type="radio" name="type" value="qsite">QSite
<input type="radio" name="type" value="text">Text
<input type="radio" name="type" value="html">HTML
<input type="radio" name="type" value="image">Image
<br>
<input type="radio" name="type" value="audio">Audio
<input type="radio" name="type" value="video">Video
<input type="radio" name="type" value="archive">Archive
<input type="radio" name="type" value="other">Other
</td>
</tr>
<tr>
<td>Title: </td>
<td><input type="text" name="title" size="60" value="<tmpl_var title>" maxlength="128"></td>
<td><input type="checkbox" name="title_re" <tmpl_if title_re>checked</tmpl_if>>regexp</td>
</tr>
<tr>
<TD>Path: </TD>
<td><INPUT type="text" name="path" size="60" value="<tmpl_var title>" maxlength="128"></td>
<td><input type="checkbox" name="path_re" <tmpl_if path_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<TD>Mimetype: </TD>
<td><INPUT type="text" name="mimetype" size="40" maxlength="40" value="<tmpl_var mimetype>"></td>
<td><input type="checkbox" name="mimetype_re" <tmpl_if mimetype_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<TD>Keywords: </TD>
<td><INPUT type="text" name="keywords" size="60" maxlength="80" value="<tmpl_var keywords>"></td>
<td><input type="checkbox" name="keywords_re" <tmpl_if keywords_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<TD>Summary: </TD>
<td><textarea name="summary" cols="50" rows=2><tmpl_var summary></textarea></td>
<td><input type="checkbox" name="summary_re" <tmpl_if summary_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<td>
Search Mode:
</td>
<td>
<input type=radio name="searchmode" value="and" checked>AND
<input type=radio name="searchmode" value="or">OR
</td>
</tr>
<tr><td colspan=3><hr></td></tr>
<tr>
<TD colspan=3 align=center>
<INPUT class="btn" type="submit" name="submit" value="Search!">
</TD>
</tr>
</form>
</TABLE>

View File

@ -0,0 +1,27 @@
<table align="center" class="mainpaneitem" <tmpl_if results>width="95%"</tmpl_if>>
<tr>
<td class="formHeading" colspan=2>Search Results</td>
</tr>
<tr>
<td style="font-style: italic" align="center"><tmpl_var numresults> items found</td>
</tr>
<tr>
<td width="90%">
<table cellspacing=0 cellpadding=5 align=center border=0 width=95%>
<tmpl_loop results>
<tr>
<td>
<div style="font-size: larger"><a href="/<tmpl_var uri>"><tmpl_var title></a></div>
<tmpl_if abstract>
<div style="font-style: italic"><tmpl_var abstract></div>
</tmpl_if>
<div style="font-size: smaller; color: #008000;"><tmpl_var uri></div>
<div style="font-size:smaller;"><tmpl_var type> (<tmpl_var mimetype>) <tmpl_var size> bytes</div>
<div/>
</td>
</tr>
</tmpl_loop>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,9 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Settings</td>
</tr>
<tr>
<td>Not implemented yet</td>
</tr>
</table>

View File

@ -0,0 +1,24 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading" colspan=2>Q Node Status</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td><b>Item</b></td>
<td><b>Value</b></td>
</tr>
<tmpl_loop items>
<tr>
<td><tmpl_var key></td>
<td><tmpl_var value></td>
</tr>
</tmpl_loop>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,6 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Tools</td>
</tr>
</table>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
* Modified by David McNab (david@rebirthing.co.nz) to allow nesting of
* templates (ie, passing a child Template object as a value argument
* to a .setParam() invocation on a parent Template object).
*
*/
package HTML.Tmpl.Element;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import HTML.*;
public class Conditional extends Element
{
private boolean control_val = false;
private Vector [] data;
public Conditional(String type, String name)
throws IllegalArgumentException
{
if(type.equalsIgnoreCase("if"))
this.type="if";
else if(type.equalsIgnoreCase("unless"))
this.type="unless";
else
throw new IllegalArgumentException(
"Unrecognised type: " + type);
this.name = name;
this.data = new Vector[2];
this.data[0] = new Vector();
}
public void addBranch() throws IndexOutOfBoundsException
{
if(data[1] != null)
throw new IndexOutOfBoundsException("Already have two branches");
if(data[0] == null)
data[0] = new Vector();
else if(data[1] == null)
data[1] = new Vector();
}
public void add(String text)
{
if(data[1] != null)
data[1].addElement(text);
else
data[0].addElement(text);
}
public void add(Element node)
{
if(data[1] != null)
data[1].addElement(node);
else
data[0].addElement(node);
}
public void setControlValue(Object control_val)
throws IllegalArgumentException
{
this.control_val = process_var(control_val);
}
public String parse(Hashtable params)
{
if(!params.containsKey(this.name))
this.control_val = false;
else
setControlValue(params.get(this.name));
StringBuffer output = new StringBuffer();
Enumeration de;
if(type.equals("if") && control_val ||
type.equals("unless") && !control_val)
de = data[0].elements();
else if(data[1] != null)
de = data[1].elements();
else
return "";
while(de.hasMoreElements()) {
Object e = de.nextElement();
String eType = e.getClass().getName();
if(eType.endsWith(".String"))
output.append((String)e);
else if (eType.endsWith(".Template"))
output.append(((Template)e).output());
else
output.append(((Element)e).parse(params));
}
return output.toString();
}
public String typeOfParam(String param)
throws NoSuchElementException
{
for(int i=0; i<data.length; i++)
{
if(data[i] == null)
continue;
for(Enumeration e = data[i].elements();
e.hasMoreElements();)
{
Object o = e.nextElement();
if(o.getClass().getName().endsWith(".String"))
continue;
if(((Element)o).Name().equals(param))
return ((Element)o).Type();
}
}
throw new NoSuchElementException(param);
}
private boolean process_var(Object control_val)
throws IllegalArgumentException
{
String control_class = "";
if(control_val == null)
return false;
control_class=control_val.getClass().getName();
if(control_class.indexOf(".") > 0)
control_class = control_class.substring(
control_class.lastIndexOf(".")+1);
if(control_class.equals("String")) {
return !(((String)control_val).equals("") ||
((String)control_val).equals("0"));
} else if(control_class.equals("Vector")) {
return !((Vector)control_val).isEmpty();
} else if(control_class.equals("Boolean")) {
return ((Boolean)control_val).booleanValue();
} else if(control_class.equals("Integer")) {
return (((Integer)control_val).intValue() != 0);
} else {
throw new IllegalArgumentException("Unrecognised type");
}
}
}

View File

@ -0,0 +1,66 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl.Element;
import java.util.Hashtable;
import java.util.NoSuchElementException;
public abstract class Element
{
protected String type;
protected String name="";
public abstract String parse(Hashtable params);
public abstract String typeOfParam(String param)
throws NoSuchElementException;
public void add(String data){}
public void add(Element node){}
public boolean contains(String param)
{
try {
return (typeOfParam(param) != null?true:false);
} catch(NoSuchElementException nse) {
return false;
}
}
public final String Type()
{
return type;
}
public final String Name()
{
return name;
}
}

View File

@ -0,0 +1,39 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl.Element;
public class If extends Conditional
{
public If(String control_var) throws IllegalArgumentException
{
super("if", control_var);
}
}

View File

@ -0,0 +1,183 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl.Element;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.NoSuchElementException;
public class Loop extends Element
{
private boolean loop_context_vars=false;
private boolean global_vars=false;
private Vector control_val = null;
private Vector data;
public Loop(String name)
{
this.type = "loop";
this.name = name;
this.data = new Vector();
}
public Loop(String name, boolean loop_context_vars)
{
this(name);
this.loop_context_vars=loop_context_vars;
}
public Loop(String name, boolean loop_context_vars, boolean global_vars)
{
this(name);
this.loop_context_vars=loop_context_vars;
this.global_vars=global_vars;
}
public void add(String text)
{
data.addElement(text);
}
public void add(Element node)
{
data.addElement(node);
}
public void setControlValue(Vector control_val)
throws IllegalArgumentException
{
this.control_val = process_var(control_val);
}
public String parse(Hashtable p)
{
if(!p.containsKey(this.name))
this.control_val = null;
else {
Object o = p.get(this.name);
if(!o.getClass().getName().endsWith(".Vector") &&
!o.getClass().getName().endsWith(".List"))
throw new ClassCastException(
"Attempt to set <tmpl_loop> with a non-list. tmpl_loop=" + this.name);
setControlValue((Vector)p.get(this.name));
}
if(control_val == null)
return "";
StringBuffer output = new StringBuffer();
Enumeration iterator = control_val.elements();
boolean first=true;
boolean last=false;
boolean inner=false;
boolean odd=true;
int counter=1;
while(iterator.hasMoreElements()) {
Hashtable params = (Hashtable)iterator.nextElement();
if(params==null)
params = new Hashtable();
if(global_vars) {
for(Enumeration e = p.keys(); e.hasMoreElements();) {
Object key = e.nextElement();
if(!params.containsKey(key))
params.put(key, p.get(key));
}
}
if(loop_context_vars) {
if(!iterator.hasMoreElements())
last=true;
inner = !first && !last;
params.put("__FIRST__", first?"1":"");
params.put("__LAST__", last?"1":"");
params.put("__ODD__", odd?"1":"");
params.put("__INNER__", inner?"1":"");
params.put("__COUNTER__", "" + (counter++));
}
Enumeration de = data.elements();
while(de.hasMoreElements()) {
Object e = de.nextElement();
if(e.getClass().getName().indexOf("String")>-1)
output.append((String)e);
else
output.append(((Element)e).parse(params));
}
first = false;
odd = !odd;
}
return output.toString();
}
public String typeOfParam(String param)
throws NoSuchElementException
{
for(Enumeration e = data.elements(); e.hasMoreElements();)
{
Object o = e.nextElement();
if(o.getClass().getName().endsWith(".String"))
continue;
if(((Element)o).Name().equals(param))
return ((Element)o).Type();
}
throw new NoSuchElementException(param);
}
private Vector process_var(Vector control_val)
throws IllegalArgumentException
{
String control_class = "";
if(control_val == null)
return null;
control_class=control_val.getClass().getName();
if(control_class.indexOf("Vector") > -1) {
if(control_val.isEmpty())
return null;
} else {
throw new IllegalArgumentException("Unrecognised type");
}
return control_val;
}
}

View File

@ -0,0 +1,39 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl.Element;
public class Unless extends Conditional
{
public Unless(String control_var) throws IllegalArgumentException
{
super("unless", control_var);
}
}

View File

@ -0,0 +1,144 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
* Modified by David McNab (david@rebirthing.co.nz) to allow nesting of
* templates (ie, passing a child Template object as a value argument
* to a .setParam() invocation on a parent Template object).
*/
package HTML.Tmpl.Element;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import HTML.Tmpl.Util;
import HTML.Template;
public class Var extends Element
{
public static final int ESCAPE_NONE = 0;
public static final int ESCAPE_URL = 1;
public static final int ESCAPE_HTML = 2;
public static final int ESCAPE_QUOTE = 4;
public Var(String name, int escape, Object default_value)
throws IllegalArgumentException
{
this(name, escape);
this.default_value = stringify(default_value);
}
public Var(String name, int escape)
throws IllegalArgumentException
{
if(name == null)
throw new IllegalArgumentException("tmpl_var must have a name");
this.type = "var";
this.name = name;
this.escape = escape;
}
public Var(String name, String escape)
throws IllegalArgumentException
{
this(name, escape, null);
}
public Var(String name, String escape, Object default_value)
throws IllegalArgumentException
{
this(name, ESCAPE_NONE, default_value);
if(escape.equalsIgnoreCase("html"))
this.escape = ESCAPE_HTML;
else if(escape.equalsIgnoreCase("url"))
this.escape = ESCAPE_URL;
else if(escape.equalsIgnoreCase("quote"))
this.escape = ESCAPE_QUOTE;
}
public Var(String name, boolean escape)
throws IllegalArgumentException
{
this(name, escape?ESCAPE_HTML:ESCAPE_NONE);
}
public String parse(Hashtable params)
{
String value = null;
if(params.containsKey(this.name))
value = stringify(params.get(this.name));
else
value = this.default_value;
if(value == null)
return "";
if(this.escape == ESCAPE_HTML)
return Util.escapeHTML(value);
else if(this.escape == ESCAPE_URL)
return Util.escapeURL(value);
else if(this.escape == ESCAPE_QUOTE)
return Util.escapeQuote(value);
else
return value;
}
public String typeOfParam(String param)
throws NoSuchElementException
{
throw new NoSuchElementException(param);
}
private String stringify(Object o)
{
if(o == null)
return null;
String cname = o.getClass().getName();
if(cname.endsWith(".String"))
return (String)o;
else if(cname.endsWith(".Integer"))
return ((Integer)o).toString();
else if(cname.endsWith(".Boolean"))
return ((Boolean)o).toString();
else if(cname.endsWith(".Date"))
return ((java.util.Date)o).toString();
else if(cname.endsWith(".Vector"))
throw new ClassCastException("Attempt to set <tmpl_var> with a non-scalar. Var name=" + this.name);
else if(cname.endsWith(".Template"))
return ((Template)o).output();
else
throw new ClassCastException("Unknown object type: " + cname);
}
// Private data starts here
private int escape=ESCAPE_NONE;
private String default_value=null;
}

View File

@ -0,0 +1,145 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl;
/**
* Pre-parse filters for HTML.Template templates.
* <p>
* The HTML.Tmpl.Filter interface allows you to write Filters
* for your templates. The filter is called after the template
* is read and before it is parsed.
* <p>
* You can use a filter to make changes in the template file before
* it is parsed by HTML.Template, so for example, use it to replace
* constants, or to translate your own tags to HTML.Template tags.
* <p>
* A common usage would be to do what you think you're doing when you
* do <code>&lt;TMPL_INCLUDE file="&lt;TMPL_VAR name="the_file"&gt;"&gt;</code>:
* <p>
* myTemplate.tmpl:
* <pre>
* &lt;TMPL_INCLUDE file="&lt;%the_file%&gt;"&gt;
* </pre>
* <p>
* myFilter.java:
* <pre>
* class myFilter implements HTML.Tmpl.Filter
* {
* private String myFile;
* private int type=SCALAR
*
* public myFilter(String myFile) {
* this.myFile = myFile;
* }
*
* public int format() {
* return this.type;
* }
*
* public String parse(String t) {
* // replace all &lt;%the_file%&gt; with myFile
* return t;
* }
*
* public String [] parse(String [] t) {
* throw new UnsupportedOperationException();
* }
* }
* </pre>
* <p>
* myClass.java:
* <pre>
* Hashtable params = new Hashtable();
* params.put("filename", "myTemplate.tmpl");
* params.put("filter", new myFilter("myFile.tmpl"));
* Template t = new Template(params);
* </pre>
*
* @author Philip S Tellis
* @version 0.0.1
*/
public interface Filter
{
/**
* Tells HTML.Template to call the parse(String) method of this filter.
*/
public final static int SCALAR=1;
/**
* Tells HTML.Template to call the parse(String []) method of this
* filter.
*/
public final static int ARRAY=2;
/**
* Tells HTML.Template what kind of filter this is.
* Should return either SCALAR or ARRAY to indicate which parse method
* must be called.
*
* @return the values SCALAR or ARRAY indicating which parse method
* is to be called
*/
public int format();
/**
* parses the template as a single string, and returns the parsed
* template as a single string.
* <p>
* Should throw an UnsupportedOperationException if it isn't implemented
*
* @param t a string containing the entire template
*
* @return a string containing the template after you've parsed it
*
* @throws UnsupportedOperationException if this method isn't
* implemented
*/
public String parse(String t);
/**
* parses the template as an array of strings, and returns the parsed
* template as an array of strings.
* <p>
* Should throw an UnsupportedOperationException if it isn't implemented
*
* @param t an array of strings containing the template - one line
* at a time
*
* @return an array of strings containing the parsed template -
* one line at a time
*
* @throws UnsupportedOperationException if this method isn't
* implemented
*/
public String [] parse(String [] t);
}

View File

@ -0,0 +1,385 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl.Parsers;
import java.util.*;
import HTML.Tmpl.Element.*;
import HTML.Tmpl.Util;
public class Parser
{
private boolean case_sensitive=false;
private boolean strict=true;
private boolean loop_context_vars=false;
private boolean global_vars=false;
public Parser()
{
}
public Parser(String [] args)
throws ArrayIndexOutOfBoundsException,
IllegalArgumentException
{
if(args.length%2 != 0)
throw new ArrayIndexOutOfBoundsException("odd number of arguments passed");
for(int i=0; i<args.length; i+=2) {
if(args[i].equals("case_sensitive")) {
String cs = args[i+1];
if(cs.equals("") || cs.equals("0"))
this.case_sensitive=false;
else
this.case_sensitive=true;
} else if(args[i].equals("strict")) {
String s = args[i+1];
if(s.equals("") || s.equals("0"))
this.strict=false;
else
this.strict=true;
} else if(args[i].equals("loop_context_vars")) {
String s = args[i+1];
if(s.equals("") || s.equals("0"))
this.loop_context_vars=false;
else
this.loop_context_vars=true;
} else if(args[i].equals("global_vars")) {
String s = args[i+1];
if(s.equals("") || s.equals("0"))
this.global_vars=false;
else
this.global_vars=true;
} else {
throw new IllegalArgumentException(args[i]);
}
}
}
public Element getElement(Properties p)
throws NoSuchElementException
{
String type = p.getProperty("type");
if(type.equals("if"))
return new If(p.getProperty("name"));
else if(type.equals("unless"))
return new Unless(p.getProperty("name"));
else if(type.equals("loop"))
return new Loop(p.getProperty("name"),
loop_context_vars, global_vars);
else
throw new NoSuchElementException(type);
}
public Vector parseLine(String line)
throws IllegalArgumentException
{
int pos=0, endpos;
Vector parts = new Vector();
char [] c = line.toCharArray();
int i=0;
StringBuffer temp = new StringBuffer();
for(i=0; i<c.length; i++) {
if(c[i] != '<') {
temp.append(c[i]);
} else {
// found a tag
Util.debug_print("line so far: " + temp);
StringBuffer tag = new StringBuffer();
for(; i<c.length && c[i] != '>'; i++) {
tag.append(c[i]);
}
// > is not allowed inside a template tag
// so we can be sure that if this is a
// template tag, it ends with a >
// add the closing > as well
if(i<c.length)
tag.append(c[i]);
// if this contains more < inside it,
// then it could possibly be a template
// tag inside a html tag
// so remove external tag parts
while(tag.toString().substring(1).indexOf("<")
> -1)
{
do {
temp.append(tag.charAt(0));
tag=new StringBuffer(
tag.toString().substring(1));
} while(tag.charAt(0) != '<');
}
Util.debug_print("tag: " + tag);
String test_tag = tag.toString().toLowerCase();
// if it doesn't contain tmpl_ it is not
// a template tag
if(test_tag.indexOf("tmpl_") < 0) {
temp.append(tag);
continue;
}
// may be a template tag
// check if it starts with tmpl_
test_tag = cleanTag(test_tag);
Util.debug_print("clean: " + test_tag);
// check if it is a closing tag
if(test_tag.startsWith("/"))
test_tag = test_tag.substring(1);
// if it still doesn't start with tmpl_
// then it is not a template tag
if(!test_tag.startsWith("tmpl_")) {
temp.append(tag);
continue;
}
// now it must be a template tag
String tag_type=getTagType(test_tag);
if(tag_type == null) {
if(strict)
throw new
IllegalArgumentException(
tag.toString());
else
temp.append(tag);
}
Util.debug_print("type: " + tag_type);
// if this was an invalid key and we've
// reached so far, then next iteration
if(tag_type == null)
continue;
// now, push the previous stuff
// into the Vector
if(temp.length()>0) {
parts.addElement(temp.toString());
temp = new StringBuffer();
}
// it is a valid template tag
// get its properties
Util.debug_print("Checking: " + tag);
Properties tag_props =
getTagProps(tag.toString());
if(tag_props.containsKey("name"))
Util.debug_print("name: " +
tag_props.getProperty("name"));
else
Util.debug_print("no name");
parts.addElement(tag_props);
}
}
if(temp.length()>0)
parts.addElement(temp.toString());
return parts;
}
private String cleanTag(String tag)
throws IllegalArgumentException
{
String test_tag = new String(tag);
// first remove < and >
if(test_tag.startsWith("<"))
test_tag = test_tag.substring(1);
if(test_tag.endsWith(">"))
test_tag = test_tag.substring(0, test_tag.length()-1);
else
throw new IllegalArgumentException("Tags must start " +
"and end on the same line");
// remove any leading !-- and trailing
// -- in case of comment style tags
if(test_tag.startsWith("!--")) {
test_tag=test_tag.substring(3);
}
if(test_tag.endsWith("--")) {
test_tag=test_tag.substring(0, test_tag.length()-2);
}
// then leading and trailing spaces
test_tag = test_tag.trim();
return test_tag;
}
private String getTagType(String tag)
{
int sp = tag.indexOf(" ");
String tag_type="";
if(sp < 0) {
tag_type = tag.toLowerCase();
} else {
tag_type = tag.substring(0, sp).toLowerCase();
}
if(tag_type.startsWith("tmpl_"))
tag_type=tag_type.substring(5);
Util.debug_print("tag_type: " + tag_type);
if(tag_type.equals("var") ||
tag_type.equals("if") ||
tag_type.equals("unless") ||
tag_type.equals("loop") ||
tag_type.equals("include") ||
tag_type.equals("else")) {
return tag_type;
} else {
return null;
}
}
private Properties getTagProps(String tag)
throws IllegalArgumentException,
NullPointerException
{
Properties p = new Properties();
tag = cleanTag(tag);
Util.debug_print("clean: " + tag);
if(tag.startsWith("/")) {
p.put("close", "true");
tag=tag.substring(1);
} else {
p.put("close", "");
}
Util.debug_print("close: " + p.getProperty("close"));
p.put("type", getTagType(tag));
Util.debug_print("type: " + p.getProperty("type"));
if(p.getProperty("type").equals("else") ||
p.getProperty("close").equals("true"))
return p;
if(p.getProperty("type").equals("var"))
p.put("escape", "");
int sp = tag.indexOf(" ");
// if we've got so far, this must succeed
tag = tag.substring(sp).trim();
Util.debug_print("checking params: " + tag);
// now, we should have either name=value pairs
// or name space escape in case of old style vars
if(tag.indexOf("=") < 0) {
// no = means old style
// first will be var name
// second if any will be escape
sp = tag.toLowerCase().indexOf(" escape");
if(sp < 0) {
// no escape
p.put("name", tag);
p.put("escape", "0");
} else {
tag = tag.substring(0, sp);
p.put("name", tag);
p.put("escape", "html");
}
} else {
// = means name=value pairs.
// use a StringTokenizer
StringTokenizer st = new StringTokenizer(tag, " =");
while(st.hasMoreTokens()) {
String key, value;
key = st.nextToken().toLowerCase();
if(st.hasMoreTokens())
value = st.nextToken();
else if(key.equals("escape"))
value = "html";
else
throw new NullPointerException(
"parameter " + key + " has no value");
if(value.startsWith("\"") &&
value.endsWith("\""))
value = value.substring(1,
value.length()-1);
else if(value.startsWith("'") &&
value.endsWith("'"))
value = value.substring(1,
value.length()-1);
if(value.length()==0)
throw new NullPointerException(
"parameter " + key + " has no value");
if(key.equals("escape"))
value=value.toLowerCase();
p.put(key, value);
}
}
String name = p.getProperty("name");
// if not case sensitive, and not special variable, flatten case
// never flatten case for includes
if(!case_sensitive && !p.getProperty("type").equals("include")
&& !( name.startsWith("__") && name.endsWith("__") ))
{
p.put("name", name.toLowerCase());
}
if(!Util.isNameChar(name))
throw new IllegalArgumentException(
"parameter name may only contain " +
"letters, digits, ., /, +, -, _");
// __var__ is allowed in the template, but not in the
// code. this is so that people can reference __FIRST__,
// etc
return p;
}
}

View File

@ -0,0 +1,130 @@
/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl;
public class Util
{
public static boolean debug=false;
public static String escapeHTML(String element)
{
String s = new String(element); // don't change the original
String [] metas = {"&", "<", ">", "\""};
String [] repls = {"&amp;", "&lt;", "&gt;", "&quot;"};
for(int i = 0; i < metas.length; i++) {
int pos=0;
do {
pos = s.indexOf(metas[i], pos);
if(pos<0)
break;
s = s.substring(0, pos) + repls[i] + s.substring(pos+1);
pos++;
} while(pos >= 0);
}
return s;
}
public static String escapeURL(String url)
{
StringBuffer s = new StringBuffer();
String no_escape = "./-_";
for(int i=0; i<url.length(); i++)
{
char c = url.charAt(i);
if(!Character.isLetterOrDigit(c) &&
no_escape.indexOf(c)<0)
{
String h = Integer.toHexString((int)c);
s.append("%");
if(h.length()<2)
s.append("0");
s.append(h);
} else {
s.append(c);
}
}
return s.toString();
}
public static String escapeQuote(String element)
{
String s = new String(element); // don't change the original
String [] metas = {"\"", "'"};
String [] repls = {"\\\"", "\\'"};
for(int i = 0; i < metas.length; i++) {
int pos=0;
do {
pos = s.indexOf(metas[i], pos);
if(pos<0)
break;
s = s.substring(0, pos) + repls[i] + s.substring(pos+1);
pos++;
} while(pos >= 0);
}
return s;
}
public static boolean isNameChar(char c)
{
return true;
}
public static boolean isNameChar(String s)
{
String alt_valid = "./+-_";
for(int i=0; i<s.length(); i++)
if(!Character.isLetterOrDigit(s.charAt(i)) &&
alt_valid.indexOf(s.charAt(i))<0)
return false;
return true;
}
public static void debug_print(String msg)
{
if(!debug)
return;
System.err.println(msg);
}
public static void debug_print(Object o)
{
debug_print(o.toString());
}
}

View File

@ -0,0 +1,19 @@
/*
* AumUtil.java
*
* Created on March 24, 2005, 3:10 PM
*/
package net.i2p.aum;
/**
*
* @author david
*/
public class AumUtil {
/** Creates a new instance of AumUtil */
public AumUtil() {
}
}

View File

@ -0,0 +1,67 @@
/*
* NonUniqueProperties.java
*
* Created on April 9, 2005, 10:46 PM
*/
package net.i2p.aum;
import java.*;
import java.util.*;
/**
* similar in some ways to Properties, except that duplicate keys
* are allowed
*/
public class DupHashtable extends Hashtable {
/** Creates a new instance of NonUniqueProperties */
public DupHashtable() {
super();
}
/** Adds a value to be stored against key */
public void put(String key, String value) {
if (!containsKey(key)) {
put(key, new Vector());
}
((Vector)get(key)).addElement(value);
}
/** retrieves a Vector of values for key, or empty vector if none */
public Vector get(String key) {
if (!containsKey(key)) {
return new Vector();
} else {
return (Vector)super.get(key);
}
}
/** returns the i-th value for given key, or dflt if key not found */
public String get(String key, int idx, String dflt) {
if (containsKey(key)) {
return get(key, idx);
} else {
return dflt;
}
}
/** returns the i-th value for given key
* @throws ArrayIndexOutOfBoundsException if idx is out of range
*/
public String get(String key, int idx) {
return (String)((Vector)get(key)).get(idx);
}
/** returns the number of values for a given key */
public int numValuesFor(String key) {
if (!containsKey(key)) {
return 0;
} else {
return ((Vector)get(key)).size();
}
}
}

View File

@ -0,0 +1,155 @@
// a simple I2P stream client that makes connections to an EchoServer,
// sends in stuff, and gets replies
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.*;
import net.i2p.util.*;
/**
* a simple program which illustrates the use of I2P stream
* sockets from a client point of view
*/
public class EchoClient extends Thread
{
public I2PSocketManager socketManager;
public I2PSocket clientSocket;
public Destination dest;
protected static Log _log;
/**
* Creates an echoclient, given an I2P Destination object
*/
public EchoClient(Destination remdest)
{
_log = new Log("EchoServer");
_init(remdest);
}
/**
* Creates an EchoClient given a destination in base64
*/
public EchoClient(String destStr) throws DataFormatException
{
_log = new Log("EchoServer");
Destination remdest = new Destination();
remdest.fromBase64(destStr);
_init(remdest);
}
private void _init(Destination remdest)
{
dest = remdest;
System.out.println("Client: dest="+dest.toBase64());
System.out.println("Client: Creating client socketManager");
// get a socket manager
socketManager = I2PSocketManagerFactory.createManager();
}
/**
* runs the EchoClient demo
*/
public void run()
{
InputStream socketIn;
OutputStreamWriter socketOut;
OutputStream socketOutStream;
System.out.println("Client: Creating connected client socket");
System.out.println("dest="+dest.toBase64());
try {
// get a client socket
clientSocket = socketManager.connect(dest);
} catch (I2PException e) {
e.printStackTrace();
return;
} catch (ConnectException e) {
e.printStackTrace();
return;
} catch (NoRouteToHostException e) {
e.printStackTrace();
return;
} catch (InterruptedIOException e) {
e.printStackTrace();
return;
}
System.out.println("Client: Successfully connected!");
try {
socketIn = clientSocket.getInputStream();
socketOutStream = clientSocket.getOutputStream();
socketOut = new OutputStreamWriter(socketOutStream);
System.out.println("Client: created streams");
socketOut.write("Hi there server!\n");
socketOut.flush();
System.out.println("Client: sent to server, awaiting reply");
String line = DataHelper.readLine(socketIn);
System.out.println("Got reply: '" + line + "'");
clientSocket.close();
} catch (NoRouteToHostException e) {
e.printStackTrace();
return;
} catch (IOException e) {
System.out.println("IOException!!");
e.printStackTrace();
return;
}
}
/**
* allows the echo client to be run from a command shell
*/
public static void main(String [] args)
{
String dest64 = args[0];
System.out.println("dest="+dest64);
Destination d = new Destination();
try {
d.fromBase64(dest64);
} catch (DataFormatException e) {
e.printStackTrace();
return;
}
EchoClient client = new EchoClient(d);
System.out.println("client: running");
client.run();
System.out.println("client: done");
}
}

View File

@ -0,0 +1,163 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.*;
import net.i2p.util.*;
/**
* a simple program which illustrates the use of I2P stream
* sockets from a server point of view
*/
public class EchoServer extends Thread
{
//public I2PClient client;
//public PrivDestination privDest;
//public I2PSession serverSession;
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
public PrivDestination key;
public Destination dest;
protected static Log _log;
public EchoServer() throws I2PException, IOException
{
_log = new Log("EchoServer");
System.out.println("Server: creating new key");
// key = PrivDestination.newKey();
// System.out.println("Server: dest=" + key.toDestinationBase64());
System.out.println("Server: creating socket manager");
Properties props = new Properties();
props.setProperty("inbound.length", "0");
props.setProperty("outbound.length", "0");
props.setProperty("inbound.lengthVariance", "0");
props.setProperty("outbound.lengthVariance", "0");
PrivDestination key = PrivDestination.newKey();
// get a socket manager
// socketManager = I2PSocketManagerFactory.createManager(key);
socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
System.out.println("Server: getting server socket");
// get a server socket
serverSocket = socketManager.getServerSocket();
System.out.println("Server: got server socket, ready to run");
dest = socketManager.getSession().getMyDestination();
System.out.println("Server: getMyDestination->"+dest.toBase64());
start();
}
/**
* run this EchoServer
*/
public void run()
{
System.out.println("Server: listening on dest:");
/**
try {
System.out.println(key.toDestinationBase64());
} catch (DataFormatException e) {
e.printStackTrace();
}
*/
System.out.println(dest.toBase64());
while (true)
{
try {
I2PSocket sessSocket = serverSocket.accept();
System.out.println("Server: Got connection from client");
InputStream socketIn = sessSocket.getInputStream();
OutputStreamWriter socketOut = new OutputStreamWriter(sessSocket.getOutputStream());
System.out.println("Server: created streams");
// read a line from input, and echo it back
String line = DataHelper.readLine(socketIn);
System.out.println("Server: got '" + line + "'");
String reply = "EchoServer: got '" + line + "'\n";
socketOut.write(reply);
socketOut.flush();
System.out.println("Server: sent trply");
sessSocket.close();
System.out.println("Server: closed socket");
} catch (ConnectException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (I2PException e) {
e.printStackTrace();
}
}
}
public Destination getDest() throws DataFormatException
{
// return key.toDestination();
return dest;
}
public String getDestBase64() throws DataFormatException
{
// return key.toDestinationBase64();
return dest.toBase64();
}
/**
* runs EchoServer from the command shell
*/
public static void main(String [] args)
{
System.out.println("Constructing an EchoServer");
try {
EchoServer myServer = new EchoServer();
System.out.println("Got an EchoServer");
System.out.println("Here's the dest:");
System.out.println(myServer.getDestBase64());
myServer.run();
} catch (I2PException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,56 @@
// runs EchoServer and EchoClient as threads
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.*;
/**
* A simple program which runs the EchoServer and EchoClient
* demos as threads
*/
public class EchoTest
{
/**
* create one instance each of EchoServer and EchoClient,
* run the server as a thread, run the client in foreground,
* display detailed results
*/
public static void main(String [] args)
{
EchoServer server;
EchoClient client;
try {
server = new EchoServer();
Destination serverDest = server.getDest();
System.out.println("EchoTest: serverDest=" + serverDest.toBase64());
client = new EchoClient(serverDest);
} catch (I2PException e) {
e.printStackTrace(); return;
} catch (IOException e) {
e.printStackTrace(); return;
}
System.out.println("Starting server...");
//server.start();
System.out.println("Starting client...");
client.run();
}
}

View File

@ -0,0 +1,322 @@
/*
* SimpleScheduler.java
*
* Created on March 24, 2005, 11:14 PM
*/
package net.i2p.aum;
import java.*;
import java.lang.*;
import java.util.*;
/**
* <p>Implements a queue of objects, where each object is 'embargoed'
* against release until a given time. Threads which attempt to .get
* items from this queue will block if the queue is empty, or if the
* first item of the queue has a 'release time' which has not yet passed.</p>
*
* <p>Think of it like a news desk which receives media releases which are
* 'embargoed' till a certain time. These releases sit in a queue, and when
* their embargo expires, they are actioned and go to print or broadcast.
* The reporters at this news desk are the 'threads', which get blocked
* until the next item's embargo expires.</p>
*
* <p>Purpose of implementing this is to provide a mechanism for scheduling
* background jobs to be executed at precise times</p>.
*/
public class EmbargoedQueue extends Thread {
/**
* items which are waiting for dispatch - stored as 2-element vectors,
* where elem 0 is Integer dispatch time, and elem 1 is the object;
* note that this list is kept in strict ascending order of time.
* Whenever an object becomes ready, it is removed from this queue
* and appended to readyItems
*/
public Vector waitingItems;
/**
* items which are ready for dispatch (their time has come).
*/
public SimpleQueue readyItems;
/** set this true to enable verbose debug messages */
public boolean debug = false;
/** Creates a new embargoed queue */
public EmbargoedQueue() {
waitingItems = new Vector();
readyItems = new SimpleQueue();
// fire up scheduler thread
start();
}
/**
* fetches the item at head of queue, blocking if queue is empty
*/
public Object get()
{
return readyItems.get();
}
/**
* adds a new object to queue without any embargo (or, an embargo that expires
* immediately)
* @param item the object to be added
*/
public synchronized void putNow(Object item)
{
putAfter(0, item);
}
/**
* adds a new object to queue, embargoed until given number of milliseconds
* have elapsed
* @param delay number of milliseconds from now when embargo expires
* @param item the object to be added
*/
public synchronized void putAfter(long delay, Object item)
{
long now = new Date().getTime();
putAt(now+delay, item);
}
/**
* adds a new object to the queue, embargoed until given time
* @param time the unixtime in milliseconds when the object's embargo expires,
* and the object is to be made available
* @param item the object to be added
*/
public synchronized void putAt(long time, Object item)
{
Vector elem = new Vector();
elem.addElement(new Long(time));
elem.addElement(item);
long now = new Date().getTime();
long future = time - now;
//System.out.println("putAt: time="+time+" ("+future+"ms from now), job="+item);
// find where to insert
int i;
int nitems = waitingItems.size();
for (i = 0; i < nitems; i++)
{
// get item i
Vector itemI = (Vector)waitingItems.get(i);
long timeI = ((Long)(itemI.get(0))).longValue();
if (time < timeI)
{
// new item earlier than item i, insert here and bust out
waitingItems.insertElementAt(elem, i);
break;
}
}
// did we insert?
if (i == nitems)
{
// no - gotta append
waitingItems.addElement(elem);
}
// debugging
if (debug) {
printWaiting();
}
// awaken this scheduler object's thread, so it can
// see if any jobs are ready
//notify();
interrupt();
}
/**
* for debugging - prints out a list of waiting items
*/
public synchronized void printWaiting()
{
int i;
long now = new Date().getTime();
System.out.println("EmbargoedQueue dump:");
System.out.println(" Waiting items:");
int nwaiting = waitingItems.size();
for (i = 0; i < nwaiting; i++)
{
Vector item = (Vector)waitingItems.get(i);
long when = ((Long)item.get(0)).longValue();
Object job = item.get(1);
int delay = (int)(when - now)/1000;
System.out.println(" "+delay+"s, t="+when+", job="+job);
}
System.out.println(" Ready items:");
int nready = readyItems.items.size();
for (i = 0; i < nready; i++)
{
//Vector item = (Vector)readyItems.items.get(i);
Object item = readyItems.items.get(i);
System.out.println(" job="+item);
}
}
/**
* scheduling thread, which wakes up every time a new job is queued, and
* if any jobs are ready, transfers them to the readyQueue and notifies
* any waiting client threads
*/
public void run()
{
// monitor the waiting queue, waiting till one becomes ready
while (true)
{
try {
if (waitingItems.size() > 0)
{
// at least 1 waiting item
Vector item = (Vector)(waitingItems.get(0));
long now = new Date().getTime();
long then = ((Long)item.get(0)).longValue();
long delay = then - now;
// ready?
if (delay <= 0)
{
// yep, ready, remove job and stick on waiting queue
waitingItems.remove(0); // ditch from waiting
Object elem = item.get(1);
readyItems.put(elem); // and add to ready
if (debug)
{
System.out.println("embargo expired on "+elem);
printWaiting();
}
}
else
{
// not ready, hang about till we get woken, or the
// job becomes ready
if (debug)
{
System.out.println("waiting for "+delay+"ms");
}
Thread.sleep(delay);
}
}
else
{
// no items yet, hang out for an interrupt
if (debug)
{
System.out.println("queue is empty");
}
synchronized (this) {
wait();
}
}
} catch (Exception e) {
//System.out.println("exception");
if (debug)
{
System.out.println("exception ("+e.getClass().getName()+") "+e.getMessage());
}
}
}
}
private static class TestThread extends Thread {
String id;
EmbargoedQueue q;
public TestThread(String id, EmbargoedQueue q) {
this.id = id;
this.q = q;
}
public void run() {
try {
print("waiting for queue");
Object item = q.get();
print("got item: '"+item+"'");
} catch (Exception e) {
e.printStackTrace();
return;
}
}
public void print(String msg) {
System.out.println("thread '"+id+"': "+msg);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
int i;
int nthreads = 7;
Thread [] threads = new Thread[nthreads];
EmbargoedQueue q = new EmbargoedQueue();
SimpleSemaphore threadPool = new SimpleSemaphore(nthreads);
// populate the queue with some stuff
q.putAfter(10000, "red");
q.putAfter(3000, "orange");
q.putAfter(6000, "yellow");
// populate threads array
for (i = 0; i < nthreads; i++) {
threads[i] = new TestThread("thread"+i, q);
}
// and launch the threads
for (i = 0; i < nthreads; i++) {
threads[i].start();
}
// wait, presumably till all these elements are actioned
try {
Thread.sleep(12000);
} catch (Exception e) {
e.printStackTrace();
return;
}
// add some more shit to the queue, randomly scheduled
Random r = new Random();
String [] items = {"green", "blue", "indigo", "violet", "black", "white", "brown"};
for (i = 0; i < items.length; i++) {
String item = items[i];
int delay = 2000 + r.nextInt(8000);
System.out.println("main: adding '"+item+"' after "+delay+"ms ...");
q.putAfter(delay, item);
}
// wait, presumably for all jobs to finish
try {
Thread.sleep(12000);
} catch (Exception e) {
e.printStackTrace();
return;
}
System.out.println("main: terminating");
}
}

View File

@ -0,0 +1,452 @@
// I2P equivalent of 'netcat'
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.naming.*;
import net.i2p.client.streaming.*;
import net.i2p.data.*;
import net.i2p.util.*;
/**
* A I2P equivalent of the much-beloved 'netcat' utility.
* This command-line utility can either connect to a remote
* destination, or listen on a private destination for incoming
* connections. Once a connection is established, input on stdin
* is sent to the remote peer, and anything received from the
* remote peer is printed to stdout
*/
public class I2PCat extends Thread
{
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
public I2PSocket sessSocket;
public PrivDestination key;
public Destination dest;
public InputStream socketIn;
public OutputStream socketOutStream;
public OutputStreamWriter socketOut;
public SockInput rxThread;
protected static Log _log;
public static String defaultHost = "127.0.0.1";
public static int defaultPort = 7654;
/**
* a thread for reading from socket and displaying on stdout
*/
private class SockInput extends Thread {
InputStream _in;
protected Log _log;
public SockInput(InputStream i) {
_in = i;
}
public void run()
{
// the thread portion, receives incoming bytes on
// the socket input stream and spits them to stdout
byte [] ch = new byte[1];
print("Receiver thread listening...");
try {
while (true) {
//String line = DataHelper.readLine(socketIn);
if (_in.read(ch) != 1) {
print("failed to receive from socket");
break;
}
//System.out.println(line);
System.out.write(ch, 0, 1);
System.out.flush();
}
} catch (IOException e) {
e.printStackTrace();
print("Receiver thread crashed, terminating!!");
System.exit(1);
}
}
void print(String msg)
{
System.out.println("-=- I2PCat: "+msg);
if (_log != null) {
_log.debug(msg);
}
}
}
public I2PCat()
{
_log = new Log("I2PCat");
}
/**
* Runs I2PCat in server mode, listening on the given destination
* for one incoming connection. Once connection is established,
* copyies data between the remote peer and
* the local terminal console.
*/
public void runServer(String keyStr) throws IOException, DataFormatException
{
Properties props = new Properties();
props.setProperty("inbound.length", "0");
props.setProperty("outbound.length", "0");
props.setProperty("inbound.lengthVariance", "0");
props.setProperty("outbound.lengthVariance", "0");
// generate new key if needed
if (keyStr.equals("new")) {
try {
key = PrivDestination.newKey();
} catch (I2PException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
print("Creating new server dest...");
socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
print("Getting server socket...");
serverSocket = socketManager.getServerSocket();
print("Server socket created, ready to run...");
dest = socketManager.getSession().getMyDestination();
print("private key follows:");
System.out.println(key.toBase64());
print("dest follows:");
System.out.println(dest.toBase64());
}
else {
key = PrivDestination.fromBase64String(keyStr);
String dest64Abbrev = key.toBase64().substring(0, 16);
print("Creating server socket manager on dest "+dest64Abbrev+"...");
socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
serverSocket = socketManager.getServerSocket();
print("Server socket created, ready to run...");
}
print("Awaiting client connection...");
I2PSocket sessSocket;
try {
sessSocket = serverSocket.accept();
} catch (I2PException e) {
e.printStackTrace();
return;
} catch (ConnectException e) {
e.printStackTrace();
return;
}
print("Got connection from client");
chat(sessSocket);
}
public void runClient(String destStr)
throws DataFormatException, IOException
{
runClient(destStr, defaultHost, defaultPort);
}
/**
* runs I2PCat in client mode, connecting to a remote
* destination then copying data between the remote peer and
* the local terminal console
*/
public void runClient(String destStr, String host, int port)
throws DataFormatException, IOException
{
// accept 'file:' prefix
if (destStr.startsWith("file:", 0))
{
String path = destStr.substring(5);
destStr = new SimpleFile(path, "r").read();
}
else if (destStr.length() < 255) {
// attempt hosts file lookup
I2PAppContext ctx = new I2PAppContext();
HostsTxtNamingService h = new HostsTxtNamingService(ctx);
Destination dest1 = h.lookup(destStr);
if (dest1 == null) {
usage("Cannot resolve hostname: '"+destStr+"'");
}
// successful lookup
runClient(dest1, host, port);
}
else {
// otherwise, bigger strings are assumed to be base64 dests
Destination dest = new Destination();
dest.fromBase64(destStr);
runClient(dest, host, port);
}
}
public void runClient(Destination dest) {
runClient(dest, "127.0.0.1", 7654);
}
/**
* An alternative constructor which accepts an I2P Destination object
*/
public void runClient(Destination dest, String host, int port)
{
this.dest = dest;
String destAbbrev = dest.toBase64().substring(0, 16)+"...";
print("Connecting via i2cp "+host+":"+port+" to destination "+destAbbrev+"...");
System.out.flush();
try {
// get a socket manager
socketManager = I2PSocketManagerFactory.createManager(host, port);
// get a client socket
print("socketManager="+socketManager);
sessSocket = socketManager.connect(dest);
} catch (I2PException e) {
e.printStackTrace();
return;
} catch (ConnectException e) {
e.printStackTrace();
return;
} catch (NoRouteToHostException e) {
e.printStackTrace();
return;
} catch (InterruptedIOException e) {
e.printStackTrace();
return;
}
print("Successfully connected!");
print("(Press Control-C to quit)");
// Perform console interaction
chat(sessSocket);
try {
sessSocket.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
/**
* Launch the background thread to copy incoming data to stdout, then
* loop in foreground copying lines from stdin and sending them to remote peer
*/
public void chat(I2PSocket sessSocket) {
try {
socketIn = sessSocket.getInputStream();
socketOutStream = sessSocket.getOutputStream();
socketOut = new OutputStreamWriter(socketOutStream);
// launch receiver thread
start();
//launchRx();
while (true) {
String line = DataHelper.readLine(System.in);
print("sent: '"+line+"'");
socketOut.write(line+"\n");
socketOut.flush();
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
/**
* executes in a thread, receiving incoming bytes on
* the socket input stream and spitting them to stdout
*/
public void run()
{
byte [] ch = new byte[1];
print("Receiver thread listening...");
try {
while (true) {
//String line = DataHelper.readLine(socketIn);
if (socketIn.read(ch) != 1) {
print("failed to receive from socket");
break;
}
//System.out.println(line);
System.out.write(ch, 0, 1);
System.out.flush();
}
} catch (IOException e) {
e.printStackTrace();
print("Receiver thread crashed, terminating!!");
System.exit(1);
}
}
public void launchRx() {
rxThread = new SockInput(socketIn);
rxThread.start();
}
static void print(String msg)
{
System.out.println("-=- I2PCat: "+msg);
if (_log != null) {
_log.debug(msg);
}
}
public static void usage(String msg)
{
usage(msg, 1);
}
public static void usage(String msg, int ret)
{
System.out.println(msg);
usage(ret);
}
public static void usage(int ret)
{
System.out.print(
"This utility is an I2P equivalent of the standard *nix 'netcat' utility\n"+
"usage:\n"+
" net.i2p.aum.I2PCat [-h]\n"+
" - display this help\n"+
" net.i2p.aum.I2PCat dest [host [port]]\n"+
" - run in client mode, 'dest' should be one of:\n"+
" hostname.i2p - an I2P hostname listed in hosts.txt\n"+
" (only works with a hosts.txt in current directory)\n"+
" base64dest - a full base64 destination string\n"+
" file:b64filename - filename of a file containing base64 dest\n"+
" net.i2p.aum.I2PCat -l privkey\n"+
" - run in server mode, 'key' should be one of:\n"+
" base64privkey - a full base64 private key string\n"+
" file:b64filename - filename of a file containing base64 privkey\n"+
"\n"
);
System.exit(ret);
}
public static void main(String [] args) throws IOException, DataFormatException
{
int argc = args.length;
// barf if no args
if (argc == 0) {
usage("Missing argument");
}
// show help on request
if (args[0].equals("-h") || args[0].equals("--help")) {
usage(0);
}
// server or client?
if (args[0].equals("-l")) {
if (argc != 2) {
usage("Bad argument count");
}
new I2PCat().runServer(args[1]);
}
else {
// client mode - barf if not 1-3 args
if (argc < 1 || argc > 3) {
usage("Bad argument count");
}
try {
int port = defaultPort;
String host = defaultHost;
if (args.length > 1) {
host = args[1];
if (args.length > 2) {
port = new Integer(args[2]).intValue();
}
}
new I2PCat().runClient(args[0], host, port);
} catch (DataFormatException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,25 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* Class which wraps an I2PSocket object with convenient methods.
* Nothing presently implemented here.
*/
public class I2PSocketHelper
{
}

View File

@ -0,0 +1,146 @@
package net.i2p.aum;
import org.apache.xmlrpc.*;
import java.lang.*;
import java.io.*;
import java.util.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.util.*;
import net.i2p.data.*;
import net.i2p.i2ptunnel.I2PTunnelXMLWrapper;
/**
* Defines the I2P tunnel management methods which will be
* exposed to XML-RPC clients
* Methods in this class are forwarded to an I2PTunnelXMLWrapper object
*/
public class I2PTunnelXMLObject
{
protected I2PTunnelXMLWrapper tunmgr;
/**
* Builds the interface object. You normally shouldn't have to
* instantiate this directly - leave it to I2PTunnelXMLServer
*/
public I2PTunnelXMLObject()
{
tunmgr = new I2PTunnelXMLWrapper();
}
/**
* Generates an I2P keypair, returning a dict with keys 'result' (usually 'ok'),
* priv' (private key as base64) and 'dest' (destination as base64)
*/
public Hashtable genkeys()
{
return tunmgr.xmlrpcGenkeys();
}
/**
* Get a list of active TCP tunnels currently being managed by this
* tunnel manager.
* @return a dict with keys 'status' (usually 'ok'),
* 'jobs' (a list of dicts representing each job, each with keys 'job' (int, job
* number), 'type' (string, 'server' or 'client'), port' (int, the port number).
* Also for server, keys 'host' (hostname, string) and 'ip' (IP address, string).
* For clients, key 'dest' (string, remote destination as base64).
*/
public Hashtable list()
{
return tunmgr.xmlrpcList();
}
/**
* Attempts to find I2P hostname in hosts.txt.
* @param hostname string, I2P hostname
* @return dict with keys 'status' ('ok' or 'fail'),
* and if successful lookup, 'dest' (base64 destination).
*/
public Hashtable lookup(String hostname)
{
return tunmgr.xmlrpcLookup(hostname);
}
/**
* Attempt to open client tunnel
* @param port local port to listen on, int
* @param dest remote dest to tunnel to, base64 string
* @return dict with keys 'status' (string - 'ok' or 'fail').
* If 'ok', also key 'result' with text output from tunnelmgr
*/
public Hashtable client(int port, String dest)
{
return tunmgr.xmlrpcClient(port, dest);
}
/**
* Attempts to open server tunnel
* @param host TCP hostname of TCP server to tunnel to
* @param port number of TCP server
* @param key - base64 private key to receive I2P connections on
* @return dict with keys 'status' (string, 'ok' or 'fail').
* if 'fail', also a key 'error' with explanatory text.
*/
public Hashtable server(String host, int port, String key)
{
return tunmgr.xmlrpcServer(host, port, key);
}
/**
* Close an existing tunnel
* @param jobnum (int) job number of connection to close
* @return dict with keys 'status' (string, 'ok' or 'fail')
*/
public Hashtable close(int jobnum)
{
return tunmgr.xmlrpcClose(jobnum);
}
/**
* Close an existing tunnel
* @param jobnum (string) job number of connection to close as string,
* 'all' to close all jobs.
* @return dict with keys 'status' (string, 'ok' or 'fail')
*/
public Hashtable close(String job)
{
return tunmgr.xmlrpcClose(job);
}
/**
* Close zero or more tunnels matching given criteria
* @param criteria A dict containing zero or more of the keys:
* 'job' (job number), 'type' (string, 'server' or 'client'),
* 'host' (hostname), 'port' (port number),
* 'ip' (IP address), 'dest' (string, remote dest)
*/
public Hashtable close(Hashtable criteria)
{
return tunmgr.xmlrpcClose(criteria);
}
/**
* simple method to help with debugging your client prog
* @param x an int
* @return x + 1
*/
public int bar(int x)
{
System.out.println("foo invoked");
return x + 1;
}
/**
* as for bar(int), but returns zero if no arg given
*/
public int bar()
{
return bar(0);
}
}

View File

@ -0,0 +1,71 @@
package net.i2p.aum;
import org.apache.xmlrpc.*;
import java.lang.*;
import java.io.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* Provides a means for programs in any language to dynamically manage
* their own I2P <-> TCP tunnels, via simple TCP XML-RPC function calls.
* This server is presently hardwired to listen on port 22322.
*/
public class I2PTunnelXMLServer
{
protected WebServer ws;
protected I2PTunnelXMLObject tunobj;
public int port = 22322;
// constructor
public void _init()
{
ws = new WebServer(port);
tunobj = new I2PTunnelXMLObject();
ws.addHandler("i2p.tunnel", tunobj);
}
// default constructor
public I2PTunnelXMLServer()
{
super();
_init();
}
// constructor which takes shell args
public I2PTunnelXMLServer(String args[])
{
super();
_init();
}
// run the server
public void run()
{
ws.start();
System.out.println("I2PTunnel XML-RPC server listening on port "+port);
ws.run();
}
public static void main(String args[])
{
I2PTunnelXMLServer tun;
tun = new I2PTunnelXMLServer();
tun.run();
}
}

View File

@ -0,0 +1,71 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import org.apache.xmlrpc.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.Base64;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* an object which is used to invoke methods on remote I2P XML-RPC
* servers. You should not instantiate these objects directly, but
* create them through
* {@link net.i2p.aum.I2PXmlRpcClientFactory#newClient(Destination) I2PXmlRpcClientFactory.newClient()}
* Note that this is really just a thin wrapper around XmlRpcClient, mostly for reasons
* of consistency with I2PXmlRpcServer[Factory].
*/
public class I2PXmlRpcClient extends XmlRpcClient
{
public static boolean debug = false;
protected static Log _log;
/**
* Construct an I2P XML-RPC client with this URL.
* Note that you should not
* use this constructor directly - use I2PXmlRpcClientFactory.newClient() instead
*/
public I2PXmlRpcClient(URL url)
{
super(url);
_log = new Log("I2PXmlRpcClient");
}
/**
* Construct a XML-RPC client for the URL represented by this String.
* Note that you should not
* use this constructor directly - use I2PXmlRpcClientFactory.newClient() instead
*/
public I2PXmlRpcClient(String url) throws MalformedURLException
{
super(url);
_log = new Log("I2PXmlRpcClientFactory");
}
/**
* Construct a XML-RPC client for the specified hostname and port.
* Note that you should not
* use this constructor directly - use I2PXmlRpcClientFactory.newClient() instead
*/
public I2PXmlRpcClient(String hostname, int port) throws MalformedURLException
{
super(hostname, port);
_log = new Log("I2PXmlRpcClient");
}
}

View File

@ -0,0 +1,229 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import org.apache.xmlrpc.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.Base64;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* Creates I2P XML-RPC client objects, which you can use
* to issue XML-RPC function calls over I2P.
* Instantiating this class causes the vm-wide http proxy system
* properties to be set to the address of the I2P eepProxy host/port.
* I2PXmlRpcClient objects need to communicate with the I2P
* eepProxy. If your eepProxy is at the standard localhost:4444 address,
* you can use the default constructor. Otherwise, you can set this
* eepProxy address by either (1) passing eepProxy hostname/port to the
* constructor, or (2) running the jvm with 'eepproxy.tcp.host' and
* 'eepproxy.tcp.port' system properties set. Note that (1) takes precedence.
* Failure to set up EepProxy host/port correctly will result in an IOException
* when you invoke .execute() on your client objects.
* Invoke this class from your shell to see a demo
*/
public class I2PXmlRpcClientFactory
{
public static boolean debug = false;
public static String _defaultEepHost = "127.0.0.1";
public static int _defaultEepPort = 4444;
protected static Log _log;
/**
* Create an I2P XML-RPC client factory, and set it to create
* clients of a given class.
* @param clientClass a class to use when creating new clients
*/
public I2PXmlRpcClientFactory()
{
this(null, 0);
}
/**
* Create an I2P XML-RPC client factory, and set it to create
* clients of a given class, and dispatch calls through a non-standard
* eepProxy.
* @param eepHost the eepProxy TCP hostname
* @param eepPort the eepProxy TCP port number
*/
public I2PXmlRpcClientFactory(String eepHost, int eepPort)
{
String eepPortStr;
_log = new Log("I2PXmlRpcClientFactory");
_log.shouldLog(Log.DEBUG);
Properties p = System.getProperties();
// determine what actual eepproxy host/port we're using
if (eepHost == null) {
eepHost = p.getProperty("eepproxy.tcp.host", _defaultEepHost);
}
if (eepPort > 0) {
eepPortStr = String.valueOf(eepPort);
}
else {
eepPortStr = p.getProperty("eepproxy.tcp.port");
if (eepPortStr == null) {
eepPortStr = String.valueOf(_defaultEepPort);
}
}
p.put("proxySet", "true");
p.put("http.proxyHost", eepHost);
p.put("http.proxyPort", eepPortStr);
}
/**
* Create an I2P XML-RPC client object, which is subsequently used for
* dispatching XML-RPC requests.
* @param dest - an I2P destination object, comprising the
* destination of the remote
* I2P XML-RPC server.
* @return a new XmlRpcClient object (refer org.apache.xmlrpc.XmlRpcClient).
*/
public I2PXmlRpcClient newClient(Destination dest) throws MalformedURLException {
return newClient(new URL("http", "i2p/"+dest.toBase64(), "/"));
}
/**
* Create an I2P XML-RPC client object, which is subsequently used for
* dispatching XML-RPC requests.
* @param hostOrDest - an I2P hostname (listed in hosts.txt) or a
* destination base64 string, for the remote I2P XML-RPC server
* @return a new XmlRpcClient object (refer org.apache.xmlrpc.XmlRpcClient).
*/
public I2PXmlRpcClient newClient(String hostOrDest)
throws DataFormatException, MalformedURLException
{
String hostname;
URL u;
try {
// try to make a dest out of the string
Destination dest = new Destination();
dest.fromBase64(hostOrDest);
// converted ok, treat as valid dest, form i2p/blahblah url from it
I2PXmlRpcClient client = newClient(new URL("http", "i2p/"+hostOrDest, "/"));
client.debug = debug;
return client;
} catch (DataFormatException e) {
if (debug) {
e.printStackTrace();
print("hostOrDest length="+hostOrDest.length());
}
// failed to load up a dest, test length
if (hostOrDest.length() < 255) {
// short-ish, assume a hostname
u = new URL("http", hostOrDest, "/");
I2PXmlRpcClient client = newClient(u);
client.debug = debug;
return client;
}
else {
// too long for a host, barf
throw new DataFormatException("Bad I2P hostname/dest:\n"+hostOrDest);
}
}
}
/**
* Create an I2P XML-RPC client object, which is subsequently used for
* dispatching XML-RPC requests. This method is not recommended.
* @param u - a URL object, containing the URL of the remote
* I2P XML-RPC server, for example, "http://xmlrpc.aum.i2p" (assuming
* there's a hosts.txt entry for 'xmlrpc.aum.i2p'), or
* "http://i2p/base64destblahblah...". Note that if you use this method
* directly, the created XML-RPC client object will ONLY work if you
* instantiate the URL object as 'new URL("http", "i2p/"+host-or-dest, "/")'.
*/
protected I2PXmlRpcClient newClient(URL u)
{
Object [] args = { u };
//return new I2PXmlRpcClient(u);
// construct and return a client object of required class
return new I2PXmlRpcClient(u);
}
/**
* Runs a demo of an I2P XML-RPC client. Assumes you have already
* launched an I2PXmlRpcServerFactory demo, because it gets its
* dest from the file 'demo.dest64' created by I2PXmlRpcServerFactory demo.
*
* Ensure you have first launched net.i2p.aum.I2PXmlRpcServerFactory
* from your command line.
*/
public static void main(String [] args) {
String destStr;
debug = true;
try {
print("Creating client factory...");
I2PXmlRpcClientFactory f = new I2PXmlRpcClientFactory();
print("Creating new client...");
if (args.length == 0) {
print("Reading dest from demo.dest64");
destStr = new SimpleFile("demo.dest64", "r").read();
}
else {
destStr = args[0];
}
XmlRpcClient c = f.newClient(destStr);
print("Invoking foo...");
Vector v = new Vector();
v.add("one");
v.add("two");
Object res = c.execute("foo.bar", v);
print("Got back object: " + res);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Used for internal debugging
*/
protected static void print(String msg)
{
if (debug) {
System.out.println("I2PXmlRpcClient: " + msg);
if (_log != null) {
System.out.println("LOGGING SOME SHIT");
_log.debug(msg);
}
}
}
}

View File

@ -0,0 +1,34 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import org.apache.xmlrpc.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.Base64;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* A simple class providing callable xmlrpc server methods, gets linked in to
* the server demo.
*/
public class I2PXmlRpcDemoClass
{
public int add1(int n) {
return n + 1;
}
public String bar(String arg1, String arg2) {
System.out.println("Demo: got hit to bar: arg1='"+arg1+"', arg2='"+arg2+"'");
return "I2P demo xmlrpc server(foo.bar): arg1='"+arg1+"', arg2='"+arg2+"'";
}
}

View File

@ -0,0 +1,428 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import org.apache.xmlrpc.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.Base64;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* An XML-RPC server which works completely within I2P, listening
* on a dest for requests.
* You should not instantiate this class directly, but instead create
* an I2PXmlRpcServerFactory object, and use its .newServer() method
* to create a server object.
*/
public class I2PXmlRpcServer extends XmlRpcServer implements Runnable
{
public class I2PXmlRpcServerWorkerThread extends Thread {
I2PSocket _sock;
public I2PXmlRpcServerWorkerThread(I2PSocket sock) {
_sock = sock;
}
public void run() {
try {
System.out.println("I2PXmlRpcServer.run: got inbound XML-RPC I2P conn");
log.info("run: Got client connection, creating streams");
InputStream socketIn = _sock.getInputStream();
OutputStreamWriter socketOut = new OutputStreamWriter(_sock.getOutputStream());
log.info("run: reading http headers");
// read headers, determine size of req
int size = readHttpHeaders(socketIn);
if (size <= 0) {
// bad news
log.info("read req failed, terminating session");
_sock.close();
return;
}
log.info("run: reading request body of "+size+" bytes");
// get raw request body
byte [] reqBody = new byte[size];
for (int i=0; i<size; i++) {
int b = socketIn.read();
reqBody[i] = (byte)b;
}
//socketIn.read(reqBody);
//log.info("reqBody='" + (new String(reqBody)) + "'");
//System.out.println("reqBody='" + (new String(reqBody)) + "'");
//System.out.println("reqBody:");
//for (int ii=0; ii<reqBody.length; ii++) {
// System.out.println("i=" + ii + " ch="+reqBody[ii]);
//}
ByteArrayInputStream reqBodyStream = new ByteArrayInputStream(reqBody);
log.info("run: executing request");
System.out.println("run: executing request");
// read and execute full request
byte [] result;
try {
result = execute(reqBodyStream);
} catch (Exception e) {
System.out.println("run: execute failed, closing socket");
_sock.close();
System.out.println("run: closed socket");
throw e;
}
log.info("run: sending response");
// fudge - manual header and response generation
socketOut.write(
"HTTP/1.0 200 OK\r\n" +
"Server: I2P XML-RPC server by aum\r\n" +
"Date: " + (new Date().toString()) + "\r\n" +
"Content-type: text/xml\r\n" +
"Content-length: " + String.valueOf(result.length) + "\r\n" +
"\r\n");
socketOut.write(new String(result));
//socketOut.write(result);
socketOut.flush();
log.info("closing socket");
System.out.println("closing socket");
//response.setContentType ("text/xml");
//response.setContentLength (result.length());
//OutputStream out = response.getOutputStream();
//out.write (result);
//out.flush ();
_sock.close();
log.info("session complete");
} catch (Exception e) {
try {
e.printStackTrace();
_sock.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
// convenience - dest this server is listening on
public Destination dest;
// server's socket manager object
public I2PSocketManager socketMgr;
// server's socket
public I2PServerSocket serverSocket;
/** socket of latest incoming connection */
public I2PSocket sessSocket;
// set to enable debugging msgs
public static boolean debug = false;
// stream-proented xmlrpc server
protected net.i2p.util.Log log;
protected I2PAppContext i2p;
public Thread serverThread;
/**
* (do not use this constructor directly)
*/
public I2PXmlRpcServer(String keyStr, Properties props, I2PAppContext i2p)
throws DataFormatException, I2PException, IOException
{
this(PrivDestination.fromBase64String(keyStr), props, i2p);
}
/**
* (do not use this constructor directly)
*/
public I2PXmlRpcServer(PrivDestination privKey, Properties props, I2PAppContext i2p)
throws DataFormatException, I2PException
{
super();
log = i2p.logManager().getLog(this.getClass());
log.info("creating socket manager for key dest "
+ privKey.getDestinationBase64().substring(0, 16)
+ "...");
// start by getting a socket manager
socketMgr = I2PSocketManagerFactory.createManager(privKey.getInputStream(), props);
if (socketMgr == null) {
throw new I2PException("Failed to create socketManager, maybe can't reach i2cp port");
}
log.info("getting server socket, socketMgr="+socketMgr);
// get a server socket
serverSocket = socketMgr.getServerSocket();
log.info("got server socket, ready to run");
dest = socketMgr.getSession().getMyDestination();
log.info("full dest="+dest.toBase64());
System.out.println("full dest="+dest.toBase64());
}
/**
* Run this server within the current thread of execution.
* This function never returns. If you want to run the server
* in a background thread, use the .start() method instead.
*/
public void run()
{
log.info("run: listening for inbound XML-RPC requests...");
while (true)
{
System.out.println("I2PXmlRpcServer.run: waiting for inbound XML-RPC I2P conn...");
try {
sessSocket = serverSocket.accept();
I2PXmlRpcServerWorkerThread sessThread = new I2PXmlRpcServerWorkerThread(sessSocket);
sessThread.start();
/**
System.out.println("I2PXmlRpcServer.run: got inbound XML-RPC I2P conn");
log.info("run: Got client connection, creating streams");
InputStream socketIn = sessSocket.getInputStream();
OutputStreamWriter socketOut = new OutputStreamWriter(sessSocket.getOutputStream());
log.info("run: reading http headers");
// read headers, determine size of req
int size = readHttpHeaders(socketIn);
if (size <= 0) {
// bad news
log.info("read req failed, terminating session");
sessSocket.close();
continue;
}
log.info("run: reading request body of "+size+" bytes");
// get raw request body
byte [] reqBody = new byte[size];
for (int i=0; i<size; i++) {
int b = socketIn.read();
reqBody[i] = (byte)b;
}
//socketIn.read(reqBody);
//log.info("reqBody='" + (new String(reqBody)) + "'");
//System.out.println("reqBody='" + (new String(reqBody)) + "'");
//System.out.println("reqBody:");
//for (int ii=0; ii<reqBody.length; ii++) {
// System.out.println("i=" + ii + " ch="+reqBody[ii]);
//}
ByteArrayInputStream reqBodyStream = new ByteArrayInputStream(reqBody);
log.info("run: executing request");
System.out.println("run: executing request");
// read and execute full request
byte [] result;
try {
result = execute(reqBodyStream);
} catch (Exception e) {
System.out.println("run: execute failed, closing socket");
sessSocket.close();
System.out.println("run: closed socket");
throw e;
}
log.info("run: sending response");
// fudge - manual header and response generation
socketOut.write(
"HTTP/1.0 200 OK\r\n" +
"Server: I2P XML-RPC server by aum\r\n" +
"Date: " + (new Date().toString()) + "\r\n" +
"Content-type: text/xml\r\n" +
"Content-length: " + String.valueOf(result.length) + "\r\n" +
"\r\n");
socketOut.write(new String(result));
socketOut.flush();
log.info("closing socket");
System.out.println("closing socket");
//response.setContentType ("text/xml");
//response.setContentLength (result.length());
//OutputStream out = response.getOutputStream();
//out.write (result);
//out.flush ();
sessSocket.close();
log.info("session complete");
**/
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Called as part of an incoming XML-RPC request,
* reads and parses http headers from input stream.
* @param in the InputStream of the socket connection from the
* currently connected client
* @return value of 'Content-Length' field, as the number of bytes
* expected in the body of the request, or -1 if the request headers
* are invalid
* @throws IOException
*/
protected int readHttpHeaders(InputStream in) throws IOException
{
int contentLength = -1;
while (true) {
// read/parse one line
String line = readline(in);
String [] flds = line.split(":\\s+", 2);
log.debug("after split: flds='"+flds+"'");
String hdrKey = flds[0];
if (flds.length == 1) {
// not an HTTP header
log.info("skipping non-header, hdrKey='"+hdrKey+"'");
continue;
}
System.out.println("I2PXmlRpcServer: '"+flds[0]+"'='"+flds[1]+"'");
String hdrVal = flds[1];
log.info("hdrKey='"+hdrKey+"', hdrVal='"+hdrVal+"'");
if (hdrKey.equals("Content-Type")) {
if (!hdrVal.equals("text/xml")) {
// barf - not text/xml content type
return -1;
}
}
if (hdrKey.equals("Content-Length")) {
// got our length now - done with headers
contentLength = new Integer(hdrVal).intValue();
break;
}
}
log.info("Got content-length, now read remaining headers");
// read and discard any remaining headers
while (true) {
String line = readline(in);
int lineLen = line.length();
log.info("line("+lineLen+")='"+line+"'");
System.out.println("Disccarding superflous header: '"+line+"'");
if (lineLen == 0) {
break;
}
}
log.info("Content length is "+contentLength);
return contentLength;
}
/**
* Called as part of an incoming XML-RPC request,
* reads and parses http headers from input stream.
* @param in the InputStream of the socket connection from the
* currently connected client
* @return the line read, as a string
* @throws IOException
*/
protected String readline(InputStream in) throws IOException
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
while (true) {
int ch = in.read();
switch (ch) {
case '\n':
case -1:
String s = os.toString();
log.debug("Got line '"+s+"'");
return os.toString();
case '\r':
break;
default:
os.write(ch);
}
}
}
/**
* Launches the server as a background thread.
* (To run within the calling thread, use the .run() method instead).
*/
public void start()
{
log.debug("Starting server as a thread");
serverThread = new Thread(this);
serverThread.start();
}
/**
public void stop()
{
if (serverThread != null) {
serverThread.stop();
}
}
**/
}

View File

@ -0,0 +1,162 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import org.apache.xmlrpc.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.Base64;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* Generates I2P-compatible XML-RPC server objects
* (of class I2PXmlRpcServer). If you instead want to create
* instances of your own
*
* Invoke this class from your shell to see a demo
* @author aum
*/
public class I2PXmlRpcServerFactory
{
public I2PSocketManager socketManager;
public Properties props;
public static int defaultTunnelLength = 2;
// set to enable debugging msgs
public static boolean debug = false;
public static Log log;
protected I2PAppContext i2p;
// hostname/port of I2P router we're using
//public static String i2cpHost = "127.0.0.1";
//public static int i2cpPort = 7654;
/**
* Create an I2P XML-RPC server factory using default
* tunnel settings
*/
public I2PXmlRpcServerFactory(I2PAppContext i2p)
{
// get a socket manager
this(defaultTunnelLength, defaultTunnelLength,
defaultTunnelLength, defaultTunnelLength, i2p);
}
/**
* Create an I2P XML-RPC server factory, using settings provided
* by arguments
* @param lengthIn The value of 'inbound.length' property
* @param lengthOut The value of 'outbound.length' property
* @param lengthVarianceIn Value of 'inbound.lengthVariance' property
* @param lengthVarianceOut Value of 'outbound.lengthVariance' property
* @param log an I2P logger
*/
public I2PXmlRpcServerFactory(int lengthIn, int lengthOut,
int lengthVarianceIn, int lengthVarianceOut,
I2PAppContext i2p)
{
this.i2p = i2p;
log = i2p.logManager().getLog(this.getClass());
// set up tunnel properties for server objects
props = new Properties();
props.setProperty("inbound.length", String.valueOf(lengthIn));
props.setProperty("outbound.length", String.valueOf(lengthOut));
props.setProperty("inbound.lengthVariance", String.valueOf(lengthVarianceIn));
props.setProperty("outbound.lengthVariance", String.valueOf(lengthVarianceIn));
}
/**
* Creates a new I2PXmlRpcServer listening on a new randomly created destination
* @return a new I2PXmlRpcServer object, whose '.addHandler()' method you should
* invoke to add a handler object.
* @throws I2PException, IOException, DataFormatException
*/
public I2PXmlRpcServer newServer() throws I2PException, IOException, DataFormatException
{
return newServer(PrivDestination.newKey());
}
/**
* Creates a new I2PXmlRpcServer listening on a given dest, from key provided
* as a base64 string
* @param keyStr base64 representation of full private key for the destination
* the server is to listen on
* @return a new I2PXmlRpcServer object
* @throws DataFormatException
*/
public I2PXmlRpcServer newServer(String keyStr)
throws DataFormatException, I2PException, IOException
{
return newServer(PrivDestination.fromBase64String(keyStr));
}
/**
* Creates a new I2PXmlRpcServer listening on a given dest, from key provided
* as a PrivDestination object
* @param key a PrivDestination object representing the private destination
* the server is to listen on
* @return a new I2PXmlRpcServer object
* @throws DataFormatException
*/
public I2PXmlRpcServer newServer(PrivDestination key) throws DataFormatException, I2PException
{
// main newServer
I2PXmlRpcServer server = new I2PXmlRpcServer(key, props, i2p);
server.debug = debug;
return server;
}
/**
* Demonstration of I2P XML-RPC server.
* Creates a server listening on a random destination, and writes the base64
* destination into a file called "demo.dest64".
*
* After launching this program from a command shell, you should
* launch I2PXmlRpcClientFactory from another command shell
* to execute the client side of the demo.
*
* This program accepts no arguments.
*/
public static void main(String [] args)
{
debug = true;
I2PXmlRpcServer.debug = true;
I2PAppContext i2p = new I2PAppContext();
I2PXmlRpcServerFactory f = new I2PXmlRpcServerFactory(0,0,0,0, i2p);
try {
f.log.info("Creating new server on a new key");
I2PXmlRpcServer s = f.newServer();
f.log.info("Creating and adding handler object");
I2PXmlRpcDemoClass demo = new I2PXmlRpcDemoClass();
s.addHandler("foo", demo);
f.log.info("Saving dest for this server in file 'demo.dest64'");
new SimpleFile("demo.dest64", "rws").write(s.dest.toBase64());
f.log.info("Running server (Press Ctrl-C to kill)");
s.run();
} catch (Exception e) { e.printStackTrace(); }
}
}

View File

@ -0,0 +1,392 @@
package net.i2p.aum;
/**
* creates a convenient map of file extensions <-> mimetypes
*/
public class Mimetypes
{
public static String [][] _map = {
{ ".bz2", "application/x-bzip2" },
{ ".csm", "application/cu-seeme" },
{ ".cu", "application/cu-seeme" },
{ ".tsp", "application/dsptype" },
{ ".xls", "application/excel" },
{ ".spl", "application/futuresplash" },
{ ".hqx", "application/mac-binhex40" },
{ ".doc", "application/msword" },
{ ".dot", "application/msword" },
{ ".bin", "application/octet-stream" },
{ ".oda", "application/oda" },
{ ".pdf", "application/pdf" },
{ ".asc", "application/pgp-keys" },
{ ".pgp", "application/pgp-signature" },
{ ".ps", "application/postscript" },
{ ".ai", "application/postscript" },
{ ".eps", "application/postscript" },
{ ".ppt", "application/powerpoint" },
{ ".rtf", "application/rtf" },
{ ".wp5", "application/wordperfect5.1" },
{ ".zip", "application/zip" },
{ ".wk", "application/x-123" },
{ ".bcpio", "application/x-bcpio" },
{ ".pgn", "application/x-chess-pgn" },
{ ".cpio", "application/x-cpio" },
{ ".deb", "application/x-debian-package" },
{ ".dcr", "application/x-director" },
{ ".dir", "application/x-director" },
{ ".dxr", "application/x-director" },
{ ".dvi", "application/x-dvi" },
{ ".pfa", "application/x-font" },
{ ".pfb", "application/x-font" },
{ ".gsf", "application/x-font" },
{ ".pcf", "application/x-font" },
{ ".pcf.Z", "application/x-font" },
{ ".gtar", "application/x-gtar" },
{ ".tgz", "application/x-gtar" },
{ ".hdf", "application/x-hdf" },
{ ".phtml", "application/x-httpd-php" },
{ ".pht", "application/x-httpd-php" },
{ ".php", "application/x-httpd-php" },
{ ".php3", "application/x-httpd-php3" },
{ ".phps", "application/x-httpd-php3-source" },
{ ".php3p", "application/x-httpd-php3-preprocessed" },
{ ".class", "application/x-java" },
{ ".latex", "application/x-latex" },
{ ".frm", "application/x-maker" },
{ ".maker", "application/x-maker" },
{ ".frame", "application/x-maker" },
{ ".fm", "application/x-maker" },
{ ".fb", "application/x-maker" },
{ ".book", "application/x-maker" },
{ ".fbdoc", "application/x-maker" },
{ ".mif", "application/x-mif" },
{ ".nc", "application/x-netcdf" },
{ ".cdf", "application/x-netcdf" },
{ ".pac", "application/x-ns-proxy-autoconfig" },
{ ".o", "application/x-object" },
{ ".pl", "application/x-perl" },
{ ".pm", "application/x-perl" },
{ ".shar", "application/x-shar" },
{ ".swf", "application/x-shockwave-flash" },
{ ".swfl", "application/x-shockwave-flash" },
{ ".sit", "application/x-stuffit" },
{ ".sv4cpio", "application/x-sv4cpio" },
{ ".sv4crc", "application/x-sv4crc" },
{ ".tar", "application/x-tar" },
{ ".gf", "application/x-tex-gf" },
{ ".pk", "application/x-tex-pk" },
{ ".PK", "application/x-tex-pk" },
{ ".texinfo", "application/x-texinfo" },
{ ".texi", "application/x-texinfo" },
{ ".~", "application/x-trash" },
{ ".%", "application/x-trash" },
{ ".bak", "application/x-trash" },
{ ".old", "application/x-trash" },
{ ".sik", "application/x-trash" },
{ ".t", "application/x-troff" },
{ ".tr", "application/x-troff" },
{ ".roff", "application/x-troff" },
{ ".man", "application/x-troff-man" },
{ ".me", "application/x-troff-me" },
{ ".ms", "application/x-troff-ms" },
{ ".ustar", "application/x-ustar" },
{ ".src", "application/x-wais-source" },
{ ".wz", "application/x-wingz" },
{ ".au", "audio/basic" },
{ ".snd", "audio/basic" },
{ ".mid", "audio/midi" },
{ ".midi", "audio/midi" },
{ ".mpga", "audio/mpeg" },
{ ".mpega", "audio/mpeg" },
{ ".mp2", "audio/mpeg" },
{ ".mp3", "audio/mpeg" },
{ ".m3u", "audio/mpegurl" },
{ ".aif", "audio/x-aiff" },
{ ".aiff", "audio/x-aiff" },
{ ".aifc", "audio/x-aiff" },
{ ".gsm", "audio/x-gsm" },
{ ".ra", "audio/x-pn-realaudio" },
{ ".rm", "audio/x-pn-realaudio" },
{ ".ram", "audio/x-pn-realaudio" },
{ ".rpm", "audio/x-pn-realaudio-plugin" },
{ ".wav", "audio/x-wav" },
{ ".gif", "image/gif" },
{ ".ief", "image/ief" },
{ ".jpeg", "image/jpeg" },
{ ".jpg", "image/jpeg" },
{ ".jpe", "image/jpeg" },
{ ".png", "image/png" },
{ ".tiff", "image/tiff" },
{ ".tif", "image/tiff" },
{ ".ras", "image/x-cmu-raster" },
{ ".bmp", "image/x-ms-bmp" },
{ ".pnm", "image/x-portable-anymap" },
{ ".pbm", "image/x-portable-bitmap" },
{ ".pgm", "image/x-portable-graymap" },
{ ".ppm", "image/x-portable-pixmap" },
{ ".rgb", "image/x-rgb" },
{ ".xbm", "image/x-xbitmap" },
{ ".xpm", "image/x-xpixmap" },
{ ".xwd", "image/x-xwindowdump" },
{ ".csv", "text/comma-separated-values" },
{ ".html", "text/html" },
{ ".htm", "text/html" },
{ ".mml", "text/mathml" },
{ ".txt", "text/plain" },
{ ".rtx", "text/richtext" },
{ ".tsv", "text/tab-separated-values" },
{ ".h++", "text/x-c++hdr" },
{ ".hpp", "text/x-c++hdr" },
{ ".hxx", "text/x-c++hdr" },
{ ".hh", "text/x-c++hdr" },
{ ".c++", "text/x-c++src" },
{ ".cpp", "text/x-c++src" },
{ ".cxx", "text/x-c++src" },
{ ".cc", "text/x-c++src" },
{ ".h", "text/x-chdr" },
{ ".csh", "text/x-csh" },
{ ".c", "text/x-csrc" },
{ ".java", "text/x-java" },
{ ".moc", "text/x-moc" },
{ ".p", "text/x-pascal" },
{ ".pas", "text/x-pascal" },
{ ".etx", "text/x-setext" },
{ ".sh", "text/x-sh" },
{ ".tcl", "text/x-tcl" },
{ ".tk", "text/x-tcl" },
{ ".tex", "text/x-tex" },
{ ".ltx", "text/x-tex" },
{ ".sty", "text/x-tex" },
{ ".cls", "text/x-tex" },
{ ".vcs", "text/x-vCalendar" },
{ ".vcf", "text/x-vCard" },
{ ".dl", "video/dl" },
{ ".fli", "video/fli" },
{ ".gl", "video/gl" },
{ ".mpeg", "video/mpeg" },
{ ".mpg", "video/mpeg" },
{ ".mpe", "video/mpeg" },
{ ".qt", "video/quicktime" },
{ ".mov", "video/quicktime" },
{ ".asf", "video/x-ms-asf" },
{ ".asx", "video/x-ms-asf" },
{ ".avi", "video/x-msvideo" },
{ ".movie", "video/x-sgi-movie" },
{ ".vrm", "x-world/x-vrml" },
{ ".vrml", "x-world/x-vrml" },
{ ".wrl", "x-world/x-vrml" },
};
/**
* Attempts to determine a mimetype
* @param path - either a file extension string (containing the
* leading '.') or a full file pathname (in which case, the extension
* will be extracted).
* @return the mimetype that corresponds to the file extension, if the
* file extension is known, or "application/octet-stream" if the
* file extension is not known.
*/
public static String guessType(String path) {
// rip the file extension from the path
// first - split 'directories', and get last part
String [] dirs = path.split("/");
String filename = dirs[dirs.length-1];
String [] bits = filename.split("\\.");
String extension = "." + bits[bits.length-1];
// default mimetype applied to unknown file extensions
String type = "application/octet-stream";
for (int i=0; i<_map.length; i++) {
String [] rec = _map[i];
if (rec[0].equals(extension)) {
type = rec[1];
break;
}
}
return type;
}
/**
* Attempts to guess the file extension corresponding to a given
* mimetype.
* @param type a mimetype string
* @return a file extension commonly used for storing files of this type,
* or defaults to ".bin" if mimetype not known
*/
public static String guessExtension(String type) {
// default extension applied to unknown mimetype
String extension = ".bin";
for (int i=0; i<_map.length; i++) {
String [] rec = _map[i];
if (rec[1].equals(type)) {
extension = rec[0];
break;
}
}
return extension;
}
}
/**
suffix_map = {
'.tgz': '.tar.gz',
'.taz': '.tar.gz',
'.tz': '.tar.gz',
}
encodings_map = {
'.gz': 'gzip',
'.Z': 'compress',
}
# Before adding new types, make sure they are either registered with IANA, at
# http://www.isi.edu/in-notes/iana/assignments/media-types
# or extensions, i.e. using the x- prefix
# If you add to these, please keep them sorted!
types_map = {
'.a' : 'application/octet-stream',
'.ai' : 'application/postscript',
'.aif' : 'audio/x-aiff',
'.aifc' : 'audio/x-aiff',
'.aiff' : 'audio/x-aiff',
'.au' : 'audio/basic',
'.avi' : 'video/x-msvideo',
'.bat' : 'text/plain',
'.bcpio' : 'application/x-bcpio',
'.bin' : 'application/octet-stream',
'.bmp' : 'image/x-ms-bmp',
'.c' : 'text/plain',
# Duplicates :(
'.cdf' : 'application/x-cdf',
'.cdf' : 'application/x-netcdf',
'.cpio' : 'application/x-cpio',
'.csh' : 'application/x-csh',
'.css' : 'text/css',
'.dll' : 'application/octet-stream',
'.doc' : 'application/msword',
'.dot' : 'application/msword',
'.dvi' : 'application/x-dvi',
'.eml' : 'message/rfc822',
'.eps' : 'application/postscript',
'.etx' : 'text/x-setext',
'.exe' : 'application/octet-stream',
'.gif' : 'image/gif',
'.gtar' : 'application/x-gtar',
'.h' : 'text/plain',
'.hdf' : 'application/x-hdf',
'.htm' : 'text/html',
'.html' : 'text/html',
'.ief' : 'image/ief',
'.jpe' : 'image/jpeg',
'.jpeg' : 'image/jpeg',
'.jpg' : 'image/jpeg',
'.js' : 'application/x-javascript',
'.ksh' : 'text/plain',
'.latex' : 'application/x-latex',
'.m1v' : 'video/mpeg',
'.man' : 'application/x-troff-man',
'.me' : 'application/x-troff-me',
'.mht' : 'message/rfc822',
'.mhtml' : 'message/rfc822',
'.mif' : 'application/x-mif',
'.mov' : 'video/quicktime',
'.movie' : 'video/x-sgi-movie',
'.mp2' : 'audio/mpeg',
'.mp3' : 'audio/mpeg',
'.mpa' : 'video/mpeg',
'.mpe' : 'video/mpeg',
'.mpeg' : 'video/mpeg',
'.mpg' : 'video/mpeg',
'.ms' : 'application/x-troff-ms',
'.nc' : 'application/x-netcdf',
'.nws' : 'message/rfc822',
'.o' : 'application/octet-stream',
'.obj' : 'application/octet-stream',
'.oda' : 'application/oda',
'.p12' : 'application/x-pkcs12',
'.p7c' : 'application/pkcs7-mime',
'.pbm' : 'image/x-portable-bitmap',
'.pdf' : 'application/pdf',
'.pfx' : 'application/x-pkcs12',
'.pgm' : 'image/x-portable-graymap',
'.pl' : 'text/plain',
'.png' : 'image/png',
'.pnm' : 'image/x-portable-anymap',
'.pot' : 'application/vnd.ms-powerpoint',
'.ppa' : 'application/vnd.ms-powerpoint',
'.ppm' : 'image/x-portable-pixmap',
'.pps' : 'application/vnd.ms-powerpoint',
'.ppt' : 'application/vnd.ms-powerpoint',
'.ps' : 'application/postscript',
'.pwz' : 'application/vnd.ms-powerpoint',
'.py' : 'text/x-python',
'.pyc' : 'application/x-python-code',
'.pyo' : 'application/x-python-code',
'.qt' : 'video/quicktime',
'.ra' : 'audio/x-pn-realaudio',
'.ram' : 'application/x-pn-realaudio',
'.ras' : 'image/x-cmu-raster',
'.rdf' : 'application/xml',
'.rgb' : 'image/x-rgb',
'.roff' : 'application/x-troff',
'.rtx' : 'text/richtext',
'.sgm' : 'text/x-sgml',
'.sgml' : 'text/x-sgml',
'.sh' : 'application/x-sh',
'.shar' : 'application/x-shar',
'.snd' : 'audio/basic',
'.so' : 'application/octet-stream',
'.src' : 'application/x-wais-source',
'.sv4cpio': 'application/x-sv4cpio',
'.sv4crc' : 'application/x-sv4crc',
'.swf' : 'application/x-shockwave-flash',
'.t' : 'application/x-troff',
'.tar' : 'application/x-tar',
'.tcl' : 'application/x-tcl',
'.tex' : 'application/x-tex',
'.texi' : 'application/x-texinfo',
'.texinfo': 'application/x-texinfo',
'.tif' : 'image/tiff',
'.tiff' : 'image/tiff',
'.tr' : 'application/x-troff',
'.tsv' : 'text/tab-separated-values',
'.txt' : 'text/plain',
'.ustar' : 'application/x-ustar',
'.vcf' : 'text/x-vcard',
'.wav' : 'audio/x-wav',
'.wiz' : 'application/msword',
'.xbm' : 'image/x-xbitmap',
'.xlb' : 'application/vnd.ms-excel',
# Duplicates :(
'.xls' : 'application/excel',
'.xls' : 'application/vnd.ms-excel',
'.xml' : 'text/xml',
'.xpm' : 'image/x-xpixmap',
'.xsl' : 'application/xml',
'.xwd' : 'image/x-xwindowdump',
'.zip' : 'application/zip',
}
# These are non-standard types, commonly found in the wild. They will only
# match if strict=0 flag is given to the API methods.
# Please sort these too
common_types = {
'.jpg' : 'image/jpg',
'.mid' : 'audio/midi',
'.midi': 'audio/midi',
'.pct' : 'image/pict',
'.pic' : 'image/pict',
'.pict': 'image/pict',
'.rtf' : 'application/rtf',
'.xul' : 'text/xul'
}
**/

View File

@ -0,0 +1,18 @@
package net.i2p.aum;
public class OOTest
{
public int add(int a, int b)
{
return (a + b);
}
public static void main(String[] args)
{
OOTest mytest = new OOTest();
System.out.println(mytest.add(3,3));
}
}

View File

@ -0,0 +1,232 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.data.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.data.Base64;
import net.i2p.util.*;
import net.i2p.data.*;
/**
* A convenience class for encapsulating and manipulating I2P private keys
*/
public class PrivDestination
//extends ByteArrayInputStream
extends DataStructureImpl
{
protected byte [] _bytes;
protected Destination _dest;
protected PrivateKey _privKey;
protected SigningPrivateKey _signingPrivKey;
protected static Log _log;
/**
* Create a PrivDestination object.
* In most cases, you'll probably want to skip this constructor,
* and create PrivDestination objects by invoking the desired static methods
* of this class.
* @param raw an array of bytes containing the raw binary private key
*/
public PrivDestination(byte [] raw) throws DataFormatException, IOException
{
//super(raw);
_log = new Log("PrivDestination");
_bytes = raw;
readBytes(getInputStream());
}
/**
* reconstitutes a PrivDestination from previously exported Base64
*/
public PrivDestination(String b64) throws DataFormatException, IOException {
this(Base64.decode(b64));
}
/**
* generates a new PrivDestination with random keys
*/
public PrivDestination() throws I2PException, IOException
{
I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
// create a dest
client.createDestination(streamOut);
_bytes = streamOut.toByteArray();
readBytes(getInputStream());
// construct from the stream
//return new PrivDestination(streamOut.toByteArray());
}
/** return the public Destination object for this private dest */
public Destination getDestination() {
return _dest;
}
/** return a PublicKey (encryption public key) object for this priv dest */
public PublicKey getPublicKey() {
return getDestination().getPublicKey();
}
/** return a PrivateKey (encryption private key) object for this priv dest */
public PrivateKey getPrivateKey() {
return _privKey;
}
/** return a SigningPublicKey object for this priv dest */
public SigningPublicKey getSigningPublicKey() {
return getDestination().getSigningPublicKey();
}
/** return a SigningPrivateKey object for this priv dest */
public SigningPrivateKey getSigningPrivateKey() {
return _signingPrivKey;
}
// static methods returning an instance
/**
* Creates a PrivDestination object
* @param base64 a string containing the base64 private key data
* @return a PrivDestination object encapsulating that key
*/
public static PrivDestination fromBase64String(String base64)
throws DataFormatException, IOException
{
return new PrivDestination(Base64.decode(base64));
}
/**
* Creates a PrivDestination object, from the base64 key data
* stored in a file.
* @param path the pathname of the file from which to read the base64 private key data
* @return a PrivDestination object encapsulating that key
*/
public static PrivDestination fromBase64File(String path)
throws FileNotFoundException, IOException, DataFormatException
{
return fromBase64String(new SimpleFile(path, "r").read());
/*
File f = new File(path);
char [] rawchars = new char[(int)(f.length())];
byte [] rawbytes = new byte[(int)(f.length())];
FileReader fr = new FileReader(f);
fr.read(rawchars);
String raw64 = new String(rawchars);
return PrivDestination.fromBase64String(raw64);
*/
}
/**
* Creates a PrivDestination object, from the binary key data
* stored in a file.
* @param path the pathname of the file from which to read the binary private key data
* @return a PrivDestination object encapsulating that key
*/
public static PrivDestination fromBinFile(String path)
throws FileNotFoundException, IOException, DataFormatException
{
byte [] raw = new SimpleFile(path, "r").readBytes();
return new PrivDestination(raw);
}
/**
* Generate a new random I2P private key
* @return a PrivDestination object encapsulating that key
*/
public static PrivDestination newKey() throws I2PException, IOException
{
return new PrivDestination();
}
public ByteArrayInputStream getInputStream()
{
return new ByteArrayInputStream(_bytes);
}
/**
* Exports the key's full contents to a string
* @return A base64-format string containing the full contents
* of this private key. The string can be used in any subsequent
* call to the .fromBase64String static constructor method.
*/
/*
public String toBase64()
{
return Base64.encode(_bytes);
}
*/
/**
* Exports the key's full contents to a byte array
* @return A byte array containing the full contents
* of this private key.
*/
/*
public byte [] toBytes()
{
return _bytes;
}
*/
/**
* Converts this key to a public destination.
* @return a standard I2P Destination object containing the
* public portion of this private key.
*/
/*
public Destination toDestination() throws DataFormatException
{
Destination dest = new Destination();
dest.readBytes(_bytes, 0);
return dest;
}
*/
/**
* Converts this key to a base64 string representing a public destination
* @return a string containing a base64 representation of the destination
* corresponding to this private key.
*/
public String getDestinationBase64() throws DataFormatException
{
return getDestination().toBase64();
}
public void readBytes(java.io.InputStream strm)
throws net.i2p.data.DataFormatException, java.io.IOException
{
_dest = new Destination();
_privKey = new PrivateKey();
_signingPrivKey = new SigningPrivateKey();
_dest.readBytes(strm);
_privKey.readBytes(strm);
_signingPrivKey.readBytes(strm);
}
public void writeBytes(java.io.OutputStream outputStream)
throws net.i2p.data.DataFormatException, java.io.IOException
{
_dest.writeBytes(outputStream);
_privKey.writeBytes(outputStream);
_signingPrivKey.writeBytes(outputStream);
}
}

View File

@ -0,0 +1,209 @@
/*
* PropertiesFile.java
*
* Created on 20 March 2005, 19:30
*/
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
/**
* builds on Properties with methods to load/save directly to/from file
*/
public class PropertiesFile extends Properties {
public String _path;
public File _file;
public boolean _fileExists;
/**
* Creates a new instance of PropertiesFile
* @param path Absolute pathname of file where properties are to be stored
*/
public PropertiesFile(String path) throws IOException {
super();
_path = path;
_file = new File(path);
_fileExists = _file.isFile();
if (_file.canRead()) {
loadFromFile();
}
}
/**
* Creates new PropertiesFile, updating its content with the
* keys/values in given hashtable
* @param path absolute pathname where properties file is located in filesystem
* @param h instance of Hashtable (or subclass). its content
* will be written to this object (note that string representations of keys/vals
* will be used)
*/
public PropertiesFile(String path, Hashtable h) throws IOException
{
this(path);
Enumeration keys = h.keys();
Object key;
while (true)
{
try {
key = keys.nextElement();
} catch (NoSuchElementException e) {
break;
}
setProperty(key.toString(), h.get(key).toString());
}
}
/**
* Loads this object from the file
*/
public void loadFromFile() throws IOException, FileNotFoundException {
if (_file.canRead()) {
InputStream fis = new FileInputStream(_file);
load(fis);
}
}
/**
* Saves this object to the file
*/
public void saveToFile() throws IOException, FileNotFoundException {
if (!_fileExists) {
_file.createNewFile();
_fileExists = true;
}
OutputStream fos = new FileOutputStream(_file);
store(fos, null);
}
/**
* Stores attribute
*/
public Object setProperty(String key, String value) {
Object o = super.setProperty(key, value);
try {
saveToFile();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
/**
* return a property as an int, fall back on default if not found or invalid
*/
public int getIntProperty(String key, int dflt) {
try {
return new Integer((String)getProperty(key)).intValue();
} catch (Exception e) {
setIntProperty(key, dflt);
return dflt;
}
}
/**
* return a property as an int
*/
public int getIntProperty(String key) {
return new Integer((String)getProperty(key)).intValue();
}
/**
* set a property as an int
*/
public void setIntProperty(String key, int value) {
setProperty(key, String.valueOf(value));
}
/**
* return a property as a long, fall back on default if not found or invalid
*/
public long getIntProperty(String key, long dflt) {
try {
return new Long((String)getProperty(key)).longValue();
} catch (Exception e) {
setLongProperty(key, dflt);
return dflt;
}
}
/**
* return a property as an int
*/
public long getLongProperty(String key) {
return new Long((String)getProperty(key)).longValue();
}
/**
* set a property as an int
*/
public void setLongProperty(String key, long value) {
setProperty(key, String.valueOf(value));
}
/**
* return a property as a float
*/
public double getFloatProperty(String key) {
return new Float((String)getProperty(key)).floatValue();
}
/**
* return a property as a float, fall back on default if not found or invalid
*/
public double getFloatProperty(String key, float dflt) {
try {
return new Float((String)getProperty(key)).floatValue();
} catch (Exception e) {
setFloatProperty(key, dflt);
return dflt;
}
}
/**
* set a property as a float
*/
public void setFloatProperty(String key, float value) {
setProperty(key, String.valueOf(value));
}
/**
* return a property as a double
*/
public double getDoubleProperty(String key) {
return new Double((String)getProperty(key)).doubleValue();
}
/**
* return a property as a double, fall back on default if not found
*/
public double getDoubleProperty(String key, double dflt) {
try {
return new Double((String)getProperty(key)).doubleValue();
} catch (Exception e) {
setDoubleProperty(key, dflt);
return dflt;
}
}
/**
* set a property as a double
*/
public void setDoubleProperty(String key, double value) {
setProperty(key, String.valueOf(value));
}
/**
* increment an integer property value
*/
public void incrementIntProperty(String key) {
setIntProperty(key, getIntProperty(key)+1);
}
}

View File

@ -0,0 +1,120 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.data.*;
/**
* SimpleFile - subclass of File which adds some python-like
* methods. Cuts out a lot of the red tape involved with reading
* from and writing to files
*/
public class SimpleFile {
public RandomAccessFile _file;
public String _path;
public SimpleFile(String path, String mode) throws FileNotFoundException {
_path = path;
_file = new RandomAccessFile(path, mode);
}
public byte [] readBytes() throws IOException {
return readBytes((int)_file.length());
}
public byte[] readBytes(int n) throws IOException {
byte [] buf = new byte[n];
_file.readFully(buf);
return buf;
}
public char [] readChars() throws IOException {
return readChars((int)_file.length());
}
public char[] readChars(int n) throws IOException {
char [] buf = new char[n];
//_file.readFully(buf);
return buf;
}
/**
* Reads all remaining content from the file
* @return the content as a String
* @throws IOException
*/
public String read() throws IOException {
return read((int)_file.length());
}
/**
* Reads one or more bytes of data from the file
* @return the content as a String
* @throws IOException
*/
public String read(int nbytes) throws IOException {
return new String(readBytes(nbytes));
}
/**
* Writes one or more bytes of data to a file
* @param buf a String containing the data to write
* @return the number of bytes written, as an int
* @throws IOException
*/
public int write(String buf) throws IOException {
return write(buf.getBytes());
}
public int write(byte [] buf) throws IOException {
_file.write(buf);
return buf.length;
}
/**
* convenient one-hit write
* @param path pathname of file to write to
* @param buf data to write
*/
public static int write(String path, String buf) throws IOException {
return new SimpleFile(path, "rws").write(buf);
}
/**
* tests if argument refers to an actual file
* @param path pathname to test
* @return true if a file, false if not
*/
public boolean isFile() {
return new File(_path).isFile();
}
/**
* tests if argument refers to a directory
* @param path pathname to test
* @return true if a directory, false if not
*/
public boolean isDir() {
return new File(_path).isDirectory();
}
/**
* tests if a file or directory exists
* @param path pathname to test
* @return true if exists, or false
*/
public boolean exists() {
return new File(_path).exists();
}
}

View File

@ -0,0 +1,123 @@
package net.i2p.aum;
import java.lang.*;
import java.io.*;
import java.util.*;
import java.net.*;
import net.i2p.data.*;
/**
* SimpleFile - subclass of File which adds some python-like
* methods. Cuts out a lot of the red tape involved with reading
* from and writing to files
*/
public class SimpleFile_old extends File {
public FileReader _reader;
public FileWriter _writer;
public SimpleFile_old(String path) {
super(path);
_reader = null;
_writer = null;
}
/**
* Reads all remaining content from the file
* @return the content as a String
* @throws IOException
*/
public String read() throws IOException {
return read((int)length());
}
/**
* Reads one or more bytes of data from the file
* @return the content as a String
* @throws IOException
*/
public String read(int nbytes) throws IOException {
// get a reader, if we don't already have one
if (_reader == null) {
_reader = new FileReader(this);
}
char [] cbuf = new char[nbytes];
int nread = _reader.read(cbuf);
if (nread == 0) {
return "";
}
return new String(cbuf, 0, nread);
}
/**
* Writes one or more bytes of data to a file
* @param buf a String containing the data to write
* @return the number of bytes written, as an int
* @throws IOException
*/
public int write(String buf) throws IOException {
// get a reader, if we don't already have one
if (_writer == null) {
_writer = new FileWriter(this);
}
_writer.write(buf);
_writer.flush();
return buf.length();
}
public int write(byte [] buf) throws IOException {
return write(new String(buf));
}
/**
* convenient one-hit write
* @param path pathname of file to write to
* @param buf data to write
*/
public static int write(String path, String buf) throws IOException {
SimpleFile_old f = new SimpleFile_old(path);
return f.write(buf);
}
/**
* tests if argument refers to an actual file
* @param path pathname to test
* @return true if a file, false if not
*/
public static boolean isFile(String path) {
return new File(path).isFile();
}
/**
* tests if argument refers to a directory
* @param path pathname to test
* @return true if a directory, false if not
*/
public static boolean isDir(String path) {
return new File(path).isDirectory();
}
/**
* tests if a file or directory exists
* @param path pathname to test
* @return true if exists, or false
*/
public static boolean exists(String path) {
return new File(path).exists();
}
}

View File

@ -0,0 +1,138 @@
/*
* SimpleQueue.java
*
* Created on March 24, 2005, 11:14 PM
*/
package net.i2p.aum;
import java.*;
import java.lang.*;
import java.util.*;
/**
* Implements simething similar to python's 'Queue' class
*/
public class SimpleQueue {
public Vector items;
/** Creates a new instance of SimpleQueue */
public SimpleQueue() {
items = new Vector();
}
/**
* fetches the item at head of queue, blocking if queue is empty
*/
public synchronized Object get()
{
while (true)
{
try {
if (items.size() == 0)
wait();
// someone has added
Object item = items.get(0);
items.remove(0);
return item;
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* adds a new object to the queue
*/
public synchronized void put(Object item)
{
items.addElement(item);
notify();
}
private static class TestThread extends Thread {
String id;
SimpleQueue q;
public TestThread(String id, SimpleQueue q) {
this.id = id;
this.q = q;
}
public void run() {
try {
print("waiting for queue");
Object item = q.get();
print("got item: '"+item+"'");
} catch (Exception e) {
e.printStackTrace();
return;
}
}
public void print(String msg) {
System.out.println("thread '"+id+"': "+msg);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
int i;
int nthreads = 7;
Thread [] threads = new Thread[nthreads];
SimpleQueue q = new SimpleQueue();
// populate the queue with some stuff
q.put("red");
q.put("orange");
q.put("yellow");
// populate threads array
for (i = 0; i < nthreads; i++) {
threads[i] = new TestThread("thread"+i, q);
}
// and launch the threads
for (i = 0; i < nthreads; i++) {
threads[i].start();
}
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
return;
}
// wait a bit and see what happens
String [] items = {"green", "blue", "indigo", "violet", "black", "white", "brown"};
for (i = 0; i < items.length; i++) {
String item = items[i];
System.out.println("main: adding '"+item+"'...");
q.put(item);
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
System.out.println("main: terminating");
}
}

View File

@ -0,0 +1,108 @@
/*
* SimpleSemaphore.java
*
* Created on March 24, 2005, 11:51 PM
*/
package net.i2p.aum;
/**
* Simple implementation of semaphores
*/
public class SimpleSemaphore {
protected int count;
/** Creates a new instance of SimpleSemaphore */
public SimpleSemaphore(int size) {
count = size;
}
public synchronized void acquire() throws InterruptedException
{
if (count == 0)
{
wait();
}
count -= 1;
}
public synchronized void release()
{
count += 1;
notify();
}
private static class TestThread extends Thread
{
String id;
SimpleSemaphore sem;
public TestThread(String id, SimpleSemaphore sem)
{
this.id = id;
this.sem = sem;
}
public void run()
{
try {
print("waiting for semaphore");
sem.acquire();
print("got semaphore");
Thread.sleep(1000);
print("releasing semaphore");
sem.release();
print("terminating");
} catch (Exception e) {
e.printStackTrace();
return;
}
}
public void print(String msg) {
System.out.println("thread '"+id+"': "+msg);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
int i;
Thread [] threads = new Thread[10];
SimpleSemaphore sem = new SimpleSemaphore(3);
// populate threads array
for (i = 0; i < 10; i++) {
threads[i] = new TestThread("thread"+i, sem);
}
// and launch the threads
for (i = 0; i < 10; i++) {
threads[i].start();
}
// wait a bit and see what happens
System.out.println("main: threads launched, waiting 20 secs");
try {
Thread.sleep(20000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("main: terminating");
}
}

View File

@ -0,0 +1,17 @@
public class helloworld
{
public static void main(String [] args)
{
helloworld h = new helloworld();
h.greet();
}
public void greet()
{
System.out.println("Hi, this is your greeting");
}
}

View File

@ -0,0 +1,72 @@
/*
* HtmlPage.java
*
* Created on April 8, 2005, 8:22 PM
*/
package net.i2p.aum.http;
import java.util.*;
import net.i2p.aum.*;
/**
* Framework for building up a page of HTML by method calls alone, breaking
* every design rule by enmeshing content, presentation and logic
*/
public class HtmlPage {
public String dtd = "<!DOCTYPE HTML PUBLIC "
+"\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
+"\"http://www.w3.org/TR/html4/loose.dtd\">";
public Tag page;
public Tag head;
public Tag body;
DupHashtable cssSettings;
/** Creates a new HtmlPage object */
public HtmlPage() {
page = new Tag("html");
head = new Tag(page, "head");
body = new Tag(page, "body");
cssSettings = new DupHashtable();
}
/** renders out the whole page into a single string */
public String toString() {
// embed stylesheet, if non-empty
if (cssSettings.size() > 0) {
Tag t1 = head.nest("style type=\"text/css\"");
t1.raw("<!--\n");
Tag cssTag = t1.nest();
t1.raw("-->\n");
Enumeration elems = cssSettings.keys();
while (elems.hasMoreElements()) {
String name = (String)elems.nextElement();
cssTag.raw(name + " { ");
Enumeration items = cssSettings.get(name).elements();
while (items.hasMoreElements()) {
String item = (String)items.nextElement();
cssTag.raw(item+";");
}
cssTag.raw(" }\n");
}
}
// now render out the whole page
return dtd + "\n" + page;
}
/** adds a setting to the page's embedded stylesheet */
public HtmlPage css(String tag, String item, String val) {
return css(tag, item+":"+val);
}
/** adds a setting to the page's embedded stylesheet */
public HtmlPage css(String tag, String setting) {
cssSettings.put(tag, setting);
return this;
}
}

View File

@ -0,0 +1,50 @@
/*
* I2PHttpRequestHandler.java
*
* Created on April 8, 2005, 11:57 PM
*/
package net.i2p.aum.http;
import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;
import java.net.*;
import net.i2p.data.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
/**
*
* @author david
*/
public abstract class I2PHttpRequestHandler extends MiniHttpRequestHandler
{
/** Creates a new instance of I2PHttpRequestHandler */
public I2PHttpRequestHandler(MiniHttpServer server, Object sock, Object arg)
throws Exception
{
super(server, sock, arg);
}
/** Extracts a readable InputStream from own socket */
public InputStream getInputStream() throws IOException {
try {
return ((I2PSocket)socket).getInputStream();
} catch (Exception e) {
return ((Socket)socket).getInputStream();
}
}
/** Extracts a writeable OutputStream from own socket */
public OutputStream getOutputStream() throws IOException {
try {
return ((I2PSocket)socket).getOutputStream();
} catch (Exception e) {
return ((Socket)socket).getOutputStream();
}
}
}

View File

@ -0,0 +1,119 @@
/*
* I2PHttpServer.java
*
* Created on April 8, 2005, 11:39 PM
*/
package net.i2p.aum.http;
import java.io.*;
import java.util.*;
import net.i2p.*;
import net.i2p.data.*;
import net.i2p.client.*;
import net.i2p.client.streaming.*;
import net.i2p.aum.*;
/**
*
* @author david
*/
public class I2PHttpServer extends MiniHttpServer {
PrivDestination privKey;
I2PSocketManager socketMgr;
public I2PHttpServer(PrivDestination key)
throws DataFormatException, IOException, I2PException
{
this(key, I2PHttpRequestHandler.class, null, null);
}
public I2PHttpServer(PrivDestination key, Class hdlrClass)
throws DataFormatException, IOException, I2PException
{
this(key, hdlrClass, null, null);
}
public I2PHttpServer(PrivDestination key, Class hdlrClass, Properties props)
throws DataFormatException, IOException, I2PException
{
this(key, hdlrClass, null, props);
}
/** Creates a new instance of I2PHttpServer */
public I2PHttpServer(PrivDestination key, Class hdlrClass, Object hdlrArg, Properties props)
throws DataFormatException, IOException, I2PException
{
super(hdlrClass, hdlrArg);
if (key != null) {
privKey = key;
} else {
privKey = new PrivDestination();
}
// get a socket manager
// socketManager = I2PSocketManagerFactory.createManager(key);
if (props == null) {
socketMgr = I2PSocketManagerFactory.createManager(privKey.getInputStream());
} else {
socketMgr = I2PSocketManagerFactory.createManager(privKey.getInputStream(), props);
}
if (socketMgr == null) {
throw new I2PException("I2PHttpServer: Failed to create socketManager");
}
String d = privKey.getDestination().toBase64();
System.out.println("Server: getting server socket for dest "+d);
// get a server socket
//serverSocket = socketManager.getServerSocket();
}
public void getServerSocket() throws IOException {
I2PServerSocket sock;
sock = socketMgr.getServerSocket();
serverSocket = sock;
System.out.println("listening on dest: "+privKey.getDestination().toBase64());
}
/**
* Listens on our 'serverSocket' object for an incoming connection,
* and returns a connected socket object. You should override this
* if you're using non-standard socket objects
*/
public Object acceptConnection() throws IOException {
I2PSocket sock;
try {
sock = ((I2PServerSocket)serverSocket).accept();
} catch (I2PException e) {
throw new IOException(e.toString());
}
System.out.println("Got connection from: "+sock.getPeerDestination().toBase64());
//System.out.println("New connection accepted" +
// sock.getInetAddress() +
// ":" + sock.getPort());
return sock;
}
public static void main(String [] args) {
try {
System.out.println("I2PHttpServer: starting up with new random key");
I2PHttpServer server = new I2PHttpServer((PrivDestination)null);
System.out.println("I2PHttpServer: running server");
server.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}

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