Compare commits

..

76 Commits

Author SHA1 Message Date
jrandom
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
jrandom
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
jrandom
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
jrandom
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
jrandom
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
jrandom
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
jrandom
3aba12631b use the logger, not stdout/stderr 2005-09-16 06:58:55 +00:00
jrandom
cfee6430d4 xml 2005-09-16 04:54:48 +00:00
jrandom
6b96df1cec runplain.sh, not startRouter.sh 2005-09-16 04:50:28 +00:00
jrandom
deecfa5047 no message 2005-09-16 04:40:07 +00:00
jrandom
6ca3f01038 launch4j 2005-09-16 04:34:59 +00:00
jrandom
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
jrandom
8c1895e04f imported fixed susidns 2005-09-16 04:04:40 +00:00
jrandom
c3d0132a98 test cvs again... 2005-09-15 05:57:28 +00:00
jrandom
d955279d17 minor news (and test cvs...) 2005-09-15 05:56:10 +00:00
jrandom
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
jrandom
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
comwiz
4293a18726 2005-09-12 comwiz
* Migrated the router tests to junit
2005-09-13 09:06:07 +00:00
jrandom
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
jrandom
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
jrandom
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
jrandom
31bdb8909a tino.i2p and fproxy.tino.i2p 2005-09-12 22:40:17 +00:00
jrandom
ee921c22ae use the low level rates (thanks bar / complication) 2005-09-12 02:58:13 +00:00
jrandom
172ffd0434 use the OS time, since it doesn't skew as much (especially on startup) 2005-09-11 04:37:15 +00:00
jrandom
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
jrandom
8ac0e85df4 updated to hq.postman.i2p 2005-09-11 01:40:15 +00:00
jrandom
249ccd5e3c now that its all implemented... 2005-09-10 23:18:41 +00:00
jrandom
727d76d43e deal with posts containing no tags by using the implicit tag "[none]" (thanks ardvark!) 2005-09-10 06:07:25 +00:00
jrandom
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
cervantes
b5d571c75f 2005-09-09 cervantes
* New build due to change in build number :P (thanks ugha!)
2005-09-10 01:13:49 +00:00
ragnarok
da56d83716 First pass at a new naming system. Probably the last as well. So sad :). 2005-09-09 19:38:43 +00:00
jrandom
f777e213ce search.i2p 2005-09-08 05:08:33 +00:00
jrandom
79906f5a7d added search.i2p 2005-09-08 05:01:01 +00:00
jrandom
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
ragnarok
c2ea8db683 Look for names in privatehosts.txt as well as userhosts.txt and hosts.txt. 2005-09-07 02:07:41 +00:00
cervantes
744671a518 Adjusted wording on the bandwidth limiter controls to reflect new router defaults 2005-09-07 01:13:56 +00:00
jrandom
7f5b127bbc fix0rz. 2005-09-06 20:45:21 +00:00
jrandom
89eff0c628 tyop 2005-09-06 20:35:28 +00:00
jrandom
177aeebb1c stuff 2005-09-06 20:15:55 +00:00
jrandom
e0e6bde4a5 throw css around like mad (very minimal stylesheet in place) 2005-09-06 20:05:09 +00:00
jrandom
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
jrandom
f958342704 admin page - no more editing config props manually (w3wt) 2005-09-05 20:53:25 +00:00
jrandom
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
jrandom
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
jrandom
6afc64ac39 deal with locations that have : in them (aka http://glog.i2p/archive/archive.txt) 2005-09-05 17:09:19 +00:00
jrandom
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
jrandom
3bb445ff40 better filtering/ignoring
ui improvements (per isamoor's suggestions)
more petname integration
2005-09-05 01:26:19 +00:00
jrandom
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
jrandom
09cb5fad59 allow you to bookmark syndie archives and later recall those bookmarks on the remote page 2005-09-04 22:43:22 +00:00
jrandom
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
jrandom
339868838d thanks BarkerJr :) 2005-09-04 20:26:42 +00:00
jrandom
c5579fa349 (the filtered blogs may be out of order) 2005-09-04 19:33:00 +00:00
jrandom
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
jrandom
779aa240d2 added glog.i2p 2005-09-03 04:07:50 +00:00
jrandom
9aaad00383 0.6.0.5 2005-09-02 19:10:05 +00:00
jrandom
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
jrandom
3e51584b3c 0.6.0.4 2005-09-01 20:27:35 +00:00
jrandom
4ff8a53084 2005-09-01 jrandom
* Don't send out a netDb store of a router if it is more than a few hours
      old, even if someone asked us for it.
2005-09-01 06:55:00 +00:00
jrandom
ccb73437c4 2005-08-31 jrandom
* Don't publish leaseSets to the netDb if they will never be looked for -
      namely, if they are for destinations that only establish outbound
      streams.  I2PTunnel's 'client' and 'httpclient' proxies have been
      modified to tell the router that it doesn't need to publish their
      leaseSet (by setting the I2CP config option 'i2cp.dontPublishLeaseSet'
      to 'true').
    * Don't publish the top 10 peer rankings of each router in the netdb, as
      it isn't being watched right now.
2005-09-01 00:26:20 +00:00
jrandom
b43114f61b 2005-08-31 jrandom
* Don't publish leaseSets to the netDb if they will never be looked for -
      namely, if they are for destinations that only establish outbound
      streams.  I2PTunnel's 'client' and 'httpclient' proxies have been
      modified to tell the router that it doesn't need to publish their
      leaseSet (by setting the I2CP config option 'i2cp.dontPublishLeaseSet'
      to 'true').
    * Don't publish the top 10 peer rankings of each router in the netdb, as
      it isn't being watched right now.
2005-09-01 00:20:16 +00:00
jrandom
9bd87ab511 make it work with any host charset or content charset 2005-08-31 09:50:23 +00:00
jrandom
b6ea55f7ef more error handling (thanks frosk) 2005-08-30 02:39:37 +00:00
jrandom
5f18cec97d 2005-08-29 jrandom
* Added the new test Floodfill netDb
2005-08-30 02:04:17 +00:00
jrandom
3ba921ec0e 2005-08-29 jrandom
* Added the new test Floodfill netDb
2005-08-30 01:59:11 +00:00
jrandom
e313da254c 2005-08-27 jrandom
* Minor logging and optimization tweaks in the router and SDK
    * Use ISO-8859-1 in the XML files (thanks redzara!)
    * The consolePassword config property can now be used to bypass the router
      console's nonce checking, allowing CLI restarts
2005-08-27 22:46:22 +00:00
jrandom
8660cf0d74 2005-08-27 jrandom
* Minor logging and optimization tweaks in the router and SDK
    * Use ISO-8859-1 in the XML files (thanks redzara!)
    * The consolePassword config property can now be used to bypass the router
      console's nonce checking, allowing CLI restarts
2005-08-27 22:15:35 +00:00
jrandom
e0bfdff152 TZ asap 2005-08-25 21:08:13 +00:00
jrandom
c27aed3603 fix up the entryId calc 2005-08-25 21:07:18 +00:00
jrandom
cdc6002f0e no message 2005-08-25 21:01:15 +00:00
jrandom
4cf3d9c1a2 HTTP file upload (rfc 1867) helper 2005-08-25 21:00:09 +00:00
jrandom
0473e08e21 remote w0rks 2005-08-25 20:59:46 +00:00
jrandom
346faa3de2 2005-08-24 jrandom
* Catch errors with corrupt tunnel messages more gracefully (no need to
      kill the thread and cause an OOM...)
    * Don't skip shitlisted peers for netDb store messages, as they aren't
      necessarily shitlisted by other people (though they probably are).
    * Adjust the netDb store per-peer timeout based on each particular peer's
      profile (timeout = 4x their average netDb store response time)
    * Don't republish leaseSets to *failed* peers - send them to peers who
      replied but just didn't know the value.
    * Set a 5 second timeout on the I2PTunnelHTTPServer reading the client's
      HTTP headers, rather than blocking indefinitely.  HTTP headers should be
      sent entirely within the first streaming packet anyway, so this won't be
      a problem.
    * Don't use the I2PTunnel*Server handler thread pool by default, as it may
      prevent any clients from accessing the server if the handlers get
      blocked by the streaming lib or other issues.
    * Don't overwrite a known status (OK/ERR-Reject/ERR-SymmetricNAT) with
      Unknown.
2005-08-24 22:55:25 +00:00
jrandom
5ec6dca64d 2005-08-23 jrandom
* Removed the concept of "no bandwidth limit" - if none is specified, its
      16KBps in/out.
    * Include ack packets in the per-peer cwin throttle (they were part of the
      bandwidth limit though).
    * Tweak the SSU cwin operation to get more accurrate estimates under
      congestions.
    * SSU improvements to resend more efficiently.
    * Added a basic scheduler to eepget to fetch multiple files sequentially.
2005-08-23 22:43:51 +00:00
jrandom
1a6b49cfb8 2005-08-23 jrandom
* Removed the concept of "no bandwidth limit" - if none is specified, its
      16KBps in/out.
    * Include ack packets in the per-peer cwin throttle (they were part of the
      bandwidth limit though).
    * Tweak the SSU cwin operation to get more accurrate estimates under
      congestions.
    * SSU improvements to resend more efficiently.
    * Added a basic scheduler to eepget to fetch multiple files sequentially.
2005-08-23 21:25:49 +00:00
cervantes
c7b75df390 Added announcement about the new Irc2P server at irc.freshcoffee.i2p 2005-08-22 13:03:11 +00:00
jrandom
f97c09291b 0.6.0.3 2005-08-21 19:21:50 +00:00
346 changed files with 21682 additions and 2394 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,49 @@ 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 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 +64,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
public void write(byte buf[], int off, int len) throws IOException {
if (_headerWritten) {
out.write(buf, off, len);
_dataWritten += len;
return;
}
@@ -62,8 +76,11 @@ 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;
}
return;
}
}
@@ -128,7 +145,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 +156,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 +174,85 @@ 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 _in;
private OutputStream _out;
public Pusher(InputStream in, OutputStream out) {
_in = in;
_out = out;
}
public void run() {
OutputStream to = null;
InternalGZIPInputStream in = null;
long start = System.currentTimeMillis();
long written = 0;
try {
in = new InternalGZIPInputStream(_in);
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.getTotalRead() + "/" + in.getTotalExpanded(), ioe);
} finally {
if (_out != null) try { _out.close(); } catch (IOException ioe) {}
}
long end = System.currentTimeMillis();
long compressed = in.getTotalRead();
long expanded = in.getTotalExpanded();
double ratio = 0;
if (expanded > 0)
ratio = compressed/expanded;
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
_context.statManager().addRateData("i2ptunnel.httpCompressed", compressed, end-start);
_context.statManager().addRateData("i2ptunnel.httpExpanded", 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 static void main(String args[]) {

View File

@@ -109,8 +109,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
_tunnelId = ++__tunnelId;
_log = _context.logManager().getLog(I2PTunnel.class);
_event = new EventDispatcherImpl();
_clientOptions = new Properties();
_clientOptions.putAll(System.getProperties());
Properties p = new Properties();
p.putAll(System.getProperties());
_clientOptions = p;
_sessions = new ArrayList(1);
addConnectionEventListener(lsnr);

View File

@@ -101,6 +101,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
this.l = l;
this.handlerName = handlerName + _clientId;
// no need to load the netDb with leaseSets for destinations that will never
// be looked up
tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true");
while (sockMgr == null) {
synchronized (sockLock) {
if (ownDest) {

View File

@@ -369,6 +369,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
if (line.length() == 0) {
String ok = getTunnel().getContext().getProperty("i2ptunnel.gzip");
boolean gzip = false;
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,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 {
@@ -60,14 +61,45 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
String modifiedHeader = getModifiedHeader(socket);
// give them 5 seconds to send in the HTTP request
socket.setReadTimeout(5*1000);
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");
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;
}
String enc = headers.getProperty("Accept-encoding");
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();
@@ -86,17 +118,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[4096];
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');
@@ -131,6 +265,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

@@ -11,6 +11,7 @@ import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.ConnectException;
import java.util.Iterator;
import java.util.Properties;
@@ -39,6 +40,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
protected InetAddress remoteHost;
protected int remotePort;
private boolean _usePool;
private Logging l;
@@ -46,15 +48,27 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** default timeout to 3 minutes - override if desired */
protected long readTimeout = DEFAULT_READ_TIMEOUT;
private static final boolean DEFAULT_USE_POOL = false;
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privData, notifyThis, tunnel);
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
init(host, port, bais, privData, l);
}
public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
try {
init(host, port, new FileInputStream(privkey), privkeyname, l);
} catch (IOException ioe) {
@@ -65,6 +79,11 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
init(host, port, privData, privkeyname, l);
}
@@ -178,22 +197,34 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
public void run() {
if (shouldUsePool()) {
I2PServerSocket i2pss = sockMgr.getServerSocket();
int handlers = getHandlerCount();
for (int i = 0; i < handlers; i++) {
I2PThread handler = new I2PThread(new Handler(i2pss), "Handle Server " + i);
handler.start();
}
/*
} else {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
try {
final I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
new I2PThread(new Runnable() { public void run() { blockingHandle(i2ps); } }).start();
} catch (I2PException ipe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting - KILLING THE TUNNEL SERVER", ipe);
return;
} catch (ConnectException ce) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting", ce);
// not killing the server..
}
}
*/
}
}
public boolean shouldUsePool() { return _usePool; }
/**
* minor thread pool to pull off the accept() concurrently. there are still lots

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

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?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">
@@ -14,4 +14,4 @@
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
</web-app>

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

@@ -20,6 +20,7 @@ public class FormHandler {
protected Log _log;
private String _nonce;
protected String _action;
protected String _passphrase;
private List _errors;
private List _notices;
private boolean _processed;
@@ -32,6 +33,7 @@ public class FormHandler {
_processed = false;
_valid = true;
_nonce = null;
_passphrase = null;
}
/**
@@ -51,6 +53,7 @@ public class FormHandler {
public void setNonce(String val) { _nonce = val; }
public void setAction(String val) { _action = val; }
public void setPassphrase(String val) { _passphrase = val; }
/**
* Override this to perform the final processing (in turn, adding formNotice
@@ -119,8 +122,14 @@ public class FormHandler {
String noncePrev = System.getProperty(getClass().getName() + ".noncePrev");
if ( ( (nonce == null) || (!_nonce.equals(nonce)) ) &&
( (noncePrev == null) || (!_nonce.equals(noncePrev)) ) ) {
addFormError("Invalid nonce, are you being spoofed?");
_valid = false;
String expected = _context.getProperty("consolePassword");
if ( (expected != null) && (expected.trim().length() > 0) && (expected.equals(_passphrase)) ) {
// ok
} else {
addFormError("Invalid nonce, are you being spoofed?");
_valid = false;
}
}
}

View File

@@ -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

@@ -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;
@@ -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,34 @@
<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 through NAT hole punching? </b>
<input type="checkbox" name="requireIntroductions" value="true" <jsp:getProperty name="nethelper" property="requireIntroductionsChecked" /> /><br />
<p>If you can't poke a hole in your NAT or firewall to allow unsolicited UDP packets to reach the
router, as detected with the <i>Status: ERR-Reject</i>, then you will need SSU introductions.
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

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?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">
@@ -14,4 +14,4 @@
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
</web-app>

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="UTF-8"?>
<!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="UTF-8"?>
<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.22 $
*/
%>
<%@ 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

@@ -14,7 +14,7 @@
srcdir="./src"
debug="true" deprecation="on" source="1.3" target="1.3"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar" />
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar" />
</target>
<target name="jar" depends="builddep, compile">
<jar destfile="./build/syndie.jar" basedir="./build/obj" includes="**/*.class">
@@ -46,6 +46,7 @@
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
<pathelement location="../../jetty/jettylib/commons-el.jar" />
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/ant.jar" />
<pathelement location="build/obj" />
<pathelement location="../../../core/java/build/i2p.jar" />
@@ -67,6 +68,7 @@
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
<pathelement location="../../jetty/jettylib/commons-el.jar" />
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="build/obj" />
<pathelement location="../../../core/java/build/i2p.jar" />
</classpath>

View File

@@ -30,6 +30,7 @@ public class Archive {
private Map _blogInfo;
private ArchiveIndex _index;
private EntryExtractor _extractor;
private String _defaultSelector;
public static final String METADATA_FILE = "meta.snm";
public static final String INDEX_FILE = "archive.txt";
@@ -50,6 +51,8 @@ public class Archive {
_blogInfo = new HashMap();
_index = null;
_extractor = new EntryExtractor(ctx);
_defaultSelector = ctx.getProperty("syndie.defaultSelector");
if (_defaultSelector == null) _defaultSelector = "";
reloadInfo();
}
@@ -63,7 +66,13 @@ public class Archive {
BlogInfo bi = new BlogInfo();
try {
bi.load(new FileInputStream(meta));
info.add(bi);
if (bi.verify(_context)) {
info.add(bi);
} else {
System.err.println("Invalid blog (but we're storing it anyway): " + bi);
new Exception("foo").printStackTrace();
info.add(bi);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
@@ -79,8 +88,17 @@ 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;
synchronized (_blogInfo) {
return (BlogInfo)_blogInfo.get(uri.getKeyHash());
}
@@ -90,14 +108,21 @@ public class Archive {
return (BlogInfo)_blogInfo.get(key);
}
}
public void storeBlogInfo(BlogInfo info) {
public boolean storeBlogInfo(BlogInfo info) {
if (!info.verify(_context)) {
System.err.println("Not storing the invalid blog " + info);
return;
new Exception("foo!").printStackTrace();
return false;
}
boolean isNew = true;
synchronized (_blogInfo) {
_blogInfo.put(info.getKey().calculateHash(), info);
BlogInfo old = (BlogInfo)_blogInfo.get(info.getKey().calculateHash());
if ( (old == null) || (old.getEdition() < info.getEdition()) )
_blogInfo.put(info.getKey().calculateHash(), info);
else
isNew = false;
}
if (!isNew) return true; // valid entry, but not stored, since its old
try {
File blogDir = new File(_rootDir, info.getKey().calculateHash().toBase64());
blogDir.mkdirs();
@@ -106,8 +131,10 @@ public class Archive {
info.write(out);
out.close();
System.out.println("Blog info written to " + blogFile.getPath());
return true;
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
@@ -142,13 +169,17 @@ public class Archive {
for (int j = 0; j < entries.length; j++) {
try {
File entryDir = getEntryDir(entries[j]);
if (!entryDir.exists()) {
EntryContainer entry = null;
if (entryDir.exists())
entry = getCachedEntry(entryDir);
if ( (entry == null) || (!entryDir.exists()) ) {
if (!extractEntry(entries[j], entryDir, info)) {
System.err.println("Entry " + entries[j].getPath() + " is not valid");
new Exception("foo!!").printStackTrace();
continue;
}
entry = getCachedEntry(entryDir);
}
EntryContainer entry = getCachedEntry(entryDir);
String tags[] = entry.getTags();
for (int t = 0; t < tags.length; t++) {
if (!rv.contains(tags[t])) {
@@ -183,7 +214,20 @@ public class Archive {
}
private EntryContainer getCachedEntry(File entryDir) {
return new CachedEntry(entryDir);
try {
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;
}
public EntryContainer getEntry(BlogURI uri) { return getEntry(uri, null); }
@@ -214,13 +258,16 @@ public class Archive {
if (blogKey == null) {
// no key, cache.
File entryDir = getEntryDir(entries[i]);
if (!entryDir.exists()) {
if (entryDir.exists())
entry = getCachedEntry(entryDir);
if ((entry == null) || !entryDir.exists()) {
if (!extractEntry(entries[i], entryDir, info)) {
System.err.println("Entry " + entries[i].getPath() + " is not valid");
new Exception("foo!!!!").printStackTrace();
continue;
}
entry = getCachedEntry(entryDir);
}
entry = getCachedEntry(entryDir);
} else {
// we have an explicit key - no caching
entry = new EntryContainer();
@@ -228,6 +275,7 @@ public class Archive {
boolean ok = entry.verifySignature(_context, info);
if (!ok) {
System.err.println("Keyed entry " + entries[i].getPath() + " is not valid");
new Exception("foo!!!!!!").printStackTrace();
continue;
}
@@ -262,7 +310,16 @@ public class Archive {
}
public boolean storeEntry(EntryContainer container) {
if (container == null) return false;
BlogURI uri = container.getURI();
if (uri == null) return false;
File blogDir = new File(_rootDir, uri.getKeyHash().toBase64());
blogDir.mkdirs();
File entryFile = new File(blogDir, getEntryFilename(uri.getEntryId()));
if (entryFile.exists()) return true;
BlogInfo info = getBlogInfo(uri);
if (info == null) {
System.out.println("no blog metadata for the uri " + uri);
@@ -274,13 +331,10 @@ public class Archive {
} else {
//System.out.println("Signature is valid: " + container.getSignature() + " for info " + info);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
container.write(baos, true);
File blogDir = new File(_rootDir, uri.getKeyHash().toBase64());
blogDir.mkdirs();
byte data[] = baos.toByteArray();
File entryFile = new File(blogDir, getEntryFilename(uri.getEntryId()));
FileOutputStream out = new FileOutputStream(entryFile);
out.write(data);
out.close();
@@ -294,7 +348,7 @@ public class Archive {
public static String getEntryFilename(long entryId) { return entryId + ".snd"; }
private static SimpleDateFormat _dateFmt = new SimpleDateFormat("yyyyMMdd");
private static SimpleDateFormat _dateFmt = new SimpleDateFormat("yyyyMMdd", Locale.UK);
public static String getIndexName(long entryId, int numBytes) {
try {
synchronized (_dateFmt) {
@@ -364,8 +418,8 @@ public class Archive {
reloadInfo();
_index = ArchiveIndexer.index(_context, this);
try {
PrintWriter out = new PrintWriter(new FileWriter(new File(_rootDir, INDEX_FILE)));
out.println(_index.toString());
FileOutputStream out = new FileOutputStream(new File(_rootDir, INDEX_FILE));
out.write(DataHelper.getUTF8(_index.toString()));
out.flush();
} catch (IOException ioe) {
ioe.printStackTrace();

View File

@@ -6,6 +6,7 @@ import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
/**
* Dig through the archive to build an index
@@ -15,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();
@@ -23,7 +24,7 @@ class ArchiveIndexer {
File headerFile = new File(rootDir, Archive.HEADER_FILE);
if (headerFile.exists()) {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(headerFile)));
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(headerFile), "UTF-8"));
String line = null;
while ( (line = in.readLine()) != null) {
StringTokenizer tok = new StringTokenizer(line, ":");
@@ -53,6 +54,8 @@ class ArchiveIndexer {
long totalSize = 0;
int newBlogs = 0;
SMLParser parser = new SMLParser(ctx);
for (int i = 0; i < blogs.size(); i++) {
BlogInfo cur = (BlogInfo)blogs.get(i);
Hash key = cur.getKey().calculateHash();
@@ -76,6 +79,7 @@ class ArchiveIndexer {
for (int t = 0; t < entryTags.length; t++) {
if (!tags.containsKey(entryTags[t])) {
tags.put(entryTags[t], new TreeMap());
//System.err.println("New tag [" + entryTags[t] + "]");
}
Map entriesByTag = (Map)tags.get(entryTags[t]);
entriesByTag.put(new Long(0-entry.getURI().getEntryId()), entry);
@@ -86,6 +90,16 @@ class ArchiveIndexer {
newEntries++;
newSize += entry.getCompleteSize();
}
HeaderReceiver rec = new HeaderReceiver();
parser.parse(entry.getEntry().getText(), rec);
String reply = rec.getHeader(HTMLRenderer.HEADER_IN_REPLY_TO);
if (reply != null) {
BlogURI parent = new BlogURI(reply.trim());
if ( (parent.getKeyHash() != null) && (parent.getEntryId() >= 0) )
rv.addReply(parent, entry.getURI());
else
System.err.println("Parent of " + entry.getURI() + " is not valid: [" + reply.trim() + "]");
}
}
long lowestEntryId = -1;
@@ -134,4 +148,43 @@ class ArchiveIndexer {
return rv;
}
private static class HeaderReceiver implements SMLParser.EventReceiver {
private Properties _headers;
public HeaderReceiver() { _headers = null; }
public String getHeader(String name) { return (_headers != null ? _headers.getProperty(name) : null); }
public void receiveHeader(String header, String value) {
if (_headers == null) _headers = new Properties();
_headers.setProperty(header, value);
}
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() {}
public void receiveBlog(String name, String blogKeyHash, String blogPath, long blogEntryId, List blogArchiveLocations, String anchorText) {}
public void receiveBold(String text) {}
public void receiveCode(String text, String codeLocationSchema, String codeLocation) {}
public void receiveCut(String summaryText) {}
public void receiveEnd() {}
public void receiveGT() {}
public void receiveH1(String text) {}
public void receiveH2(String text) {}
public void receiveH3(String text) {}
public void receiveH4(String text) {}
public void receiveH5(String text) {}
public void receiveHR() {}
public void receiveHeaderEnd() {}
public void receiveImage(String alternateText, int attachmentId) {}
public void receiveItalic(String text) {}
public void receiveLT() {}
public void receiveLeftBracket() {}
public void receiveLink(String schema, String location, String text) {}
public void receiveNewline() {}
public void receivePlain(String text) {}
public void receivePre(String text) {}
public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {}
public void receiveRightBracket() {}
public void receiveUnderline(String text) {}
}
}

View File

@@ -1,6 +1,7 @@
package net.i2p.syndie;
import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
@@ -18,39 +19,87 @@ public class BlogManager {
private File _archiveDir;
private File _userDir;
private File _cacheDir;
private File _tempDir;
private File _rootDir;
private Archive _archive;
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
String rootDir = I2PAppContext.getGlobalContext().getProperty("syndie.rootDir");
if (rootDir == null)
rootDir = System.getProperty("user.home");
rootDir = rootDir + File.separatorChar + ".syndie";
if (false) {
if (rootDir == null)
rootDir = System.getProperty("user.home");
rootDir = rootDir + File.separatorChar + ".syndie";
} else {
if (rootDir == null)
rootDir = "./syndie";
}
_instance = new BlogManager(I2PAppContext.getGlobalContext(), rootDir);
}
public static BlogManager instance() { return _instance; }
public BlogManager(I2PAppContext ctx, String rootDir) {
_context = ctx;
File root = new File(rootDir);
root.mkdirs();
_blogKeyDir = new File(root, "blogkeys");
_privKeyDir = new File(root, "privkeys");
_rootDir = new File(rootDir);
_rootDir.mkdirs();
readConfig();
_blogKeyDir = new File(_rootDir, "blogkeys");
_privKeyDir = new File(_rootDir, "privkeys");
String archiveDir = _context.getProperty("syndie.archiveDir");
if (archiveDir != null)
_archiveDir = new File(archiveDir);
else
_archiveDir = new File(root, "archive");
_userDir = new File(root, "users");
_cacheDir = new File(root, "cache");
_archiveDir = new File(_rootDir, "archive");
_userDir = new File(_rootDir, "users");
_cacheDir = new File(_rootDir, "cache");
_tempDir = new File(_rootDir, "temp");
_blogKeyDir.mkdirs();
_privKeyDir.mkdirs();
_archiveDir.mkdirs();
_cacheDir.mkdirs();
_userDir.mkdirs();
_tempDir.mkdirs();
_archive = new Archive(ctx, _archiveDir.getAbsolutePath(), _cacheDir.getAbsolutePath());
_archive.regenerateIndex();
}
private File getConfigFile() { return new File(_rootDir, "syndie.config"); }
private void readConfig() {
File config = getConfigFile();
if (config.exists()) {
try {
Properties p = new Properties();
DataHelper.loadProps(p, config);
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());
}
}
public void writeConfig() {
File config = new File(_rootDir, "syndie.config");
FileOutputStream out = null;
try {
out = new FileOutputStream(config);
for (Iterator iter = _context.getPropertyNames().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
if (name.startsWith("syndie."))
out.write(DataHelper.getUTF8(name + '=' + _context.getProperty(name) + '\n'));
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
}
public BlogInfo createBlog(String name, String description, String contactURL, String archives[]) {
return createBlog(name, null, description, contactURL, archives);
}
@@ -93,6 +142,7 @@ public class BlogManager {
}
public Archive getArchive() { return _archive; }
public File getTempDir() { return _tempDir; }
public List listMyBlogs() {
File files[] = _privKeyDir.listFiles();
@@ -135,14 +185,18 @@ public class BlogManager {
}
public String login(User user, String login, String pass) {
File userFile = new File(_userDir, Base64.encode(_context.sha().calculateHash(login.getBytes()).getData()));
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()));
System.out.println("Attempting to login to " + login + " w/ pass = " + pass
+ ": file = " + userFile.getAbsolutePath() + " passHash = "
+ Base64.encode(_context.sha().calculateHash(pass.getBytes()).getData()));
+ Base64.encode(passHash.getData()));
if (userFile.exists()) {
try {
Properties props = new Properties();
BufferedReader in = new BufferedReader(new FileReader(userFile));
FileInputStream fin = new FileInputStream(userFile);
BufferedReader in = new BufferedReader(new InputStreamReader(fin, "UTF-8"));
String line = null;
while ( (line = in.readLine()) != null) {
int split = line.indexOf('=');
@@ -154,66 +208,133 @@ 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(user.getUsername().getBytes()).getData());
String userHash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(user.getUsername())).getData());
File userFile = new File(_userDir, userHash);
FileWriter out = null;
FileOutputStream out = null;
try {
out = new FileWriter(userFile);
out.write("password=" + user.getHashedPassword() + "\n");
out.write("blog=" + user.getBlog().toBase64() + "\n");
out.write("lastid=" + user.getMostRecentEntry() + "\n");
out.write("lastmetaedition=" + user.getLastMetaEntry() + "\n");
out.write("lastlogin=" + user.getLastLogin() + "\n");
out.write("addressbook=" + user.getAddressbookLocation() + "\n");
out.write("showimages=" + user.getShowImages() + "\n");
out.write("showexpanded=" + user.getShowExpanded() + "\n");
StringBuffer buf = new StringBuffer();
buf.append("groups=");
Map groups = user.getBlogGroups();
for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
List selectors = (List)groups.get(name);
buf.append(name).append(':');
for (int i = 0; i < selectors.size(); i++) {
buf.append(selectors.get(i));
if (i + 1 < selectors.size())
buf.append(",");
}
if (iter.hasNext())
buf.append(' ');
}
buf.append('\n');
out.write(buf.toString());
// shitlist=hash,hash,hash
List shitlistedBlogs = user.getShitlistedBlogs();
if (shitlistedBlogs.size() > 0) {
buf.setLength(0);
buf.append("shitlistedblogs=");
for (int i = 0; i < shitlistedBlogs.size(); i++) {
Hash blog = (Hash)shitlistedBlogs.get(i);
buf.append(blog.toBase64());
if (i + 1 < shitlistedBlogs.size())
buf.append(',');
}
buf.append('\n');
out.write(buf.toString());
}
out = new FileOutputStream(userFile);
out.write(DataHelper.getUTF8(user.export()));
user.getPetNameDB().store(user.getAddressbookLocation());
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
@@ -221,31 +342,38 @@ public class BlogManager {
}
}
public String register(User user, String login, String password, String registrationPassword, String blogName, String blogDescription, String contactURL) {
String hashedRegistrationPassword = getRegistrationPassword();
if (hashedRegistrationPassword != null) {
if (!hashedRegistrationPassword.equals(Base64.encode(_context.sha().calculateHash(registrationPassword.getBytes()).getData())))
return "Invalid registration password";
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 "<span class=\"b_regMsgErr\">Invalid registration password</span>";
} catch (UnsupportedEncodingException uee) {
return "<span class=\"b_regMsgErr\">Error registering</span>";
}
}
String userHash = Base64.encode(_context.sha().calculateHash(login.getBytes()).getData());
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(password.getBytes()).getData());
FileWriter out = null;
String hashedPassword = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(password)).getData());
FileOutputStream out = null;
try {
out = new FileWriter(userFile);
out.write("password=" + hashedPassword + "\n");
out.write("blog=" + Base64.encode(info.getKey().calculateHash().getData()) + "\n");
out.write("lastid=-1\n");
out.write("lastmetaedition=0\n");
out.write("addressbook=userhosts-"+userHash + ".txt\n");
out.write("showimages=false\n");
out.write("showexpanded=false\n");
out = new FileOutputStream(userFile);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
bw.write("password=" + hashedPassword + "\n");
bw.write("blog=" + Base64.encode(info.getKey().calculateHash().getData()) + "\n");
bw.write("lastid=-1\n");
bw.write("lastmetaedition=0\n");
bw.write("addressbook=userhosts-"+userHash + ".txt\n");
bw.write("showimages=false\n");
bw.write("showexpanded=false\n");
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) {}
}
@@ -254,6 +382,43 @@ 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>";
Map newNames = new HashMap();
PetNameDB db = user.getPetNameDB();
for (Iterator names = db.getNames().iterator(); names.hasNext(); ) {
PetName pn = db.get((String)names.next());
if (pn == null) continue;
if (pn.getNetwork().equalsIgnoreCase("i2p")) {
try {
Destination d = new Destination(pn.getLocation().trim());
newNames.put(pn.getName(), d);
} catch (DataFormatException dfe) {
// ignore
}
}
}
// horribly inefficient...
for (Iterator iter = newNames.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
Destination existing = _context.namingService().lookup(name);
if (existing == null) {
Destination known = (Destination)newNames.get(name);
try {
FileOutputStream fos = new FileOutputStream("userhosts.txt", true);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
osw.write(name + "=" + known.toBase64() + "\n");
osw.close();
} 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);
@@ -289,7 +454,7 @@ public class BlogManager {
raw.append('\n');
if ( (entryHeaders != null) && (entryHeaders.trim().length() > 0) ) {
System.out.println("Entry headers: " + entryHeaders);
BufferedReader userHeaders = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(entryHeaders.getBytes())));
BufferedReader userHeaders = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(DataHelper.getUTF8(entryHeaders)), "UTF-8"));
String line = null;
while ( (line = userHeaders.readLine()) != null) {
line = line.trim();
@@ -306,7 +471,7 @@ public class BlogManager {
raw.append('\n');
raw.append(sml);
EntryContainer c = new EntryContainer(uri, tagList, raw.toString().getBytes());
EntryContainer c = new EntryContainer(uri, tagList, DataHelper.getUTF8(raw));
if ((fileNames != null) && (fileStreams != null) && (fileNames.size() == fileStreams.size()) ) {
for (int i = 0; i < fileNames.size(); i++) {
String name = (String)fileNames.get(i);
@@ -347,28 +512,58 @@ public class BlogManager {
}
}
public String addAddress(User user, String name, String location, String schema) {
if (!user.getAuthenticated()) return "Not logged in";
/**
* read in the syndie blog metadata file from the stream, verifying it and adding it to
* the archive if necessary
*
*/
public boolean importBlogMetadata(InputStream metadataStream) throws IOException {
try {
BlogInfo info = new BlogInfo();
info.load(metadataStream);
return _archive.storeBlogInfo(info);
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
/**
* read in the syndie entry file from the stream, verifying it and adding it to
* the archive if necessary
*
*/
public boolean importBlogEntry(InputStream entryStream) throws IOException {
try {
EntryContainer c = new EntryContainer();
c.load(entryStream);
return _archive.storeEntry(c);
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
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
FileWriter 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 FileWriter(userHostsFile, true);
out.write(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>";
}
}
@@ -392,7 +587,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) )
@@ -403,30 +598,40 @@ 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 GregorianCalendar _cal = new GregorianCalendar();
private long getDayBegin(long now) {
synchronized (_cal) {
_cal.setTimeInMillis(now);
_cal.set(Calendar.MILLISECOND, 0);
_cal.set(Calendar.SECOND, 0);
_cal.set(Calendar.MINUTE, 0);
_cal.set(Calendar.HOUR, 0);
_cal.set(Calendar.HOUR_OF_DAY, 0);
return _cal.getTimeInMillis();
private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);
private final long getDayBegin(long now) {
synchronized (_dateFormat) {
try {
String str = _dateFormat.format(new Date(now));
return _dateFormat.parse(str).getTime();
} catch (ParseException pe) {
pe.printStackTrace();
// wtf
return -1;
}
}
}
}

View File

@@ -111,12 +111,12 @@ 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 {
File f = File.createTempFile("syndie", ".html");
Writer out = new FileWriter(f);
Writer out = new OutputStreamWriter(new FileOutputStream(f), "UTF-8");
renderer.render(null, mgr.getArchive(), entry, out, summaryOnly, showImages);
out.flush();
out.close();

View File

@@ -21,13 +21,17 @@ class CachedEntry extends EntryContainer {
private Entry _entry;
private Attachment _attachments[];
public CachedEntry(File entryDir) {
public CachedEntry(File entryDir) throws IOException {
_entryDir = entryDir;
importMeta();
_entry = new CachedEntryDetails();
_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,19 +113,17 @@ 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 {
in = new BufferedReader(new FileReader(propsFile));
in = new BufferedReader(new InputStreamReader(new FileInputStream(propsFile), "UTF-8"));
String line = null;
while ( (line = in.readLine()) != null) {
int split = line.indexOf('=');
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) {}
}
@@ -152,7 +160,7 @@ class CachedEntry extends EntryContainer {
in = new FileInputStream(f);
int read = DataHelper.read(in, buf);
if (read != buf.length) throw new IOException("read: " + read + " file size: " + buf.length + " for " + f.getPath());
_text = new String(buf);
_text = DataHelper.getUTF8(buf);
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
@@ -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

@@ -65,36 +65,36 @@ public class EntryExtractor {
}
}
private void extractHeaders(EntryContainer entry, File entryDir) throws IOException {
FileWriter out = null;
FileOutputStream out = null;
try {
out = new FileWriter(new File(entryDir, HEADERS));
out = new FileOutputStream(new File(entryDir, HEADERS));
Map headers = entry.getHeaders();
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
String k = (String)iter.next();
String v = (String)headers.get(k);
out.write(k.trim() + '=' + v.trim() + '\n');
out.write(DataHelper.getUTF8(k.trim() + '=' + v.trim() + '\n'));
}
} finally {
out.close();
}
}
private void extractMeta(EntryContainer entry, File entryDir) throws IOException {
FileWriter out = null;
FileOutputStream out = null;
try {
out = new FileWriter(new File(entryDir, META));
out.write("format=" + entry.getFormat() + '\n');
out.write("size=" + entry.getCompleteSize() + '\n');
out.write("blog=" + entry.getURI().getKeyHash().toBase64() + '\n');
out.write("entry=" + entry.getURI().getEntryId() + '\n');
out = new FileOutputStream(new File(entryDir, META));
out.write(DataHelper.getUTF8("format=" + entry.getFormat() + '\n'));
out.write(DataHelper.getUTF8("size=" + entry.getCompleteSize() + '\n'));
out.write(DataHelper.getUTF8("blog=" + entry.getURI().getKeyHash().toBase64() + '\n'));
out.write(DataHelper.getUTF8("entry=" + entry.getURI().getEntryId() + '\n'));
} finally {
out.close();
}
}
private void extractEntry(EntryContainer entry, File entryDir) throws IOException {
FileWriter out = null;
FileOutputStream out = null;
try {
out = new FileWriter(new File(entryDir, ENTRY));
out.write(entry.getEntry().getText());
out = new FileOutputStream(new File(entryDir, ENTRY));
out.write(DataHelper.getUTF8(entry.getEntry().getText()));
} finally {
out.close();
}
@@ -115,16 +115,16 @@ public class EntryExtractor {
}
}
private void extractAttachmentMetadata(int num, Attachment attachment, File entryDir) throws IOException {
FileWriter out = null;
FileOutputStream out = null;
try {
out = new FileWriter(new File(entryDir, ATTACHMENT_PREFIX + num + ATTACHMENT_META_SUFFIX));
out = new FileOutputStream(new File(entryDir, ATTACHMENT_PREFIX + num + ATTACHMENT_META_SUFFIX));
Map meta = attachment.getMeta();
for (Iterator iter = meta.keySet().iterator(); iter.hasNext(); ) {
String k = (String)iter.next();
String v = (String)meta.get(k);
out.write(k + '=' + v + '\n');
out.write(DataHelper.getUTF8(k + '=' + v + '\n'));
}
out.write(ATTACHMENT_DATA_SIZE + '=' + attachment.getDataLength());
out.write(DataHelper.getUTF8(ATTACHMENT_DATA_SIZE + '=' + attachment.getDataLength()));
} finally {
out.close();
}

View File

@@ -0,0 +1,172 @@
package net.i2p.syndie;
import java.util.*;
import net.i2p.data.DataHelper;
/**
*
*/
public class PetName {
private String _name;
private String _network;
private String _protocol;
private List _groups;
private boolean _isPublic;
private String _location;
public PetName() {
this(null, null, null, null);
}
public PetName(String name, String network, String protocol, String location) {
_name = name;
_network = network;
_protocol = protocol;
_location = location;
_groups = new ArrayList();
_isPublic = false;
}
/**
* @param dbLine name:network:protocol:isPublic:group1,group2,group3:location
*/
public PetName(String dbLine) {
_groups = new ArrayList();
StringTokenizer tok = new StringTokenizer(dbLine, ":\n", true);
int tokens = tok.countTokens();
System.out.println("Tokens: " + tokens);
if (tokens < 7) {
return;
}
String s = tok.nextToken();
if (":".equals(s)) {
_name = null;
} else {
_name = s;
s = tok.nextToken(); // skip past the :
}
s = tok.nextToken();
if (":".equals(s)) {
_network = null;
} else {
_network = s;
s = tok.nextToken(); // skip past the :
}
s = tok.nextToken();
if (":".equals(s)) {
_protocol = null;
} else {
_protocol = s;
s = tok.nextToken(); // skip past the :
}
s = tok.nextToken();
if (":".equals(s)) {
_isPublic = false;
} else {
if ("true".equals(s))
_isPublic = true;
else
_isPublic = false;
s = tok.nextToken(); // skip past the :
}
s = tok.nextToken();
if (":".equals(s)) {
// noop
} else {
StringTokenizer gtok = new StringTokenizer(s, ",");
while (gtok.hasMoreTokens())
_groups.add(gtok.nextToken().trim());
s = tok.nextToken(); // skip past the :
}
while (tok.hasMoreTokens()) {
if (_location == null)
_location = tok.nextToken();
else
_location = _location + tok.nextToken();
}
}
public String getName() { return _name; }
public String getNetwork() { return _network; }
public String getProtocol() { return _protocol; }
public String getLocation() { return _location; }
public boolean getIsPublic() { return _isPublic; }
public int getGroupCount() { return _groups.size(); }
public String getGroup(int i) { return (String)_groups.get(i); }
public void setName(String name) { _name = name; }
public void setNetwork(String network) { _network = network; }
public void setProtocol(String protocol) { _protocol = protocol; }
public void setLocation(String location) { _location = location; }
public void setIsPublic(boolean pub) { _isPublic = pub; }
public void addGroup(String name) {
if ( (name != null) && (name.length() > 0) && (!_groups.contains(name)) )
_groups.add(name);
}
public void removeGroup(String name) { _groups.remove(name); }
public void setGroups(String groups) {
if (groups != null) {
_groups.clear();
StringTokenizer tok = new StringTokenizer(groups, ", \t");
while (tok.hasMoreTokens())
addGroup(tok.nextToken().trim());
} else {
_groups.clear();
}
}
public boolean isMember(String group) {
for (int i = 0; i < getGroupCount(); i++)
if (getGroup(i).equals(group))
return true;
return false;
}
public String toString() {
StringBuffer buf = new StringBuffer(256);
if (_name != null) buf.append(_name.trim());
buf.append(':');
if (_network != null) buf.append(_network.trim());
buf.append(':');
if (_protocol != null) buf.append(_protocol.trim());
buf.append(':').append(_isPublic).append(':');
if (_groups != null) {
for (int i = 0; i < _groups.size(); i++) {
buf.append(((String)_groups.get(i)).trim());
if (i + 1 < _groups.size())
buf.append(',');
}
}
buf.append(':');
if (_location != null) buf.append(_location.trim());
return buf.toString();
}
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof PetName) ) return false;
PetName pn = (PetName)obj;
return DataHelper.eq(_name, pn._name) &&
DataHelper.eq(_location, pn._location) &&
DataHelper.eq(_network, pn._network) &&
DataHelper.eq(_protocol, pn._protocol);
}
public int hashCode() {
int rv = 0;
rv += DataHelper.hashCode(_name);
rv += DataHelper.hashCode(_location);
rv += DataHelper.hashCode(_network);
rv += DataHelper.hashCode(_protocol);
return rv;
}
public static void main(String args[]) {
test("a:b:c:true:e:f");
test("a:::true::d");
test("a:::true::");
test("a:b::true::");
test(":::trye::");
test("a:b:c:true:e:http://foo.bar");
}
private static void test(String line) {
PetName pn = new PetName(line);
String val = pn.toString();
System.out.println("OK? " + val.equals(line) + ": " + line + " [" + val + "]");
}
}

View File

@@ -0,0 +1,74 @@
package net.i2p.syndie;
import java.io.*;
import java.util.*;
/**
*
*/
public class PetNameDB {
/** name (String) to PetName mapping */
private Map _names;
public PetNameDB() {
_names = Collections.synchronizedMap(new HashMap());
}
public PetName get(String name) { return (PetName)_names.get(name); }
public boolean exists(String name) { return _names.containsKey(name); }
public void set(String name, PetName pn) { _names.put(name, pn); }
public void remove(String name) { _names.remove(name); }
public Set getNames() { return new HashSet(_names.keySet()); }
public List getGroups() {
List rv = new ArrayList();
for (Iterator iter = new HashSet(_names.values()).iterator(); iter.hasNext(); ) {
PetName name = (PetName)iter.next();
for (int i = 0; i < name.getGroupCount(); i++)
if (!rv.contains(name.getGroup(i)))
rv.add(name.getGroup(i));
}
return rv;
}
public String getNameByLocation(String location) {
if (location == null) return null;
synchronized (_names) {
for (Iterator iter = _names.values().iterator(); iter.hasNext(); ) {
PetName name = (PetName)iter.next();
if ( (name.getLocation() != null) && (name.getLocation().trim().equals(location.trim())) )
return name.getName();
}
}
return null;
}
public void load(String location) throws IOException {
File f = new File(location);
if (!f.exists()) return;
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
String line = null;
while ( (line = in.readLine()) != null) {
PetName name = new PetName(line);
if (name.getName() != null)
_names.put(name.getName(), name);
}
} finally {
in.close();
}
}
public void store(String location) throws IOException {
Writer out = null;
try {
out = new OutputStreamWriter(new FileOutputStream(location), "UTF-8");
for (Iterator names = getNames().iterator(); names.hasNext(); ) {
PetName name = get((String)names.next());
if (name != null)
out.write(name.toString() + "\n");
}
} finally {
out.close();
}
}
}

View File

@@ -1,5 +1,7 @@
package net.i2p.syndie;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
@@ -24,9 +26,18 @@ public class User {
private String _addressbookLocation;
private boolean _showImagesByDefault;
private boolean _showExpandedByDefault;
private String _defaultSelector;
private long _lastLogin;
private long _lastMetaEntry;
private boolean _allowAccessRemote;
private boolean _authenticated;
private String _eepProxyHost;
private int _eepProxyPort;
private String _webProxyHost;
private int _webProxyPort;
private String _torProxyHost;
private int _torProxyPort;
private PetNameDB _petnames;
public User() {
_context = I2PAppContext.getGlobalContext();
@@ -40,11 +51,20 @@ public class User {
_mostRecentEntry = -1;
_blogGroups = new HashMap();
_shitlistedBlogs = new ArrayList();
_defaultSelector = null;
_addressbookLocation = "userhosts.txt";
_showImagesByDefault = false;
_showImagesByDefault = true;
_showExpandedByDefault = false;
_allowAccessRemote = false;
_eepProxyHost = null;
_webProxyHost = null;
_torProxyHost = null;
_eepProxyPort = -1;
_webProxyPort = -1;
_torProxyPort = -1;
_lastLogin = -1;
_lastMetaEntry = 0;
_petnames = new PetNameDB();
}
public boolean getAuthenticated() { return _authenticated; }
@@ -60,21 +80,35 @@ public class User {
public long getLastLogin() { return _lastLogin; }
public String getHashedPassword() { return _hashedPassword; }
public long getLastMetaEntry() { return _lastMetaEntry; }
public String getDefaultSelector() { return _defaultSelector; }
public void setDefaultSelector(String sel) { _defaultSelector = sel; }
public boolean getAllowAccessRemote() { return _allowAccessRemote; }
public void setAllowAccessRemote(boolean allow) { _allowAccessRemote = true; }
public void setMostRecentEntry(long id) { _mostRecentEntry = id; }
public void setLastMetaEntry(long id) { _lastMetaEntry = id; }
public String getEepProxyHost() { return _eepProxyHost; }
public int getEepProxyPort() { return _eepProxyPort; }
public String getWebProxyHost() { return _webProxyHost; }
public int getWebProxyPort() { return _webProxyPort; }
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();
}
public String login(String login, String pass, Properties props) {
String expectedPass = props.getProperty("password");
String hpass = Base64.encode(_context.sha().calculateHash(pass.getBytes()).getData());
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;
@@ -128,18 +162,82 @@ 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"));
_defaultSelector = props.getProperty("defaultselector");
String allow = props.getProperty("allowaccessremote", "false");
_allowAccessRemote = (allow != null) && (allow.equals("true"));
_eepProxyPort = getInt(props.getProperty("eepproxyport"));
_webProxyPort = getInt(props.getProperty("webproxyport"));
_torProxyPort = getInt(props.getProperty("torproxyport"));
_eepProxyHost = props.getProperty("eepproxyhost");
_webProxyHost = props.getProperty("webproxyhost");
_torProxyHost = props.getProperty("torproxyhost");
_lastLogin = _context.clock().now();
_authenticated = true;
return LOGIN_OK;
}
public static final String LOGIN_OK = "Logged in";
private int getInt(String val) {
if (val == null) return -1;
try { return Integer.parseInt(val); } catch (NumberFormatException nfe) { return -1; }
}
public static final String LOGIN_OK = "<span class=\"b_loginMsgOk\">Logged in</span>";
public String export() {
StringBuffer buf = new StringBuffer(512);
buf.append("password=" + getHashedPassword() + "\n");
buf.append("blog=" + getBlog().toBase64() + "\n");
buf.append("lastid=" + getMostRecentEntry() + "\n");
buf.append("lastmetaedition=" + getLastMetaEntry() + "\n");
buf.append("lastlogin=" + getLastLogin() + "\n");
buf.append("addressbook=" + getAddressbookLocation() + "\n");
buf.append("showimages=" + getShowImages() + "\n");
buf.append("showexpanded=" + getShowExpanded() + "\n");
buf.append("defaultselector=" + getDefaultSelector() + "\n");
buf.append("allowaccessremote=" + _allowAccessRemote + "\n");
buf.append("groups=");
Map groups = getBlogGroups();
for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
List selectors = (List)groups.get(name);
buf.append(name).append(':');
for (int i = 0; i < selectors.size(); i++) {
buf.append(selectors.get(i));
if (i + 1 < selectors.size())
buf.append(",");
}
if (iter.hasNext())
buf.append(' ');
}
buf.append('\n');
// shitlist=hash,hash,hash
List shitlistedBlogs = getShitlistedBlogs();
if (shitlistedBlogs.size() > 0) {
buf.setLength(0);
buf.append("shitlistedblogs=");
for (int i = 0; i < shitlistedBlogs.size(); i++) {
Hash blog = (Hash)shitlistedBlogs.get(i);
buf.append(blog.toBase64());
if (i + 1 < shitlistedBlogs.size())
buf.append(',');
}
buf.append('\n');
}
return buf.toString();
}
}

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;
@@ -26,16 +29,24 @@ public class ArchiveIndex {
protected List _newestBlogs;
/** list of BlogURI objects */
protected List _newestEntries;
/** parent message to a set of replies, ordered with the oldest first */
protected Map _replies;
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();
_headers = new Properties();
_replies = Collections.synchronizedMap(new HashMap());
_generatedOn = -1;
if (shouldLoad)
setIsLocal("true");
@@ -77,6 +88,58 @@ public class ArchiveIndex {
/** get the raw entry size (including attachments) from the given blog/tag pair */
public long getBlogEntrySizeKB(int index, int entryIndex) { return ((EntrySummary)((BlogSummary)_blogs.get(index)).entries.get(entryIndex)).size; }
public boolean getEntryIsKnown(BlogURI uri) { return getEntry(uri) != null; }
public long getBlogEntrySizeKB(BlogURI uri) {
EntrySummary entry = getEntry(uri);
if (entry == null) return -1;
return entry.size;
}
private EntrySummary getEntry(BlogURI uri) {
if ( (uri == null) || (uri.getKeyHash() == null) || (uri.getEntryId() < 0) ) return null;
for (int i = 0; i < _blogs.size(); i++) {
BlogSummary summary = (BlogSummary)_blogs.get(i);
if (summary.blog.equals(uri.getKeyHash())) {
for (int j = 0; j < summary.entries.size(); j++) {
EntrySummary entry = (EntrySummary)summary.entries.get(j);
if (entry.entry.equals(uri))
return entry;
}
}
}
return null;
}
public Set getBlogEntryTags(BlogURI uri) {
Set tags = new HashSet();
if ( (uri == null) || (uri.getKeyHash() == null) || (uri.getEntryId() < 0) ) return tags;
for (int i = 0; i < _blogs.size(); i++) {
BlogSummary summary = (BlogSummary)_blogs.get(i);
if (summary.blog.equals(uri.getKeyHash())) {
for (int j = 0; j < summary.entries.size(); j++) {
EntrySummary entry = (EntrySummary)summary.entries.get(j);
if (entry.entry.equals(uri)) {
tags.add(summary.tag);
break;
}
}
}
}
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(); }
public Hash getNewestBlog(int index) { return (Hash)_newestBlogs.get(index); }
@@ -100,7 +163,13 @@ public class ArchiveIndex {
rv.add(getBlog(i));
return rv;
}
public List getReplies(BlogURI uri) {
Set replies = (Set)_replies.get(uri);
if (replies == null) return Collections.EMPTY_LIST;
synchronized (replies) {
return new ArrayList(replies);
}
}
public void setLocation(String location) {
try {
File l = new File(location);
@@ -143,7 +212,7 @@ public class ArchiveIndex {
_newestBlogs = new ArrayList();
_newestEntries = new ArrayList();
_headers = new Properties();
BufferedReader in = new BufferedReader(new InputStreamReader(index));
BufferedReader in = new BufferedReader(new InputStreamReader(index, "UTF-8"));
String line = null;
line = in.readLine();
if (line == null)
@@ -193,8 +262,8 @@ public class ArchiveIndex {
_newestBlogs = parseNewestBlogs(val);
else if (key.equals("NewestEntries"))
_newestEntries = parseNewestEntries(val);
else
System.err.println("Key: " + key + " val: " + val);
//else
// System.err.println("Key: " + key + " val: " + val);
}
}
@@ -217,14 +286,31 @@ public class ArchiveIndex {
}
if (tag != null) {
if (!tag.equals(summary.tag)) {
System.out.println("Tag [" + summary.tag + "] does not match the requested [" + tag + "]");
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++) {
b.append((int)tag.charAt(j));
b.append('.');
if (summary.tag.length() > j+1)
b.append((int)summary.tag.charAt(j));
else
b.append('_');
b.append(' ');
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("tag.summary: " + b.toString());
}
continue;
}
}
for (int j = 0; j < summary.entries.size(); j++) {
EntrySummary entry = (EntrySummary)summary.entries.get(j);
ordered.put(new Long(0-entry.entry.getEntryId()), entry.entry);
String k = (Long.MAX_VALUE-entry.entry.getEntryId()) + "-" + entry.entry.getKeyHash().toBase64();
ordered.put(k, entry.entry);
//System.err.println("Including match: " + k);
}
}
for (Iterator iter = ordered.values().iterator(); iter.hasNext(); ) {
@@ -264,8 +350,10 @@ public class ArchiveIndex {
if (tok.countTokens() < 4)
return;
tok.nextToken();
Hash keyHash = new Hash(Base64.decode(tok.nextToken()));
long when = getIndexDate(tok.nextToken());
String keyStr = tok.nextToken();
Hash keyHash = new Hash(Base64.decode(keyStr));
String whenStr = tok.nextToken();
long when = getIndexDate(whenStr);
String tag = tok.nextToken();
BlogSummary summary = new BlogSummary();
summary.blog = keyHash;
@@ -281,7 +369,7 @@ public class ArchiveIndex {
_blogs.add(summary);
}
private SimpleDateFormat _dateFmt = new SimpleDateFormat("yyyyMMdd");
private SimpleDateFormat _dateFmt = new SimpleDateFormat("yyyyMMdd", Locale.UK);
private long getIndexDate(String yyyymmdd) {
synchronized (_dateFmt) {
try {
@@ -315,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

@@ -2,6 +2,7 @@ package net.i2p.syndie.data;
import java.io.*;
import java.util.*;
import net.i2p.data.DataHelper;
/**
*
@@ -85,7 +86,7 @@ public class Attachment {
for (int i = 0; i < _keys.size(); i++) {
meta.append(_keys.get(i)).append(':').append(_values.get(i)).append('\n');
}
_rawMetadata = meta.toString().getBytes();
_rawMetadata = DataHelper.getUTF8(meta);
}
private void parseMeta() {
@@ -96,10 +97,10 @@ public class Attachment {
int valBegin = -1;
for (int i = 0; i < _rawMetadata.length; i++) {
if (_rawMetadata[i] == ':') {
key = new String(_rawMetadata, keyBegin, i - keyBegin);
key = DataHelper.getUTF8(_rawMetadata, keyBegin, i - keyBegin);
valBegin = i + 1;
} else if (_rawMetadata[i] == '\n') {
val = new String(_rawMetadata, valBegin, i - valBegin);
val = DataHelper.getUTF8(_rawMetadata, valBegin, i - valBegin);
_keys.add(key);
_values.add(val);
keyBegin = i + 1;

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>
@@ -13,6 +14,7 @@ import net.i2p.I2PAppContext;
* Required keys:
* Owner: base64 of their signing public key
* Signature: base64 of the DSA signature of the rest of the ordered metadata
* Edition: base10 unique identifier for this metadata (higher clobbers lower)
*
* Optional keys:
* Posters: comma delimited list of base64 signing public keys that
@@ -53,18 +55,27 @@ public class BlogInfo {
public static final String SIGNATURE = "Signature";
public static final String NAME = "Name";
public static final String DESCRIPTION = "Description";
public static final String EDITION = "Edition";
public void load(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
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) {
if (log.shouldLog(Log.DEBUG))
log.debug("Read info line [" + line + "]");
line = line.trim();
int len = line.length();
int split = line.indexOf(':');
if ( (len <= 0) || (split <= 0) || (split >= len - 2) )
if ( (len <= 0) || (split <= 0) ) {
continue;
} else if (split >= len - 1) {
names.add(line.substring(0, split).trim());
vals.add("");
continue;
}
String key = line.substring(0, split).trim();
String val = line.substring(split+1).trim();
@@ -76,6 +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] + "]");
}
String keyStr = getProperty(OWNER_KEY);
@@ -102,13 +114,22 @@ public class BlogInfo {
if ( (includeRealSignature) || (!SIGNATURE.equals(_optionNames[i])) )
buf.append(_optionNames[i]).append(':').append(_optionValues[i]).append('\n');
}
out.write(buf.toString().getBytes());
String s = buf.toString();
out.write(s.getBytes("UTF-8"));
}
public String getProperty(String name) {
for (int i = 0; i < _optionNames.length; i++) {
if (_optionNames[i].equals(name))
return _optionValues[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);
//}
return val;
}
}
return null;
}
@@ -133,6 +154,18 @@ public class BlogInfo {
_optionValues = values;
}
public int getEdition() {
String e = getProperty(EDITION);
if (e != null) {
try {
return Integer.parseInt(e);
} catch (NumberFormatException nfe) {
return 0;
}
}
return 0;
}
public String[] getProperties() { return _optionNames; }
public SigningPublicKey[] getPosters() { return _posters; }
@@ -151,7 +184,9 @@ public class BlogInfo {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
write(out, false);
return ctx.dsa().verifySignature(_signature, out.toByteArray(), _key);
out.close();
byte data[] = out.toByteArray();
return ctx.dsa().verifySignature(_signature, data, _key);
} catch (IOException ioe) {
return false;
}
@@ -192,4 +227,54 @@ public class BlogInfo {
}
return buf.toString();
}
private static final String TEST_STRING = "\u20AC\u00DF\u6771\u10400\u00F6";
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
if (true) {
try {
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
SigningPublicKey pub = (SigningPublicKey)keys[0];
SigningPrivateKey priv = (SigningPrivateKey)keys[1];
Properties opts = new Properties();
opts.setProperty("Name", TEST_STRING);
opts.setProperty("Description", TEST_STRING);
opts.setProperty("Edition", "0");
opts.setProperty("ContactURL", TEST_STRING);
String nameOrig = opts.getProperty("Name");
BlogInfo info = new BlogInfo(pub, null, opts);
info.sign(ctx, priv);
boolean ok = info.verify(ctx);
System.err.println("sign&verify: " + ok);
FileOutputStream o = new FileOutputStream("bloginfo-test.dat");
info.write(o, true);
o.close();
FileInputStream i = new FileInputStream("bloginfo-test.dat");
byte buf[] = new byte[4096];
int sz = DataHelper.read(i, buf);
BlogInfo read = new BlogInfo();
read.load(new ByteArrayInputStream(buf, 0, sz));
ok = read.verify(ctx);
System.err.println("write to disk, verify read: " + ok);
System.err.println("Data: " + Base64.encode(buf, 0, sz));
System.err.println("Str : " + new String(buf, 0, sz));
System.err.println("Name ok? " + read.getProperty("Name").equals(TEST_STRING));
System.err.println("Desc ok? " + read.getProperty("Description").equals(TEST_STRING));
System.err.println("Name ok? " + read.getProperty("ContactURL").equals(TEST_STRING));
} catch (Exception e) { e.printStackTrace(); }
} else {
try {
FileInputStream in = new FileInputStream(args[0]);
BlogInfo info = new BlogInfo();
info.load(in);
boolean ok = info.verify(I2PAppContext.getGlobalContext());
System.out.println("OK? " + ok + " :" + info);
} catch (Exception e) { e.printStackTrace(); }
}
}
}

View File

@@ -31,6 +31,19 @@ public class BlogURI {
_entryId = -1;
}
}
} else if (uri.startsWith("entry://")) {
int off = "entry://".length();
_blogHash = new Hash(Base64.decode(uri.substring(off, off+44))); // 44 chars == base64(32 bytes)
int entryStart = uri.indexOf('/', off+1);
if (entryStart < 0) {
_entryId = -1;
} else {
try {
_entryId = Long.parseLong(uri.substring(entryStart+1).trim());
} catch (NumberFormatException nfe) {
_entryId = -1;
}
}
} else {
_blogHash = null;
_entryId = -1;
@@ -61,7 +74,10 @@ public class BlogURI {
DataHelper.eq(_blogHash, ((BlogURI)obj)._blogHash);
}
public int hashCode() {
return (int)_entryId;
int rv = (int)_entryId;
if (_blogHash != null)
rv += _blogHash.hashCode();
return rv;
}
public static void main(String args[]) {
@@ -69,6 +85,8 @@ public class BlogURI {
test("blog://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=");
test("blog://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/");
test("blog://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/123456789");
test("entry://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/");
test("entry://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/123456789");
}
private static void test(String uri) {
BlogURI u = new BlogURI(uri);

View File

@@ -0,0 +1,86 @@
package net.i2p.syndie.data;
import java.io.*;
import java.util.*;
import net.i2p.data.*;
import net.i2p.I2PAppContext;
/**
* Create a new blog metadata & set of entries using some crazy UTF8 encoded chars,
* then make sure they're always valid. These blogs & entries can then be fed into
* jetty/syndie/etc to see how and where they are getting b0rked.
*/
public class EncodingTestGenerator {
public EncodingTestGenerator() {}
public static final String TEST_STRING = "\u20AC\u00DF\u6771\u10400\u00F6";
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
try {
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
SigningPublicKey pub = (SigningPublicKey)keys[0];
SigningPrivateKey priv = (SigningPrivateKey)keys[1];
Properties opts = new Properties();
opts.setProperty("Name", TEST_STRING);
opts.setProperty("Description", TEST_STRING);
opts.setProperty("Edition", "0");
opts.setProperty("ContactURL", TEST_STRING);
String nameOrig = opts.getProperty("Name");
BlogInfo info = new BlogInfo(pub, null, opts);
info.sign(ctx, priv);
boolean ok = info.verify(ctx);
System.err.println("sign&verify: " + ok);
FileOutputStream o = new FileOutputStream("encodedMeta.dat");
info.write(o, true);
o.close();
FileInputStream i = new FileInputStream("encodedMeta.dat");
byte buf[] = new byte[4096];
int sz = DataHelper.read(i, buf);
BlogInfo read = new BlogInfo();
read.load(new ByteArrayInputStream(buf, 0, sz));
ok = read.verify(ctx);
System.err.println("write to disk, verify read: " + ok);
System.err.println("Name ok? " + read.getProperty("Name").equals(TEST_STRING));
System.err.println("Desc ok? " + read.getProperty("Description").equals(TEST_STRING));
System.err.println("Name ok? " + read.getProperty("ContactURL").equals(TEST_STRING));
// ok now lets create some entries
BlogURI uri = new BlogURI(read.getKey().calculateHash(), 0);
String tags[] = new String[4];
for (int j = 0; j < tags.length; j++)
tags[j] = TEST_STRING + "_" + j;
StringBuffer smlOrig = new StringBuffer(512);
smlOrig.append("Subject: ").append(TEST_STRING).append("\n\n");
smlOrig.append("Hi with ").append(TEST_STRING);
EntryContainer container = new EntryContainer(uri, tags, DataHelper.getUTF8(smlOrig));
container.seal(ctx, priv, null);
ok = container.verifySignature(ctx, read);
System.err.println("Sealed and verified entry: " + ok);
FileOutputStream fos = new FileOutputStream("encodedEntry.dat");
container.write(fos, true);
fos.close();
System.out.println("Written to " + new File("encodedEntry.dat").getAbsolutePath());
FileInputStream fis = new FileInputStream("encodedEntry.dat");
EntryContainer read2 = new EntryContainer();
read2.load(fis);
ok = read2.verifySignature(ctx, read);
System.out.println("Read ok? " + ok);
read2.parseRawData(ctx);
String tagsRead[] = read2.getTags();
for (int j = 0; j < tagsRead.length; j++) {
if (!tags[j].equals(tagsRead[j]))
System.err.println("Tag error [" + j + "]: read = [" + tagsRead[j] + "] want [" + tags[j] + "]");
else
System.err.println("Tag ok [" + j + "]");
}
String readText = read2.getEntry().getText();
ok = readText.equals(smlOrig.toString());
System.err.println("SML text ok? " + ok);
} catch (Exception e) { e.printStackTrace(); }
}
}

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>
@@ -58,7 +59,7 @@ public class EntryContainer {
public EntryContainer(BlogURI uri, String tags[], byte smlData[]) {
this();
_entryURI = uri;
_entryData = new Entry(new String(smlData));
_entryData = new Entry(DataHelper.getUTF8(smlData));
setHeader(HEADER_BLOGKEY, Base64.encode(uri.getKeyHash().getData()));
StringBuffer buf = new StringBuffer();
for (int i = 0; tags != null && i < tags.length; i++)
@@ -71,8 +72,35 @@ public class EntryContainer {
public int getFormat() { return _format; }
private String readLine(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
int i = 0;
while (true) {
int c = in.read();
if ( (c == (int)'\n') || (c == (int)'\r') ) {
break;
} else if (c == -1) {
if (i == 0)
return null;
else
break;
} else {
baos.write(c);
}
i++;
}
return DataHelper.getUTF8(baos.toByteArray());
//BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8"), 1);
//String line = r.readLine();
//return line;
}
public void load(InputStream source) throws IOException {
String fmt = DataHelper.readLine(source).trim();
String line = readLine(source);
if (line == null) throw new IOException("No format line in the entry");
//System.err.println("read container format line [" + line + "]");
String fmt = line.trim();
if (FORMAT_ZIP_UNENCRYPTED_STR.equals(fmt)) {
_format = FORMAT_ZIP_UNENCRYPTED;
} else if (FORMAT_ZIP_ENCRYPTED_STR.equals(fmt)) {
@@ -81,35 +109,51 @@ public class EntryContainer {
throw new IOException("Unsupported entry format: " + fmt);
}
String line = null;
while ( (line = DataHelper.readLine(source)) != null) {
while ( (line = readLine(source)) != null) {
//System.err.println("read container header line [" + line + "]");
line = line.trim();
int len = line.length();
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();
String sigStr = DataHelper.readLine(source);
String sigStr = readLine(source);
//System.err.println("read container signature line [" + line + "]");
if ( (sigStr == null) || (sigStr.indexOf("Signature:") == -1) )
throw new IOException("No signature line");
sigStr = sigStr.substring("Signature:".length()+1).trim();
_signature = new Signature(Base64.decode(sigStr));
//System.out.println("Sig: " + _signature.toBase64());
line = DataHelper.readLine(source).trim();
line = readLine(source);
//System.err.println("read container size line [" + line + "]");
if (line == null)
throw new IOException("No size line");
line = line.trim();
int dataSize = -1;
try {
int index = line.indexOf("Size:");
if (index == 0)
dataSize = Integer.parseInt(line.substring("Size:".length()+1).trim());
else
throw new IOException("Invalid size line");
} catch (NumberFormatException nfe) {
throw new IOException("Invalid entry size: " + line);
}
@@ -123,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
@@ -157,7 +203,7 @@ public class EntryContainer {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream out = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry(ZIP_ENTRY);
byte data[] = _entryData.getText().getBytes();
byte data[] = DataHelper.getUTF8(_entryData.getText());
ze.setTime(0);
out.putNextEntry(ze);
out.write(data);
@@ -214,7 +260,7 @@ public class EntryContainer {
String name = entry.getName();
if (ZIP_ENTRY.equals(name)) {
_entryData = new Entry(new String(entryData));
_entryData = new Entry(DataHelper.getUTF8(entryData));
} else if (name.startsWith(ZIP_ATTACHMENT_PREFIX)) {
attachments.put(name, (Object)entryData);
} else if (name.startsWith(ZIP_ATTACHMENT_META_PREFIX)) {
@@ -229,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) ) {
@@ -303,8 +353,9 @@ public class EntryContainer {
String keyHash = getHeader(HEADER_BLOGKEY);
String idVal = getHeader(HEADER_ENTRYID);
if (keyHash == null)
if (keyHash == null) {
throw new IOException("Missing " + HEADER_BLOGKEY + " header");
}
long entryId = -1;
if ( (idVal != null) && (idVal.length() > 0) ) {
@@ -377,7 +428,7 @@ public class EntryContainer {
String str = buf.toString();
//System.out.println("Writing raw: \n[" + str + "] / " + I2PAppContext.getGlobalContext().sha().calculateHash(str.getBytes()) + ", raw data: " + I2PAppContext.getGlobalContext().sha().calculateHash(_rawData).toBase64() + "\n");
out.write(str.getBytes());
out.write(DataHelper.getUTF8(str));
out.write(_rawData);
}

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);
@@ -67,4 +71,36 @@ public class LocalArchiveIndex extends ArchiveIndex {
if (!_newestEntries.contains(entry))
_newestEntries.add(entry);
}
public void addReply(BlogURI parent, BlogURI reply) {
Set replies = (Set)_replies.get(parent);
if (replies == null) {
replies = Collections.synchronizedSet(new TreeSet(BlogURIComparator.HIGHEST_ID_FIRST));
_replies.put(parent, replies);
}
replies.add(reply);
//System.err.println("Adding reply to " + parent + " from child " + reply + " (# replies: " + replies.size() + ")");
}
private static class BlogURIComparator implements Comparator {
public static final BlogURIComparator HIGHEST_ID_FIRST = new BlogURIComparator(true);
public static final BlogURIComparator HIGHEST_ID_LAST = new BlogURIComparator(false);
private boolean _highestFirst;
public BlogURIComparator(boolean highestFirst) {
_highestFirst = highestFirst;
}
public int compare(Object lhs, Object rhs) {
if ( (lhs == null) || !(lhs instanceof BlogURI) ) return 1;
if ( (rhs == null) || !(rhs instanceof BlogURI) ) return -1;
BlogURI l = (BlogURI)lhs;
BlogURI r = (BlogURI)rhs;
if (l.getEntryId() > r.getEntryId())
return (_highestFirst ? 1 : -1);
else if (l.getEntryId() < r.getEntryId())
return (_highestFirst ? -1 : 1);
else
return DataHelper.compareTo(l.getKeyHash().getData(), r.getKeyHash().getData());
}
}
}

View File

@@ -0,0 +1,81 @@
package net.i2p.syndie.data;
import java.io.*;
import java.text.*;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.syndie.Archive;
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(I2PAppContext.getGlobalContext(), false); }
private static ArchiveIndex index() { return BlogManager.instance().getArchive().getIndex(); }
public String getVersion() { return index().getVersion(); }
public Properties getHeaders() { return index().getHeaders(); }
public int getAllBlogs() { return index().getAllBlogs(); }
public int getNewBlogs() { return index().getNewBlogs(); }
public int getAllEntries() { return index().getAllEntries(); }
public int getNewEntries() { return index().getNewEntries(); }
public long getTotalSize() { return index().getTotalSize(); }
public long getNewSize() { return index().getNewSize(); }
public long getGeneratedOn() { return index().getGeneratedOn(); }
public String getNewSizeStr() { return index().getNewSizeStr(); }
public String getTotalSizeStr() { return index().getTotalSizeStr(); }
/** how many blogs/tags are indexed */
public int getIndexBlogs() { return index().getIndexBlogs(); }
/** get the blog used for the given blog/tag pair */
public Hash getBlog(int index) { return index().getBlog(index); }
/** get the tag used for the given blog/tag pair */
public String getBlogTag(int index) { return index().getBlogTag(index); }
/** get the highest entry ID for the given blog/tag pair */
public long getBlogLastUpdated(int index) { return index().getBlogLastUpdated(index); }
/** get the entry count for the given blog/tag pair */
public int getBlogEntryCount(int index) { return index().getBlogEntryCount(index); }
/** get the entry from the given blog/tag pair */
public BlogURI getBlogEntry(int index, int entryIndex) { return index().getBlogEntry(index, entryIndex); }
/** get the raw entry size (including attachments) from the given blog/tag pair */
public long getBlogEntrySizeKB(int index, int entryIndex) { return index().getBlogEntrySizeKB(index, entryIndex); }
public boolean getEntryIsKnown(BlogURI uri) { return index().getEntryIsKnown(uri); }
public long getBlogEntrySizeKB(BlogURI uri) { return index().getBlogEntrySizeKB(uri); }
public Set getBlogEntryTags(BlogURI uri) { return index().getBlogEntryTags(uri); }
/** how many 'new' blogs are listed */
public int getNewestBlogCount() { return index().getNewestBlogCount(); }
public Hash getNewestBlog(int index) { return index().getNewestBlog(index); }
/** how many 'new' entries are listed */
public int getNewestBlogEntryCount() { return index().getNewestBlogEntryCount(); }
public BlogURI getNewestBlogEntry(int index) { return index().getNewestBlogEntry(index); }
/** list of locally known tags (String) under the given blog */
public List getBlogTags(Hash blog) { return index().getBlogTags(blog); }
/** list of unique blogs locally known (set of Hash) */
public Set getUniqueBlogs() { return index().getUniqueBlogs(); }
public void setLocation(String location) { return; }
public void setIsLocal(String val) { return; }
public void load(File location) throws IOException { return; }
/** load up the index from an archive.txt */
public void load(InputStream index) throws IOException { return; }
/**
* Dig through the index for BlogURIs matching the given criteria, ordering the results by
* their own entryIds.
*
* @param out where to store the matches
* @param blog if set, what blog key must the entries be under
* @param tag if set, what tag must the entry be in
*
*/
public void selectMatchesOrderByEntryId(List out, Hash blog, String tag) {
index().selectMatchesOrderByEntryId(out, blog, tag);
}
/** export the index into an archive.txt */
public String toString() { return index().toString(); }
}

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

@@ -0,0 +1,147 @@
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.*;
import net.i2p.syndie.web.*;
/**
*
*/
public class HTMLPreviewRenderer extends HTMLRenderer {
private List _filenames;
private List _fileTypes;
private List _files;
public HTMLPreviewRenderer(I2PAppContext ctx, List filenames, List fileTypes, List files) {
super(ctx);
_filenames = filenames;
_fileTypes = fileTypes;
_files = files;
}
protected String getAttachmentURLBase() { return "viewtempattachment.jsp"; }
protected String getAttachmentURL(int id) {
return getAttachmentURLBase() + "?" +
ArchiveViewerBean.PARAM_ATTACHMENT + "=" + id;
}
public void receiveAttachment(int id, String anchorText) {
if (!continueBody()) { return; }
if ( (id < 0) || (_files == null) || (id >= _files.size()) ) {
_bodyBuffer.append(sanitizeString(anchorText));
} else {
File f = (File)_files.get(id);
String name = (String)_filenames.get(id);
String type = (String)_fileTypes.get(id);
_bodyBuffer.append("<a ").append(getClass("attachmentView")).append(" href=\"").append(getAttachmentURL(id)).append("\">");
_bodyBuffer.append(sanitizeString(anchorText)).append("</a>");
_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 ").append(getClass("summDetail")).append(" >\n");
_postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summDetail")).append("> \n");
if (_files.size() > 0) {
_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);
String name = (String)_filenames.get(i);
String type = (String)_fileTypes.get(i);
_postBodyBuffer.append(sanitizeString(name));
_postBodyBuffer.append(" (").append(f.length()/1024).append("KB");
_postBodyBuffer.append(", type ").append(sanitizeString(type)).append(")</option>\n");
}
_postBodyBuffer.append("</select>\n");
_postBodyBuffer.append("<input ").append(getClass("summDetailAttachmentDl")).append(" type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
}
if (_blogs.size() > 0) {
_postBodyBuffer.append(getSpan("summDetailBlog")).append("Blog references:</span> ");
for (int i = 0; i < _blogs.size(); i++) {
Blog b = (Blog)_blogs.get(i);
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(getSpan("summDetailExternal")).append("External links:</span> ");
for (int i = 0; i < _links.size(); i++) {
Link l = (Link)_links.get(i);
_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(getSpan("summDetailExternalNet")).append(" (").append(sanitizeString(l.schema)).append(")</span></a> ");
}
_postBodyBuffer.append("<br />\n");
}
if (_addresses.size() > 0) {
_postBodyBuffer.append(getSpan("summDetailAddr")).append("Addresses:</span>");
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) {
_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(getSpan("summDetailArchive")).append("Archives:</span>");
for (int i = 0; i < _archives.size(); i++) {
ArchiveRef a = (ArchiveRef)_archives.get(i);
_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(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");
}
_postBodyBuffer.append("</td>\n</form>\n</tr>\n");
_postBodyBuffer.append("</table>\n");
}
}

File diff suppressed because it is too large Load Diff

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();
@@ -196,6 +203,7 @@ public class SMLParser {
private static final String T_HR = "hr";
private static final String T_PRE = "pre";
private static final String T_ATTACHMENT = "attachment";
private static final String T_ARCHIVE = "archive";
private static final String P_ATTACHMENT = "attachment";
private static final String P_WHO_QUOTED = "author";
@@ -210,7 +218,13 @@ 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";
private static final String P_ARCHIVE_LOCATION_SCHEMA = "schema";
private static final String P_ARCHIVE_LOCATION = "location";
private static final String P_ARCHIVE_POSTING_KEY = "postingkey";
private void parseTag(String tagName, Map attr, String body, EventReceiver receiver) {
tagName = tagName.toLowerCase();
@@ -241,10 +255,14 @@ public class SMLParser {
}
receiver.receiveBlog(getString(P_BLOG_NAME, attr), getString(P_BLOG_HASH, attr), getString(P_BLOG_TAG, attr),
getLong(P_BLOG_ENTRY, attr), locations, body);
} else if (T_ARCHIVE.equals(tagName)) {
receiver.receiveArchive(getString(P_ARCHIVE_NAME, attr), getString(P_ARCHIVE_DESCRIPTION, attr),
getString(P_ARCHIVE_LOCATION_SCHEMA, attr), getString(P_ARCHIVE_LOCATION, attr),
getString(P_ARCHIVE_POSTING_KEY, attr), body);
} 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)) {
@@ -262,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 + "]");
}
}
@@ -371,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);
@@ -426,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

@@ -0,0 +1,186 @@
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.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
/**
*
*/
public class ArchiveServlet extends HttpServlet {
public void doGet(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")) {
ExportServlet.export(req, resp);
} else {
String blog = getBlog(path);
if (path.endsWith(Archive.METADATA_FILE)) {
renderMetadata(blog, resp);
} else if (path.endsWith(".snd")) {
renderEntry(blog, getEntry(path), resp);
} else {
renderBlogIndex(blog, resp);
}
}
}
private String getBlog(String path) {
//System.err.println("Blog: [" + path + "]");
int start = 0;
int end = -1;
int len = path.length();
for (int i = 0; i < len; i++) {
if (path.charAt(i) != '/') {
start = i;
break;
}
}
for (int j = start + 1; j < len; j++) {
if (path.charAt(j) == '/') {
end = j;
break;
}
}
if (end < 0) end = len;
String rv = path.substring(start, end);
//System.err.println("Blog: [" + path + "] rv: [" + rv + "]");
return rv;
}
private long getEntry(String path) {
int start = path.lastIndexOf('/');
if (start < 0) return -1;
if (!(path.endsWith(".snd"))) return -1;
String rv = path.substring(start+1, path.length()-".snd".length());
//System.err.println("Entry: [" + path + "] rv: [" + rv + "]");
try {
return Long.parseLong(rv);
} catch (NumberFormatException nfe) {
return -1;
}
}
private void renderRootIndex(HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//resp.setCharacterEncoding("UTF-8");
OutputStream out = resp.getOutputStream();
out.write(DataHelper.getUTF8("<a href=\"archive.txt\">archive.txt</a><br />\n"));
ArchiveIndex index = BlogManager.instance().getArchive().getIndex();
Set blogs = index.getUniqueBlogs();
for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
String s = blog.toBase64();
out.write(DataHelper.getUTF8("<a href=\"" + s + "/\">" + s + "</a><br />\n"));
}
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");
resp.setHeader(HEADER_EXPORT_CAPABLE, "true");
OutputStream out = resp.getOutputStream();
ArchiveIndex index = BlogManager.instance().getArchive().getIndex();
out.write(DataHelper.getUTF8(index.toString()));
out.close();
}
private void renderMetadata(String blog, HttpServletResponse resp) throws ServletException, IOException {
byte b[] = Base64.decode(blog);
if ( (b == null) || (b.length != Hash.HASH_LENGTH) ) {
resp.sendError(404, "Invalid blog requested");
return;
}
Hash h = new Hash(b);
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(h);
if (info == null) {
resp.sendError(404, "Blog does not exist");
return;
}
resp.setContentType("application/x-syndie-meta");
OutputStream out = resp.getOutputStream();
info.write(out);
out.close();
}
private void renderBlogIndex(String blog, HttpServletResponse resp) throws ServletException, IOException {
byte b[] = Base64.decode(blog);
if ( (b == null) || (b.length != Hash.HASH_LENGTH) ) {
resp.sendError(404, "Invalid blog requested");
return;
}
Hash h = new Hash(b);
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(h);
if (info == null) {
resp.sendError(404, "Blog does not exist");
return;
}
resp.setContentType("text/html;charset=utf-8");
//resp.setCharacterEncoding("UTF-8");
OutputStream out = resp.getOutputStream();
out.write(DataHelper.getUTF8("<a href=\"..\">..</a><br />\n"));
out.write(DataHelper.getUTF8("<a href=\"" + Archive.METADATA_FILE + "\">" + Archive.METADATA_FILE + "</a><br />\n"));
List entries = new ArrayList(64);
BlogManager.instance().getArchive().getIndex().selectMatchesOrderByEntryId(entries, h, null);
for (int i = 0; i < entries.size(); i++) {
BlogURI entry = (BlogURI)entries.get(i);
out.write(DataHelper.getUTF8("<a href=\"" + entry.getEntryId() + ".snd\">" + entry.getEntryId() + ".snd</a><br />\n"));
}
out.close();
}
private void renderEntry(String blog, long entryId, HttpServletResponse resp) throws ServletException, IOException {
byte b[] = Base64.decode(blog);
if ( (b == null) || (b.length != Hash.HASH_LENGTH) ) {
resp.sendError(404, "Invalid blog requested");
return;
}
Hash h = new Hash(b);
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(h);
if (info == null) {
resp.sendError(404, "Blog does not exist");
return;
}
File root = BlogManager.instance().getArchive().getArchiveDir();
File blogDir = new File(root, blog);
if (!blogDir.exists()) {
resp.sendError(404, "Blog does not exist");
return;
}
File entry = new File(blogDir, entryId + ".snd");
if (!entry.exists()) {
resp.sendError(404, "Entry does not exist");
return;
}
resp.setContentType("application/x-syndie-post");
dump(entry, resp);
}
private void dump(File source, HttpServletResponse resp) throws ServletException, IOException {
FileInputStream in = new FileInputStream(source);
OutputStream out = resp.getOutputStream();
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
out.close();
in.close();
}
}

View File

@@ -20,25 +20,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");
private 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";
@@ -79,70 +60,157 @@ public class ArchiveViewerBean {
public static final String SEL_BLOGTAG = "blogtag://";
public static final String SEL_ENTRY = "entry://";
public static final String SEL_GROUP = "group://";
/** submit field for the selector form */
public static final String PARAM_SELECTOR_ACTION = "action";
public static final String SEL_ACTION_SET_AS_DEFAULT = "Set as default";
public static void renderBlogSelector(User user, Map parameters, Writer out) throws IOException {
out.write("<select name=\"");
String sel = getString(parameters, PARAM_SELECTOR);
String action = getString(parameters, PARAM_SELECTOR_ACTION);
if ( (sel != null) && (action != null) && (SEL_ACTION_SET_AS_DEFAULT.equals(action)) ) {
user.setDefaultSelector(HTMLRenderer.sanitizeString(sel, false));
BlogManager.instance().saveUser(user);
}
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("<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();
out.write("<option value=\"group://" + Base64.encode(name.getBytes()) + "\">" +
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");
}
}
Archive archive = BlogManager.instance().getArchive();
ArchiveIndex index = archive.getIndex();
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();
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://");
out.write(blog);
out.write("\">");
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
name = Base64.encode(cur.getData());
out.write(name);
out.write("- all posts</option>\n");
out.write("</option>\n");
}
////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 = 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);
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);
if (false) {
StringBuffer b = new StringBuffer(tag.length()*2);
for (int k = 0; k < tag.length(); k++) {
b.append((int)tag.charAt(k));
b.append(' ');
}
System.out.println("tag in select: " + tag + ": " + b.toString());
}
if (!allTags.contains(tag))
allTags.add(tag);
out.write("<option value=\"blogtag://");
out.write(blog);
out.write("/");
out.write(Base64.encode(tag));
byte utf8tag[] = DataHelper.getUTF8(tag);
String encoded = Base64.encode(utf8tag);
if (false) {
byte utf8dec[] = Base64.decode(encoded);
String travel = DataHelper.getUTF8(utf8dec);
StringBuffer b = new StringBuffer();
for (int k = 0; k < travel.length(); k++) {
b.append((int)travel.charAt(k));
b.append(' ');
}
b.append(" encoded into: ");
for (int k = 0; k < encoded.length(); k++) {
b.append((int)encoded.charAt(k));
b.append(' ');
}
System.out.println("UTF8(unbase64(base64(UTF8(tag)))) == tag: " + b.toString());
}
out.write(encoded);
out.write("\">");
out.write(name);
out.write("- posts with the tag &quot;");
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://");
out.write(Base64.encode(tag));
out.write(Base64.encode(DataHelper.getUTF8(tag)));
out.write("\">Posts in any blog with the tag &quot;");
out.write(tag);
out.write("&quot;</option>\n");
}
*/
out.write("</select>");
int numPerPage = getInt(parameters, PARAM_NUM_PER_PAGE, 5);
@@ -157,23 +225,42 @@ public class ArchiveViewerBean {
}
public static void renderBlogs(User user, Map parameters, Writer out) throws IOException {
private static String getDefaultSelector(User user, Map parameters) {
if ( (user == null) || (user.getDefaultSelector() == null) )
return BlogManager.instance().getArchive().getDefaultSelector();
else
return user.getDefaultSelector();
}
public static void renderBlogs(User user, Map parameters, Writer out, String afterPagination) throws IOException {
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 = new String(Base64.decode(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);
} catch (NumberFormatException nfe) {}
}
String group = getString(parameters, PARAM_GROUP);
if (group != null) group = new String(Base64.decode(group));
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) {
Selector s = new Selector(sel);
blog = s.blog;
@@ -188,7 +275,7 @@ public class ArchiveViewerBean {
boolean showImages = getBool(parameters, PARAM_SHOW_IMAGES, (user != null ? user.getShowImages() : false));
boolean regenerateIndex = getBool(parameters, PARAM_REGENERATE_INDEX, false);
try {
renderBlogs(user, blog, tag, entryId, group, numPerPage, pageNum, expandEntries, showImages, regenerateIndex, out);
renderBlogs(user, blog, tag, entryId, group, numPerPage, pageNum, expandEntries, showImages, regenerateIndex, sel, out, afterPagination);
} catch (IOException ioe) {
ioe.printStackTrace();
throw ioe;
@@ -210,47 +297,93 @@ 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);
if (tag != null) tag = new String(Base64.decode(tag));
System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] tag: [" + tag + "]");
String origTag = tag;
byte rawDecode[] = null;
if (tag != null) {
rawDecode = Base64.decode(tag);
tag = DataHelper.getUTF8(rawDecode);
}
//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++) {
b.append((int)tag.charAt(j));
if (rawDecode.length > j)
b.append('.').append((int)rawDecode[j]);
b.append(' ');
}
b.append("encoded as ");
for (int j = 0; j < origTag.length(); j++) {
b.append((int)origTag.charAt(j)).append(' ');
}
//System.out.println("selected tag: " + b.toString());
}
} else if (selector.startsWith(SEL_TAG)) {
tag = selector.substring(SEL_TAG.length());
if (tag != null) tag = new String(Base64.decode(tag));
System.out.println("Selector [" + selector + "] tag: [" + tag + "]");
byte rawDecode[] = null;
if (tag != null) {
rawDecode = Base64.decode(tag);
tag = DataHelper.getUTF8(rawDecode);
}
//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++) {
b.append((int)tag.charAt(j));
if (rawDecode.length > j)
b.append('.').append((int)rawDecode[j]);
b.append(' ');
}
//System.out.println("selected tag: " + b.toString());
}
} else if (selector.startsWith(SEL_ENTRY)) {
int entryStart = selector.lastIndexOf('/');
String blogStr = selector.substring(SEL_ENTRY.length(), entryStart);
String blogStr = blogStr = selector.substring(SEL_ENTRY.length(), entryStart);
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 = new String(Base64.decode(selector.substring(SEL_GROUP.length())));
System.out.println("Selector [" + selector + "] group: [" + group + "]");
group = DataHelper.getUTF8(Base64.decode(selector.substring(SEL_GROUP.length())));
//System.out.println("Selector [" + selector + "] group: [" + group + "]");
}
}
}
}
public static void renderBlogs(User user, Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum,
boolean expandEntries, boolean showImages, boolean regenerateIndex, Writer out) throws IOException {
private static void renderBlogs(User user, Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum,
boolean expandEntries, boolean showImages, boolean regenerateIndex, String selector, Writer out, String afterPagination) throws IOException {
Archive archive = BlogManager.instance().getArchive();
if (regenerateIndex)
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;
@@ -268,26 +401,33 @@ public class ArchiveViewerBean {
pages = entries.size() / numPerPage;
if (numPerPage * pages < entries.size())
pages++;
out.write("<i>");
if (pageNum > 0) {
String prevURL = HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum-1, expandEntries, showImages);
System.out.println("prevURL: " + prevURL);
out.write(" <a href=\"" + prevURL + "\">&lt;&lt;</a>");
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 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 = HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum+1, expandEntries, showImages);
System.out.println("nextURL: " + nextURL);
out.write(" <a href=\"" + nextURL + "\">&gt;&gt;</a>");
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 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>");
}
}
/*
out.write(" <i>");
if (showImages)
@@ -305,9 +445,13 @@ public class ArchiveViewerBean {
"\">Expand details</a>");
out.write("</i>");
*/
if (afterPagination != null)
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);
@@ -323,7 +467,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));
@@ -333,7 +479,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);
@@ -342,14 +488,58 @@ 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 final String getString(Map parameters, String param) {
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;
Object vals = parameters.get(param);
@@ -369,6 +559,24 @@ public class ArchiveViewerBean {
return null;
}
}
public static final String[] getStrings(Map parameters, String param) {
if ( (parameters == null) || (parameters.get(param) == null) )
return null;
Object vals = parameters.get(param);
if (vals.getClass().isArray()) {
return (String[])vals;
} else if (vals instanceof Collection) {
Collection c = (Collection)vals;
if (c.size() <= 0) return null;
String rv[] = new String[c.size()];
int i = 0;
for (Iterator iter = c.iterator(); iter.hasNext(); i++)
rv[i] = (String)iter.next();
return rv;
} else {
return null;
}
}
private static final int getInt(Map param, String key, int defaultVal) {
String val = getString(param, key);
@@ -440,7 +648,7 @@ public class ArchiveViewerBean {
}
private static void renderInvalidAttachment(Map parameters, OutputStream out) throws IOException {
out.write("<b>No such entry, or no such attachment</b>".getBytes());
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 {
@@ -454,43 +662,44 @@ public class ArchiveViewerBean {
return;
}
String props[] = info.getProperties();
out.write("<table border=\"0\">");
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");
out.write("<tr class=\"b_metaField\"><td class=\"b_metaField\"><span class=\"b_metaField\">" + HTMLRenderer.sanitizeString(props[i])
+ ":</span></td><td class=\"b_metaValue\"><span class=\"b_metaValue\">" + HTMLRenderer.sanitizeString(info.getProperty(props[i])) + "</span></td></tr>\n");
}
}
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>");
}
out.write("</table>");
} else {
out.write("Blog not specified");
out.write("<span class=\"b_metaMsgErr\">Blog not specified</span>");
}
}
}

View File

@@ -0,0 +1,100 @@
package net.i2p.syndie.web;
import java.io.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
/**
* Dump out a whole series of blog metadata and entries as a zip stream. All metadata
* is written before any entries, so it can be processed in order safely.
*
* HTTP parameters:
* = meta (multiple values): base64 hash of the blog for which metadata is requested
* = entry (multiple values): blog URI of an entry being requested
*/
public class ExportServlet extends HttpServlet {
public void doGet(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");
resp.setContentType("application/x-syndie-zip");
resp.setStatus(200);
OutputStream out = resp.getOutputStream();
ZipOutputStream zo = new ZipOutputStream(out);
List metaFiles = getMetaFiles(meta);
ZipEntry ze = null;
byte buf[] = new byte[1024];
int read = -1;
for (int i = 0; metaFiles != null && i < metaFiles.size(); i++) {
ze = new ZipEntry("meta" + i);
ze.setTime(0);
zo.putNextEntry(ze);
FileInputStream in = new FileInputStream((File)metaFiles.get(i));
while ( (read = in.read(buf)) != -1)
zo.write(buf, 0, read);
zo.closeEntry();
}
List entryFiles = getEntryFiles(entries);
for (int i = 0; entryFiles != null && i < entryFiles.size(); i++) {
ze = new ZipEntry("entry" + i);
ze.setTime(0);
zo.putNextEntry(ze);
FileInputStream in = new FileInputStream((File)entryFiles.get(i));
while ( (read = in.read(buf)) != -1)
zo.write(buf, 0, read);
zo.closeEntry();
}
zo.finish();
zo.close();
}
private static List getMetaFiles(String blogHashes[]) {
if ( (blogHashes == null) || (blogHashes.length <= 0) ) return null;
File dir = BlogManager.instance().getArchive().getArchiveDir();
List rv = new ArrayList(blogHashes.length);
for (int i = 0; i < blogHashes.length; i++) {
byte hv[] = Base64.decode(blogHashes[i]);
if ( (hv == null) || (hv.length != Hash.HASH_LENGTH) )
continue;
File blogDir = new File(dir, blogHashes[i]);
File metaFile = new File(blogDir, Archive.METADATA_FILE);
if (metaFile.exists())
rv.add(metaFile);
}
return rv;
}
private static List getEntryFiles(String blogURIs[]) {
if ( (blogURIs == null) || (blogURIs.length <= 0) ) return null;
File dir = BlogManager.instance().getArchive().getArchiveDir();
List rv = new ArrayList(blogURIs.length);
for (int i = 0; i < blogURIs.length; i++) {
BlogURI uri = new BlogURI(blogURIs[i]);
if (uri.getEntryId() < 0)
continue;
File blogDir = new File(dir, uri.getKeyHash().toBase64());
File entryFile = new File(blogDir, uri.getEntryId() + ".snd");
if (entryFile.exists())
rv.add(entryFile);
}
return rv;
}
}

View File

@@ -0,0 +1,174 @@
package net.i2p.syndie.web;
import java.io.*;
import java.util.*;
import net.i2p.I2PAppContext;
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() {
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(PostBean.class);
reinitialize();
}
public void reinitialize() {
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();
if (_localFiles != null)
for (int i = 0; i < _localFiles.size(); i++)
((File)_localFiles.get(i)).delete();
_localFiles = new ArrayList();
_previewed = false;
}
public User getUser() { return _user; }
public String getSubject() { return (_subject != null ? _subject : ""); }
public String getTags() { return (_tags != null ? _tags : ""); }
public String getText() { return (_text != null ? _text : ""); }
public String getHeaders() { return (_headers != null ? _headers : ""); }
public void setUser(User user) { _user = user; }
public void setSubject(String subject) { _subject = subject; }
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()) )
return (String)_fileTypes.get(id);
return "application/octet-stream";
}
public void writeAttachmentData(int id, OutputStream out) throws IOException {
FileInputStream in = new FileInputStream((File)_localFiles.get(id));
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
out.close();
}
public void addAttachment(String filename, InputStream fileStream, String mimeType) {
_filenames.add(filename);
_fileStreams.add(fileStream);
_fileTypes.add(mimeType);
}
public int getAttachmentCount() { return (_filenames != null ? _filenames.size() : 0); }
public BlogURI postEntry() throws IOException {
if (!_previewed) return null;
List localStreams = new ArrayList(_localFiles.size());
for (int i = 0; i < _localFiles.size(); i++) {
File f = (File)_localFiles.get(i);
localStreams.add(new FileInputStream(f));
}
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 {
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(_context, _filenames, _fileTypes, _localFiles);
r.render(_user, BlogManager.instance().getArchive(), null, smlContent, out, false, true);
_previewed = true;
}
private String renderSMLContent() {
StringBuffer raw = new StringBuffer();
raw.append("Subject: ").append(_subject).append('\n');
raw.append("Tags: ");
StringTokenizer tok = new StringTokenizer(_tags, " \t\n");
while (tok.hasMoreTokens())
raw.append(tok.nextToken()).append('\t');
raw.append('\n');
raw.append(_headers.trim());
raw.append("\n\n");
raw.append(_text.trim());
return raw.toString();
}
private void cacheAttachments() throws IOException {
File postCacheDir = new File(BlogManager.instance().getTempDir(), _user.getBlog().toBase64());
if (!postCacheDir.exists())
postCacheDir.mkdirs();
for (int i = 0; i < _fileStreams.size(); i++) {
InputStream in = (InputStream)_fileStreams.get(i);
File f = File.createTempFile("attachment", ".dat", postCacheDir);
FileOutputStream o = new FileOutputStream(f);
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
o.write(buf, 0, read);
o.close();
in.close();
_localFiles.add(f);
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

@@ -0,0 +1,754 @@
package net.i2p.syndie.web;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.util.EepGet;
import net.i2p.util.EepGetScheduler;
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;
private int _proxyPort;
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() {
_remoteSchema = null;
_remoteLocation = null;
_remoteIndex = null;
_fetchIndexInProgress = false;
_proxyHost = null;
_proxyPort = -1;
_exportCapable = false;
_statusMessages = new ArrayList();
}
public String getRemoteSchema() { return _remoteSchema; }
public String getRemoteLocation() { return _remoteLocation; }
public ArchiveIndex getRemoteIndex() { return _remoteIndex; }
public String getProxyHost() { return _proxyHost; }
public int getProxyPort() { return _proxyPort; }
public boolean getFetchIndexInProgress() { return _fetchIndexInProgress; }
public String getStatus() {
StringBuffer buf = new StringBuffer();
while (_statusMessages.size() > 0)
buf.append(_statusMessages.remove(0)).append("\n");
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;
Set blogs = new HashSet();
if ("ALL".equals(meta)) {
Set localBlogs = BlogManager.instance().getArchive().getIndex().getUniqueBlogs();
Set remoteBlogs = _remoteIndex.getUniqueBlogs();
for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
if (!localBlogs.contains(blog)) {
if (!ignoreBlog(user, blog))
blogs.add(blog);
}
}
} else {
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());
for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
urls.add(buildMetaURL(blog));
try {
tmpFiles.add(File.createTempFile("fetchMeta", ".txt", BlogManager.instance().getTempDir()));
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + blog.toBase64() + ": " + ioe.getMessage());
}
}
for (int i = 0; i < urls.size(); i++)
_statusMessages.add("Scheduling up metadata fetches for " + HTMLRenderer.sanitizeString((String)urls.get(i)));
fetch(urls, tmpFiles, user, new MetadataStatusListener());
}
private String buildMetaURL(Hash blog) {
String loc = _remoteLocation.trim();
int root = loc.lastIndexOf('/');
return loc.substring(0, root + 1) + blog.toBase64() + "/" + Archive.METADATA_FILE;
}
public void fetchSelectedEntries(User user, Map parameters) {
String entries[] = ArchiveViewerBean.getStrings(parameters, "entry");
if ( (entries == null) || (entries.length <= 0) ) return;
List urls = new ArrayList(entries.length);
List tmpFiles = new ArrayList(entries.length);
for (int i = 0; i < entries.length; 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) {
_statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(entries[i]) + ": " + ioe.getMessage());
}
}
for (int i = 0; i < urls.size(); i++)
_statusMessages.add("Scheduling blog post fetching for " + HTMLRenderer.sanitizeString(entries[i]));
fetch(urls, tmpFiles, user, new BlogStatusListener());
}
public void fetchSelectedBulk(User user, Map parameters) {
String entries[] = ArchiveViewerBean.getStrings(parameters, "entry");
String action = ArchiveViewerBean.getString(parameters, "action");
if ("Fetch all new entries".equals(action)) {
ArchiveIndex localIndex = BlogManager.instance().getArchive().getIndex();
List uris = new ArrayList();
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);
if (!localIndex.getEntryIsKnown(uri))
uris.add(uri);
}
matches.clear();
}
entries = new String[uris.size()];
for (int i = 0; i < uris.size(); i++)
entries[i] = ((BlogURI)uris.get(i)).toString();
}
if ( (entries == null) || (entries.length <= 0) ) return;
if (_exportCapable) {
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 bulk blog post fetch of " + HTMLRenderer.sanitizeString(entries[i]));
}
}
for (Iterator iter = meta.iterator(); iter.hasNext(); ) {
Hash blog = (Hash)iter.next();
url.append("meta=").append(blog.toBase64()).append('&');
_statusMessages.add("Scheduling bulk blog metadata fetch of " + 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());
}
} 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()));
}
}
}
private String buildExportURL() {
String loc = _remoteLocation.trim();
int root = loc.lastIndexOf('/');
return loc.substring(0, root + 1) + "export.zip?";
}
private String buildEntryURL(BlogURI uri) {
String loc = _remoteLocation.trim();
int root = loc.lastIndexOf('/');
return loc.substring(0, root + 1) + uri.getKeyHash().toBase64() + "/" + uri.getEntryId() + ".snd";
}
public void fetchAllEntries(User user, Map parameters) {
ArchiveIndex localIndex = BlogManager.instance().getArchive().getIndex();
List uris = new ArrayList();
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);
if (!localIndex.getEntryIsKnown(uri))
uris.add(uri);
}
entries.clear();
}
List urls = new ArrayList(uris.size());
List tmpFiles = new ArrayList(uris.size());
for (int i = 0; i < uris.size(); i++) {
urls.add(buildEntryURL((BlogURI)uris.get(i)));
try {
tmpFiles.add(File.createTempFile("fetchBlog", ".txt", BlogManager.instance().getTempDir()));
} catch (IOException ioe) {
_statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(uris.get(i).toString()) + ": " + ioe.getMessage());
}
}
for (int i = 0; i < urls.size(); i++)
_statusMessages.add("Fetch all entries: " + HTMLRenderer.sanitizeString((String)urls.get(i)));
fetch(urls, tmpFiles, user, new BlogStatusListener());
}
private void fetch(List urls, List tmpFiles, User user, EepGet.StatusListener lsnr) {
EepGetScheduler scheduler = new EepGetScheduler(I2PAppContext.getGlobalContext(), urls, tmpFiles, _proxyHost, _proxyPort, lsnr);
scheduler.fetch();
}
public void fetchIndex(User user, String schema, String location, String proxyHost, String proxyPort) {
_fetchIndexInProgress = true;
_remoteIndex = null;
_remoteLocation = location;
_remoteSchema = schema;
_proxyHost = null;
_proxyPort = -1;
_exportCapable = false;
if ( (schema == null) || (schema.trim().length() <= 0) ||
(location == null) || (location.trim().length() <= 0) ) {
_statusMessages.add("Location must be specified");
_fetchIndexInProgress = false;
return;
}
if ("web".equals(schema)) {
if ( (proxyHost != null) && (proxyHost.trim().length() > 0) &&
(proxyPort != null) && (proxyPort.trim().length() > 0) ) {
_proxyHost = proxyHost;
try {
_proxyPort = Integer.parseInt(proxyPort);
} catch (NumberFormatException nfe) {
_statusMessages.add("Proxy port " + HTMLRenderer.sanitizeString(proxyPort) + " is invalid");
_fetchIndexInProgress = false;
return;
}
}
} else {
_statusMessages.add(new String("Remote schema " + HTMLRenderer.sanitizeString(schema) + " currently not supported"));
_fetchIndexInProgress = false;
return;
}
_statusMessages.add("Fetching index from " + HTMLRenderer.sanitizeString(_remoteLocation) +
(_proxyHost != null ? " via " + HTMLRenderer.sanitizeString(_proxyHost) + ":" + _proxyPort : ""));
File archiveFile = new File(BlogManager.instance().getTempDir(), user.getBlog().toBase64() + "_remoteArchive.txt");
archiveFile.delete();
EepGet eep = new EepGet(I2PAppContext.getGlobalContext(), ((_proxyHost != null) && (_proxyPort > 0)),
_proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location);
eep.addStatusListener(new IndexFetcherStatusListener(archiveFile));
eep.fetch();
}
private class IndexFetcherStatusListener implements EepGet.StatusListener {
private File _archiveFile;
public IndexFetcherStatusListener(File file) {
_archiveFile = file;
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
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");
_fetchIndexInProgress = false;
ArchiveIndex i = new ArchiveIndex(I2PAppContext.getGlobalContext(), false);
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 {
public MetadataStatusListener() {}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
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) {
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 {
public BlogStatusListener() {}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
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) {
if (url.endsWith(".snm")) {
handleMetadata(url, outputFile);
return;
}
_statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
File file = new File(outputFile);
FileInputStream in = null;
try {
EntryContainer c = new EntryContainer();
in = new FileInputStream(file);
c.load(in);
BlogURI uri = c.getURI();
if ( (uri == null) || (uri.getKeyHash() == null) ) {
_statusMessages.add("Blog post at " + HTMLRenderer.sanitizeString(url) + " was corrupt - no URI");
return;
}
Archive a = BlogManager.instance().getArchive();
BlogInfo info = a.getBlogInfo(uri);
if (info == null) {
_statusMessages.add("Blog post " + uri.toString() + " cannot be imported, as we don't have their blog metadata");
return;
}
boolean ok = a.storeEntry(c);
if (!ok) {
_statusMessages.add("Blog post at " + url + ": " + uri.toString() + " has an invalid signature");
return;
} else {
_statusMessages.add("Blog post " + uri.toString() + " imported");
BlogManager.instance().getArchive().regenerateIndex();
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error importing", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
file.delete();
}
}
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) {}
}
/**
* Receive the status of a fetch for the zip containing blogs and metadata (as generated by
* the ExportServlet)
*/
private class BulkFetchListener implements EepGet.StatusListener {
private File _tmp;
public BulkFetchListener(File tmp) {
_tmp = tmp;
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? cause.getMessage() : ""));
}
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.substring(0, url.indexOf('?'))) + " successful, importing the data");
File file = new File(outputFile);
ZipInputStream zi = null;
try {
zi = new ZipInputStream(new FileInputStream(file));
while (true) {
ZipEntry entry = zi.getNextEntry();
if (entry == null)
break;
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
byte buf[] = new byte[1024];
int read = -1;
while ( (read = zi.read(buf)) != -1)
out.write(buf, 0, read);
if (entry.getName().startsWith("meta")) {
BlogInfo i = new BlogInfo();
i.load(new ByteArrayInputStream(out.toByteArray()));
boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i);
if (ok) {
_statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported");
} else {
_statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged");
}
} else if (entry.getName().startsWith("entry")) {
EntryContainer c = new EntryContainer();
c.load(new ByteArrayInputStream(out.toByteArray()));
BlogURI uri = c.getURI();
if ( (uri == null) || (uri.getKeyHash() == null) ) {
_statusMessages.add("Blog post " + HTMLRenderer.sanitizeString(entry.getName()) + " was corrupt - no URI");
continue;
}
Archive a = BlogManager.instance().getArchive();
BlogInfo info = a.getBlogInfo(uri);
if (info == null) {
_statusMessages.add("Blog post " + HTMLRenderer.sanitizeString(entry.getName()) + " cannot be imported, as we don't have their blog metadata");
continue;
}
boolean ok = a.storeEntry(c);
if (!ok) {
_statusMessages.add("Blog post " + uri.toString() + " has an invalid signature");
continue;
} else {
_statusMessages.add("Blog post " + uri.toString() + " imported");
}
}
}
BlogManager.instance().getArchive().regenerateIndex();
} catch (IOException ioe) {
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) {}
file.delete();
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_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);
}
private void post(List blogURIs, User user) {
List files = new ArrayList(blogURIs.size()+1);
Set meta = new HashSet(4);
Map uploads = new HashMap(files.size());
String importURL = getImportURL();
_statusMessages.add("Uploading through " + HTMLRenderer.sanitizeString(importURL));
for (int i = 0; i < blogURIs.size(); i++) {
BlogURI uri = (BlogURI)blogURIs.get(i);
File blogDir = new File(BlogManager.instance().getArchive().getArchiveDir(), uri.getKeyHash().toBase64());
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(uri);
if (!meta.contains(uri.getKeyHash())) {
uploads.put("blogmeta" + meta.size(), new File(blogDir, Archive.METADATA_FILE));
meta.add(uri.getKeyHash());
_statusMessages.add("Scheduling upload of the blog metadata for " + HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME)));
}
uploads.put("blogpost" + i, new File(blogDir, uri.getEntryId() + ".snd"));
_statusMessages.add("Scheduling upload of " + HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME))
+ ": " + getEntryDate(uri.getEntryId()));
}
EepPost post = new EepPost();
post.postFiles(importURL, _proxyHost, _proxyPort, uploads, new Runnable() { public void run() { _statusMessages.add("Upload complete"); } });
}
private String getImportURL() {
String loc = _remoteLocation.trim();
int archiveRoot = loc.lastIndexOf('/');
int syndieRoot = loc.lastIndexOf('/', archiveRoot-1);
return loc.substring(0, syndieRoot + 1) + "import.jsp";
}
public void renderDeltaForm(User user, ArchiveIndex localIndex, Writer out) throws IOException {
Archive archive = BlogManager.instance().getArchive();
StringBuffer buf = new StringBuffer(512);
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++;
}
}
if (newBlogs > 0) {
out.write(buf.toString());
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 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 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 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("<span class=\"b_remBlog\">" + blog.toBase64() + "</span>\n");
}
buf.append("</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 class=\"b_remDetail\">\n");
if (!archive.getIndex().getEntryIsKnown(uri)) {
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 class=\"b_remDetail\"><a class=\"b_remLocal\" href=\"" + page + "\">(local)</a></td>\n");
}
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 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");
}
if (shownEntries > 0) {
out.write(buf.toString());
buf.setLength(0);
}
int remote = shownEntries;
// now for posts in known blogs that we have and they don't
entries.clear();
localIndex.selectMatchesOrderByEntryId(entries, blog, null);
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 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 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 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");
}
}
if (shownEntries > remote) // skip blogs we have already syndicated
out.write(buf.toString());
}
// now for posts in blogs we have and they don't
int newBefore = localNew;
buf.setLength(0);
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;
}
entries.clear();
localIndex.selectMatchesOrderByEntryId(entries, blog, null);
for (int i = 0; i < entries.size(); i++) {
BlogURI uri = (BlogURI)entries.get(i);
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 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");
localNew++;
}
}
if (localNew > newBefore)
out.write(buf.toString());
out.write("</table>\n");
if (newEntries > 0) {
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("<span class=\"b_remNoRemotePosts\">" + HTMLRenderer.sanitizeString(_remoteLocation) + " has no new posts to offer us</span>\n");
}
if (localNew > 0) {
out.write("<input class=\"b_remPostSelected\" type=\"submit\" name=\"action\" value=\"Post selected entries\" /> \n");
}
out.write("<hr />\n");
}
private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);
private String getDate(long when) {
synchronized (_dateFormat) {
return _dateFormat.format(new Date(when));
}
}
private final String getEntryDate(long when) {
synchronized (_dateFormat) {
try {
String str = _dateFormat.format(new Date(when));
long dayBegin = _dateFormat.parse(str).getTime();
return str + "." + (when - dayBegin);
} catch (ParseException pe) {
pe.printStackTrace();
// wtf
return "unknown";
}
}
}
private long getId(long id) {
synchronized (_dateFormat) {
try {
String str = _dateFormat.format(new Date(id));
long dayBegin = _dateFormat.parse(str).getTime();
return (id - dayBegin);
} catch (ParseException pe) {
pe.printStackTrace();
// wtf
return id;
}
}
}
}

View File

@@ -1,8 +1,39 @@
<%@page import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<form action="index.jsp">
<b>Blogs:</b> <%ArchiveViewerBean.renderBlogSelector(user, request.getParameterMap(), out);%>
<input type="submit" value="Refresh" /></form>
<hr />
<%@page contentType="text/html; charset=UTF-8" import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*" %>
<% request.setCharacterEncoding("UTF-8"); %>
<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")));
<%ArchiveViewerBean.renderBlogs(user, request.getParameterMap(), out); out.flush(); %>
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,28 +1,3 @@
<%@page import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*, net.i2p.data.Base64" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.data.ArchiveIndex" id="archive" />
<%if (request.getRequestURI().indexOf("register.jsp") == -1) {%>
<jsp:include page="_leftnavprotected.jsp" />
<hr />
<% } %>
<u>Local archive:</u><br />
<b>Posts:</b> <jsp:getProperty name="archive" property="newEntries" />/<jsp:getProperty name="archive" property="allEntries" /><br />
<b>Blogs:</b> <jsp:getProperty name="archive" property="newBlogs" />/<jsp:getProperty name="archive" property="allBlogs" /><br />
<b>Size:</b> <jsp:getProperty name="archive" property="newSizeStr" />/<jsp:getProperty name="archive" property="totalSizeStr" /><br />
<i>(new/total)</i>
<hr />
<u>Latest blogs:</u><br />
<%!int i = 0; %>
<%for (i = 0; i < archive.getNewestBlogCount(); i++) {
String keyHash = Base64.encode(archive.getNewestBlog(i).getData());
%><a href="viewmetadata.jsp?<%=ArchiveViewerBean.PARAM_BLOG%>=<%=keyHash%>">
<%=ArchiveViewerBean.getBlogName(keyHash)%></a><br />
<% } %>
<hr />
<u>Latest posts:</u><br />
<%for (i = 0; i < archive.getNewestBlogEntryCount(); i++) {
String keyHash = Base64.encode(archive.getNewestBlogEntry(i).getKeyHash().getData());
long entryId = archive.getNewestBlogEntry(i).getEntryId();
%><a href="index.jsp?<%=ArchiveViewerBean.PARAM_BLOG%>=<%=keyHash%>&<%=ArchiveViewerBean.PARAM_ENTRY%>=<%=entryId%>&<%=ArchiveViewerBean.PARAM_EXPAND_ENTRIES%>=true">
<%=ArchiveViewerBean.getEntryTitle(keyHash, entryId)%></a><br />
<% } %>
<jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive" />

View File

@@ -1,41 +0,0 @@
<%@page import="net.i2p.syndie.*, net.i2p.syndie.sml.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.data.ArchiveIndex" id="archive" />
<%
if ("true".equals(request.getParameter("logout"))) {
user.invalidate();
}
String login = request.getParameter("login");
String pass = request.getParameter("password");
String loginSubmit = request.getParameter("Login");
if ( (login != null) && (pass != null) && (loginSubmit != null) && (loginSubmit.equals("Login")) ) {
String loginResult = BlogManager.instance().login(user, login, pass);
out.write("<b>" + loginResult + "</b>");
}
if (user.getAuthenticated()) {%>
<b><u><jsp:getProperty property="username" name="user" />:</u></b><br />
<a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())%>">My blog</a><br />
<a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, user.getMostRecentEntry(), -1, -1, true, user.getShowImages())%>">Last post</a><br />
<a href="<%=HTMLRenderer.getPostURL(user.getBlog())%>">Post</a><br />
<a href="<%=HTMLRenderer.getMetadataURL(user.getBlog())%>">Metadata</a><br />
<a href="index.jsp?logout=true">Logout</a><br />
<!--
<hr />
<u>Remote Archives:</u><br />
<a href="viewarchive.jsp?url=eep://politics.i2p/archive/">politics.i2p</a><br />
<a href="viewarchive.jsp?url=freenet://SSK@.../TFE/archive/">TFE</a><br />
-->
<%} else {%>
<form action="<%=request.getRequestURI() + "?" + (request.getQueryString() != null ? request.getQueryString() : "")%>">
<b>Login:</b> <input type="text" name="login" size="8" /><br />
<b>Pass:</b> <input type="password" name="password" size="8" /><br /><%
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" /><br /></form>
<a href="register.jsp">Register</a><br />
<% } %>

View File

@@ -1,7 +1,5 @@
<%@page import="net.i2p.syndie.BlogManager" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<jsp:useBean scope="session" class="net.i2p.syndie.data.ArchiveIndex" id="archive" />
<% session.setAttribute("archive", BlogManager.instance().getArchive().getIndex()); %>
<!--
<center>[syndiemedia]</center>
-->

View File

@@ -1,3 +1,46 @@
<td valign="top" align="left" bgcolor="#cccc88" height="10"><a href="index.jsp">Blogs</a></td>
<td valign="top" align="left" bgcolor="#cccc88" height="10">Remote archives</td>
<td valign="top" align="left" bgcolor="#cccc88" height="10">Manage</td>
<%@page import="net.i2p.syndie.*, net.i2p.syndie.sml.*, net.i2p.syndie.web.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<form action="<%=request.getRequestURI() + "?" + (request.getQueryString() != null ? request.getQueryString() : "")%>">
<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");
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 class=\"b_topnavLoginResult\">" + loginResult + "</b>");
}
%>
<% if (user.getAuthenticated()) { %>
<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 {%>
<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 class="b_topnavLoginSubmit" type="submit" name="Login" value="Login" />
<a class="b_topnavRegister" href="register.jsp">Register</a>
<% } %>
</td>
</form>

View File

@@ -1,45 +0,0 @@
<%@page import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.*" %>
<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
<html>
<head>
<title>SyndieMedia</title>
</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 = new String(Base64.decode(nameStr));
location = new String(Base64.decode(locStr));
schema = new String(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.*, 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 userhosts.txt:</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,32 +1,36 @@
<%@page import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*" %>
<%@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" >
</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");
String desc = request.getParameter("description");
if (loc != null) loc = HTMLRenderer.sanitizeString(new String(Base64.decode(loc)));
if (schema != null) schema = HTMLRenderer.sanitizeString(new String(Base64.decode(schema)));
if (desc != null) desc = HTMLRenderer.sanitizeString(new String(Base64.decode(desc)));
if (loc != null) loc = HTMLRenderer.sanitizeString(DataHelper.getUTF8(Base64.decode(loc)));
if (schema != null) schema = HTMLRenderer.sanitizeString(DataHelper.getUTF8(Base64.decode(schema)));
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

@@ -0,0 +1,68 @@
<%@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" >
</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"><%
String contentType = request.getContentType();
if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
MultiPartRequest req = new MultiPartRequest(request);
int metaId = 0;
while (true) {
InputStream meta = req.getInputStream("blogmeta" + metaId);
if ( (meta == null) || (meta.available() <= 0) )
break;
if (!BlogManager.instance().importBlogMetadata(meta)) {
%><span class="b_importMsgErr">Metadata <%=metaId%> failed to be imported</span><br /><%
break;
}
metaId++;
}
int entryId = 0;
while (true) {
InputStream entry = req.getInputStream("blogpost" + entryId);
if ( (entry == null) || (entry.available() <= 0) )
break;
if (!BlogManager.instance().importBlogEntry(entry)) {
%><span class="b_importMsgErr">Entry <%=entryId%> failed to be imported</span><br /><%
break;
}
entryId++;
}
if ( (entryId > 0) || (metaId > 0) ) {
BlogManager.instance().getArchive().regenerateIndex();
session.setAttribute("index", BlogManager.instance().getArchive().getIndex());
}
%><span class="b_importMsgOk">Imported <%=entryId%> posts and <%=metaId%> blog metadata files.</span>
<%
} else { %><form action="import.jsp" method="POST" enctype="multipart/form-data">
<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 class="b_importSubmit" type="submit" name="Post" value="Post entry" /> <input class="b_importCancel" type="reset" value="Cancel" />
<% } %>
</td></tr>
</table>
</body>

View File

@@ -1,14 +1,27 @@
<%@page contentType="text/html" import="net.i2p.syndie.web.*" %>
<%@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="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,74 +1,137 @@
<%@page 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.*" %>
<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.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>
<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"><%
String contentType = request.getContentType();
if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
if (!user.getAuthenticated()) { %>You must be logged in to post<%
} else {
MultiPartRequest req = new MultiPartRequest(request);
String entrySubject = req.getString("entrysubject");
String entryTags = req.getString("entrytags");
String entryText = req.getString("entrytext");
String entryHeaders = req.getString("entryheaders");
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);
} else {
replyTo = null;
}
if (!user.getAuthenticated()) {
%><span class="b_postMsgErr">You must be logged in to post</span><%
} else {
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) {
%><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 {
%><span class="b_postMsgErro">There was an unknown error posting the entry...</span><%
}
List fileStreams = new ArrayList();
List fileNames = new ArrayList();
List fileTypes = new ArrayList();
for (int i = 0; i < 32; i++) {
String filename = req.getFilename("entryfile" + i);
if ( (filename != null) && (filename.trim().length() > 0) ) {
fileNames.add(filename.trim());
fileStreams.add(req.getInputStream("entryfile" + i));
Hashtable params = req.getParams("entryfile" + i);
String type = "application/octet-stream";
for (Iterator iter = params.keySet().iterator(); iter.hasNext(); ) {
String cur = (String)iter.next();
if ("content-type".equalsIgnoreCase(cur)) {
type = (String)params.get(cur);
break;
post.reinitialize();
post.setUser(user);
} else {
// logged in but not confirmed...
String contentType = request.getContentType();
if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
// not confirmed but they posted stuff... gobble up what they give
// and display it as a preview (then we show the confirm form)
post.reinitialize();
post.setUser(user);
MultiPartRequest req = new MultiPartRequest(request);
String entrySubject = req.getString("entrysubject");
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, "UTF-8");
else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_IN_REPLY_TO + ": " + new String(r, "UTF-8");
} else {
replyTo = null;
}
}
fileTypes.add(type);
}
}
BlogURI entry = BlogManager.instance().createBlogEntry(user, entrySubject, entryTags, entryHeaders, entryText, fileNames, fileStreams, fileTypes);
if (entry != null) {
// it has been rebuilt...
request.setAttribute("index", BlogManager.instance().getArchive().getIndex());
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);
post.setHeaders(entryHeaders);
for (int i = 0; i < 32; i++) {
String filename = req.getFilename("entryfile" + i);
if ( (filename != null) && (filename.trim().length() > 0) ) {
Hashtable params = req.getParams("entryfile" + i);
String type = "application/octet-stream";
for (Iterator iter = params.keySet().iterator(); iter.hasNext(); ) {
String cur = (String)iter.next();
if ("content-type".equalsIgnoreCase(cur)) {
type = (String)params.get(cur);
break;
}
}
post.addAttachment(filename.trim(), req.getInputStream("entryfile" + i), type);
}
}
post.renderPreview(out);
%><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");
}
%>
Blog entry <a href="<%=HTMLRenderer.getPageURL(user.getBlog(), null, entry.getEntryId(), -1, -1, user.getShowExpanded(), user.getShowImages())%>">posted</a>!
<% } else { %>
There was an error posting... dunno what it was...
<% }
}
} else { %><form action="post.jsp" method="POST" enctype="multipart/form-data">
Post subject: <input type="text" size="80" name="entrysubject" /><br />
Post tags: <input type="text" size="20" name="entrytags" /><br />
Post content (in raw SML, no headers):<br />
<textarea rows="6" cols="80" name="entrytext"></textarea><br />
<b>SML cheatsheet:</b><br /><textarea rows="6" cols="80" readonly="true">
</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">
<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>
@@ -81,32 +144,29 @@ Post content (in raw SML, no headers):<br />
* [blog name="name" bloghash="base64hash" blogtag="tag"]description[/blog] = link to all posts in the blog with the specified tag
* [blog name="name" blogtag="tag"]description[/blog] = link to all posts in all blogs with the specified tag
* [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)
* 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"></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="Post entry" /> <input type="reset" value="Cancel" />
<% } %>
</td></tr>
<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
} // end of the 'logged in' section
%></td></tr>
</table>
</body>

View File

@@ -1,17 +1,19 @@
<%@page import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.*" %>
<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" >
</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"))) ) {
@@ -22,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

@@ -0,0 +1,83 @@
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="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 remote</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 (!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>
<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)) {
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)) {
//remote.fetchSelectedEntries(user, request.getParameterMap());
remote.fetchSelectedBulk(user, request.getParameterMap());
} else if ("Fetch all new entries".equals(action)) {
//remote.fetchAllEntries(user, request.getParameterMap());
remote.fetchSelectedBulk(user, request.getParameterMap());
} else if ("Post selected entries".equals(action)) {
remote.postSelectedEntries(user, request.getParameterMap());
}
String msgs = remote.getStatus();
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()) { %><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!
%><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 class="b_remoteIndex" rows="5" cols="120"><%=remote.getRemoteIndex()%></textarea><%
}
}
%>
</td></form></tr>
</table>
</body>

View File

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

107
apps/syndie/jsp/syndie.css Normal file
View File

@@ -0,0 +1,107 @@
body {
margin : 0px;
padding : 0px;
font-family: Arial, Helvetica, sans-serif;
background-color : #EEEEEE;
color: #000000;
font-size: 12px;
}
.b_topnavUser {
text-align: right;
background-color: #CCCCDD;
border-spacing: 0px;
margin: 0px;
padding: 0px;
border-width: 0px;
border: 0px;
}
.b_topnavHome {
background-color: #CCCCDD;
color: #000000;
width: 50px;
text-align: left;
}
.b_topnav {
background-color: #CCCCDD;
border-spacing: 0px;
margin: 0px;
padding: 0px;
border-width: 0px;
border: 0px;
}
.b_content {
border: 0px;
border-spacing: 0px;
margin: 0px;
}
.s_summary_overall {
border: 0px;
border-spacing: 0px;
margin: 0px;
}
.s_detail_overall {
border: 0px;
border-spacing: 0px;
margin: 0px;
}
.s_detail_subject {
font-size: 10px;
text-align: left;
background-color: #BBBBFF;
border-spacing: 0px;
margin: 0px;
padding: 0px;
border-width: 0px;
border: 0px;
border-style: none;
}
.s_detail_meta {
font-size: 10px;
text-align: right;
background-color: #BBBBFF;
border-spacing: 0px;
margin: 0px;
padding: 0px;
border-width: 0px;
border: 0px;
border-style: none;
}
.s_summary_subject {
font-size: 10px;
text-align: left;
background-color: #BBBBFF;
border-spacing: 0px;
margin: 0px;
padding: 0px;
border-width: 0px;
border: 0px;
border-style: none;
}
.s_summary_meta {
font-size: 10px;
text-align: right;
background-color: #BBBBFF;
border-spacing: 0px;
margin: 0px;
padding: 0px;
border-width: 0px;
border: 0px;
border-style: none;
}
.s_summary_summDetail {
font-size: 10px;
}
.s_detail_summDetail {
}
.s_detail_summDetailBlog {
}
.s_detail_summDetailBlogLink {
}
td.s_detail_summDetail {
background-color: #DDDDFF;
}
td.s_summary_summ {
font-size: 10px;
background-color: #DDDDFF;
}

View File

@@ -0,0 +1 @@
<%response.sendRedirect("../index.jsp");%>

View File

@@ -1,3 +1,4 @@
<% request.setCharacterEncoding("UTF-8"); %>
<%
java.util.Map params = request.getParameterMap();
response.setContentType(net.i2p.syndie.web.ArchiveViewerBean.getAttachmentContentType(params));

View File

@@ -1,16 +1,35 @@
<%@page contentType="text/html" import="net.i2p.syndie.web.*" %>
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.syndie.web.*, 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>
<title>SyndieMedia metadata</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"><%
ArchiveViewerBean.renderMetadata(request.getParameterMap(), out);
if (user.getAuthenticated()) {
if ("Authorize".equals(request.getParameter("action"))) {
%><span class="b_metaStatus"><%=BlogManager.instance().authorizeRemoteAccess(user, request.getParameter("password"))%></span><%
}
if (!user.getAllowAccessRemote()) {
if (user.getBlog().toBase64().equals(request.getParameter("blog"))) {
%><hr /><form action="viewmetadata.jsp" method="POST">
<input type="hidden" name="blog" value="<%=request.getParameter("blog")%>" />
<span class="b_metaAuthorize">To access remote instances from this instance, please supply the Syndie administration password:</span>
<input class="b_metaAuthorize" type="password" name="password" />
<input class="b_metaAuthorizeSubmit" type="submit" name="action" value="Authorize" />
</form><%
}
}
}
%></td></tr>
</table>
</body>

View File

@@ -0,0 +1,16 @@
<%@page import="net.i2p.syndie.web.ArchiveViewerBean" %><jsp:useBean
scope="session" class="net.i2p.syndie.web.PostBean" id="post" /><%
request.setCharacterEncoding("UTF-8");
String id = request.getParameter(ArchiveViewerBean.PARAM_ATTACHMENT);
if (id != null) {
try {
int attachmentId = Integer.parseInt(id);
if ( (attachmentId < 0) || (attachmentId >= post.getAttachmentCount()) ) {
%>Attachment <%=attachmentId%> does not exist<%
} else {
response.setContentType(post.getContentType(attachmentId));
post.writeAttachmentData(attachmentId, response.getOutputStream());
}
} catch (NumberFormatException nfe) {}
}
%>

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