Compare commits

...

90 Commits

Author SHA1 Message Date
6019a03029 * 2005-10-01 0.6.1.1 released 2005-10-01 19:20:09 +00:00
df5736f571 * Add a notModified flag to Eepget and Eepget status listeners. 2005-10-01 00:57:32 +00:00
9a73c6defe * Support conditional get for remote archive imports. 2005-09-30 23:42:28 +00:00
9dfa87ba47 2005-09-30 jrandom
* Killed three more streaming lib bugs, one of which caused excess packets
      to be transmitted (dupacking dupacks), one that was the root of many of
      the old hung streams (shrinking highest received), and another that was
      releasing data too soon.
2005-09-30 23:12:57 +00:00
934a269753 2005-09-30 jrandom
* Only allow autodetection of our IP address if we haven't received an
      inbound connection in the last two minutes.
    * Increase the default max streaming resends to 8 from 5 (and down from
      the earlier 10)
2005-09-30 20:29:19 +00:00
1c0dfc242b * Fix history.txt formatting. 2005-09-30 17:51:32 +00:00
3bc3e5d47e * Export petnames from syndie to the router's petname db instead of userhosts.txt. 2005-09-30 07:32:46 +00:00
55869af2cc 2005-09-29 jrandom
* Support noreseed.i2p in addition to .i2pnoreseed for disabling automatic
      reseeding - useful on OSes that make it hard to create dot files.
      Thanks Complication (and anon)!
    * Fixed the installer version string (thanks Frontier!)
    * Added cleaner rejection of invalid IP addresses, shitlist those who send
      us invalid IP addresses, verify again that we are not sending invalid IP
      addresses, and log an error if it happens. (Thanks Complication, ptm,
      and adab!)
2005-09-30 07:17:56 +00:00
9f336dd05b Provide a store method on PetNameDB that takes no arguments, and writes the db back to where it was loaded from. 2005-09-30 05:28:31 +00:00
411ca5e6c3 Ignore case when checking network name. 2005-09-30 05:20:41 +00:00
c528e4db03 * 2005-09-29 0.6.1 released
2005-09-29  jrandom
    * Let syndie users modify their metadata.
    * Reseed the router on startup if there aren't enough peer references
      known locally.  This can be disabled by creating the file .i2pnoreseed
      in your home directory, and the existing detection and reseed handling
      on the web interface is unchanged.
2005-09-29 19:24:43 +00:00
848ead7683 * 2005-09-29 0.6.1 released
2005-09-29  jrandom
    * Let syndie users modify their metadata.
    * Reseed the router on startup if there aren't enough peer references
      known locally.  This can be disabled by creating the file .i2pnoreseed
      in your home directory, and the existing detection and reseed handling
      on the web interface is unchanged.
2005-09-29 19:19:22 +00:00
1b8419b9b5 added tracker-fr.i2p 2005-09-29 03:54:30 +00:00
900420719e 2005-09-28 jrandom
* Fix for at least some (all?) of the wrong stream errors in the streaming
      lib
2005-09-28 09:17:54 +00:00
ef7d1ba964 2005-09-27 jrandom
* Properly suggest filenames for attachments in Syndie (thanks all!)
    * Fixed the Syndie authorization scheme for single user vs. multiuser
2005-09-27 22:42:49 +00:00
ab1654c784 ; (1.181) added syncline.i2p, cerebrum.i2p, news.underscore.i2p,
;               onionforum.i2p, frostmirror.i2p, ptm.i2p, gloinsblog.i2p
;               underscore.i2p, mac7.i2p, wiht.i2p, jazzy.i2p, trwcln.i2p
2005-09-27 22:21:05 +00:00
24bad8e4bb 2005-09-26 jrandom
* I2PTunnel bugfix (thanks Complication!)
    * Increase the SSU cwin slower during congestion avoidance (at k/cwin^2
      instead of k/cwin)
    * Limit the number of inbound SSU sessions being built at once (using
      half of the i2np.udp.maxConcurrentEstablish config prop)
    * Don't shitlist on a message send failure alone (unless there aren't any
      common transports).
    * More careful bandwidth bursting
2005-09-27 07:17:40 +00:00
f6d8200bc8 oops (thanks Complication!) 2005-09-27 00:56:49 +00:00
aef33548b3 2005-09-26 jrandom
* Reworded the SSU introductions config section (thanks duck!)
    * Force identity content encoding for I2PTunnel httpserver requests
      (thanks redzara!)
    * Further x-i2p-gzip bugfixes for the end of streams
    * Reduce the minimum bandwidth limits to 3KBps steady and burst (though
      I2P's performance at 3KBps is another issue)
    * Cleaned up some streaming lib structures
2005-09-26 23:45:52 +00:00
56ecdcce82 2005-09-25 jrandom
* Allow reseeding on the console if the netDb knows less than 30 peers,
      rather than less than 10 (without internet connectivity, we keep the
      last 15 router references)
    * Reenable the x-i2p-gzip HTTP processing by default, flushing the stream
      more aggressively.
    * Show the status that used to be called "ERR-Reject" as "OK (NAT)"
    * Reduced the default maximum number of streaming lib resends of a packet
      (10 retransmits is a bit much with a reasonable RTO)
2005-09-25 23:52:58 +00:00
b9b59ff95f 2005-09-25 Complication
* Better i2paddresshelper handling in the I2PTunnel httpclient, plus a new
      conflict resolution page if the i2paddresshelper parameter differs from
      an existing name to destination mapping.
2005-09-25  jrandom
    * Fix a long standing streaming lib bug (in the inactivity detection code)
    * Improved handling of initial streaming lib packet retransmissions to
      kill the "lost first packet" bug (where a page shows up with the first
      few KB missing)
    * Add support for initial window sizes greater than 1 - useful for
      eepsites to transmit e.g. 4 packets full of data along with the initial
      ACK, thereby cutting down on the rtt latency.  The congestion window
      size can and does still shrink down to 1 packet though.
    * Adjusted the streaming lib retransmission calculation algorithm to be
      more TCP-like.
2005-09-25 09:28:59 +00:00
aa9dd3e5c6 mention syndie bug stuff (good idea jnymo) 2005-09-24 04:08:42 +00:00
30bd659149 2005-09-21 redzara
* Use ISO-8859-1 for the susidns xml
2005-09-21 23:01:00 +00:00
3286ca49c8 2005-09-21 susi
* Bugfix in susidns for deleting entries
2005-09-21  jrandom
    * Add support for HTTP POST to EepGet
    * Use HTTP POST for syndie bulk fetches, since there's a lot of data to
      put in that URL.
2005-09-21 06:43:04 +00:00
7700d12178 damn thee, syntax 2005-09-20 03:43:57 +00:00
557b7e3f2e only build exe files on ant dist or ant installer 2005-09-20 03:38:14 +00:00
3e1e9146e1 don't build the exe files on x86_64 or osx 2005-09-20 03:17:06 +00:00
40d8d1aac1 * Made MetaNamingService the default naming service. 2005-09-19 00:56:47 +00:00
1457b8efba include the lib64 wrapper (thanks mule) 2005-09-19 00:36:59 +00:00
3821e80ac8 2005-09-18 jrandom
* Added support for pure 64bit linux with jbigi and the java service
      wrapper (no need for jcpuid if we're on os.arch=amd64).  Thanks mule
      et al for help testing!
    * UI cleanup in Syndie (thanks gloin and bar!)
2005-09-18 23:08:16 +00:00
d40bb459ea * Get the PetNameDB for the PetNameNamingService from the router context. 2005-09-18 22:36:10 +00:00
edf04f07c9 * Implemented a MetaNamingService. 2005-09-18 08:50:56 +00:00
2bdea23986 * Updated history.txt. 2005-09-18 05:41:46 +00:00
6be0c4b694 * Moved PetName and PetNameDB to core.
* Implement PetNameNamingService.
2005-09-18 05:28:51 +00:00
2a272f465c * 2005-09-17 0.6.0.6 released
2005-09-17  jrandom
    * Clean up syndie a bit more and bundle a default introductory post with
      both new installs and updates.
    * Typo fixes on the console (thanks bar!)
2005-09-18 01:29:58 +00:00
a8ecd32b45 2005-09-17 jrandom
* Updated the bandwidth limiter to use two tiers of bandwidth - our normal
      steady state rate, plus a new limit on how fast we transfer when
      bursting.  This is different from the old "burst as fast as possible
      until we're out of tokens" policy, and should help those with congested
      networks.  See /config.jsp to manage this rate.
    * Bugfixes in Syndie to handle missing cache files (no data was lost, the
      old posts just didn't show up).
    * Log properly in EepPost
2005-09-17 23:01:44 +00:00
20c42a175d 2005-09-17 jrandom
* Bugfixes in Syndie to handle missing cache files (no data was lost, the
      old posts just didn't show up).
    * Log properly in EepPost
2005-09-17 20:08:25 +00:00
d6c3ffde87 2005-09-17 jrandom
* Added the natively compiled jbigi and patched java service wrapper for
      OS X.  Thanks Bill Dorsey for letting me use your machine!
    * Don't build i2p.exe or i2pinstall.exe when run on OS X machines, as we
      don't bundle the binutils necessary (and there'd be a naming conflict
      if we did).
    * Added 'single user' functionality to syndie - if the single user
      checkbox on the admin page is checked, all users are allowed to control
      the instance and sync up with remote syndie nodes.
    * Temporarily disable the x-i2p-gzip in i2ptunnel until it is more closely
      debugged.
2005-09-17 07:31:48 +00:00
177e0ae6a3 2005-09-16 jrandom
* Reject unroutable IPs in SSU like we do for the TCP transport (unless
      you have i2np.udp.allowLocal=true defined - useful for private nets)
2005-09-16 21:24:42 +00:00
dab1b4d256 2005-09-16 jrandom
* Adjust I2PTunnelHTTPServer so it can be used for outproxy operators
      (just specify the spoofed host as an empty string), allowing them to
      honor x-i2p-gzip encoding.
    * Let windows users build the exes too (thanks bar and redzara!)
    * Allow I2PTunnel httpserver operators to disable gzip compression on
      individual tunnels with the i2ptunnel.gzip=false client option
      (good idea susi!)
2005-09-16 18:28:26 +00:00
3aba12631b use the logger, not stdout/stderr 2005-09-16 06:58:55 +00:00
cfee6430d4 xml 2005-09-16 04:54:48 +00:00
6b96df1cec runplain.sh, not startRouter.sh 2005-09-16 04:50:28 +00:00
deecfa5047 no message 2005-09-16 04:40:07 +00:00
6ca3f01038 launch4j 2005-09-16 04:34:59 +00:00
d89f589f2b 2005-09-16 jrandom
* Added the i2p.exe and i2pinstall.exe for windows users, using launch4j.
    * Added runplain.sh for *nix/osx users having problems using the java
      service wrapper (called from the install dir as: sh runplain.sh)
    * Bundle susidns and syndie, with links on the top nav
    * Have I2PTunnelHTTPClient and I2PTunnelHTTPServer use the x-i2p-gzip
      content-encoding (if offered), reducing the payload size before it
      reaches the streaming lib.  The existing compression is at the i2cp
      level, so we've been packetizing 4KB of uncompressed data and then
      compressing those messages, rather than compressing and then packetizing
      4KB of compressed data.  This should reduce the number of round trips
      to fetch web pages substantially.
    * Adjust the startup and timing of the addressbook so that susidns always
      has config to work off, and expose a method for susidns to tell it to
      reload its config and rerun.
2005-09-16 04:12:24 +00:00
8c1895e04f imported fixed susidns 2005-09-16 04:04:40 +00:00
c3d0132a98 test cvs again... 2005-09-15 05:57:28 +00:00
d955279d17 minor news (and test cvs...) 2005-09-15 05:56:10 +00:00
76266dce0d 2005-09-15 jrandom
* Error handling for failed intro packets (thanks red.hand!)
    * More carefully verify intro addresses
2005-09-15 05:39:31 +00:00
5694206b35 2005-09-13 jrandom
* More careful error handling with introductions (thanks dust!)
    * Fix the forceIntroducers checkbox on config.jsp (thanks Complication!)
    * Hide the shitlist on the summary so it doesn't confuse new users.
2005-09-13 23:02:35 +00:00
4293a18726 2005-09-12 comwiz
* Migrated the router tests to junit
2005-09-13 09:06:07 +00:00
9865af4174 2005-09-12 jrandom
* Removed guaranteed delivery mode entirely (so existing i2phex clients
      using it can get the benefits of mode=best_effort).  Guaranteed delivery
      is offered at the streaming lib level.
    * Improve the peer selection code for peer testing, as everyone now
      supports tests.
    * Give the watchdog its fangs - if it detects obscene job lag or if
      clients have been unable to get a leaseSet for more than 5 minutes,
      restart the router.  This was disabled a year ago due to spurious
      restarts, and can be disabled by "watchdog.haltOnHang=false", but the
      cause of the spurious restarts should be gone.
2005-09-13 03:32:29 +00:00
c8c109093d 2005-09-12 jrandom
* Bugfix for skewed store which could kill a UDP thread (causing complete
      comm failure and eventual OOM)
2005-09-13 01:12:43 +00:00
b5784d6025 2005-09-12 jrandom
* More aggressively publish updated routerInfo.
    * Expose the flag to force SSU introductions on the router console
    * Don't give people the option to disable SNTP time sync, at least not
      through the router console, because there is no reason to disable it.
      No, not even if your OS is "ntp synced", because chances are, its not.
2005-09-13 00:11:56 +00:00
31bdb8909a tino.i2p and fproxy.tino.i2p 2005-09-12 22:40:17 +00:00
ee921c22ae use the low level rates (thanks bar / complication) 2005-09-12 02:58:13 +00:00
172ffd0434 use the OS time, since it doesn't skew as much (especially on startup) 2005-09-11 04:37:15 +00:00
d9b4406c09 2005-09-10 jrandom
* Test the router's reachability earlier and more aggressively
    * Use the low level bandwidth limiter's rates for the router console, and
      if the router has net.i2p.router.transport.FIFOBandwidthLimiter=INFO in
      the logger config, keep track of the 1 second transfer rates as the stat
      'bw.sendBps1s' and 'bw.recvBps1s', allowing closer monitoring of burst
      behavior.
2005-09-11 03:22:51 +00:00
8ac0e85df4 updated to hq.postman.i2p 2005-09-11 01:40:15 +00:00
249ccd5e3c now that its all implemented... 2005-09-10 23:18:41 +00:00
727d76d43e deal with posts containing no tags by using the implicit tag "[none]" (thanks ardvark!) 2005-09-10 06:07:25 +00:00
44770b7c07 2005-09-09 jrandom
* Added preliminary support for NAT hole punching through SSU introducers
    * Honor peer test results from peers that we have an SSU session with if
      those sessions are idle for 3 minutes or more.
2005-09-10 04:30:36 +00:00
b5d571c75f 2005-09-09 cervantes
* New build due to change in build number :P (thanks ugha!)
2005-09-10 01:13:49 +00:00
da56d83716 First pass at a new naming system. Probably the last as well. So sad :). 2005-09-09 19:38:43 +00:00
f777e213ce search.i2p 2005-09-08 05:08:33 +00:00
79906f5a7d added search.i2p 2005-09-08 05:01:01 +00:00
54074e76b5 2005-09-07 BarkerJr
* HTML cleanup for the router console (thanks!)
2005-09-07  jrandom
    * Lay the foundation for 'client routers' - the ability for peers to opt
      out of participating in tunnels entirely due to firewall/NAT issues.
      Individual routers have control over where those peers are used in
      tunnels - in outbound or inbound, exploratory or client tunnels, or
      none at all.  The defaults with this build are to simply act as before -
      placing everyone as potential participants in any tunnel.
    * Another part of the foundation includes the option for netDb
      participants to refuse to answer queries regarding peers who are marked
      as unreachable, though this too is disabled by default (meaning the
      routerInfo is retrievable from the netDb).
2005-09-07 22:31:11 +00:00
c2ea8db683 Look for names in privatehosts.txt as well as userhosts.txt and hosts.txt. 2005-09-07 02:07:41 +00:00
744671a518 Adjusted wording on the bandwidth limiter controls to reflect new router defaults 2005-09-07 01:13:56 +00:00
7f5b127bbc fix0rz. 2005-09-06 20:45:21 +00:00
89eff0c628 tyop 2005-09-06 20:35:28 +00:00
177aeebb1c stuff 2005-09-06 20:15:55 +00:00
e0e6bde4a5 throw css around like mad (very minimal stylesheet in place) 2005-09-06 20:05:09 +00:00
e6b145716f allow publishing to a remote archive automatically when posting (optionally) with 0 additional clicks
allow transparently attaching any 'public' pet names in your addressbook to a blog post (with a checkbox)
2005-09-06 03:03:55 +00:00
f958342704 admin page - no more editing config props manually (w3wt) 2005-09-05 20:53:25 +00:00
5a1f738505 2005-09-05 jrandom
* Expose the HTTP headers to EepGet status listeners
    * Handle DSA key failures properly (if the signature is not invertable, it
      is obviously invalid)
2005-09-05 19:29:55 +00:00
8147cdf40c 2005-09-05 jrandom
* Expose the HTTP headers to EepGet status listeners
    * Handle DSA key failures properly (if the signature is not invertable, it
      is obviously invalid)
also, syndie now properly detects whether the remote archive can send a filtered export.zip
by examining the HTTP headers for X-Syndie-Export-Capable: true.  If the remote archive
does not set that header (and neither freesites, nor apache or anything other than the ArchiveServlet will),
it uses individual HTTP requests for individual blog posts and metadata fetches.
2005-09-05 19:27:08 +00:00
6afc64ac39 deal with locations that have : in them (aka http://glog.i2p/archive/archive.txt) 2005-09-05 17:09:19 +00:00
61b8e3598b added rss2.0 support via rss.jsp
rss.jsp can in turn receive all the filters that index.jsp can - e.g. ?blog=blah or ?selector=group://foo,
and by default returns the latest 10 values (overridden with ?wanted=15).  If you want it to pull
with a user's blog's preferences (filters, groups, etc), you can specify ?login=user&password=password
2005-09-05 05:33:33 +00:00
3bb445ff40 better filtering/ignoring
ui improvements (per isamoor's suggestions)
more petname integration
2005-09-05 01:26:19 +00:00
59a8037599 allow exporting eepsite destinations from the syndie database into userhosts.txt (so the eepproxy can get it) 2005-09-05 00:00:11 +00:00
09cb5fad59 allow you to bookmark syndie archives and later recall those bookmarks on the remote page 2005-09-04 22:43:22 +00:00
ee8e45ecf7 allow web based control of who gets to access remote repositories.
if the prop "syndie.remotePassword" is set, users can enter it while viewing their metadata
2005-09-04 21:51:17 +00:00
339868838d thanks BarkerJr :) 2005-09-04 20:26:42 +00:00
c5579fa349 (the filtered blogs may be out of order) 2005-09-04 19:33:00 +00:00
d4a859547c 2005-09-04 jrandom
* Don't persist peer profiles until we are shutting down, as the
      persistence process gobbles RAM and wall time.
    * Bugfix to allow you to check/uncheck the sharedClient setting on the
      I2PTunnel web interface.
    * Be more careful when expiring a failed tunnel message fragment so we
      don't drop the data while attempting to read it.
2005-09-04 19:15:49 +00:00
779aa240d2 added glog.i2p 2005-09-03 04:07:50 +00:00
9aaad00383 0.6.0.5 2005-09-02 19:10:05 +00:00
6422f7ef78 2005-09-02 jrandom
* Don't refuse to send a netDb store if the targetted peer has failed a
      bit (the value was an arbitrary amount).
    * Logging changes
2005-09-02 18:34:14 +00:00
338 changed files with 19035 additions and 2602 deletions

View File

@ -36,6 +36,7 @@ import java.io.File;
*/
public class Daemon {
public static final String VERSION = "2.0.3";
private static final Daemon _instance = new Daemon();
/**
* Update the router and published address books using remote data from the
@ -56,7 +57,7 @@ public class Daemon {
* @param log
* The log to write changes and conflicts to.
*/
public static void update(AddressBook master, AddressBook router,
public void update(AddressBook master, AddressBook router,
File published, SubscriptionList subscriptions, Log log) {
router.merge(master, true, null);
Iterator iter = subscriptions.iterator();
@ -77,7 +78,7 @@ public class Daemon {
* @param home
* The directory containing addressbook's configuration files.
*/
public static void update(Map settings, String home) {
public void update(Map settings, String home) {
File masterFile = new File(home, (String) settings
.get("master_addressbook"));
File routerFile = new File(home, (String) settings
@ -104,7 +105,7 @@ public class Daemon {
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
Log log = new Log(logFile);
Daemon.update(master, router, published, subscriptions, log);
update(master, router, published, subscriptions, log);
}
/**
@ -118,6 +119,10 @@ public class Daemon {
* others are ignored.
*/
public static void main(String[] args) {
_instance.run(args);
}
public void run(String[] args) {
String settingsLocation = "config.txt";
Map settings = new HashMap();
String home;
@ -151,19 +156,36 @@ public class Daemon {
File settingsFile = new File(homeFile, settingsLocation);
settings = ConfigParser.parse(settingsFile, defaultSettings);
// wait
try {
Thread.currentThread().sleep(5*60*1000);
} catch (InterruptedException ie) {}
while (true) {
settings = ConfigParser.parse(settingsFile, defaultSettings);
long delay = Long.parseLong((String) settings.get("update_delay"));
if (delay < 1) {
delay = 1;
}
Daemon.update(settings, home);
update(settings, home);
try {
Thread.sleep(delay * 60 * 60 * 1000);
synchronized (this) {
wait(delay * 60 * 60 * 1000);
}
} catch (InterruptedException exp) {
}
settings = ConfigParser.parse(settingsFile, defaultSettings);
}
}
/**
* Call this to get the addressbook to reread its config and
* refetch its subscriptions.
*/
public static void wakeup() {
synchronized (_instance) {
_instance.notifyAll();
}
}
}

View File

@ -44,10 +44,10 @@ public class DaemonThread extends Thread {
* @see java.lang.Runnable#run()
*/
public void run() {
try {
Thread.sleep(5 * 60 * 1000);
} catch (InterruptedException exp) {
}
//try {
// Thread.sleep(5 * 60 * 1000);
//} catch (InterruptedException exp) {
//}
Daemon.main(this.args);
}
}

View File

@ -8,36 +8,50 @@ package net.i2p.i2ptunnel;
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.FilterOutputStream;
import java.io.OutputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.util.ByteCache;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Simple stream for delivering an HTTP response to
* the client, trivially filtered to make sure "Connection: close"
* is always in the response.
* is always in the response. Perhaps add transparent handling of the
* Content-encoding: x-i2p-gzip, adjusting the headers to say Content-encoding: identity?
* Content-encoding: gzip is trivial as well, but Transfer-encoding: chunked makes it
* more work than is worthwhile at the moment.
*
*/
class HTTPResponseOutputStream extends FilterOutputStream {
private static final Log _log = new Log(HTTPResponseOutputStream.class);
private I2PAppContext _context;
private Log _log;
private ByteCache _cache;
protected ByteArray _headerBuffer;
private boolean _headerWritten;
private byte _buf1[];
protected boolean _gzip;
private long _dataWritten;
private InternalGZIPInputStream _in;
private static final int CACHE_SIZE = 8*1024;
public HTTPResponseOutputStream(OutputStream raw) {
super(raw);
_context = I2PAppContext.getGlobalContext();
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "i2ptunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "i2ptunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "i2ptunnel", new long[] { 60*1000, 30*60*1000 });
_log = _context.logManager().getLog(getClass());
_cache = ByteCache.getInstance(8, CACHE_SIZE);
_headerBuffer = _cache.acquire();
_headerWritten = false;
_gzip = false;
_dataWritten = 0;
_buf1 = new byte[1];
}
@ -51,6 +65,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
public void write(byte buf[], int off, int len) throws IOException {
if (_headerWritten) {
out.write(buf, off, len);
_dataWritten += len;
out.flush();
return;
}
@ -62,8 +78,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
if (headerReceived()) {
writeHeader();
_headerWritten = true;
if (i + 1 < len) // write out the remaining
if (i + 1 < len) {
// write out the remaining
out.write(buf, off+i+1, len-i-1);
_dataWritten += len-i-1;
out.flush();
}
return;
}
}
@ -128,7 +148,10 @@ class HTTPResponseOutputStream extends FilterOutputStream {
if ( (keyLen <= 0) || (valLen <= 0) )
throw new IOException("Invalid header @ " + j);
String key = new String(_headerBuffer.getData(), lastEnd+1, keyLen);
String val = new String(_headerBuffer.getData(), j+2, valLen);
String val = new String(_headerBuffer.getData(), j+2, valLen).trim();
if (_log.shouldLog(Log.INFO))
_log.info("Response header [" + key + "] = [" + val + "]");
if ("Connection".equalsIgnoreCase(key)) {
out.write("Connection: close\n".getBytes());
@ -136,6 +159,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
} else if ("Proxy-Connection".equalsIgnoreCase(key)) {
out.write("Proxy-Connection: close\n".getBytes());
proxyConnectionSent = true;
} else if ( ("Content-encoding".equalsIgnoreCase(key)) && ("x-i2p-gzip".equalsIgnoreCase(val)) ) {
_gzip = true;
} else {
out.write((key.trim() + ": " + val.trim() + "\n").getBytes());
}
@ -152,13 +177,103 @@ class HTTPResponseOutputStream extends FilterOutputStream {
if (!proxyConnectionSent)
out.write("Proxy-Connection: close\n".getBytes());
out.write("\n".getBytes()); // end of the headers
finishHeaders();
boolean shouldCompress = shouldCompress();
if (_log.shouldLog(Log.INFO))
_log.info("After headers: gzip? " + _gzip + " compress? " + shouldCompress);
// done, shove off
if (_headerBuffer.getData().length == CACHE_SIZE)
_cache.release(_headerBuffer);
else
_headerBuffer = null;
if (shouldCompress) {
beginProcessing();
}
}
protected boolean shouldCompress() { return _gzip; }
protected void finishHeaders() throws IOException {
out.write("\n".getBytes()); // end of the headers
}
protected void beginProcessing() throws IOException {
//out.flush();
PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);
new I2PThread(new Pusher(pi, out), "HTTP decompresser").start();
out = po;
}
private class Pusher implements Runnable {
private InputStream _inRaw;
private OutputStream _out;
public Pusher(InputStream in, OutputStream out) {
_inRaw = in;
_out = out;
}
public void run() {
OutputStream to = null;
_in = null;
long start = System.currentTimeMillis();
long written = 0;
try {
_in = new InternalGZIPInputStream(_inRaw);
byte buf[] = new byte[8192];
int read = -1;
while ( (read = _in.read(buf)) != -1) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read " + read + " and writing it to the browser/streams");
_out.write(buf, 0, read);
_out.flush();
written += read;
}
if (_log.shouldLog(Log.INFO))
_log.info("Decompressed: " + written + ", " + _in.getTotalRead() + "/" + _in.getTotalExpanded());
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
} finally {
if (_log.shouldLog(Log.WARN) && (_in != null))
_log.warn("After decompression, written=" + written +
(_in != null ?
" read=" + _in.getTotalRead()
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
+ ", finished=" + _in.getFinished()
: ""));
if (_out != null) try {
_out.close();
} catch (IOException ioe) {}
}
long end = System.currentTimeMillis();
double compressed = (_in != null ? _in.getTotalRead() : 0);
double expanded = (_in != null ? _in.getTotalExpanded() : 0);
double ratio = 0;
if (expanded > 0)
ratio = compressed/expanded;
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
}
}
private class InternalGZIPInputStream extends GZIPInputStream {
public InternalGZIPInputStream(InputStream in) throws IOException {
super(in);
}
public long getTotalRead() { return super.inf.getTotalIn(); }
public long getTotalExpanded() { return super.inf.getTotalOut(); }
public long getRemaining() { return super.inf.getRemaining(); }
public boolean getFinished() { return super.inf.finished(); }
public String toString() {
return "Read: " + getTotalRead() + " expanded: " + getTotalExpanded() + " remaining: " + getRemaining() + " finished: " + getFinished();
}
}
public String toString() {
return super.toString() + ": " + _in;
}
public static void main(String args[]) {

View File

@ -96,6 +96,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
.getBytes();
private final static byte[] ERR_AHELPER_CONFLICT =
("HTTP/1.1 409 Conflict\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"\r\n"+
"<html><body><H1>I2P ERROR: Destination key conflict</H1>"+
"The addresshelper link you followed specifies a different destination key "+
"than a host entry in your host database. "+
"Someone could be trying to impersonate another eepsite, "+
"or people have given two eepsites identical names.<P/>"+
"You can resolve the conflict by considering which key you trust, "+
"and either discarding the addresshelper link, "+
"discarding the host entry from your host database, "+
"or naming one of them differently.<P/>")
.getBytes();
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
@ -179,6 +195,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
return opts;
}
private static final boolean DEFAULT_GZIP = true;
private static long __requestId = 0;
protected void clientConnectionRun(Socket s) {
OutputStream out = null;
@ -243,50 +261,102 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
// Quick hack for foo.bar.i2p
if (host.toLowerCase().endsWith(".i2p")) {
// Destination gets the host name
destination = host;
// Host becomes the destination key
host = getHostName(destination);
if ( (host != null) && ("i2p".equals(host)) ) {
int pos2;
if ((pos2 = request.indexOf("?")) != -1) {
// Try to find an address helper in the fragments
// and split the request into it's component parts for rebuilding later
String fragments = request.substring(pos2 + 1);
String uriPath = request.substring(0, pos2);
pos2 = fragments.indexOf(" ");
String protocolVersion = fragments.substring(pos2 + 1);
String urlEncoding = "";
fragments = fragments.substring(0, pos2);
fragments = fragments + "&";
String fragment;
while(fragments.length() > 0) {
pos2 = fragments.indexOf("&");
fragment = fragments.substring(0, pos2);
fragments = fragments.substring(pos2 + 1);
if (fragment.startsWith("i2paddresshelper")) {
pos2 = fragment.indexOf("=");
if (pos2 >= 0) {
addressHelpers.put(destination,fragment.substring(pos2 + 1));
}
} else {
// append each fragment unless it's the address helper
if ("".equals(urlEncoding)) {
urlEncoding = "?" + fragment;
} else {
urlEncoding = urlEncoding + "&" + fragment;
}
}
}
// reconstruct the request minus the i2paddresshelper GET var
request = uriPath + urlEncoding + " " + protocolVersion;
}
String addressHelper = (String) addressHelpers.get(destination);
if (addressHelper != null) {
destination = addressHelper;
host = getHostName(destination);
ahelper = 1;
int pos2;
if ((pos2 = request.indexOf("?")) != -1) {
// Try to find an address helper in the fragments
// and split the request into it's component parts for rebuilding later
String ahelperKey = null;
boolean ahelperConflict = false;
String fragments = request.substring(pos2 + 1);
String uriPath = request.substring(0, pos2);
pos2 = fragments.indexOf(" ");
String protocolVersion = fragments.substring(pos2 + 1);
String urlEncoding = "";
fragments = fragments.substring(0, pos2);
String initialFragments = fragments;
fragments = fragments + "&";
String fragment;
while(fragments.length() > 0) {
pos2 = fragments.indexOf("&");
fragment = fragments.substring(0, pos2);
fragments = fragments.substring(pos2 + 1);
// Fragment looks like addresshelper key
if (fragment.startsWith("i2paddresshelper=")) {
pos2 = fragment.indexOf("=");
ahelperKey = fragment.substring(pos2 + 1);
// Key contains data, lets not ignore it
if (ahelperKey != null) {
// Host resolvable only with addresshelper
if ( (host == null) || ("i2p".equals(host)) )
{
// Cannot check, use addresshelper key
addressHelpers.put(destination,ahelperKey);
} else {
// Host resolvable from database, verify addresshelper key
// Silently bypass correct keys, otherwise alert
if (!host.equals(ahelperKey))
{
// Conflict: handle when URL reconstruction done
ahelperConflict = true;
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + host + "], specified key [" + ahelperKey + "].");
}
}
}
} else {
// Other fragments, just pass along
// Append each fragment to urlEncoding
if ("".equals(urlEncoding)) {
urlEncoding = "?" + fragment;
} else {
urlEncoding = urlEncoding + "&" + fragment;
}
}
}
// Reconstruct the request minus the i2paddresshelper GET var
request = uriPath + urlEncoding + " " + protocolVersion;
// Did addresshelper key conflict?
if (ahelperConflict)
{
String str;
byte[] header;
str = FileUtil.readTextFile("docs/ahelper-conflict-header.ht", 100, true);
if (str != null) header = str.getBytes();
else header = ERR_AHELPER_CONFLICT;
if (out != null) {
long alias = I2PAppContext.getGlobalContext().random().nextLong();
String trustedURL = protocol + uriPath + urlEncoding;
String conflictURL = protocol + alias + ".i2p/?" + initialFragments;
out.write(header);
out.write(("To visit the destination in your host database, click <a href=\"" + trustedURL + "\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"" + conflictURL + "\">here</a>.<P/>").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();
}
s.close();
return;
}
}
String addressHelper = (String) addressHelpers.get(destination);
if (addressHelper != null) {
destination = addressHelper;
host = getHostName(destination);
ahelper = 1;
}
line = method + " " + request.substring(pos);
} else if (host.indexOf(".") != -1) {
// The request must be forwarded to a WWW proxy
@ -369,6 +439,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
if (line.length() == 0) {
String ok = getTunnel().getContext().getProperty("i2ptunnel.gzip");
boolean gzip = DEFAULT_GZIP;
if (ok != null)
gzip = Boolean.valueOf(ok).booleanValue();
if (gzip)
newRequest.append("Accept-Encoding: x-i2p-gzip\r\n");
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
newRequest.append("Connection: close\r\n\r\n");
break;

View File

@ -3,16 +3,13 @@
*/
package net.i2p.i2ptunnel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FilterOutputStream;
import java.io.*;
import java.net.Socket;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
@ -30,12 +27,38 @@ import net.i2p.util.Log;
*
*/
public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner {
private Log _log;
public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
super(s, i2ps, slock, initialI2PData, sockList, onTimeout);
_log = I2PAppContext.getGlobalContext().logManager().getLog(I2PTunnelHTTPClientRunner.class);
}
protected OutputStream getSocketOut() throws IOException {
OutputStream raw = super.getSocketOut();
return new HTTPResponseOutputStream(raw);
}
protected void close(OutputStream out, InputStream in, OutputStream i2pout, InputStream i2pin, Socket s, I2PSocket i2ps, Thread t1, Thread t2) throws InterruptedException, IOException {
try {
i2pin.close();
i2pout.close();
} catch (IOException ioe) {
// ignore
if (_log.shouldLog(Log.DEBUG))
_log.debug("Unable to close the i2p socket output stream: " + i2pout, ioe);
}
try {
in.close();
out.close();
} catch (IOException ioe) {
// ignore
if (_log.shouldLog(Log.DEBUG))
_log.debug("Unable to close the browser output stream: " + out, ioe);
}
i2ps.close();
s.close();
t1.join(30*1000);
t2.join(30*1000);
}
}

View File

@ -3,14 +3,13 @@
*/
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Iterator;
import java.util.Properties;
import java.util.zip.GZIPOutputStream;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -24,7 +23,9 @@ import net.i2p.util.Log;
/**
* Simple extension to the I2PTunnelServer that filters the HTTP
* headers sent from the client to the server, replacing the Host
* header with whatever this instance has been configured with.
* header with whatever this instance has been configured with, and
* if the browser set Accept-encoding: x-i2p-gzip, gzip the http
* message body and set Content-encoding: x-i2p-gzip.
*
*/
public class I2PTunnelHTTPServer extends I2PTunnelServer {
@ -62,14 +63,46 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
try {
// give them 5 seconds to send in the HTTP request
socket.setReadTimeout(5*1000);
String modifiedHeader = getModifiedHeader(socket);
InputStream in = socket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
if ( (_spoofHost != null) && (_spoofHost.trim().length() > 0) )
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
// we keep the enc sent by the browser before clobbering it, since it may have
// been x-i2p-gzip
String enc = headers.getProperty("Accept-encoding");
headers.setProperty("Accept-encoding", "identity;q=1, *;q=0");
String modifiedHeader = formatHeaders(headers, command);
//String modifiedHeader = getModifiedHeader(socket);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
socket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = getTunnel().getContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
// instead of i2ptunnelrunner, use something that reads the HTTP
// request from the socket, modifies the headers, sends the request to the
// server, reads the response headers, rewriting to include Content-encoding: x-i2p-gzip
// if it was one of the Accept-encoding: values, and gzip the payload
Properties opts = getTunnel().getClientOptions();
boolean allowGZIP = true;
if (opts != null) {
String val = opts.getProperty("i2ptunnel.gzip");
if ( (val != null) && (!Boolean.valueOf(val).booleanValue()) )
allowGZIP = false;
}
if (_log.shouldLog(Log.INFO))
_log.info("HTTP server encoding header: " + enc);
if ( allowGZIP && (enc != null) && (enc.indexOf("x-i2p-gzip") >= 0) ) {
I2PThread req = new I2PThread(new CompressedRequestor(s, socket, modifiedHeader), "http compressor");
req.start();
} else {
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
}
} catch (SocketException ex) {
try {
socket.close();
@ -88,17 +121,119 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
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 class CompressedRequestor implements Runnable {
private Socket _webserver;
private I2PSocket _browser;
private String _headers;
public CompressedRequestor(Socket webserver, I2PSocket browser, String headers) {
_webserver = webserver;
_browser = browser;
_headers = headers;
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info("Compressed requestor running");
OutputStream serverout = null;
OutputStream browserout = null;
InputStream browserin = null;
InputStream serverin = null;
try {
serverout = _webserver.getOutputStream();
if (_log.shouldLog(Log.INFO))
_log.info("request headers: " + _headers);
serverout.write(_headers.getBytes());
browserin = _browser.getInputStream();
I2PThread sender = new I2PThread(new Sender(serverout, browserin, "server: browser to server"), "http compressed sender");
sender.start();
browserout = _browser.getOutputStream();
serverin = _webserver.getInputStream();
CompressedResponseOutputStream compressedOut = new CompressedResponseOutputStream(browserout);
Sender s = new Sender(compressedOut, serverin, "server: server to browser");
if (_log.shouldLog(Log.INFO))
_log.info("Before pumping the compressed response");
s.run(); // same thread
if (_log.shouldLog(Log.INFO))
_log.info("After pumping the compressed response: " + compressedOut.getTotalRead() + "/" + compressedOut.getTotalCompressed());
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("error compressing", ioe);
} finally {
if (browserout != null) try { browserout.close(); } catch (IOException ioe) {}
if (serverout != null) try { serverout.close(); } catch (IOException ioe) {}
if (browserin != null) try { browserin.close(); } catch (IOException ioe) {}
if (serverin != null) try { serverin.close(); } catch (IOException ioe) {}
}
}
}
private class Sender implements Runnable {
private OutputStream _out;
private InputStream _in;
private String _name;
public Sender(OutputStream out, InputStream in, String name) {
_out = out;
_in = in;
_name = name;
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": Begin sending");
try {
byte buf[] = new byte[16*1024];
int read = 0;
int total = 0;
while ( (read = _in.read(buf)) != -1) {
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": read " + read + " and sending through the stream");
_out.write(buf, 0, read);
total += read;
}
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": Done sending: " + total);
_out.flush();
} catch (IOException ioe) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Error sending", ioe);
} finally {
if (_in != null) try { _in.close(); } catch (IOException ioe) {}
if (_out != null) try { _out.close(); } catch (IOException ioe) {}
}
}
}
private class CompressedResponseOutputStream extends HTTPResponseOutputStream {
private InternalGZIPOutputStream _gzipOut;
public CompressedResponseOutputStream(OutputStream o) {
super(o);
}
protected boolean shouldCompress() { return true; }
protected void finishHeaders() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Including x-i2p-gzip as the content encoding in the response");
out.write("Content-encoding: x-i2p-gzip\n".getBytes());
super.finishHeaders();
}
protected void beginProcessing() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Beginning compression processing");
out.flush();
_gzipOut = new InternalGZIPOutputStream(out);
out = _gzipOut;
}
public long getTotalRead() { return _gzipOut.getTotalRead(); }
public long getTotalCompressed() { return _gzipOut.getTotalCompressed(); }
}
private class InternalGZIPOutputStream extends GZIPOutputStream {
public InternalGZIPOutputStream(OutputStream target) throws IOException {
super(target);
}
public long getTotalRead() { return super.def.getTotalIn(); }
public long getTotalCompressed() { return super.def.getTotalOut(); }
}
private String formatHeaders(Properties headers, StringBuffer command) {
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
buf.append(command.toString()).append('\n');
@ -133,6 +268,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
String name = buf.substring(0, split);
String value = buf.substring(split+2); // ": "
if ("Accept-encoding".equalsIgnoreCase(name))
name = "Accept-encoding";
headers.setProperty(name, value);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the header [" + name + "] = [" + value + "]");

View File

@ -153,11 +153,8 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
onTimeout.run();
}
// now one connection is dead - kill the other as well.
s.close();
i2ps.close();
t1.join(30*1000);
t2.join(30*1000);
// now one connection is dead - kill the other as well, after making sure we flush
close(out, in, i2pout, i2pin, s, i2ps, t1, t2);
} catch (InterruptedException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Interrupted", ex);
@ -188,6 +185,27 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
}
}
protected void close(OutputStream out, InputStream in, OutputStream i2pout, InputStream i2pin, Socket s, I2PSocket i2ps, Thread t1, Thread t2) throws InterruptedException, IOException {
try {
out.flush();
} catch (IOException ioe) {
// ignore
}
try {
i2pout.flush();
} catch (IOException ioe) {
// ignore
}
in.close();
i2pin.close();
// ok, yeah, there's a race here in theory, if data comes in after flushing and before
// closing, but its better than before...
s.close();
i2ps.close();
t1.join(30*1000);
t2.join(30*1000);
}
public void errorOccurred() {
synchronized (finishLock) {
finished = true;

View File

@ -479,9 +479,12 @@ public class IndexBean {
public void setStartOnLoad(String moo) {
_startOnLoad = true;
}
public void setSharedClient(String moo) {
public void setShared(String moo) {
_sharedClient=true;
}
public void setShared(boolean val) {
_sharedClient=val;
}
public void setConnectDelay(String moo) {
_connectDelay = true;
}

View File

@ -166,9 +166,9 @@ if (curTunnel >= 0) {
</td>
<td>
<% if (editBean.isSharedClient(curTunnel)) { %>
<input type="checkbox" value="true" name="sharedClient" checked="true" />
<input type="checkbox" value="true" name="shared" checked="true" />
<% } else { %>
<input type="checkbox" value="true" name="sharedClient" />
<input type="checkbox" value="true" name="shared" />
<% } %>
<i>(Share tunnels with other clients and httpclients? Change requires restart of client proxy)</i>
</td>

View File

@ -16,6 +16,7 @@ import java.util.Iterator;
import java.util.Set;
import net.i2p.time.Timestamper;
import net.i2p.router.transport.udp.UDPTransport;
/**
* Handler to deal with form submissions from the main config form and act
@ -29,11 +30,14 @@ public class ConfigNetHandler extends FormHandler {
private boolean _saveRequested;
private boolean _recheckReachabilityRequested;
private boolean _timeSyncEnabled;
private boolean _requireIntroductions;
private String _tcpPort;
private String _udpPort;
private String _inboundRate;
private String _inboundBurstRate;
private String _inboundBurst;
private String _outboundRate;
private String _outboundBurstRate;
private String _outboundBurst;
private String _reseedFrom;
private String _sharePct;
@ -57,6 +61,7 @@ public class ConfigNetHandler extends FormHandler {
public void setSave(String moo) { _saveRequested = true; }
public void setEnabletimesync(String moo) { _timeSyncEnabled = true; }
public void setRecheckReachability(String moo) { _recheckReachabilityRequested = true; }
public void setRequireIntroductions(String moo) { _requireIntroductions = true; }
public void setHostname(String hostname) {
_hostname = (hostname != null ? hostname.trim() : null);
@ -70,12 +75,18 @@ public class ConfigNetHandler extends FormHandler {
public void setInboundrate(String rate) {
_inboundRate = (rate != null ? rate.trim() : null);
}
public void setInboundburstrate(String rate) {
_inboundBurstRate = (rate != null ? rate.trim() : null);
}
public void setInboundburstfactor(String factor) {
_inboundBurst = (factor != null ? factor.trim() : null);
}
public void setOutboundrate(String rate) {
_outboundRate = (rate != null ? rate.trim() : null);
}
public void setOutboundburstrate(String rate) {
_outboundBurstRate = (rate != null ? rate.trim() : null);
}
public void setOutboundburstfactor(String factor) {
_outboundBurst = (factor != null ? factor.trim() : null);
}
@ -253,7 +264,14 @@ public class ConfigNetHandler extends FormHandler {
}
}
if (_timeSyncEnabled) {
if (_requireIntroductions) {
_context.router().setConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS, "true");
addFormNotice("Requiring SSU introduers");
} else {
_context.router().removeConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS);
}
if (true || _timeSyncEnabled) {
// Time sync enable, means NOT disabled
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
} else {
@ -283,14 +301,22 @@ public class ConfigNetHandler extends FormHandler {
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS, _outboundRate);
updated = true;
}
if ( (_inboundBurstRate != null) && (_inboundBurstRate.length() > 0) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST_KBPS, _inboundBurstRate);
updated = true;
}
if ( (_outboundBurstRate != null) && (_outboundBurstRate.length() > 0) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST_KBPS, _outboundBurstRate);
updated = true;
}
String inRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS);
String inBurstRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST_KBPS);
if (_inboundBurst != null) {
int rateKBps = 0;
int burstSeconds = 0;
try {
rateKBps = Integer.parseInt(inRate);
rateKBps = Integer.parseInt(inBurstRate);
burstSeconds = Integer.parseInt(_inboundBurst);
} catch (NumberFormatException nfe) {
// ignore
@ -302,13 +328,13 @@ public class ConfigNetHandler extends FormHandler {
}
}
String outRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS);
String outBurstRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST_KBPS);
if (_outboundBurst != null) {
int rateKBps = 0;
int burstSeconds = 0;
try {
rateKBps = Integer.parseInt(outRate);
rateKBps = Integer.parseInt(outBurstRate);
burstSeconds = Integer.parseInt(_outboundBurst);
} catch (NumberFormatException nfe) {
// ignore

View File

@ -2,6 +2,10 @@ package net.i2p.router.web;
import net.i2p.time.Timestamper;
import net.i2p.router.RouterContext;
import net.i2p.router.CommSystemFacade;
import net.i2p.data.RouterAddress;
import net.i2p.router.transport.udp.UDPAddress;
import net.i2p.router.transport.udp.UDPTransport;
public class ConfigNetHelper {
private RouterContext _context;
@ -43,19 +47,12 @@ public class ConfigNetHelper {
return "" + port;
}
public String getUdpPort() {
int port = 8887;
String val = _context.getProperty(PROP_I2NP_UDP_PORT);
if (val == null)
val = _context.getProperty(PROP_I2NP_INTERNAL_UDP_PORT);
if (val != null) {
try {
port = Integer.parseInt(val);
} catch (NumberFormatException nfe) {
// ignore, use default from above
}
}
return "" + port;
public String getUdpAddress() {
RouterAddress addr = _context.router().getRouterInfo().getTargetAddress("SSU");
if (addr == null)
return "unknown";
UDPAddress ua = new UDPAddress(addr);
return ua.toString();
}
public String getEnableTimeSyncChecked() {
@ -66,8 +63,29 @@ public class ConfigNetHelper {
return " checked ";
}
public String getRequireIntroductionsChecked() {
short status = _context.commSystem().getReachabilityStatus();
switch (status) {
case CommSystemFacade.STATUS_OK:
if ("true".equalsIgnoreCase(_context.getProperty(UDPTransport.PROP_FORCE_INTRODUCERS, "false")))
return "checked=\"true\"";
return "";
case CommSystemFacade.STATUS_DIFFERENT:
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
return "checked=\"true\"";
case CommSystemFacade.STATUS_UNKNOWN:
if ("true".equalsIgnoreCase(_context.getProperty(UDPTransport.PROP_FORCE_INTRODUCERS, "false")))
return "checked=\"true\"";
return "";
default:
return "checked=\"true\"";
}
}
public static final String PROP_INBOUND_KBPS = "i2np.bandwidth.inboundKBytesPerSecond";
public static final String PROP_OUTBOUND_KBPS = "i2np.bandwidth.outboundKBytesPerSecond";
public static final String PROP_INBOUND_BURST_KBPS = "i2np.bandwidth.inboundBurstKBytesPerSecond";
public static final String PROP_OUTBOUND_BURST_KBPS = "i2np.bandwidth.outboundBurstKBytesPerSecond";
public static final String PROP_INBOUND_BURST = "i2np.bandwidth.inboundBurstKBytes";
public static final String PROP_OUTBOUND_BURST = "i2np.bandwidth.outboundBurstKBytes";
public static final String PROP_SHARE_PERCENTAGE = "router.sharePercentage";
@ -78,14 +96,28 @@ public class ConfigNetHelper {
if (rate != null)
return rate;
else
return "-1";
return "16";
}
public String getOutboundRate() {
String rate = _context.getProperty(PROP_OUTBOUND_KBPS);
if (rate != null)
return rate;
else
return "-1";
return "16";
}
public String getInboundBurstRate() {
String rate = _context.getProperty(PROP_INBOUND_BURST_KBPS);
if (rate != null)
return rate;
else
return "32";
}
public String getOutboundBurstRate() {
String rate = _context.getProperty(PROP_OUTBOUND_BURST_KBPS);
if (rate != null)
return rate;
else
return "32";
}
public String getInboundBurstFactorBox() {
String rate = _context.getProperty(PROP_INBOUND_KBPS);

View File

@ -198,7 +198,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
// ignore
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (_log.shouldLog(Log.INFO))
_log.info("News fetched from " + url + " with " + (alreadyTransferred+bytesTransferred));
@ -224,4 +224,5 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
File temp = new File(TEMP_NEWS_FILE);
temp.delete();
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
}

View File

@ -28,14 +28,19 @@ public class ReseedHandler {
if (nonce == null) return;
if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) ||
nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.noncePrev"))) {
synchronized (_reseedRunner) {
if (_reseedRunner.isRunning()) {
return;
} else {
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true");
I2PThread reseed = new I2PThread(_reseedRunner, "Reseed");
reseed.start();
}
requestReseed();
}
}
public static void requestReseed() {
synchronized (_reseedRunner) {
if (_reseedRunner.isRunning()) {
return;
} else {
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true");
System.out.println("Reseeding");
I2PThread reseed = new I2PThread(_reseedRunner, "Reseed");
reseed.start();
}
}
}
@ -46,7 +51,8 @@ public class ReseedHandler {
public boolean isRunning() { return _isRunning; }
public void run() {
_isRunning = true;
reseed();
reseed(false);
System.out.println("Reseeding complete");
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false");
_isRunning = false;
}
@ -59,7 +65,7 @@ public class ReseedHandler {
* save them into this router's netDb dir.
*
*/
private static void reseed() {
private static void reseed(boolean echoStatus) {
String seedURL = System.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
if ( (seedURL == null) || (seedURL.trim().length() <= 0) )
seedURL = DEFAULT_SEED_URL;
@ -85,10 +91,16 @@ public class ReseedHandler {
try {
fetchSeed(seedURL, (String)iter.next());
fetched++;
if (echoStatus) {
System.out.print(".");
if (fetched % 60 == 0)
System.out.println();
}
} catch (Exception e) {
errors++;
}
}
if (echoStatus) System.out.println();
} catch (Throwable t) {
I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class).error("Error reseeding", t);
}
@ -172,7 +184,11 @@ public class ReseedHandler {
}
public static void main(String args[]) {
reseed();
//System.out.println("Done reseeding");
if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) {
System.out.println("Not reseeding, as requested");
return; // not reseeding on request
}
System.out.println("Reseeding");
reseed(true);
}
}

View File

@ -72,6 +72,24 @@ public class RouterConsoleRunner {
} catch (Throwable t) {
t.printStackTrace();
}
// we check the i2p installation directory (.) for a flag telling us not to reseed,
// but also check the home directory for that flag too, since new users installing i2p
// don't have an installation directory that they can put the flag in yet.
File noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed");
File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p");
File noReseedFileAlt2 = new File(".i2pnoreseed");
File noReseedFileAlt3 = new File("noreseed.i2p");
if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) {
File netDb = new File("netDb");
// sure, some of them could be "my.info" or various leaseSet- files, but chances are,
// if someone has those files, they've already been seeded (at least enough to let them
// get i2p started - they can reseed later in the web console)
String names[] = (netDb.exists() ? netDb.list() : null);
if ( (names == null) || (names.length < 15) ) {
ReseedHandler.requestReseed();
}
}
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
I2PThread t = new I2PThread(fetcher, "NewsFetcher");

View File

@ -95,7 +95,7 @@ public class SummaryHelper {
}
public boolean allowReseed() {
return (_context.netDb().getKnownRouters() < 10);
return (_context.netDb().getKnownRouters() < 30);
}
public int getAllPeers() { return _context.netDb().getKnownRouters(); }
@ -108,7 +108,7 @@ public class SummaryHelper {
case CommSystemFacade.STATUS_DIFFERENT:
return "ERR-SymmetricNAT";
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
return "ERR-Reject";
return "OK (NAT)";
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
default:
return "Unknown";
@ -206,14 +206,12 @@ public class SummaryHelper {
if (_context == null)
return "0.0";
RateStat receiveRate = _context.statManager().getRate("transport.receiveMessageSize");
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
if (receiveRate == null) return "0.0";
Rate rate = receiveRate.getRate(60*1000);
double bytes = rate.getLastTotalValue();
double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d);
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(bps);
double kbps = rate.getAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
}
/**
* How fast we have been sending data over the last minute (pretty printed
@ -224,14 +222,12 @@ public class SummaryHelper {
if (_context == null)
return "0.0";
RateStat receiveRate = _context.statManager().getRate("transport.sendMessageSize");
RateStat receiveRate = _context.statManager().getRate("bw.sendRate");
if (receiveRate == null) return "0.0";
Rate rate = receiveRate.getRate(60*1000);
double bytes = rate.getLastTotalValue();
double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d);
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(bps);
double kbps = rate.getAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
}
/**
@ -243,14 +239,12 @@ public class SummaryHelper {
if (_context == null)
return "0.0";
RateStat receiveRate = _context.statManager().getRate("transport.receiveMessageSize");
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
if (receiveRate == null) return "0.0";
Rate rate = receiveRate.getRate(5*60*1000);
double bytes = rate.getLastTotalValue();
double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d);
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(bps);
double kbps = rate.getAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
}
/**
@ -262,14 +256,12 @@ public class SummaryHelper {
if (_context == null)
return "0.0";
RateStat receiveRate = _context.statManager().getRate("transport.sendMessageSize");
RateStat receiveRate = _context.statManager().getRate("bw.sendRate");
if (receiveRate == null) return "0.0";
Rate rate = receiveRate.getRate(5*60*1000);
double bytes = rate.getLastTotalValue();
double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d);
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(bps);
double kbps = rate.getAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
}
/**
@ -281,20 +273,11 @@ public class SummaryHelper {
if (_context == null)
return "0.0";
long received = _context.bandwidthLimiter().getTotalAllocatedInboundBytes();
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
if (receiveRate == null) return "0.0";
double kbps = receiveRate.getLifetimeAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
// we use the unadjusted time, since thats what getWhenStarted is based off
long lifetime = _context.clock().now()-_context.clock().getOffset()
- _context.router().getWhenStarted();
lifetime /= 1000;
if (received > 0) {
double receivedKBps = received / (lifetime*1024.0);
return fmt.format(receivedKBps);
} else {
return "0.0";
}
return fmt.format(kbps);
}
/**
@ -306,20 +289,11 @@ public class SummaryHelper {
if (_context == null)
return "0.0";
long sent = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
if (sendRate == null) return "0.0";
double kbps = sendRate.getLifetimeAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
// we use the unadjusted time, since thats what getWhenStarted is based off
long lifetime = _context.clock().now()-_context.clock().getOffset()
- _context.router().getWhenStarted();
lifetime /= 1000;
if (sent > 0) {
double sendKBps = sent / (lifetime*1024.0);
return fmt.format(sendKBps);
} else {
return "0.0";
}
return fmt.format(kbps);
}
/**

View File

@ -3,6 +3,7 @@ package net.i2p.router.web;
import java.io.File;
import java.text.DecimalFormat;
import net.i2p.I2PAppContext;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
@ -93,7 +94,7 @@ public class UpdateHandler {
public void run() {
_isRunning = true;
update();
System.setProperty("net.i2p.router.web.ReseedHandler.updateInProgress", "false");
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
_isRunning = false;
}
private void update() {
@ -143,7 +144,7 @@ public class UpdateHandler {
buf.append(" transferred<br />");
_status = buf.toString();
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
_status = "<b>Update downloaded</b><br />";
TrustedUpdate up = new TrustedUpdate(_context);
boolean ok = up.migrateVerified(RouterVersion.VERSION, SIGNED_UPDATE_FILE, "i2pupdate.zip");
@ -165,6 +166,7 @@ public class UpdateHandler {
_status = "<b>Transfer failed</b><br />";
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
}
private void restart() {

View File

@ -2,7 +2,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - logs</title>
<title>I2P Router Console - config networking</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
@ -28,60 +28,38 @@
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" />
<input type="hidden" name="action" value="blah" />
UDP port: <i><jsp:getProperty name="nethelper" property="udpPort" /></i><br />
<!-- <input name="udpPort" type="text" size="5" value="<jsp:getProperty name="nethelper" property="udpPort" />" /><br /> -->
<b>You must poke a hole in your firewall or NAT (if applicable) to receive new inbound UDP packets on
this port from arbitrary peers (this requirement will be removed in i2p 0.6.1, but is necessary now)</b><br />
TCP port: <input name="tcpPort" type="text" size="5" value="<jsp:getProperty name="nethelper" property="tcpPort" />" /> <br />
<b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
connections on it (this requirement will be removed in i2p 0.6.1, but is necessary now)</b>
<br />
<b>External UDP address:</b> <i><jsp:getProperty name="nethelper" property="udpAddress" /></i><br />
<b>Require SSU introductions? </b>
<input type="checkbox" name="requireIntroductions" value="true" <jsp:getProperty name="nethelper" property="requireIntroductionsChecked" /> /><br />
<p>If you can, please poke a hole in your NAT or firewall to allow unsolicited UDP packets to reach
you on your external UDP address. If you can't, I2P now includes supports UDP hole punching
with "SSU introductions" - peers who will relay a request from someone you don't know to your
router for your router so that you can make an outbound connection to them. I2P will use these
introductions automatically if it detects that the port is not forwarded (as shown by
the <i>Status: OK (NAT)</i> line), or you can manually require them here.
Users behind symmetric NATs, such as OpenBSD's pf, are not currently supported.</p>
<input type="submit" name="recheckReachability" value="Check network reachability..." />
<hr />
<b>Bandwidth limiter</b><br />
Inbound rate:
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBps
bursting up to
<input name="inboundburstrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundBurstRate" />" /> KBps for
<jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br />
Outbound rate:
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBps
bursting up to
<input name="outboundburstrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundBurstRate" />" /> KBps for
<jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br />
<i>A negative rate means there is no limit</i><br />
<i>KBps = kilobytes per second = 1024 bytes per second.<br />
A negative rate means a default limit of 16KBytes per second.</i><br />
Bandwidth share percentage:
<jsp:getProperty name="nethelper" property="sharePercentageBox" /><br />
Sharing a higher percentage will improve your anonymity and help the network
<hr />
Enable internal time synchronization? <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
be within a few seconds of "correct". You will need to be able to send outbound UDP
packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).</i>
<hr />
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
<i>Changing the TCP or UDP port will force a 'soft restart' - dropping your connections and clients as
if the router was stopped and restarted. <b>Please be patient</b> - it may take
a few seconds to complete.</i>
</form>
<hr />
<b>Advanced network config:</b>
<p>
One advanced network option has to do with reseeding - you should never need to
reseed your router as long as you can find at least one other peer on the network. However,
when you do need to reseed, a link will show up on the left hand side which will
fetch all of the routerInfo-* files from http://dev.i2p.net/i2pdb/. That URL is just an
apache folder pointing at the netDb/ directory of a router - anyone can run one, and you can
configure your router to seed off an alternate URL by adding the java environmental property
"i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...). You can
also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC,
whatever) and saving them to your netDb/ directory.</p>
<p>
With the SSU transport, the internal UDP port may be different from the external
UDP port (in case of a firewall/NAT) - the UDP port field above specifies the
external one and assumes they are the same, but if you want to set the internal
port to something else, you can add "i2np.udp.internalPort=1234" to the
<a href="configadvanced.jsp">advanced</a> config and restart the router.
</p>
</div>
</body>

View File

@ -3,7 +3,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - config clients</title>
<title>I2P Router Console - config logging</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
<jsp:useBean class="net.i2p.router.web.ConfigLoggingHelper" id="logginghelper" scope="request" />

View File

@ -3,7 +3,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - config clients</title>
<title>I2P Router Console - config service</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>

View File

@ -45,7 +45,7 @@ more information).</p>
<p>The router by default also includes human's public domain <a href="http://www.i2p.net/sam">SAM</a> bridge,
which other client applications (such the <a href="http://duck.i2p/i2p-bt/">bittorrent port</a>) can use.
There is also an optimized library for doing large number calculations - jbigi - which in turn uses the
LGPL licensed <a href="http://swox.com/gmp/">GMP</a> library, tuned for various PC architectures. For
LGPL licensed <a href="http://swox.com/gmp/">GMP</a> library, tuned for various PC architectures. Launchers for windows users are built with <a href="http://launch4j.sourceforge.net/">Launch4J</a>, and the installer is built with <a href="http://www.izforge.com/izpack/">IzPack</a>. For
details on other applications available, as well as their licenses, please see the
<a href="http://www.i2p.net/licenses">license policy</a>. Source for the I2P code and most bundled
client applications can be found on our <a href="http://www.i2p.net/download">download page</a>, and is

View File

@ -15,14 +15,16 @@
</div>
<h4>
<a href="susimail/susimail">Susimail</a> |
<a href="susidns/">SusiDNS</a> |
<a href="syndie/">Syndie</a> |
<a href="i2ptunnel/">I2PTunnel</a> |
<a href="tunnels.jsp">Tunnels</a> |
<a href="profiles.jsp">Profiles</a> |
<a href="netdb.jsp">NetDB</a> |
<a href="logs.jsp">Logs</a> |
<a href="oldconsole.jsp">Internals</a> |
<a href="oldstats.jsp">Stats</a> |
<a href="i2ptunnel/" target="_blank">I2PTunnel</a> |
<a href="susimail/susimail" target="_blank">Susimail</a>
<a href="oldconsole.jsp">Internals</a>
<jsp:useBean class="net.i2p.router.web.NavHelper" id="navhelper" scope="request" />
<jsp:setProperty name="navhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="navhelper" property="clientAppLinks" />

View File

@ -3,7 +3,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - home</title>
<title>I2P Router Console - internals</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>

View File

@ -3,7 +3,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - home</title>
<title>I2P Router Console - statistics</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>

View File

@ -40,7 +40,7 @@
<b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
<b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br />
<b>Failing:</b> <jsp:getProperty name="helper" property="failingPeers" /><br />
<b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br />
<!-- <b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /> -->
<b>Known:</b> <jsp:getProperty name="helper" property="allPeers" /><br /><%
if (helper.getActivePeers() <= 0) {
%><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%

View File

@ -24,8 +24,8 @@ public class Connection {
private Log _log;
private ConnectionManager _connectionManager;
private Destination _remotePeer;
private byte _sendStreamId[];
private byte _receiveStreamId[];
private long _sendStreamId;
private long _receiveStreamId;
private long _lastSendTime;
private long _lastSendId;
private boolean _resetReceived;
@ -72,7 +72,7 @@ public class Connection {
private long _lifetimeDupMessageSent;
private long _lifetimeDupMessageReceived;
public static final long MAX_RESEND_DELAY = 5*1000;
public static final long MAX_RESEND_DELAY = 8*1000;
public static final long MIN_RESEND_DELAY = 3*1000;
/** wait up to 5 minutes after disconnection so we can ack/close packets */
@ -205,7 +205,7 @@ public class Connection {
_resetSent = true;
if (_resetSentOn <= 0)
_resetSentOn = _context.clock().now();
if ( (_remotePeer == null) || (_sendStreamId == null) ) return;
if ( (_remotePeer == null) || (_sendStreamId <= 0) ) return;
PacketLocal reply = new PacketLocal(_context, _remotePeer);
reply.setFlag(Packet.FLAG_RESET);
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
@ -256,14 +256,18 @@ public class Connection {
remaining = 0;
if (packet.isFlagSet(Packet.FLAG_CLOSE) || (remaining < 2)) {
packet.setOptionalDelay(0);
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
} else {
int delay = _options.getRTT() / 2;
int delay = _options.getRTO() / 2;
packet.setOptionalDelay(delay);
_log.debug("Requesting ack delay of " + delay + "ms for packet " + packet);
if (delay > 0)
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Requesting ack delay of " + delay + "ms for packet " + packet);
}
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
long timeout = _options.getRTT() + MIN_RESEND_DELAY;
long timeout = _options.getRTO();
if (timeout > MAX_RESEND_DELAY)
timeout = MAX_RESEND_DELAY;
if (_log.shouldLog(Log.DEBUG))
@ -307,16 +311,20 @@ public class Connection {
}
List ackPackets(long ackThrough, long nacks[]) {
if (nacks == null) {
_highestAckedThrough = ackThrough;
if (ackThrough < _highestAckedThrough) {
// dupack which won't tell us anything
} else {
long lowest = -1;
for (int i = 0; i < nacks.length; i++) {
if ( (lowest < 0) || (nacks[i] < lowest) )
lowest = nacks[i];
if (nacks == null) {
_highestAckedThrough = ackThrough;
} else {
long lowest = -1;
for (int i = 0; i < nacks.length; i++) {
if ( (lowest < 0) || (nacks[i] < lowest) )
lowest = nacks[i];
}
if (lowest - 1 > _highestAckedThrough)
_highestAckedThrough = lowest - 1;
}
if (lowest - 1 > _highestAckedThrough)
_highestAckedThrough = lowest - 1;
}
List acked = null;
@ -463,7 +471,9 @@ public class Connection {
_receiver.destroy();
if (_activityTimer != null)
SimpleTimer.getInstance().removeEvent(_activityTimer);
_activityTimer = null;
//_activityTimer = null;
if (_inputStream != null)
_inputStream.streamErrorOccurred(new IOException("disconnected!"));
if (_disconnectScheduledOn < 0) {
_disconnectScheduledOn = _context.clock().now();
@ -514,17 +524,30 @@ public class Connection {
synchronized (_connectLock) { _connectLock.notifyAll(); }
}
private boolean _remotePeerSet = false;
/** who are we talking with */
public Destination getRemotePeer() { return _remotePeer; }
public void setRemotePeer(Destination peer) { _remotePeer = peer; }
public void setRemotePeer(Destination peer) {
if (_remotePeerSet) throw new RuntimeException("Remote peer already set [" + _remotePeer + ", " + peer + "]");
_remotePeerSet = true;
_remotePeer = peer;
}
private boolean _sendStreamIdSet = false;
/** what stream do we send data to the peer on? */
public byte[] getSendStreamId() { return _sendStreamId; }
public void setSendStreamId(byte[] id) { _sendStreamId = id; }
public long getSendStreamId() { return _sendStreamId; }
public void setSendStreamId(long id) {
if (_sendStreamIdSet) throw new RuntimeException("Send stream ID already set [" + _sendStreamId + ", " + id + "]");
_sendStreamIdSet = true;
_sendStreamId = id;
}
private boolean _receiveStreamIdSet = false;
/** stream the peer sends data to us on. (may be null) */
public byte[] getReceiveStreamId() { return _receiveStreamId; }
public void setReceiveStreamId(byte[] id) {
public long getReceiveStreamId() { return _receiveStreamId; }
public void setReceiveStreamId(long id) {
if (_receiveStreamIdSet) throw new RuntimeException("Receive stream ID already set [" + _receiveStreamId + ", " + id + "]");
_receiveStreamIdSet = true;
_receiveStreamId = id;
synchronized (_connectLock) { _connectLock.notifyAll(); }
}
@ -651,7 +674,7 @@ public class Connection {
void waitForConnect() {
long expiration = _context.clock().now() + _options.getConnectTimeout();
while (true) {
if (_connected && (_receiveStreamId != null) && (_sendStreamId != null) ) {
if (_connected && (_receiveStreamId > 0) && (_sendStreamId > 0) ) {
// w00t
if (_log.shouldLog(Log.DEBUG))
_log.debug("waitForConnect(): Connected and we have stream IDs");
@ -695,11 +718,19 @@ public class Connection {
}
private void resetActivityTimer() {
if (_options.getInactivityTimeout() <= 0) return;
if (_activityTimer == null) return;
if (_options.getInactivityTimeout() <= 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Resetting the inactivity timer, but its gone!", new Exception("where did it go?"));
return;
}
if (_activityTimer == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Resetting the inactivity timer, but its gone!", new Exception("where did it go?"));
return;
}
long howLong = _activityTimer.getTimeLeft();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Resetting the inactivity timer to " + howLong);
_log.debug("Resetting the inactivity timer to " + howLong, new Exception("Reset by"));
// this will get rescheduled, and rescheduled, and rescheduled...
SimpleTimer.getInstance().addEvent(_activityTimer, howLong);
}
@ -707,15 +738,34 @@ public class Connection {
private class ActivityTimer implements SimpleTimer.TimedEvent {
public void timeReached() {
// uh, nothing more to do...
if (!_connected) return;
if (!_connected) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but we are already closed");
return;
}
// we got rescheduled already
if (getTimeLeft() > 0) return;
long left = getTimeLeft();
if (left > 0) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but there is time left (" + left + ")");
SimpleTimer.getInstance().addEvent(ActivityTimer.this, left);
return;
}
// these are either going to time out or cause further rescheduling
if (getUnackedPacketsSent() > 0) return;
if (getUnackedPacketsSent() > 0) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but there are unacked packets");
return;
}
// wtf, this shouldn't have been scheduled
if (_options.getInactivityTimeout() <= 0) return;
if (_options.getInactivityTimeout() <= 0) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but there is no timer...");
return;
}
// if one of us can't talk...
if ( (_closeSentOn > 0) || (_closeReceivedOn > 0) ) return;
if ( (_closeSentOn > 0) || (_closeReceivedOn > 0) ) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but we are closing");
return;
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, with action=" + _options.getInactivityAction());
// bugger it, might as well do the hard work now
switch (_options.getInactivityAction()) {
@ -741,7 +791,9 @@ public class Connection {
_log.debug(buf.toString());
}
disconnect(true);
_inputStream.streamErrorOccurred(new IOException("Inactivity timeout"));
_outputStream.streamErrorOccurred(new IOException("Inactivity timeout"));
disconnect(false);
break;
}
}
@ -762,18 +814,19 @@ public class Connection {
public String toString() {
StringBuffer buf = new StringBuffer(128);
buf.append("[Connection ");
if (_receiveStreamId != null)
buf.append(Base64.encode(_receiveStreamId));
if (_receiveStreamId > 0)
buf.append(Packet.toId(_receiveStreamId));
else
buf.append("unknown");
buf.append("<-->");
if (_sendStreamId != null)
buf.append(Base64.encode(_sendStreamId));
if (_sendStreamId > 0)
buf.append(Packet.toId(_sendStreamId));
else
buf.append("unknown");
buf.append(" wsize: ").append(_options.getWindowSize());
buf.append(" cwin: ").append(_congestionWindowEnd - _highestAckedThrough);
buf.append(" rtt: ").append(_options.getRTT());
buf.append(" rto: ").append(_options.getRTO());
// not synchronized to avoid some kooky races
buf.append(" unacked outbound: ").append(_outboundPackets.size()).append(" ");
/*
@ -877,11 +930,14 @@ public class Connection {
}
// revamp various fields, in case we need to ack more, etc
_inputStream.updateAcks(_packet);
_packet.setOptionalDelay(getOptions().getChoke());
int choke = getOptions().getChoke();
_packet.setOptionalDelay(choke);
if (choke > 0)
_packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
_packet.setOptionalMaxSize(getOptions().getMaxMessageSize());
_packet.setResendDelay(getOptions().getResendDelay());
_packet.setReceiveStreamId(_receiveStreamId);
_packet.setSendStreamId(_sendStreamId);
//_packet.setReceiveStreamId(_receiveStreamId);
//_packet.setSendStreamId(_sendStreamId);
int newWindowSize = getOptions().getWindowSize();
@ -950,10 +1006,10 @@ public class Connection {
disconnect(false);
} else {
//long timeout = _options.getResendDelay() << numSends;
long rtt = _options.getRTT();
if (rtt < MIN_RESEND_DELAY)
rtt = MIN_RESEND_DELAY;
long timeout = rtt << (numSends-1);
long rto = _options.getRTO();
if (rto < MIN_RESEND_DELAY)
rto = MIN_RESEND_DELAY;
long timeout = rto << (numSends-1);
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
timeout = MAX_RESEND_DELAY;
if (_log.shouldLog(Log.DEBUG))

View File

@ -2,6 +2,7 @@ package net.i2p.client.streaming;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/**
@ -142,15 +143,18 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
data.setValid(size);
data.setOffset(0);
packet.setPayload(data);
if ( (ackOnly && !forceIncrement) && (!isFirst) )
packet.setSequenceNum(0);
if ( (ackOnly && !forceIncrement) && (!isFirst) )
packet.setSequenceNum(0);
else
packet.setSequenceNum(con.getNextOutboundPacketNum());
packet.setSendStreamId(con.getSendStreamId());
packet.setReceiveStreamId(con.getReceiveStreamId());
con.getInputStream().updateAcks(packet);
packet.setOptionalDelay(con.getOptions().getChoke());
int choke = con.getOptions().getChoke();
packet.setOptionalDelay(choke);
if (choke > 0)
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
packet.setResendDelay(con.getOptions().getResendDelay());
if (con.getOptions().getProfile() == ConnectionOptions.PROFILE_INTERACTIVE)
@ -166,6 +170,9 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
packet.setOptionalFrom(con.getSession().getMyDestination());
packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize());
}
if (DataHelper.eq(con.getSendStreamId(), Packet.STREAM_ID_UNKNOWN)) {
packet.setFlag(Packet.FLAG_NO_ACK);
}
// don't set the closed flag if this is a plain ACK and there are outstanding
// packets sent, otherwise the other side could receive the CLOSE prematurely,

View File

@ -127,7 +127,7 @@ class ConnectionHandler {
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
reply.setAckThrough(packet.getSequenceNum());
reply.setSendStreamId(packet.getReceiveStreamId());
reply.setReceiveStreamId(null);
reply.setReceiveStreamId(0);
reply.setOptionalFrom(_manager.getSession().getMyDestination());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending RST: " + reply + " because of " + packet);

View File

@ -31,9 +31,9 @@ public class ConnectionManager {
private PacketQueue _outboundQueue;
private SchedulerChooser _schedulerChooser;
private ConnectionPacketHandler _conPacketHandler;
/** Inbound stream ID (ByteArray) to Connection map */
/** Inbound stream ID (Long) to Connection map */
private Map _connectionByInboundId;
/** Ping ID (ByteArray) to PingRequest */
/** Ping ID (Long) to PingRequest */
private Map _pendingPings;
private boolean _allowIncoming;
private int _maxConcurrentStreams;
@ -71,16 +71,16 @@ public class ConnectionManager {
_context.statManager().createRateStat("stream.receiveActive", "How many streams are active when a new one is received (period being not yet dropped)", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
}
Connection getConnectionByInboundId(byte[] id) {
Connection getConnectionByInboundId(long id) {
synchronized (_connectionLock) {
return (Connection)_connectionByInboundId.get(new ByteArray(id));
return (Connection)_connectionByInboundId.get(new Long(id));
}
}
/**
* not guaranteed to be unique, but in case we receive more than one packet
* on an inbound connection that we havent ack'ed yet...
*/
Connection getConnectionByOutboundId(byte[] id) {
Connection getConnectionByOutboundId(long id) {
synchronized (_connectionLock) {
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
Connection con = (Connection)iter.next();
@ -107,8 +107,7 @@ public class ConnectionManager {
*/
public Connection receiveConnection(Packet synPacket) {
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, new ConnectionOptions(_defaultOptions));
byte receiveId[] = new byte[4];
_context.random().nextBytes(receiveId);
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
boolean reject = false;
int active = 0;
int total = 0;
@ -122,16 +121,13 @@ public class ConnectionManager {
reject = true;
} else {
while (true) {
ByteArray ba = new ByteArray(receiveId);
Connection oldCon = (Connection)_connectionByInboundId.put(ba, con);
Connection oldCon = (Connection)_connectionByInboundId.put(new Long(receiveId), con);
if (oldCon == null) {
break;
} else {
_connectionByInboundId.put(ba, oldCon);
_connectionByInboundId.put(new Long(receiveId), oldCon);
// receiveId already taken, try another
// (need to realloc receiveId, as ba.getData() points to the old value)
receiveId = new byte[4];
_context.random().nextBytes(receiveId);
receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
}
}
}
@ -148,7 +144,7 @@ public class ConnectionManager {
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
reply.setAckThrough(synPacket.getSequenceNum());
reply.setSendStreamId(synPacket.getReceiveStreamId());
reply.setReceiveStreamId(null);
reply.setReceiveStreamId(0);
reply.setOptionalFrom(_session.getMyDestination());
// this just sends the packet - no retries or whatnot
_outboundQueue.enqueue(reply);
@ -160,7 +156,7 @@ public class ConnectionManager {
con.getPacketHandler().receivePacket(synPacket, con);
} catch (I2PException ie) {
synchronized (_connectionLock) {
_connectionByInboundId.remove(new ByteArray(receiveId));
_connectionByInboundId.remove(new Long(receiveId));
}
return null;
}
@ -179,8 +175,7 @@ public class ConnectionManager {
*/
public Connection connect(Destination peer, ConnectionOptions opts) {
Connection con = null;
byte receiveId[] = new byte[4];
_context.random().nextBytes(receiveId);
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
long expiration = _context.clock().now() + opts.getConnectTimeout();
if (opts.getConnectTimeout() <= 0)
expiration = _context.clock().now() + DEFAULT_STREAM_DELAY_MAX;
@ -213,11 +208,10 @@ public class ConnectionManager {
con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, opts);
con.setRemotePeer(peer);
ByteArray ba = new ByteArray(receiveId);
while (_connectionByInboundId.containsKey(ba)) {
_context.random().nextBytes(receiveId);
while (_connectionByInboundId.containsKey(new Long(receiveId))) {
receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
}
_connectionByInboundId.put(ba, con);
_connectionByInboundId.put(new Long(receiveId), con);
break; // stop looping as a psuedo-wait
}
}
@ -284,7 +278,7 @@ public class ConnectionManager {
public void removeConnection(Connection con) {
boolean removed = false;
synchronized (_connectionLock) {
Object o = _connectionByInboundId.remove(new ByteArray(con.getReceiveStreamId()));
Object o = _connectionByInboundId.remove(new Long(con.getReceiveStreamId()));
removed = (o == con);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connection removed? " + removed + " remaining: "
@ -320,11 +314,9 @@ public class ConnectionManager {
return ping(peer, timeoutMs, blocking, null, null, null);
}
public boolean ping(Destination peer, long timeoutMs, boolean blocking, SessionKey keyToUse, Set tagsToSend, PingNotifier notifier) {
byte id[] = new byte[4];
_context.random().nextBytes(id);
ByteArray ba = new ByteArray(id);
Long id = new Long(_context.random().nextLong(Packet.MAX_STREAM_ID-1)+1);
PacketLocal packet = new PacketLocal(_context, peer);
packet.setSendStreamId(id);
packet.setSendStreamId(id.longValue());
packet.setFlag(Packet.FLAG_ECHO);
packet.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
packet.setOptionalFrom(_session.getMyDestination());
@ -336,7 +328,7 @@ public class ConnectionManager {
PingRequest req = new PingRequest(peer, packet, notifier);
synchronized (_pendingPings) {
_pendingPings.put(ba, req);
_pendingPings.put(id, req);
}
_outboundQueue.enqueue(packet);
@ -349,10 +341,10 @@ public class ConnectionManager {
}
synchronized (_pendingPings) {
_pendingPings.remove(ba);
_pendingPings.remove(id);
}
} else {
SimpleTimer.getInstance().addEvent(new PingFailed(ba, notifier), timeoutMs);
SimpleTimer.getInstance().addEvent(new PingFailed(id, notifier), timeoutMs);
}
boolean ok = req.pongReceived();
@ -364,17 +356,17 @@ public class ConnectionManager {
}
private class PingFailed implements SimpleTimer.TimedEvent {
private ByteArray _ba;
private Long _id;
private PingNotifier _notifier;
public PingFailed(ByteArray ba, PingNotifier notifier) {
_ba = ba;
public PingFailed(Long id, PingNotifier notifier) {
_id = id;
_notifier = notifier;
}
public void timeReached() {
boolean removed = false;
synchronized (_pendingPings) {
Object o = _pendingPings.remove(_ba);
Object o = _pendingPings.remove(_id);
if (o != null)
removed = true;
}
@ -411,11 +403,10 @@ public class ConnectionManager {
public boolean pongReceived() { return _ponged; }
}
void receivePong(byte pingId[]) {
ByteArray ba = new ByteArray(pingId);
void receivePong(long pingId) {
PingRequest req = null;
synchronized (_pendingPings) {
req = (PingRequest)_pendingPings.remove(ba);
req = (PingRequest)_pendingPings.remove(new Long(pingId));
}
if (req != null)
req.pong();

View File

@ -13,6 +13,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
private int _receiveWindow;
private int _profile;
private int _rtt;
private int _rttDev;
private int _rto;
private int _trend[];
private int _resendDelay;
private int _sendAckDelay;
@ -52,6 +54,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
public static final String PROP_SLOW_START_GROWTH_RATE_FACTOR = "i2p.streaming.slowStartGrowthRateFactor";
private static final int TREND_COUNT = 3;
static final int INITIAL_WINDOW_SIZE = 4;
static final int DEFAULT_MAX_SENDS = 8;
public ConnectionOptions() {
super();
@ -68,6 +72,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
public ConnectionOptions(ConnectionOptions opts) {
super(opts);
if (opts != null) {
setMaxWindowSize(opts.getMaxWindowSize());
setConnectDelay(opts.getConnectDelay());
setProfile(opts.getProfile());
setRTT(opts.getRTT());
@ -80,7 +85,6 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
setInactivityTimeout(opts.getInactivityTimeout());
setInactivityAction(opts.getInactivityAction());
setInboundBufferSize(opts.getInboundBufferSize());
setMaxWindowSize(opts.getMaxWindowSize());
setCongestionAvoidanceGrowthRateFactor(opts.getCongestionAvoidanceGrowthRateFactor());
setSlowStartGrowthRateFactor(opts.getSlowStartGrowthRateFactor());
}
@ -90,6 +94,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
super.init(opts);
_trend = new int[TREND_COUNT];
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK));
setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, 4*1024));
@ -97,22 +102,23 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, 1));
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 10));
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, INITIAL_WINDOW_SIZE));
setMaxResends(getInt(opts, PROP_MAX_RESENDS, DEFAULT_MAX_SENDS));
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 2*60*1000));
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 1));
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 1));
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
}
public void setProperties(Properties opts) {
super.setProperties(opts);
if (opts == null) return;
if (opts.containsKey(PROP_MAX_WINDOW_SIZE))
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
if (opts.containsKey(PROP_CONNECT_DELAY))
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
if (opts.containsKey(PROP_PROFILE))
@ -124,17 +130,17 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
if (opts.containsKey(PROP_INITIAL_RECEIVE_WINDOW))
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
if (opts.containsKey(PROP_INITIAL_RESEND_DELAY))
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 500));
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
if (opts.containsKey(PROP_INITIAL_ACK_DELAY))
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
if (opts.containsKey(PROP_INITIAL_WINDOW_SIZE))
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, 1));
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, INITIAL_WINDOW_SIZE));
if (opts.containsKey(PROP_MAX_RESENDS))
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 10));
setMaxResends(getInt(opts, PROP_MAX_RESENDS, DEFAULT_MAX_SENDS));
if (opts.containsKey(PROP_WRITE_TIMEOUT))
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
if (opts.containsKey(PROP_INACTIVITY_TIMEOUT))
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 2*60*1000));
if (opts.containsKey(PROP_INACTIVITY_ACTION))
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
@ -145,8 +151,6 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
if (opts.containsKey(PROP_CONNECT_TIMEOUT))
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
if (opts.containsKey(PROP_MAX_WINDOW_SIZE))
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
}
/**
@ -191,6 +195,10 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
*/
public int getRTT() { return _rtt; }
public void setRTT(int ms) {
if (_rto == 0) {
_rttDev = ms;
_rto = (int)Connection.MAX_RESEND_DELAY;
}
synchronized (_trend) {
_trend[0] = _trend[1];
_trend[1] = _trend[2];
@ -201,10 +209,12 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
else
_trend[2] = 0;
}
_rtt = ms;
if (_rtt > 60*1000)
_rtt = 60*1000;
}
public int getRTO() { return _rto; }
/**
* If we have 3 consecutive rtt increases, we are trending upwards (1), or if we have
@ -225,7 +235,15 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
private static final double RTT_DAMPENING = 0.9;
public void updateRTT(int measuredValue) {
setRTT((int)(RTT_DAMPENING*_rtt + (1-RTT_DAMPENING)*measuredValue));
_rttDev = _rttDev + (int)(0.25d*(Math.abs(measuredValue-_rtt)-_rttDev));
int smoothed = (int)(RTT_DAMPENING*_rtt + (1-RTT_DAMPENING)*measuredValue);
_rto = smoothed + (_rttDev<<2);
if (_rto < Connection.MIN_RESEND_DELAY)
_rto = (int)Connection.MIN_RESEND_DELAY;
else if (_rto > Connection.MAX_RESEND_DELAY)
_rto = (int)Connection.MAX_RESEND_DELAY;
setRTT(smoothed);
}
/** How long after sending a packet will we wait before resending? */

View File

@ -96,16 +96,16 @@ public class ConnectionPacketHandler {
boolean allowAck = true;
if ( (!packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) &&
( (packet.getSendStreamId() == null) ||
(packet.getReceiveStreamId() == null) ||
(DataHelper.eq(packet.getSendStreamId(), Packet.STREAM_ID_UNKNOWN)) ||
(DataHelper.eq(packet.getReceiveStreamId(), Packet.STREAM_ID_UNKNOWN)) ) )
( (packet.getSendStreamId() <= 0) ||
(packet.getReceiveStreamId() <= 0) ) )
allowAck = false;
if (allowAck)
if (allowAck) {
isNew = con.getInputStream().messageReceived(packet.getSequenceNum(), packet.getPayload());
else
isNew = con.getInputStream().messageReceived(con.getInputStream().getHighestReadyBockId(), null);
} else {
con.getInputStream().notifyActivity();
isNew = false;
}
if ( (packet.getSequenceNum() == 0) && (packet.getPayloadSize() > 0) ) {
if (_log.shouldLog(Log.DEBUG))
@ -160,9 +160,7 @@ public class ConnectionPacketHandler {
}
}
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE) &&
((packet.getSendStreamId() == null) ||
DataHelper.eq(packet.getSendStreamId(), Packet.STREAM_ID_UNKNOWN) ) ) {
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE) && (packet.getSendStreamId() <= 0) ) {
// don't honor the ACK 0 in SYN packets received when the other side
// has obviously not seen our messages
} else {
@ -170,10 +168,16 @@ public class ConnectionPacketHandler {
}
con.eventOccurred();
if (fastAck) {
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fast ack for dup " + packet);
con.ackImmediately();
if (!isNew) {
// if we're congested (fastAck) but this is also a new packet,
// we've already scheduled an ack above, so there is no need to schedule
// a fast ack (we can wait a few ms)
} else {
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fast ack for dup " + packet);
con.ackImmediately();
}
}
}
@ -187,6 +191,7 @@ public class ConnectionPacketHandler {
}
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew, boolean choke) {
if (ackThrough < 0) return false;
//if ( (nacks != null) && (nacks.length > 0) )
// con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
@ -196,8 +201,8 @@ public class ConnectionPacketHandler {
// could actually be acking data (this fixes the buggered up ack of packet 0 problem).
// this is called after packet verification, which places the stream IDs as necessary if
// the SYN verifies (so if we're acking w/out stream IDs, no SYN has been received yet)
if ( (packet != null) && (packet.getSendStreamId() != null) && (packet.getReceiveStreamId() != null) &&
(con != null) && (con.getSendStreamId() != null) && (con.getReceiveStreamId() != null) &&
if ( (packet != null) && (packet.getSendStreamId() > 0) && (packet.getReceiveStreamId() > 0) &&
(con != null) && (con.getSendStreamId() > 0) && (con.getReceiveStreamId() > 0) &&
(!DataHelper.eq(packet.getSendStreamId(), Packet.STREAM_ID_UNKNOWN)) &&
(!DataHelper.eq(packet.getReceiveStreamId(), Packet.STREAM_ID_UNKNOWN)) &&
(!DataHelper.eq(con.getSendStreamId(), Packet.STREAM_ID_UNKNOWN)) &&
@ -315,6 +320,12 @@ public class ConnectionPacketHandler {
return congested;
}
/**
* If we don't know the send stream id yet (we're just creating a connection), allow
* the first three packets to come in. The first of those should be the SYN, of course...
*/
private static final int MAX_INITIAL_PACKETS = ConnectionOptions.INITIAL_WINDOW_SIZE;
/**
* Make sure this packet is ok and that we can continue processing its data.
*
@ -328,14 +339,14 @@ public class ConnectionPacketHandler {
} else {
verifySignature(packet, con);
if (con.getSendStreamId() == null) {
if (con.getSendStreamId() <= 0) {
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
con.setSendStreamId(packet.getReceiveStreamId());
con.setRemotePeer(packet.getOptionalFrom());
return true;
} else {
// neither RST nor SYN and we dont have the stream id yet?
if (packet.getSequenceNum() <= 2) {
if (packet.getSequenceNum() < MAX_INITIAL_PACKETS) {
return true;
} else {
if (_log.shouldLog(Log.ERROR))

View File

@ -44,6 +44,7 @@ public class MessageHandler implements I2PSessionListener {
_log.warn("Error receiving the message", ise);
return;
}
if (data == null) return;
Packet packet = new Packet();
try {
packet.readPacket(data, 0, data.length);

View File

@ -193,6 +193,8 @@ public class MessageInputStream extends InputStream {
}
}
public void notifyActivity() { synchronized (_dataLock) { _dataLock.notifyAll(); } }
/**
* A new message has arrived - toss it on the appropriate queue (moving
* previously pending messages to the ready queue if it fills the gap, etc).
@ -435,7 +437,9 @@ public class MessageInputStream extends InputStream {
*
*/
void streamErrorOccurred(IOException ioe) {
_streamError = ioe;
if (_streamError == null)
_streamError = ioe;
_locallyClosed = true;
synchronized (_dataLock) {
_dataLock.notifyAll();
}

View File

@ -312,11 +312,16 @@ public class MessageOutputStream extends OutputStream {
/** nonblocking close */
public void closeInternal() {
_closed = true;
_streamError = new IOException("Closed internally");
if (_streamError == null)
_streamError = new IOException("Closed internally");
clearData(true);
}
private void clearData(boolean shouldFlush) {
ByteArray ba = null;
synchronized (_dataLock) {
// flush any data, but don't wait for it
if (_dataReceiver != null)
if ( (_dataReceiver != null) && (_valid > 0) && shouldFlush)
_dataReceiver.writeData(_buf, 0, _valid);
_written += _valid;
_valid = 0;
@ -345,7 +350,9 @@ public class MessageOutputStream extends OutputStream {
}
void streamErrorOccurred(IOException ioe) {
_streamError = ioe;
if (_streamError == null)
_streamError = ioe;
clearData(false);
}
/**

View File

@ -10,6 +10,7 @@ import net.i2p.data.Destination;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
/**
* Contain a single packet transferred as part of a streaming connection.
@ -51,8 +52,8 @@ import net.i2p.util.ByteCache;
*
*/
public class Packet {
private byte _sendStreamId[];
private byte _receiveStreamId[];
private long _sendStreamId;
private long _receiveStreamId;
private long _sequenceNum;
private long _ackThrough;
private long _nacks[];
@ -64,7 +65,6 @@ public class Packet {
private Destination _optionFrom;
private int _optionDelay;
private int _optionMaxSize;
private ByteCache _cache;
/**
* The receiveStreamId will be set to this when the packet doesn't know
@ -72,7 +72,9 @@ public class Packet {
* synchronize packet)
*
*/
public static final byte STREAM_ID_UNKNOWN[] = new byte[] { 0x00, 0x00, 0x00, 0x00 };
public static final long STREAM_ID_UNKNOWN = 0l;
public static final long MAX_STREAM_ID = 0xffffffffl;
/**
* This packet is creating a new socket connection (if the receiveStreamId
@ -135,43 +137,36 @@ public class Packet {
* ping reply (if receiveStreamId is set).
*/
public static final int FLAG_ECHO = (1 << 9);
/**
* If set, this packet doesn't really want to ack anything
*/
public static final int FLAG_NO_ACK = (1 << 10);
public static final int DEFAULT_MAX_SIZE = 32*1024;
private static final int MAX_DELAY_REQUEST = 65535;
public Packet() {
_cache = ByteCache.getInstance(128, MAX_PAYLOAD_SIZE);
}
public Packet() { }
/** what stream is this packet a part of? */
public byte[] getSendStreamId() {
if ( (_sendStreamId == null) || (DataHelper.eq(_sendStreamId, STREAM_ID_UNKNOWN)) )
return null;
else
return _sendStreamId;
}
public void setSendStreamId(byte[] id) {
private boolean _sendStreamIdSet = false;
/** what stream do we send data to the peer on? */
public long getSendStreamId() { return _sendStreamId; }
public void setSendStreamId(long id) {
if (_sendStreamIdSet) throw new RuntimeException("Send stream ID already set [" + _sendStreamId + ", " + id + "]");
_sendStreamIdSet = true;
_sendStreamId = id;
if ( (id != null) && (DataHelper.eq(id, STREAM_ID_UNKNOWN)) )
_sendStreamId = null;
}
private boolean _receiveStreamIdSet = false;
/**
* Stream that replies should be sent on. if the
* connection is still being built, this should be
* null.
*
* stream the replies should be sent on. this should be 0 if the
* connection is still being built.
*/
public byte[] getReceiveStreamId() {
if ( (_receiveStreamId == null) || (DataHelper.eq(_receiveStreamId, STREAM_ID_UNKNOWN)) )
return null;
else
return _receiveStreamId;
}
public void setReceiveStreamId(byte[] id) {
public long getReceiveStreamId() { return _receiveStreamId; }
public void setReceiveStreamId(long id) {
if (_receiveStreamIdSet) throw new RuntimeException("Receive stream ID already set [" + _receiveStreamId + ", " + id + "]");
_receiveStreamIdSet = true;
_receiveStreamId = id;
if ( (id != null) && (DataHelper.eq(id, STREAM_ID_UNKNOWN)) )
_receiveStreamId = null;
}
/** 0-indexed sequence number for this Packet in the sendStream */
@ -181,11 +176,21 @@ public class Packet {
/**
* The highest packet sequence number that received
* on the receiveStreamId. This field is ignored on the initial
* connection packet (where receiveStreamId is the unknown id).
* connection packet (where receiveStreamId is the unknown id) or
* if FLAG_NO_ACK is set.
*
*/
public long getAckThrough() { return _ackThrough; }
public void setAckThrough(long id) { _ackThrough = id; }
public long getAckThrough() {
if (isFlagSet(FLAG_NO_ACK))
return -1;
else
return _ackThrough;
}
public void setAckThrough(long id) {
if (id < 0)
setFlag(FLAG_NO_ACK);
_ackThrough = id;
}
/**
* List of packet sequence numbers below the getAckThrough() value
@ -209,8 +214,6 @@ public class Packet {
/** get the actual payload of the message. may be null */
public ByteArray getPayload() { return _payload; }
public void setPayload(ByteArray payload) {
//if ( (_payload != null) && (_payload != payload) )
// _cache.release(_payload);
_payload = payload;
if ( (payload != null) && (payload.getValid() > MAX_PAYLOAD_SIZE) )
throw new IllegalArgumentException("Too large payload: " + payload.getValid());
@ -219,15 +222,11 @@ public class Packet {
return (_payload == null ? 0 : _payload.getValid());
}
public void releasePayload() {
//if (_payload != null)
// _cache.release(_payload);
_payload = null;
//_payload = null;
}
public ByteArray acquirePayload() {
ByteArray old = _payload;
_payload = new ByteArray(new byte[Packet.MAX_PAYLOAD_SIZE]); //_cache.acquire();
//if (old != null)
// _cache.release(old);
_payload = new ByteArray(new byte[Packet.MAX_PAYLOAD_SIZE]);
return _payload;
}
@ -240,6 +239,7 @@ public class Packet {
else
_flags &= ~flag;
}
public void setFlags(int flags) { _flags = flags; }
/** the signature on the packet (only included if the flag for it is set) */
public Signature getOptionalSignature() { return _optionSignature; }
@ -263,7 +263,6 @@ public class Packet {
*/
public int getOptionalDelay() { return _optionDelay; }
public void setOptionalDelay(int delayMs) {
setFlag(FLAG_DELAY_REQUESTED, delayMs > 0);
if (delayMs > MAX_DELAY_REQUEST)
_optionDelay = MAX_DELAY_REQUEST;
else if (delayMs < 0)
@ -297,15 +296,9 @@ public class Packet {
*/
private int writePacket(byte buffer[], int offset, boolean includeSig) throws IllegalStateException {
int cur = offset;
if ( (_sendStreamId != null) && (_sendStreamId.length == 4) )
System.arraycopy(_sendStreamId, 0, buffer, cur, _sendStreamId.length);
else
System.arraycopy(STREAM_ID_UNKNOWN, 0, buffer, cur, STREAM_ID_UNKNOWN.length);
DataHelper.toLong(buffer, cur, 4, (_sendStreamId >= 0 ? _sendStreamId : STREAM_ID_UNKNOWN));
cur += 4;
if ( (_receiveStreamId != null) && (_receiveStreamId.length == 4) )
System.arraycopy(_receiveStreamId, 0, buffer, cur, _receiveStreamId.length);
else
System.arraycopy(STREAM_ID_UNKNOWN, 0, buffer, cur, STREAM_ID_UNKNOWN.length);
DataHelper.toLong(buffer, cur, 4, (_receiveStreamId >= 0 ? _receiveStreamId : STREAM_ID_UNKNOWN));
cur += 4;
DataHelper.toLong(buffer, cur, 4, _sequenceNum > 0 ? _sequenceNum : 0);
cur += 4;
@ -383,7 +376,7 @@ public class Packet {
size += 4; // sequenceNum
size += 4; // ackThrough
if (_nacks != null) {
size++; // nacks length
size++; // nacks length
size += 4 * _nacks.length;
} else {
size++; // nacks length
@ -425,32 +418,31 @@ public class Packet {
if (length < 22) // min header size
throw new IllegalArgumentException("Too small: len=" + buffer.length);
int cur = offset;
_sendStreamId = new byte[4];
System.arraycopy(buffer, cur, _sendStreamId, 0, 4);
setSendStreamId(DataHelper.fromLong(buffer, cur, 4));
cur += 4;
_receiveStreamId = new byte[4];
System.arraycopy(buffer, cur, _receiveStreamId, 0, 4);
setReceiveStreamId(DataHelper.fromLong(buffer, cur, 4));
cur += 4;
_sequenceNum = DataHelper.fromLong(buffer, cur, 4);
setSequenceNum(DataHelper.fromLong(buffer, cur, 4));
cur += 4;
_ackThrough = DataHelper.fromLong(buffer, cur, 4);
setAckThrough(DataHelper.fromLong(buffer, cur, 4));
cur += 4;
int numNacks = (int)DataHelper.fromLong(buffer, cur, 1);
cur++;
if (length < 22 + numNacks*4)
throw new IllegalArgumentException("Too small with " + numNacks + " nacks: " + length);
if (numNacks > 0) {
_nacks = new long[numNacks];
long nacks[] = new long[numNacks];
for (int i = 0; i < numNacks; i++) {
_nacks[i] = DataHelper.fromLong(buffer, cur, 4);
nacks[i] = DataHelper.fromLong(buffer, cur, 4);
cur += 4;
}
setNacks(nacks);
} else {
_nacks = null;
setNacks(null);
}
_resendDelay = (int)DataHelper.fromLong(buffer, cur, 1);
setResendDelay((int)DataHelper.fromLong(buffer, cur, 1));
cur++;
_flags = (int)DataHelper.fromLong(buffer, cur, 2);
setFlags((int)DataHelper.fromLong(buffer, cur, 2));
cur += 2;
int optionSize = (int)DataHelper.fromLong(buffer, cur, 2);
@ -466,33 +458,36 @@ public class Packet {
throw new IllegalArgumentException("length: " + length + " offset: " + offset + " begin: " + payloadBegin);
// skip ahead to the payload
_payload = new ByteArray(new byte[payloadSize]); //_cache.acquire();
System.arraycopy(buffer, payloadBegin, _payload.getData(), 0, payloadSize);
_payload.setValid(payloadSize);
_payload.setOffset(0);
//_payload = new ByteArray(new byte[payloadSize]);
_payload = new ByteArray(buffer, payloadBegin, payloadSize);
//System.arraycopy(buffer, payloadBegin, _payload.getData(), 0, payloadSize);
//_payload.setValid(payloadSize);
//_payload.setOffset(0);
// ok now lets go back and deal with the options
if (isFlagSet(FLAG_DELAY_REQUESTED)) {
_optionDelay = (int)DataHelper.fromLong(buffer, cur, 2);
setOptionalDelay((int)DataHelper.fromLong(buffer, cur, 2));
cur += 2;
}
if (isFlagSet(FLAG_FROM_INCLUDED)) {
_optionFrom = new Destination();
Destination optionFrom = new Destination();
try {
cur += _optionFrom.readBytes(buffer, cur);
cur += optionFrom.readBytes(buffer, cur);
setOptionalFrom(optionFrom);
} catch (DataFormatException dfe) {
throw new IllegalArgumentException("Bad from field: " + dfe.getMessage());
}
}
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) {
_optionMaxSize = (int)DataHelper.fromLong(buffer, cur, 2);
setOptionalMaxSize((int)DataHelper.fromLong(buffer, cur, 2));
cur += 2;
}
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
_optionSignature = new Signature();
Signature optionSignature = new Signature();
byte buf[] = new byte[Signature.SIGNATURE_BYTES];
System.arraycopy(buffer, cur, buf, 0, Signature.SIGNATURE_BYTES);
_optionSignature.setData(buf);
optionSignature.setData(buf);
setOptionalSignature(optionSignature);
cur += Signature.SIGNATURE_BYTES;
}
}
@ -518,7 +513,12 @@ public class Packet {
}
boolean ok = ctx.dsa().verifySignature(_optionSignature, buffer, 0, size, from.getSigningPublicKey());
if (!ok) {
ctx.logManager().getLog(Packet.class).error("Signature failed on " + toString(), new Exception("moo"));
Log l = ctx.logManager().getLog(Packet.class);
l.error("Signature failed on " + toString(), new Exception("moo"));
if (false) {
l.error(Base64.encode(buffer, 0, size));
l.error("Signature: " + Base64.encode(_optionSignature.getData()));
}
}
return ok;
}
@ -533,6 +533,12 @@ public class Packet {
setFlag(FLAG_SIGNATURE_INCLUDED);
int size = writePacket(buffer, offset, false);
_optionSignature = ctx.dsa().sign(buffer, offset, size, key);
if (false) {
Log l = ctx.logManager().getLog(Packet.class);
l.error("Signing: " + toString());
l.error(Base64.encode(buffer, 0, size));
l.error("Signature: " + Base64.encode(_optionSignature.getData()));
}
// jump into the signed data and inject the signature where we
// previously placed a bunch of zeroes
int signatureOffset = offset
@ -566,7 +572,7 @@ public class Packet {
else
buf.append('\t');
buf.append(toFlagString());
buf.append(" ACK ").append(_ackThrough);
buf.append(" ACK ").append(getAckThrough());
if (_nacks != null) {
buf.append(" NACK");
for (int i = 0; i < _nacks.length; i++) {
@ -578,11 +584,8 @@ public class Packet {
return buf;
}
static final String toId(byte id[]) {
if (id == null)
return Base64.encode(STREAM_ID_UNKNOWN);
else
return Base64.encode(id);
static final String toId(long id) {
return Base64.encode(DataHelper.toLong(4, id));
}
private final String toFlagString() {

View File

@ -22,19 +22,26 @@ public class PacketHandler {
private I2PAppContext _context;
private Log _log;
private int _lastDelay;
private int _dropped;
public PacketHandler(I2PAppContext ctx, ConnectionManager mgr) {
_manager = mgr;
_context = ctx;
_dropped = 0;
_log = ctx.logManager().getLog(PacketHandler.class);
_lastDelay = _context.random().nextInt(30*1000);
}
private boolean choke(Packet packet) {
if (false) {
// artificial choke: 2% random drop and a 0-30s
private boolean choke(Packet packet) {
if (true) return true;
//if ( (_dropped == 0) && true ) { //&& (_manager.getSent() <= 0) ) {
// _dropped++;
// return false;
//}
if (true) {
// artificial choke: 2% random drop and a 0-5s
// random tiered delay from 0-30s
if (_context.random().nextInt(100) >= 95) {
if (_context.random().nextInt(100) >= 98) {
displayPacket(packet, "DROP", null);
return false;
} else {
@ -42,7 +49,7 @@ public class PacketHandler {
/*
int delay = _context.random().nextInt(5*1000);
*/
int delay = _context.random().nextInt(6*1000);
int delay = _context.random().nextInt(1*1000);
int delayFactor = _context.random().nextInt(100);
if (delayFactor > 80) {
if (delayFactor > 98)
@ -90,14 +97,12 @@ public class PacketHandler {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("packet received: " + packet);
byte sendId[] = packet.getSendStreamId();
if (!isNonZero(sendId))
sendId = null;
long sendId = packet.getSendStreamId();
Connection con = (sendId != null ? _manager.getConnectionByInboundId(sendId) : null);
Connection con = (sendId > 0 ? _manager.getConnectionByInboundId(sendId) : null);
if (con != null) {
receiveKnownCon(con, packet);
displayPacket(packet, "RECV", "wsize " + con.getOptions().getWindowSize());
displayPacket(packet, "RECV", "wsize " + con.getOptions().getWindowSize() + " rto " + con.getOptions().getRTO());
} else {
receiveUnknownCon(packet, sendId);
displayPacket(packet, "UNKN", null);
@ -120,9 +125,9 @@ public class PacketHandler {
private void receiveKnownCon(Connection con, Packet packet) {
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
if (packet.getSendStreamId() != null) {
if (packet.getSendStreamId() > 0) {
receivePing(packet);
} else if (packet.getReceiveStreamId() != null) {
} else if (packet.getReceiveStreamId() > 0) {
receivePong(packet);
} else {
if (_log.shouldLog(Log.WARN))
@ -155,17 +160,17 @@ public class PacketHandler {
_log.warn("Received forged reset for " + con, ie);
}
} else {
if ( (con.getSendStreamId() == null) ||
if ( (con.getSendStreamId() <= 0) ||
(DataHelper.eq(con.getSendStreamId(), packet.getReceiveStreamId())) ) {
byte oldId[] =con.getSendStreamId();
long oldId =con.getSendStreamId();
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) // con fully established, w00t
con.setSendStreamId(packet.getReceiveStreamId());
try {
con.getPacketHandler().receivePacket(packet, con);
} catch (I2PException ie) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received forged packet for " + con + ": " + packet, ie);
if (_log.shouldLog(Log.ERROR))
_log.error("Received forged packet for " + con + "/" + oldId + ": " + packet, ie);
con.setSendStreamId(oldId);
}
} else if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
@ -207,11 +212,11 @@ public class PacketHandler {
_manager.getPacketQueue().enqueue(reply);
}
private void receiveUnknownCon(Packet packet, byte sendId[]) {
private void receiveUnknownCon(Packet packet, long sendId) {
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
if (packet.getSendStreamId() != null) {
if (packet.getSendStreamId() > 0) {
receivePing(packet);
} else if (packet.getReceiveStreamId() != null) {
} else if (packet.getReceiveStreamId() > 0) {
receivePong(packet);
} else {
if (_log.shouldLog(Log.WARN))
@ -221,7 +226,7 @@ public class PacketHandler {
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Packet received on an unknown stream (and not an ECHO): " + packet);
if (sendId == null) {
if (sendId <= 0) {
Connection con = _manager.getConnectionByOutboundId(packet.getReceiveStreamId());
if (con != null) {
if (con.getAckedPackets() <= 0) {
@ -250,7 +255,7 @@ public class PacketHandler {
}
_log.warn("Packet belongs to no other cons: " + packet + " connections: "
+ buf.toString() + " sendId: "
+ (sendId != null ? Base64.encode(sendId) : " unknown"));
+ (sendId > 0 ? Packet.toId(sendId) : " unknown"));
}
packet.releasePayload();
}
@ -282,25 +287,7 @@ public class PacketHandler {
_manager.receivePong(packet.getReceiveStreamId());
}
private static final boolean isValidMatch(byte conStreamId[], byte packetStreamId[]) {
if ( (conStreamId == null) || (packetStreamId == null) ||
(conStreamId.length != packetStreamId.length) )
return false;
boolean nonZeroFound = false;
for (int i = 0; i < conStreamId.length; i++) {
if (conStreamId[i] != packetStreamId[i]) return false;
if (conStreamId[i] != 0x0) nonZeroFound = true;
}
return nonZeroFound;
}
private static final boolean isNonZero(byte[] b) {
boolean nonZeroFound = false;
for (int i = 0; b != null && i < b.length; i++) {
if (b[i] != 0x0)
nonZeroFound = true;
}
return nonZeroFound;
private static final boolean isValidMatch(long conStreamId, long packetStreamId) {
return ( (conStreamId == packetStreamId) && (conStreamId != 0) );
}
}

View File

@ -5,7 +5,6 @@ import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
@ -27,7 +26,6 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
private long _ackOn;
private long _cancelledOn;
private SimpleTimer.TimedEvent _resendEvent;
private ByteCache _cache = ByteCache.getInstance(128, MAX_PAYLOAD_SIZE);
public PacketLocal(I2PAppContext ctx, Destination to) {
this(ctx, to, null);
@ -71,8 +69,11 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
public void prepare() {
if (_connection != null)
_connection.getInputStream().updateAcks(this);
if (_numSends > 0) // so we can debug to differentiate resends
if (_numSends > 0) {
// so we can debug to differentiate resends
setOptionalDelay(_numSends * 1000);
setFlag(FLAG_DELAY_REQUESTED);
}
}
public long getCreatedOn() { return _createdOn; }

View File

@ -125,7 +125,7 @@ class PacketQueue {
_log.debug(msg);
}
Connection c = packet.getConnection();
String suffix = (c != null ? "wsize " + c.getOptions().getWindowSize() : null);
String suffix = (c != null ? "wsize " + c.getOptions().getWindowSize() + " rto " + c.getOptions().getRTO() : null);
_connectionManager.getPacketHandler().displayPacket(packet, "SEND", suffix);
}

View File

@ -41,7 +41,7 @@ class SchedulerClosed extends SchedulerImpl {
(!con.getResetReceived()) &&
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
boolean conTimeout = (con.getOptions().getConnectTimeout() < con.getLifetime()) &&
con.getSendStreamId() == null &&
con.getSendStreamId() <= 0 &&
con.getLifetime() < Connection.DISCONNECT_TIMEOUT;
return (ok || conTimeout);
}

View File

@ -36,7 +36,7 @@ class SchedulerDead extends SchedulerImpl {
boolean nothingLeftToDo = (con.getDisconnectScheduledOn() > 0) &&
(timeSinceClose >= Connection.DISCONNECT_TIMEOUT);
boolean timedOut = (con.getOptions().getConnectTimeout() < con.getLifetime()) &&
con.getSendStreamId() == null &&
con.getSendStreamId() <= 0 &&
con.getLifetime() >= Connection.DISCONNECT_TIMEOUT;
return nothingLeftToDo || timedOut;
}

View File

@ -31,7 +31,7 @@ class SchedulerPreconnect extends SchedulerImpl {
public boolean accept(Connection con) {
return (con != null) &&
(con.getSendStreamId() == null) &&
(con.getSendStreamId() <= 0) &&
(con.getLastSendId() < 0);
}

View File

@ -19,7 +19,7 @@ class SchedulerReceived extends SchedulerImpl {
public boolean accept(Connection con) {
return (con != null) &&
(con.getLastSendId() < 0) &&
(con.getSendStreamId() != null);
(con.getSendStreamId() > 0);
}
public void eventOccurred(Connection con) {

4
apps/susidns/readme.txt Normal file
View File

@ -0,0 +1,4 @@
The src/ dir contains susidns 0.13 retrieved from http://susi.i2p/ on 2005/09/15
The contents are released under GPL. Please see http://susi.i2p/ for more info
The paths in the src/build.xml were updated to reference jars in the i2p
source tree.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<display-name>susidns</display-name>
<!-- precompiled servlets -->
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="susidns" default="all" basedir=".">
<property name="jetty" value="../../jetty/" />
<property name="project" value="susidns" />
<property name="src" value="java/src" />
<property name="bin" value="./WEB-INF/classes" />
<property name="lib" value="${jetty}/jettylib" />
<property name="tmp" value="./tmp" />
<property name="jsp" value="./jsp" />
<path id="cp">
<pathelement path="${classpath}" />
<pathelement location="${bin}" />
<pathelement location="${lib}/javax.servlet.jar"/>
<pathelement location="${lib}/org.mortbay.jetty.jar"/>
<pathelement location="WEB-INF/lib/jstl.jar" />
<pathelement location="WEB-INF/lib/standard.jar" />
<pathelement location="${lib}/jasper-compiler.jar" />
<pathelement location="${lib}/jasper-runtime.jar" />
<pathelement location="${lib}/javax.servlet.jar" />
<pathelement location="${lib}/commons-logging.jar" />
<pathelement location="${lib}/commons-el.jar" />
<pathelement location="${lib}/ant.jar" />
<pathelement location="../../../core/java/build/i2p.jar" />
</path>
<target name="compile">
<mkdir dir="${bin}" />
<javac debug="true" deprecation="on" source="1.3" target="1.3"
classpathref="cp" destdir="${bin}" srcdir="${src}" includes="**/*.java" />
</target>
<target name="precompilejsp">
<delete file="WEB-INF/web-fragment.xml" />
<delete file="WEB-INF/web-out.xml" />
<mkdir dir="${tmp}" />
<java classname="org.apache.jasper.JspC" fork="true" classpathref="cp">
<arg value="-d" />
<arg value="WEB-INF/classes" />
<arg value="-v" />
<arg value="-p" />
<arg value="i2p.susi.dns.jsp" />
<arg value="-webinc" />
<arg value="WEB-INF/web-fragment.xml" />
<arg value="-webapp" />
<arg value="./jsp" />
</java>
<javac debug="true" deprecation="on" source="1.3" target="1.3"
destdir="${bin}" srcdir="./WEB-INF/classes" includes="**/*.java" classpathref="cp">
</javac>
<copy file="WEB-INF/web-template.xml" tofile="WEB-INF/web-out.xml" />
<loadfile property="jspc.web.fragment" srcfile="WEB-INF/web-fragment.xml" />
<replace file="WEB-INF//web-out.xml">
<replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" />
</replace>
</target>
<target name="all" depends="compile,precompilejsp,war"/>
<target name="war">
<war destfile="${project}.war" webxml="WEB-INF/web-out.xml">
<fileset dir=".">
<include name="WEB-INF/**/*.class"/>
<include name="WEB-INF/lib/*.jar"/>
<include name="${src}/**/*.java"/>
<include name="jsp/*.jsp"/>
<include name="images/*.png"/>
<include name="css.css"/>
<include name="index.html"/>
<include name="build.xml"/>
<include name="WEB-INF/web-template.xml"/>
<include name="WEB-INF/web-out.xml"/>
<include name="WEB-INF/classes/${project}.properties"/>
</fileset>
</war>
</target>
<target name="clean">
<delete file="susidns.war" />
<delete>
<fileset dir="." includes="**/*.class" />
<fileset dir="." includes="tmp" />
</delete>
</target>
<target name="distclean" depends="clean" />
</project>

94
apps/susidns/src/css.css Normal file
View File

@ -0,0 +1,94 @@
p {
font-family:Verdana,Tahoma,Arial,Helvetica;
color:black;
line-height:12pt;
margin-left:5mm;
margin-right:5mm;
font-size:10pt;
}
span.addrhlpr {
font-size:7pt;
}
h3 {
font-family:Verdana,Tahoma,Arial,Helvetica;
color:black;
font-size:12pt;
letter-spacing:2pt;
line-height:18pt;
font-weight:bold;
}
body {
background-color: white;
color:black;
}
a {
color:#327BBF;
text-decoration:none;
}
a:hover {
text-decoration:underline;
}
th {
font-family:Verdana,Tahoma,Arial,Helvetica;
color:black;
line-height:12pt;
margin-left:5mm;
margin-right:5mm;
font-size:10pt;
}
td {
font-family:Verdana,Tahoma,Arial,Helvetica;
color:black;
line-height:12pt;
margin-left:5mm;
margin-right:5mm;
font-size:10pt;
vertical-align:center;
}
li {
font-family:Verdana,Tahoma,Arial,Helvetica;
color:black;
line-height:12pt;
margin-left:5mm;
margin-right:5mm;
font-size:10pt;
}
tr.list1 {
background-color:#E0E0E0;
}
tr.list0 {
background-color:white;
}
p.messages {
background-color:#92CAFF;
color:#327BBF;
color:black;
border-style:dotted;
padding-top: 5mm;
padding-right: 5mm;
padding-bottom: 5mm;
padding-left: 5mm;
}
#help {
border-style:dotted;
padding-top: 5mm;
padding-right: 5mm;
padding-bottom: 5mm;
padding-left: 5mm;
}
p.footer {
font-size:7pt;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="refresh" content="0;url=index.jsp" />
<title>susidns</title>
</head>
<body>
<a href="index.jsp">Enter</a>
</body>
</html>

View File

@ -0,0 +1,61 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.1 $
*/
package i2p.susi.dns;
public class AddressBean
{
private String name, destination;
public AddressBean()
{
}
public AddressBean(String name, String destination)
{
this.name = name;
this.destination = destination;
}
public String getDestination()
{
return destination;
}
public void setDestination(String destination)
{
this.destination = destination;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}

View File

@ -0,0 +1,44 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.2 $
*/
package i2p.susi.dns;
import java.util.Comparator;
public class AddressByNameSorter implements Comparator
{
public int compare(Object arg0, Object arg1)
{
AddressBean a = (AddressBean)arg0;
AddressBean b = (AddressBean)arg1;
if( a == null )
return 1;
if( b == null )
return -1;
return a.getName().compareToIgnoreCase(b.getName());
}
}

View File

@ -0,0 +1,262 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.7 $
*/
package i2p.susi.dns;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;
public class AddressbookBean
{
private String book, action, serial, lastSerial, filter, search, hostname, destination;
private Properties properties, addressbook;
private int trClass;
private LinkedList deletionMarks;
private static Comparator sorter;
static {
sorter = new AddressByNameSorter();
}
public String getSearch() {
return search;
}
public void setSearch(String search) {
this.search = search;
}
public boolean isHasFilter()
{
return filter != null && filter.length() > 0;
}
public void setTrClass(int trClass) {
this.trClass = trClass;
}
public int getTrClass() {
trClass = 1 - trClass;
return trClass;
}
public boolean isIsEmpty()
{
return ! isNotEmpty();
}
public boolean isNotEmpty()
{
return addressbook != null && addressbook.size() > 0;
}
public AddressbookBean()
{
properties = new Properties();
deletionMarks = new LinkedList();
}
private long configLastLoaded = 0;
private void loadConfig()
{
long currentTime = System.currentTimeMillis();
if( properties.size() > 0 && currentTime - configLastLoaded < 10000 )
return;
try {
properties.clear();
properties.load( new FileInputStream( ConfigBean.configFileName ) );
configLastLoaded = currentTime;
}
catch (Exception e) {
Debug.debug( e.getClass().getName() + ": " + e.getMessage() );
}
}
public String getFileName()
{
loadConfig();
String filename = properties.getProperty( getBook() + "_addressbook" );
return ConfigBean.addressbookPrefix + filename;
}
private Object[] entries;
public Object[] getEntries()
{
return entries;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getBook()
{
if( book == null || ( book.compareToIgnoreCase( "master" ) != 0 &&
book.compareToIgnoreCase( "router" ) != 0 ) &&
book.compareToIgnoreCase( "published" ) != 0 )
book = "master";
return book;
}
public void setBook(String book) {
this.book = book;
}
public String getSerial() {
lastSerial = "" + Math.random();
action = null;
return lastSerial;
}
public void setSerial(String serial) {
this.serial = serial;
}
public String getMessages()
{
loadConfig();
String message = "";
if( action != null ) {
if( lastSerial != null && serial != null && serial.compareTo( lastSerial ) == 0 ) {
boolean changed = false;
if( action.compareToIgnoreCase( "add") == 0 ) {
if( addressbook != null && hostname != null && destination != null ) {
addressbook.put( hostname, destination );
changed = true;
message += "Destination added.<br/>";
}
}
if( action.compareToIgnoreCase( "delete" ) == 0 ) {
Iterator it = deletionMarks.iterator();
int deleted = 0;
while( it.hasNext() ) {
String name = (String)it.next();
addressbook.remove( name );
changed = true;
deleted++;
}
if( changed ) {
message += "" + deleted + " destination(s) deleted.<br/>";
}
}
if( changed ) {
try {
save();
message += "Addressbook saved.<br/>";
} catch (Exception e) {
Debug.debug( e.getClass().getName() + ": " + e.getMessage() );
message += "ERROR: Could not write addressbook file.<br/>";
}
}
}
else {
message += "Invalid nonce. Are you being spoofed?";
}
}
action = null;
addressbook = new Properties();
try {
addressbook.load( new FileInputStream( getFileName() ) );
LinkedList list = new LinkedList();
Enumeration e = addressbook.keys();
while( e.hasMoreElements() ) {
String name = (String)e.nextElement();
String destination = addressbook.getProperty( name );
if( filter != null && filter.length() > 0 ) {
if( filter.compareTo( "0-9" ) == 0 ) {
char first = name.charAt(0);
if( first < '0' || first > '9' )
continue;
}
else if( ! name.toLowerCase().startsWith( filter.toLowerCase() ) ) {
continue;
}
}
if( search != null && search.length() > 0 ) {
if( name.indexOf( search ) == -1 ) {
continue;
}
}
list.addLast( new AddressBean( name, destination ) );
}
Object array[] = list.toArray();
Arrays.sort( array, sorter );
entries = array;
}
catch (Exception e) {
Debug.debug( e.getClass().getName() + ": " + e.getMessage() );
}
if( message.length() > 0 )
message = "<p class=\"messages\">" + message + "</p>";
return message;
}
private void save() throws IOException
{
String filename = properties.getProperty( getBook() + "_addressbook" );
addressbook.store( new FileOutputStream( ConfigBean.addressbookPrefix + filename ), null );
}
public String getFilter() {
return filter;
}
public boolean isMaster()
{
return getBook().compareToIgnoreCase( "master" ) == 0;
}
public boolean isRouter()
{
return getBook().compareToIgnoreCase( "router" ) == 0;
}
public void setFilter(String filter) {
if( filter != null && ( filter.length() == 0 || filter.compareToIgnoreCase( "none" ) == 0 ) ) {
filter = null;
search = null;
}
this.filter = filter;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getHostname() {
return hostname;
}
public void setResetDeletionMarks( String dummy ) {
deletionMarks.clear();
}
public void setMarkedForDeletion( String name ) {
deletionMarks.addLast( name );
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
}

View File

@ -0,0 +1,157 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.3 $
*/
package i2p.susi.dns;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
public class ConfigBean implements Serializable {
/*
* as this is not provided as constant in addressbook, we define it here
*/
public static String addressbookPrefix = "addressbook/";
public static String configFileName = addressbookPrefix + "config.txt";
private String action, config;
private String serial, lastSerial;
private boolean saved;
public static String getConfigFileName() {
return configFileName;
}
public String getfileName() {
return getConfigFileName();
}
public boolean isSaved() {
return saved;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getConfig()
{
if( config != null )
return config;
reload();
return config;
}
private void reload()
{
File file = new File( configFileName );
if( file != null && file.isFile() ) {
StringBuffer buf = new StringBuffer();
try {
BufferedReader br = new BufferedReader( new FileReader( file ) );
String line;
while( ( line = br.readLine() ) != null ) {
buf.append( line );
buf.append( "\n" );
}
config = buf.toString();
saved = true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void save()
{
File file = new File( configFileName );
try {
PrintWriter out = new PrintWriter( new FileOutputStream( file ) );
out.print( config );
out.flush();
out.close();
saved = true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setConfig(String config) {
this.config = config;
this.saved = false;
/*
* as this is a property file we need a newline at the end of the last line!
*/
if( ! this.config.endsWith( "\n" ) ) {
this.config += "\n";
}
}
public String getMessages() {
String message = "";
if( action != null ) {
if( lastSerial != null && serial != null && serial.compareTo( lastSerial ) == 0 ) {
if( action.compareToIgnoreCase( "save") == 0 ) {
save();
message = "Configuration saved.";
}
else if( action.compareToIgnoreCase( "reload") == 0 ) {
reload();
message = "Configuration reloaded.";
}
}
else {
message = "Invalid nonce. Are you being spoofed?";
}
}
if( message.length() > 0 )
message = "<p class=\"messages\">" + message + "</p>";
return message;
}
public String getSerial()
{
lastSerial = "" + Math.random();
action = null;
return lastSerial;
}
public void setSerial(String serial ) {
this.serial = serial;
}
}

View File

@ -0,0 +1,56 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.1 $
*/
package i2p.susi.dns;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
public class Debug
{
private static Log _log;
private static I2PAppContext _context;
static
{
try {
_context = I2PAppContext.getGlobalContext(); // new I2PAppContext();
_log = _context.logManager().getLog(Debug.class);
}
catch( NoClassDefFoundError e ) {
_context = null;
_log = null;
}
}
public static void debug( String msg )
{
if( _log != null ) {
_log.debug( msg );
}
else {
System.err.println( "DEBUG: [susidns] " + msg );
}
}
}

View File

@ -0,0 +1,171 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.3 $
*/
package i2p.susi.dns;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;
public class SubscriptionsBean
{
private String action, fileName, content, serial, lastSerial;
private boolean saved;
Properties properties;
public SubscriptionsBean()
{
properties = new Properties();
}
private long configLastLoaded = 0;
private void loadConfig()
{
long currentTime = System.currentTimeMillis();
if( properties.size() > 0 && currentTime - configLastLoaded < 10000 )
return;
try {
properties.clear();
properties.load( new FileInputStream( ConfigBean.configFileName ) );
configLastLoaded = currentTime;
}
catch (Exception e) {
Debug.debug( e.getClass().getName() + ": " + e.getMessage() );
}
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getFileName()
{
loadConfig();
fileName = ConfigBean.addressbookPrefix + properties.getProperty( "subscriptions", "subscriptions.txt" );
return fileName;
}
private void reload()
{
File file = new File( getFileName() );
if( file != null && file.isFile() ) {
StringBuffer buf = new StringBuffer();
try {
BufferedReader br = new BufferedReader( new FileReader( file ) );
String line;
while( ( line = br.readLine() ) != null ) {
buf.append( line );
buf.append( "\n" );
}
content = buf.toString();
saved = true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void save()
{
File file = new File( getFileName() );
try {
PrintWriter out = new PrintWriter( new FileOutputStream( file ) );
out.print( content );
out.flush();
out.close();
saved = true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getMessages() {
String message = "";
if( action != null ) {
if( lastSerial != null && serial != null && serial.compareTo( lastSerial ) == 0 ) {
if( action.compareToIgnoreCase( "save") == 0 ) {
save();
message = "Subscriptions saved.";
}
else if( action.compareToIgnoreCase( "reload") == 0 ) {
reload();
message = "Subscriptions reloaded.";
}
}
else {
message = "Invalid nonce. Are you being spoofed?";
}
}
if( message.length() > 0 )
message = "<p class=\"messages\">" + message + "</p>";
return message;
}
public String getSerial()
{
lastSerial = "" + Math.random();
action = null;
return lastSerial;
}
public void setSerial(String serial ) {
this.serial = serial;
}
public void setContent(String content) {
this.content = content;
this.saved = false;
/*
* as this is a property file we need a newline at the end of the last line!
*/
if( ! this.content.endsWith( "\n" ) ) {
this.content += "\n";
}
}
public String getContent()
{
if( content != null )
return content;
reload();
return content;
}
}

View File

@ -0,0 +1,39 @@
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.4 $
*/
package i2p.susi.dns;
public class VersionBean {
private static String version = "0.4";
private static String url = "http://susi.i2p/?i2paddresshelper=T2DU1KAz3meB0B53U8Y06-I7vHR7XmC0qXAJfLW6b-1L1FVKoySRZz4xazHAwyv2xtRpvKrv6ukLm1tThEW0zQWtZPtX8G6KkzMibD8t7IS~4yw-9VkBtUydyYfsX08AK3v~-egSW8HCXTdyIJVtrETJb337VDUHW-7D4L1JLbwSH4if2ooks6yFTrljK5aVMi-16dZOVvmoyJc3jBqSdK6kraO4gW5-vHTmbLwL498p9nug1KOg1DqgN2GeU5X1QlVrlpFb~IIfdP~O8NT7u-LAjW3jSJsMbLDHMSYTIhC7xmJIiBoi-qk8p6TLynAmvJ7HRvbx4N1EB-uJHyD16wsZkkHyEOfmXbj0ZqLyKEGb3thPwCz-M9v~c2Qt3WbwjXJAtHpjlHkdJ4Fg91cX2oak~JoapnPf6Syw8hko5syf6VVoCYLnrrYyM8oGl8mLclHkj~VCidQNqMSM74IhrHfK6HmRikqtZBexb5M6wfMTTqBvaHURdD21GOpFKYBUAAAA";
public String getVersion() {
return version;
}
public String getUrl() {
return url;
}
}

View File

@ -0,0 +1,168 @@
<%
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.1 $
*/
%>
<%@ page contentType="text/html"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<jsp:useBean id="version" class="i2p.susi.dns.VersionBean" scope="application" />
<jsp:useBean id="book" class="i2p.susi.dns.AddressbookBean" scope="session" />
<jsp:setProperty name="book" property="*" />
<jsp:setProperty name="book" property="resetDeletionMarks" value="1"/>
<c:forEach items="${paramValues.checked}" var="checked">
<jsp:setProperty name="book" property="markedForDeletion" value="${checked}"/>
</c:forEach>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>${book.book} addressbook - susidns v${version.version}</title>
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<div id="logo">
<img src="images/logo.png" alt="susidns logo" border="0"/>
</div>
<div id="navi">
<p>addressbooks
<a href="addressbook.jsp?book=master">master</a> |
<a href="addressbook.jsp?book=router">router</a> |
<a href="addressbook.jsp?book=published">published</a> *
<a href="subscriptions.jsp">subscriptions</a> *
<a href="config.jsp">configuration</a>
</p>
</div>
<div id="headline">
<h3>${book.book} addressbook at ${book.fileName}</h3>
</div>
<div id="messages">${book.messages}</div>
<div id="filter">
<p>Filter: <a href="addressbook.jsp?filter=a">a</a>
<a href="addressbook.jsp?filter=b">b</a>
<a href="addressbook.jsp?filter=c">c</a>
<a href="addressbook.jsp?filter=d">d</a>
<a href="addressbook.jsp?filter=e">e</a>
<a href="addressbook.jsp?filter=f">f</a>
<a href="addressbook.jsp?filter=g">g</a>
<a href="addressbook.jsp?filter=h">h</a>
<a href="addressbook.jsp?filter=i">i</a>
<a href="addressbook.jsp?filter=j">j</a>
<a href="addressbook.jsp?filter=k">k</a>
<a href="addressbook.jsp?filter=l">l</a>
<a href="addressbook.jsp?filter=m">m</a>
<a href="addressbook.jsp?filter=n">n</a>
<a href="addressbook.jsp?filter=o">o</a>
<a href="addressbook.jsp?filter=p">p</a>
<a href="addressbook.jsp?filter=q">q</a>
<a href="addressbook.jsp?filter=r">r</a>
<a href="addressbook.jsp?filter=s">s</a>
<a href="addressbook.jsp?filter=t">t</a>
<a href="addressbook.jsp?filter=u">u</a>
<a href="addressbook.jsp?filter=v">v</a>
<a href="addressbook.jsp?filter=w">w</a>
<a href="addressbook.jsp?filter=x">x</a>
<a href="addressbook.jsp?filter=y">y</a>
<a href="addressbook.jsp?filter=z">z</a>
<a href="addressbook.jsp?filter=0-9">0-9</a>
<a href="addressbook.jsp?filter=none">all</a></p>
<c:if test="${book.hasFilter}">
<p>Current filter: ${book.filter}</p>
</c:if>
</div>
<form method="POST" action="addressbook.jsp">
<div id="search">
<table><tr>
<td class="search">Search: <input type="text" name="search" value="${book.search}" size="20" /></td>
<td class="search"><input type="image" src="images/search.png" name="submitsearch" value="search" alt="Search" /></td>
</tr>
</table>
</div>
</form>
<form method="POST" action="addressbook.jsp">
<input type="hidden" name="serial" value="${book.serial}"/>
<c:if test="${book.notEmpty}">
<div id="book">
<jsp:setProperty name="book" property="trClass" value="0" />
<table class="book" cellspacing="0" cellpadding="5">
<tr class="head">
<c:if test="${book.master || book.router}">
<th>&nbsp;</th>
</c:if>
<th>Name</th>
<th>Destination</th>
</tr>
<c:forEach items="${book.entries}" var="addr">
<tr class="list${book.trClass}">
<c:if test="${book.master || book.router}">
<td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" alt="Mark for deletion"></td>
</c:if>
<td class="names"><a href="http://${addr.name}/">${addr.name}</a> -
<span class="addrhlpr"><a href="http://${addr.name}/?i2paddresshelper=${addr.destination}">(addrhlpr)</a></span>
</td>
<td class="destinations"><input type="text" name="dest_${addr.name}" value="${addr.destination}" size="20"></td>
</tr>
</c:forEach>
</table>
</div>
<c:if test="${book.master || book.router}">
<div id="buttons">
<p class="buttons"><input type="image" name="action" value="delete" src="images/delete.png" alt="Delete checked" />
</p>
</div>
</c:if>
</c:if>
<c:if test="${book.isEmpty}">
<div id="book">
<p class="book">The ${book.book} addressbook is empty.</p>
</div>
</c:if>
<div id="add">
<p class="add">
<h3>Add new destination:</h3>
Hostname: <input type="text" name="hostname" value="" size="20"> Destination: <input type="text" name="destination" value="" size="20"><br/>
<input type="image" name="action" value="add" src="images/add.png" alt="Add destination" />
</p>
</div>
</form>
<div id="footer">
<p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005</p>
</div>
</body>
</html>

View File

@ -0,0 +1,96 @@
<%
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.14 $
*/
%>
<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="version" class="i2p.susi.dns.VersionBean" scope="application"/>
<jsp:useBean id="cfg" class="i2p.susi.dns.ConfigBean" scope="session"/>
<jsp:setProperty name="cfg" property="*" />
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>configuration - susidns v${version.version}</title>
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<div id="logo">
<img src="images/logo.png" alt="susidns logo" border="0"/>
</div>
<div id="navi">
<p>
addressbooks
<a href="addressbook.jsp?book=master">master</a> |
<a href="addressbook.jsp?book=router">router</a> |
<a href="addressbook.jsp?book=published">published</a> *
<a href="subscriptions.jsp">subscriptions</a> *
<a href="config.jsp">configuration</a>
</p>
</div>
<div id="headline">
<h3>${cfg.fileName}</h3>
</div>
<div id="messages">${cfg.messages}</div>
<form method="POST" action="config.jsp">
<div id="config">
<input type="hidden" name="serial" value="${cfg.serial}" />
<textarea name="config" rows="10" cols="80">${cfg.config}</textarea>
</div>
<div id="buttons">
<input type="image" src="images/save.png" name="action" value="save" alt="Save Config"/>
<input type="image" src="images/reload.png" name="action" value="reload" alt="Reload Config"/>
</div>
</form>
<div id="help">
<h3>Hints</h3>
<ol>
<li>All file or directory paths here are relative to the addressbooks working directory, which normally
is located at $I2P/addressbook/.</li>
<li>If you want to manually add lines to an addressbook, add them to the master addressbook. The router
addressbook and the published addressbook are overwritten by the addressbook application.</li>
<li><b>Important:</b>When you publish your addressbook, <b>ALL</b> destinations appear there, even those
from your master addressbook. Unfortunately the master addressbook points to your userhosts.txt, which was
used for private destinations before. So if you want to keep the destinations in your userhosts.txt secret,
please change the master addressbook to a different file before turning on addressbook publishing.</li>
</ol>
<h3>Options</h3>
<ul>
<li><b>subscriptions</b> - file containing the list of subscriptions URLs (no need to change)</li>
<li><b>update_delay</b> - update interval in hours (no need to change)</li>
<li><b>published_addressbook</b> - your public hosts.txt file (choose a path within your webserver document root)</li>
<li><b>router_addressbook</b> - your hosts.txt (no need to change)</li>
<li><b>master_addressbook</b> - your personal addressbook, it gets never overwritten by the addressbook</li>
<li><b>proxy_port</b> - http port for your eepProxy (no need to change)</li>
<li><b>proxy_host</b> - hostname for your eepProxy (no need to change)</li>
<li><b>should_publish</b> - true/false whether to write the published addressbook</li>
<li><b>etags</b> - file containing the etags header from the fetched subscription URLs (no need to change)</li>
<li><b>last_modified</b> - file containing the modification timestamp for each fetched subscription URL (no need to change)</li>
<li><b>log</b> - file to log activity to (change to /dev/null if you like)</li>
</ul>
</div>
<div id="footer">
<p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005 </p>
</div>
</body>
</html>

View File

@ -0,0 +1,80 @@
<%
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.1 $
*/
%>
<%@ page contentType="text/html"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<jsp:useBean id="version" class="i2p.susi.dns.VersionBean" scope="application" />
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>introduction - susidns v${version.version}</title>
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<div id="logo">
<img src="images/logo.png" alt="susidns logo" border="0"/>
</div>
<div id="navi">
<p>addressbooks
<a href="addressbook.jsp?book=master">master</a> |
<a href="addressbook.jsp?book=router">router</a> |
<a href="addressbook.jsp?book=published">published</a> *
<a href="subscriptions.jsp">subscriptions</a> *
<a href="config.jsp">configuration</a>
</p>
</div>
<div id="content">
<h3>Huh? what addressbook?</h3>
<p>
The addressbook application is part of your i2p installation. It regularly updates your hosts.txt file
from distributed sources. It keeps your hosts.txt up to date, so it automatically contains all new
eepsites announced on <a href="http://orion.i2p">orion</a>
or in the <a href="http://forum.i2p/viewforum.php?f=16">forum</a>.
</p>
<p>
(To speak the truth: In its default configuration the addressbook does not poll
orion, but dev.i2p only. Subscribing to <a href="http://orion.i2p">orion</a> is an easy task,
just add <a href="http://orion.i2p/hosts.txt">http://orion.i2p/hosts.txt</a> to your <a href="subscriptions.jsp">subscriptions</a> file.)
</p>
<p>If you have questions about naming in i2p, there is an excellent <a href="http://forum.i2p.net/viewtopic.php?t=134">introduction</a>
from duck in the forum.</p>
<h3>How does the addressbook work?</h3>
<p>The addressbook application regularly (normally once per hour) polls your subscriptions and merges their content
into your so called router addressbook (normally your plain hosts.txt). Then it merges your so called master addressbook (normally
your userhosts.txt) into the router addressbook as well. If configured the router addressbook is now written to the so published addressbook,
which is a publicly available copy of your hosts.txt somewhere in your eepsites document root. (Yes, this means that, with activated publication,
your once private keys from userhosts.txt now are publicly available for everybody.)
</p>
<p><img src="images/how.png" border="0" alt="addressbook working scheme"/></p>
</div>
<div id="footer">
<p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005</p>
</div>
</body>
</html>

View File

@ -0,0 +1,78 @@
<%
/*
* Created on Sep 02, 2005
*
* This file is part of susidns project, see http://susi.i2p/
*
* Copyright (C) 2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.7 $
*/
%>
<%@ page contentType="text/html"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<jsp:useBean id="version" class="i2p.susi.dns.VersionBean" scope="application" />
<jsp:useBean id="subs" class="i2p.susi.dns.SubscriptionsBean" scope="session" />
<jsp:setProperty name="subs" property="*" />
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>subscriptions - susidns v${version.version}</title>
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<div id="logo">
<img src="images/logo.png" alt="susidns logo" border="0"/>
</div>
<div id="navi">
<p>addressbooks
<a href="addressbook.jsp?book=master">master</a> |
<a href="addressbook.jsp?book=router">router</a> |
<a href="addressbook.jsp?book=published">published</a> *
<a href="subscriptions.jsp">subscriptions</a> *
<a href="config.jsp">configuration</a>
</p>
</div>
<div id="headline">
<h3>${subs.fileName}</h3>
</div>
<div id="messages">${subs.messages}</div>
<form method="POST" action="subscriptions.jsp">
<div id="content">
<input type="hidden" name="serial" value="${subs.serial}" />
<textarea name="content" rows="10" cols="80">${subs.content}</textarea>
</div>
<div id="buttons">
<input type="image" src="images/save.png" name="action" value="save" alt="Save Subscriptions" />
<input type="image" src="images/reload.png" name="action" value="reload" alt="Reload Subscriptions" />
</div>
</form>
<div id="help">
<h3>Explanation</h3>
<p class="help">
The subscription file contains a list of (i2p) URLs. The addressbook application
regularly (once per hour) checks this list for new eepsites. Those URLs simply contain the published hosts.txt
file of other people. Default subscription is the hosts.txt from dev.i2p. The most
popular collaboration site for eepsite is orion.i2p. So its a good idea to add http://orion.i2p/hosts.txt
as a 2nd subscription.
</p>
</div>
<div id="footer">
<p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005</p>
</div>
</body>
</html>

View File

@ -90,6 +90,12 @@ public class Archive {
}
public String getDefaultSelector() { return _defaultSelector; }
public void setDefaultSelector(String sel) {
if (sel == null)
_defaultSelector = "";
else
_defaultSelector = sel;
}
public BlogInfo getBlogInfo(BlogURI uri) {
if (uri == null) return null;
@ -209,15 +215,19 @@ public class Archive {
private EntryContainer getCachedEntry(File entryDir) {
try {
return new CachedEntry(entryDir);
CachedEntry ce = new CachedEntry(entryDir);
if (ce.isValid())
return ce;
return null;
} catch (IOException ioe) {
ioe.printStackTrace();
File files[] = entryDir.listFiles();
for (int i = 0; i < files.length; i++)
files[i].delete();
entryDir.delete();
return null;
}
File files[] = entryDir.listFiles();
for (int i = 0; i < files.length; i++)
files[i].delete();
entryDir.delete();
return null;
}
public EntryContainer getEntry(BlogURI uri) { return getEntry(uri, null); }

View File

@ -16,7 +16,7 @@ class ArchiveIndexer {
private static final int RECENT_ENTRY_COUNT = 10;
public static ArchiveIndex index(I2PAppContext ctx, Archive source) {
LocalArchiveIndex rv = new LocalArchiveIndex();
LocalArchiveIndex rv = new LocalArchiveIndex(ctx);
rv.setGeneratedOn(ctx.clock().now());
File rootDir = source.getArchiveDir();
@ -54,7 +54,7 @@ class ArchiveIndexer {
long totalSize = 0;
int newBlogs = 0;
SMLParser parser = new SMLParser();
SMLParser parser = new SMLParser(ctx);
for (int i = 0; i < blogs.size(); i++) {
BlogInfo cur = (BlogInfo)blogs.get(i);
@ -158,7 +158,7 @@ class ArchiveIndexer {
_headers.setProperty(header, value);
}
public void receiveAddress(String name, String schema, String location, String anchorText) {}
public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {}
public void receiveArchive(String name, String description, String locationSchema, String location, String postingKey, String anchorText) {}
public void receiveAttachment(int id, String anchorText) {}
public void receiveBegin() {}

View File

@ -4,6 +4,8 @@ import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetName;
import net.i2p.client.naming.PetNameDB;
import net.i2p.data.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
@ -63,8 +65,9 @@ public class BlogManager {
_archive.regenerateIndex();
}
private File getConfigFile() { return new File(_rootDir, "syndie.config"); }
private void readConfig() {
File config = new File(_rootDir, "syndie.config");
File config = getConfigFile();
if (config.exists()) {
try {
Properties p = new Properties();
@ -72,10 +75,13 @@ public class BlogManager {
for (Iterator iter = p.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
System.setProperty(key, p.getProperty(key));
System.out.println("Read config prop [" + key + "] = [" + p.getProperty(key) + "]");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
} else {
System.out.println("Config doesn't exist: " + config.getPath());
}
}
@ -137,8 +143,30 @@ public class BlogManager {
return info;
}
public boolean updateMetadata(User user, Hash blog, Properties opts) {
if (!user.getAuthenticated()) return false;
BlogInfo oldInfo = getArchive().getBlogInfo(blog);
if (oldInfo == null) return false;
if (!user.getBlog().equals(oldInfo.getKey().calculateHash())) return false;
int oldEdition = 0;
try {
String ed = oldInfo.getProperty("Edition");
if (ed != null)
oldEdition = Integer.parseInt(ed);
} catch (NumberFormatException nfe) {}
opts.setProperty("Edition", oldEdition + 1 + "");
BlogInfo info = new BlogInfo(oldInfo.getKey(), oldInfo.getPosters(), opts);
SigningPrivateKey key = getMyPrivateKey(oldInfo);
info.sign(_context, key);
getArchive().storeBlogInfo(info);
user.setLastMetaEntry(oldEdition+1);
saveUser(user);
return true;
}
public Archive getArchive() { return _archive; }
public File getTempDir() { return _tempDir; }
public File getRootDir() { return _rootDir; }
public List listMyBlogs() {
File files[] = _privKeyDir.listFiles();
@ -181,6 +209,7 @@ public class BlogManager {
}
public String login(User user, String login, String pass) {
if ( (login == null) || (pass == null) ) return "<span class=\"b_loginMsgErr\">Login not specified</span>";
Hash userHash = _context.sha().calculateHash(DataHelper.getUTF8(login));
Hash passHash = _context.sha().calculateHash(DataHelper.getUTF8(pass));
File userFile = new File(_userDir, Base64.encode(userHash.getData()));
@ -203,20 +232,124 @@ public class BlogManager {
return user.login(login, pass, props);
} catch (IOException ioe) {
ioe.printStackTrace();
return "Error logging in - corrupt userfile";
return "<span class=\"b_loginMsgErr\">Error logging in - corrupt userfile</span>";
}
} else {
return "User does not exist";
return "<span class=\"b_loginMsgErr\">User does not exist</span>";
}
}
/** hash of the password required to register and create a new blog (null means no password required) */
public String getRegistrationPassword() {
public String getRegistrationPasswordHash() {
String pass = _context.getProperty("syndie.registrationPassword");
if ( (pass == null) || (pass.trim().length() <= 0) ) return null;
return pass;
}
/** Password required to access the remote syndication functinoality (null means no password required) */
public String getRemotePasswordHash() {
String pass = _context.getProperty("syndie.remotePassword");
System.out.println("Remote password? [" + pass + "]");
if ( (pass == null) || (pass.trim().length() <= 0) ) return null;
return pass;
}
public String getAdminPasswordHash() {
String pass = _context.getProperty("syndie.adminPassword");
if ( (pass == null) || (pass.trim().length() <= 0) ) return "";
return pass;
}
public boolean isConfigured() {
File cfg = getConfigFile();
return (cfg.exists());
}
/**
* If true, this syndie instance is meant for just one local user, so we don't need
* to password protect registration, remote.jsp, or admin.jsp
*
*/
public boolean isSingleUser() {
String isSingle = _context.getProperty("syndie.singleUser");
return ( (isSingle != null) && (Boolean.valueOf(isSingle).booleanValue()) );
}
public String getDefaultProxyHost() { return _context.getProperty("syndie.defaultProxyHost", ""); }
public String getDefaultProxyPort() { return _context.getProperty("syndie.defaultProxyPort", ""); }
public boolean authorizeAdmin(String pass) {
if (isSingleUser()) return true;
String admin = getAdminPasswordHash();
if ( (admin == null) || (admin.trim().length() <= 0) )
return false;
String hash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(pass.trim())).getData());
return (hash.equals(admin));
}
public boolean authorizeRemote(String pass) {
if (isSingleUser()) return true;
String rem = getRemotePasswordHash();
if ( (rem == null) || (rem.trim().length() <= 0) )
return false;
String hash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(pass.trim())).getData());
return (hash.equals(rem));
}
public boolean authorizeRemote(User user) {
if (isSingleUser()) return true;
return (user.getAuthenticated() && user.getAllowAccessRemote());
}
public void configure(String registrationPassword, String remotePassword, String adminPass, String defaultSelector,
String defaultProxyHost, int defaultProxyPort, boolean isSingleUser, Properties opts) {
File cfg = getConfigFile();
Writer out = null;
try {
out = new OutputStreamWriter(new FileOutputStream(cfg), "UTF-8");
if (registrationPassword != null)
out.write("syndie.registrationPassword="+Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(registrationPassword.trim())).getData()) + "\n");
if (remotePassword != null)
out.write("syndie.remotePassword="+Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(remotePassword.trim())).getData()) + "\n");
if (adminPass != null)
out.write("syndie.adminPassword="+Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(adminPass.trim())).getData()) + "\n");
if (defaultSelector != null)
out.write("syndie.defaultSelector="+defaultSelector.trim() + "\n");
if (defaultProxyHost != null)
out.write("syndie.defaultProxyHost="+defaultProxyHost.trim() + "\n");
if (defaultProxyPort > 0)
out.write("syndie.defaultProxyPort="+defaultProxyPort + "\n");
out.write("syndie.singleUser=" + isSingleUser + "\n");
if (opts != null) {
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = opts.getProperty(key);
out.write(key.trim() + "=" + val.trim() + "\n");
}
}
_archive.setDefaultSelector(defaultSelector);
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
readConfig();
}
}
public String authorizeRemoteAccess(User user, String password) {
if (!user.getAuthenticated()) return "<span class=\"b_remoteMsgErr\">Not logged in</span>";
String remPass = getRemotePasswordHash();
if (remPass == null)
return "<span class=\"b_remoteMsgErr\">Remote access password not configured - please <a href=\"admin.jsp\">specify</a> a remote " +
"archive password</span>";
if (authorizeRemote(password)) {
user.setAllowAccessRemote(true);
saveUser(user);
return "<span class=\"b_remoteMsgOk\">Remote access authorized</span>";
} else {
return "<span class=\"b_remoteMsgErr\">Remote access denied</span>";
}
}
public void saveUser(User user) {
if (!user.getAuthenticated()) return;
String userHash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(user.getUsername())).getData());
@ -225,6 +358,7 @@ public class BlogManager {
try {
out = new FileOutputStream(userFile);
out.write(DataHelper.getUTF8(user.export()));
user.getPetNameDB().store(user.getAddressbookLocation());
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
@ -232,21 +366,20 @@ public class BlogManager {
}
}
public String register(User user, String login, String password, String registrationPassword, String blogName, String blogDescription, String contactURL) {
System.err.println("Register [" + login + "] pass [" + password + "] name [" + blogName + "] descr [" + blogDescription + "] contact [" + contactURL + "]");
System.err.println("reference bad string: [" + EncodingTestGenerator.TEST_STRING + "]");
String hashedRegistrationPassword = getRegistrationPassword();
if (hashedRegistrationPassword != null) {
System.err.println("Register [" + login + "] pass [" + password + "] name [" + blogName + "] descr [" + blogDescription + "] contact [" + contactURL + "] regPass [" + registrationPassword + "]");
String hashedRegistrationPassword = getRegistrationPasswordHash();
if ( (hashedRegistrationPassword != null) && (!isSingleUser()) ) {
try {
if (!hashedRegistrationPassword.equals(Base64.encode(_context.sha().calculateHash(registrationPassword.getBytes("UTF-8")).getData())))
return "Invalid registration password";
return "<span class=\"b_regMsgErr\">Invalid registration password</span>";
} catch (UnsupportedEncodingException uee) {
return "Error registering";
return "<span class=\"b_regMsgErr\">Error registering</span>";
}
}
String userHash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(login)).getData());
File userFile = new File(_userDir, userHash);
if (userFile.exists()) {
return "Cannot register the login " + login + ": it already exists";
return "<span class=\"b_regMsgErr\">Cannot register the login " + login + ": it already exists</span>";
} else {
BlogInfo info = createBlog(blogName, blogDescription, contactURL, null);
String hashedPassword = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(password)).getData());
@ -264,7 +397,7 @@ public class BlogManager {
bw.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
return "Internal error registering - " + ioe.getMessage();
return "<span class=\"b_regMsgErr\">Internal error registering - " + ioe.getMessage() + "</span>";
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
@ -273,6 +406,29 @@ public class BlogManager {
return loginResult;
}
}
public String exportHosts(User user) {
if (!user.getAuthenticated() || !user.getAllowAccessRemote())
return "<span class=\"b_addrMsgErr\">Not authorized to export the hosts</span>";
PetNameDB userDb = user.getPetNameDB();
PetNameDB routerDb = _context.petnameDb();
// horribly inefficient...
for (Iterator names = userDb.getNames().iterator(); names.hasNext();) {
PetName pn = userDb.get((String)names.next());
if (pn == null) continue;
Destination existing = _context.namingService().lookup(pn.getName());
if (existing == null && pn.getNetwork().equalsIgnoreCase("i2p")) {
routerDb.set(pn.getName(), pn);
try {
routerDb.store();
} catch (IOException ioe) {
ioe.printStackTrace();
return "<span class=\"b_addrMsgErr\">Error exporting the hosts: " + ioe.getMessage() + "</span>";
}
}
}
return "<span class=\"b_addrMsgOk\">Hosts exported</span>";
}
public BlogURI createBlogEntry(User user, String subject, String tags, String entryHeaders, String sml) {
return createBlogEntry(user, subject, tags, entryHeaders, sml, null, null, null);
@ -397,30 +553,27 @@ public class BlogManager {
return false;
}
}
public String addAddress(User user, String name, String location, String schema) {
if (!user.getAuthenticated()) return "Not logged in";
public String addAddress(User user, String name, String protocol, String location, String schema) {
if (!user.getAuthenticated()) return "<span class=\"b_addrMsgErr\">Not logged in</span>";
boolean ok = validateAddressName(name);
if (!ok) return "Invalid name: " + HTMLRenderer.sanitizeString(name);
if (!ok) return "<span class=\"b_addrMsgErr\">Invalid name: " + HTMLRenderer.sanitizeString(name) + "</span>";
ok = validateAddressLocation(location);
if (!ok) return "Invalid location: " + HTMLRenderer.sanitizeString(location);
if (!validateAddressSchema(schema)) return "Unsupported schema: " + HTMLRenderer.sanitizeString(schema);
if (!ok) return "<span class=\"b_addrMsgErr\">Invalid location: " + HTMLRenderer.sanitizeString(location) + "</span>";
if (!validateAddressSchema(schema)) return "<span class=\"b_addrMsgErr\">Unsupported schema: " + HTMLRenderer.sanitizeString(schema) + "</span>";
// no need to quote user/location further, as they've been sanitized
FileOutputStream out = null;
try {
File userHostsFile = new File(user.getAddressbookLocation());
Properties knownHosts = getKnownHosts(user, true);
if (knownHosts.containsKey(name)) return "Name is already in use";
PetNameDB names = user.getPetNameDB();
if (names.exists(name))
return "<span class=\"b_addrMsgErr\">Name is already in use</span>";
PetName pn = new PetName(name, schema, protocol, location);
names.set(name, pn);
out = new FileOutputStream(userHostsFile, true);
out.write(DataHelper.getUTF8(name + "=" + location + '\n'));
return "Address " + name + " written to your hosts file (" + userHostsFile.getName() + ")";
try {
names.store(user.getAddressbookLocation());
return "<span class=\"b_addrMsgOk\">Address " + name + " written to your addressbook</span>";
} catch (IOException ioe) {
return "Error writing out host entry: " + ioe.getMessage();
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
return "<span class=\"b_addrMsgErr\">Error writing out the name: " + ioe.getMessage() + "</span>";
}
}
@ -444,7 +597,7 @@ public class BlogManager {
}
private boolean validateAddressName(String name) {
if ( (name == null) || (name.trim().length() <= 0) || (!name.endsWith(".i2p")) ) return false;
if ( (name == null) || (name.trim().length() <= 0) ) return false;
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (!Character.isLetterOrDigit(c) && ('.' != c) && ('-' != c) && ('_' != c) )
@ -455,18 +608,27 @@ public class BlogManager {
private boolean validateAddressLocation(String location) {
if ( (location == null) || (location.trim().length() <= 0) ) return false;
try {
Destination d = new Destination(location);
return (d.getPublicKey() != null);
} catch (DataFormatException dfe) {
dfe.printStackTrace();
return false;
if (false) {
try {
Destination d = new Destination(location);
return (d.getPublicKey() != null);
} catch (DataFormatException dfe) {
dfe.printStackTrace();
return false;
}
} else {
// not everything is an i2p destination...
return true;
}
}
private boolean validateAddressSchema(String schema) {
if ( (schema == null) || (schema.trim().length() <= 0) ) return false;
return "eep".equals(schema) || "i2p".equals(schema);
if (true) {
return true;
} else {
return "eep".equals(schema) || "i2p".equals(schema);
}
}
private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);

View File

@ -111,7 +111,7 @@ public class CLI {
entryKey = new SessionKey(Base64.decode(args[5]));
EntryContainer entry = mgr.getArchive().getEntry(new BlogURI(new Hash(Base64.decode(args[2])), id), entryKey);
if (entry != null) {
HTMLRenderer renderer = new HTMLRenderer();
HTMLRenderer renderer = new HTMLRenderer(I2PAppContext.getGlobalContext());
boolean summaryOnly = "true".equalsIgnoreCase(args[5]);
boolean showImages = "true".equalsIgnoreCase(args[6]);
try {

View File

@ -28,6 +28,10 @@ class CachedEntry extends EntryContainer {
_attachments = null;
}
public boolean isValid() {
return (_entry != null) && (_blog != null);
}
// always available, loaded from meta
public int getFormat() { return _format; }
public BlogURI getURI() { return _blog; }
@ -70,7 +74,7 @@ class CachedEntry extends EntryContainer {
}
// now the actual lazy loading code
private void importMeta() {
private void importMeta() throws IOException {
Properties meta = readProps(new File(_entryDir, EntryExtractor.META));
_format = getInt(meta, "format");
_size = getInt(meta, "size");
@ -78,8 +82,14 @@ class CachedEntry extends EntryContainer {
}
private Properties importHeaders() {
if (_headers == null)
_headers = readProps(new File(_entryDir, EntryExtractor.HEADERS));
if (_headers == null) {
try {
_headers = readProps(new File(_entryDir, EntryExtractor.HEADERS));
} catch (IOException ioe) {
ioe.printStackTrace();
_headers = new Properties();
}
}
return _headers;
}
@ -103,7 +113,7 @@ class CachedEntry extends EntryContainer {
return;
}
private static Properties readProps(File propsFile) {
private static Properties readProps(File propsFile) throws IOException {
Properties rv = new Properties();
BufferedReader in = null;
try {
@ -114,8 +124,6 @@ class CachedEntry extends EntryContainer {
if ( (split <= 0) || (split >= line.length()) ) continue;
rv.setProperty(line.substring(0, split).trim(), line.substring(split+1).trim());
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
@ -222,15 +230,20 @@ class CachedEntry extends EntryContainer {
private void importAttachmentHeaders() {
if (_attachmentHeaders == null) {
Properties props = readProps(_metaFile);
String sz = (String)props.remove(EntryExtractor.ATTACHMENT_DATA_SIZE);
if (sz != null) {
try {
_dataSize = Integer.parseInt(sz);
} catch (NumberFormatException nfe) {}
try {
Properties props = readProps(_metaFile);
String sz = (String)props.remove(EntryExtractor.ATTACHMENT_DATA_SIZE);
if (sz != null) {
try {
_dataSize = Integer.parseInt(sz);
} catch (NumberFormatException nfe) {}
}
_attachmentHeaders = props;
} catch (IOException ioe) {
ioe.printStackTrace();
_attachmentHeaders = new Properties();
}
_attachmentHeaders = props;
}
}
}

View File

@ -1,8 +1,10 @@
package net.i2p.syndie;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetNameDB;
import net.i2p.data.*;
/**
@ -36,6 +38,7 @@ public class User {
private int _webProxyPort;
private String _torProxyHost;
private int _torProxyPort;
private PetNameDB _petnames;
public User() {
_context = I2PAppContext.getGlobalContext();
@ -51,7 +54,7 @@ public class User {
_shitlistedBlogs = new ArrayList();
_defaultSelector = null;
_addressbookLocation = "userhosts.txt";
_showImagesByDefault = false;
_showImagesByDefault = true;
_showExpandedByDefault = false;
_allowAccessRemote = false;
_eepProxyHost = null;
@ -62,6 +65,7 @@ public class User {
_torProxyPort = -1;
_lastLogin = -1;
_lastMetaEntry = 0;
_petnames = new PetNameDB();
}
public boolean getAuthenticated() { return _authenticated; }
@ -92,8 +96,11 @@ public class User {
public String getTorProxyHost() { return _torProxyHost; }
public int getTorProxyPort() { return _torProxyPort; }
public PetNameDB getPetNameDB() { return _petnames; }
public void invalidate() {
BlogManager.instance().saveUser(this);
if (_authenticated)
BlogManager.instance().saveUser(this);
init();
}
@ -102,7 +109,7 @@ public class User {
String hpass = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(pass)).getData());
if (!hpass.equals(expectedPass)) {
_authenticated = false;
return "Incorrect password";
return "<span class=\"b_loginMsgErr\">Incorrect password</span>";
}
_username = login;
@ -156,10 +163,16 @@ public class User {
}
String addr = props.getProperty("addressbook", "userhosts.txt");
if (addr != null)
if (addr != null) {
_addressbookLocation = addr;
try {
_petnames.load(addr);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
String show = props.getProperty("showimages", "false");
String show = props.getProperty("showimages", "true");
_showImagesByDefault = (show != null) && (show.equals("true"));
show = props.getProperty("showexpanded", "false");
_showExpandedByDefault = (show != null) && (show.equals("true"));
@ -182,7 +195,7 @@ public class User {
try { return Integer.parseInt(val); } catch (NumberFormatException nfe) { return -1; }
}
public static final String LOGIN_OK = "Logged in";
public static final String LOGIN_OK = "<span class=\"b_loginMsgOk\">Logged in</span>";
public String export() {
StringBuffer buf = new StringBuffer(512);

View File

@ -7,11 +7,14 @@ import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.Archive;
import net.i2p.syndie.BlogManager;
import net.i2p.util.Log;
/**
* Simple read-only summary of an archive
*/
public class ArchiveIndex {
private I2PAppContext _context;
private Log _log;
protected String _version;
protected long _generatedOn;
protected int _allBlogs;
@ -31,9 +34,14 @@ public class ArchiveIndex {
protected Properties _headers;
public ArchiveIndex() {
this(false); //true);
this(I2PAppContext.getGlobalContext(), false);
}
public ArchiveIndex(boolean shouldLoad) {
public ArchiveIndex(I2PAppContext ctx) {
this(ctx, false); //true);
}
public ArchiveIndex(I2PAppContext ctx, boolean shouldLoad) {
_context = ctx;
_log = ctx.logManager().getLog(ArchiveIndex.class);
_blogs = new ArrayList();
_newestBlogs = new ArrayList();
_newestEntries = new ArrayList();
@ -117,6 +125,20 @@ public class ArchiveIndex {
}
return tags;
}
public int getBlogEntryCount(Hash blog) {
Set uris = new HashSet(64);
for (int i = 0; i < _blogs.size(); i++) {
BlogSummary summary = (BlogSummary)_blogs.get(i);
if (summary.blog.equals(blog)) {
uris.addAll(summary.entries);
//for (int j = 0; j < summary.entries.size(); j++) {
// EntrySummary entry = (EntrySummary)summary.entries.get(j);
// uris.add(entry.entry);
//}
}
}
return uris.size();
}
/** how many 'new' blogs are listed */
public int getNewestBlogCount() { return _newestBlogs.size(); }
@ -264,7 +286,8 @@ public class ArchiveIndex {
}
if (tag != null) {
if (!tag.equals(summary.tag)) {
System.out.println("Tag [" + summary.tag + "] does not match the requested [" + tag + "] in " + summary.blog.toBase64());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Tag [" + summary.tag + "] does not match the requested [" + tag + "] in " + summary.blog.toBase64());
if (false) {
StringBuffer b = new StringBuffer(tag.length()*2);
for (int j = 0; j < tag.length(); j++) {
@ -276,7 +299,8 @@ public class ArchiveIndex {
b.append('_');
b.append(' ');
}
System.out.println("tag.summary: " + b.toString());
if (_log.shouldLog(Log.DEBUG))
_log.debug("tag.summary: " + b.toString());
}
continue;
}
@ -379,6 +403,14 @@ public class ArchiveIndex {
size = kb;
entry = uri;
}
public int hashCode() {
return entry.hashCode();
}
public boolean equals(Object obj) {
if ( (obj instanceof EntrySummary) && (((EntrySummary)obj).entry.equals(entry)) )
return true;
return false;
}
}
/** export the index into an archive.txt */

View File

@ -4,6 +4,7 @@ import java.io.*;
import java.util.*;
import net.i2p.data.*;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Blog metadata. Formatted as: <pre>
@ -57,12 +58,14 @@ public class BlogInfo {
public static final String EDITION = "Edition";
public void load(InputStream in) throws IOException {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
List names = new ArrayList();
List vals = new ArrayList();
String line = null;
while ( (line = reader.readLine()) != null) {
System.err.println("Read info line [" + line + "]");
if (log.shouldLog(Log.DEBUG))
log.debug("Read info line [" + line + "]");
line = line.trim();
int len = line.length();
int split = line.indexOf(':');
@ -84,7 +87,7 @@ public class BlogInfo {
for (int i = 0; i < _optionNames.length; i++) {
_optionNames[i] = (String)names.get(i);
_optionValues[i] = (String)vals.get(i);
System.out.println("Loaded info: [" + _optionNames[i] + "] = [" + _optionValues[i] + "]");
//System.out.println("Loaded info: [" + _optionNames[i] + "] = [" + _optionValues[i] + "]");
}
String keyStr = getProperty(OWNER_KEY);
@ -119,12 +122,12 @@ public class BlogInfo {
for (int i = 0; i < _optionNames.length; i++) {
if (_optionNames[i].equals(name)) {
String val = _optionValues[i];
System.out.println("getProperty[" + name + "] = [" + val + "] [sz=" + val.length() +"]");
for (int j = 0; j < val.length(); j++) {
char c = (char)val.charAt(j);
if (c != (c & 0x7F))
System.out.println("char " + j + ": " + (int)c);
}
//System.out.println("getProperty[" + name + "] = [" + val + "] [sz=" + val.length() +"]");
//for (int j = 0; j < val.length(); j++) {
// char c = (char)val.charAt(j);
// if (c != (c & 0x7F))
// System.out.println("char " + j + ": " + (int)c);
//}
return val;
}
}

View File

@ -6,6 +6,7 @@ import java.util.zip.*;
import net.i2p.data.*;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Securely wrap up an entry and any attachments. Container format:<pre>
@ -115,12 +116,19 @@ public class EntryContainer {
if (len <= 0)
break;
int split = line.indexOf(':');
if ( (split <= 0) || (split >= len - 2) )
if (split <= 0) {
throw new IOException("Invalid format of the syndie entry: line=" + line);
String key = line.substring(0, split);
String val = line.substring(split+1);
_rawKeys.add(key);
_rawValues.add(val);
} else if (split >= len - 2) {
// foo:\n
String key = line.substring(0, split);
_rawKeys.add(key);
_rawValues.add("");
} else {
String key = line.substring(0, split);
String val = line.substring(split+1);
_rawKeys.add(key);
_rawValues.add(val);
}
}
parseHeaders();
@ -159,7 +167,9 @@ public class EntryContainer {
}
public void seal(I2PAppContext ctx, SigningPrivateKey signingKey, SessionKey entryKey) throws IOException {
System.out.println("Sealing " + _entryURI);
Log l = ctx.logManager().getLog(getClass());
if (l.shouldLog(Log.DEBUG))
l.debug("Sealing " + _entryURI);
if (entryKey == null)
_format = FORMAT_ZIP_UNENCRYPTED;
else
@ -265,17 +275,21 @@ public class EntryContainer {
for (int i = 0; i < attachments.size(); i++) {
byte data[] = (byte[])attachments.get(ZIP_ATTACHMENT_PREFIX + i + ZIP_ATTACHMENT_SUFFIX);
byte metadata[] = (byte[])attachmentMeta.get(ZIP_ATTACHMENT_META_PREFIX + i + ZIP_ATTACHMENT_META_SUFFIX);
if ( (data != null) && (metadata != null) )
if ( (data != null) && (metadata != null) ) {
_attachments[i] = new Attachment(data, metadata);
else
System.out.println("Unable to get " + i + ": " + data + "/" + metadata);
} else {
Log l = ctx.logManager().getLog(getClass());
if (l.shouldLog(Log.WARN))
l.warn("Unable to get " + i + ": " + data + "/" + metadata);
}
}
//System.out.println("Attachments: " + _attachments.length + "/" + attachments.size() + ": " + attachments);
}
public BlogURI getURI() { return _entryURI; }
private static final String NO_TAGS[] = new String[0];
public static final String NO_TAGS_TAG = "[none]";
private static final String NO_TAGS[] = new String[] { NO_TAGS_TAG };
public String[] getTags() {
String tags = getHeader(HEADER_BLOGTAGS);
if ( (tags == null) || (tags.trim().length() <= 0) ) {
@ -340,8 +354,6 @@ public class EntryContainer {
String idVal = getHeader(HEADER_ENTRYID);
if (keyHash == null) {
System.err.println("Headers: " + _rawKeys);
System.err.println("Values : " + _rawValues);
throw new IOException("Missing " + HEADER_BLOGKEY + " header");
}
@ -350,8 +362,6 @@ public class EntryContainer {
try {
entryId = Long.parseLong(idVal.trim());
} catch (NumberFormatException nfe) {
System.err.println("Headers: " + _rawKeys);
System.err.println("Values : " + _rawValues);
throw new IOException("Invalid format of entryId (" + idVal + ")");
}
}

View File

@ -1,16 +1,19 @@
package net.i2p.syndie.data;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.Archive;
import net.i2p.util.Log;
/**
* writable archive index (most are readonly)
*/
public class LocalArchiveIndex extends ArchiveIndex {
public LocalArchiveIndex() {
super(false);
private Log _log;
public LocalArchiveIndex(I2PAppContext ctx) {
super(ctx, false);
_log = ctx.logManager().getLog(getClass());
}
public void setGeneratedOn(long when) { _generatedOn = when; }
@ -46,7 +49,8 @@ public class LocalArchiveIndex extends ArchiveIndex {
if (summary.blog.equals(key) && (summary.tag.equals(tag)) ) {
long entryId = Archive.getEntryIdFromIndexName(entry);
int kb = Archive.getSizeFromIndexName(entry);
System.out.println("Adding entry " + entryId + ", size=" + kb + "KB [" + entry + "]");
if (_log.shouldLog(Log.INFO))
_log.info("Adding entry " + entryId + ", size=" + kb + "KB [" + entry + "]");
EntrySummary entrySummary = new EntrySummary(new BlogURI(key, entryId), kb);
for (int j = 0; j < summary.entries.size(); j++) {
EntrySummary cur = (EntrySummary)summary.entries.get(j);

View File

@ -12,7 +12,7 @@ import net.i2p.syndie.BlogManager;
* Simple read-only summary of an archive, proxied to the BlogManager's instance
*/
public class TransparentArchiveIndex extends ArchiveIndex {
public TransparentArchiveIndex() { super(false); }
public TransparentArchiveIndex() { super(I2PAppContext.getGlobalContext(), false); }
private static ArchiveIndex index() { return BlogManager.instance().getArchive().getIndex(); }

View File

@ -1,52 +1,108 @@
package net.i2p.syndie.sml;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
*
*/
public class EventReceiverImpl implements SMLParser.EventReceiver {
private I2PAppContext _context;
private Log _log;
public EventReceiverImpl(I2PAppContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(EventReceiverImpl.class);
}
public void receiveHeader(String header, String value) {
System.out.println("Receive header [" + header + "] = [" + value + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive header [" + header + "] = [" + value + "]");
}
public void receiveLink(String schema, String location, String text) {
System.out.println("Receive link [" + schema + "]/[" + location+ "]/[" + text + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive link [" + schema + "]/[" + location+ "]/[" + text + "]");
}
public void receiveBlog(String name, String blogKeyHash, String blogPath, long blogEntryId,
List blogArchiveLocations, String anchorText) {
System.out.println("Receive blog [" + name + "]/[" + blogKeyHash + "]/[" + blogPath
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive blog [" + name + "]/[" + blogKeyHash + "]/[" + blogPath
+ "]/[" + blogEntryId + "]/[" + blogArchiveLocations + "]/[" + anchorText + "]");
}
public void receiveArchive(String name, String description, String locationSchema, String location,
String postingKey, String anchorText) {
System.out.println("Receive archive [" + name + "]/[" + description + "]/[" + locationSchema
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive archive [" + name + "]/[" + description + "]/[" + locationSchema
+ "]/[" + location + "]/[" + postingKey + "]/[" + anchorText + "]");
}
public void receiveImage(String alternateText, int attachmentId) {
System.out.println("Receive image [" + alternateText + "]/[" + attachmentId + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive image [" + alternateText + "]/[" + attachmentId + "]");
}
public void receiveAddress(String name, String schema, String location, String anchorText) {
System.out.println("Receive address [" + name + "]/[" + schema + "]/[" + location + "]/[" + anchorText+ "]");
public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive address [" + name + "]/[" + schema + "]/[" + location + "]/[" + anchorText+ "]");
}
public void receiveBold(String text) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive bold [" + text+ "]");
}
public void receiveItalic(String text) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive italic [" + text+ "]");
}
public void receiveUnderline(String text) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive underline [" + text+ "]");
}
public void receiveBold(String text) { System.out.println("Receive bold [" + text+ "]"); }
public void receiveItalic(String text) { System.out.println("Receive italic [" + text+ "]"); }
public void receiveUnderline(String text) { System.out.println("Receive underline [" + text+ "]"); }
public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
System.out.println("Receive quote [" + text + "]/[" + whoQuoted + "]/[" + quoteLocationSchema + "]/[" + quoteLocation + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive quote [" + text + "]/[" + whoQuoted + "]/[" + quoteLocationSchema + "]/[" + quoteLocation + "]");
}
public void receiveCode(String text, String codeLocationSchema, String codeLocation) {
System.out.println("Receive code [" + text+ "]/[" + codeLocationSchema + "]/[" + codeLocation + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive code [" + text+ "]/[" + codeLocationSchema + "]/[" + codeLocation + "]");
}
public void receiveCut(String summaryText) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive cut [" + summaryText + "]");
}
public void receivePlain(String text) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive plain [" + text + "]");
}
public void receiveNewline() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive NL");
}
public void receiveLT() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive LT");
}
public void receiveGT() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive GT");
}
public void receiveBegin() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive begin");
}
public void receiveEnd() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive end");
}
public void receiveHeaderEnd() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive header end");
}
public void receiveLeftBracket() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive [");
}
public void receiveRightBracket() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive ]");
}
public void receiveCut(String summaryText) { System.out.println("Receive cut [" + summaryText + "]"); }
public void receivePlain(String text) { System.out.println("Receive plain [" + text + "]"); }
public void receiveNewline() { System.out.println("Receive NL"); }
public void receiveLT() { System.out.println("Receive LT"); }
public void receiveGT() { System.out.println("Receive GT"); }
public void receiveBegin() { System.out.println("Receive begin"); }
public void receiveEnd() { System.out.println("Receive end"); }
public void receiveHeaderEnd() { System.out.println("Receive header end"); }
public void receiveLeftBracket() { System.out.println("Receive ["); }
public void receiveRightBracket() { System.out.println("Receive ]"); }
public void receiveH1(String text) {}
public void receiveH2(String text) {}

View File

@ -3,6 +3,7 @@ package net.i2p.syndie.sml;
import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
@ -16,8 +17,8 @@ public class HTMLPreviewRenderer extends HTMLRenderer {
private List _fileTypes;
private List _files;
public HTMLPreviewRenderer(List filenames, List fileTypes, List files) {
super();
public HTMLPreviewRenderer(I2PAppContext ctx, List filenames, List fileTypes, List files) {
super(ctx);
_filenames = filenames;
_fileTypes = fileTypes;
_files = files;
@ -37,23 +38,24 @@ public class HTMLPreviewRenderer extends HTMLRenderer {
File f = (File)_files.get(id);
String name = (String)_filenames.get(id);
String type = (String)_fileTypes.get(id);
_bodyBuffer.append("<a href=\"").append(getAttachmentURL(id)).append("\">");
_bodyBuffer.append("<a ").append(getClass("attachmentView")).append(" href=\"").append(getAttachmentURL(id)).append("\">");
_bodyBuffer.append(sanitizeString(anchorText)).append("</a>");
_bodyBuffer.append(" (").append(f.length()/1024).append("KB, ");
_bodyBuffer.append(" \"").append(sanitizeString(name)).append("\", ");
_bodyBuffer.append(sanitizeString(type)).append(")");
_bodyBuffer.append(getSpan("attachmentSummary")).append(" (");
_bodyBuffer.append(getSpan("attachmentSummarySize")).append(f.length()/1024).append("KB</span>, ");
_bodyBuffer.append(getSpan("attachmentSummaryName")).append(" \"").append(sanitizeString(name)).append("\"</span>, ");
_bodyBuffer.append(getSpan("attachmentSummaryType")).append(sanitizeString(type)).append("</span>)</span>");
}
}
public void receiveEnd() {
_postBodyBuffer.append("</td></tr>\n");
_postBodyBuffer.append("<tr>\n");
_postBodyBuffer.append("<tr ").append(getClass("summDetail")).append(" >\n");
_postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" class=\"syndieEntryAttachmentsCell\"\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summDetail")).append("> \n");
if (_files.size() > 0) {
_postBodyBuffer.append("<b>Attachments:</b> ");
_postBodyBuffer.append("<select name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
_postBodyBuffer.append(getSpan("summDetailAttachment")).append("Attachments:</span> ");
_postBodyBuffer.append("<select ").append(getClass("summDetailAttachmentId")).append(" name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
for (int i = 0; i < _files.size(); i++) {
_postBodyBuffer.append("<option value=\"").append(i).append("\">");
File f = (File)_files.get(i);
@ -64,53 +66,77 @@ public class HTMLPreviewRenderer extends HTMLRenderer {
_postBodyBuffer.append(", type ").append(sanitizeString(type)).append(")</option>\n");
}
_postBodyBuffer.append("</select>\n");
_postBodyBuffer.append("<input type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
_postBodyBuffer.append("<input ").append(getClass("summDetailAttachmentDl")).append(" type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
}
if (_blogs.size() > 0) {
_postBodyBuffer.append("<b>Blog references:</b> ");
_postBodyBuffer.append(getSpan("summDetailBlog")).append("Blog references:</span> ");
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
_postBodyBuffer.append("<a href=\"").append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
boolean expanded = (_user != null ? _user.getShowExpanded() : false);
boolean images = (_user != null ? _user.getShowImages() : false);
_postBodyBuffer.append("<a ").append(getClass("summDetailBlogLink")).append(" href=\"");
_postBodyBuffer.append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, expanded, images));
_postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_links.size() > 0) {
_postBodyBuffer.append("<b>External links:</b> ");
_postBodyBuffer.append(getSpan("summDetailExternal")).append("External links:</span> ");
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
_postBodyBuffer.append("<a href=\"externallink.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(l.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(l.location));
_postBodyBuffer.append("<a ").append(getClass("summDetailExternalLink")).append(" href=\"externallink.jsp?");
if (l.schema != null)
_postBodyBuffer.append("schema=").append(sanitizeURL(l.schema)).append('&');
if (l.location != null)
_postBodyBuffer.append("location=").append(sanitizeURL(l.location)).append('&');
_postBodyBuffer.append("\">").append(sanitizeString(l.location));
_postBodyBuffer.append(" (").append(sanitizeString(l.schema)).append(")</a> ");
_postBodyBuffer.append(getSpan("summDetailExternalNet")).append(" (").append(sanitizeString(l.schema)).append(")</span></a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_addresses.size() > 0) {
_postBodyBuffer.append("<b>Addresses:</b> ");
_postBodyBuffer.append(getSpan("summDetailAddr")).append("Addresses:</span>");
for (int i = 0; i < _addresses.size(); i++) {
Address a = (Address)_addresses.get(i);
_postBodyBuffer.append("<a href=\"addaddress.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
_postBodyBuffer.append(sanitizeURL(a.name));
_postBodyBuffer.append("\">").append(sanitizeString(a.name));
String knownName = null;
if (_user != null)
knownName = _user.getPetNameDB().getNameByLocation(a.location);
if (knownName != null) {
_postBodyBuffer.append(' ').append(getSpan("summDetailAddrKnown"));
_postBodyBuffer.append(sanitizeString(knownName)).append("</span>");
} else {
_postBodyBuffer.append(" <a ").append(getClass("summDetailAddrLink")).append(" href=\"addresses.jsp?");
if (a.schema != null)
_postBodyBuffer.append("network=").append(sanitizeTagParam(a.schema)).append('&');
if (a.location != null)
_postBodyBuffer.append("location=").append(sanitizeTagParam(a.location)).append('&');
if (a.name != null)
_postBodyBuffer.append("name=").append(sanitizeTagParam(a.name)).append('&');
if (a.protocol != null)
_postBodyBuffer.append("protocol=").append(sanitizeTagParam(a.protocol)).append('&');
_postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
}
}
_postBodyBuffer.append("<br />\n");
}
if (_archives.size() > 0) {
_postBodyBuffer.append("<b>Archives:</b>");
_postBodyBuffer.append(getSpan("summDetailArchive")).append("Archives:</span>");
for (int i = 0; i < _archives.size(); i++) {
ArchiveRef a = (ArchiveRef)_archives.get(i);
_postBodyBuffer.append(" <a href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
_postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveLink")).append(" href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
_postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
if (a.description != null)
_postBodyBuffer.append(": ").append(sanitizeString(a.description));
_postBodyBuffer.append(": ").append(getSpan("summDetailArchiveDesc")).append(sanitizeString(a.description)).append("</span>");
if (null == _user.getPetNameDB().getNameByLocation(a.location)) {
_postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveBookmark")).append(" href=\"");
_postBodyBuffer.append(getBookmarkURL(a.name, a.location, a.locationSchema, "syndiearchive"));
_postBodyBuffer.append("\">bookmark</a>");
}
}
_postBodyBuffer.append("<br />\n");
}

View File

@ -3,15 +3,19 @@ package net.i2p.syndie.sml;
import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetName;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.web.*;
import net.i2p.util.Log;
/**
*
*/
public class HTMLRenderer extends EventReceiverImpl {
private Log _log;
protected SMLParser _parser;
protected Writer _out;
protected User _user;
@ -31,8 +35,10 @@ public class HTMLRenderer extends EventReceiverImpl {
protected StringBuffer _bodyBuffer;
protected StringBuffer _postBodyBuffer;
public HTMLRenderer() {
_parser = new SMLParser();
public HTMLRenderer(I2PAppContext ctx) {
super(ctx);
_log = ctx.logManager().getLog(HTMLRenderer.class);
_parser = new SMLParser(ctx);
}
/**
@ -43,7 +49,7 @@ public class HTMLRenderer extends EventReceiverImpl {
System.err.println("Usage: HTMLRenderer smlFile outputFile");
return;
}
HTMLRenderer renderer = new HTMLRenderer();
HTMLRenderer renderer = new HTMLRenderer(I2PAppContext.getGlobalContext());
Writer out = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024*512);
@ -61,20 +67,49 @@ public class HTMLRenderer extends EventReceiverImpl {
}
}
/**
* Retrieve: class="s_summary_$element" or class="s_detail_$element ss_$style_detail_$element"
*/
protected String getClass(String element) {
StringBuffer rv = new StringBuffer(64);
rv.append(" class=\"s_");
if (_cutBody)
rv.append("summary_");
else
rv.append("detail_");
rv.append(element);
if (_entry != null) {
String style = sanitizeStyle(_entry.getHeader(HEADER_STYLE));
if (style != null) {
rv.append(" ss_").append(style);
if (_cutBody)
rv.append("summary_");
else
rv.append("detail_");
rv.append(element);
}
}
rv.append("\" ");
return rv.toString();
}
protected String getSpan(String element) {
return "<span " + getClass(element) + ">";
}
public void renderUnknownEntry(User user, Archive archive, BlogURI uri, Writer out) throws IOException {
BlogInfo info = archive.getBlogInfo(uri);
if (info == null)
out.write("<br />The blog " + uri.getKeyHash().toBase64() + " is not known locally. "
+ "Please get it from an archive and <a href=\""
out.write("<br /><span " + getClass("unknownBlog") + ">The blog <span " + getClass("blogURI") + ">" + uri.getKeyHash().toBase64() + "</span> is not known locally. "
+ "Please get it from an archive and <a " + getClass("unknownRetry") + " href=\""
+ getPageURL(uri.getKeyHash(), null, uri.getEntryId(), -1, -1, user.getShowExpanded(), user.getShowImages())
+ "\">try again</a>");
+ "\">try again</a></span>");
else
out.write("<br />The blog <a href=\""
out.write("<br /><span " + getClass("unknownEntry") + ">The blog <a " + getClass("unknownRetry") + " href=\""
+ getPageURL(uri.getKeyHash(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())
+ "\">" + info.getProperty(BlogInfo.NAME) + "</a> is known, but the entry " + uri.getEntryId() + " is not. "
+ "Please get it from an archive and <a href=\""
+ "Please get it from an archive and <a " + getClass("unknownRetry") + " href=\""
+ getPageURL(uri.getKeyHash(), null, uri.getEntryId(), -1, -1, user.getShowExpanded(), user.getShowImages())
+ "\">try again</a>");
+ "\">try again</a></span>");
}
public void render(User user, Archive archive, EntryContainer entry, Writer out, boolean cutBody, boolean showImages) throws IOException {
@ -83,6 +118,15 @@ public class HTMLRenderer extends EventReceiverImpl {
render(user, archive, entry, entry.getEntry().getText(), out, cutBody, showImages);
}
public void render(User user, Archive archive, EntryContainer entry, String rawSML, Writer out, boolean cutBody, boolean showImages) throws IOException {
prepare(user, archive, entry, rawSML, out, cutBody, showImages);
_out.write(_preBodyBuffer.toString());
_out.write(_bodyBuffer.toString());
_out.write(_postBodyBuffer.toString());
//int len = _preBodyBuffer.length() + _bodyBuffer.length() + _postBodyBuffer.length();
//System.out.println("Wrote " + len);
}
protected void prepare(User user, Archive archive, EntryContainer entry, String rawSML, Writer out, boolean cutBody, boolean showImages) throws IOException {
_user = user;
_archive = archive;
_entry = entry;
@ -100,11 +144,6 @@ public class HTMLRenderer extends EventReceiverImpl {
_cutReached = false;
_cutSize = 1024;
_parser.parse(rawSML, this);
_out.write(_preBodyBuffer.toString());
_out.write(_bodyBuffer.toString());
_out.write(_postBodyBuffer.toString());
//int len = _preBodyBuffer.length() + _bodyBuffer.length() + _postBodyBuffer.length();
//System.out.println("Wrote " + len);
}
public void receivePlain(String text) {
@ -114,64 +153,64 @@ public class HTMLRenderer extends EventReceiverImpl {
public void receiveBold(String text) {
if (!continueBody()) { return; }
_bodyBuffer.append("<b>").append(sanitizeString(text)).append("</b>");
_bodyBuffer.append("<em ").append(getClass("bold")).append(" >").append(sanitizeString(text)).append("</em>");
}
public void receiveItalic(String text) {
if (!continueBody()) { return; }
_bodyBuffer.append("<i>").append(sanitizeString(text)).append("</i>");
_bodyBuffer.append("<em ").append(getClass("italic")).append(" >").append(sanitizeString(text)).append("</em>");
}
public void receiveUnderline(String text) {
if (!continueBody()) { return; }
_bodyBuffer.append("<u>").append(sanitizeString(text)).append("</u>");
_bodyBuffer.append("<em ").append(getClass("underline")).append(" >").append(sanitizeString(text)).append("</em>");
}
public void receiveHR() {
if (!continueBody()) { return; }
_bodyBuffer.append("<hr />");
_bodyBuffer.append(getSpan("hr")).append("<hr /></span>");
}
public void receiveH1(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append("<h1>").append(sanitizeString(body)).append("</h1>");
_bodyBuffer.append("<h1 ").append(getClass("h1")).append(" >").append(sanitizeString(body)).append("</span></h1>");
}
public void receiveH2(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append("<h2>").append(sanitizeString(body)).append("</h2>");
_bodyBuffer.append("<h2 ").append(getClass("h2")).append(" >").append(sanitizeString(body)).append("</span></h2>");
}
public void receiveH3(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append("<h3>").append(sanitizeString(body)).append("</h3>");
_bodyBuffer.append("<h3 ").append(getClass("h3")).append(" >").append(sanitizeString(body)).append("</span></h3>");
}
public void receiveH4(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append("<h4>").append(sanitizeString(body)).append("</h4>");
_bodyBuffer.append("<h4 ").append(getClass("h4")).append(" >").append(sanitizeString(body)).append("</span></h4>");
}
public void receiveH5(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append("<h5>").append(sanitizeString(body)).append("</h5>");
_bodyBuffer.append("<h5 ").append(getClass("h5")).append(" >").append(sanitizeString(body)).append("</span></h5>");
}
public void receivePre(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append("<pre>").append(sanitizeString(body)).append("</pre>");
_bodyBuffer.append("<pre ").append(getClass("pre")).append(" >").append(sanitizeString(body)).append("</pre>");
}
public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
if (!continueBody()) { return; }
_bodyBuffer.append("<quote>").append(sanitizeString(text)).append("</quote>");
_bodyBuffer.append("<quote ").append(getClass("quote")).append(" >").append(sanitizeString(text)).append("</quote>");
}
public void receiveCode(String text, String codeLocationSchema, String codeLocation) {
if (!continueBody()) { return; }
_bodyBuffer.append("<code>").append(sanitizeString(text)).append("</code>");
_bodyBuffer.append("<code ").append(getClass("code")).append(" >").append(sanitizeString(text)).append("</code>");
}
public void receiveImage(String alternateText, int attachmentId) {
if (!continueBody()) { return; }
if (_showImages) {
_bodyBuffer.append("<img src=\"").append(getAttachmentURL(attachmentId)).append("\"");
_bodyBuffer.append("<img ").append(getClass("img")).append(" src=\"").append(getAttachmentURL(attachmentId)).append("\"");
if (alternateText != null)
_bodyBuffer.append(" alt=\"").append(sanitizeTagParam(alternateText)).append("\"");
_bodyBuffer.append(" />");
} else {
_bodyBuffer.append("[image: attachment ").append(attachmentId);
_bodyBuffer.append(": ").append(sanitizeString(alternateText));
_bodyBuffer.append(" <a href=\"").append(getEntryURL(true)).append("\">view images</a>]");
_bodyBuffer.append(getSpan("imgSummary")).append("[image: ").append(getSpan("imgSummaryAttachment")).append(" attachment ").append(attachmentId);
_bodyBuffer.append(":</span> ").append(getSpan("imgSummaryAlt")).append(sanitizeString(alternateText));
_bodyBuffer.append("</span> <a ").append(getClass("imgSummaryLink")).append(" href=\"").append(getEntryURL(true)).append("\">view images</a>]</span>");
}
}
@ -179,7 +218,7 @@ public class HTMLRenderer extends EventReceiverImpl {
if (!continueBody()) { return; }
_cutReached = true;
if (_cutBody) {
_bodyBuffer.append("<a href=\"").append(getEntryURL()).append("\">");
_bodyBuffer.append("<a ").append(getClass("cutExplicit")).append(" href=\"").append(getEntryURL()).append("\">");
if ( (summaryText != null) && (summaryText.length() > 0) )
_bodyBuffer.append(sanitizeString(summaryText));
else
@ -187,7 +226,7 @@ public class HTMLRenderer extends EventReceiverImpl {
_bodyBuffer.append("</a>\n");
} else {
if (summaryText != null)
_bodyBuffer.append(sanitizeString(summaryText));
_bodyBuffer.append(getSpan("cutIgnore")).append(sanitizeString(summaryText)).append("</span>\n");
}
}
@ -198,7 +237,7 @@ public class HTMLRenderer extends EventReceiverImpl {
// System.out.println("rv: " + rv + " Cut reached: " + _cutReached + " bodyBufferSize: " + _bodyBuffer.length() + " cutBody? " + _cutBody);
if (!rv && !_cutReached) {
// exceeded the allowed size
_bodyBuffer.append("<a href=\"").append(getEntryURL()).append("\">more inside...</a>");
_bodyBuffer.append("<a ").append(getClass("cutImplicit")).append(" href=\"").append(getEntryURL()).append("\">more inside...</a>\n");
_cutReached = true;
}
return rv;
@ -207,26 +246,26 @@ public class HTMLRenderer extends EventReceiverImpl {
public void receiveNewline() {
if (!continueBody()) { return; }
if (true || (_lastNewlineAt >= _bodyBuffer.length()))
_bodyBuffer.append("<br />\n");
_bodyBuffer.append(getSpan("nl")).append("<br /></span>\n");
else
_lastNewlineAt = _bodyBuffer.length();
}
public void receiveLT() {
if (!continueBody()) { return; }
_bodyBuffer.append("&lt;");
_bodyBuffer.append(getSpan("lt")).append("&lt;</span>");
}
public void receiveGT() {
if (!continueBody()) { return; }
_bodyBuffer.append("&gt;");
_bodyBuffer.append(getSpan("gt")).append("&gt;</span>");
}
public void receiveBegin() {}
public void receiveLeftBracket() {
if (!continueBody()) { return; }
_bodyBuffer.append('[');
_bodyBuffer.append(getSpan("lb")).append("[</span>");
}
public void receiveRightBracket() {
if (!continueBody()) { return; }
_bodyBuffer.append(']');
_bodyBuffer.append(getSpan("rb")).append("]</span>");
}
protected static class Blog {
@ -263,7 +302,8 @@ public class HTMLRenderer extends EventReceiverImpl {
*
*/
public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) {
System.out.println("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description);
byte blogData[] = Base64.decode(hash);
if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) )
return;
@ -283,7 +323,7 @@ public class HTMLRenderer extends EventReceiverImpl {
Hash blog = new Hash(blogData);
if (entryId > 0) {
String pageURL = getPageURL(blog, tag, entryId, -1, -1, true, (_user != null ? _user.getShowImages() : false));
_bodyBuffer.append("<a href=\"").append(pageURL).append("\">");
_bodyBuffer.append("<a ").append(getClass("blogEntryLink")).append(" href=\"").append(pageURL).append("\">");
if ( (description != null) && (description.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(description));
} else if ( (name != null) && (name.trim().length() > 0) ) {
@ -296,29 +336,30 @@ public class HTMLRenderer extends EventReceiverImpl {
String url = getPageURL(blog, null, -1, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false));
_bodyBuffer.append(" [<a href=\"").append(url);
_bodyBuffer.append(getSpan("blogEntrySummary")).append(" [<a ").append(getClass("blogLink")).append(" href=\"").append(url);
_bodyBuffer.append("\">");
if ( (name != null) && (name.trim().length() > 0) )
_bodyBuffer.append(sanitizeString(name));
else
_bodyBuffer.append("view");
_bodyBuffer.append("</a> (<a href=\"").append(getMetadataURL(blog)).append("\">meta</a>)");
_bodyBuffer.append("</a> (<a ").append(getClass("blogMeta")).append(" href=\"").append(getMetadataURL(blog)).append("\">meta</a>)");
if ( (tag != null) && (tag.trim().length() > 0) ) {
url = getPageURL(blog, tag, -1, -1, -1, false, false);
_bodyBuffer.append(" <a href=\"").append(url);
_bodyBuffer.append(" <a ").append(getClass("blogTagLink")).append(" href=\"").append(url);
_bodyBuffer.append("\">Tag: ").append(sanitizeString(tag)).append("</a>");
}
if ( (locations != null) && (locations.size() > 0) ) {
_bodyBuffer.append(" Archives: ");
_bodyBuffer.append(getSpan("blogArchive")).append(" Archives: ");
for (int i = 0; i < locations.size(); i++) {
SafeURL surl = (SafeURL)locations.get(i);
if (_user.getAuthenticated() && _user.getAllowAccessRemote())
_bodyBuffer.append("<a href=\"").append(getArchiveURL(blog, surl)).append("\">").append(sanitizeString(surl.toString())).append("</a> ");
if (_user.getAuthenticated() && BlogManager.instance().authorizeRemote(_user) )
_bodyBuffer.append("<a ").append(getClass("blogArchiveView")).append(" href=\"").append(getArchiveURL(blog, surl)).append("\">").append(sanitizeString(surl.toString())).append("</a> ");
else
_bodyBuffer.append(sanitizeString(surl.toString())).append(' ');
_bodyBuffer.append(getSpan("blogArchiveURL")).append(sanitizeString(surl.toString())).append("</span> ");
}
_bodyBuffer.append("</span>");
}
_bodyBuffer.append("] ");
_bodyBuffer.append("]</span> ");
}
protected static class ArchiveRef {
@ -346,18 +387,24 @@ public class HTMLRenderer extends EventReceiverImpl {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(anchorText)).append(" [Archive ");
_bodyBuffer.append(getSpan("archive")).append(sanitizeString(anchorText)).append("</span>");
_bodyBuffer.append(getSpan("archiveSummary")).append(" [Archive ");
if (name != null)
_bodyBuffer.append(sanitizeString(name));
_bodyBuffer.append(getSpan("archiveSummaryName")).append(sanitizeString(name)).append("</span>");
if (location != null) {
_bodyBuffer.append(" at ");
SafeURL surl = new SafeURL(locationSchema + "://" + location);
_bodyBuffer.append("<a href=\"").append(getArchiveURL(null, surl));
_bodyBuffer.append("<a ").append(getClass("archiveSummaryLink")).append(" href=\"").append(getArchiveURL(null, surl));
_bodyBuffer.append("\">").append(sanitizeString(surl.toString())).append("</a>");
if (_user.getAuthenticated()) {
_bodyBuffer.append(" <a ").append(getClass("archiveBookmarkLink")).append(" href=\"");
_bodyBuffer.append(getBookmarkURL(sanitizeString(name), surl.getLocation(), surl.getSchema(), "syndiearchive"));
_bodyBuffer.append("\">bookmark it</a>");
}
}
if (description != null)
_bodyBuffer.append(": ").append(sanitizeString(description));
_bodyBuffer.append("]");
_bodyBuffer.append(": ").append(getSpan("archiveSummaryDesc")).append(sanitizeString(description)).append("</span>");
_bodyBuffer.append("]</span>");
}
protected static class Link {
@ -377,7 +424,7 @@ public class HTMLRenderer extends EventReceiverImpl {
_links.add(l);
if (!continueBody()) { return; }
if ( (schema == null) || (location == null) ) return;
_bodyBuffer.append("<a href=\"externallink.jsp?schema=");
_bodyBuffer.append("<a ").append(getClass("externalLink")).append(" href=\"externallink.jsp?schema=");
_bodyBuffer.append(sanitizeURL(schema)).append("&location=");
_bodyBuffer.append(sanitizeURL(location)).append("&description=");
_bodyBuffer.append(sanitizeURL(text)).append("\">").append(sanitizeString(text)).append("</a>");
@ -387,48 +434,69 @@ public class HTMLRenderer extends EventReceiverImpl {
public String name;
public String schema;
public String location;
public String protocol;
public int hashCode() { return -1; }
public boolean equals(Object o) {
Address a = (Address)o;
return DataHelper.eq(schema, a.schema) && DataHelper.eq(location, a.location) && DataHelper.eq(name, a.name);
return DataHelper.eq(schema, a.schema) && DataHelper.eq(location, a.location) && DataHelper.eq(protocol, a.protocol) && DataHelper.eq(name, a.name);
}
}
public void receiveAddress(String name, String schema, String location, String anchorText) {
public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
Address a = new Address();
a.name = name;
a.schema = schema;
a.location = location;
a.protocol = protocol;
if (!_addresses.contains(a))
_addresses.add(a);
if (!continueBody()) { return; }
if ( (schema == null) || (location == null) ) return;
_bodyBuffer.append("<a href=\"addaddress.jsp?schema=");
_bodyBuffer.append(sanitizeURL(schema)).append("&name=");
_bodyBuffer.append(sanitizeURL(name)).append("&location=");
_bodyBuffer.append(sanitizeURL(location)).append("\">").append(sanitizeString(anchorText)).append("</a>");
String knownName = null;
if (_user != null)
knownName = _user.getPetNameDB().getNameByLocation(location);
if (knownName != null) {
_bodyBuffer.append(getSpan("addr")).append(sanitizeString(anchorText)).append("</span>");
_bodyBuffer.append(getSpan("addrKnownName")).append("(").append(sanitizeString(knownName)).append(")</span>");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receiving address [" + location + "]");
_bodyBuffer.append("<a ").append(getClass("addrAdd")).append(" href=\"addresses.jsp?");
if (schema != null)
_bodyBuffer.append("network=").append(sanitizeTagParam(schema)).append('&');
if (name != null)
_bodyBuffer.append("name=").append(sanitizeTagParam(name)).append('&');
if (protocol != null)
_bodyBuffer.append("protocol=").append(sanitizeTagParam(protocol)).append('&');
if (location != null)
_bodyBuffer.append("location=").append(sanitizeTagParam(location));
_bodyBuffer.append("\">").append(sanitizeString(anchorText)).append("</a>");
}
}
public void receiveAttachment(int id, String anchorText) {
if (!continueBody()) { return; }
Attachment attachments[] = _entry.getAttachments();
if ( (id < 0) || (id >= attachments.length)) {
_bodyBuffer.append(sanitizeString(anchorText));
_bodyBuffer.append(getSpan("attachmentUnknown")).append(sanitizeString(anchorText)).append("</span>");
} else {
_bodyBuffer.append("<a href=\"").append(getAttachmentURL(id)).append("\">");
_bodyBuffer.append("<a ").append(getClass("attachmentView")).append(" href=\"").append(getAttachmentURL(id)).append("\">");
_bodyBuffer.append(sanitizeString(anchorText)).append("</a>");
_bodyBuffer.append(" (").append(attachments[id].getDataLength()/1024).append("KB, ");
_bodyBuffer.append(" \"").append(sanitizeString(attachments[id].getName())).append("\", ");
_bodyBuffer.append(sanitizeString(attachments[id].getMimeType())).append(")");
_bodyBuffer.append(getSpan("attachmentSummary")).append(" (");
_bodyBuffer.append(getSpan("attachmentSummarySize")).append(attachments[id].getDataLength()/1024).append("KB</span>, ");
_bodyBuffer.append(getSpan("attachmentSummaryName")).append(" \"").append(sanitizeString(attachments[id].getName())).append("\"</span>, ");
_bodyBuffer.append(getSpan("attachmentSummaryDesc")).append(" \"").append(sanitizeString(attachments[id].getDescription())).append("\"</span>, ");
_bodyBuffer.append(getSpan("attachmentSummaryType")).append(sanitizeString(attachments[id].getMimeType())).append("</span>)</span>");
}
}
public void receiveEnd() {
_postBodyBuffer.append("</td></tr>\n");
_postBodyBuffer.append("</td></tr>\n<!-- end of the post body -->");
if (_cutBody) {
_postBodyBuffer.append("<tr class=\"syndieEntryAttachmentsCell\">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" class=\"syndieEntryAttachmentsCell\">");
_postBodyBuffer.append("<a href=\"").append(getEntryURL()).append("\">View details...</a> ");
_postBodyBuffer.append("<!-- beginning of the post summary -->\n");
_postBodyBuffer.append("<tr ").append(getClass("summ")).append(">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summ")).append(" >");
_postBodyBuffer.append("<a ").append(getClass("summLink")).append(" href=\"").append(getEntryURL()).append("\">View details...</a> ");
_postBodyBuffer.append(getSpan("summ"));
if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
int num = _entry.getAttachments().length;
if (num == 1)
@ -447,7 +515,7 @@ public class HTMLRenderer extends EventReceiverImpl {
if (links == 1)
_postBodyBuffer.append("1 external link ");
else if (links > 1)
_postBodyBuffer.append(links).append(" external links");
_postBodyBuffer.append(links).append(" external links ");
int addrs = _addresses.size();
if (addrs == 1)
@ -473,11 +541,13 @@ public class HTMLRenderer extends EventReceiverImpl {
String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) )
_postBodyBuffer.append(" <a href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">(view parent)</a>\n");
_postBodyBuffer.append(" <a ").append(getClass("summParent")).append(" href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">(view parent)</a>\n");
_postBodyBuffer.append("</td></tr>\n");
_postBodyBuffer.append("</span></td></tr>\n");
_postBodyBuffer.append("<!-- end of the post summary -->\n");
} else {
_postBodyBuffer.append("<tr class=\"syndieEntryAttachmentsCell\">\n");
_postBodyBuffer.append("<!-- beginning of the post summary details -->\n");
_postBodyBuffer.append("<tr ").append(getClass("summDetail")).append(">\n");
_postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
_postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_BLOG);
_postBodyBuffer.append("\" value=\"");
@ -493,11 +563,11 @@ public class HTMLRenderer extends EventReceiverImpl {
else
_postBodyBuffer.append("unknown");
_postBodyBuffer.append("\" />\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" class=\"syndieEntryAttachmentsCell\">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summDetail")).append(" >\n");
if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
_postBodyBuffer.append("<b>Attachments:</b> ");
_postBodyBuffer.append("<select name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
_postBodyBuffer.append(getSpan("summDetailAttachment")).append("Attachments:</span> ");
_postBodyBuffer.append("<select ").append(getClass("summDetailAttachmentId")).append(" name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
for (int i = 0; i < _entry.getAttachments().length; i++) {
_postBodyBuffer.append("<option value=\"").append(i).append("\">");
Attachment a = _entry.getAttachments()[i];
@ -510,53 +580,77 @@ public class HTMLRenderer extends EventReceiverImpl {
_postBodyBuffer.append(", type ").append(sanitizeString(a.getMimeType())).append(")</option>\n");
}
_postBodyBuffer.append("</select>\n");
_postBodyBuffer.append("<input type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
_postBodyBuffer.append("<input ").append(getClass("summDetailAttachmentDl")).append(" type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
}
if (_blogs.size() > 0) {
_postBodyBuffer.append("<b>Blog references:</b> ");
_postBodyBuffer.append(getSpan("summDetailBlog")).append("Blog references:</span>");
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
_postBodyBuffer.append("<a href=\"").append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
_postBodyBuffer.append("<a ").append(getClass("summDetailBlogLink")).append(" href=\"");
boolean expanded = (_user != null ? _user.getShowExpanded() : false);
boolean images = (_user != null ? _user.getShowImages() : false);
_postBodyBuffer.append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, expanded, images));
_postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_links.size() > 0) {
_postBodyBuffer.append("<b>External links:</b> ");
_postBodyBuffer.append(getSpan("summDetailExternal")).append("External links:</span> ");
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
_postBodyBuffer.append("<a href=\"externallink.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(l.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(l.location));
_postBodyBuffer.append("<a ").append(getClass("summDetailExternalLink")).append(" href=\"externallink.jsp?");
if (l.schema != null)
_postBodyBuffer.append("schema=").append(sanitizeURL(l.schema)).append('&');
if (l.location != null)
_postBodyBuffer.append("location=").append(sanitizeURL(l.location)).append('&');
_postBodyBuffer.append("\">").append(sanitizeString(l.location));
_postBodyBuffer.append(" (").append(sanitizeString(l.schema)).append(")</a> ");
_postBodyBuffer.append(getSpan("summDetailExternalNet")).append(" (").append(sanitizeString(l.schema)).append(")</span></a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_addresses.size() > 0) {
_postBodyBuffer.append("<b>Addresses:</b>");
_postBodyBuffer.append(getSpan("summDetailAddr")).append("Addresses:</span>");
for (int i = 0; i < _addresses.size(); i++) {
Address a = (Address)_addresses.get(i);
_postBodyBuffer.append(" <a href=\"addaddress.jsp?schema=");
_postBodyBuffer.append(sanitizeURL(a.schema)).append("&location=");
_postBodyBuffer.append(sanitizeURL(a.location)).append("&name=");
_postBodyBuffer.append(sanitizeURL(a.name));
_postBodyBuffer.append("\">").append(sanitizeString(a.name));
String knownName = null;
if (_user != null)
knownName = _user.getPetNameDB().getNameByLocation(a.location);
if (knownName != null) {
_postBodyBuffer.append(' ').append(getSpan("summDetailAddrKnown"));
_postBodyBuffer.append(sanitizeString(knownName)).append("</span>");
} else {
_postBodyBuffer.append(" <a ").append(getClass("summDetailAddrLink")).append(" href=\"addresses.jsp?");
if (a.schema != null)
_postBodyBuffer.append("network=").append(sanitizeTagParam(a.schema)).append('&');
if (a.location != null)
_postBodyBuffer.append("location=").append(sanitizeTagParam(a.location)).append('&');
if (a.name != null)
_postBodyBuffer.append("name=").append(sanitizeTagParam(a.name)).append('&');
if (a.protocol != null)
_postBodyBuffer.append("protocol=").append(sanitizeTagParam(a.protocol)).append('&');
_postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
}
}
_postBodyBuffer.append("<br />\n");
}
if (_archives.size() > 0) {
_postBodyBuffer.append("<b>Archives:</b>");
_postBodyBuffer.append(getSpan("summDetailArchive")).append("Archives:</span>");
for (int i = 0; i < _archives.size(); i++) {
ArchiveRef a = (ArchiveRef)_archives.get(i);
_postBodyBuffer.append(" <a href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
_postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveLink")).append(" href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
_postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
if (a.description != null)
_postBodyBuffer.append(": ").append(sanitizeString(a.description));
_postBodyBuffer.append(": ").append(getSpan("summDetailArchiveDesc")).append(sanitizeString(a.description)).append("</span>");
if (null == _user.getPetNameDB().getNameByLocation(a.location)) {
_postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveBookmark")).append(" href=\"");
_postBodyBuffer.append(getBookmarkURL(a.name, a.location, a.locationSchema, "syndiearchive"));
_postBodyBuffer.append("\">bookmark it</a>");
}
}
_postBodyBuffer.append("<br />\n");
}
@ -564,21 +658,23 @@ public class HTMLRenderer extends EventReceiverImpl {
if (_entry != null) {
List replies = _archive.getIndex().getReplies(_entry.getURI());
if ( (replies != null) && (replies.size() > 0) ) {
_postBodyBuffer.append("<b>Replies:</b> ");
_postBodyBuffer.append(getSpan("summDetailReplies")).append("Replies:</span> ");
for (int i = 0; i < replies.size(); i++) {
BlogURI reply = (BlogURI)replies.get(i);
_postBodyBuffer.append("<a href=\"");
_postBodyBuffer.append("<a ").append(getClass("summDetailReplyLink")).append(" href=\"");
_postBodyBuffer.append(getPageURL(reply.getKeyHash(), null, reply.getEntryId(), -1, -1, true, _user.getShowImages()));
_postBodyBuffer.append("\">");
_postBodyBuffer.append(getSpan("summDetailReplyAuthor"));
BlogInfo replyAuthor = _archive.getBlogInfo(reply);
if (replyAuthor != null) {
_postBodyBuffer.append(sanitizeString(replyAuthor.getProperty(BlogInfo.NAME)));
} else {
_postBodyBuffer.append(reply.getKeyHash().toBase64().substring(0,16));
}
_postBodyBuffer.append(" on ");
_postBodyBuffer.append("</span> on ");
_postBodyBuffer.append(getSpan("summDetailReplyDate"));
_postBodyBuffer.append(getEntryDate(reply.getEntryId()));
_postBodyBuffer.append("</a> ");
_postBodyBuffer.append("</a></span> ");
}
_postBodyBuffer.append("<br />");
}
@ -586,21 +682,38 @@ public class HTMLRenderer extends EventReceiverImpl {
String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) ) {
_postBodyBuffer.append(" <a href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">(view parent)</a><br />\n");
_postBodyBuffer.append(" <a ").append(getClass("summDetailParent")).append(" href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">(view parent)</a><br />\n");
}
_postBodyBuffer.append("</td>\n</form>\n</tr>\n");
_postBodyBuffer.append("<!-- end of the post summary details -->\n");
}
_postBodyBuffer.append("</table>\n");
}
public void receiveHeader(String header, String value) {
//System.err.println("Receive header [" + header + "] = [" + value + "]");
_headers.put(header, value);
if (HEADER_PETNAME.equals(header)) {
StringTokenizer tok = new StringTokenizer(value, "\t\n");
if (tok.countTokens() != 4)
return;
String name = tok.nextToken();
String net = tok.nextToken();
String proto = tok.nextToken();
String loc = tok.nextToken();
Address a = new Address();
a.name = sanitizeString(name, false);
a.schema = sanitizeString(net, false);
a.protocol = sanitizeString(proto, false);
a.location = sanitizeString(loc, false);
_addresses.add(a);
} else {
_headers.put(header, value);
}
}
public void receiveHeaderEnd() {
_preBodyBuffer.append("<table width=\"100%\" border=\"0\">\n");
_preBodyBuffer.append("<table ").append(getClass("overall")).append(" width=\"100%\" border=\"0\">\n");
renderSubjectCell();
renderMetaCell();
renderPreBodyCell();
@ -609,46 +722,76 @@ public class HTMLRenderer extends EventReceiverImpl {
public static final String HEADER_SUBJECT = "Subject";
public static final String HEADER_BGCOLOR = "bgcolor";
public static final String HEADER_IN_REPLY_TO = "InReplyTo";
public static final String HEADER_STYLE = "Style";
public static final String HEADER_PETNAME = "PetName";
private void renderSubjectCell() {
_preBodyBuffer.append("<tr class=\"syndieEntrySubjectCell\"><td align=\"left\" valign=\"top\" class=\"syndieEntrySubjectCell\" width=\"400\"> ");
_preBodyBuffer.append("<form action=\"index.jsp\">");
_preBodyBuffer.append("<tr ").append(getClass("subject")).append(">");
_preBodyBuffer.append("<td ").append(getClass("subject")).append(" align=\"left\" valign=\"top\" width=\"400\"> ");
String subject = (String)_headers.get(HEADER_SUBJECT);
if (subject == null)
subject = "[no subject]";
_preBodyBuffer.append(sanitizeString(subject));
_preBodyBuffer.append("</td>\n");
_preBodyBuffer.append(getSpan("subjectText")).append(sanitizeString(subject));
_preBodyBuffer.append("</span></td>\n");
}
private void renderPreBodyCell() {
_preBodyBuffer.append("</form>");
String bgcolor = (String)_headers.get(HEADER_BGCOLOR);
if (_cutBody)
_preBodyBuffer.append("<tr class=\"syndieEntrySummaryCell\"><td colspan=\"2\" align=\"left\" valign=\"top\" class=\"syndieEntrySummaryCell\" " + (bgcolor != null ? "bgcolor=\"" + sanitizeTagParam(bgcolor) + "\"" : "") + "\">");
else
_preBodyBuffer.append("<tr class=\"syndieEntryBodyCell\"><td colspan=\"2\" align=\"left\" valign=\"top\" class=\"syndieEntryBodyCell\" " + (bgcolor != null ? "bgcolor=\"" + sanitizeTagParam(bgcolor) + "\"" : "") + "\">");
_preBodyBuffer.append("<tr ").append(getClass("body")).append(" >");
_preBodyBuffer.append("<td colspan=\"2\" align=\"left\" valign=\"top\" ").append(getClass("body"));
_preBodyBuffer.append((bgcolor != null ? " bgcolor=\"" + sanitizeTagParam(bgcolor) + "\"" : "") + ">");
}
private void renderMetaCell() {
String tags[] = (_entry != null ? _entry.getTags() : null);
if ( (tags != null) && (tags.length > 0) )
_preBodyBuffer.append("<form action=\"index.jsp\">");
_preBodyBuffer.append("<td nowrap=\"true\" align=\"right\" valign=\"top\" class=\"syndieEntryMetaCell\">\n");
_preBodyBuffer.append("<td nowrap=\"nowrap\" align=\"right\" valign=\"top\" ");
_preBodyBuffer.append(getClass("meta")).append(">\n");
String knownName = null;
if ( (_entry != null) && (_user != null) )
knownName = _user.getPetNameDB().getNameByLocation(_entry.getURI().getKeyHash().toBase64());
//if (knownName != null)
// _preBodyBuffer.append("Pet name: ").append(sanitizeString(knownName)).append(" ");
BlogInfo info = null;
if (_entry != null)
info = _archive.getBlogInfo(_entry.getURI());
if (info != null) {
_preBodyBuffer.append("<a href=\"").append(getMetadataURL()).append("\">");
String nameStr = info.getProperty("Name");
if (nameStr == null)
_preBodyBuffer.append("[no name]");
else
_preBodyBuffer.append(sanitizeString(nameStr));
_preBodyBuffer.append("<a ").append(getClass("metaLink")).append(" href=\"").append(getMetadataURL()).append("\">");
if (knownName != null) {
_preBodyBuffer.append(getSpan("metaKnown")).append(sanitizeString(knownName)).append("</span>");
} else {
String nameStr = info.getProperty("Name");
if (nameStr == null)
_preBodyBuffer.append(getSpan("metaUnknown")).append("[no name]</span>");
else
_preBodyBuffer.append(getSpan("metaUnknown")).append(sanitizeString(nameStr)).append("</span>");
}
_preBodyBuffer.append("</a>");
} else {
_preBodyBuffer.append("[unknown blog]");
_preBodyBuffer.append(getSpan("metaUnknown")).append("[unknown blog]</span>");
}
if ( (_user != null) && (_user.getAuthenticated()) && (_entry != null) ) {
PetName pn = _user.getPetNameDB().get(knownName);
if ( (pn == null) || (!pn.isMember("Favorites")) )
_preBodyBuffer.append(" <input ").append(getClass("bookmark")).append(" type=\"submit\" name=\"action\" value=\"Bookmark blog\" />");
if ( (pn == null) || (!pn.isMember("Ignore")) )
_preBodyBuffer.append(" <input ").append(getClass("ignore")).append(" type=\"submit\" name=\"action\" value=\"Ignore blog\" />");
else
_preBodyBuffer.append(" <input ").append(getClass("unignore")).append(" type=\"submit\" name=\"action\" value=\"Unignore blog\" />");
_preBodyBuffer.append(" <input type=\"hidden\" name=\"blog\" value=\"").append(_entry.getURI().getKeyHash().toBase64()).append("\" />");
if (info != null)
_preBodyBuffer.append(" <input type=\"hidden\" name=\"name\" value=\"").append(sanitizeTagParam(info.getProperty("Name"))).append("\" />");
}
if ( (tags != null) && (tags.length > 0) ) {
_preBodyBuffer.append(" Tags: ");
_preBodyBuffer.append("<select name=\"selector\">");
_preBodyBuffer.append(getSpan("metaTags")).append(" Tags: ");
_preBodyBuffer.append("<select ").append(getClass("metaTagList")).append(" name=\"selector\">");
for (int i = 0; tags != null && i < tags.length; i++) {
_preBodyBuffer.append("<option value=\"blogtag://");
_preBodyBuffer.append(_entry.getURI().getKeyHash().toBase64());
@ -666,7 +809,7 @@ public class HTMLRenderer extends EventReceiverImpl {
*/
}
_preBodyBuffer.append("</select>");
_preBodyBuffer.append("<input type=\"submit\" value=\"View\" />\n");
_preBodyBuffer.append("<input ").append(getClass("metaTagView")).append(" type=\"submit\" value=\"View\" /></span>\n");
//_preBodyBuffer.append("</i>");
}
_preBodyBuffer.append(" ");
@ -676,15 +819,18 @@ public class HTMLRenderer extends EventReceiverImpl {
_preBodyBuffer.append(" <a href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">In reply to</a>\n");
*/
_preBodyBuffer.append(getSpan("metaDate"));
if (_entry != null)
_preBodyBuffer.append(getEntryDate(_entry.getURI().getEntryId()));
else
_preBodyBuffer.append(getEntryDate(new Date().getTime()));
if ( (_user != null) && (_user.getAuthenticated()) )
_preBodyBuffer.append(" <a href=\"").append(getPostURL(_user.getBlog(), true)).append("\">Reply</a>\n");
_preBodyBuffer.append("</span>");
if ( (_user != null) && (_user.getAuthenticated()) ) {
_preBodyBuffer.append(" <a ").append(getClass("replyLink"));
_preBodyBuffer.append(" href=\"").append(getPostURL(_user.getBlog(), true)).append("\">Reply</a>\n");
}
_preBodyBuffer.append("\n</td>");
if ( (tags != null) && (tags.length > 0) )
_preBodyBuffer.append("</form>");
_preBodyBuffer.append("</tr>\n");
}
@ -694,9 +840,10 @@ public class HTMLRenderer extends EventReceiverImpl {
try {
String str = _dateFormat.format(new Date(when));
long dayBegin = _dateFormat.parse(str).getTime();
return str + "." + (when - dayBegin);
return str + " [" + (when - dayBegin) + "]";
} catch (ParseException pe) {
pe.printStackTrace();
if (_log.shouldLog(Log.WARN))
_log.warn("Error formatting", pe);
// wtf
return "unknown";
}
@ -726,8 +873,12 @@ public class HTMLRenderer extends EventReceiverImpl {
return str;
}
public static final String sanitizeURL(String str) { return Base64.encode(DataHelper.getUTF8(str)); }
public static final String sanitizeURL(String str) {
if (str == null) return "";
return Base64.encode(DataHelper.getUTF8(str));
}
public static final String sanitizeTagParam(String str) {
if (str == null) return "";
str = str.replace('&', '_'); // this should be &amp;
if (str.indexOf('\"') < 0)
return sanitizeString(str);
@ -735,8 +886,42 @@ public class HTMLRenderer extends EventReceiverImpl {
return sanitizeString(str);
}
private String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
private String getEntryURL(boolean showImages) {
public static final String sanitizeXML(String orig) {
if (orig == null) return "";
if (orig.indexOf('&') < 0) return orig;
StringBuffer rv = new StringBuffer(orig.length()+32);
for (int i = 0; i < orig.length(); i++) {
if (orig.charAt(i) == '&')
rv.append("&amp;");
else
rv.append(orig.charAt(i));
}
return rv.toString();
}
public static final String sanitizeXML(StringBuffer orig) {
if (orig == null) return "";
if (orig.indexOf("&") < 0) return orig.toString();
for (int i = 0; i < orig.length(); i++) {
if (orig.charAt(i) == '&') {
orig = orig.replace(i, i+1, "&amp;");
i += "&amp;".length();
}
}
return orig.toString();
}
private static final String STYLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
public static String sanitizeStyle(String style) {
if ( (style == null) || (style.trim().length() <= 0) ) return null;
char c[] = style.toCharArray();
for (int i = 0; i < c.length; i++)
if (STYLE_CHARS.indexOf(c[i]) < 0)
c[i] = '_';
return new String(c);
}
protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
protected String getEntryURL(boolean showImages) {
if (_entry == null) return "unknown";
return "index.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" +
Base64.encode(_entry.getURI().getKeyHash().getData()) +
@ -820,4 +1005,11 @@ public class HTMLRenderer extends EventReceiverImpl {
+ "&schema=" + sanitizeTagParam(archiveLocation.getSchema())
+ "&location=" + sanitizeTagParam(archiveLocation.getLocation());
}
public static String getBookmarkURL(String name, String location, String schema, String protocol) {
return "addresses.jsp?name=" + sanitizeTagParam(name)
+ "&network=" + sanitizeTagParam(schema)
+ "&protocol=" + sanitizeTagParam(protocol)
+ "&location=" + sanitizeTagParam(location);
}
}

View File

@ -0,0 +1,310 @@
package net.i2p.syndie.sml;
import java.io.*;
import java.util.*;
import java.text.SimpleDateFormat;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
/**
*
*/
public class RSSRenderer extends HTMLRenderer {
public RSSRenderer(I2PAppContext ctx) {
super(ctx);
}
public void render(User user, Archive archive, EntryContainer entry, String urlPrefix, Writer out) throws IOException {
if (entry == null) return;
prepare(user, archive, entry, entry.getEntry().getText(), out, true, false);
out.write(" <item>\n");
out.write(" <title>" + sanitizeXML(sanitizeString((String)_headers.get(HEADER_SUBJECT))) + "</title>\n");
out.write(" <link>" + urlPrefix + sanitizeXML(getEntryURL()) + "</link>\n");
out.write(" <guid isPermalink=\"false\">" + urlPrefix + entry.getURI().toString() + "</guid>\n");
out.write(" <pubDate>" + getRFC822Date(entry.getURI().getEntryId()) + "</pubDate>\n");
String author = user.getPetNameDB().getNameByLocation(entry.getURI().getKeyHash().toBase64());
if (author == null) {
BlogInfo info = archive.getBlogInfo(entry.getURI());
if (info != null)
author = info.getProperty(BlogInfo.NAME);
}
if (author == null)
author = entry.getURI().getKeyHash().toBase64();
out.write(" <author>" + sanitizeXML(sanitizeString(author)) + "@syndie.invalid</author>\n");
String tags[] = entry.getTags();
if (tags != null)
for (int i = 0; i < tags.length; i++)
out.write(" <category>" + sanitizeXML(sanitizeString(tags[i])) + "</category>\n");
out.write(" <description>" + sanitizeXML(_bodyBuffer.toString()) + "</description>\n");
//renderEnclosures(user, entry, urlPrefix, out);
out.write(" </item>\n");
}
public void receiveBold(String text) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(text));
}
public void receiveItalic(String text) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(text));
}
public void receiveUnderline(String text) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(text));
}
public void receiveHR() {
if (!continueBody()) { return; }
}
public void receiveH1(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(body));
}
public void receiveH2(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(body));
}
public void receiveH3(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(body));
}
public void receiveH4(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(body));
}
public void receiveH5(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(body));
}
public void receivePre(String body) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(body));
}
public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(text));
}
public void receiveCode(String text, String codeLocationSchema, String codeLocation) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(text));
}
public void receiveImage(String alternateText, int attachmentId) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(alternateText));
}
public void receiveCut(String summaryText) {
if (!continueBody()) { return; }
_cutReached = true;
if (_cutBody) {
if ( (summaryText != null) && (summaryText.length() > 0) )
_bodyBuffer.append(sanitizeString(summaryText));
else
_bodyBuffer.append("more inside...");
} else {
if (summaryText != null)
_bodyBuffer.append(sanitizeString(summaryText));
}
}
/** are we either before the cut or rendering without cutting? */
protected boolean continueBody() {
boolean rv = ( (!_cutReached) && (_bodyBuffer.length() <= _cutSize) ) || (!_cutBody);
//if (!rv)
// System.out.println("rv: " + rv + " Cut reached: " + _cutReached + " bodyBufferSize: " + _bodyBuffer.length() + " cutBody? " + _cutBody);
if (!rv && !_cutReached) {
// exceeded the allowed size
_bodyBuffer.append("more inside...");
_cutReached = true;
}
return rv;
}
public void receiveNewline() {
if (!continueBody()) { return; }
if (true || (_lastNewlineAt >= _bodyBuffer.length()))
_bodyBuffer.append("\n");
else
_lastNewlineAt = _bodyBuffer.length();
}
public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) {
byte blogData[] = Base64.decode(hash);
if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) )
return;
Blog b = new Blog();
b.name = name;
b.hash = hash;
b.tag = tag;
b.entryId = entryId;
b.locations = locations;
if (!_blogs.contains(b))
_blogs.add(b);
if (!continueBody()) { return; }
if (hash == null) return;
Hash blog = new Hash(blogData);
if ( (description != null) && (description.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(description));
} else if ( (name != null) && (name.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(name));
} else {
_bodyBuffer.append("[view entry]");
}
}
public void receiveArchive(String name, String description, String locationSchema, String location,
String postingKey, String anchorText) {
ArchiveRef a = new ArchiveRef();
a.name = name;
a.description = description;
a.locationSchema = locationSchema;
a.location = location;
if (!_archives.contains(a))
_archives.add(a);
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(anchorText));
}
public void receiveLink(String schema, String location, String text) {
Link l = new Link();
l.schema = schema;
l.location = location;
if (!_links.contains(l))
_links.add(l);
if (!continueBody()) { return; }
if ( (schema == null) || (location == null) ) return;
_bodyBuffer.append(sanitizeString(text));
}
public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
Address a = new Address();
a.name = name;
a.schema = schema;
a.location = location;
a.protocol = protocol;
if (!_addresses.contains(a))
_addresses.add(a);
if (!continueBody()) { return; }
if ( (schema == null) || (location == null) ) return;
String knownName = null;
if (_user != null)
knownName = _user.getPetNameDB().getNameByLocation(location);
if (knownName != null) {
_bodyBuffer.append(sanitizeString(anchorText));
} else {
_bodyBuffer.append(sanitizeString(anchorText));
}
}
public void receiveAttachment(int id, String anchorText) {
if (!continueBody()) { return; }
_bodyBuffer.append(sanitizeString(anchorText));
}
// Mon, 03 Jun 2005 13:04:11 +0000
private static final SimpleDateFormat _rfc822Date = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
private static final String getRFC822Date(long when) {
synchronized (_rfc822Date) {
return _rfc822Date.format(new Date(when));
}
}
private void renderEnclosures(User user, EntryContainer entry, String urlPrefix, Writer out) throws IOException {
if (entry.getAttachments() != null) {
for (int i = 0; i < _entry.getAttachments().length; i++) {
Attachment a = _entry.getAttachments()[i];
out.write(" <enclosure url=\"" + urlPrefix + sanitizeXML(getAttachmentURL(i))
+ "\" length=\"" + a.getDataLength()
+ "\" type=\"" + sanitizeTagParam(a.getMimeType()) + "\" syndietype=\"attachment\" />\n");
}
}
if (_blogs.size() > 0) {
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
out.write(" <enclosure url=\"" + urlPrefix +
sanitizeXML(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId,
-1, -1, (_user != null ? _user.getShowExpanded() : false),
(_user != null ? _user.getShowImages() : false)))
+ "\" length=\"1\" type=\"text/html\" syndietype=\"blog\" />\n");
}
}
if (_links.size() > 0) {
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
StringBuffer url = new StringBuffer(128);
url.append("externallink.jsp?schema=");
url.append(sanitizeURL(l.schema)).append("&location=");
url.append(sanitizeURL(l.location));
out.write(" <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"link\" />\n");
}
}
if (_addresses.size() > 0) {
for (int i = 0; i < _addresses.size(); i++) {
Address a = (Address)_addresses.get(i);
String knownName = null;
if (_user != null)
knownName = _user.getPetNameDB().getNameByLocation(a.location);
if (knownName == null) {
StringBuffer url = new StringBuffer(128);
url.append("addresses.jsp?network=");
url.append(sanitizeTagParam(a.schema)).append("&location=");
url.append(sanitizeTagParam(a.location)).append("&name=");
url.append(sanitizeTagParam(a.name)).append("&protocol=");
url.append(sanitizeTagParam(a.protocol));
out.write(" <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"address\" />\n");
}
}
}
if (_archives.size() > 0) {
for (int i = 0; i < _archives.size(); i++) {
ArchiveRef a = (ArchiveRef)_archives.get(i);
String url = getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location));
out.write(" <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"archive\" />\n");
}
}
if (_entry != null) {
List replies = _archive.getIndex().getReplies(_entry.getURI());
if ( (replies != null) && (replies.size() > 0) ) {
for (int i = 0; i < replies.size(); i++) {
BlogURI reply = (BlogURI)replies.get(i);
String url = getPageURL(reply.getKeyHash(), null, reply.getEntryId(), -1, -1, true, _user.getShowImages());
out.write(" <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"reply\" />\n");
}
}
}
String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) ) {
String url = getPageURL(sanitizeTagParam(inReplyTo));
out.write(" <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"parent\" />\n");
}
}
public void receiveHeaderEnd() {}
public void receiveEnd() {}
public static void main(String args[]) {
test("");
test("&");
test("a&");
test("&a");
test("a&a");
test("aa&aa");
}
private static final void test(String str) {
StringBuffer t = new StringBuffer(str);
String sanitized = sanitizeXML(t);
System.out.println("[" + str + "] --> [" + sanitized + "]");
}
}

View File

@ -3,6 +3,8 @@ package net.i2p.syndie.sml;
import java.lang.String;
import java.util.*;
import net.i2p.syndie.data.*;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Parse out the SML from the text, firing off info to the receiver whenever certain
@ -12,6 +14,7 @@ import net.i2p.syndie.data.*;
*
*/
public class SMLParser {
private Log _log;
private static final char TAG_BEGIN = '[';
private static final char TAG_END = ']';
private static final char LT = '<';
@ -23,6 +26,10 @@ public class SMLParser {
private static final char NL = '\n';
private static final char CR = '\n';
private static final char LF = '\f';
public SMLParser(I2PAppContext ctx) {
_log = ctx.logManager().getLog(SMLParser.class);
}
public void parse(String rawSML, EventReceiver receiver) {
receiver.receiveBegin();
@ -211,6 +218,7 @@ public class SMLParser {
private static final String P_ADDRESS_NAME = "name";
private static final String P_ADDRESS_LOCATION = "location";
private static final String P_ADDRESS_SCHEMA = "schema";
private static final String P_ADDRESS_PROTOCOL = "proto";
private static final String P_ATTACHMENT_ID = "id";
private static final String P_ARCHIVE_NAME = "name";
private static final String P_ARCHIVE_DESCRIPTION = "description";
@ -254,7 +262,7 @@ public class SMLParser {
} else if (T_LINK.equals(tagName)) {
receiver.receiveLink(getString(P_LINK_SCHEMA, attr), getString(P_LINK_LOCATION, attr), body);
} else if (T_ADDRESS.equals(tagName)) {
receiver.receiveAddress(getString(P_ADDRESS_NAME, attr), getString(P_ADDRESS_SCHEMA, attr), getString(P_ADDRESS_LOCATION, attr), body);
receiver.receiveAddress(getString(P_ADDRESS_NAME, attr), getString(P_ADDRESS_SCHEMA, attr), getString(P_ADDRESS_PROTOCOL, attr), getString(P_ADDRESS_LOCATION, attr), body);
} else if (T_H1.equals(tagName)) {
receiver.receiveH1(body);
} else if (T_H2.equals(tagName)) {
@ -272,7 +280,8 @@ public class SMLParser {
} else if (T_ATTACHMENT.equals(tagName)) {
receiver.receiveAttachment((int)getLong(P_ATTACHMENT_ID, attr), body);
} else {
System.out.println("need to learn how to parse the tag [" + tagName + "]");
if (_log.shouldLog(Log.WARN))
_log.warn("need to learn how to parse the tag [" + tagName + "]");
}
}
@ -381,7 +390,7 @@ public class SMLParser {
public void receiveArchive(String name, String description, String locationSchema, String location,
String postingKey, String anchorText);
public void receiveImage(String alternateText, int attachmentId);
public void receiveAddress(String name, String schema, String location, String anchorText);
public void receiveAddress(String name, String schema, String protocol, String location, String anchorText);
public void receiveAttachment(int id, String anchorText);
public void receiveBold(String text);
public void receiveItalic(String text);
@ -436,7 +445,8 @@ public class SMLParser {
test("A: B\n\n[b]This[/b] is [i]special[/i][cut]why?[/cut][u]because I say so[/u].\neven if you dont care");
}
private static void test(String rawSML) {
SMLParser parser = new SMLParser();
parser.parse(rawSML, new EventReceiverImpl());
I2PAppContext ctx = I2PAppContext.getGlobalContext();
SMLParser parser = new SMLParser(ctx);
parser.parse(rawSML, new EventReceiverImpl(ctx));
}
}

View File

@ -8,6 +8,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
@ -17,14 +18,14 @@ import net.i2p.syndie.data.*;
*/
public class ArchiveServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getPathInfo();
if ( (path == null) || (path.trim().length() <= 1) ) {
renderRootIndex(resp);
return;
} else if (path.endsWith(Archive.INDEX_FILE)) {
renderSummary(resp);
} else if (path.endsWith("export.zip")) {
} else if (path.indexOf("export.zip") != -1) {
ExportServlet.export(req, resp);
} else {
String blog = getBlog(path);
@ -89,12 +90,18 @@ public class ArchiveServlet extends HttpServlet {
out.close();
}
public static final String HEADER_EXPORT_CAPABLE = "X-Syndie-Export-Capable";
private void renderSummary(HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain;charset=utf-8");
//resp.setCharacterEncoding("UTF-8");
OutputStream out = resp.getOutputStream();
ArchiveIndex index = BlogManager.instance().getArchive().getIndex();
out.write(DataHelper.getUTF8(index.toString()));
byte[] indexUTF8 = DataHelper.getUTF8(index.toString());
resp.setHeader(HEADER_EXPORT_CAPABLE, "true");
Hash hash = I2PAppContext.getGlobalContext().sha().calculateHash(indexUTF8);
resp.setHeader("ETag", "\"" + hash.toBase64() + "\"");
OutputStream out = resp.getOutputStream();
out.write(indexUTF8);
out.close();
}

View File

@ -4,6 +4,8 @@ import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetName;
import net.i2p.client.naming.PetNameDB;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
@ -20,25 +22,6 @@ public class ArchiveViewerBean {
else
return HTMLRenderer.sanitizeString(info.getProperty("Name"));
}
public static String getEntryTitle(String keyHash, long entryId) {
String name = getBlogName(keyHash);
return getEntryTitleDate(name, entryId);
}
private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);
public static final String getEntryTitleDate(String blogName, long when) {
synchronized (_dateFormat) {
try {
String str = _dateFormat.format(new Date(when));
long dayBegin = _dateFormat.parse(str).getTime();
return blogName + ":<br /> <i>" + str + "-" + (when - dayBegin) + "</i>";
} catch (ParseException pe) {
pe.printStackTrace();
// wtf
return "unknown";
}
}
}
/** base64 encoded hash of the blog's public key, or null for no filtering by blog */
public static final String PARAM_BLOG = "blog";
@ -91,23 +74,22 @@ public class ArchiveViewerBean {
BlogManager.instance().saveUser(user);
}
out.write("<select name=\"");
out.write("<select class=\"b_selector\" name=\"");
out.write(PARAM_SELECTOR);
out.write("\">");
out.write("<option value=\"");
out.write(getDefaultSelector(user, parameters));
out.write("\">Default blog filter</option>\n");
out.write("\">");
out.write("<option value=\"");
out.write(SEL_ALL);
out.write("\">All posts from all blogs</option>\n");
Map groups = null;
List groups = null;
if (user != null)
groups = user.getBlogGroups();
groups = user.getPetNameDB().getGroups();
if (groups != null) {
for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
for (int i = 0; i < groups.size(); i++) {
String name = (String)groups.get(i);
out.write("<option value=\"group://" + Base64.encode(DataHelper.getUTF8(name)) + "\">" +
"Group: " + HTMLRenderer.sanitizeString(name) + "</option>\n");
}
@ -118,11 +100,21 @@ public class ArchiveViewerBean {
for (int i = 0; i < index.getNewestBlogCount(); i++) {
Hash cur = index.getNewestBlog(i);
String knownName = user.getPetNameDB().getNameByLocation(cur.toBase64());
PetName pn = null;
if (knownName != null) {
pn = user.getPetNameDB().get(knownName);
knownName = pn.getName();
}
if ( (pn != null) && (pn.isMember("Ignore")) )
continue;
String blog = Base64.encode(cur.getData());
out.write("<option value=\"blog://" + blog + "\">");
out.write("New blog: ");
BlogInfo info = archive.getBlogInfo(cur);
String name = info.getProperty(BlogInfo.NAME);
String name = knownName;
if ( (name == null) && (info != null) )
name = info.getProperty(BlogInfo.NAME);
if (name != null)
name = HTMLRenderer.sanitizeString(name);
else
@ -131,24 +123,43 @@ public class ArchiveViewerBean {
out.write("</option>\n");
}
List allTags = new ArrayList();
////List allTags = new ArrayList();
// perhaps sort this by name (even though it isnt unique...)
Set blogs = index.getUniqueBlogs();
for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
Hash cur = (Hash)iter.next();
String knownName = user.getPetNameDB().getNameByLocation(cur.toBase64());
PetName pn = null;
if (knownName != null) {
pn = user.getPetNameDB().get(knownName);
knownName = pn.getName();
}
if ( (pn != null) && (pn.isMember("Ignore")) )
continue;
String blog = Base64.encode(cur.getData());
out.write("<option value=\"blog://");
out.write(blog);
out.write("\">");
BlogInfo info = archive.getBlogInfo(cur);
String name = info.getProperty(BlogInfo.NAME);
String name = knownName;
if ( (name == null) && (info != null) )
name = info.getProperty(BlogInfo.NAME);
if (name != null)
name = HTMLRenderer.sanitizeString(name);
else
name = Base64.encode(cur.getData());
out.write(name);
out.write("- all posts</option>\n");
if (info != null) {
int howMany = index.getBlogEntryCount(info.getKey().calculateHash());
if (howMany == 1)
out.write(" [1 post]");
else
out.write(" [" + howMany + " posts]");
}
out.write("</option>\n");
/*
List tags = index.getBlogTags(cur);
for (int j = 0; j < tags.size(); j++) {
String tag = (String)tags.get(j);
@ -190,7 +201,9 @@ public class ArchiveViewerBean {
out.write(tag);
out.write("&quot;</option>\n");
}
*/
}
/*
for (int i = 0; i < allTags.size(); i++) {
String tag = (String)allTags.get(i);
out.write("<option value=\"tag://");
@ -199,6 +212,7 @@ public class ArchiveViewerBean {
out.write(tag);
out.write("&quot;</option>\n");
}
*/
out.write("</select>");
int numPerPage = getInt(parameters, PARAM_NUM_PER_PAGE, 5);
@ -224,10 +238,12 @@ public class ArchiveViewerBean {
String blogStr = getString(parameters, PARAM_BLOG);
Hash blog = null;
if (blogStr != null) blog = new Hash(Base64.decode(blogStr));
if ( (blog != null) && (blog.getData() == null) ) blog = null;
String tag = getString(parameters, PARAM_TAG);
if (tag != null) tag = DataHelper.getUTF8(Base64.decode(tag));
long entryId = -1;
if (blogStr != null) {
if (blog != null) {
String entryIdStr = getString(parameters, PARAM_ENTRY);
try {
entryId = Long.parseLong(entryIdStr);
@ -237,6 +253,14 @@ public class ArchiveViewerBean {
if (group != null) group = DataHelper.getUTF8(Base64.decode(group));
String sel = getString(parameters, PARAM_SELECTOR);
if (getString(parameters, "action") != null) {
tag = null;
blog = null;
sel = null;
group = null;
}
if ( (sel == null) && (blog == null) && (group == null) && (tag == null) )
sel = getDefaultSelector(user, parameters);
if (sel != null) {
@ -275,12 +299,21 @@ public class ArchiveViewerBean {
if (selector != null) {
if (selector.startsWith(SEL_BLOG)) {
String blogStr = selector.substring(SEL_BLOG.length());
System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "]");
blog = new Hash(Base64.decode(blogStr));
//System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "]");
byte h[] = Base64.decode(blogStr);
if (h != null)
blog = new Hash(h);
//else
// System.out.println("blog string does not decode properly: [" + blogStr + "]");
} else if (selector.startsWith(SEL_BLOGTAG)) {
int tagStart = selector.lastIndexOf('/');
String blogStr = selector.substring(SEL_BLOGTAG.length(), tagStart);
blog = new Hash(Base64.decode(blogStr));
if (blog.getData() == null) {
System.out.println("Blog string [" + blogStr + "] does not decode");
blog = null;
return;
}
tag = selector.substring(tagStart+1);
String origTag = tag;
byte rawDecode[] = null;
@ -288,7 +321,7 @@ public class ArchiveViewerBean {
rawDecode = Base64.decode(tag);
tag = DataHelper.getUTF8(rawDecode);
}
System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] tag: [" + tag + "]");
//System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] tag: [" + tag + "]");
if (false && tag != null) {
StringBuffer b = new StringBuffer(tag.length()*2);
for (int j = 0; j < tag.length(); j++) {
@ -301,7 +334,7 @@ public class ArchiveViewerBean {
for (int j = 0; j < origTag.length(); j++) {
b.append((int)origTag.charAt(j)).append(' ');
}
System.out.println("selected tag: " + b.toString());
//System.out.println("selected tag: " + b.toString());
}
} else if (selector.startsWith(SEL_TAG)) {
tag = selector.substring(SEL_TAG.length());
@ -310,7 +343,7 @@ public class ArchiveViewerBean {
rawDecode = Base64.decode(tag);
tag = DataHelper.getUTF8(rawDecode);
}
System.out.println("Selector [" + selector + "] tag: [" + tag + "]");
//System.out.println("Selector [" + selector + "] tag: [" + tag + "]");
if (false && tag != null) {
StringBuffer b = new StringBuffer(tag.length()*2);
for (int j = 0; j < tag.length(); j++) {
@ -319,7 +352,7 @@ public class ArchiveViewerBean {
b.append('.').append((int)rawDecode[j]);
b.append(' ');
}
System.out.println("selected tag: " + b.toString());
//System.out.println("selected tag: " + b.toString());
}
} else if (selector.startsWith(SEL_ENTRY)) {
int entryStart = selector.lastIndexOf('/');
@ -327,12 +360,16 @@ public class ArchiveViewerBean {
String entryStr = selector.substring(entryStart+1);
try {
entry = Long.parseLong(entryStr);
blog = new Hash(Base64.decode(blogStr));
System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] entry: [" + entry + "]");
Hash h = new Hash(Base64.decode(blogStr));
if (h.getData() != null)
blog = h;
//else
// System.out.println("Blog does not decode [" + blogStr + "]");
//System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] entry: [" + entry + "]");
} catch (NumberFormatException nfe) {}
} else if (selector.startsWith(SEL_GROUP)) {
group = DataHelper.getUTF8(Base64.decode(selector.substring(SEL_GROUP.length())));
System.out.println("Selector [" + selector + "] group: [" + group + "]");
//System.out.println("Selector [" + selector + "] group: [" + group + "]");
}
}
}
@ -345,10 +382,10 @@ public class ArchiveViewerBean {
archive.regenerateIndex();
ArchiveIndex index = archive.getIndex();
List entries = pickEntryURIs(user, index, blog, tag, entryId, group);
System.out.println("Searching for " + blog + "/" + tag + "/" + entryId + "/" + pageNum + "/" + numPerPage + "/" + group);
System.out.println("Entry URIs: " + entries);
//System.out.println("Searching for " + blog + "/" + tag + "/" + entryId + "/" + pageNum + "/" + numPerPage + "/" + group);
//System.out.println("Entry URIs: " + entries);
HTMLRenderer renderer = new HTMLRenderer();
HTMLRenderer renderer = new HTMLRenderer(I2PAppContext.getGlobalContext());
int start = pageNum * numPerPage;
int end = start + numPerPage;
int pages = 1;
@ -366,31 +403,29 @@ public class ArchiveViewerBean {
pages = entries.size() / numPerPage;
if (numPerPage * pages < entries.size())
pages++;
out.write("<i>");
if (pageNum > 0) {
String prevURL = null;
if ( (selector == null) || (selector.trim().length() <= 0) )
prevURL = HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum-1, expandEntries, showImages);
else
prevURL = HTMLRenderer.getPageURL(user, selector, numPerPage, pageNum-1);
System.out.println("prevURL: " + prevURL);
out.write(" <a href=\"" + prevURL + "\">&lt;&lt;</a>");
//System.out.println("prevURL: " + prevURL);
out.write(" <a class=\"b_selectorPrevMore\" href=\"" + prevURL + "\">&lt;&lt;</a>");
} else {
out.write(" &lt;&lt; ");
out.write(" <span class=\"b_selectorPrevNone\">&lt;&lt;</span> ");
}
out.write("Page " + (pageNum+1) + " of " + pages);
out.write("<span class=\"b_selectorPage\">Page " + (pageNum+1) + " of " + pages + "</span>");
if (pageNum + 1 < pages) {
String nextURL = null;
if ( (selector == null) || (selector.trim().length() <= 0) )
nextURL = HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum+1, expandEntries, showImages);
else
nextURL = HTMLRenderer.getPageURL(user, selector, numPerPage, pageNum+1);
System.out.println("nextURL: " + nextURL);
out.write(" <a href=\"" + nextURL + "\">&gt;&gt;</a>");
//System.out.println("nextURL: " + nextURL);
out.write(" <a class=\"b_selectorNextMore\" href=\"" + nextURL + "\">&gt;&gt;</a>");
} else {
out.write(" &gt;&gt;");
out.write(" <span class=\"b_selectorNextNone\">&gt;&gt;</span>");
}
out.write("</i>");
}
}
@ -418,7 +453,7 @@ public class ArchiveViewerBean {
out.write(afterPagination);
if (entries.size() <= 0) end = -1;
System.out.println("Entries.size: " + entries.size() + " start=" + start + " end=" + end);
//System.out.println("Entries.size: " + entries.size() + " start=" + start + " end=" + end);
for (int i = start; i < end; i++) {
BlogURI uri = (BlogURI)entries.get(i);
EntryContainer c = archive.getEntry(uri);
@ -434,7 +469,9 @@ public class ArchiveViewerBean {
}
}
private static List pickEntryURIs(User user, ArchiveIndex index, Hash blog, String tag, long entryId, String group) {
public static List pickEntryURIs(User user, ArchiveIndex index, Hash blog, String tag, long entryId, String group) {
if ( (blog != null) && ( (blog.getData() == null) || (blog.getData().length != Hash.HASH_LENGTH) ) )
blog = null;
List rv = new ArrayList(16);
if ( (blog != null) && (entryId >= 0) ) {
rv.add(new BlogURI(blog, entryId));
@ -444,7 +481,7 @@ public class ArchiveViewerBean {
if ( (group != null) && (user != null) ) {
List selectors = (List)user.getBlogGroups().get(group);
if (selectors != null) {
System.out.println("Selectors for group " + group + ": " + selectors);
//System.out.println("Selectors for group " + group + ": " + selectors);
for (int i = 0; i < selectors.size(); i++) {
String sel = (String)selectors.get(i);
Selector s = new Selector(sel);
@ -453,13 +490,57 @@ public class ArchiveViewerBean {
else
index.selectMatchesOrderByEntryId(rv, s.blog, s.tag);
}
return rv;
}
PetNameDB db = user.getPetNameDB();
for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
PetName pn = db.get(name);
if ("syndie".equals(pn.getNetwork()) && "syndieblog".equals(pn.getProtocol()) && pn.isMember(group)) {
byte pnLoc[] = Base64.decode(pn.getLocation());
if (pnLoc != null) {
Hash pnHash = new Hash(pnLoc);
index.selectMatchesOrderByEntryId(rv, pnHash, null);
}
}
}
sort(rv);
if (rv.size() > 0)
return rv;
}
index.selectMatchesOrderByEntryId(rv, blog, tag);
filterIgnored(user, rv);
return rv;
}
private static void filterIgnored(User user, List uris) {
for (int i = 0; i < uris.size(); i++) {
BlogURI uri = (BlogURI)uris.get(i);
Hash k = uri.getKeyHash();
if (k == null) continue;
String pname = user.getPetNameDB().getNameByLocation(k.toBase64());
if (pname != null) {
PetName pn = user.getPetNameDB().get(pname);
if ( (pn != null) && (pn.isMember("Ignore")) ) {
uris.remove(i);
i--;
}
}
}
}
private static void sort(List uris) {
TreeMap ordered = new TreeMap();
while (uris.size() > 0) {
BlogURI uri = (BlogURI)uris.remove(0);
int off = 0;
while (ordered.containsKey(new Long(0 - off - uri.getEntryId())))
off++;
ordered.put(new Long(0-off-uri.getEntryId()), uri);
}
for (Iterator iter = ordered.values().iterator(); iter.hasNext(); )
uris.add(iter.next());
}
public static final String getString(Map parameters, String param) {
if ( (parameters == null) || (parameters.get(param) == null) )
return null;
@ -539,6 +620,17 @@ public class ArchiveViewerBean {
return "application/octet-stream";
}
public static final boolean getAttachmentShouldShowInline(Map parameters) {
Attachment a = getAttachment(parameters);
if (a == null)
return true;
String mime = a.getMimeType();
if ( (mime != null) && ((mime.startsWith("image/") || mime.startsWith("text/plain"))) )
return true;
else
return false;
}
public static final int getAttachmentContentLength(Map parameters) {
Attachment a = getAttachment(parameters);
if (a != null)
@ -547,6 +639,14 @@ public class ArchiveViewerBean {
return -1;
}
public static final String getAttachmentFilename(Map parameters) {
Attachment a = getAttachment(parameters);
if (a != null)
return a.getName();
else
return "attachment.dat";
}
private static final Attachment getAttachment(Map parameters) {
String blogStr = getString(parameters, PARAM_BLOG);
Hash blog = null;
@ -569,10 +669,63 @@ public class ArchiveViewerBean {
}
private static void renderInvalidAttachment(Map parameters, OutputStream out) throws IOException {
out.write(DataHelper.getUTF8("<b>No such entry, or no such attachment</b>"));
out.write(DataHelper.getUTF8("<span class=\"b_msgErr\">No such entry, or no such attachment</span>"));
}
public static void renderMetadata(Map parameters, Writer out) throws IOException {
private static String getURL(String uri, Map parameters) {
StringBuffer rv = new StringBuffer(128);
rv.append(uri);
rv.append('?');
if (parameters != null) {
for (Iterator iter = parameters.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String vals[] = getStrings(parameters, key);
// we are already looking at the page with the given parameters, no need to further sanitize
if ( (key != null) && (vals != null) )
for (int i = 0; i < vals.length; i++)
rv.append(key).append('=').append(vals[i]).append('&');
}
}
return rv.toString();
}
private static void updateMetadata(User viewer, Map parameters, Writer out) throws IOException {
if ( (viewer == null) || (!viewer.getAuthenticated()) )
return;
String blogStr = getString(parameters, PARAM_BLOG);
if (blogStr != null) {
Hash blog = new Hash(Base64.decode(blogStr));
Archive archive = BlogManager.instance().getArchive();
BlogInfo info = archive.getBlogInfo(blog);
if (info != null) {
boolean isUser = viewer.getBlog().equals(info.getKey().calculateHash());
if (!isUser)
return;
Properties toSave = new Properties();
String existing[] = info.getProperties();
for (int i = 0; i < existing.length; i++) {
String newVal = getString(parameters, existing[i]);
if ( (newVal != null) && (newVal.length() > 0) )
toSave.setProperty(existing[i], newVal.trim());
else
toSave.setProperty(existing[i], info.getProperty(existing[i]));
}
boolean saved = BlogManager.instance().updateMetadata(viewer, blog, toSave);
if (saved)
out.write("<p><em class=\"b_msgOk\">Blog metadata saved</em></p>\n");
else
out.write("<p><em class=\"b_msgErr\">Blog metadata could not be saved</em></p>\n");
}
}
}
/**
* @param currentURI URI of the with current page without any parameters tacked on
*/
public static void renderMetadata(User viewer, String currentURI, Map parameters, Writer out) throws IOException {
if (parameters.get("action") != null) {
updateMetadata(viewer, parameters, out);
}
String blogStr = getString(parameters, PARAM_BLOG);
if (blogStr != null) {
Hash blog = new Hash(Base64.decode(blogStr));
@ -582,44 +735,59 @@ public class ArchiveViewerBean {
out.write("Blog " + blog.toBase64() + " does not exist");
return;
}
boolean isUser = ( (viewer != null) && (viewer.getAuthenticated()) && (viewer.getBlog().equals(info.getKey().calculateHash())) );
String props[] = info.getProperties();
out.write("<table border=\"0\">");
if (isUser) {
out.write("<form action=\"" + getURL(currentURI, parameters) + "\" method=\"GET\">\n");
out.write("<input type=\"hidden\" name=\"submit_blog\" value=\"" + blog.toBase64() + "\" />\n");
}
out.write("<table class=\"b_meta\" border=\"0\">");
for (int i = 0; i < props.length; i++) {
if (props[i].equals(BlogInfo.OWNER_KEY)) {
out.write("<tr><td><b>Blog:</b></td><td>");
out.write("<tr class=\"b_metaBlog\"><td class=\"b_metaBlog\"><span class=\"b_metaBlog\">Blog:</span></td>");
String blogURL = HTMLRenderer.getPageURL(blog, null, -1, -1, -1, false, false);
out.write("<a href=\"" + blogURL + "\">" + Base64.encode(blog.getData()) + "</td></tr>\n");
out.write("<td class=\"b_metaBlog\"><a class=\"b_metaBlog\" href=\"" + blogURL + "\">" + Base64.encode(blog.getData()) + "</td></tr>\n");
} else if (props[i].equals(BlogInfo.SIGNATURE)) {
continue;
} else if (props[i].equals(BlogInfo.POSTERS)) {
SigningPublicKey keys[] = info.getPosters();
if ( (keys != null) && (keys.length > 0) ) {
out.write("<tr><td><b>Allowed authors:</b></td><td>");
out.write("<tr class=\"b_metaAuthor\"><td class=\"b_metaAuthor\"><span class=\"b_metaAuthor\">Allowed authors:</span></td>");
out.write("<td class=\"b_metaAuthor\">");
for (int j = 0; j < keys.length; j++) {
out.write(keys[j].calculateHash().toBase64());
out.write("<span class=\"b_metaAuthor\">" + keys[j].calculateHash().toBase64() + "</span>");
if (j + 1 < keys.length)
out.write("<br />\n");
}
out.write("</td></tr>\n");
}
} else {
out.write("<tr><td>" + HTMLRenderer.sanitizeString(props[i]) + ":</td><td>" +
HTMLRenderer.sanitizeString(info.getProperty(props[i])) + "</td></tr>\n");
String field = HTMLRenderer.sanitizeString(props[i]);
String val = HTMLRenderer.sanitizeString(info.getProperty(props[i]));
out.write("<tr class=\"b_metaField\"><td class=\"b_metaField\"><span class=\"b_metaField\">" + field
+ ":</span></td><td class=\"b_metaValue\"><span class=\"b_metaValue\">" + val + "</span></td></tr>\n");
if (isUser && (!field.equals("Edition")))
out.write("<tr class=\"b_metaField\"><td>&nbsp;</td><td class=\"b_metaValue\"><input type=\"text\" name=\""
+ HTMLRenderer.sanitizeTagParam(props[i]) + "\" value=\""
+ HTMLRenderer.sanitizeTagParam(info.getProperty(props[i])) + "\" size=\"40\" ></td></tr>");
}
}
List tags = BlogManager.instance().getArchive().getIndex().getBlogTags(blog);
if ( (tags != null) && (tags.size() > 0) ) {
out.write("<tr><td>Known tags:</td><td>");
out.write("<tr class=\"b_metaTags\"><td class=\"b_metaTags\"><span class=\"b_metaTags\">Known tags:</span></td><td class=\"b_metaTags\">");
for (int i = 0; i < tags.size(); i++) {
String tag = (String)tags.get(i);
out.write("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, false, false) + "\">" +
out.write("<a class=\"b_metaTag\" href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, false, false) + "\">" +
HTMLRenderer.sanitizeString(tag) + "</a> ");
}
out.write("</td></tr>");
}
if (isUser)
out.write("<tr class=\"b_metaField\"><td colspan=\"2\" class=\"b_metaField\"><input type=\"submit\" name=\"action\" value=\"Save changes\" class=\"b_metaSave\" /></td></tr>\n");
out.write("</table>");
} else {
out.write("Blog not specified");
out.write("<span class=\"b_metaMsgErr\">Blog not specified</span>");
}
}
}

View File

@ -24,17 +24,98 @@ import net.i2p.syndie.data.*;
*/
public class ExportServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
export(req, resp);
}
public static void export(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String meta[] = req.getParameterValues("meta");
String entries[] = req.getParameterValues("entry");
try {
doExport(req, resp);
} catch (ServletException se) {
se.printStackTrace();
throw se;
} catch (IOException ioe) {
ioe.printStackTrace();
throw ioe;
}
}
private static void doExport(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String meta[] = null;
String entries[] = null;
String type = req.getHeader("Content-Type");
if ( (type == null) || (type.indexOf("boundary") == -1) ) {
// it has to be POSTed with the request, name=value pairs. the export servlet doesn't allow any
// free form fields, so no worry about newlines, so lets parse 'er up
List metaList = new ArrayList();
List entryList = new ArrayList();
StringBuffer key = new StringBuffer();
StringBuffer val = null;
String lenStr = req.getHeader("Content-length");
int len = -1;
if (lenStr != null)
try { len = Integer.valueOf(lenStr).intValue(); } catch (NumberFormatException nfe) {}
int read = 0;
int c = 0;
InputStream in = req.getInputStream();
while ( (len == -1) || (read < len) ){
c = in.read();
if ( (c == '=') && (val == null) ) {
val = new StringBuffer(128);
} else if ( (c == -1) || (c == '&') ) {
String k = (key == null ? "" : key.toString());
String v = (val == null ? "" : val.toString());
if ("meta".equals(k))
metaList.add(v.trim());
else if ("entry".equals(k))
entryList.add(v.trim());
key.setLength(0);
val = null;
// no newlines in the export servlet
if (c == -1)
break;
} else {
if (val == null)
key.append((char)c);
else
val.append((char)c);
}
read++;
}
if (metaList != null) {
meta = new String[metaList.size()];
for (int i = 0; i < metaList.size(); i++)
meta[i] = (String)metaList.get(i);
}
if (entryList != null) {
entries = new String[entryList.size()];
for (int i = 0; i < entryList.size(); i++)
entries[i] = (String)entryList.get(i);
}
} else {
meta = req.getParameterValues("meta");
entries = req.getParameterValues("entry");
}
resp.setContentType("application/x-syndie-zip");
resp.setStatus(200);
OutputStream out = resp.getOutputStream();
ZipOutputStream zo = new ZipOutputStream(out);
if (false) {
StringBuffer bbuf = new StringBuffer(1024);
bbuf.append("meta: ");
if (meta != null)
for (int i = 0; i < meta.length; i++)
bbuf.append(meta[i]).append(", ");
bbuf.append("entries: ");
if (entries != null)
for (int i = 0; i < entries.length; i++)
bbuf.append(entries[i]).append(", ");
System.out.println(bbuf.toString());
}
ZipOutputStream zo = null;
if ( (meta != null) && (entries != null) && (meta.length + entries.length > 0) )
zo = new ZipOutputStream(out);
List metaFiles = getMetaFiles(meta);
@ -62,8 +143,10 @@ public class ExportServlet extends HttpServlet {
zo.closeEntry();
}
zo.finish();
zo.close();
if (zo != null) {
zo.finish();
zo.close();
}
}
private static List getMetaFiles(String blogHashes[]) {

View File

@ -2,34 +2,46 @@ package net.i2p.syndie.web;
import java.io.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetName;
import net.i2p.syndie.*;
import net.i2p.syndie.data.BlogURI;
import net.i2p.syndie.sml.HTMLPreviewRenderer;
import net.i2p.util.Log;
/**
*
*/
public class PostBean {
private I2PAppContext _context;
private Log _log;
private User _user;
private String _subject;
private String _tags;
private String _headers;
private String _text;
private String _archive;
private List _filenames;
private List _fileStreams;
private List _localFiles;
private List _fileTypes;
private boolean _previewed;
public PostBean() { reinitialize(); }
public PostBean() {
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(PostBean.class);
reinitialize();
}
public void reinitialize() {
System.out.println("Reinitializing " + (_text != null ? "(with " + _text.length() + " bytes of sml!)" : ""));
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reinitializing " + (_text != null ? "(with " + _text.length() + " bytes of sml!)" : ""));
_user = null;
_subject = null;
_tags = null;
_text = null;
_headers = null;
_archive = null;
_filenames = new ArrayList();
_fileStreams = new ArrayList();
_fileTypes = new ArrayList();
@ -51,6 +63,7 @@ public class PostBean {
public void setTags(String tags) { _tags = tags; }
public void setText(String text) { _text = text; }
public void setHeaders(String headers) { _headers = headers; }
public void setArchive(String archive) { _archive = archive; }
public String getContentType(int id) {
if ( (id >= 0) && (id < _fileTypes.size()) )
@ -81,20 +94,45 @@ public class PostBean {
File f = (File)_localFiles.get(i);
localStreams.add(new FileInputStream(f));
}
return BlogManager.instance().createBlogEntry(_user, _subject, _tags, _headers, _text,
_filenames, localStreams, _fileTypes);
BlogURI uri = BlogManager.instance().createBlogEntry(_user, _subject, _tags, _headers, _text,
_filenames, localStreams, _fileTypes);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Posted the entry " + uri.toString() + " (archive = " + _archive + ")");
if ( (uri != null) && BlogManager.instance().authorizeRemote(_user) ) {
PetName pn = _user.getPetNameDB().get(_archive);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Archive to petname? " + pn + " (protocol: " + (pn != null ? pn.getProtocol() : "") + ")");
if ( (pn != null) && ("syndiearchive".equals(pn.getProtocol())) ) {
RemoteArchiveBean r = new RemoteArchiveBean();
Map params = new HashMap();
params.put("localentry", new String[] { uri.toString() });
String proxyHost = BlogManager.instance().getDefaultProxyHost();
String port = BlogManager.instance().getDefaultProxyPort();
int proxyPort = 4444;
try { proxyPort = Integer.parseInt(port); } catch (NumberFormatException nfe) {}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Posting the entry " + uri.toString() + " to " + pn.getLocation());
r.postSelectedEntries(_user, params, proxyHost, proxyPort, pn.getLocation());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Post status: " + r.getStatus());
}
}
return uri;
}
public void renderPreview(Writer out) throws IOException {
System.out.println("Subject: " + _subject);
System.out.println("Text: " + _text);
System.out.println("Headers: " + _headers);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Subject: " + _subject);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Text: " + _text);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Headers: " + _headers);
// cache all the _fileStreams into temporary files, storing those files in _localFiles
// then render the page accordingly with an HTMLRenderer, altered to use a different
// 'view attachment'
cacheAttachments();
String smlContent = renderSMLContent();
HTMLPreviewRenderer r = new HTMLPreviewRenderer(_filenames, _fileTypes, _localFiles);
HTMLPreviewRenderer r = new HTMLPreviewRenderer(_context, _filenames, _fileTypes, _localFiles);
r.render(_user, BlogManager.instance().getArchive(), null, smlContent, out, false, true);
_previewed = true;
}
@ -128,7 +166,8 @@ public class PostBean {
o.close();
in.close();
_localFiles.add(f);
System.out.println("Caching attachment " + i + " temporarily in "
if (_log.shouldLog(Log.DEBUG))
_log.debug("Caching attachment " + i + " temporarily in "
+ f.getAbsolutePath() + " w/ " + f.length() + "bytes");
}
_fileStreams.clear();

View File

@ -0,0 +1,95 @@
package net.i2p.syndie.web;
import java.io.*;
import java.util.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
/**
*
*/
public class RSSServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/rss+xml");
User user = (User)req.getSession().getAttribute("user");
if (user == null) {
String login = req.getParameter("login");
String pass = req.getParameter("password");
user = new User();
BlogManager.instance().login(user, login, pass); // ignore failures - user will just be unauthorized
if (!user.getAuthenticated())
user.invalidate();
}
String selector = req.getParameter("selector");
if ( (selector == null) || (selector.length() <= 0) ) {
selector = getDefaultSelector(user);
}
ArchiveViewerBean.Selector sel = new ArchiveViewerBean.Selector(selector);
Archive archive = BlogManager.instance().getArchive();
ArchiveIndex index = archive.getIndex();
List entries = ArchiveViewerBean.pickEntryURIs(user, index, sel.blog, sel.tag, sel.entry, sel.group);
StringBuffer cur = new StringBuffer();
cur.append(req.getScheme());
cur.append("://");
cur.append(req.getServerName());
if (req.getServerPort() != 80)
cur.append(':').append(req.getServerPort());
cur.append(req.getContextPath()).append('/');
String urlPrefix = cur.toString();
Writer out = resp.getWriter();
out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
out.write("<rss version=\"2.0\">\n");
out.write(" <channel>\n");
out.write(" <title>Syndie feed</title>\n");
out.write(" <link>" + urlPrefix + HTMLRenderer.sanitizeXML(HTMLRenderer.getPageURL(sel.blog, sel.tag, sel.entry, sel.group, 5, 0, false, false)) +"</link>\n");
out.write(" <description>Summary of the latest Syndie posts</description>\n");
out.write(" <generator>Syndie</generator>\n");
int count = 10;
String wanted = req.getParameter("wanted");
if (wanted != null) {
try {
count = Integer.parseInt(wanted);
} catch (NumberFormatException nfe) {
count = 10;
}
}
if (count < 0) count = 10;
if (count > 100) count = 100;
RSSRenderer r = new RSSRenderer(I2PAppContext.getGlobalContext());
for (int i = 0; i < count && i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
EntryContainer entry = archive.getEntry(uri);
r.render(user, archive, entry, urlPrefix, out);
}
out.write(" </channel>\n");
out.write("</rss>\n");
out.close();
}
private static String getDefaultSelector(User user) {
if ( (user == null) || (user.getDefaultSelector() == null) )
return BlogManager.instance().getArchive().getDefaultSelector();
else
return user.getDefaultSelector();
}
}

View File

@ -5,6 +5,7 @@ import java.text.*;
import java.util.*;
import java.util.zip.*;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.PetNameDB;
import net.i2p.data.*;
import net.i2p.util.EepGet;
import net.i2p.util.EepGetScheduler;
@ -12,11 +13,14 @@ import net.i2p.util.EepPost;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
import net.i2p.syndie.*;
import net.i2p.util.Log;
/**
*
*/
public class RemoteArchiveBean {
private I2PAppContext _context;
private Log _log;
private String _remoteSchema;
private String _remoteLocation;
private String _proxyHost;
@ -24,8 +28,11 @@ public class RemoteArchiveBean {
private ArchiveIndex _remoteIndex;
private List _statusMessages;
private boolean _fetchIndexInProgress;
private boolean _exportCapable;
public RemoteArchiveBean() {
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(RemoteArchiveBean.class);
reinitialize();
}
public void reinitialize() {
@ -35,6 +42,7 @@ public class RemoteArchiveBean {
_fetchIndexInProgress = false;
_proxyHost = null;
_proxyPort = -1;
_exportCapable = false;
_statusMessages = new ArrayList();
}
@ -51,6 +59,12 @@ public class RemoteArchiveBean {
return buf.toString();
}
private boolean ignoreBlog(User user, Hash blog) {
PetNameDB db = user.getPetNameDB();
String name = db.getNameByLocation(blog.toBase64());
return ( (name != null) && (db.get(name).isMember("Ignore")) );
}
public void fetchMetadata(User user, Map parameters) {
String meta = ArchiveViewerBean.getString(parameters, "blog");
if (meta == null) return;
@ -61,11 +75,17 @@ public class RemoteArchiveBean {
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (!localBlogs.contains(blog)) {
blogs.add(blog);
if (!ignoreBlog(user, blog))
blogs.add(blog);
}
}
} else {
blogs.add(new Hash(Base64.decode(meta.trim())));
byte h[] = Base64.decode(meta.trim());
if (h != null) {
Hash blog = new Hash(h);
if (!ignoreBlog(user, blog))
blogs.add(blog);
}
}
List urls = new ArrayList(blogs.size());
List tmpFiles = new ArrayList(blogs.size());
@ -96,7 +116,10 @@ public class RemoteArchiveBean {
List urls = new ArrayList(entries.length);
List tmpFiles = new ArrayList(entries.length);
for (int i = 0; i < entries.length; i++) {
urls.add(buildEntryURL(new BlogURI(entries[i])));
BlogURI uri = new BlogURI(entries[i]);
if (ignoreBlog(user, uri.getKeyHash()))
continue;
urls.add(buildEntryURL(uri));
try {
tmpFiles.add(File.createTempFile("fetchBlog", ".txt", BlogManager.instance().getTempDir()));
} catch (IOException ioe) {
@ -118,6 +141,9 @@ public class RemoteArchiveBean {
List matches = new ArrayList();
for (Iterator iter = _remoteIndex.getUniqueBlogs().iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (ignoreBlog(user, blog))
continue;
_remoteIndex.selectMatchesOrderByEntryId(matches, blog, null);
for (int i = 0; i < matches.size(); i++) {
BlogURI uri = (BlogURI)matches.get(i);
@ -131,31 +157,58 @@ public class RemoteArchiveBean {
entries[i] = ((BlogURI)uris.get(i)).toString();
}
if ( (entries == null) || (entries.length <= 0) ) return;
StringBuffer url = new StringBuffer(512);
url.append(buildExportURL());
Set meta = new HashSet();
for (int i = 0; i < entries.length; i++) {
BlogURI uri = new BlogURI(entries[i]);
if (uri.getEntryId() >= 0) {
url.append("entry=").append(uri.toString()).append('&');
meta.add(uri.getKeyHash());
_statusMessages.add("Scheduling blog post fetching for " + HTMLRenderer.sanitizeString(entries[i]));
if (_exportCapable) {
StringBuffer url = new StringBuffer(512);
url.append(buildExportURL());
StringBuffer postData = new StringBuffer(512);
Set meta = new HashSet();
for (int i = 0; i < entries.length; i++) {
BlogURI uri = new BlogURI(entries[i]);
if (uri.getEntryId() >= 0) {
postData.append("entry=").append(uri.toString()).append('&');
meta.add(uri.getKeyHash());
_statusMessages.add("Scheduling bulk blog post fetch of " + HTMLRenderer.sanitizeString(entries[i]));
}
}
for (Iterator iter = meta.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
postData.append("meta=").append(blog.toBase64()).append('&');
_statusMessages.add("Scheduling bulk blog metadata fetch of " + blog.toBase64());
}
try {
File tmp = File.createTempFile("fetchBulk", ".zip", BlogManager.instance().getTempDir());
boolean shouldProxy = (_proxyHost != null) && (_proxyPort > 0);
EepGet get = new EepGet(_context, shouldProxy, _proxyHost, _proxyPort, 0, tmp.getAbsolutePath(), url.toString(), postData.toString());
get.addStatusListener(new BulkFetchListener(tmp));
get.fetch();
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(url.toString()) + ": " + ioe.getMessage());
}
} else {
List urls = new ArrayList(entries.length+8);
for (int i = 0; i < entries.length; i++) {
BlogURI uri = new BlogURI(entries[i]);
if (uri.getEntryId() >= 0) {
String metaURL = buildMetaURL(uri.getKeyHash());
if (!urls.contains(metaURL)) {
urls.add(metaURL);
_statusMessages.add("Scheduling blog metadata fetch of " + HTMLRenderer.sanitizeString(entries[i]));
}
urls.add(buildEntryURL(uri));
_statusMessages.add("Scheduling blog post fetch of " + HTMLRenderer.sanitizeString(entries[i]));
}
}
List tmpFiles = new ArrayList(1);
try {
for (int i = 0; i < urls.size(); i++) {
File t = File.createTempFile("fetchBulk", ".dat", BlogManager.instance().getTempDir());
tmpFiles.add(t);
}
fetch(urls, tmpFiles, user, new BlogStatusListener());
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch posts: " + HTMLRenderer.sanitizeString(urls.toString()));
}
}
for (Iterator iter = meta.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
url.append("meta=").append(blog.toBase64()).append('&');
_statusMessages.add("Scheduling blog metadata fetching for " + blog.toBase64());
}
List urls = new ArrayList(1);
urls.add(url.toString());
List tmpFiles = new ArrayList(1);
try {
File tmp = File.createTempFile("fetchBulk", ".zip", BlogManager.instance().getTempDir());
tmpFiles.add(tmp);
fetch(urls, tmpFiles, user, new BulkFetchListener(tmp));
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(url.toString()) + ": " + ioe.getMessage());
}
}
@ -177,6 +230,8 @@ public class RemoteArchiveBean {
List entries = new ArrayList();
for (Iterator iter = _remoteIndex.getUniqueBlogs().iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (ignoreBlog(user, blog))
continue;
_remoteIndex.selectMatchesOrderByEntryId(entries, blog, null);
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
@ -213,9 +268,10 @@ public class RemoteArchiveBean {
_remoteSchema = schema;
_proxyHost = null;
_proxyPort = -1;
_exportCapable = false;
if ( (schema == null) || (schema.trim().length() <= 0) ||
(location == null) || (location.trim().length() <= 0) ) {
(location == null) || (location.trim().length() <= 0) ) {
_statusMessages.add("Location must be specified");
_fetchIndexInProgress = false;
return;
@ -240,13 +296,30 @@ public class RemoteArchiveBean {
}
_statusMessages.add("Fetching index from " + HTMLRenderer.sanitizeString(_remoteLocation) +
(_proxyHost != null ? " via " + HTMLRenderer.sanitizeString(_proxyHost) + ":" + _proxyPort : ""));
(_proxyHost != null ? " via " + HTMLRenderer.sanitizeString(_proxyHost) + ":" + _proxyPort : ""));
File archiveFile = new File(BlogManager.instance().getTempDir(), user.getBlog().toBase64() + "_remoteArchive.txt");
archiveFile.delete();
Properties etags = new Properties();
try {
etags.load(new FileInputStream(new File(BlogManager.instance().getRootDir(), "etags")));
} catch (Exception exp) {
//ignore
}
EepGet eep = new EepGet(I2PAppContext.getGlobalContext(), ((_proxyHost != null) && (_proxyPort > 0)),
_proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location);
_proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location, etags.getProperty(location));
eep.addStatusListener(new IndexFetcherStatusListener(archiveFile));
eep.fetch();
if (eep.getETag() != null) {
etags.setProperty(location, eep.getETag());
}
try {
etags.store(new FileOutputStream(new File(BlogManager.instance().getRootDir(), "etags")), "etags");
} catch (Exception exp) {
//ignore
}
}
private class IndexFetcherStatusListener implements EepGet.StatusListener {
@ -259,22 +332,33 @@ public class RemoteArchiveBean {
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
_fetchIndexInProgress = false;
ArchiveIndex i = new ArchiveIndex(false);
try {
i.load(_archiveFile);
_statusMessages.add("Archive fetched and loaded");
_remoteIndex = i;
} catch (IOException ioe) {
_statusMessages.add("Archive is corrupt: " + ioe.getMessage());
ArchiveIndex i = new ArchiveIndex(I2PAppContext.getGlobalContext(), false);
if (!notModified) {
try {
i.load(_archiveFile);
_statusMessages.add("Archive fetched and loaded");
_remoteIndex = i;
} catch (IOException ioe) {
_statusMessages.add("Archive is corrupt: " + ioe.getMessage());
}
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
_fetchIndexInProgress = false;
}
public void headerReceived(String url, int currentAttempt, String key, String val) {
if (ArchiveServlet.HEADER_EXPORT_CAPABLE.equals(key) && ("true".equals(val))) {
_statusMessages.add("Remote archive is bulk export capable");
_exportCapable = true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Header received: [" + key + "] = [" + val + "]");
}
}
}
private class MetadataStatusListener implements EepGet.StatusListener {
@ -284,31 +368,37 @@ public class RemoteArchiveBean {
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
File info = new File(outputFile);
FileInputStream in = null;
try {
BlogInfo i = new BlogInfo();
in = new FileInputStream(info);
i.load(in);
boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i);
if (ok) {
_statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported");
BlogManager.instance().getArchive().reloadInfo();
} else {
_statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged");
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
info.delete();
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
handleMetadata(url, outputFile);
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);;
}
public void headerReceived(String url, int currentAttempt, String key, String val) {}
}
private void handleMetadata(String url, String outputFile) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
File info = new File(outputFile);
FileInputStream in = null;
try {
BlogInfo i = new BlogInfo();
in = new FileInputStream(info);
i.load(in);
boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i);
if (ok) {
_statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported");
BlogManager.instance().getArchive().reloadInfo();
} else {
_statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged");
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error handling metadata", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
info.delete();
}
}
private class BlogStatusListener implements EepGet.StatusListener {
@ -318,7 +408,11 @@ public class RemoteArchiveBean {
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (url.endsWith(".snm")) {
handleMetadata(url, outputFile);
return;
}
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
File file = new File(outputFile);
FileInputStream in = null;
@ -346,7 +440,8 @@ public class RemoteArchiveBean {
BlogManager.instance().getArchive().regenerateIndex();
}
} catch (IOException ioe) {
ioe.printStackTrace();
if (_log.shouldLog(Log.WARN))
_log.warn("Error importing", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
file.delete();
@ -355,6 +450,7 @@ public class RemoteArchiveBean {
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
}
public void headerReceived(String url, int currentAttempt, String key, String val) {}
}
/**
@ -371,7 +467,7 @@ public class RemoteArchiveBean {
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url.substring(0, url.indexOf('?'))) + " successful, importing the data");
File file = new File(outputFile);
ZipInputStream zi = null;
@ -424,7 +520,8 @@ public class RemoteArchiveBean {
BlogManager.instance().getArchive().regenerateIndex();
} catch (IOException ioe) {
ioe.printStackTrace();
if (_log.shouldLog(Log.WARN))
_log.debug("Error importing", ioe);
_statusMessages.add("Error importing from " + HTMLRenderer.sanitizeString(url) + ": " + ioe.getMessage());
} finally {
if (zi != null) try { zi.close(); } catch (IOException ioe) {}
@ -435,15 +532,26 @@ public class RemoteArchiveBean {
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
_tmp.delete();
}
public void headerReceived(String url, int currentAttempt, String key, String val) {}
}
public void postSelectedEntries(User user, Map parameters) {
postSelectedEntries(user, parameters, _proxyHost, _proxyPort, _remoteLocation);
}
public void postSelectedEntries(User user, Map parameters, String proxyHost, int proxyPort, String location) {
String entries[] = ArchiveViewerBean.getStrings(parameters, "localentry");
if ( (entries == null) || (entries.length <= 0) ) return;
List uris = new ArrayList(entries.length);
for (int i = 0; i < entries.length; i++)
uris.add(new BlogURI(entries[i]));
if ( (proxyPort > 0) && (proxyHost != null) && (proxyHost.trim().length() > 0) ) {
_proxyPort = proxyPort;
_proxyHost = proxyHost;
} else {
_proxyPort = -1;
_proxyHost = null;
}
_remoteLocation = location;
post(uris, user);
}
@ -480,12 +588,14 @@ public class RemoteArchiveBean {
public void renderDeltaForm(User user, ArchiveIndex localIndex, Writer out) throws IOException {
Archive archive = BlogManager.instance().getArchive();
StringBuffer buf = new StringBuffer(512);
buf.append("<b>New blogs:</b> <select name=\"blog\"><option value=\"ALL\">All</option>\n");
buf.append("<em class=\"b_remMeta\">New blogs:</em> <select class=\"b_remMeta\"name=\"blog\"><option value=\"ALL\">All</option>\n");
Set localBlogs = archive.getIndex().getUniqueBlogs();
Set remoteBlogs = _remoteIndex.getUniqueBlogs();
int newBlogs = 0;
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (ignoreBlog(user, blog))
continue;
if (!localBlogs.contains(blog)) {
buf.append("<option value=\"" + blog.toBase64() + "\">" + blog.toBase64() + "</option>\n");
newBlogs++;
@ -493,48 +603,57 @@ public class RemoteArchiveBean {
}
if (newBlogs > 0) {
out.write(buf.toString());
out.write("</select> <input type=\"submit\" name=\"action\" value=\"Fetch metadata\" /><br />\n");
out.write("</select> <input class=\"b_remMetaFetch\" type=\"submit\" name=\"action\" value=\"Fetch metadata\" /><br />\n");
}
int newEntries = 0;
int localNew = 0;
out.write("<table border=\"1\" width=\"100%\">\n");
out.write("<table class=\"b_remDelta\" border=\"1\" width=\"100%\">\n");
List entries = new ArrayList();
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (ignoreBlog(user, blog))
continue;
buf.setLength(0);
int shownEntries = 0;
buf.append("<tr><td colspan=\"5\" align=\"left\" valign=\"top\">\n");
buf.append("<tr class=\"b_remBlog\"><td class=\"b_remBlog\" colspan=\"5\" align=\"left\" valign=\"top\">\n");
BlogInfo info = archive.getBlogInfo(blog);
if (info != null) {
buf.append("<a href=\"" + HTMLRenderer.getPageURL(blog, null, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\"><b>" + HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME)) + "</b></a>: " +
HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.DESCRIPTION)) + "\n");
buf.append("<a class=\"b_remBlog\" href=\"");
buf.append(HTMLRenderer.getPageURL(blog, null, -1, -1, -1, user.getShowExpanded(), user.getShowImages()));
buf.append("\">").append(HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME))).append("</a>: ");
buf.append("<span class=\"b_remBlogDesc\">").append(HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.DESCRIPTION)));
buf.append("</span>\n");
} else {
buf.append("<b>" + blog.toBase64() + "</b>\n");
buf.append("<span class=\"b_remBlog\">" + blog.toBase64() + "</span>\n");
}
buf.append("</td></tr>\n");
buf.append("<tr><td>&nbsp;</td><td nowrap=\"true\"><b>Posted on</b></td><td nowrap=\"true\"><b>#</b></td><td nowrap=\"true\"><b>Size</b></td><td width=\"90%\" nowrap=\"true\"><b>Tags</b></td></tr>\n");
buf.append("<tr class=\"b_remHeader\"><td class=\"b_remHeader\">&nbsp;</td><td class=\"b_remHeader\" nowrap=\"nowrap\">");
buf.append("<em class=\"b_remHeader\">Posted on</em></td>");
buf.append("<td class=\"b_remHeader\" nowrap=\"nowrap\"><em class=\"b_remHeader\">#</em></td>");
buf.append("<td class=\"b_remHeader\" nowrap=\"nowrap\"><em class=\"b_remHeader\">Size</em></td>");
buf.append("<td class=\"b_remHeader\" width=\"90%\" nowrap=\"true\"><em class=\"b_remHeader\">Tags</em></td></tr>\n");
entries.clear();
_remoteIndex.selectMatchesOrderByEntryId(entries, blog, null);
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
buf.append("<tr>\n");
buf.append("<tr class=\"b_remDetail\">\n");
if (!archive.getIndex().getEntryIsKnown(uri)) {
buf.append("<td><input type=\"checkbox\" name=\"entry\" value=\"" + uri.toString() + "\" /></td>\n");
buf.append("<td class=\"b_remDetail\"><input class=\"b_remSelect\" type=\"checkbox\" name=\"entry\" value=\"" + uri.toString() + "\" /></td>\n");
newEntries++;
shownEntries++;
} else {
String page = HTMLRenderer.getPageURL(blog, null, uri.getEntryId(), -1, -1,
user.getShowExpanded(), user.getShowImages());
buf.append("<td><a href=\"" + page + "\">(local)</a></td>\n");
buf.append("<td class=\"b_remDetail\"><a class=\"b_remLocal\" href=\"" + page + "\">(local)</a></td>\n");
}
buf.append("<td>" + getDate(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + getId(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + _remoteIndex.getBlogEntrySizeKB(uri) + "KB</td>\n");
buf.append("<td>");
buf.append("<td class=\"b_remDetail\"><span class=\"b_remDate\">" + getDate(uri.getEntryId()) + "</span></td>\n");
buf.append("<td class=\"b_remDetail\"><span class=\"b_remNum\">" + getId(uri.getEntryId()) + "</span></td>\n");
buf.append("<td class=\"b_remDetail\"><span class=\"b_remSize\">" + _remoteIndex.getBlogEntrySizeKB(uri) + "KB</span></td>\n");
buf.append("<td class=\"b_remDetail\">");
for (Iterator titer = new TreeSet(_remoteIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
String tag = (String)titer.next();
buf.append("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
buf.append("<a class=\"b_remTag\" href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
}
buf.append("</td>\n");
buf.append("</tr>\n");
@ -548,22 +667,22 @@ public class RemoteArchiveBean {
// now for posts in known blogs that we have and they don't
entries.clear();
localIndex.selectMatchesOrderByEntryId(entries, blog, null);
buf.append("<tr><td colspan=\"5\">Entries we have, but the remote Syndie doesn't:</td></tr>\n");
buf.append("<tr class=\"b_remLocalHeader\"><td class=\"b_remLocalHeader\" colspan=\"5\"><span class=\"b_remLocalHeader\">Entries we have, but the remote Syndie doesn't:</span></td></tr>\n");
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
if (!_remoteIndex.getEntryIsKnown(uri)) {
buf.append("<tr>\n");
buf.append("<td><input type=\"checkbox\" name=\"localentry\" value=\"" + uri.toString() + "\" /></td>\n");
buf.append("<tr class=\"b_remLocalDetail\">\n");
buf.append("<td class=\"b_remLocalDetail\"><input class=\"b_remLocalSend\" type=\"checkbox\" name=\"localentry\" value=\"" + uri.toString() + "\" /></td>\n");
shownEntries++;
newEntries++;
localNew++;
buf.append("<td>" + getDate(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + getId(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + localIndex.getBlogEntrySizeKB(uri) + "KB</td>\n");
buf.append("<td>");
buf.append("<td class=\"b_remLocalDate\"><span class=\"b_remLocalDate\">" + getDate(uri.getEntryId()) + "</span></td>\n");
buf.append("<td class=\"b_remLocalNum\"><span class=\"b_remLocalNum\">" + getId(uri.getEntryId()) + "</span></td>\n");
buf.append("<td class=\"b_remLocalSize\"><span class=\"b_remLocalSize\">" + localIndex.getBlogEntrySizeKB(uri) + "KB</span></td>\n");
buf.append("<td class=\"b_remLocalTags\">");
for (Iterator titer = new TreeSet(localIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
String tag = (String)titer.next();
buf.append("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
buf.append("<a class=\"b_remLocalTag\" href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
}
buf.append("</td>\n");
buf.append("</tr>\n");
@ -577,12 +696,14 @@ public class RemoteArchiveBean {
// now for posts in blogs we have and they don't
int newBefore = localNew;
buf.setLength(0);
buf.append("<tr><td colspan=\"5\">Blogs the remote Syndie doesn't have</td></tr>\n");
buf.append("<tr class=\"b_remLocalHeader\"><td class=\"b_remLocalHeader\" colspan=\"5\"><span class=\"b_remLocalHeader\">Blogs the remote Syndie doesn't have</span></td></tr>\n");
for (Iterator iter = localBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (remoteBlogs.contains(blog)) {
//System.err.println("Remote index has " + blog.toBase64());
continue;
} else if (ignoreBlog(user, blog)) {
continue;
}
entries.clear();
@ -590,15 +711,15 @@ public class RemoteArchiveBean {
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
buf.append("<tr>\n");
buf.append("<td><input type=\"checkbox\" name=\"localentry\" value=\"" + uri.toString() + "\" /></td>\n");
buf.append("<td>" + getDate(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + getId(uri.getEntryId()) + "</td>\n");
buf.append("<td>" + localIndex.getBlogEntrySizeKB(uri) + "KB</td>\n");
buf.append("<td>");
buf.append("<tr class=\"b_remLocalDetail\">\n");
buf.append("<td class=\"b_remLocalDetail\"><input class=\"b_remLocalSend\" type=\"checkbox\" name=\"localentry\" value=\"" + uri.toString() + "\" /></td>\n");
buf.append("<td class=\"b_remLocalDate\"><span class=\"b_remLocalDate\">" + getDate(uri.getEntryId()) + "</span></td>\n");
buf.append("<td class=\"b_remLocalNum\"><span class=\"b_remLocalNum\">" + getId(uri.getEntryId()) + "</span></td>\n");
buf.append("<td class=\"b_remLocalSize\"><span class=\"b_remLocalSize\">" + localIndex.getBlogEntrySizeKB(uri) + "KB</span></td>\n");
buf.append("<td class=\"b_remLocalTags\">");
for (Iterator titer = new TreeSet(localIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
String tag = (String)titer.next();
buf.append("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
buf.append("<a class=\"b_remLocalTag\" href=\"" + HTMLRenderer.getPageURL(blog, tag, -1, -1, -1, user.getShowExpanded(), user.getShowImages()) + "\">" + tag + "</a> \n");
}
buf.append("</td>\n");
buf.append("</tr>\n");
@ -610,13 +731,13 @@ public class RemoteArchiveBean {
out.write("</table>\n");
if (newEntries > 0) {
out.write("<input type=\"submit\" name=\"action\" value=\"Fetch selected entries\" /> \n");
out.write("<input type=\"submit\" name=\"action\" value=\"Fetch all new entries\" /> \n");
out.write("<input class=\"b_remFetchSelected\" type=\"submit\" name=\"action\" value=\"Fetch selected entries\" /> \n");
out.write("<input class=\"b_remFetchAll\" type=\"submit\" name=\"action\" value=\"Fetch all new entries\" /> \n");
} else {
out.write(HTMLRenderer.sanitizeString(_remoteLocation) + " has no new posts to offer us\n");
out.write("<span class=\"b_remNoRemotePosts\">" + HTMLRenderer.sanitizeString(_remoteLocation) + " has no new posts to offer us</span>\n");
}
if (localNew > 0) {
out.write("<input type=\"submit\" name=\"action\" value=\"Post selected entries\" /> \n");
out.write("<input class=\"b_remPostSelected\" type=\"submit\" name=\"action\" value=\"Post selected entries\" /> \n");
}
out.write("<hr />\n");
}

View File

@ -1,9 +1,39 @@
<%@page contentType="text/html; charset=UTF-8" import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*" %>
<%@page contentType="text/html; charset=UTF-8" import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*, net.i2p.client.naming.PetName" %>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" /><table border="0" width="100%">
<tr><form action="index.jsp"><td nowrap="true">
<b>Blogs:</b> <%ArchiveViewerBean.renderBlogSelector(user, request.getParameterMap(), out);%>
<input type="submit" value="Refresh" />
<input type="submit" name="action" value="<%=ArchiveViewerBean.SEL_ACTION_SET_AS_DEFAULT%>" />
<!-- char encoding: [<%=response.getCharacterEncoding()%>] content type [<%=response.getContentType()%>] Locale [<%=response.getLocale()%>] -->
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" /><%
if (user.getAuthenticated() && (null != request.getParameter("action")) ) {
%><!-- <%=request.getParameterMap()%> --><%
String blog = request.getParameter("blog");
String group = null;
if (request.getParameter("action").equals("Bookmark blog"))
group = "Favorites";
else if (request.getParameter("action").equals("Ignore blog"))
group = "Ignore";
boolean unignore = ("Unignore blog".equals(request.getParameter("action")));
String name = user.getPetNameDB().getNameByLocation(blog);
if (name == null)
name = request.getParameter("name");
if (name == null)
name = blog;
if ( (name != null) && (blog != null) && ( (group != null) || (unignore) ) ) {
PetName pn = user.getPetNameDB().get(name);
if (pn != null) {
if (unignore)
pn.removeGroup("Ignore");
else
pn.addGroup(group);
} else {
pn = new PetName(name, "syndie", "syndieblog", blog);
pn.addGroup(group);
user.getPetNameDB().set(name, pn);
}
BlogManager.instance().saveUser(user);
}
}
%><table border="0" width="100%" class="b_content">
<tr class="b_content"><form action="index.jsp"><td nowrap="nowrap">
<em class="b_selectorTitle">Blogs:</em> <span class="b_selector"><%ArchiveViewerBean.renderBlogSelector(user, request.getParameterMap(), out);%></span>
<input type="submit" value="Refresh" class="b_selectorRefresh" />
<input type="submit" name="action" value="<%=ArchiveViewerBean.SEL_ACTION_SET_AS_DEFAULT%>" class="b_selectorDefault" />
<%ArchiveViewerBean.renderBlogs(user, request.getParameterMap(), out, "</td></form></tr><tr><td align=\"left\" valign=\"top\">");%></td></tr></table>

View File

@ -1,14 +1,18 @@
<%@page import="net.i2p.syndie.*, net.i2p.syndie.sml.*, net.i2p.syndie.web.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<td valign="top" align="left" class="syndieTopNavBlogsCell" height="10"><a href="index.jsp">Home</a></td>
<td valign="top" align="left" class="syndieTopNavRemoteCell" height="10">
<a href="remote.jsp">Remote archives</a>
<a href="import.jsp">Import</a>
</td>
<form action="<%=request.getRequestURI() + "?" + (request.getQueryString() != null ? request.getQueryString() : "")%>">
<td nowrap="true" valign="top" align="right" class="syndieTopNavManageCell" height="10"><%
<td nowrap="nowrap" colspan="2" height="10" class="b_topnav">
<span class="b_topnavHome"><a href="index.jsp" class="b_topnavHome">Home</a></span>
<a href="admin.jsp" class="b_topnavAdmin">Syndie admin</a>
<a href="remote.jsp" class="b_topnavRemote">Remote archives</a>
<a href="import.jsp" class="b_topnavImport">Import</a>
</td><td nowrap="nowrap" height="10" class="b_topnavUser"><%
if ("true".equals(request.getParameter("logout"))) {
user.invalidate();
RemoteArchiveBean rem = (RemoteArchiveBean)session.getAttribute("remote");
if (rem != null) rem.reinitialize();
PostBean post = (PostBean)session.getAttribute("post");
if (post != null) post.reinitialize();
}
String login = request.getParameter("login");
String pass = request.getParameter("password");
@ -16,27 +20,27 @@ String loginSubmit = request.getParameter("Login");
if ( (login != null) && (pass != null) && (loginSubmit != null) && (loginSubmit.equals("Login")) ) {
String loginResult = BlogManager.instance().login(user, login, pass);
if (!user.getAuthenticated())
out.write("<b>" + loginResult + "</b>");
out.write("<b class=\"b_topnavLoginResult\">" + loginResult + "</b>");
}
%>
<% if (user.getAuthenticated()) { %>
Logged in as: <b><jsp:getProperty property="username" name="user" />:</b>
<a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())%>"><%=HTMLRenderer.sanitizeString(ArchiveViewerBean.getBlogName(user.getBlogStr()))%></a>
<a href="<%=HTMLRenderer.getPostURL(user.getBlog())%>">Post</a>
<a href="<%=HTMLRenderer.getMetadataURL(user.getBlog())%>">Metadata</a>
<a href="index.jsp?logout=true">Logout</a><br />
<span class="b_topnavUsername">Logged in as:</span> <em class="b_topnavUsername"><jsp:getProperty property="username" name="user" />:</em>
<a class="b_topnavBlog" href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())%>"><%=HTMLRenderer.sanitizeString(ArchiveViewerBean.getBlogName(user.getBlogStr()))%></a>
<a class="b_topnavPost" href="<%=HTMLRenderer.getPostURL(user.getBlog())%>">Post</a>
<a class="b_topnavMeta" href="<%=HTMLRenderer.getMetadataURL(user.getBlog())%>">Metadata</a>
<a class="b_topnavAddr" href="addresses.jsp">Addressbook</a>
<a class="b_topnavLogout" href="index.jsp?logout=true">Logout</a>
<%} else {%>
Login: <input type="text" name="login" size="8" />
Pass: <input type="password" name="password" size="8" /><%
<span class="b_topnavLogin">Login:</span> <input class="b_topnavLogin" type="text" name="login" size="8" />
<span class="b_topnavPass">Pass:</span> <input class="b_topnavPass" type="password" name="password" size="8" /><%
java.util.Enumeration params = request.getParameterNames();
while (params.hasMoreElements()) {
String p = (String)params.nextElement();
String val = request.getParameter(p);
%><input type="hidden" name="<%=p%>" value="<%=val%>" /><%
}%>
<input type="submit" name="Login" value="Login" />
<a href="register.jsp">Register</a>
<input class="b_topnavLoginSubmit" type="submit" name="Login" value="Login" />
<a class="b_topnavRegister" href="register.jsp">Register</a>
<% } %>
</td>
</form>

View File

@ -1,47 +0,0 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.*, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><%
String nameStr = request.getParameter("name");
String locStr = request.getParameter("location");
String schemaStr = request.getParameter("schema");
String name = null;
String location = null;
String schema = null;
try {
name = DataHelper.getUTF8(Base64.decode(nameStr));
location = DataHelper.getUTF8(Base64.decode(locStr));
schema = DataHelper.getUTF8(Base64.decode(schemaStr));
} catch (NullPointerException npe) {
// ignore
}
if ( (name == null) || (location == null) || (schema == null) ) {
out.write("<b>No location specified</b>");
} else if (user.getAuthenticated() && ("Add".equals(request.getParameter("action"))) ) {
out.write("<b>" + BlogManager.instance().addAddress(user, name, location, schema) + "</b>");
} else { %>Are you sure you really want to add the
addressbook mapping of <%=HTMLRenderer.sanitizeString(name)%> to
<input type="text" size="20" value="<%=HTMLRenderer.sanitizeString(location)%>" />, applicable within the
schema <%=HTMLRenderer.sanitizeString(schema)%>?
<% if (!user.getAuthenticated()) { %>
<p />If so, add the line
<input type="text" size="20" value="<%=HTMLRenderer.sanitizeString(name)%>=<%=HTMLRenderer.sanitizeString(location)%>" />
to your <code>userhosts.txt</code>.
<% } else { %><br />
<a href="addaddress.jsp?name=<%=HTMLRenderer.sanitizeURL(name)%>&location=<%=HTMLRenderer.sanitizeURL(location)%>&schema=<%=HTMLRenderer.sanitizeURL(schema)%>&action=Add">Yes, add it</a>.
<% }
} %></td></tr>
</table>
</body>

View File

@ -0,0 +1,202 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, net.i2p.client.naming.PetName, net.i2p.client.naming.PetNameDB, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %><%
request.setCharacterEncoding("UTF-8"); %><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user"
/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia addressbook</title>
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
if (!user.getAuthenticated()) {
%><span class="b_addrMsgErr">You must log in to view your addressbook</span><%
} else {
PetNameDB names = user.getPetNameDB();
String action = request.getParameter("action");
if ( (action != null) && ("Change".equals(action)) ) {
String oldPetname = request.getParameter("petname");
PetName cur = names.get(oldPetname);
if (cur != null) {
cur.setName(request.getParameter("name"));
cur.setNetwork(request.getParameter("network"));
cur.setProtocol(request.getParameter("protocol"));
cur.setIsPublic(null != request.getParameter("isPublic"));
cur.setLocation(request.getParameter("location"));
cur.setGroups(request.getParameter("groups"));
names.remove(oldPetname);
names.set(cur.getName(), cur);
names.store(user.getAddressbookLocation());
%><span class="b_addrMsgOk">Address updated</span><%
}
} else if ( (action != null) && ("Add".equals(action)) ) {
PetName cur = names.get(request.getParameter("name"));
if (cur != null) { %><span class="b_addrMsgErr">Address already exists</span><% } else {
cur = new PetName();
cur.setName(request.getParameter("name"));
cur.setNetwork(request.getParameter("network"));
cur.setProtocol(request.getParameter("protocol"));
cur.setIsPublic(null != request.getParameter("isPublic"));
cur.setLocation(request.getParameter("location"));
cur.setGroups(request.getParameter("groups"));
names.set(cur.getName(), cur);
names.store(user.getAddressbookLocation());
%><span class="b_addrMsgOk">Address added</span><%
}
} else if ( (action != null) && ("Delete".equals(action)) ) {
PetName cur = names.get(request.getParameter("name"));
if (cur != null) {
names.remove(cur.getName());
names.store(user.getAddressbookLocation());
%><span class="b_addrMsgOk">Address removed</span><%
}
} else if ( (action != null) && ("Export".equals(action)) ) {
%><%=BlogManager.instance().exportHosts(user)%><%
}
TreeSet sorted = new TreeSet(names.getNames());
%><table border="0" width="100%" class="b_addr">
<tr class="b_addrHeader">
<td class="b_addrHeader"><em class="b_addrHeader">Name</em></td>
<td class="b_addrHeader"><em class="b_addrHeader">Network</em></td>
<td class="b_addrHeader"><em class="b_addrHeader">Protocol</em></td>
<td class="b_addrHeader"><em class="b_addrHeader">Location</em></td>
<td class="b_addrHeader"><em class="b_addrHeader">Public?</em></td>
<td class="b_addrHeader"><em class="b_addrHeader">Groups</em></td>
<td class="b_addrHeader">&nbsp;</td></tr>
<%
StringBuffer buf = new StringBuffer(128);
for (Iterator iter = sorted.iterator(); iter.hasNext(); ) {
PetName name = names.get((String)iter.next());
buf.append("<tr class=\"b_addrDetail\"><form action=\"addresses.jsp\" method=\"POST\">");
buf.append("<input type=\"hidden\" name=\"petname\" value=\"").append(name.getName()).append("\" />");
buf.append("<td class=\"b_addrName\"><input class=\"b_addrName\" type=\"text\" size=\"20\" name=\"name\" value=\"").append(name.getName()).append("\" /></td>");
buf.append("<td class=\"b_addrNet\"><select class=\"b_addrNet\" name=\"network\">");
String net = name.getNetwork();
if (net == null) net = "";
buf.append("<option value=\"i2p\" ");
if ("i2p".equals(net))
buf.append("selected=\"true\" ");
buf.append("/>I2P</option>");
buf.append("<option value=\"syndie\" ");
if ( ("syndie".equals(net)) || ("".equals(net)) )
buf.append("selected=\"true\" ");
buf.append("/>Syndie</option>");
buf.append("<option value=\"tor\" ");
if ("tor".equals(net))
buf.append("selected=\"true\" ");
buf.append("/>TOR</option>");
buf.append("<option value=\"freenet\" ");
if ("freenet".equals(net))
buf.append("selected=\"true\" ");
buf.append("/>Freenet</option>");
buf.append("<option value=\"internet\" ");
if ("internet".equals(net))
buf.append("selected=\"true\" ");
buf.append("/>Internet</option>");
buf.append("</select></td>");
buf.append("<td class=\"b_addrProto\"><select class=\"b_addrProto\" name=\"protocol\">");
String proto = name.getProtocol();
if (proto == null) proto = "";
buf.append("<option value=\"http\" ");
if ("http".equals(proto))
buf.append("selected=\"true\" ");
buf.append("/>HTTP</option>");
buf.append("<option value=\"irc\" ");
if ("irc".equals(proto))
buf.append("selected=\"true\" ");
buf.append("/>IRC</option>");
buf.append("<option value=\"i2phex\" ");
if ("i2phex".equals(proto))
buf.append("selected=\"true\" ");
buf.append("/>I2Phex</option>");
buf.append("<option value=\"syndiearchive\" ");
if ("syndiearchive".equals(proto))
buf.append("selected=\"true\" ");
buf.append("/>Syndie archive</option>");
buf.append("<option value=\"syndieblog\" ");
if ("syndieblog".equals(proto))
buf.append("selected=\"true\" ");
buf.append("/>Syndie blog</option>");
buf.append("</select></td>");
buf.append("<td class=\"b_addrLoc\">");
if (name.getLocation() != null)
buf.append("<input class=\"b_addrLoc\" name=\"location\" size=\"50\" value=\"").append(name.getLocation()).append("\" />");
else
buf.append("<input class=\"b_addrLoc\" name=\"location\" size=\"50\" value=\"\" />");
buf.append("</td>");
buf.append("<td class=\"b_addrPublic\"><input class=\"b_addrPublic\" type=\"checkbox\" name=\"isPublic\" ");
if (name.getIsPublic())
buf.append("checked=\"true\" ");
buf.append(" /></td>");
buf.append("<td class=\"b_addrGroup\"><input class=\"b_addrGroup\" type=\"text\" name=\"groups\" size=\"10\" value=\"");
for (int j = 0; j < name.getGroupCount(); j++) {
buf.append(HTMLRenderer.sanitizeTagParam(name.getGroup(j)));
if (j + 1 < name.getGroupCount())
buf.append(',');
}
buf.append("\" /></td><td class=\"b_addrDetail\" nowrap=\"nowrap\">");
buf.append("<input class=\"b_addrChange\" type=\"submit\" name=\"action\" value=\"Change\" /> <input class=\"b_addrDelete\" type=\"submit\" name=\"action\" value=\"Delete\" />");
buf.append("</td></form></tr>");
out.write(buf.toString());
buf.setLength(0);
}
String net = request.getParameter("network");
String proto = request.getParameter("protocol");
String name = request.getParameter("name");
String loc = request.getParameter("location");
boolean active = (request.getParameter("action") != null);
if (net == null || active) net = "";
if (proto == null || active) proto = "";
if (name == null || active) name = "";
if (loc == null || active) loc= "";
%>
<tr class="b_addrDetail"><form action="addresses.jsp" method="POST">
<td class="b_addrName"><input class="b_addrName" type="text" name="name" size="20" value="<%=name%>" /></td>
<td class="b_addrNet"><select class="b_addrNet" name="network">
<option value="i2p" <%="i2p".equalsIgnoreCase(net) ? " selected=\"true\" " : ""%>>I2P</option>
<option value="syndie" <%="syndie".equalsIgnoreCase(net) ? " selected=\"true\" " : ""%>>Syndie</option>
<option value="tor" <%="tor".equalsIgnoreCase(net) ? " selected=\"true\" " : ""%>>Tor</option>
<option value="freenet" <%="freenet".equalsIgnoreCase(net) ? " selected=\"true\" " : ""%>>Freenet</option>
<option value="internet" <%="internet".equalsIgnoreCase(net) ? " selected=\"true\" " : ""%>>Internet</option></select></td>
<td class="b_addrProto"><select class="b_addrProto" name="protocol">
<option value="http" <%="http".equalsIgnoreCase(proto) ? " selected=\"true\" " : ""%>>HTTP</option>
<option value="irc" <%="irc".equalsIgnoreCase(proto) ? " selected=\"true\" " : ""%>>IRC</option>
<option value="i2phex" <%="i2phex".equalsIgnoreCase(proto) ? " selected=\"true\" " : ""%>>I2Phex</option>
<option value="syndiearchive" <%="syndiearchive".equalsIgnoreCase(proto) ? " selected=\"true\" " : ""%>>Syndie archive</option>
<option value="syndieblog" <%="syndieblog".equalsIgnoreCase(proto) ? " selected=\"true\" " : ""%>>Syndie blog</option></select></td>
<td class="b_addrLoc"><input class="b_addrLoc" type="text" size="50" name="location" value="<%=loc%>" /></td>
<td class="b_addrPublic"><input class="b_addrPublic" type="checkbox" name="isPublic" /></td>
<td class="b_addrGroup"><input class="b_addrGroup" type="text" name="groups" size="10" /></td>
<td class="b_addrDetail"><input class="b_addrAdd" type="submit" name="action" value="Add" /></td>
</form></tr>
<tr class="b_addrExport"><form action="addresses.jsp" method="POST">
<td class="b_addrExport" colspan="7">
<span class="b_addrExport">Export the eepsites to your router's petname db</span>
<input class="b_addrExportSubmit" type="submit" name="action" value="Export" /></td>
</form></tr>
</table>
<%
}
%>
</td></tr>
</table>
</body>

93
apps/syndie/jsp/admin.jsp Normal file
View File

@ -0,0 +1,93 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %><%
request.setCharacterEncoding("UTF-8");
%><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user"
/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"><html>
<head>
<title>SyndieMedia admin</title>
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
if (!user.getAuthenticated()) {
%><span class="b_adminMsgErr">You must be logged in to configure your Syndie instance!</span><%
} else {
String action = request.getParameter("action");
if ( (action != null) && ("Save".equals(action)) ) {
boolean configured = BlogManager.instance().isConfigured();
String adminPass = request.getParameter("adminpass");
String regPass = request.getParameter("regpass");
String remotePass = request.getParameter("remotepass");
String proxyHost = request.getParameter("proxyhost");
String proxyPort = request.getParameter("proxyport");
String selector = request.getParameter("selector");
boolean isSingleUser = BlogManager.instance().isSingleUser();
String singleSet = request.getParameter("singleuser");
if (singleSet != null)
isSingleUser = true;
else
isSingleUser = false;
if (configured) {
if (BlogManager.instance().authorizeAdmin(adminPass)) {
int port = -1;
try { port = Integer.parseInt(proxyPort); } catch (NumberFormatException nfe) { port = 4444; }
BlogManager.instance().configure(regPass, remotePass, adminPass, selector, proxyHost, port, isSingleUser, null);
%><span class="b_adminMsgOk">Configuration updated</span><%
} else {
%><span class="b_adminMsgErr">Invalid admin password. If you lost it, please update your syndie.config.</span><%
}
} else {
int port = -1;
try { port = Integer.parseInt(proxyPort); } catch (NumberFormatException nfe) { port = 4444; }
BlogManager.instance().configure(regPass, remotePass, adminPass, selector, proxyHost, port, isSingleUser, null);
%><span class="b_adminMsgOk">Configuration saved</span><%
}
} else {
%><form action="admin.jsp" method="POST">
<em class="b_adminField">Single user?</em> <input type="checkbox" class="b_adminField" name="singleuser" <%=BlogManager.instance().isSingleUser() ? " checked=\"true\" " : ""%> /><br />
<span class="b_adminDescr">If this is checked, the registration, admin, and remote passwords are unnecessary - anyone
can register and administer Syndie, as well as use any remote functionality. This should not be checked if untrusted
parties can access this web interface.</span><br />
<em class="b_adminField">Registration password:</em> <input class="b_adminField" type="text" name="regpass" size="10" /><br />
<span class="b_adminDescr">Users must specify this password on the registration form to proceed. If this is
blank, anyone can register.</span><br />
<em class="b_adminField">Remote password:</em> <input class="b_adminField" type="text" name="remotepass" size="10" /><br />
<span class="b_adminDescr">To access remote archives, users must first provide this password on their
metadata page. Remote access is 'dangerous', as it allows the user to instruct
this Syndie instance to establish HTTP connections with arbitrary locations. If
this field is not specified, no one can use remote archives.</span><br />
<em class="b_adminField">Default remote proxy host:</em> <input class="b_adminField" type="text" name="proxyhost" size="20" value="localhost" /><br />
<em class="b_adminField">Default remote proxy port:</em> <input class="b_adminField" type="text" name="proxyport" size="5" value="4444" /><br />
<span class="b_adminDescr">This is the default HTTP proxy shown on the remote archive page.</span><br />
<em class="b_adminField">Default blog selector:</em> <input class="b_adminField" type="text" name="selector" size="40" value="ALL" /><br />
<span class="b_adminDescr">The selector lets you choose what blog (or blogs) are shown on the front page for
new, unregistered users. Valid values include:<ul class="b_adminDescr">
<li class="b_adminDescr"><code class="b_adminDescr">ALL</code>: all blogs</li>
<li class="b_adminDescr"><code class="b_adminDescr">blog://$blogHash</code>: all posts in the blog identified by $blogHash</li>
<li class="b_adminDescr"><code class="b_adminDescr">blogtag://$blogHash/$tagBase64</code>: all posts in the blog identified by $blogHash
tagged by the tag whose modified base64 encoding is $tagBase64</li>
<li class="b_adminDescr"><code class="b_adminDescr">tag://$tagBase64</code>: all posts in any blog tagged by the tag whose
modified base64 encoding is $tagBase64</li>
</ul>
</span>
<hr />
<% if (!BlogManager.instance().isConfigured()) {
long passNum = new Random().nextLong(); %>
<em class="b_adminField">Administrative password:</em> <input class="b_adminField" type="password" name="adminpass" size="10" value="<%=passNum%>" /> <br />
<span class="b_adminDescr b_adminDescrFirstRun">Since this Syndie instance is not already configured, you can specify a new
administrative password which must be presented whenever you update this configuration.
The default value filled in there is <code class="b_adminDescr b_adminDescrFirstRun"><%=passNum%></code></span><br />
<% } else { %>
<em class="b_adminField">Administrative password:</em> <input class="b_adminField" type="password" name="adminpass" size="10" value="" /> <br />
<% } %>
<input class="b_adminSave" type="submit" name="action" value="Save" />
<% }
} %>
</td></tr>
</table>
</body>

View File

@ -1,17 +1,19 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.*, net.i2p.syndie.web.*, net.i2p.syndie.sml.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.*, net.i2p.syndie.web.*, net.i2p.syndie.sml.*" %><%
request.setCharacterEncoding("UTF-8");
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3">Are you sure you really want to go to
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content">
<span class="b_externalWarning">Are you sure you really want to go to
<%
String loc = request.getParameter("location");
String schema = request.getParameter("schema");
@ -21,14 +23,14 @@ if (schema != null) schema = HTMLRenderer.sanitizeString(DataHelper.getUTF8(Base
if (desc != null) desc = HTMLRenderer.sanitizeString(DataHelper.getUTF8(Base64.decode(desc)));
if ( (loc != null) && (schema != null) ) {
out.write(loc + " (" + schema + ")");
out.write("<span class=\"b_externalLoc\">" + loc + "</span> <span class=\"b_externalNet\"(" + schema + ")</span>");
if (desc != null)
out.write(": " + desc);
out.write(": <span class=\"b_externalDesc\"" + desc + "\"</span>");
out.write("? ");
out.write("<a href=\"" + loc + "\">yes</a>");
out.write("<a class=\"b_external\" href=\"" + loc + "\">yes</a>");
} else {
out.write("(some unspecified location...)");
out.write("<span class=\"b_externalUnknown\">(some unspecified location...)</span>");
}
%></td></tr>
%></span></td></tr>
</table>
</body>

View File

@ -1,18 +1,19 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive" />
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %><%
request.setCharacterEncoding("UTF-8");
%><jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive"
/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia import</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><%
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
String contentType = request.getContentType();
if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
@ -20,10 +21,10 @@ if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
int metaId = 0;
while (true) {
InputStream meta = req.getInputStream("blogmeta" + metaId);
if (meta == null)
if ( (meta == null) || (meta.available() <= 0) )
break;
if (!BlogManager.instance().importBlogMetadata(meta)) {
System.err.println("blog meta " + metaId + " failed to be imported");
%><span class="b_importMsgErr">Metadata <%=metaId%> failed to be imported</span><br /><%
break;
}
metaId++;
@ -31,10 +32,10 @@ if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
int entryId = 0;
while (true) {
InputStream entry = req.getInputStream("blogpost" + entryId);
if (entry == null)
if ( (entry == null) || (entry.available() <= 0) )
break;
if (!BlogManager.instance().importBlogEntry(entry)) {
System.err.println("blog entry " + entryId + " failed to be imported");
%><span class="b_importMsgErr">Entry <%=entryId%> failed to be imported</span><br /><%
break;
}
entryId++;
@ -44,23 +45,23 @@ if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
BlogManager.instance().getArchive().regenerateIndex();
session.setAttribute("index", BlogManager.instance().getArchive().getIndex());
}
%>Imported <%=entryId%> posts and <%=metaId%> blog metadata files.
%><span class="b_importMsgOk">Imported <%=entryId%> posts and <%=metaId%> blog metadata files.</span>
<%
} else { %><form action="import.jsp" method="POST" enctype="multipart/form-data">
Blog metadata 0: <input type="file" name="blogmeta0" /><br />
Blog metadata 1: <input type="file" name="blogmeta1" /><br />
Post 0: <input type="file" name="blogpost0" /><br />
Post 1: <input type="file" name="blogpost1" /><br />
Post 2: <input type="file" name="blogpost2" /><br />
Post 3: <input type="file" name="blogpost3" /><br />
Post 4: <input type="file" name="blogpost4" /><br />
Post 5: <input type="file" name="blogpost5" /><br />
Post 6: <input type="file" name="blogpost6" /><br />
Post 7: <input type="file" name="blogpost7" /><br />
Post 8: <input type="file" name="blogpost8" /><br />
Post 9: <input type="file" name="blogpost9" /><br />
<span class="b_importField">Blog metadata 0:</span> <input class="b_importField" type="file" name="blogmeta0" /><br />
<span class="b_importField">Blog metadata 1:</span> <input class="b_importField" type="file" name="blogmeta1" /><br />
<span class="b_importField">Post 0:</span> <input class="b_importField" type="file" name="blogpost0" /><br />
<span class="b_importField">Post 1:</span> <input class="b_importField" type="file" name="blogpost1" /><br />
<span class="b_importField">Post 2:</span> <input class="b_importField" type="file" name="blogpost2" /><br />
<span class="b_importField">Post 3:</span> <input class="b_importField" type="file" name="blogpost3" /><br />
<span class="b_importField">Post 4:</span> <input class="b_importField" type="file" name="blogpost4" /><br />
<span class="b_importField">Post 5:</span> <input class="b_importField" type="file" name="blogpost5" /><br />
<span class="b_importField">Post 6:</span> <input class="b_importField" type="file" name="blogpost6" /><br />
<span class="b_importField">Post 7:</span> <input class="b_importField" type="file" name="blogpost7" /><br />
<span class="b_importField">Post 8:</span> <input class="b_importField" type="file" name="blogpost8" /><br />
<span class="b_importField">Post 9:</span> <input class="b_importField" type="file" name="blogpost9" /><br />
<hr />
<input type="submit" name="Post" value="Post entry" /> <input type="reset" value="Cancel" />
<input class="b_importSubmit" type="submit" name="Post" value="Post entry" /> <input class="b_importCancel" type="reset" value="Cancel" />
<% } %>
</td></tr>
</table>

View File

@ -1,16 +1,27 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.syndie.web.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.syndie.web.*" %><%
request.setCharacterEncoding("UTF-8");
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
<link href="style.jsp" rel="stylesheet" type="text/css" >
<link href="rss.jsp?<%
if (request.getParameter("blog") != null)
out.write("blog=" + request.getParameter("blog") + "&");
if (request.getParameter("entry") != null)
out.write("entry=" + request.getParameter("entry") + "&");
if (request.getParameter("tag") != null)
out.write("tag=" + request.getParameter("tag") + "&");
if (request.getParameter("selector") != null)
out.write("selector=" + request.getParameter("selector") + "&");
%>" rel="alternate" type="application/rss+xml" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><jsp:include page="_bodyindex.jsp" /></td></tr>
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><jsp:include page="_bodyindex.jsp" /></td></tr>
</table>
</body>

View File

@ -1,31 +1,34 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.web.PostBean" id="post" />
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.client.naming.PetName, net.i2p.client.naming.PetNameDB, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*" %><%
request.setCharacterEncoding("UTF-8");
%><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user"
/><jsp:useBean scope="session" class="net.i2p.syndie.web.PostBean" id="post"
/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
<title>SyndieMedia post</title>
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><%
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
if (!user.getAuthenticated()) {
%>You must be logged in to post<%
%><span class="b_postMsgErr">You must be logged in to post</span><%
} else {
String confirm = request.getParameter("confirm");
if ( (confirm != null) && (confirm.equalsIgnoreCase("true")) ) {
String confirm = request.getParameter("action");
if ( (confirm != null) && (confirm.equalsIgnoreCase("confirm")) ) {
String archive = request.getParameter("archive");
post.setArchive(archive);
BlogURI uri = post.postEntry();
if (uri != null) {
%>Blog entry <a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, uri.getEntryId(), -1, -1,
user.getShowExpanded(), user.getShowImages())%>">posted</a>!<%
%><span class="b_postMsgOk">Blog entry <a class="b_postOkLink" href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, uri.getEntryId(), -1, -1,
user.getShowExpanded(), user.getShowImages())%>">posted</a>!</span><%
} else {
%>There was an unknown error posting the entry...<%
%><span class="b_postMsgErro">There was an unknown error posting the entry...</span><%
}
post.reinitialize();
post.setUser(user);
@ -43,17 +46,34 @@ if (!user.getAuthenticated()) {
String entryTags = req.getString("entrytags");
String entryText = req.getString("entrytext");
String entryHeaders = req.getString("entryheaders");
String style = req.getString("style");
if ( (style != null) && (style.trim().length() > 0) ) {
if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_STYLE + ": " + style;
else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_STYLE + ": " + style;
}
String replyTo = req.getString(ArchiveViewerBean.PARAM_IN_REPLY_TO);
if ( (replyTo != null) && (replyTo.trim().length() > 0) ) {
byte r[] = Base64.decode(replyTo);
if (r != null) {
if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r);
else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r);
if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r, "UTF-8");
else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r, "UTF-8");
} else {
replyTo = null;
}
}
String includeNames = req.getString("includenames");
if ( (includeNames != null) && (includeNames.trim().length() > 0) ) {
PetNameDB db = user.getPetNameDB();
if (entryHeaders == null) entryHeaders = "";
for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
PetName pn = db.get((String)iter.next());
if ( (pn != null) && (pn.getIsPublic()) ) {
entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_PETNAME + ": " +
pn.getName() + "\t" + pn.getNetwork() + "\t" + pn.getProtocol() + "\t" + pn.getLocation();
}
}
}
post.setSubject(entrySubject);
post.setTags(entryTags);
post.setText(entryText);
@ -76,16 +96,42 @@ if (!user.getAuthenticated()) {
}
post.renderPreview(out);
%><hr />Please <a href="post.jsp?confirm=true">confirm</a> that this is ok. Otherwise, just go back and make changes.<%
%><hr /><span class="b_postConfirm"><form action="post.jsp" method="POST">
Please confirm that the above is ok<% if (BlogManager.instance().authorizeRemote(user)) { %>, and select what additional archives you
want the post transmitted to. Otherwise, just hit your browser's back arrow and
make changes.
<select class="b_postConfirm" name="archive">
<option name="">-None-</option>
<%
PetNameDB db = user.getPetNameDB();
TreeSet names = new TreeSet();
for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
PetName pn = db.get(name);
if ("syndiearchive".equals(pn.getProtocol()))
names.add(pn.getName());
}
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(name) + "\">" + HTMLRenderer.sanitizeString(name) + "</option>\n");
}
%>
</select><br /><% } %></span>
<input class="b_postConfirm" type="submit" name="action" value="Confirm" /><%
} else {
// logged in and not confirmed because they didn't send us anything!
// give 'em a new form
%><form action="post.jsp" method="POST" enctype="multipart/form-data">
Post subject: <input type="text" size="80" name="entrysubject" value="<%=post.getSubject()%>" /><br />
Post tags: <input type="text" size="20" name="entrytags" value="<%=post.getTags()%>" /><br />
Post content (in raw SML, no headers):<br />
<textarea rows="6" cols="80" name="entrytext"><%=post.getText()%></textarea><br />
<b>SML cheatsheet:</b><br /><textarea rows="6" cols="80" readonly="true">
<span class="b_postField">Post subject:</span> <input class="b_postSubject" type="text" size="80" name="entrysubject" value="<%=post.getSubject()%>" /><br />
<span class="b_postField">Post tags:</span> <input class="b_postTags" type="text" size="20" name="entrytags" value="<%=post.getTags()%>" /><br />
<span class="b_postField">Post style:</span> <select class="b_postStyle" name="style">
<option value="default" selected="true">Default</option>
<option value="meta">Meta (hide everything but the metadata)</option>
</select><br />
<span class="b_postField">Include public names?</span> <input class="b_postNames" type="checkbox" name="includenames" value="true" /><br />
<span class="b_postField">Post content (in raw SML, no headers):</span><br />
<textarea class="b_postText" rows="6" cols="80" name="entrytext"><%=post.getText()%></textarea><br />
<span class="b_postField">SML cheatsheet:</span><br /><textarea class="b_postCheatsheet" rows="6" cols="80" readonly="true">
* newlines are newlines are newlines.
* all &lt; and &gt; are replaced with their &amp;symbol;
* [b][/b] = <b>bold</b>
@ -100,30 +146,23 @@ Post content (in raw SML, no headers):<br />
* [link schema="eep" location="http://forum.i2p"]text[/link] = offer a link to an external resource (accessible with the given schema)
* [archive name="name" description="they have good stuff" schema="eep" location="http://syndiemedia.i2p/archive/archive.txt"]foo![/archive] = offer an easy way to sync up with a new Syndie archive
SML headers are newline delimited key=value pairs. Example keys are:
* bgcolor = background color of the post (e.g. bgcolor=#ffccaa or bgcolor=red)
SML headers are newline delimited key:value pairs. Example keys are:
* bgcolor = background color of the post (e.g. bgcolor:#ffccaa or bgcolor=red)
* bgimage = attachment number to place as the background image for the post (only shown if images are enabled) (e.g. bgimage=1)
* textfont = font to put most text into
</textarea><br />
SML post headers:<br />
<textarea rows="3" cols="80" name="entryheaders"><%=post.getHeaders()%></textarea><br /><%
<span class="b_postField">SML post headers:</span><br />
<textarea class="b_postHeaders" rows="3" cols="80" name="entryheaders"><%=post.getHeaders()%></textarea><br /><%
String s = request.getParameter(ArchiveViewerBean.PARAM_IN_REPLY_TO);
if ( (s != null) && (s.trim().length() > 0) ) {%>
<input type="hidden" name="<%=ArchiveViewerBean.PARAM_IN_REPLY_TO%>" value="<%=request.getParameter(ArchiveViewerBean.PARAM_IN_REPLY_TO)%>" />
<% } %>
Attachment 0: <input type="file" name="entryfile0" /><br />
Attachment 1: <input type="file" name="entryfile1" /><br />
Attachment 2: <input type="file" name="entryfile2" /><br />
Attachment 3: <input type="file" name="entryfile3" /><br /><!--
Attachment 4: <input type="file" name="entryfile4" /><br />
Attachment 5: <input type="file" name="entryfile5" /><br />
Attachment 6: <input type="file" name="entryfile6" /><br />
Attachment 7: <input type="file" name="entryfile7" /><br />
Attachment 8: <input type="file" name="entryfile8" /><br />
Attachment 9: <input type="file" name="entryfile9" /><br />-->
<span class="b_postField">Attachment 0:</span> <input class="b_postField" type="file" name="entryfile0" /><br />
<span class="b_postField">Attachment 1:</span> <input class="b_postField" type="file" name="entryfile1" /><br />
<span class="b_postField">Attachment 2:</span> <input class="b_postField" type="file" name="entryfile2" /><br />
<span class="b_postField">Attachment 3:</span> <input class="b_postField" type="file" name="entryfile3" /><br />
<hr />
<input type="submit" name="Post" value="Preview..." /> <input type="reset" value="Cancel" />
<input class="b_postPreview" type="submit" name="Post" value="Preview..." /> <input class="b_postReset" type="reset" value="Cancel" />
<%
} // end of the 'logged in, not confirmed, nothing posted' section
} // end of the 'logged in, not confirmed' section

View File

@ -1,18 +1,19 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.*" %><%
request.setCharacterEncoding("UTF-8");
%><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user"
/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><td valign="top" align="left" colspan="3"><%
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
String regLogin = request.getParameter("login");
boolean showForm = true;
if ( (regLogin != null) && ("Register".equals(request.getParameter("Register"))) ) {
@ -23,25 +24,25 @@ if ( (regLogin != null) && ("Register".equals(request.getParameter("Register")))
String url = request.getParameter("contacturl");
String regResult = BlogManager.instance().register(user, regLogin, regUserPass, regPass, blogName, desc, url);
if (User.LOGIN_OK.equals(regResult)) {
out.print("<b>Registration successful.</b> <a href=\"index.jsp\">Continue...</a>\n");
showForm = false;
%><span class="b_regMsgOk">Registration successful.</span> <a class="b_reg" href="index.jsp">Continue...</a>
<% showForm = false;
} else {
out.print("<b>" + regResult + "</b>");
%><span class="b_regMsgErr"><%=regResult%></span><%
}
}
if (showForm) {%><form action="register.jsp" method="POST">
<p>To create a new blog (and Syndie user account), please fill out the following form.
<p class="b_reg">To create a new blog (and Syndie user account), please fill out the following form.
You may need to enter a registration password given to you by this Syndie instance's
operator, or there may be no registration password in place (in which case you can
leave that field blank).</p>
<p>
<b>Syndie login:</b> <input type="text" size="8" name="login" /><br />
<b>New password:</b> <input type="password" size="8" name="password" /><br />
<b>Registration password:</b> <input type="password" size="8" name="registrationpassword" /><br />
<b>Blog name:</b> <input type="text" size="32" name="blogname" /><br />
<b>Brief description:</b> <input type="text" size="60" name="description" /><br />
<b>Contact URL:</b> <input type="text" size="20" name="contacturl" /> <i>(e.g. mailto://user@mail.i2p, http://foo.i2p/, etc)</i><br />
<input type="submit" name="Register" value="Register" />
<p class="b_reg">
<em class="b_regField">Syndie login:</em> <input class="b_regField" type="text" size="8" name="login" /><br />
<em class="b_regField">New password:</em> <input class="b_regField" type="password" size="8" name="password" /><br />
<em class="b_regField">Registration password:</em> <input class="b_regField" type="password" size="8" name="registrationpassword" /><br />
<em class="b_regField">Blog name:</em> <input class="b_regField" type="text" size="32" name="blogname" /><br />
<em class="b_regField">Brief description:</em> <input class="b_regField" type="text" size="60" name="description" /><br />
<em class="b_regField">Contact URL:</em> <input class="b_regField" type="text" size="20" name="contacturl" /> <span class="b_reg">(e.g. mailto://user@mail.i2p, http://foo.i2p/, etc)</span><br />
<input class="b_regSubmit" type="submit" name="Register" value="Register" />
</p>
</form><% } %>
</td></tr>

View File

@ -1,37 +1,54 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.syndie.web.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean scope="session" class="net.i2p.syndie.web.RemoteArchiveBean" id="remote" />
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive" />
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.client.naming.PetName, net.i2p.syndie.web.*, net.i2p.syndie.*, net.i2p.syndie.sml.*, java.util.*" %><%
request.setCharacterEncoding("UTF-8");
%><jsp:useBean scope="session" class="net.i2p.syndie.web.RemoteArchiveBean" id="remote"
/><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user"
/><jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive"
/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>SyndieMedia</title>
<link href="style.jsp" rel="stylesheet" type="text/css" />
<title>SyndieMedia remote</title>
<link href="style.jsp" rel="stylesheet" type="text/css" >
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="100%">
<tr><td colspan="5" valign="top" align="left"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2"><jsp:include page="_leftnav.jsp" /></td>
<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
<jsp:include page="_topnav.jsp" />
<td valign="top" align="left" rowspan="2"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr><form action="remote.jsp" method="POST"><td valign="top" align="left" colspan="3">
<%
if (!user.getAuthenticated() || !user.getAllowAccessRemote()) {
%>Sorry, you are not allowed to access remote archives from here. Perhaps you should install Syndie yourself?<%
} else { %>Import from:
<select name="schema">
<td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
if (!BlogManager.instance().authorizeRemote(user)) {
%><span class="b_remoteMsgErr">Sorry, you are not allowed to access remote archives from here. Perhaps you should install Syndie yourself?</span><%
} else { %><form action="remote.jsp" method="POST"><span class="b_remoteChooser"><span class="b_remoteChooserField">Import from:</span>
<select class="b_remoteChooserNet" name="schema">
<option value="web" <%=("web".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>I2P/TOR/Freenet</option>
<option value="mnet" <%=("mnet".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>MNet</option>
<option value="feedspace" <%=("feedspace".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>Feedspace</option>
<option value="usenet" <%=("usenet".equals(request.getParameter("schema")) ? "selected=\"true\"" : "")%>>Usenet</option>
</select>
Proxy <input type="text" size="10" name="proxyhost" value="localhost" />:<input type="text" size="4" name="proxyport" value="4444" />
<input name="location" size="40" value="<%=(request.getParameter("location") != null ? request.getParameter("location") : "")%>" />
<input type="submit" name="action" value="Continue..." /><br />
<span class="b_remoteChooserField">Proxy</span>
<input class="b_remoteChooserHost" type="text" size="10" name="proxyhost" value="<%=BlogManager.instance().getDefaultProxyHost()%>" />
<input class="b_remoteChooserPort" type="text" size="4" name="proxyport" value="<%=BlogManager.instance().getDefaultProxyPort()%>" /><br />
<span class="b_remoteChooserField">Bookmarked archives:</span> <select class="b_remoteChooserPN" name="archivepetname"><option value="">Custom location</option><%
for (Iterator iter = user.getPetNameDB().getNames().iterator(); iter.hasNext(); ) {
PetName pn = user.getPetNameDB().get((String)iter.next());
if ("syndiearchive".equals(pn.getProtocol())) {
%><option value="<%=HTMLRenderer.sanitizeTagParam(pn.getName())%>"><%=HTMLRenderer.sanitizeString(pn.getName())%></option><%
}
}
%></select> or
<input class="b_remoteChooserLocation" name="location" size="30" value="<%=(request.getParameter("location") != null ? request.getParameter("location") : "")%>" />
<input class="b_remoteChooserContinue" type="submit" name="action" value="Continue..." /><br />
</span>
<%
String action = request.getParameter("action");
if ("Continue...".equals(action)) {
remote.fetchIndex(user, request.getParameter("schema"), request.getParameter("location"), request.getParameter("proxyhost"), request.getParameter("proxyport"));
String location = request.getParameter("location");
String pn = request.getParameter("archivepetname");
if ( (pn != null) && (pn.trim().length() > 0) ) {
PetName pnval = user.getPetNameDB().get(pn);
if (pnval != null) location = pnval.getLocation();
}
remote.fetchIndex(user, request.getParameter("schema"), location, request.getParameter("proxyhost"), request.getParameter("proxyport"));
} else if ("Fetch metadata".equals(action)) {
remote.fetchMetadata(user, request.getParameterMap());
} else if ("Fetch selected entries".equals(action)) {
@ -44,20 +61,20 @@ Proxy <input type="text" size="10" name="proxyhost" value="localhost" />:<input
remote.postSelectedEntries(user, request.getParameterMap());
}
String msgs = remote.getStatus();
if ( (msgs != null) && (msgs.length() > 0) ) { %><pre><%=msgs%>
<a href="remote.jsp">Refresh</a></pre><br /><%
if ( (msgs != null) && (msgs.length() > 0) ) { %><pre class="b_remoteProgress"><%=msgs%>
<a class="b_remoteProgress" href="remote.jsp">Refresh</a></pre><br /><%
}
if (remote.getFetchIndexInProgress()) { %><b>Please wait while the index is being fetched
from <%=remote.getRemoteLocation()%></b>. <%
if (remote.getFetchIndexInProgress()) { %><span class="b_remoteProgress">Please wait while the index is being fetched
from <%=remote.getRemoteLocation()%>.</span><%
} else if (remote.getRemoteIndex() != null) {
// remote index is NOT null!
%><b><%=remote.getRemoteLocation()%></b>
<a href="remote.jsp?schema=<%=remote.getRemoteSchema()%>&location=<%=remote.getRemoteLocation()%><%
%><span class="b_remoteLocation"><%=remote.getRemoteLocation()%></span>
<a class="b_remoteRefetch" href="remote.jsp?schema=<%=remote.getRemoteSchema()%>&location=<%=remote.getRemoteLocation()%><%
if (remote.getProxyHost() != null && remote.getProxyPort() > 0) {
%>&proxyhost=<%=remote.getProxyHost()%>&proxyport=<%=remote.getProxyPort()%><%
} %>&action=Continue...">(refetch)</a>:<br />
<%remote.renderDeltaForm(user, archive, out);%>
<textarea style="font-size:8pt" rows="5" cols="120"><%=remote.getRemoteIndex()%></textarea><%
<textarea class="b_remoteIndex" rows="5" cols="120"><%=remote.getRemoteIndex()%></textarea><%
}
}
%>

View File

@ -1,3 +1,7 @@
<%@page contentType="text/css; charset=UTF-8" pageEncoding="UTF-8" %>
<%@page contentType="text/css; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.util.FileUtil" %>
<% request.setCharacterEncoding("UTF-8"); %>
<%@include file="syndie.css" %>
<%@include file="syndie.css" %>
<%
String content = FileUtil.readTextFile("./docs/syndie_standard.css", -1, true);
if (content != null) out.write(content);
%>

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