Changeset c8606746


Ignore:
Timestamp:
Aug 20, 2014 2:11:15 AM (6 years ago)
Author:
str4d <str4d@…>
Branches:
master
Children:
a12f898
Parents:
fdb54c3 (diff), 33b7f08d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

propagate from branch 'i2p.i2p' (head e8883e85a7761bbda9df59b3f6b57601cc01bb5a)

to branch 'i2p.i2p.str4d.eddsa' (head a1cc4ae4a17efaca2825dae64b2cc55aa520ca27)

Files:
24 added
55 edited
2 moved

Legend:

Unmodified
Added
Removed
  • .tx/config

    rfdb54c3 rc8606746  
    1313trans.nb = apps/i2ptunnel/locale/messages_nb.po
    1414trans.nl = apps/i2ptunnel/locale/messages_nl.po
     15trans.nn = apps/i2ptunnel/locale/messages_nn.po
    1516trans.pl = apps/i2ptunnel/locale/messages_pl.po
    1617trans.pt = apps/i2ptunnel/locale/messages_pt.po
    1718trans.pt_BR = apps/i2ptunnel/locale/messages_pt_BR.po
     19trans.ro = apps/i2ptunnel/locale/messages_ro.po
    1820trans.ru_RU = apps/i2ptunnel/locale/messages_ru.po
    1921trans.sk = apps/i2ptunnel/locale/messages_sk.po
     
    4042trans.ro = apps/i2ptunnel/locale-proxy/messages_ro.po
    4143trans.ru_RU = apps/i2ptunnel/locale-proxy/messages_ru.po
     44trans.sk = apps/i2ptunnel/locale-proxy/messages_sk.po
    4245trans.sv_SE = apps/i2ptunnel/locale-proxy/messages_sv.po
    4346trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
     
    8184trans.fr = apps/routerconsole/locale-news/messages_fr.po
    8285trans.he = apps/routerconsole/locale-news/messages_he.po
     86trans.it = apps/routerconsole/locale-news/messages_it.po
    8387trans.ja = apps/routerconsole/locale-news/messages_ja.po
    84 trans.it = apps/routerconsole/locale-news/messages_it.po
     88trans.ko = apps/routerconsole/locale-news/messages_ko.po
     89trans.nb = apps/routerconsole/locale-news/messages_nb.po
    8590trans.nl = apps/routerconsole/locale-news/messages_nl.po
    8691trans.pl = apps/routerconsole/locale-news/messages_pl.po
     
    9297trans.sv_SE = apps/routerconsole/locale-news/messages_sv.po
    9398trans.tr_TR = apps/routerconsole/locale-news/messages_tr.po
     99trans.uk_UA = apps/routerconsole/locale-news/messages_uk.po
    94100trans.zh_CN = apps/routerconsole/locale-news/messages_zh.po
    95101
     
    98104source_file = apps/routerconsole/locale-countries/messages_en.po
    99105source_lang = en
     106trans.ca = apps/routerconsole/locale-countries/messages_ca.po
    100107trans.da = apps/routerconsole/locale-countries/messages_da.po
    101108trans.de = apps/routerconsole/locale-countries/messages_de.po
     
    116123trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
    117124trans.sk = apps/routerconsole/locale-countries/messages_sk.po
     125trans.sq = apps/routerconsole/locale-countries/messages_sq.po
    118126trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
     127trans.uk_UA = apps/routerconsole/locale-countries/messages_uk.po
    119128trans.tr_TR = apps/routerconsole/locale-countries/messages_tr.po
    120129trans.vi = apps/routerconsole/locale-countries/messages_vi.po
     
    135144trans.pl = apps/i2psnark/locale/messages_pl.po
    136145trans.pt = apps/i2psnark/locale/messages_pt.po
     146trans.pt_BR = apps/i2psnark/locale/messages_pt_bR.po
    137147trans.ro = apps/i2psnark/locale/messages_ro.po
    138148trans.ru_RU = apps/i2psnark/locale/messages_ru.po
     
    162172trans.ru_RU = apps/susidns/locale/messages_ru.po
    163173trans.sv_SE = apps/susidns/locale/messages_sv.po
     174trans.tr_TR = apps/susidns/locale/messages_tr.po
    164175trans.uk_UA = apps/susidns/locale/messages_uk.po
    165176trans.vi = apps/susidns/locale/messages_vi.po
     
    195206source_lang = en
    196207trans.cs = apps/susimail/locale/messages_cs.po
     208trans.da = apps/susimail/locale/messages_da.po
    197209trans.de = apps/susimail/locale/messages_de.po
    198210trans.es = apps/susimail/locale/messages_es.po
     
    243255trans.pl = installer/resources/locale/po/messages_pl.po
    244256trans.ja = installer/resources/locale/po/messages_ja.po
     257trans.pl = installer/resources/locale/po/messages_pl.po
    245258trans.pt = installer/resources/locale/po/messages_pt.po
    246259trans.pt_BR = installer/resources/locale/po/messages_pt_BR.po
     
    275288source_file = apps/ministreaming/locale/messages_en.po
    276289source_lang = en
     290trans.de = apps/ministreaming/locale/messages_de.po
     291trans.es = apps/ministreaming/locale/messages_es.po
     292trans.fr = apps/ministreaming/locale/messages_fr.po
     293trans.it = apps/ministreaming/locale/messages_it.po
     294trans.nb = apps/ministreaming/locale/messages_nb.po
     295trans.ru_RU = apps/ministreaming/locale/messages_ru.po
     296trans.uk_UA = apps/ministreaming/locale/messages_uk.po
     297trans.zh_CN = apps/ministreaming/locale/messages_zh.po
    277298
    278299[main]
  • apps/BOB/src/net/i2p/BOB/BOB.java

    rfdb54c3 rc8606746  
    120120        public final static String PROP_CFG_VER = "BOB.CFG.VER";
    121121
     122        /** unused when started via the ClientApp interface */
    122123        private static BOB _bob;
    123124
    124         private NamedDB database;
    125         private Properties props = new Properties();
    126         private AtomicBoolean spin = new AtomicBoolean(true);
     125        private final NamedDB database;
     126        private final Properties props = new Properties();
     127        private final AtomicBoolean spin = new AtomicBoolean(true);
    127128        private static final String P_RUNNING = "RUNNING";
    128129        private static final String P_STARTING = "STARTING";
    129130        private static final String P_STOPPING = "STOPPING";
    130         private AtomicBoolean lock = new AtomicBoolean(false);
     131        private final AtomicBoolean lock = new AtomicBoolean(false);
    131132        // no longer used.
    132133        // private static int maxConnections = 0;
     
    144145         * @deprecated unused
    145146         */
    146         public static void stop() {
    147                 _bob.shutdown(null);
     147        public synchronized static void stop() {
     148                if (_bob != null)
     149                        _bob.shutdown(null);
    148150        }
    149151
     
    190192         * @param args
    191193         */
    192         public static void main(String[] args) {
     194        public synchronized static void main(String[] args) {
    193195                try {
    194196                        _bob = new BOB(I2PAppContext.getGlobalContext(), null, args);
  • apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java

    rfdb54c3 rc8606746  
    5858    public long getSavedTorrentTime(Snark snark);
    5959    public BitField getSavedTorrentBitField(Snark snark);
     60    public boolean getSavedPreserveNamesSetting(Snark snark);
    6061}
  • apps/i2psnark/java/src/org/klomp/snark/IdleChecker.java

    rfdb54c3 rc8606746  
    6767                        _log.warn("Closing tunnels on idle");
    6868                    _util.disconnect();
    69                     _mgr.addMessage(_util.getString("I2P tunnel closed."));
     69                    _mgr.addMessage(_util.getString("No more torrents running.") + ' ' +
     70                                    _util.getString("I2P tunnel closed."));
    7071                    schedule(3 * CHECK_TIME);
    7172                    return;
  • apps/i2psnark/java/src/org/klomp/snark/MagnetURI.java

    rfdb54c3 rc8606746  
    4343            String dn = getParam("dn", url);
    4444            if (dn != null)
    45                 name += " (" + Storage.filterName(dn) + ')';
     45                name += " (" + dn + ')';
    4646        } else if (url.startsWith(MAGGOT)) {
    4747            // maggot://0691e40aae02e552cfcb57af1dca56214680c0c5:0b557bbdf8718e95d352fbe994dec3a383e2ede7
     
    8383
    8484    /**
    85      *  @return pretty name or null
     85     *  @return pretty name or null, NOT HTML escaped
    8686     */
    8787    public String getName() {
  • apps/i2psnark/java/src/org/klomp/snark/PeerState.java

    rfdb54c3 rc8606746  
    164164        if (bitfield != null)
    165165          {
    166             // XXX - Be liberal in what you except?
     166            // XXX - Be liberal in what you accept?
    167167            if (_log.shouldLog(Log.WARN))
    168168              _log.warn("Got unexpected bitfield message from " + peer);
  • apps/i2psnark/java/src/org/klomp/snark/Snark.java

    rfdb54c3 rc8606746  
    3535import net.i2p.data.Destination;
    3636import net.i2p.util.Log;
     37import net.i2p.util.SecureFile;
    3738
    3839/**
     
    222223  private ConnectionAcceptor acceptor;
    223224  private TrackerClient trackerclient;
    224   private String rootDataDir = ".";
     225  private final File rootDataDir;
    225226  private final CompleteListener completeListener;
    226227  private volatile boolean stopped;
     
    239240
    240241
    241   /** from main() via parseArguments() single torrent */
     242  /**
     243   * from main() via parseArguments() single torrent
     244   *
     245   * @deprecated unused
     246   */
    242247  Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
    243248        StorageListener slistener, CoordinatorListener clistener) {
     
    245250  }
    246251
    247   /** single torrent - via router */
     252  /**
     253   * single torrent - via router
     254   *
     255   * @deprecated unused
     256   */
    248257  public Snark(I2PAppContext ctx, Properties opts, String torrent,
    249258               StorageListener slistener, boolean start, String rootDir) {
     
    276285  }
    277286
    278   /** multitorrent */
     287  /**
     288   * multitorrent
     289   */
    279290  public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
    280291        StorageListener slistener, CoordinatorListener clistener,
    281292        CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
    282293        ConnectionAcceptor connectionAcceptor, boolean start, String rootDir)
     294  {
     295      this(util, torrent, ip, user_port, slistener, clistener, complistener,
     296           peerCoordinatorSet, connectionAcceptor, start, rootDir, null);
     297  }
     298
     299  /**
     300   * multitorrent
     301   *
     302   * @param baseFile if null, use rootDir/torrentName; if non-null, use it instead
     303   * @since 0.9.11
     304   */
     305  public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
     306        StorageListener slistener, CoordinatorListener clistener,
     307        CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
     308        ConnectionAcceptor connectionAcceptor, boolean start, String rootDir, File baseFile)
    283309  {
    284310    if (slistener == null)
     
    292318
    293319    this.torrent = torrent;
    294     this.rootDataDir = rootDir;
     320    this.rootDataDir = new File(rootDir);
    295321
    296322    stopped = true;
     
    395421          {
    396422            activity = "Checking storage";
    397             storage = new Storage(_util, meta, slistener);
     423            boolean shouldPreserve = completeListener != null && completeListener.getSavedPreserveNamesSetting(this);
     424            if (baseFile == null) {
     425                String base = meta.getName();
     426                if (!shouldPreserve)
     427                    base = Storage.filterName(base);
     428                if (_util.getFilesPublic())
     429                    baseFile = new File(rootDataDir, base);
     430                else
     431                    baseFile = new SecureFile(rootDataDir, base);
     432            }
     433            storage = new Storage(_util, baseFile, meta, slistener, shouldPreserve);
    398434            if (completeListener != null) {
    399                 storage.check(rootDataDir,
    400                               completeListener.getSavedTorrentTime(this),
     435                storage.check(completeListener.getSavedTorrentTime(this),
    401436                              completeListener.getSavedTorrentBitField(this));
    402437            } else {
    403                 storage.check(rootDataDir);
     438                storage.check();
    404439            }
    405440            // have to figure out when to reopen
     
    453488    this.infoHash = ih;
    454489    this.additionalTrackerURL = trackerURL;
    455     this.rootDataDir = rootDir;
     490    this.rootDataDir = rootDir != null ? new File(rootDir) : null;   // null only for FetchAndAdd extension
    456491    stopped = true;
    457492    id = generateID();
     
    548583        if (storage != null) {
    549584            try {
    550                  storage.reopen(rootDataDir);
     585                 storage.reopen();
    551586             }   catch (IOException ioe) {
    552587                 try { storage.close(); } catch (IOException ioee) {
     
    11031138  public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) {
    11041139      try {
     1140          String base = Storage.filterName(metainfo.getName());
     1141          File baseFile;
     1142          if (_util.getFilesPublic())
     1143              baseFile = new File(rootDataDir, base);
     1144          else
     1145              baseFile = new SecureFile(rootDataDir, base);
    11051146          // The following two may throw IOE...
    1106           storage = new Storage(_util, metainfo, this);
    1107           storage.check(rootDataDir);
     1147          storage = new Storage(_util, baseFile, metainfo, this, false);
     1148          storage.check();
    11081149          // ... so don't set meta until here
    11091150          meta = metainfo;
  • apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java

    rfdb54c3 rc8606746  
    1616import java.util.HashMap;
    1717import java.util.HashSet;
     18import java.util.Iterator;
    1819import java.util.List;
    1920import java.util.Map;
     
    4142
    4243import org.klomp.snark.dht.DHT;
     44import org.klomp.snark.dht.KRPC;
    4345
    4446/**
     
    5658    private final Set<String> _magnets;
    5759    private final Object _addSnarkLock;
    58     private /* FIXME final FIXME */ File _configFile;
     60    private File _configFile;
     61    private File _configDir;
     62    /** one lock for all config, files for simplicity */
     63    private final Object _configLock = new Object();
    5964    private Properties _config;
    6065    private final I2PAppContext _context;
     
    8287    public static final String PROP_UPBW_MAX = "i2psnark.upbw.max";
    8388    public static final String PROP_DIR = "i2psnark.dir";
    84     public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
    85     public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
    86     public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
    87     public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
     89    private static final String PROP_META_PREFIX = "i2psnark.zmeta.";
     90    private static final String PROP_META_STAMP = "stamp";
     91    private static final String PROP_META_BASE = "base";
     92    private static final String PROP_META_BITFIELD = "bitfield";
     93    private static final String PROP_META_PRIORITY = "priority";
     94    private static final String PROP_META_PRESERVE_NAMES = "preserveFileNames";
     95    //private static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
     96    //private static final String PROP_META_PRIORITY_SUFFIX = ".priority";
     97    private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
    8898
    8999    private static final String CONFIG_FILE_SUFFIX = ".config";
     100    private static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
    90101    public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
    91     public static final String PROP_AUTO_START = "i2snark.autoStart";   // oops
     102    public static final String PROP_OLD_AUTO_START = "i2snark.autoStart";   // oops
     103    public static final String PROP_AUTO_START = "i2psnark.autoStart";      // convert in migration to new config file
    92104    public static final String DEFAULT_AUTO_START = "false";
    93105    //public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
     
    110122    public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
    111123    private static final int DEFAULT_PAGE_SIZE = 50;
     124    public static final String CONFIG_DIR_SUFFIX = ".d";
     125    private static final String SUBDIR_PREFIX = "s";
     126    private static final String B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~";
    112127
    113128    /**
     
    170185        _util = new I2PSnarkUtil(_context, ctxName);
    171186        String cfile = ctxName + CONFIG_FILE_SUFFIX;
    172         _configFile = new File(cfile);
    173         if (!_configFile.isAbsolute())
    174             _configFile = new File(_context.getConfigDir(), cfile);
     187        File configFile = new File(cfile);
     188        if (!configFile.isAbsolute())
     189            configFile = new File(_context.getConfigDir(), cfile);
     190        _configDir = migrateConfig(configFile);
     191        _configFile = new File(_configDir, CONFIG_FILE);
    175192        _trackerMap = new ConcurrentHashMap<String, Tracker>(4);
    176193        loadConfig(null);
     
    342359    }
    343360
     361    /**
     362     *  Migrate the old flat config file to the new config dir
     363     *  containing the config file minus the per-torrent entries,
     364     *  the dht file, and 16 subdirs for per-torrent config files
     365     *  Caller must synch.
     366     *
     367     *  @return the new config directory, non-null
     368     *  @throws RuntimeException on creation fail
     369     *  @since 0.9.11
     370     */
     371    private File migrateConfig(File oldFile) {
     372        File dir = new SecureDirectory(oldFile + CONFIG_DIR_SUFFIX);
     373        if ((!dir.exists()) && (!dir.mkdirs())) {
     374            _log.error("Error creating I2PSnark config dir " + dir);
     375            throw new RuntimeException("Error creating I2PSnark config dir " + dir);
     376        }
     377        // move the DHT file as-is
     378        String oldName = oldFile.toString();
     379        if (oldName.endsWith(CONFIG_FILE_SUFFIX)) {
     380            String oldDHT = oldName.replace(CONFIG_FILE_SUFFIX, KRPC.DHT_FILE_SUFFIX);
     381            File oldDHTFile = new File(oldDHT);
     382            if (oldDHTFile.exists()) {
     383                File newDHTFile = new File(dir, "i2psnark" + KRPC.DHT_FILE_SUFFIX);
     384                FileUtil.rename(oldDHTFile, newDHTFile);
     385            }
     386        }
     387        if (!oldFile.exists())
     388            return dir;
     389        Properties oldProps = new Properties();
     390        try {
     391            DataHelper.loadProps(oldProps, oldFile);
     392            // a good time to fix this ancient typo
     393            String auto = (String) oldProps.remove(PROP_OLD_AUTO_START);
     394            if (auto != null)
     395                oldProps.setProperty(PROP_AUTO_START, auto);
     396        } catch (IOException ioe) {
     397           _log.error("Error loading I2PSnark config " + oldFile, ioe);
     398           return dir;
     399        }
     400        // Gather the props for each torrent, removing them from config
     401        // old b64 of hash as key
     402        Map<String, Properties> configs = new HashMap<String, Properties>(16);
     403        for (Iterator<Map.Entry<Object, Object>> iter = oldProps.entrySet().iterator(); iter.hasNext(); ) {
     404            Map.Entry<Object, Object> e = iter.next();
     405            String k = (String) e.getKey();
     406            if (k.startsWith(PROP_META_PREFIX)) {
     407                iter.remove();
     408                String v = (String) e.getValue();
     409                try {
     410                    k = k.substring(PROP_META_PREFIX.length());
     411                    String h = k.substring(0, 28);  // length of b64 of 160 bit infohash
     412                    k = k.substring(29); // skip '.'
     413                    Properties tprops = configs.get(h);
     414                    if (tprops == null) {
     415                        tprops = new OrderedProperties();
     416                        configs.put(h, tprops);
     417                    }
     418                    if (k.equals(PROP_META_BITFIELD)) {
     419                        // old config was timestamp,bitfield; split them
     420                        int comma = v.indexOf(',');
     421                        if (comma > 0 && v.length() > comma + 1) {
     422                            tprops.put(PROP_META_STAMP, v.substring(0, comma));
     423                            tprops.put(PROP_META_BITFIELD, v.substring(comma + 1));
     424                        } else {
     425                            // timestamp only??
     426                            tprops.put(PROP_META_STAMP, v);
     427                        }
     428                    } else {
     429                        tprops.put(k, v);
     430                    }
     431                } catch (IndexOutOfBoundsException ioobe) {
     432                    continue;
     433                }
     434            }
     435        }
     436        // Now make a config file for each torrent
     437        for (Map.Entry<String, Properties> e : configs.entrySet()) {
     438            String b64 = e.getKey();
     439            Properties props = e.getValue();
     440            if (props.isEmpty())
     441                continue;
     442            b64 = b64.replace('$', '=');
     443            byte[] ih = Base64.decode(b64);
     444            if (ih == null || ih.length != 20)
     445                continue;
     446            File cfg = configFile(dir, ih);
     447            if (!cfg.exists()) {
     448                File subdir = cfg.getParentFile();
     449                if (!subdir.exists())
     450                    subdir.mkdirs();
     451                try {
     452                    DataHelper.storeProps(props, cfg);
     453                } catch (IOException ioe) {
     454                    _log.error("Error storing I2PSnark config " + cfg, ioe);
     455                }
     456            }
     457        }
     458        // now store in new location, minus the zmeta entries
     459        File newFile = new File(dir, CONFIG_FILE);
     460        Properties newProps = new OrderedProperties();
     461        newProps.putAll(oldProps);
     462        try {
     463            DataHelper.storeProps(newProps, newFile);
     464        } catch (IOException ioe) {
     465            _log.error("Error storing I2PSnark config " + newFile, ioe);
     466            return dir;
     467        }
     468        oldFile.delete();
     469        if (_log.shouldLog(Log.WARN))
     470            _log.warn("Config migrated from " + oldFile + " to " + dir);
     471        return dir;
     472    }
     473
     474    /**
     475     *  The config for a torrent
     476     *  @return non-null, possibly empty
     477     *  @since 0.9.11
     478     */
     479    private Properties getConfig(Snark snark) {
     480        return getConfig(snark.getInfoHash());
     481    }
     482
     483    /**
     484     *  The config for a torrent
     485     *  @param ih 20-byte infohash
     486     *  @return non-null, possibly empty
     487     *  @since 0.9.11
     488     */
     489    private Properties getConfig(byte[] ih) {
     490        Properties rv = new OrderedProperties();
     491        File conf = configFile(_configDir, ih);
     492        synchronized(_configLock) {  // one lock for all
     493            try {
     494                DataHelper.loadProps(rv, conf);
     495            } catch (IOException ioe) {}
     496        }
     497        return rv;
     498    }
     499
     500    /**
     501     *  The config file for a torrent
     502     *  @param confDir the config directory
     503     *  @param ih 20-byte infohash
     504     *  @since 0.9.11
     505     */
     506    private static File configFile(File confDir, byte[] ih) {
     507        String hex = I2PSnarkUtil.toHex(ih);
     508        File subdir = new SecureDirectory(confDir, SUBDIR_PREFIX + B64.charAt((ih[0] >> 2) & 0x3f));
     509        return new File(subdir, hex + CONFIG_FILE_SUFFIX);
     510    }
     511
    344512    /** null to set initial defaults */
    345513    public void loadConfig(String filename) {
     514        synchronized(_configLock) {
     515            locked_loadConfig(filename);
     516        }
     517    }
     518
     519    /** null to set initial defaults */
     520    private void locked_loadConfig(String filename) {
    346521        if (_config == null)
    347522            _config = new OrderedProperties();
     
    350525            if (!cfg.isAbsolute())
    351526                cfg = new File(_context.getConfigDir(), filename);
    352             _configFile = cfg;
    353             if (cfg.exists()) {
     527            _configDir = migrateConfig(cfg);
     528            _configFile = new File(_configDir, CONFIG_FILE);
     529            if (_configFile.exists()) {
    354530                try {
    355                     DataHelper.loadProps(_config, cfg);
     531                    DataHelper.loadProps(_config, _configFile);
    356532                } catch (IOException ioe) {
    357                    _log.error("Error loading I2PSnark config '" + filename + "'", ioe);
     533                   _log.error("Error loading I2PSnark config " + _configFile, ioe);
    358534                }
    359535            }
     
    389565        updateConfig();
    390566    }
     567
    391568    /**
    392569     * Get current theme.
     
    503680     */
    504681    public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
     682                             String startDelay, String pageSize, String seedPct, String eepHost,
     683                             String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
     684                             String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
     685        synchronized(_configLock) {
     686            locked_updateConfig(dataDir, filesPublic, autoStart, refreshDelay,
     687                                startDelay,  pageSize,  seedPct,  eepHost,
     688                                eepPort,  i2cpHost,  i2cpPort,  i2cpOpts,
     689                                upLimit,  upBW, useOpenTrackers, useDHT,  theme);
     690        }
     691    }
     692
     693    private void locked_updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
    505694                             String startDelay, String pageSize, String seedPct, String eepHost,
    506695                             String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
     
    8371026    public void saveConfig() {
    8381027        try {
    839             synchronized (_configFile) {
     1028            synchronized (_configLock) {
    8401029                DataHelper.storeProps(_config, _configFile);
    8411030            }
     
    8451034    }
    8461035   
    847     public Properties getConfig() { return _config; }
    848    
    849     /** @since Jetty 7 */
    850     public String getConfigFilename() {
    851         return _configFile.getAbsolutePath();
    852     }
    853 
    8541036    /** hardcoded for sanity.  perhaps this should be customizable, for people who increase their ulimit, etc. */
    8551037    public static final int MAX_FILES_PER_TORRENT = 512;
     
    9091091    /**
    9101092     *  Caller must verify this torrent is not already added.
     1093     *
     1094     *  @param filename the absolute path to save the metainfo to, generally ending in ".torrent"
     1095     *  @param baseFile may be null, if so look in rootDataDir
    9111096     *  @throws RuntimeException via Snark.fatal()
    9121097     */
    913     private void addTorrent(String filename) { addTorrent(filename, false); }
     1098    private void addTorrent(String filename) {
     1099        addTorrent(filename, null, false);
     1100    }
    9141101
    9151102    /**
    9161103     *  Caller must verify this torrent is not already added.
     1104     *
     1105     *  @param filename the absolute path to save the metainfo to, generally ending in ".torrent"
     1106     *  @param baseFile may be null, if so look in rootDataDir
    9171107     *  @throws RuntimeException via Snark.fatal()
    9181108     */
    919     private void addTorrent(String filename, boolean dontAutoStart) {
     1109    private void addTorrent(String filename, File baseFile, boolean dontAutoStart) {
    9201110        if ((!dontAutoStart) && !_util.connected()) {
    9211111            addMessage(_("Connecting to I2P"));
     
    9981188                        // TODO load saved closest DHT nodes and pass to the Snark ?
    9991189                        // This may take a LONG time
     1190                        if (baseFile == null)
     1191                            baseFile = getSavedBaseFile(info.getInfoHash());
     1192                        if (_log.shouldLog(Log.INFO))
     1193                            _log.info("New Snark, torrent: " + filename + " base: " + baseFile);
    10001194                        torrent = new Snark(_util, filename, null, -1, null, null, this,
    10011195                                            _peerCoordinatorSet, _connectionAcceptor,
    1002                                             false, dataDir.getPath());
     1196                                            false, dataDir.getPath(), baseFile);
    10031197                        loadSavedFilePriorities(torrent);
    10041198                        synchronized (_snarks) {
     
    11431337     * This may take a LONG time to create or check the storage.
    11441338     *
     1339     * Called from servlet. This is only for the 'create torrent' form.
     1340     *
    11451341     * @param metainfo the metainfo for the torrent
    11461342     * @param bitfield the current completion status of the torrent
    11471343     * @param filename the absolute path to save the metainfo to, generally ending in ".torrent", which is also the name of the torrent
    11481344     *                 Must be a filesystem-safe name.
     1345     * @param baseFile may be null, if so look in rootDataDir
    11491346     * @throws RuntimeException via Snark.fatal()
     1347     * @return success
    11501348     * @since 0.8.4
    11511349     */
    1152     public void addTorrent(MetaInfo metainfo, BitField bitfield, String filename, boolean dontAutoStart) throws IOException {
     1350    public boolean addTorrent(MetaInfo metainfo, BitField bitfield, String filename,
     1351                              File baseFile, boolean dontAutoStart) throws IOException {
    11531352        // prevent interference by DirMonitor
    11541353        synchronized (_snarks) {
     
    11561355            if (snark != null) {
    11571356                addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
    1158                 return;
     1357                return false;
    11591358            }
    11601359            // so addTorrent won't recheck
    1161             saveTorrentStatus(metainfo, bitfield, null); // no file priorities
     1360            saveTorrentStatus(metainfo, bitfield, null, baseFile, true); // no file priorities
    11621361            try {
    11631362                locked_writeMetaInfo(metainfo, filename, areFilesPublic());
    11641363                // hold the lock for a long time
    1165                 addTorrent(filename, dontAutoStart);
     1364                addTorrent(filename, baseFile, dontAutoStart);
    11661365            } catch (IOException ioe) {
    11671366                addMessage(_("Failed to copy torrent file to {0}", filename));
    11681367                _log.error("Failed to write torrent file", ioe);
    1169             }
    1170         }
     1368                return false;
     1369            }
     1370        }
     1371        return true;
    11711372    }
    11721373
     
    12361437     */
    12371438    public long getSavedTorrentTime(Snark snark) {
    1238         byte[] ih = snark.getInfoHash();
    1239         String infohash = Base64.encode(ih);
    1240         infohash = infohash.replace('=', '$');
    1241         String time = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
     1439        Properties config = getConfig(snark);
     1440        String time = config.getProperty(PROP_META_STAMP);
    12421441        if (time == null)
    12431442            return 0;
    1244         int comma = time.indexOf(',');
    1245         if (comma <= 0)
    1246             return 0;
    1247         time = time.substring(0, comma);
    12481443        try { return Long.parseLong(time); } catch (NumberFormatException nfe) {}
    12491444        return 0;
     
    12591454        if (metainfo == null)
    12601455            return null;
    1261         byte[] ih = snark.getInfoHash();
    1262         String infohash = Base64.encode(ih);
    1263         infohash = infohash.replace('=', '$');
    1264         String bf = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
     1456        Properties config = getConfig(snark);
     1457        String bf = config.getProperty(PROP_META_BITFIELD);
    12651458        if (bf == null)
    12661459            return null;
    1267         int comma = bf.indexOf(',');
    1268         if (comma <= 0)
    1269             return null;
    1270         bf = bf.substring(comma + 1).trim();
    12711460        int len = metainfo.getPieces();
    12721461        if (bf.equals(".")) {
     
    12951484        if (metainfo.getFiles() == null)
    12961485            return;
    1297         byte[] ih = snark.getInfoHash();
    1298         String infohash = Base64.encode(ih);
    1299         infohash = infohash.replace('=', '$');
    1300         String pri = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
     1486        Properties config = getConfig(snark);
     1487        String pri = config.getProperty(PROP_META_PRIORITY);
    13011488        if (pri == null)
    13021489            return;
     
    13131500        storage.setFilePriorities(rv);
    13141501    }
    1315    
     1502
     1503    /**
     1504     * Get the base location for a torrent from the config file.
     1505     * @return File or null, doesn't necessarily exist
     1506     * @since 0.9.11
     1507     */
     1508    private File getSavedBaseFile(byte[] ih) {
     1509        Properties config = getConfig(ih);
     1510        String base = config.getProperty(PROP_META_BASE);
     1511        if (base == null)
     1512            return null;
     1513        return new File(base);
     1514    }
     1515
     1516    /**
     1517     * Get setting for a torrent from the config file.
     1518     * @return setting, false if not found
     1519     * @since 0.9.15
     1520     */
     1521    public boolean getSavedPreserveNamesSetting(Snark snark) {
     1522        Properties config = getConfig(snark);
     1523        return Boolean.parseBoolean(config.getProperty(PROP_META_PRESERVE_NAMES));
     1524    }
     1525   
     1526    /**
     1527     * Save the completion status of a torrent and other data in the config file
     1528     * for that torrent. Does nothing for magnets.
     1529     *
     1530     * @since 0.9.15
     1531     */
     1532    public void saveTorrentStatus(Snark snark) {
     1533        MetaInfo meta = snark.getMetaInfo();
     1534        Storage storage = snark.getStorage();
     1535        if (meta == null || storage == null)
     1536            return;
     1537        saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
     1538                          storage.getBase(), storage.getPreserveFileNames());
     1539    }
     1540
    13161541    /**
    13171542     * Save the completion status of a torrent and the current time in the config file
    1318      * in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
    1319      * The config file property key is appended with the Base64 of the infohash,
    1320      * with the '=' changed to '$' since a key can't contain '='.
     1543     * for that torrent.
    13211544     * The time is a standard long converted to string.
    13221545     * The status is either a bitfield converted to Base64 or "." for a completed
     
    13251548     * @param bitfield non-null
    13261549     * @param priorities may be null
    1327      */
    1328     public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) {
     1550     * @param base may be null
     1551     */
     1552    private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
     1553                                   File base, boolean preserveNames) {
     1554        synchronized (_configLock) {
     1555            locked_saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames);
     1556        }
     1557    }
     1558
     1559    private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
     1560                                          File base, boolean preserveNames) {
    13291561        byte[] ih = metainfo.getInfoHash();
    1330         String infohash = Base64.encode(ih);
    1331         infohash = infohash.replace('=', '$');
    1332         String now = "" + System.currentTimeMillis();
    13331562        String bfs;
    13341563        if (bitfield.complete()) {
     
    13381567          bfs = Base64.encode(bf);
    13391568        }
    1340         _config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
     1569        Properties config = getConfig(ih);
     1570        config.setProperty(PROP_META_STAMP, Long.toString(System.currentTimeMillis()));
     1571        config.setProperty(PROP_META_BITFIELD, bfs);
     1572        config.setProperty(PROP_META_PRESERVE_NAMES, Boolean.toString(preserveNames));
     1573        if (base != null)
     1574            config.setProperty(PROP_META_BASE, base.getAbsolutePath());
    13411575
    13421576        // now the file priorities
    1343         String prop = PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX;
    13441577        if (priorities != null) {
    13451578            boolean nonzero = false;
     
    13591592                        buf.append(',');
    13601593                }
    1361                 _config.setProperty(prop, buf.toString());
     1594                config.setProperty(PROP_META_PRIORITY, buf.toString());
    13621595            } else {
    1363                 _config.remove(prop);
     1596                config.remove(PROP_META_PRIORITY);
    13641597            }
    13651598        } else {
    1366             _config.remove(prop);
     1599            config.remove(PROP_META_PRIORITY);
    13671600        }
    13681601
    13691602        // TODO save closest DHT nodes too
    13701603
    1371         saveConfig();
    1372     }
    1373    
    1374     /**
    1375      * Remove the status of a torrent from the config file.
    1376      * This may help the config file from growing too big.
     1604        File conf = configFile(_configDir, ih);
     1605        File subdir = conf.getParentFile();
     1606        if (!subdir.exists())
     1607            subdir.mkdirs();
     1608        try {
     1609            DataHelper.storeProps(config, conf);
     1610        } catch (IOException ioe) {
     1611            _log.error("Unable to save the config to " + conf);
     1612        }
     1613    }
     1614   
     1615    /**
     1616     * Remove the status of a torrent by removing the config file.
    13771617     */
    13781618    public void removeTorrentStatus(MetaInfo metainfo) {
    13791619        byte[] ih = metainfo.getInfoHash();
    1380         String infohash = Base64.encode(ih);
    1381         infohash = infohash.replace('=', '$');
    1382         _config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
    1383         _config.remove(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
    1384         saveConfig();
     1620        File conf = configFile(_configDir, ih);
     1621        synchronized (_configLock) {
     1622            conf.delete();
     1623            File subdir = conf.getParentFile();
     1624            String[] files = subdir.list();
     1625            if (files != null && files.length == 0)
     1626                subdir.delete();
     1627        }
    13851628    }
    13861629   
     
    15671810            return;
    15681811        StringBuilder buf = new StringBuilder(256);
    1569         buf.append("<a href=\"").append(_contextPath).append('/').append(storage.getBaseName());
     1812        String base = DataHelper.escapeHTML(storage.getBaseName());
     1813        buf.append("<a href=\"").append(_contextPath).append('/').append(base);
    15701814        if (meta.getFiles() != null)
    15711815            buf.append('/');
    1572         buf.append("\">").append(storage.getBaseName()).append("</a>");
     1816        buf.append("\">").append(base).append("</a>");
    15731817        addMessageNoEscape(_("Download finished: {0}", buf.toString())); //  + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')');
    15741818        updateStatus(snark);
     
    15821826        Storage storage = snark.getStorage();
    15831827        if (meta != null && storage != null)
    1584             saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities());
     1828            saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
     1829                              storage.getBase(), storage.getPreserveFileNames());
    15851830    }
    15861831   
     
    16041849                return null;
    16051850            }
    1606             saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities
     1851            saveTorrentStatus(meta, storage.getBitField(), null,
     1852                              storage.getBase(), storage.getPreserveFileNames()); // no file priorities
    16071853            // temp for addMessage() in case canonical throws
    16081854            String name = storage.getBaseName();
     
    17051951                    // Snark.fatal() throws a RuntimeException
    17061952                    // don't let one bad torrent kill the whole loop
    1707                     addTorrent(name, !shouldAutoStart());
     1953                    addTorrent(name, null, !shouldAutoStart());
    17081954                } catch (Exception e) {
    17091955                    addMessage(_("Error: Could not add the torrent {0}", name) + ": " + e);
  • apps/i2psnark/java/src/org/klomp/snark/Storage.java

    rfdb54c3 rc8606746  
    3333import java.util.List;
    3434import java.util.Map;
     35import java.util.SortedSet;
    3536import java.util.StringTokenizer;
     37import java.util.TreeSet;
    3638import java.util.concurrent.ConcurrentHashMap;
    3739import java.util.concurrent.atomic.AtomicInteger;
     
    5355  private final MetaInfo metainfo;
    5456  private final List<TorrentFile> _torrentFiles;
     57  private final File _base;
    5558  private final StorageListener listener;
    5659  private final I2PSnarkUtil _util;
     
    6467  private final int pieces;
    6568  private final long total_length;
     69  private final boolean _preserveFileNames;
    6670  private boolean changed;
    6771  private volatile boolean _isChecking;
     
    8488
    8589  /**
    86    * Creates a new storage based on the supplied MetaInfo.  This will
     90   * Creates a new storage based on the supplied MetaInfo.
     91   *
     92   * Does not check storage. Caller MUST call check(), which will
    8793   * try to create and/or check all needed files in the MetaInfo.
    8894   *
    89    * Does not check storage. Caller MUST call check()
    90    */
    91   public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
     95   * @param baseFile the torrent data file or dir
     96   * @param preserveFileNames if true, do not remap names to a 'safe' charset
     97   */
     98  public Storage(I2PSnarkUtil util, File baseFile, MetaInfo metainfo, StorageListener listener, boolean preserveFileNames)
    9299  {
    93100    _util = util;
    94101    _log = util.getContext().logManager().getLog(Storage.class);
     102    _base = baseFile;
    95103    this.metainfo = metainfo;
    96104    this.listener = listener;
     
    103111    int sz = files != null ? files.size() : 1;
    104112    _torrentFiles = new ArrayList<TorrentFile>(sz);
     113    _preserveFileNames = preserveFileNames;
    105114  }
    106115
     
    122131  {
    123132    _util = util;
     133    _base = baseFile;
    124134    _log = util.getContext().logManager().getLog(Storage.class);
    125135    this.listener = listener;
     136    _preserveFileNames = true;
    126137    // Create names, rafs and lengths arrays.
    127138    _torrentFiles = getFiles(baseFile);
     
    306317
    307318  /**
    308    *  @param file canonical path (non-directory)
     319   *  @param file non-canonical path (non-directory)
    309320   *  @return number of bytes remaining; -1 if unknown file
    310321   *  @since 0.7.14
    311322   */
    312   public long remaining(String file) {
     323  public long remaining(File file) {
    313324      long bytes = 0;
    314325      for (TorrentFile tf : _torrentFiles) {
    315326          File f = tf.RAFfile;
    316           // use canonical in case snark dir or sub dirs are symlinked
    317           String canonical = null;
    318           if (f != null) {
    319               try {
    320                   canonical = f.getCanonicalPath();
    321               } catch (IOException ioe) {
    322                   f = null;
    323               }
    324           }
    325           if (f != null && canonical.equals(file)) {
     327          if (f.equals(file)) {
    326328              if (complete())
    327329                  return 0;
     
    349351
    350352  /**
    351    *  @param file canonical path (non-directory)
     353   *  @param file non-canonical path (non-directory)
    352354   *  @since 0.8.1
    353355   */
    354   public int getPriority(String file) {
     356  public int getPriority(File file) {
    355357      if (complete() || metainfo.getFiles() == null)
    356358          return 0;
    357359      for (TorrentFile tf : _torrentFiles) {
    358360          File f = tf.RAFfile;
    359           // use canonical in case snark dir or sub dirs are symlinked
    360           if (f != null) {
    361               try {
    362                   String canonical = f.getCanonicalPath();
    363                   if (canonical.equals(file))
    364                       return tf.priority;
    365               } catch (IOException ioe) {}
    366           }
     361          if (f.equals(file))
     362              return tf.priority;
    367363      }
    368364      return 0;
     
    372368   *  Must call Snark.updatePiecePriorities()
    373369   *  (which calls getPiecePriorities()) after calling this.
    374    *  @param file canonical path (non-directory)
     370   *  @param file non-canonical path (non-directory)
    375371   *  @param pri default 0; <0 to disable
    376372   *  @since 0.8.1
    377373   */
    378   public void setPriority(String file, int pri) {
     374  public void setPriority(File file, int pri) {
    379375      if (complete() || metainfo.getFiles() == null)
    380376          return;
    381377      for (TorrentFile tf : _torrentFiles) {
    382378          File f = tf.RAFfile;
    383           // use canonical in case snark dir or sub dirs are symlinked
    384           if (f != null) {
    385               try {
    386                   String canonical = f.getCanonicalPath();
    387                   if (canonical.equals(file)) {
    388                       tf.priority = pri;
    389                       return;
    390                   }
    391               } catch (IOException ioe) {}
     379          if (f.equals(file)) {
     380              tf.priority = pri;
     381              return;
    392382          }
    393383      }
     
    484474   */
    485475  public String getBaseName() {
    486       return filterName(metainfo.getName());
     476      return optFilterName(metainfo.getName());
     477  }
     478
     479  /** @since 0.9.15 */
     480  public boolean getPreserveFileNames() {
     481      return _preserveFileNames;
    487482  }
    488483
     
    491486   * Only call this once, and only after the constructor with the metainfo.
    492487   */
    493   public void check(String rootDir) throws IOException
    494   {
    495     check(rootDir, 0, null);
     488  public void check() throws IOException
     489  {
     490    check(0, null);
    496491  }
    497492
     
    501496   * Only call this once, and only after the constructor with the metainfo.
    502497   */
    503   public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
    504   {
    505     File base;
     498  public void check(long savedTime, BitField savedBitField) throws IOException
     499  {
    506500    boolean areFilesPublic = _util.getFilesPublic();
    507     if (areFilesPublic)
    508         base = new File(rootDir, filterName(metainfo.getName()));
    509     else
    510         base = new SecureFile(rootDir, filterName(metainfo.getName()));
    511501    boolean useSavedBitField = savedTime > 0 && savedBitField != null;
    512502
     
    518508        // Create base as file.
    519509        if (_log.shouldLog(Log.INFO))
    520             _log.info("Creating/Checking file: " + base);
     510            _log.info("Creating/Checking file: " + _base);
    521511        // createNewFile() can throw a "Permission denied" IOE even if the file exists???
    522512        // so do it second
    523         if (!base.exists() && !base.createNewFile())
    524           throw new IOException("Could not create file " + base);
    525 
    526         _torrentFiles.add(new TorrentFile(base, base, metainfo.getTotalLength()));
     513        if (!_base.exists() && !_base.createNewFile())
     514          throw new IOException("Could not create file " + _base);
     515
     516        _torrentFiles.add(new TorrentFile(_base, _base, metainfo.getTotalLength()));
    527517        if (useSavedBitField) {
    528             long lm = base.lastModified();
     518            long lm = _base.lastModified();
    529519            if (lm <= 0 || lm > savedTime)
    530520                useSavedBitField = false;
    531             else if (base.length() != metainfo.getTotalLength())
     521            else if (_base.length() != metainfo.getTotalLength())
    532522                useSavedBitField = false;
    533523        }
     
    537527        // Create base as dir.
    538528        if (_log.shouldLog(Log.INFO))
    539             _log.info("Creating/Checking directory: " + base);
    540         if (!base.mkdir() && !base.isDirectory())
    541           throw new IOException("Could not create directory " + base);
     529            _log.info("Creating/Checking directory: " + _base);
     530        if (!_base.mkdir() && !_base.isDirectory())
     531          throw new IOException("Could not create directory " + _base);
    542532
    543533        List<Long> ls = metainfo.getLengths();
     
    547537          {
    548538            List<String> path = files.get(i);
    549             File f = createFileFromNames(base, path, areFilesPublic);
     539            File f = createFileFromNames(_base, path, areFilesPublic);
    550540            // dup file name check after filtering
    551541            for (int j = 0; j < i; j++) {
     
    563553                        lastPath = '_' + lastPath;
    564554                    path.set(last, lastPath);
    565                     f = createFileFromNames(base, path, areFilesPublic);
     555                    f = createFileFromNames(_base, path, areFilesPublic);
    566556                    j = 0;
    567557                }
    568558            }
    569559            long len = ls.get(i).longValue();
    570             _torrentFiles.add(new TorrentFile(base, f, len));
     560            _torrentFiles.add(new TorrentFile(_base, f, len));
    571561            total += len;
    572562            if (useSavedBitField) {
     
    615605   * @throws IOE on fail
    616606   */
    617   public void reopen(String rootDir) throws IOException
     607  public void reopen() throws IOException
    618608  {
    619609      if (_torrentFiles.isEmpty())
     
    637627        0x2028, 0x2029
    638628     };
     629
     630  /**
     631   *  Filter the name, but only if configured to do so.
     632   *  We will do so on torrents received from others, but not
     633   *  on those we created ourselves, so we do not lose track of files.
     634   *
     635   *  @since 0.9.15
     636   */
     637  private String optFilterName(String name) {
     638      if (_preserveFileNames)
     639          return name;
     640      return filterName(name);
     641  }
    639642
    640643  /**
     
    691694   *  things going in the wrong place if there are duplicates
    692695   *  in intermediate path elements after filtering.
    693    */
    694   private static File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
     696   *
     697   *  @param names path elements
     698   */
     699  private File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
    695700  {
    696701    File f = null;
     
    698703    while (it.hasNext())
    699704      {
    700         String name = filterName(it.next());
     705        String name = optFilterName(it.next());
    701706        if (it.hasNext())
    702707          {
     
    726731  }
    727732
    728   public static File getFileFromNames(File base, List<String> names)
    729   {
    730     Iterator<String> it = names.iterator();
    731     while (it.hasNext())
    732       {
    733         String name = filterName(it.next());
    734         base = new File(base, name);
    735       }
    736     return base;
     733  /**
     734   *  The base file or directory.
     735   *  @return the File
     736   *  @since 0.9.11
     737   */
     738  public File getBase() {
     739      return _base;
     740  }
     741
     742  /**
     743   *  Does not include directories. Unsorted.
     744   *  @return a new List
     745   *  @since 0.9.11
     746   */
     747  public List<File> getFiles() {
     748      List<File> rv = new ArrayList<File>(_torrentFiles.size());
     749      for (TorrentFile tf : _torrentFiles) {
     750          rv.add(tf.RAFfile);
     751      }
     752      return rv;
     753  }
     754
     755  /**
     756   *  Includes the base for a multi-file torrent.
     757   *  Sorted bottom-up for easy deletion.
     758   *  Slow. Use for deletion only.
     759   *  @since 0.9.11
     760   *  @return a new Set or null for a single-file torrent
     761   */
     762  public SortedSet<File> getDirectories() {
     763      if (!_base.isDirectory())
     764          return null;
     765      SortedSet<File> rv = new TreeSet<File>(Collections.reverseOrder());
     766      rv.add(_base);
     767      for (TorrentFile tf : _torrentFiles) {
     768          File f = tf.RAFfile;
     769          do {
     770              f = f.getParentFile();
     771          } while (f != null && rv.add(f));
     772      }
     773      return rv;
    737774  }
    738775
  • apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java

    rfdb54c3 rc8606746  
    291291    }
    292292
     293    public boolean getSavedPreserveNamesSetting(Snark snark) {
     294        return _smgr.getSavedPreserveNamesSetting(snark);
     295    }
     296
    293297    //////// end CompleteListener methods
    294298
  • apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java

    rfdb54c3 rc8606746  
    4141import net.i2p.util.SimpleTimer2;
    4242
     43import org.klomp.snark.SnarkManager;
    4344import org.klomp.snark.TrackerClient;
    4445import org.klomp.snark.bencode.BDecoder;
     
    153154    private static final long EXPLORE_TIME = 877*1000;
    154155    private static final long BLACKLIST_CLEAN_TIME = 17*60*1000;
    155     private static final String DHT_FILE_SUFFIX = ".dht.dat";
     156    public static final String DHT_FILE_SUFFIX = ".dht.dat";
    156157
    157158    private static final int SEND_CRYPTO_TAGS = 8;
     
    186187        }
    187188        _myNodeInfo = new NodeInfo(_myNID, session.getMyDestination(), _qPort);
    188         _dhtFile = new File(ctx.getConfigDir(), baseName + DHT_FILE_SUFFIX);
    189         _backupDhtFile = baseName.equals("i2psnark") ? null : new File(ctx.getConfigDir(), "i2psnark" + DHT_FILE_SUFFIX);
     189        File conf = new File(ctx.getConfigDir(), baseName + ".config" + SnarkManager.CONFIG_DIR_SUFFIX);
     190        _dhtFile = new File(conf, "i2psnark" + DHT_FILE_SUFFIX);
     191        if (baseName.equals("i2psnark")) {
     192            _backupDhtFile = null;
     193        } else {
     194            File bconf = new File(ctx.getConfigDir(), "i2psnark.config" + SnarkManager.CONFIG_DIR_SUFFIX);
     195            _backupDhtFile = new File(bconf, "i2psnark" + DHT_FILE_SUFFIX);
     196        }
    190197        _knownNodes = new DHTNodes(ctx, _myNID);
    191198
  • apps/i2psnark/java/src/org/klomp/snark/web/BasicServlet.java

    rfdb54c3 rc8606746  
    182182        if (_warBase != null && pathInContext.startsWith(_warBase)) {
    183183            r = new JarContent(pathInContext);
    184         } else if (!pathInContext.contains("..") &&
    185                    !pathInContext.endsWith("/")) {
    186             File f = new File(_resourceBase, pathInContext);
     184        } else {
     185            File f = getResource(pathInContext);
    187186            // exists && !directory
    188             if (f.isFile())
     187            if (f != null && f.isFile())
    189188                r = new FileContent(f);
    190189        }
     
    284283                    if (content.getLastModified()/1000 <= ifmsl/1000)
    285284                    {
    286                         response.reset();
     285                        try {
     286                            response.reset();
     287                        } catch (IllegalStateException ise) {
     288                            // committed
     289                            return true;
     290                        }
    287291                        response.setStatus(304);
    288292                        response.flushBuffer();
     
    556560     *  Simple version of URIUtil.encodePath()
    557561     */
    558     protected static String encodePath(String path) throws MalformedURLException {
    559         try {
    560             URI uri = new URI(null, null, path, null);
    561             return uri.toString();
    562         } catch (URISyntaxException use) {
    563             // for ease of use, since a USE is not an IOE but a MUE is...
    564             throw new MalformedURLException(use.getMessage());
    565         }
     562    protected static String encodePath(String path) /* throws MalformedURLException */ {
     563        // Does NOT handle a ':' correctly, throws MUE.
     564        // Can't convert to %3a before hand or the % gets escaped
     565        //try {
     566        //    URI uri = new URI(null, null, path, null);
     567        //    return uri.toString();
     568        //} catch (URISyntaxException use) {
     569        //    // for ease of use, since a USE is not an IOE but a MUE is...
     570        //    throw new MalformedURLException(use.getMessage());
     571        //}
     572        return URIUtil.encodePath(path);
    566573    }
    567574
  • apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java

    rfdb54c3 rc8606746  
    1010import java.util.ArrayList;
    1111import java.util.Arrays;
     12import java.util.Collection;
    1213import java.util.Collections;
    1314import java.util.Comparator;
     
    1920import java.util.Set;
    2021import java.util.TreeMap;
    21 import java.util.TreeSet;
    2222
    2323import javax.servlet.ServletConfig;
     
    6060    private static final String DEFAULT_NAME = "i2psnark";
    6161    public static final String PROP_CONFIG_FILE = "i2psnark.configFile";
     62    private static final String WARBASE = "/.icons/";
    6263 
    6364    public I2PSnarkServlet() {
     
    7475        // limited protection against overwriting other config files or directories
    7576        // in case you named your war "router.war"
     77        // We don't handle bad characters in the context path. Don't do that.
    7678        String configName = _contextName;
    7779        if (!configName.equals(DEFAULT_NAME))
     
    8587        loadMimeMap("org/klomp/snark/web/mime");
    8688        setResourceBase(_manager.getDataDir());
    87         setWarBase("/.icons/");
     89        setWarBase(WARBASE);
    8890    }
    8991   
     
    9698
    9799    /**
    98      *  We override this instead of passing a resource base to super(), because
    99      *  if a resource base is set, super.getResource() always uses that base,
    100      *  and we can't get any resources (like icons) out of the .war
     100     *  We override this to set the file relative to the storage dirctory
     101     *  for the torrent.
     102     *
     103     *  @param pathInContext should always start with /
    101104     */
    102105    @Override
     
    104107    {
    105108        if (pathInContext == null || pathInContext.equals("/") || pathInContext.equals("/index.jsp") ||
    106             pathInContext.equals("/index.html") || pathInContext.startsWith("/.icons/"))
     109            !pathInContext.startsWith("/") || pathInContext.length() == 0 ||
     110            pathInContext.equals("/index.html") || pathInContext.startsWith(WARBASE))
    107111            return super.getResource(pathInContext);
    108112        // files in the i2psnark/ directory
     113        // get top level
     114        pathInContext = pathInContext.substring(1);
     115        File top = new File(pathInContext);
     116        File parent;
     117        while ((parent = top.getParentFile()) != null) {
     118            top = parent;
     119        }
     120        Snark snark = _manager.getTorrentByBaseName(top.getPath());
     121        if (snark != null) {
     122            Storage storage = snark.getStorage();
     123            if (storage != null) {
     124                File sbase = storage.getBase();
     125                String child = pathInContext.substring(top.getPath().length());
     126                return new File(sbase, child);
     127            }
     128        }
    109129        return new File(_resourceBase, pathInContext);
    110130    }
     
    155175        // since we are not overriding handle*(), do this here
    156176        String method = req.getMethod();
     177        // this is the part after /i2psnark
     178        String path = req.getServletPath();
     179
     180        // in-war icons etc.
     181        if (path != null && path.startsWith(WARBASE)) {
     182            if (method.equals("GET") || method.equals("HEAD"))
     183                super.doGet(req, resp);
     184            else  // no POST either
     185                resp.sendError(405);
     186        }
     187
    157188        _themePath = "/themes/snark/" + _manager.getTheme() + '/';
    158189        _imgPath = _themePath + "images/";
    159         // this is the part after /i2psnark
    160         String path = req.getServletPath();
    161190        resp.setHeader("X-Frame-Options", "SAMEORIGIN");
    162191        resp.setHeader("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'");
     
    194223        boolean isConfigure = "/configure".equals(path);
    195224        // index.jsp doesn't work, it is grabbed by the war handler before here
    196         if (!(path == null || path.equals("/") || path.equals("/index.jsp") || path.equals("/index.html") || path.equals("/_post") || isConfigure)) {
     225        if (!(path == null || path.equals("/") || path.equals("/index.jsp") ||
     226              path.equals("/index.html") || path.equals("/_post") || isConfigure)) {
    197227            if (path.endsWith("/")) {
    198228                // Listing of a torrent (torrent detail page)
     
    849879                                break;
    850880                            }
     881                            Storage storage = snark.getStorage();
     882                            if (storage == null)
     883                                break;
    851884                            // step 1 delete files
    852                             for (int i = 0; i < files.size(); i++) {
    853                                 // multifile torrents have the getFiles() return lists of lists of filenames, but
    854                                 // each of those lists just contain a single file afaict...
    855                                 File df = Storage.getFileFromNames(f, files.get(i));
     885                            for (File df : storage.getFiles()) {
    856886                                if (df.delete()) {
    857887                                    //_manager.addMessage(_("Data file deleted: {0}", df.getAbsolutePath()));
     
    860890                                }
    861891                            }
    862                             // step 2 make Set of dirs with reverse sort
    863                             Set<File> dirs = new TreeSet<File>(Collections.reverseOrder());
    864                             for (List<String> list : files) {
    865                                 for (int i = 1; i < list.size(); i++) {
    866                                     dirs.add(Storage.getFileFromNames(f, list.subList(0, i)));
    867                                 }
    868                             }
    869                             // step 3 delete dirs bottom-up
     892                            // step 2 delete dirs bottom-up
     893                            Set<File> dirs = storage.getDirectories();
     894                            if (_log.shouldLog(Log.INFO))
     895                                _log.info("Dirs to delete: " + DataHelper.toString(dirs));
     896                            boolean ok = false;
    870897                            for (File df : dirs) {
    871898                                if (df.delete()) {
     899                                    ok = true;
    872900                                    //_manager.addMessage(_("Data dir deleted: {0}", df.getAbsolutePath()));
    873901                                } else {
     902                                    ok = false;
    874903                                    _manager.addMessage(_("Directory could not be deleted: {0}", df.getAbsolutePath()));
    875904                                    if (_log.shouldLog(Log.WARN))
     
    877906                                }
    878907                            }
    879                             // step 4 delete base
    880                             if (f.delete()) {
    881                                 _manager.addMessage(_("Directory deleted: {0}", f.getAbsolutePath()));
    882                             } else {
    883                                 _manager.addMessage(_("Directory could not be deleted: {0}", f.getAbsolutePath()));
    884                                 if (_log.shouldLog(Log.WARN))
    885                                     _log.warn("Could not delete dir " + f);
    886                             }
     908                            // step 3 message for base (last one)
     909                            if (ok)
     910                                _manager.addMessage(_("Directory deleted: {0}", storage.getBase()));
    887911                            break;
    888912                        }
     
    921945                processTrackerForm(taction, req);
    922946        } else if ("Create".equals(action)) {
    923             String baseData = req.getParameter("baseFile");
     947            String baseData = req.getParameter("nofilter_baseFile");
    924948            if (baseData != null && baseData.trim().length() > 0) {
    925                 File baseFile = new File(_manager.getDataDir(), baseData);
     949                File baseFile = new File(baseData.trim());
     950                if (!baseFile.isAbsolute())
     951                    baseFile = new File(_manager.getDataDir(), baseData);
    926952                String announceURL = req.getParameter("announceURL");
    927953                // make the user add a tracker on the config form now
     
    931957
    932958                if (baseFile.exists()) {
     959                    String torrentName = baseFile.getName();
     960                    if (torrentName.toLowerCase(Locale.US).endsWith(".torrent")) {
     961                        _manager.addMessage(_("Cannot add a torrent ending in \".torrent\": {0}", baseFile.getAbsolutePath()));
     962                        return;
     963                    }
     964                    Snark snark = _manager.getTorrentByBaseName(torrentName);
     965                    if (snark != null) {
     966                        _manager.addMessage(_("Torrent with this name is already running: {0}", torrentName));
     967                        return;
     968                    }
     969                    if (isParentOf(baseFile,_manager.getDataDir()) ||
     970                        isParentOf(baseFile, _manager.util().getContext().getBaseDir()) ||
     971                        isParentOf(baseFile, _manager.util().getContext().getConfigDir())) {
     972                        _manager.addMessage(_("Cannot add a torrent including an I2P directory: {0}", baseFile.getAbsolutePath()));
     973                        return;
     974                    }
     975                    Collection<Snark> snarks = _manager.getTorrents();
     976                    for (Snark s : snarks) {
     977                        Storage storage = s.getStorage();
     978                        if (storage == null)
     979                            continue;
     980                        File sbase = storage.getBase();
     981                        if (isParentOf(sbase, baseFile)) {
     982                            _manager.addMessage(_("Cannot add torrent {0} inside another torrent: {1}",
     983                                                  baseFile.getAbsolutePath(), sbase));
     984                            return;
     985                        }
     986                        if (isParentOf(baseFile, sbase)) {
     987                            _manager.addMessage(_("Cannot add torrent {0} including another torrent: {1}",
     988                                                  baseFile.getAbsolutePath(), sbase));
     989                            return;
     990                        }
     991                    }
     992
    933993                    if (announceURL.equals("none"))
    934994                        announceURL = null;
     
    9831043                        // FIXME is the storage going to stay around thanks to the info reference?
    9841044                        // now add it, but don't automatically start it
    985                         _manager.addTorrent(info, s.getBitField(), torrentFile.getAbsolutePath(), true);
     1045                        boolean ok = _manager.addTorrent(info, s.getBitField(), torrentFile.getAbsolutePath(), baseFile, true);
     1046                        if (!ok)
     1047                            return;
    9861048                        _manager.addMessage(_("Torrent created for \"{0}\"", baseFile.getName()) + ": " + torrentFile.getAbsolutePath());
    9871049                        if (announceURL != null && !_manager.util().getOpenTrackers().contains(announceURL))
     
    13341396        }
    13351397
    1336         String encodedBaseName = urlEncode(fullBasename);
     1398        String encodedBaseName = encodePath(fullBasename);
    13371399        // File type icon column
    13381400        out.write("</td>\n<td>");
     
    13841446            out.write(buf.toString());
    13851447        }
    1386         out.write(basename);
     1448        out.write(DataHelper.escapeHTML(basename));
    13871449        if (remaining == 0 || isMultiFile)
    13881450            out.write("</a>");
     
    17631825   
    17641826    private void writeSeedForm(PrintWriter out, HttpServletRequest req, List<Tracker> sortedTrackers) throws IOException {
    1765         String baseFile = req.getParameter("baseFile");
    1766         if (baseFile == null || baseFile.trim().length() <= 0)
    1767             baseFile = "";
    1768         else
    1769             baseFile = DataHelper.stripHTML(baseFile);    // XSS
    1770        
    17711827        out.write("<a name=\"add\"></a><div class=\"newtorrentsection\"><div class=\"snarkNewTorrent\">\n");
    17721828        // *not* enctype="multipart/form-data", so that the input type=file sends the filename, not the file
     
    17841840        //out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n");
    17851841        out.write(_("Data to seed"));
    1786         out.write(":<td><code>" + _manager.getDataDir().getAbsolutePath() + File.separatorChar
    1787                   + "</code><input type=\"text\" name=\"baseFile\" size=\"58\" value=\"" + baseFile
     1842        out.write(":<td>"
     1843                  + "<input type=\"text\" name=\"nofilter_baseFile\" size=\"58\" value=\""
    17881844                  + "\" spellcheck=\"false\" title=\"");
    1789         out.write(_("File or directory to seed (must be within the specified path)"));
     1845        out.write(_("File or directory to seed (full path or within the directory {0} )",
     1846                    _manager.getDataDir().getAbsolutePath() + File.separatorChar));
    17901847        out.write("\" ><tr><td>\n");
    17911848        out.write(_("Trackers"));
     
    21872244    }
    21882245   
    2189     /** @since 0.7.14 */
     2246    /**
     2247     * This is for a full URL. For a path only, use encodePath().
     2248     * @since 0.7.14
     2249     */
    21902250    static String urlify(String s) {
    21912251        return urlify(s, 100);
    21922252    }
    21932253   
    2194     /** @since 0.9 */
     2254    /**
     2255     * This is for a full URL. For a path only, use encodePath().
     2256     * @since 0.9
     2257     */
    21952258    private static String urlify(String s, int max) {
    21962259        StringBuilder buf = new StringBuilder(256);
     
    21992262        String display;
    22002263        if (s.length() <= max)
    2201             display = link;
     2264            display = DataHelper.escapeHTML(link);
    22022265        else
    2203             display = urlEncode(s.substring(0, max)) + "&hellip;";
     2266            display = DataHelper.escapeHTML(s.substring(0, max)) + "&hellip;";
    22042267        buf.append("<a href=\"").append(link).append("\">").append(display).append("</a>");
    22052268        return buf.toString();
    22062269    }
    22072270   
    2208     /** @since 0.8.13 */
     2271    /**
     2272     * This is for a full URL. For a path only, use encodePath().
     2273     * @since 0.8.13
     2274     */
    22092275    private static String urlEncode(String s) {
    22102276        return s.replace(";", "%3B").replace("&", "&amp;").replace(" ", "%20")
     
    22312297
    22322298        public int compare(File l, File r) {
    2233             if (l.isDirectory() && !r.isDirectory())
     2299            boolean ld = l.isDirectory();
     2300            boolean rd = r.isDirectory();
     2301            if (ld && !rd)
    22342302                return -1;
    2235             if (r.isDirectory() && !l.isDirectory())
     2303            if (rd && !ld)
    22362304                return 1;
    22372305            return Collator.getInstance().compare(l.getName(), r.getName());
     
    22622330     *
    22632331     * Get the resource list as a HTML directory listing.
    2264      * @param r The Resource
    2265      * @param base The base URL
     2332     * @param xxxr The Resource unused
     2333     * @param base The encoded base URL
    22662334     * @param parent True if the parent directory should be included
    22672335     * @param postParams map of POST parameters or null if not a POST
     
    22692337     * @since 0.7.14
    22702338     */
    2271     private String getListHTML(File r, String base, boolean parent, Map<String, String[]> postParams)
     2339    private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams)
    22722340        throws IOException
    22732341    {
    2274         File[] ls = null;
    2275         if (r.isDirectory()) {
    2276             ls = r.listFiles();
    2277             Arrays.sort(ls, new ListingComparator());
    2278         }  // if r is not a directory, we are only showing torrent info section
    2279        
    2280         String title = decodePath(base);
     2342        String decodedBase = decodePath(base);
     2343        String title = decodedBase;
    22812344        String cpath = _contextPath + '/';
    22822345        if (title.startsWith(cpath))
     
    22852348        // Get the snark associated with this directory
    22862349        String torrentName;
     2350        String pathInTorrent;
    22872351        int slash = title.indexOf('/');
    2288         if (slash > 0)
     2352        if (slash > 0) {
    22892353            torrentName = title.substring(0, slash);
    2290         else
     2354            pathInTorrent = title.substring(slash);
     2355        } else {
    22912356            torrentName = title;
     2357            pathInTorrent = "/";
     2358        }
    22922359        Snark snark = _manager.getTorrentByBaseName(torrentName);
    22932360
    22942361        if (snark != null && postParams != null) {
    22952362            // caller must P-R-G
    2296             savePriorities(snark, postParams);
     2363            String[] val = postParams.get("nonce");
     2364            if (val != null) {
     2365                String nonce = val[0];
     2366                if (String.valueOf(_nonce).equals(nonce))
     2367                    savePriorities(snark, postParams);
     2368                else
     2369                    _manager.addMessage("Please retry form submission (bad nonce)");
     2370            }
    22972371            return null;
    22982372        }
    22992373
     2374        File r;
     2375        if (snark != null) {
     2376            Storage storage = snark.getStorage();
     2377            if (storage != null) {
     2378                File sbase = storage.getBase();
     2379                if (pathInTorrent.equals("/"))
     2380                    r = sbase;
     2381                else
     2382                    r = new File(sbase, pathInTorrent);
     2383            } else {
     2384                // magnet, dummy
     2385                r = new File("");
     2386            }
     2387        } else {
     2388            // dummy
     2389            r = new File("");
     2390        }
    23002391        StringBuilder buf=new StringBuilder(4096);
    23012392        buf.append(DOCTYPE).append("<HTML><HEAD><TITLE>");
     
    23032394            title = title.substring(0, title.length() - 1);
    23042395        String directory = title;
    2305         title = _("Torrent") + ": " + title;
     2396        title = _("Torrent") + ": " + DataHelper.escapeHTML(title);
    23062397        buf.append(title);
    23072398        buf.append("</TITLE>").append(HEADER_A).append(_themePath).append(HEADER_B).append("<link rel=\"shortcut icon\" href=\"" + _themePath + "favicon.ico\">" +
    23082399             "</HEAD><BODY>\n<center><div class=\"snarknavbar\"><a href=\"").append(_contextPath).append("/\" title=\"Torrents\"");
    2309         buf.append(" class=\"snarkRefresh\"><img alt=\"\" border=\"0\" src=\"" + _imgPath + "arrow_refresh.png\">&nbsp;&nbsp;");
     2400        buf.append(" class=\"snarkRefresh\"><img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("arrow_refresh.png\">&nbsp;&nbsp;");
    23102401        if (_contextName.equals(DEFAULT_NAME))
    23112402            buf.append(_("I2PSnark"));
     
    23162407        if (parent)  // always true
    23172408            buf.append("<div class=\"page\"><div class=\"mainsection\">");
    2318         boolean showPriority = ls != null && snark != null && snark.getStorage() != null && !snark.getStorage().complete();
    2319         if (showPriority)
     2409        boolean showPriority = snark != null && snark.getStorage() != null && !snark.getStorage().complete() &&
     2410                               r.isDirectory();
     2411        if (showPriority) {
    23202412            buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
     2413            buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
     2414        }
    23212415        if (snark != null) {
    23222416            // first table - torrent info
     
    23252419               .append(_("Torrent"))
    23262420               .append(":</b> ")
    2327                .append(snark.getBaseName())
     2421               .append(DataHelper.escapeHTML(snark.getBaseName()))
    23282422               .append("</th></tr>\n");
    23292423
    23302424            String fullPath = snark.getName();
    2331             String baseName = urlEncode((new File(fullPath)).getName());
     2425            String baseName = encodePath((new File(fullPath)).getName());
    23322426            buf.append("<tr><td>")
    2333                .append("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" >&nbsp;<b>")
     2427               .append("<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("file.png\" >&nbsp;<b>")
    23342428               .append(_("Torrent file"))
    23352429               .append(":</b> <a href=\"").append(_contextPath).append('/').append(baseName).append("\">")
    2336                .append(fullPath)
     2430               .append(DataHelper.escapeHTML(fullPath))
    23372431               .append("</a></td></tr>\n");
     2432            buf.append("<tr><td>")
     2433               .append("<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("file.png\" >&nbsp;<b>")
     2434               .append(_("Data location"))
     2435               .append(":</b> ")
     2436               .append(DataHelper.escapeHTML(snark.getStorage().getBase().getPath()))
     2437               .append("</td></tr>\n");
    23382438
    23392439            String announce = null;
     
    24362536
    24372537            buf.append("<tr><td>")
    2438                .append("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "size.png\" >&nbsp;<b>")
     2538               .append("<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("size.png\" >&nbsp;<b>")
    24392539               .append(_("Size"))
    24402540               .append(":</b> ")
     
    24432543            double completion = (pieces - snark.getNeeded()) / (double) pieces;
    24442544            if (completion < 1.0)
    2445                 buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"" + _imgPath + "head_rx.png\" >&nbsp;<b>")
     2545                buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_rx.png\" >&nbsp;<b>")
    24462546                   .append(_("Completion"))
    24472547                   .append(":</b> ")
    24482548                   .append((new DecimalFormat("0.00%")).format(completion));
    24492549            else
    2450                 buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"" + _imgPath + "head_rx.png\" >&nbsp;")
     2550                buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_rx.png\" >&nbsp;")
    24512551                   .append(_("Complete"));
    24522552            // else unknown
    24532553            long needed = snark.getNeededLength();
    24542554            if (needed > 0)
    2455                 buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"" + _imgPath + "head_rx.png\" >&nbsp;<b>")
     2555                buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_rx.png\" >&nbsp;<b>")
    24562556                   .append(_("Remaining"))
    24572557                   .append(":</b> ")
     
    24602560                List<List<String>> files = meta.getFiles();
    24612561                int fileCount = files != null ? files.size() : 1;
    2462                 buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" >&nbsp;<b>")
     2562                buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("file.png\" >&nbsp;<b>")
    24632563                   .append(_("Files"))
    24642564                   .append(":</b> ")
    24652565                   .append(fileCount);
    24662566            }
    2467             buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" >&nbsp;<b>")
     2567            buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("file.png\" >&nbsp;<b>")
    24682568               .append(_("Pieces"))
    24692569               .append(":</b> ")
    24702570               .append(pieces);
    2471             buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" >&nbsp;<b>")
     2571            buf.append("&nbsp;<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("file.png\" >&nbsp;<b>")
    24722572               .append(_("Piece size"))
    24732573               .append(":</b> ")
     
    24822582        }
    24832583        buf.append("</table>\n");
     2584
     2585        if (snark != null && !r.exists()) {
     2586            // fixup TODO
     2587            buf.append("<p>Does not exist<br>resource=\"").append(r.toString())
     2588               .append("\"<br>base=\"").append(base)
     2589               .append("\"<br>torrent=\"").append(torrentName)
     2590               .append("\"</p></div></div></BODY></HTML>");
     2591            return buf.toString();
     2592        }
     2593
     2594        File[] ls = null;
     2595        if (r.isDirectory()) {
     2596            ls = r.listFiles();
     2597            Arrays.sort(ls, new ListingComparator());
     2598        }  // if r is not a directory, we are only showing torrent info section
     2599       
    24842600        if (ls == null) {
    24852601            // We are only showing the torrent info section
     
    24922608        buf.append("<tr>\n")
    24932609           .append("<th colspan=2>")
    2494            .append("<img border=\"0\" src=\"" + _imgPath + "file.png\" title=\"")
     2610           .append("<img border=\"0\" src=\"").append(_imgPath).append("file.png\" title=\"")
    24952611           .append(_("Directory"))
    24962612           .append(": ")
     
    25002616           .append("\"></th>\n");
    25012617        buf.append("<th align=\"right\">")
    2502            .append("<img border=\"0\" src=\"" + _imgPath + "size.png\" title=\"")
     2618           .append("<img border=\"0\" src=\"").append(_imgPath).append("size.png\" title=\"")
    25032619           .append(_("Size"))
    25042620           .append("\" alt=\"")
     
    25062622           .append("\"></th>\n");
    25072623        buf.append("<th class=\"headerstatus\">")
    2508            .append("<img border=\"0\" src=\"" + _imgPath + "status.png\" title=\"")
     2624           .append("<img border=\"0\" src=\"").append(_imgPath).append("status.png\" title=\"")
    25092625           .append(_("Status"))
    25102626           .append("\" alt=\"")
     
    25132629        if (showPriority)
    25142630            buf.append("<th class=\"headerpriority\">")
    2515                .append("<img border=\"0\" src=\"" + _imgPath + "priority.png\" title=\"")
     2631               .append("<img border=\"0\" src=\"").append(_imgPath).append("priority.png\" title=\"")
    25162632               .append(_("Priority"))
    25172633               .append("\" alt=\"")
     
    25202636        buf.append("</tr>\n</thead>\n");
    25212637        buf.append("<tr><td colspan=\"" + (showPriority ? '5' : '4') + "\" class=\"ParentDir\"><A HREF=\"");
    2522         buf.append(addPaths(base,"../"));
    2523         buf.append("\"><img alt=\"\" border=\"0\" src=\"" + _imgPath + "up.png\"> ")
     2638        URIUtil.encodePath(buf, addPaths(decodedBase,"../"));
     2639        buf.append("\"><img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("up.png\"> ")
    25242640           .append(_("Up to higher level directory"))
    25252641           .append("</A></td></tr>\n");
     
    25312647        for (int i=0 ; i< ls.length ; i++)
    25322648        {   
    2533             String encoded = encodePath(ls[i].getName());
     2649            //String encoded = encodePath(ls[i].getName());
    25342650            // bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
    25352651            // http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
     
    25452661            String status = "";
    25462662            long length = item.length();
     2663            int priority = 0;
    25472664            if (item.isDirectory()) {
    25482665                complete = true;
     
    25552672                } else {
    25562673                    Storage storage = snark.getStorage();
    2557                     try {
    2558                         File f = item;
    2559                             long remaining = storage.remaining(f.getCanonicalPath());
     2674
     2675                            long remaining = storage.remaining(item);
    25602676                            if (remaining < 0) {
    25612677                                complete = true;
     
    25652681                                status = toImg("tick") + ' ' + _("Complete");
    25662682                            } else {
    2567                                 int priority = storage.getPriority(f.getCanonicalPath());
     2683                                priority = storage.getPriority(item);
    25682684                                if (priority < 0)
    25692685                                    status = toImg("cancel");
     
    25762692                                         " (" + DataHelper.formatSize2(remaining) + "B " + _("remaining") + ")";
    25772693                            }
    2578                     } catch (IOException ioe) {
    2579                         status = "Not a file? " + ioe;
    2580                     }
     2694
    25812695                }
    25822696            }
    25832697
    2584             String path=addPaths(base,encoded);
     2698            String path = addPaths(decodedBase, ls[i].getName());
    25852699            if (item.isDirectory() && !path.endsWith("/"))
    25862700                path=addPaths(path,"/");
    2587             path = urlEncode(path);
     2701            path = encodePath(path);
    25882702            String icon = toIcon(item);
    25892703
     
    26062720            if (complete)
    26072721                buf.append("<a href=\"").append(path).append("\">");
    2608             buf.append(item.getName().replace("&", "&amp;"));
     2722            buf.append(DataHelper.escapeHTML(item.getName()));
    26092723            if (complete)
    26102724                buf.append("</a>");
     
    26182732            if (showPriority) {
    26192733                buf.append("<td class=\"priority\">");
    2620                 File f = item;
    26212734                if ((!complete) && (!item.isDirectory())) {
    2622                     int pri = snark.getStorage().getPriority(f.getCanonicalPath());
    2623                     buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
    2624                     if (pri > 0)
     2735                    buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(item).append("\" ");
     2736                    if (priority > 0)
    26252737                        buf.append("checked=\"true\"");
    26262738                    buf.append('>').append(_("High"));
    26272739
    2628                     buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
    2629                     if (pri == 0)
     2740                    buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(item).append("\" ");
     2741                    if (priority == 0)
    26302742                        buf.append("checked=\"true\"");
    26312743                    buf.append('>').append(_("Normal"));
    26322744
    2633                     buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
    2634                     if (pri < 0)
     2745                    buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(item).append("\" ");
     2746                    if (priority < 0)
    26352747                        buf.append("checked=\"true\"");
    26362748                    buf.append('>').append(_("Skip"));
     
    27042816        else if (plc.endsWith(".iso"))
    27052817            icon = "cd";
     2818        else if (mime.equals("application/x-bittorrent"))
     2819            icon = "magnet";
    27062820        else
    27072821            icon = "page_white";
     
    27282842            if (key.startsWith("pri.")) {
    27292843                try {
    2730                     String file = key.substring(4);
     2844                    File file = new File(key.substring(4));
    27312845                    String val = entry.getValue()[0];   // jetty arrays
    27322846                    int pri = Integer.parseInt(val);
     
    27372851        }
    27382852         snark.updatePiecePriorities();
    2739         _manager.saveTorrentStatus(snark.getMetaInfo(), storage.getBitField(), storage.getFilePriorities());
     2853        _manager.saveTorrentStatus(snark);
     2854    }
     2855
     2856    /**
     2857     *  Is "a" equal to "b",
     2858     *  or is "a" a directory and a parent of file or directory "b",
     2859     *  canonically speaking?
     2860     *
     2861     *  @since 0.9.15
     2862     */
     2863    private static boolean isParentOf(File a, File b) {
     2864        try {
     2865            a = a.getCanonicalFile();
     2866            b = b.getCanonicalFile();
     2867        } catch (IOException ioe) {
     2868            return false;
     2869        }
     2870        if (a.equals(b))
     2871            return true;
     2872        if (!a.isDirectory())
     2873            return false;
     2874        // easy case
     2875        if (!b.getPath().startsWith(a.getPath()))
     2876            return false;
     2877        // dir by dir
     2878        while (!a.equals(b)) {
     2879            b = b.getParentFile();
     2880            if (b == null)
     2881                return false;
     2882        }
     2883        return true;
    27402884    }
    27412885}
  • apps/i2psnark/mime.properties

    rfdb54c3 rc8606746  
    2525sud     = application/zip
    2626tbz     = application/x-bzip2
     27torrent = application/x-bittorrent
    2728txt     = text/plain
    2829war     = application/java-archive
  • apps/ministreaming/java/bundle-messages.sh

    rfdb54c3 rc8606746  
    5252        fi
    5353
    54         if [ -s build/obj/net/i2p/streaming/messages_$LG.class -a \
    55              build/obj/net/i2p/streaming/messages_$LG.class -nt $i -a \
     54        if [ -s build/obj/net/i2p/client/streaming/messages_$LG.class -a \
     55             build/obj/net/i2p/client/streaming/messages_$LG.class -nt $i -a \
    5656             ! -s $TMPFILE ]
    5757        then
  • apps/routerconsole/java/src/net/i2p/router/update/PluginUpdateHandler.java

    rfdb54c3 rc8606746  
    4444        Properties props = PluginStarter.pluginProperties(_context, appName);
    4545        String oldVersion = props.getProperty("version");
    46         String xpi2pURL = props.getProperty("updateURL");
     46        String xpi2pURL = props.getProperty("updateURL.su3");
     47        if (xpi2pURL == null)
     48            xpi2pURL = props.getProperty("updateURL");
    4749        List<URI> updateSources = null;
    4850        if (xpi2pURL != null) {
  • apps/routerconsole/java/src/net/i2p/router/update/PluginUpdateRunner.java

    rfdb54c3 rc8606746  
    22
    33import java.io.File;
     4import java.io.FileInputStream;
    45import java.io.IOException;
    56import java.lang.IllegalArgumentException;
     
    1011
    1112import net.i2p.CoreVersion;
     13import net.i2p.crypto.SU3File;
    1214import net.i2p.crypto.TrustedUpdate;
    1315import net.i2p.data.DataFormatException;
     
    122124        @Override
    123125        public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
    124             boolean update = false;
    125126            updateStatus("<b>" + _("Plugin downloaded") + "</b>");
    126127            File f = new File(_updateFile);
     
    131132                return;
    132133            }
    133 
     134            boolean isSU3;
     135            try {
     136                isSU3 = isSU3File(f);
     137            } catch (IOException ioe) {
     138                f.delete();
     139                statusDone("<b>" + ioe + "</b>");
     140                return;
     141            }
     142            if (isSU3)
     143                processSU3(f, appDir, url);
     144            else
     145                processSUD(f, appDir, url);
     146        }
     147
     148        /**
     149         *  @since 0.9.15
     150         *  @return if SU3
     151         */
     152        private static boolean isSU3File(File f) throws IOException {
     153            FileInputStream fis = null;
     154            try {
     155                fis = new FileInputStream(f);
     156                for (int i = 0; i < SU3File.MAGIC.length(); i++) {
     157                    if (fis.read() != SU3File.MAGIC.charAt(i))
     158                        return false;
     159                }
     160                return true;
     161            } finally {
     162                if (fis != null) try { fis.close(); } catch (IOException ioe) {}
     163            }
     164        }
     165
     166        /**
     167         *  @since 0.9.15
     168         *  @return success
     169         */
     170        private void processSUD(File f, File appDir, String url) {
    134171            TrustedUpdate up = new TrustedUpdate(_context);
    135172            File to = new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + ZIP);
     
    142179                return;
    143180            }
    144             File tempDir = new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + "-unzip");
    145             if (!FileUtil.extractZip(to, tempDir, Log.ERROR)) {
    146                 f.delete();
    147                 to.delete();
    148                 FileUtil.rmdir(tempDir, false);
    149                 statusDone("<b>" + _("Plugin from {0} is corrupt", url) + "</b>");
    150                 return;
    151             }
    152             File installProps = new File(tempDir, "plugin.config");
    153             Properties props = new OrderedProperties();
    154             try {
    155                 DataHelper.loadProps(props, installProps);
    156             } catch (IOException ioe) {
    157                 f.delete();
    158                 to.delete();
    159                 FileUtil.rmdir(tempDir, false);
    160                 statusDone("<b>" + _("Plugin from {0} does not contain the required configuration file", url) + "</b>");
    161                 return;
    162             }
    163             // we don't need this anymore, we will unzip again
    164             FileUtil.rmdir(tempDir, false);
     181            Properties props = getPluginConfig(f, to, url);
     182            if (props == null)
     183                return;
    165184
    166185            // ok, now we check sigs and deal with a bad sig
     
    252271            String sudVersion = TrustedUpdate.getVersionString(f);
    253272            f.delete();
    254 
     273            processFinal(to, appDir, url, props, sudVersion, pubkey, signer);
     274        }
     275
     276        /**
     277         *  @since 0.9.15
     278         */
     279        private void processSU3(File f, File appDir, String url) {
     280            SU3File su3 = new SU3File(_context, f);
     281            File to = new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + ZIP);
     282            String sudVersion;
     283            String signingKeyName;
     284            try {
     285                su3.verifyAndMigrate(to);
     286                if (su3.getFileType() != SU3File.TYPE_ZIP)
     287                    throw new IOException("bad file type");
     288                if (su3.getContentType() != SU3File.CONTENT_PLUGIN)
     289                    throw new IOException("bad content type");
     290                sudVersion = su3.getVersionString();
     291                signingKeyName = su3.getSignerString();
     292            } catch (IOException ioe) {
     293                statusDone("<b>" + ioe + ' ' + _("from {0}", url) + " </b>");
     294                f.delete();
     295                to.delete();
     296                return;
     297            }
     298            Properties props = getPluginConfig(f, to, url);
     299            if (props == null)
     300                return;
     301            String signer = props.getProperty("signer");
     302            if (signer == null || signer.length() <= 0) {
     303                f.delete();
     304                to.delete();
     305                statusDone("<b>" + _("Plugin from {0} contains an invalid key", url) + "</b>");
     306                return;
     307            }
     308            if (!signer.equals(signingKeyName)) {
     309                f.delete();
     310                to.delete();
     311                if (signingKeyName == null)
     312                    _log.error("Failed to verify plugin signature, corrupt plugin or bad signature, signed by: " + signer);
     313                else
     314                    // shouldn't happen
     315                    _log.error("Plugin signer \"" + signer + "\" does not match new signer in plugin.config file \"" + signingKeyName + "\"");
     316                statusDone("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>");
     317                return;
     318            }
     319            processFinal(to, appDir, url, props, sudVersion, null, signer);
     320        }
     321
     322        /**
     323         *  @since 0.9.15
     324         *  @return null on error
     325         */
     326        private Properties getPluginConfig(File f, File to, String url) {
     327            File tempDir = new File(_context.getTempDir(), "tmp" + _context.random().nextInt() + "-unzip");
     328            if (!FileUtil.extractZip(to, tempDir, Log.ERROR)) {
     329                f.delete();
     330                to.delete();
     331                FileUtil.rmdir(tempDir, false);
     332                statusDone("<b>" + _("Plugin from {0} is corrupt", url) + "</b>");
     333                return null;
     334            }
     335            File installProps = new File(tempDir, "plugin.config");
     336            Properties props = new OrderedProperties();
     337            try {
     338                DataHelper.loadProps(props, installProps);
     339            } catch (IOException ioe) {
     340                f.delete();
     341                to.delete();
     342                statusDone("<b>" + _("Plugin from {0} does not contain the required configuration file", url) + "</b>");
     343                return null;
     344            } finally {
     345                // we don't need this anymore, we will unzip again
     346                FileUtil.rmdir(tempDir, false);
     347            }
     348            return props;
     349        }
     350
     351        /**
     352         *  @param pubkey null OK for su3
     353         *  @since 0.9.15
     354         */
     355        private void processFinal(File to, File appDir, String url, Properties props, String sudVersion, String pubkey, String signer) {
     356            boolean update = false;
    255357            String appName = props.getProperty("name");
    256358            String version = props.getProperty("version");
     
    303405                } catch (IOException ioe) {
    304406                    to.delete();
    305                     FileUtil.rmdir(tempDir, false);
    306407                    statusDone("<b>" + _("Installed plugin does not contain the required configuration file", url) + "</b>");
    307408                    return;
     
    310411                String oldKeyName = oldProps.getProperty("signer");
    311412                String oldAppName = oldProps.getProperty("name");
    312                 if ((!pubkey.equals(oldPubkey)) || (!signer.equals(oldKeyName)) || (!appName.equals(oldAppName))) {
     413                if ((pubkey != null && !pubkey.equals(oldPubkey)) || (!signer.equals(oldKeyName)) || (!appName.equals(oldAppName))) {
    313414                    to.delete();
    314415                    statusDone("<b>" + _("Signature of downloaded plugin does not match installed plugin") + "</b>");
  • apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java

    rfdb54c3 rc8606746  
    105105            } catch (NumberFormatException nfe) {}
    106106            if (appnum >= 0) {
    107                 deleteClient(appnum);
     107                if (_context.getBooleanProperty(ConfigClientsHelper.PROP_ENABLE_CLIENT_CHANGE) ||
     108                    isAdvanced()) {
     109                    deleteClient(appnum);
     110                } else {
     111                    addFormError("Delete client disabled");
     112                }
    108113            } else if (pluginsEnabled) {
    109114                try {
     
    386391    private void updatePlugin(String app) {
    387392        Properties props = PluginStarter.pluginProperties(_context, app);
    388         String url = props.getProperty("updateURL");
     393        String url = props.getProperty("updateURL.su3");
     394        if (url == null)
     395            url = props.getProperty("updateURL");
    389396        if (url == null) {
    390397            addFormError(_("No update URL specified for {0}",app));
  • apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java

    rfdb54c3 rc8606746  
    142142                       showStop,
    143143                       // show delete button, show start button
    144                        !isConsole, showStart);
     144                       allowEdit && !isConsole, showStart);
    145145        }
    146146       
     
    249249                        .append("<a href=\"").append(s).append("\">").append(_("Website")).append("</a><td>&nbsp;");
    250250                }
    251                 String updateURL = stripHTML(appProps, "updateURL");
     251                String updateURL = stripHTML(appProps, "updateURL.su3");
     252                if (updateURL == null)
     253                    updateURL = stripHTML(appProps, "updateURL");
    252254                if (updateURL != null) {
    253255                    desc.append("<tr><td>")
  • apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java

    rfdb54c3 rc8606746  
    299299        buf.append("<select style=\"text-align: right !important;\" name=\"sharePercentage\">\n");
    300300        boolean found = false;
    301         for (int i = 30; i <= 110; i += 10) {
     301        for (int i = 100; i >= -10; i -= 10) {
    302302            int val = i;
    303             if (i == 110) {
     303            if (i == -10) {
    304304                if (found)
    305305                    break;
  • apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHandler.java

    rfdb54c3 rc8606746  
    6161        if (ok) {
    6262            if (!oldTheme.equals(_config))
    63                 addFormNotice(_("Theme change saved.") +
     63                addFormNoticeNoEscape(_("Theme change saved.") +
    6464                              " <a href=\"configui\">" +
    6565                              _("Refresh the page to view.") +
    6666                              "</a>");
    6767            if (oldForceMobileConsole != _forceMobileConsole)
    68                 addFormNotice(_("Mobile console option saved.") +
     68                addFormNoticeNoEscape(_("Mobile console option saved.") +
    6969                              " <a href=\"configui\">" +
    7070                              _("Refresh the page to view.") +
  • apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java

    rfdb54c3 rc8606746  
    211211        if (_refreshFrequency != oldFreq) {
    212212            changes.put(PROP_REFRESH_FREQUENCY, ""+_refreshFrequency);
    213             addFormNotice(_("Updating refresh frequency to {0}",
     213            addFormNoticeNoEscape(_("Updating refresh frequency to {0}",
    214214                            _refreshFrequency <= 0 ? _("Never") : DataHelper.formatDuration2(_refreshFrequency)));
    215215        }
  • apps/routerconsole/java/src/net/i2p/router/web/EventLogHelper.java

    rfdb54c3 rc8606746  
    3333    private static final String[] _events = new String[] {
    3434        EventLog.ABORTED, _x("Aborted startup"),
     35        EventLog.BECAME_FLOODFILL, _x("Enabled floodfill"),
    3536        EventLog.CHANGE_IP, _x("Changed IP"),
    3637        EventLog.CHANGE_PORT, _x("Changed port"),
     
    4243        EventLog.NETWORK, _x("Network error"),
    4344        EventLog.NEW_IDENT, _x("New router identity"),
     45        EventLog.NOT_FLOODFILL, _x("Disabled floodfill"),
    4446        EventLog.OOM, _x("Out of memory error"),
    4547        EventLog.REKEYED, _x("New router identity"),
  • apps/routerconsole/jsp/confignet.jsp

    rfdb54c3 rc8606746  
    185185     <%=intl._("I2P does not work well behind this type of firewall. You will probably not be able to accept inbound connections, which will limit your participation in the network.")%>
    186186<li class="tidylist"><b><%=intl._("ERR - UDP Port In Use - Set i2np.udp.internalPort=xxxx in advanced config and restart")%></b> -
    187      <%=intl._("I2P was unable to bind to port 8887 or other configured port.")%>
     187     <%=intl._("I2P was unable to bind to the configured port noted on the advanced network configuration page .")%>
    188188     <%=intl._("Check to see if another program is using the configured port. If so, stop that program or configure I2P to use a different port.")%>
    189189     <%=intl._("This may be a transient error, if the other program is no longer using the port.")%>
  • apps/sam/java/src/net/i2p/sam/SAMBridge.java

    rfdb54c3 rc8606746  
    162162     * @param name name of the destination
    163163     * @return null if the name does not exist, or if it is improperly formatted
     164     * @deprecated unused
    164165     */
    165166    public Destination getDestination(String name) {
  • apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java

    rfdb54c3 rc8606746  
    1313import java.util.Properties;
    1414
     15import net.i2p.client.I2PSession;
    1516import net.i2p.client.I2PSessionException;
    1617import net.i2p.client.datagram.I2PDatagramDissector;
     
    8990                dgram = dgramMaker.makeI2PDatagram(data);
    9091        }
    91         return sendBytesThroughMessageSession(dest, dgram);
     92        // TODO pass ports through
     93        return sendBytesThroughMessageSession(dest, dgram, I2PSession.PROTO_DATAGRAM,
     94                                              I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
    9295    }
    9396
  • apps/sam/java/src/net/i2p/sam/SAMHandler.java

    rfdb54c3 rc8606746  
    1414import java.util.Properties;
    1515
     16import net.i2p.I2PAppContext;
    1617import net.i2p.data.DataHelper;
    1718import net.i2p.util.I2PAppThread;
     
    5556    protected SAMHandler(SocketChannel s,
    5657                         int verMajor, int verMinor, Properties i2cpProps) throws IOException {
    57         _log = new Log(getClass());
     58        _log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
    5859        socket = s;
    5960
  • apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java

    rfdb54c3 rc8606746  
    1010
    1111import java.io.IOException;
    12 import java.io.UnsupportedEncodingException;
     12import java.net.Socket;
     13import java.net.SocketTimeoutException;
    1314import java.nio.channels.SocketChannel;
    14 import java.nio.ByteBuffer;
    1515import java.util.Properties;
    1616import java.util.StringTokenizer;
    1717
     18import net.i2p.I2PAppContext;
    1819import net.i2p.data.DataHelper;
    1920import net.i2p.util.Log;
     
    2728    private static final String VERSION = "3.1";
    2829
     30    private static final int HELLO_TIMEOUT = 60*1000;
     31
    2932    /**
    3033     * Return the right SAM handler depending on the protocol version
     
    3740     */
    3841    public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps) throws SAMException {
    39         String line;
    4042        StringTokenizer tok;
    41         Log log = new Log(SAMHandlerFactory.class);
     43        Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMHandlerFactory.class);
    4244
    4345        try {
    44             line = DataHelper.readLine(s.socket().getInputStream());
     46            Socket sock = s.socket();
     47            sock.setSoTimeout(HELLO_TIMEOUT);
     48            String line = DataHelper.readLine(sock.getInputStream());
     49            sock.setSoTimeout(0);
    4550            if (line == null) {
    4651                log.debug("Connection closed by client");
     
    4853            }
    4954            tok = new StringTokenizer(line.trim(), " ");
     55        } catch (SocketTimeoutException e) {
     56            throw new SAMException("Timeout waiting for HELLO VERSION", e);
    5057        } catch (IOException e) {
    5158            throw new SAMException("Error reading from socket", e);
  • apps/sam/java/src/net/i2p/sam/SAMMessageSession.java

    rfdb54c3 rc8606746  
    1414import java.util.Properties;
    1515
     16import net.i2p.I2PAppContext;
    1617import net.i2p.client.I2PClient;
    1718import net.i2p.client.I2PClientFactory;
     
    3435
    3536    protected final Log _log;
    36 
    37     private I2PSession session = null;
    38 
    39     private SAMMessageSessionHandler handler = null;
     37    private I2PSession session;
     38    private SAMMessageSessionHandler handler;
    4039
    4140    /**
     
    4948     */
    5049    protected SAMMessageSession(String dest, Properties props) throws IOException, DataFormatException, I2PSessionException {
    51         _log = new Log(getClass());
     50        _log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
    5251        ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(dest));
    5352        initSAMMessageSession(bais, props);
     
    104103     * @param dest Destination
    105104     * @param data Bytes to be sent
     105     * @param proto I2CP protocol
     106     * @param fromPort I2CP from port
     107     * @param toPort I2CP to port
    106108     *
    107109     * @return True if the data was sent, false otherwise
    108      * @throws DataFormatException
    109      */
    110     protected boolean sendBytesThroughMessageSession(String dest, byte[] data) throws DataFormatException {
     110     * @throws DataFormatException on unknown / bad dest
     111     */
     112    protected boolean sendBytesThroughMessageSession(String dest, byte[] data,
     113                                                     int proto, int fromPort, int toPort) throws DataFormatException {
    111114        Destination d = SAMUtils.getDest(dest);
    112115
     
    116119
    117120        try {
    118             return session.sendMessage(d, data);
     121            return session.sendMessage(d, data, proto, fromPort, toPort);
    119122        } catch (I2PSessionException e) {
    120123            _log.error("I2PSessionException while sending data", e);
  • apps/sam/java/src/net/i2p/sam/SAMRawSession.java

    rfdb54c3 rc8606746  
    1313import java.util.Properties;
    1414
     15import net.i2p.client.I2PSession;
    1516import net.i2p.client.I2PSessionException;
    1617import net.i2p.data.DataFormatException;
     
    7475        if (data.length > RAW_SIZE_MAX)
    7576            throw new DataFormatException("Data size limit exceeded (" + data.length + ")");
    76         return sendBytesThroughMessageSession(dest, data);
     77        // TODO pass ports through
     78        return sendBytesThroughMessageSession(dest, data, I2PSession.PROTO_DATAGRAM_RAW,
     79                                              I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
    7780    }
    7881
  • apps/sam/java/src/net/i2p/sam/SAMStreamSession.java

    rfdb54c3 rc8606746  
    2626import java.util.concurrent.atomic.AtomicInteger;
    2727
     28import net.i2p.I2PAppContext;
    2829import net.i2p.I2PException;
    2930import net.i2p.client.I2PClient;
     
    108109                            Properties props,  SAMStreamReceiver recv) throws IOException, DataFormatException, SAMException {
    109110        this.recv = recv;
    110         _log = new Log(getClass());
     111        _log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
    111112        if (_log.shouldLog(Log.DEBUG))
    112                 _log.debug("SAM STREAM session instantiated");
     113            _log.debug("SAM STREAM session instantiated");
    113114
    114115        Properties allprops = (Properties) System.getProperties().clone();
  • apps/sam/java/src/net/i2p/sam/SAMUtils.java

    rfdb54c3 rc8606746  
    124124     *
    125125     * @param name Hostname to be resolved
    126      * @param pubKey A stream to write the Destination public key (may be null)
    127126     *
    128127     * @return the Destination for the specified hostname, or null if not found
    129128     */
    130     public static Destination lookupHost(String name, OutputStream pubKey) {
     129    private static Destination lookupHost(String name) {
    131130        NamingService ns = I2PAppContext.getGlobalContext().namingService();
    132131        Destination dest = ns.lookup(name);
    133 
    134         if ((pubKey != null) && (dest != null)) {
    135             try {
    136                 dest.writeBytes(pubKey);
    137             } catch (IOException e) {
    138                 e.printStackTrace();
    139                 return null;
    140             } catch (DataFormatException e) {
    141                 e.printStackTrace();
    142                 return null;
    143             }
    144         }
    145 
    146132        return dest;
    147133    }
     
    152138     * @param s Hostname or key to be resolved
    153139     *
    154      * @return the Destination for the specified hostname, or null if not found
     140     * @return the Destination for the specified hostname, non-null
     141     * @throws DataFormatException on bad Base 64 or name not found
    155142     */
    156143    public static Destination getDest(String s) throws DataFormatException
    157144    {
    158         Destination d = new Destination() ;
    159         try {
    160                 d.fromBase64(s);
    161         } catch (DataFormatException e) {
    162                 d = lookupHost(s, null);
    163                 if ( d==null ) {
    164                         throw e ;
    165                 }
    166         }
    167         return d ;
     145        // NamingService caches b64 so just use it for everything
     146        // TODO: Add a static local cache here so SAM doesn't flush the
     147        // NamingService cache
     148        Destination d = lookupHost(s);
     149        if (d == null) {
     150            String msg;
     151            if (s.length() >= 516)
     152                msg = "Bad Base64 dest: ";
     153            else if (s.length() == 60 && s.endsWith(".b32.i2p"))
     154                msg = "Lease set not found: ";
     155            else
     156                msg = "Host name not found: ";
     157            throw new DataFormatException(msg + s);
     158        }
     159        return d;
    168160    }
    169161
  • apps/sam/java/src/net/i2p/sam/SAMv1Handler.java

    rfdb54c3 rc8606746  
    247247                    if (destKeystream == null) {
    248248                        if (_log.shouldLog(Log.DEBUG))
    249                             _log.debug("Custom destination specified [" + dest + "] but it isnt know, creating a new one");
     249                            _log.debug("Custom destination specified [" + dest + "] but it isn't known, creating a new one");
    250250                        ByteArrayOutputStream baos = new ByteArrayOutputStream(640);
    251251                        SAMUtils.genRandomKey(baos, null);
     
    285285                        && !dir.equals("BOTH")) {
    286286                        if (_log.shouldLog(Log.DEBUG))
    287                             _log.debug("Unknow DIRECTION parameter value: [" + dir + "]");
     287                            _log.debug("Unknown DIRECTION parameter value: [" + dir + "]");
    288288                        return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown DIRECTION parameter\"\n");
    289289                    }
  • apps/sam/java/src/net/i2p/sam/SAMv3Handler.java

    rfdb54c3 rc8606746  
    2626import java.util.StringTokenizer;
    2727
     28import net.i2p.I2PAppContext;
    2829import net.i2p.I2PException;
    2930import net.i2p.client.I2PClient;
     
    160161                                        outBuf.put(inBuf);
    161162                                        outBuf.flip();
    162                                         new I2PAppThread(new MessageDispatcher(outBuf.array()), "MessageDispatcher").start();
     163                                        // A new thread for every message is wildly inefficient...
     164                                        //new I2PAppThread(new MessageDispatcher(outBuf.array()), "MessageDispatcher").start();
     165                                        // inline
     166                                        // Even though we could be sending messages through multiple sessions,
     167                                        // that isn't a common use case, and blocking should be rare.
     168                                        // Inside router context, I2CP drops on overflow.
     169                                        (new MessageDispatcher(outBuf.array())).run();
    163170                                }
    164171                        }
     
    195202                                if (rec!=null) {
    196203                                        rec.getHandler().session.sendBytes(dest,data);
     204                                } else {
     205                                        Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMv3Handler.class);
     206                                        if (log.shouldLog(Log.WARN))
     207                                                log.warn("Dropping datagram, no session for " + nick);
    197208                                }
    198209                        } catch (Exception e) {
    199                                 // FIXME log? throw?
     210                                Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMv3Handler.class);
     211                                if (log.shouldLog(Log.WARN))
     212                                        log.warn("Error handling datagram", e);
    200213                        }
    201214                }
  • build.xml

    rfdb54c3 rc8606746  
    10951095    <target name="updater200" depends="prepupdate, preplicenses, pack200, zipit200" />
    10961096    <target name="updater200WithJetty" depends="prepjupdate, preplicenses, pack200, zipit200" />
     1097    <target name="updater200WithJettyAndGeoIP" depends="prepjupdate, prepgeoupdate, preplicenses, pack200, zipit200" />
    10971098    <target name="updater200WithJettyFixes" depends="prepjupdatefixes, preplicenses, pack200, zipit200" />
    10981099    <target name="updater200WithJettyFixesAndJbigi" depends="prepjupdatefixes, prepjbigiupdate, preplicenses, pack200, zipit200" />
     
    11061107    <target name="updaterWithJetty" depends="prepjupdate, preplicenses, zipit" />
    11071108    <target name="updaterWithJettyRepack" depends="prepjupdate, preplicenses, repack200, zipit" />
     1109    <target name="updaterWithJettyAndGeoIPRepack" depends="prepjupdate, prepgeoupdate, preplicenses, repack200, zipit" />
    11081110    <target name="updaterWithJettyFixes" depends="prepjupdatefixes, preplicenses, zipit" />
    11091111    <target name="updaterWithJettyFixesAndJbigi" depends="prepjupdatefixes, prepjbigiupdate, preplicenses, zipit" />
     
    12361238        <copy file="installer/resources/geoipv6.dat.gz" todir="pkg-temp/geoip/" />
    12371239        <copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
     1240      <!--
    12381241        <copy todir="pkg-temp/docs/icons/flags" >
    12391242          <fileset dir="installer/resources/icons/flags" />
    12401243        </copy>
     1244       -->
    12411245    </target>
    12421246
     
    15601564<!--
    15611565    <target name="release" depends="distclean, updaterWithJettyFixesAndJbigi , updater200WithJettyFixes, preppkg, installer, getReleaseNumber" >
     1566    <target name="release" depends="verifyReleaseBuildNumbers, -pre-release, distclean, testscripts, updaterWithJettyRepack, updater200WithJetty, delete-j6-update, installer-nowindows, delete-nonwindows, installer-windows">
    15621567-->
    1563     <target name="release" depends="verifyReleaseBuildNumbers, -pre-release, distclean, testscripts, updaterWithJettyRepack, updater200WithJetty, delete-j6-update, installer-nowindows, delete-nonwindows, installer-windows">
     1568    <target name="release" depends="verifyReleaseBuildNumbers, -pre-release, distclean, testscripts, updaterWithJettyAndGeoIPRepack, updater200WithJettyAndGeoIP, delete-j6-update, installer-nowindows, delete-nonwindows, installer-windows">
    15641569        <echo message="New version number is ${release.number}" />
    15651570        <copy file="i2pupdate.zip" tofile="i2pupdate_${release.number}.zip" />
  • core/java/src/net/i2p/I2PAppContext.java

    rfdb54c3 rc8606746  
    2323import net.i2p.crypto.SHA256Generator;
    2424import net.i2p.crypto.SessionKeyManager;
    25 import net.i2p.crypto.TransientSessionKeyManager;
    2625import net.i2p.data.Base64;
    2726import net.i2p.data.RoutingKeyGenerator;
     
    7776   
    7877    private StatManager _statManager;
    79     private SessionKeyManager _sessionKeyManager;
     78    protected SessionKeyManager _sessionKeyManager;
    8079    private NamingService _namingService;
    8180    private ElGamalEngine _elGamalEngine;
     
    9796    private final PortMapper _portMapper;
    9897    private volatile boolean _statManagerInitialized;
    99     private volatile boolean _sessionKeyManagerInitialized;
     98    protected volatile boolean _sessionKeyManagerInitialized;
    10099    private volatile boolean _namingServiceInitialized;
    101100    private volatile boolean _elGamalEngineInitialized;
     
    600599     * use RouterContext.clientManager.getClientSessionKeyManager(dest)
    601600     *
     601     * As of 0.9.15, this returns a dummy SessionKeyManager in I2PAppContext.
     602     * The dummy SKM does NOT handle session tags.
     603     * Overridden in RouterContext to return the full TransientSessionKeyManager.
    602604     */
    603605    public SessionKeyManager sessionKeyManager() {
     
    607609    }
    608610
    609     private void initializeSessionKeyManager() {
     611    protected void initializeSessionKeyManager() {
    610612        synchronized (_lock3) {
    611613            if (_sessionKeyManager == null)
    612614                //_sessionKeyManager = new PersistentSessionKeyManager(this);
    613                 _sessionKeyManager = new TransientSessionKeyManager(this);
     615                _sessionKeyManager = new SessionKeyManager(this);
    614616            _sessionKeyManagerInitialized = true;
    615617        }
  • core/java/src/net/i2p/client/I2PSessionImpl.java

    rfdb54c3 rc8606746  
    150150     *  @since 0.8.9
    151151     */
    152     private static final Map<Object, Destination> _lookupCache = new LHMCache<Object, Destination>(16);
     152    private static final Map<Object, Destination> _lookupCache = new LHMCache<Object, Destination>(64);
    153153    private static final String MIN_HOST_LOOKUP_VERSION = "0.9.11";
    154154    private static final boolean TEST_LOOKUP = false;
     
    10301030     *  Called by the message handler
    10311031     *  on reception of DestReplyMessage
     1032     *  @param d non-null
    10321033     */
    10331034    void destReceived(Destination d) {
     
    10381039        for (LookupWaiter w : _pendingLookups) {
    10391040            if (h.equals(w.hash)) {
    1040                 w.destination = d;
    10411041                synchronized (w) {
     1042                    w.destination = d;
    10421043                    w.notifyAll();
    10431044                }
     
    10491050     *  Called by the message handler
    10501051     *  on reception of DestReplyMessage
     1052     *  @param h non-null
    10511053     */
    10521054    void destLookupFailed(Hash h) {
     
    10631065     *  Called by the message handler
    10641066     *  on reception of HostReplyMessage
     1067     *  @param d non-null
    10651068     *  @since 0.9.11
    10661069     */
    10671070    void destReceived(long nonce, Destination d) {
    1068         // notify by hash
    1069         destReceived(d);
    1070         // notify by nonce
     1071        // notify by nonce and hash
     1072        Hash h = d.calculateHash();
    10711073        for (LookupWaiter w : _pendingLookups) {
    1072             if (nonce == w.nonce) {
    1073                 w.destination = d;
    1074                 if (w.name != null) {
    1075                     synchronized (_lookupCache) {
     1074            if (nonce == w.nonce || h.equals(w.hash)) {
     1075                synchronized (_lookupCache) {
     1076                    if (w.name != null)
    10761077                        _lookupCache.put(w.name, d);
    1077                     }
     1078                    _lookupCache.put(h, d);
    10781079                }
    10791080                synchronized (w) {
     1081                    w.destination = d;
    10801082                    w.notifyAll();
    10811083                }
     
    11181120        /** the request (nonce mode) */
    11191121        public final long nonce;
    1120         /** the reply */
    1121         public volatile Destination destination;
     1122        /** the reply; synch on this */
     1123        public Destination destination;
    11221124
    11231125        public LookupWaiter(Hash h) {
     
    11791181        }
    11801182        _pendingLookups.offer(waiter);
     1183        Destination rv = null;
    11811184        try {
    11821185            if (_routerSupportsHostLookup) {
     
    11951198                synchronized (waiter) {
    11961199                    waiter.wait(maxWait);
     1200                    rv = waiter.destination;
    11971201                }
    11981202            } catch (InterruptedException ie) {
     
    12021206            _pendingLookups.remove(waiter);
    12031207        }
    1204         return waiter.destination;
     1208        return rv;
    12051209    }
    12061210
     
    12701274        LookupWaiter waiter = new LookupWaiter(name, nonce);
    12711275        _pendingLookups.offer(waiter);
     1276        Destination rv = null;
    12721277        try {
    12731278            if (_log.shouldLog(Log.INFO))
     
    12801285                synchronized (waiter) {
    12811286                    waiter.wait(maxWait);
     1287                    rv = waiter.destination;
    12821288                }
    12831289            } catch (InterruptedException ie) {
     
    12871293            _pendingLookups.remove(waiter);
    12881294        }
    1289         return waiter.destination;
     1295        return rv;
    12901296    }
    12911297
  • core/java/src/net/i2p/crypto/SU3File.java

    rfdb54c3 rc8606746  
    1616import java.security.PrivateKey;
    1717import java.security.PublicKey;
     18import java.security.cert.CertificateFactory;
     19import java.security.cert.X509Certificate;
    1820import java.util.ArrayList;
    1921import java.util.Arrays;
     
    5052    private String _signer;
    5153    private int _signerLength;
     54    private int _fileType = -1;
    5255    private ContentType _contentType;
    5356    private long _contentLength;
     
    5558    private boolean _headerVerified;
    5659    private SigType _sigType;
    57 
    58     private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
     60    private boolean _verifySignature = true;
     61    private File _certFile;
     62
     63    public static final String MAGIC = "I2Psu3";
     64    private static final byte[] MAGIC_BYTES = DataHelper.getASCII(MAGIC);
    5965    private static final int FILE_VERSION = 0;
    6066    private static final int MIN_VERSION_BYTES = 16;
    6167    private static final int VERSION_OFFSET = 40; // Signature.SIGNATURE_BYTES; avoid early ctx init
    6268
    63     private static final int TYPE_ZIP = 0;
     69    public static final int TYPE_ZIP = 0;
     70    public static final int TYPE_XML = 1;
    6471
    6572    public static final int CONTENT_UNKNOWN = 0;
     
    6774    public static final int CONTENT_PLUGIN = 2;
    6875    public static final int CONTENT_RESEED = 3;
     76    public static final int CONTENT_NEWS = 4;
    6977
    7078    private enum ContentType {
     
    7280        ROUTER(CONTENT_ROUTER, "router"),
    7381        PLUGIN(CONTENT_PLUGIN, "plugin"),
    74         RESEED(CONTENT_RESEED, "reseed")
     82        RESEED(CONTENT_RESEED, "reseed"),
     83        NEWS(CONTENT_NEWS, "news")
    7584        ;
    7685
     
    126135    }
    127136
     137    /**
     138     *  Should the signature be verified? Default true
     139     *  @since 0.9.15
     140     */
     141    public void setVerifySignature(boolean shouldVerify) {
     142        _verifySignature = shouldVerify;
     143    }
     144
     145    /**
     146     *  Use this X.509 cert file for verification instead of $I2P/certificates/content_type/foo_at_mail.i2p
     147     *  @since 0.9.15
     148     */
     149    private void setPublicKeyCertificate(File certFile) {
     150        _certFile = certFile;
     151    }
     152
     153    /**
     154     *  This does not check the signature, but it will fail if the signer is unknown,
     155     *  unless setVerifySignature(false) has been called.
     156     */
    128157    public String getVersionString() throws IOException {
    129158        verifyHeader();
     
    131160    }
    132161
     162    /**
     163     *  This does not check the signature, but it will fail if the signer is unknown,
     164     *  unless setVerifySignature(false) has been called.
     165     */
    133166    public String getSignerString() throws IOException {
    134167        verifyHeader();
     
    137170
    138171    /**
     172     *  This does not check the signature, but it will fail if the signer is unknown,
     173     *  unless setVerifySignature(false) has been called.
     174     *
    139175     *  @return null if unknown
    140176     *  @since 0.9.9
     
    146182
    147183    /**
     184     *  This does not check the signature, but it will fail if the signer is unknown,
     185     *  unless setVerifySignature(false) has been called.
     186     *
    148187     *  @return -1 if unknown
    149188     *  @since 0.9.9
     
    155194
    156195    /**
     196     *  This does not check the signature, but it will fail if the signer is unknown,
     197     *  unless setVerifySignature(false) has been called.
     198     *
     199     *  @return -1 if unknown
     200     *  @since 0.9.15
     201     */
     202    public int getFileType() throws IOException {
     203        verifyHeader();
     204        return _fileType;
     205    }
     206
     207    /**
     208     *  This does not check the signature, but it will fail if the signer is unknown,
     209     *  unless setVerifySignature(false) has been called.
     210     *
    157211     *  Throws IOE if verify vails.
    158212     */
     
    177231     */
    178232    private void verifyHeader(InputStream in) throws IOException, DataFormatException {
    179         byte[] magic = new byte[MAGIC.length];
     233        byte[] magic = new byte[MAGIC_BYTES.length];
    180234        DataHelper.read(in, magic);
    181         if (!DataHelper.eq(magic, MAGIC))
     235        if (!DataHelper.eq(magic, MAGIC_BYTES))
    182236            throw new IOException("Not an su3 file");
    183237        skip(in, 1);
     
    205259            throw new IOException("bad content length");
    206260        skip(in, 1);
    207         foo = in.read();
    208         if (foo != TYPE_ZIP)
    209             throw new IOException("bad type");
     261        _fileType = in.read();
     262        if (_fileType != TYPE_ZIP && _fileType != TYPE_XML)
     263            throw new IOException("bad file type");
    210264        skip(in, 1);
    211265        int cType = in.read();
     
    232286        _signer = DataHelper.getUTF8(data);
    233287
    234         KeyRing ring = new DirKeyRing(new File(_context.getBaseDir(), "certificates"));
    235         try {
    236             _signerPubkey = ring.getKey(_signer, _contentType.getName(), _sigType);
    237         } catch (GeneralSecurityException gse) {
    238             IOException ioe = new IOException("keystore error");
    239             ioe.initCause(gse);
    240             throw ioe;
    241         }
    242 
    243         if (_signerPubkey == null)
    244             throw new IOException("unknown signer: " + _signer);
     288        if (_verifySignature) {
     289            if (_certFile != null) {
     290                _signerPubkey = loadKey(_certFile);
     291            } else {
     292                KeyRing ring = new DirKeyRing(new File(_context.getBaseDir(), "certificates"));
     293                try {
     294                    _signerPubkey = ring.getKey(_signer, _contentType.getName(), _sigType);
     295                } catch (GeneralSecurityException gse) {
     296                    IOException ioe = new IOException("keystore error");
     297                    ioe.initCause(gse);
     298                    throw ioe;
     299                }
     300                if (_signerPubkey == null)
     301                    throw new IOException("unknown signer: " + _signer + " for content type: " + _contentType.getName());
     302            }
     303        }
    245304        _headerVerified = true;
    246305    }
     
    288347            in.mark(10);
    289348            // following is a dup of that in verifyHeader()
    290             byte[] magic = new byte[MAGIC.length];
     349            byte[] magic = new byte[MAGIC_BYTES.length];
    291350            DataHelper.read(in, magic);
    292             if (!DataHelper.eq(magic, MAGIC))
     351            if (!DataHelper.eq(magic, MAGIC_BYTES))
    293352                throw new IOException("Not an su3 file");
    294353            skip(in, 1);
     
    311370            else
    312371                skip(in, getContentOffset());
    313             if (_signerPubkey == null)
    314                 throw new IOException("unknown signer: " + _signer);
     372            if (_verifySignature) {
     373                if (_signerPubkey == null)
     374                    throw new IOException("unknown signer: " + _signer + " for content type: " + _contentType.getName());
     375            }
    315376            if (migrateTo != null)  // else verify only
    316377                out = new FileOutputStream(migrateTo);
     
    325386                tot += read;
    326387            }
    327             byte[] sha = md.digest();
    328             din.on(false);
    329             Signature signature = new Signature(_sigType);
    330             signature.readBytes(in);
    331             SimpleDataStructure hash = _sigType.getHashInstance();
    332             hash.setData(sha);
    333             //System.out.println("hash\n" + HexDump.dump(sha));
    334             //System.out.println("sig\n" + HexDump.dump(signature.getData()));
    335             rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
     388            if (_verifySignature) {
     389                byte[] sha = md.digest();
     390                din.on(false);
     391                Signature signature = new Signature(_sigType);
     392                signature.readBytes(in);
     393                SimpleDataStructure hash = _sigType.getHashInstance();
     394                hash.setData(sha);
     395                //System.out.println("hash\n" + HexDump.dump(sha));
     396                //System.out.println("sig\n" + HexDump.dump(signature.getData()));
     397                rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
     398            } else {
     399                rv = true;
     400            }
    336401        } catch (DataFormatException dfe) {
    337402            IOException ioe = new IOException("foo");
     
    353418     *
    354419     *  @param content the input file, probably in zip format
    355      *  @param contentType 0-255, 0 for zip
     420     *  @param fileType 0-255, 0 for zip
     421     *  @param contentType 0-255
    356422     *  @param version 1-255 bytes when converted to UTF-8
    357423     *  @param signer ID of the public key, 1-255 bytes when converted to UTF-8
    358424     */
    359     public void write(File content, int contentType, String version,
     425    public void write(File content, int fileType, int contentType, String version,
    360426                      String signer, PrivateKey privkey, SigType sigType) throws IOException {
    361427        InputStream in = null;
     
    366432            MessageDigest md = sigType.getDigestInstance();
    367433            out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md);
    368             out.write(MAGIC);
     434            out.write(MAGIC_BYTES);
    369435            out.write((byte) 0);
    370436            out.write((byte) FILE_VERSION);
     
    387453            DataHelper.writeLong(out, 8, contentLength);
    388454            out.write((byte) 0);
    389             out.write((byte) TYPE_ZIP);
     455            if (fileType < 0 || fileType > 255)
     456                throw new IllegalArgumentException("bad content type");
     457            out.write((byte) fileType);
    390458            out.write((byte) 0);
    391459            if (contentType < 0 || contentType > 255)
     
    444512            String stype = null;
    445513            String ctype = null;
     514            String ftype = null;
     515            String kfile = null;
    446516            boolean error = false;
    447             Getopt g = new Getopt("SU3File", args, "t:c:");
     517            boolean shouldVerify = true;
     518            Getopt g = new Getopt("SU3File", args, "t:c:f:k:x");
    448519            int c;
    449520            while ((c = g.getopt()) != -1) {
     
    455526                case 'c':
    456527                    ctype = g.getOptarg();
     528                    break;
     529
     530                case 'f':
     531                    ftype = g.getOptarg();
     532                    break;
     533
     534                case 'k':
     535                    kfile = g.getOptarg();
     536                    break;
     537
     538                case 'x':
     539                    shouldVerify = false;
    457540                    break;
    458541
     
    477560                props.setProperty("prng.bufferSize", "16384");
    478561                new I2PAppContext(props);
    479                 ok = signCLI(stype, ctype, a.get(0), a.get(1), a.get(2), a.get(3), a.get(4), "");
     562                ok = signCLI(stype, ctype, ftype, a.get(0), a.get(1), a.get(2), a.get(3), a.get(4), "");
    480563            } else if ("bulksign".equals(cmd)) {
    481564                Properties props = new Properties();
     
    484567                ok = bulkSignCLI(stype, ctype, a.get(0), a.get(1), a.get(2), a.get(3));
    485568            } else if ("verifysig".equals(cmd)) {
    486                 ok = verifySigCLI(a.get(0));
     569                ok = verifySigCLI(a.get(0), kfile);
    487570            } else if ("keygen".equals(cmd)) {
    488571                ok = genKeysCLI(stype, a.get(0), a.get(1), a.get(2));
    489572            } else if ("extract".equals(cmd)) {
    490                 ok = extractCLI(a.get(0), a.get(1));
     573                ok = extractCLI(a.get(0), a.get(1), shouldVerify);
    491574            } else {
    492575                showUsageCLI();
     
    503586    private static final void showUsageCLI() {
    504587        System.err.println("Usage: SU3File keygen       [-t type|code] publicKeyFile keystore.ks you@mail.i2p");
    505         System.err.println("       SU3File sign         [-c type|code] [-t type|code] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p");
    506         System.err.println("       SU3File bulksign     [-c type|code] [-t type|code] directory keystore.ks version you@mail.i2p");
     588        System.err.println("       SU3File sign         [-t type|code] [-c type|code] [-f type|code] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p");
     589        System.err.println("       SU3File bulksign     [-t type|code] [-c type|code] directory keystore.ks version you@mail.i2p");
    507590        System.err.println("       SU3File showversion  signedFile.su3");
    508         System.err.println("       SU3File verifysig    signedFile.su3");
    509         System.err.println("       SU3File extract      signedFile.su3 outFile.zip");
     591        System.err.println("       SU3File verifysig    [-k file.crt] signedFile.su3  ## -k use this pubkey cert for verification");
     592        System.err.println("       SU3File extract      [-x] [-k file.crt] signedFile.su3 outFile   ## -x don't check sig");
    510593        System.err.println(dumpTypes());
    511594    }
     
    534617            buf.append('\n');
    535618        }
     619        buf.append("Available file types (-f):\n");
     620        buf.append("      ZIP\t(code: 0) DEFAULT\n");
     621        buf.append("      XML\t(code: 1)\n");
    536622        return buf.toString();
    537623    }
     
    559645        try {
    560646            SU3File file = new SU3File(signedFile);
     647            file.setVerifySignature(false);
    561648            String versionString = file.getVersionString();
    562649            if (versionString.equals(""))
     
    579666
    580667    /**
     668     *  Zip only
    581669     *  @return success
    582670     *  @since 0.9.9
     
    598686            while (keypw.length() < 6) {
    599687                System.out.print("Enter password for key \"" + signerName + "\": ");
    600                 keypw = DataHelper.readLine(System.in).trim();
     688                keypw = DataHelper.readLine(System.in);
     689                if (keypw == null) {
     690                    System.out.println("\nEOF reading password");
     691                    return false;
     692                }
     693                keypw = keypw.trim();
    601694                if (keypw.length() > 0 && keypw.length() < 6)
    602695                    System.out.println("Key password must be at least 6 characters");
     
    613706                continue;
    614707            String signedFile = inputFile.substring(0, inputFile.length() - 4) + ".su3";
    615             boolean rv = signCLI(stype, ctype, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
     708            boolean rv = signCLI(stype, ctype, null, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
    616709            if (!rv)
    617710                return false;
     
    627720     *  @since 0.9.9
    628721     */
    629     private static final boolean signCLI(String stype, String ctype, String inputFile, String signedFile,
     722    private static final boolean signCLI(String stype, String ctype, String ftype, String inputFile, String signedFile,
    630723                                         String privateKeyFile, String version, String signerName, String keypw) {
    631724        SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
     
    639732            return false;
    640733        }
    641         return signCLI(type, ct, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
     734        int ft = TYPE_ZIP;
     735        if (ftype != null) {
     736            if (ftype.equalsIgnoreCase("ZIP")) {
     737                ft = TYPE_ZIP;
     738            } else if (ftype.equalsIgnoreCase("XML")) {
     739                ft = TYPE_XML;
     740            } else {
     741                try {
     742                    ft = Integer.parseInt(ftype);
     743                } catch (NumberFormatException nfe) {
     744                    ft = -1;
     745                }
     746                if (ft != TYPE_ZIP && ft != TYPE_XML) {
     747                    System.out.println("File type " + ftype + " is not supported");
     748                    return false;
     749                }
     750            }
     751        }
     752        return signCLI(type, ct, ft, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
    642753    }
    643754
     
    646757     *  @since 0.9.9
    647758     */
    648     private static final boolean signCLI(SigType type, ContentType ctype, String inputFile, String signedFile,
     759    private static final boolean signCLI(SigType type, ContentType ctype, int ftype, String inputFile, String signedFile,
    649760                                         String privateKeyFile, String version, String signerName, String keypw) {
    650761        try {
    651762            while (keypw.length() < 6) {
    652763                System.out.print("Enter password for key \"" + signerName + "\": ");
    653                 keypw = DataHelper.readLine(System.in).trim();
     764                keypw = DataHelper.readLine(System.in);
     765                if (keypw == null) {
     766                    System.out.println("\nEOF reading password");
     767                    return false;
     768                }
     769                keypw = keypw.trim();
    654770                if (keypw.length() > 0 && keypw.length() < 6)
    655771                    System.out.println("Key password must be at least 6 characters");
     
    662778            }
    663779            SU3File file = new SU3File(signedFile);
    664             file.write(new File(inputFile), ctype.getCode(), version, signerName, pk, type);
     780            file.write(new File(inputFile), ftype, ctype.getCode(), version, signerName, pk, type);
    665781            System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
    666782            return true;
     
    677793
    678794    /** @return valid */
    679     private static final boolean verifySigCLI(String signedFile) {
     795    private static final boolean verifySigCLI(String signedFile, String pkFile) {
    680796        InputStream in = null;
    681797        try {
    682798            SU3File file = new SU3File(signedFile);
     799            if (pkFile != null)
     800                file.setPublicKeyCertificate(new File(pkFile));
    683801            boolean isValidSignature = file.verify();
    684802            if (isValidSignature)
     
    698816     *  @since 0.9.9
    699817     */
    700     private static final boolean extractCLI(String signedFile, String outFile) {
     818    private static final boolean extractCLI(String signedFile, String outFile, boolean verifySig) {
    701819        InputStream in = null;
    702820        try {
    703821            SU3File file = new SU3File(signedFile);
     822            file.setVerifySignature(verifySig);
    704823            File out = new File(outFile);
    705824            boolean ok = file.verifyAndMigrate(out);
     
    745864            while (alias.length() == 0) {
    746865                System.out.print("Enter key name (example@mail.i2p): ");
    747                 alias = DataHelper.readLine(System.in).trim();
     866                alias = DataHelper.readLine(System.in);
     867                if (alias == null) {
     868                    System.out.println("\nEOF reading key name");
     869                    return false;
     870                }
     871                alias = alias.trim();
    748872            }
    749873            while (keypw.length() < 6) {
    750874                System.out.print("Enter new key password: ");
    751                 keypw = DataHelper.readLine(System.in).trim();
     875                keypw = DataHelper.readLine(System.in);
     876                if (keypw == null) {
     877                    System.out.println("\nEOF reading password");
     878                    return false;
     879                }
     880                keypw = keypw.trim();
    752881                if (keypw.length() > 0 && keypw.length() < 6)
    753882                    System.out.println("Key password must be at least 6 characters");
     
    777906        return true;
    778907    }
     908
     909    /**
     910     *  For the -k CLI option
     911     *  @return non-null, throws IOE on all errors
     912     *  @since 0.9.15
     913     */
     914    private static PublicKey loadKey(File kd) throws IOException {
     915        InputStream fis = null;
     916        try {
     917            fis = new FileInputStream(kd);
     918            CertificateFactory cf = CertificateFactory.getInstance("X.509");
     919            X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
     920            cert.checkValidity();
     921            return cert.getPublicKey();
     922        } catch (GeneralSecurityException gse) {
     923            IOException ioe = new IOException("cert error");
     924            ioe.initCause(gse);
     925            throw ioe;
     926        } finally {
     927            try { if (fis != null) fis.close(); } catch (IOException foo) {}
     928        }
     929    }
    779930}
  • core/java/src/net/i2p/crypto/SigType.java

    rfdb54c3 rc8606746  
    182182    }
    183183
     184    /**
     185     *  @return true if supported in this JVM
     186     *  @since 0.9.15
     187     */
     188    public static boolean isAvailable(int code) {
     189        SigType type = getByCode(code);
     190        if (type == null)
     191            return false;
     192        return type.isAvailable();
     193    }
     194
     195    /**
     196     *  @param stype number or name
     197     *  @return true if supported in this JVM
     198     *  @since 0.9.15
     199     */
     200    public static boolean isAvailable(String stype) {
     201        SigType type = parseSigType(stype);
     202        if (type == null)
     203            return false;
     204        return type.isAvailable();
     205    }
     206
    184207    private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
    185208
  • core/java/src/net/i2p/data/Base32.java

    rfdb54c3 rc8606746  
    5757
    5858    private final static byte BAD_ENCODING = -9; // Indicates error in encoding
     59
    5960    /** Defeats instantiation. */
    6061    private Base32() { // nop
     
    137138
    138139    /**
     140     *  Returns lower case.
     141     *  Does not add trailing '='.
     142     *
    139143     *  @param source if null will return ""
    140144     */
     
    144148
    145149    /**
     150     * Returns lower case.
     151     * Does not add trailing '='.
     152     *
    146153     * @param source The data to convert non-null
    147154     */
     
    183190     * Decodes data from Base32 notation and
    184191     * returns it as a string.
     192     * Case-insensitive.
     193     * Does not allow trailing '='.
    185194     *
    186195     * @param s the string to decode, if null returns null
     
    195204
    196205    /**
     206     * Case-insensitive.
     207     * Does not allow trailing '='.
     208     *
    197209     * @param s non-null
    198210     * @return decoded data, null on error
  • core/java/src/net/i2p/data/KeysAndCert.java

    rfdb54c3 rc8606746  
    131131    }
    132132   
    133     /** the public key has enough randomness in it to use it by itself for speed */
     133    /** the signing key has enough randomness in it to use it by itself for speed */
    134134    @Override
    135135    public int hashCode() {
    136         if (_publicKey == null)
     136        // don't use public key, some app devs thinking of using
     137        // an all-zeros or leading-zeros public key for destinations
     138        if (_signingKey == null)
    137139            return 0;
    138         return _publicKey.hashCode();
     140        return _signingKey.hashCode();
    139141    }
    140142   
    141143    @Override
    142144    public String toString() {
    143         StringBuilder buf = new StringBuilder(64);
     145        StringBuilder buf = new StringBuilder(256);
    144146        buf.append('[').append(getClass().getSimpleName()).append(": ");
    145147        buf.append("\n\tHash: ").append(getHash().toBase64());
  • core/java/test/junit/net/i2p/crypto/CryptoTestSuite.java

    rfdb54c3 rc8606746  
    2929        suite.addTestSuite(HMACSHA256Test.class);
    3030        suite.addTestSuite(KeyGeneratorTest.class);
    31         suite.addTestSuite(SessionEncryptionTest.class);
    3231        suite.addTestSuite(SHA1HashTest.class);
    3332        suite.addTestSuite(SHA256Test.class);
  • history.txt

    rfdb54c3 rc8606746  
     12014-08-19 zzz
     2 * i2psnark:
     3   - Don't filter create torrent form, and
     4     fix exception on ':' in file names (ticket #1342)
     5   - Don't remap file names on torrents we created, and
     6     save remap setting in torrent config file (tickets #571, 771)
     7   - Escaping fixes since names may not be remapped
     8   - Use better encodePath() from Jetty
     9   - Don't say create torrent succeeded when it didn't
     10   - Add more sanity checks for base path of created torrent
     11
     122014-08-18 zzz
     13 * i2psnark:
     14   - Don't send HTML-only headers for icons
     15   - Catch IllegalStateException for icons
     16 * Tunnels: Use consistent tunnel pair for Delivery Status Message
     17   to reduce network connections (ticket #1350)
     18
     192014-08-15 zzz
     20 * Console: Escaping fix (ticket #1348)
     21 * I2CP: Lookup synch cleanups
     22
     232014-08-13 dg
     24 * Console, EventLog: add BECAME_FLOODFILL and NOT_FLOODFILL, they are now shown
     25   at the /events page when the router changes its floodfill state
     26
     272014-08-13 zzz
     28 * Console: Escaping fix (ticket #1346)
     29 * i2psnark: Fix add torrent NPE
     30
     312014-08-10 zzz
     32Prop from i2p.i2p.zzz.snarkconfig:
     33 * i2psnark:
     34   - Move config file and DHT persistence file to a config dir
     35   - Move per-torrent configuration from "zmeta" in the main config file
     36     to a per-torrent config file (ticket #1132)
     37   - Split timestamp and bitfield into separate configs
     38   - Fix misspelling of autoStart config
     39   - Remove two unused SnarkManager methods
     40   - Refactor file deletion in prep for better file name handling (ticket #571)
     41   - Don't use canonical files in directory listings,
     42     for speed and to avoid file comparison problems (tickets #1079, #1148)
     43   - Set base file/dir in Storage constructor, make final,
     44     in prep for arbitrary locations (ticket #1028)
     45   - Add missing nonce protection for file priority setting
     46   - Add torrent mime type
     47
     482014-08-10 zzz
     49Prop from i2p.i2p.zzz.test2:
     50 * Console: Show share options below 30% (ticket #1329)
     51 * Crypto: Move TransientSessionKeyManager from core to router.
     52   I2PAppContext will return the dummy SessionKeyManager which
     53   is sufficient for non-tag uses (e.g. Bote).
     54   Client use of end-to-end encryption using SessionTags was
     55   disabled in release 0.6, 2005-07-27.
     56 * KeysAndCert: Change hashcode to prevent possible collisions
     57   caused by apps with zeroed pubkey
     58 * Plugins: Start of SU3 support (ticket #1227)
     59 * SAM:
     60   - Don't spawn a thread for each transmitted datagram
     61   - Set protocol field for raw and signed datagrams
     62   - Enforce a 60s timeout for HELLO
     63   - Use naming service cache to reduce Destination object churn
     64   - Get Log object from the log manager
     65 * SigType: Add static isAvailable() methods
     66 * SSU: Speed up introductions by responding to HolePunch (ticket #1333)
     67 * SU3File:
     68   - Add support for XML and NEWS types
     69   - Fix NPE on EOF reading input
     70   - Add -x option to bypass signature verification
     71   - Add -k option to use specified private key cert for verification
     72   - Don't verify signature in showversion
     73 * UPnP: Disable external entities in XML parser
     74
     752014-08-10 zzz
     76 * Console: Hide client delete buttons unless advanced on /configclients
     77
    178* 2014-08-09 0.9.14.1 released
    279
  • router/java/src/net/i2p/router/RouterContext.java

    rfdb54c3 rc8606746  
    1414import net.i2p.internal.InternalClientManager;
    1515import net.i2p.router.client.ClientManagerFacadeImpl;
     16import net.i2p.router.crypto.TransientSessionKeyManager;
    1617import net.i2p.router.dummy.*;
    1718import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
     
    6869    // split up big lock on this to avoid deadlocks
    6970    private volatile boolean _initialized;
    70     private final Object _lock1 = new Object(), _lock2 = new Object();
     71    private final Object _lock1 = new Object(), _lock2 = new Object(), _lock3 = new Object();
    7172
    7273    private static final List<RouterContext> _contexts = new CopyOnWriteArrayList<RouterContext>();
     
    566567        return _appManager;
    567568    }
     569
     570    /**
     571     *  As of 0.9.15, this returns a dummy SessionKeyManager in I2PAppContext.
     572     *  Overridden in RouterContext to return the full TransientSessionKeyManager.
     573     *
     574     *  @since 0.9.15
     575     */
     576    @Override
     577    protected void initializeSessionKeyManager() {
     578        synchronized (_lock3) {
     579            if (_sessionKeyManager == null)
     580                //_sessionKeyManager = new PersistentSessionKeyManager(this);
     581                _sessionKeyManager = new TransientSessionKeyManager(this);
     582            _sessionKeyManagerInitialized = true;
     583        }
     584    }
    568585}
  • router/java/src/net/i2p/router/RouterVersion.java

    rfdb54c3 rc8606746  
    1919    public final static String ID = "Monotone";
    2020    public final static String VERSION = CoreVersion.VERSION;
    21     public final static long BUILD = 0;
     21    public final static long BUILD = 7;
    2222
    2323    /** for example "-test" */
  • router/java/src/net/i2p/router/client/ClientConnectionRunner.java

    rfdb54c3 rc8606746  
    2626import net.i2p.client.I2PClient;
    2727import net.i2p.crypto.SessionKeyManager;
    28 import net.i2p.crypto.TransientSessionKeyManager;
    2928import net.i2p.data.Destination;
    3029import net.i2p.data.Hash;
     
    4443import net.i2p.router.JobImpl;
    4544import net.i2p.router.RouterContext;
     45import net.i2p.router.crypto.TransientSessionKeyManager;
    4646import net.i2p.util.ConcurrentHashSet;
    4747import net.i2p.util.I2PThread;
  • router/java/src/net/i2p/router/client/ClientMessageEventListener.java

    rfdb54c3 rc8606746  
    1212
    1313import net.i2p.CoreVersion;
     14import net.i2p.crypto.SigType;
    1415import net.i2p.data.Hash;
    1516import net.i2p.data.Payload;
     
    196197                _log.debug("Signature verified correctly on create session message");
    197198        } else {
    198             if (_log.shouldLog(Log.ERROR))
    199                 _log.error("Signature verification *FAILED* on a create session message.  Hijack attempt?");
    200199            // For now, we do NOT send a SessionStatusMessage - see javadoc above
    201             _runner.disconnectClient("Invalid signature on CreateSessionMessage");
     200            int itype = in.getDestination().getCertificate().getCertificateType();
     201            SigType stype = SigType.getByCode(itype);
     202            if (stype == null || !stype.isAvailable()) {
     203                _log.error("Client requested unsupported signature type " + itype);
     204                _runner.disconnectClient("Unsupported signature type " + itype);
     205            } else {
     206                _log.error("Signature verification failed on a create session message");
     207                _runner.disconnectClient("Invalid signature on CreateSessionMessage");
     208            }
    202209            return;
    203210        }
  • router/java/src/net/i2p/router/crypto/TransientSessionKeyManager.java

    rfdb54c3 rc8606746  
    1 package net.i2p.crypto;
     1package net.i2p.router.crypto;
    22
    33/*
     
    2626
    2727import net.i2p.I2PAppContext;
     28import net.i2p.crypto.SessionKeyManager;
     29import net.i2p.crypto.TagSetHandle;
    2830import net.i2p.data.DataHelper;
    2931import net.i2p.data.PublicKey;
  • router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java

    rfdb54c3 rc8606746  
    730730     */
    731731    private TunnelInfo selectInboundTunnel() {
    732         return getContext().tunnelManager().selectInboundTunnel(_from.calculateHash());
     732        // Use tunnel EP closest to his hash, as a simple cache to minimize connections
     733        return getContext().tunnelManager().selectInboundTunnel(_from.calculateHash(), _to.calculateHash());
    733734    }
    734735   
  • router/java/src/net/i2p/router/networkdb/kademlia/FloodfillMonitorJob.java

    rfdb54c3 rc8606746  
    1010import net.i2p.router.RouterContext;
    1111import net.i2p.router.peermanager.PeerProfile;
     12import net.i2p.router.util.EventLog;
    1213import net.i2p.util.Log;
    1314
     
    4344        boolean ff = shouldBeFloodfill();
    4445        _facade.setFloodfillEnabled(ff);
    45         if (ff != wasFF)
     46        if (ff != wasFF) {
     47                        if(ff) {
     48                                getContext().router().eventLog().addEvent(EventLog.BECAME_FLOODFILL);
     49                        } else {
     50                                getContext().router().eventLog().addEvent(EventLog.NOT_FLOODFILL);
     51                        }
    4652            getContext().router().rebuildRouterInfo();
     53        }
    4754        if (_log.shouldLog(Log.INFO))
    4855            _log.info("Should we be floodfill? " + ff);
  • router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java

    rfdb54c3 rc8606746  
    969969
    970970    /**
     971     *  Called from UDPReceiver.
     972     *  Accelerate response to RelayResponse if we haven't sent it yet.
     973     *
     974     *  @since 0.9.15
     975     */
     976    void receiveHolePunch(InetAddress from, int fromPort) {
     977        RemoteHostId id = new RemoteHostId(from.getAddress(), fromPort);
     978        OutboundEstablishState state = _outboundStates.get(id);
     979        if (state != null) {
     980            boolean sendNow = state.receiveHolePunch();
     981            if (sendNow) {
     982                if (_log.shouldLog(Log.WARN))
     983                    _log.warn("Hole punch from " + state + ", sending SessionRequest now");
     984                notifyActivity();
     985            } else {
     986                if (_log.shouldLog(Log.WARN))
     987                    _log.warn("Hole punch from " + state + ", already sent SessionRequest");
     988            }
     989        } else {
     990            // HolePunch received before RelayResponse, and we didn't know the IP/port, or it changed
     991            if (_log.shouldLog(Log.WARN))
     992                _log.warn("No state found for hole punch from " + from + " port " + fromPort);
     993        }
     994    }
     995
     996    /**
    971997     *  Are IP and port valid? This is only for checking the relay response.
    972998     *  Reject all IPv6, for now, even if we are configured for it.
  • router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java

    rfdb54c3 rc8606746  
    100100    private static final long MAX_DELAY = 15*1000;
    101101
     102    private static final long WAIT_FOR_HOLE_PUNCH_DELAY = 500;
     103
    102104    /**
    103105     *  @param claimedAddress an IP/port based RemoteHostId, or null if unknown
     
    557559        if (_currentState != OutboundState.OB_STATE_PENDING_INTRO)
    558560            return; // we've already successfully been introduced, so don't overwrite old settings
    559         _nextSend = _context.clock().now() + 500; // wait briefly for the hole punching
     561        _nextSend = _context.clock().now() + WAIT_FOR_HOLE_PUNCH_DELAY; // wait briefly for the hole punching
    560562        _currentState = OutboundState.OB_STATE_INTRODUCED;
    561563        if (_claimedAddress != null && bobPort == _bobPort && DataHelper.eq(bobIP, _bobIP)) {
     
    571573            _log.info("Introduced to " + _remoteHostId + ", now lets get on with establishing");
    572574    }
     575
     576    /**
     577     *  Accelerate response to RelayResponse if we haven't sent it yet.
     578     *
     579     *  @return true if we should send the SessionRequest now
     580     *  @since 0.9.15
     581     */
     582    synchronized boolean receiveHolePunch() {
     583        if (_currentState != OutboundState.OB_STATE_INTRODUCED)
     584            return false;
     585        if (_requestSentCount > 0)
     586            return false;
     587        long now = _context.clock().now();
     588        if (_log.shouldLog(Log.WARN))
     589            _log.warn(toString() + " accelerating SessionRequest by " + (_nextSend - now) + " ms");
     590        _nextSend = now;
     591        return true;
     592    }
    573593   
    574594    /** how long have we been trying to establish this session? */
  • router/java/src/net/i2p/router/transport/udp/UDPReceiver.java

    rfdb54c3 rc8606746  
    22
    33import java.io.IOException;
     4import java.net.DatagramPacket;
    45import java.net.DatagramSocket;
    56import java.util.Arrays;
     
    221222                //}
    222223                UDPPacket packet = UDPPacket.acquire(_context, true);
     224                DatagramPacket dpacket = packet.getPacket();
    223225
    224226                // Android ICS bug
    225227                // http://code.google.com/p/android/issues/detail?id=24748
    226228                if (_isAndroid)
    227                     packet.getPacket().setLength(UDPPacket.MAX_PACKET_SIZE);
     229                    dpacket.setLength(UDPPacket.MAX_PACKET_SIZE);
    228230               
    229231                // block before we read...
     
    237239                    //    _log.info("Before blocking socket.receive on " + System.identityHashCode(packet));
    238240                    //synchronized (Runner.this) {
    239                         _socket.receive(packet.getPacket());
     241                        _socket.receive(dpacket);
    240242                    //}
    241                     int size = packet.getPacket().getLength();
     243                    int size = dpacket.getLength();
    242244                    if (_log.shouldLog(Log.INFO))
    243245                        _log.info("After blocking socket.receive: packet is " + size + " bytes on " + System.identityHashCode(packet));
     
    267269                        // nat hole punch packets are 0 bytes
    268270                        if (_log.shouldLog(Log.INFO))
    269                             _log.info("Received a 0 byte udp packet from " + packet.getPacket().getAddress() + ":" + packet.getPacket().getPort());
     271                            _log.info("Received a 0 byte udp packet from " + dpacket.getAddress() + ":" + dpacket.getPort());
     272                        _transport.getEstablisher().receiveHolePunch(dpacket.getAddress(), dpacket.getPort());
    270273                        packet.release();
    271274                    }
  • router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java

    rfdb54c3 rc8606746  
    161161            // ok, they want us to send it remotely, but that'd bust our anonymity,
    162162            // so we send it out a tunnel first
    163             TunnelInfo out = _context.tunnelManager().selectOutboundTunnel(_client);
     163            // TODO use the OCMOSJ cache to pick OB tunnel we are already using?
     164            TunnelInfo out = _context.tunnelManager().selectOutboundTunnel(_client, target);
    164165            if (out == null) {
    165166                if (_log.shouldLog(Log.WARN))
  • router/java/src/net/i2p/router/util/EventLog.java

    rfdb54c3 rc8606746  
    3333    /** for convenience, not required */
    3434    public static final String ABORTED = "aborted";
     35    public static final String BECAME_FLOODFILL = "becameFloodfill";
    3536    public static final String CHANGE_IP = "changeIP";
    3637    public static final String CHANGE_PORT = "changePort";
     
    3940    public static final String CRITICAL = "critical";
    4041    public static final String INSTALLED = "installed";
    41     public static final String INSTALL_FAILED = "intallFailed";
     42    public static final String INSTALL_FAILED = "installFailed";
    4243    public static final String NETWORK = "network";
    4344    public static final String NEW_IDENT = "newIdent";
     45    public static final String NOT_FLOODFILL = "disabledFloodfill";
    4446    public static final String OOM = "oom";
    4547    public static final String REKEYED = "rekeyed";
  • router/java/src/org/cybergarage/xml/parser/JaxpParser.java

    rfdb54c3 rc8606746  
    2222package org.cybergarage.xml.parser;
    2323
     24import java.io.ByteArrayInputStream;
    2425import java.io.FilterInputStream;
    2526import java.io.IOException;
     
    2829import javax.xml.parsers.DocumentBuilder;
    2930import javax.xml.parsers.DocumentBuilderFactory;
     31import javax.xml.parsers.ParserConfigurationException;
    3032
    3133import org.cybergarage.xml.Node;
     
    3436import org.w3c.dom.Document;
    3537import org.w3c.dom.NamedNodeMap;
     38import org.xml.sax.EntityResolver;
    3639import org.xml.sax.InputSource;
    3740
     
    117120               
    118121                try {
     122                        // https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing
    119123                        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
     124                        factory.setValidating(false);
     125                        factory.setNamespaceAware(true);
     126                        factory.setExpandEntityReferences(false);
     127                        try {
     128                            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
     129                        } catch (ParserConfigurationException pce) {}
     130                        try {
     131                            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
     132                        } catch (ParserConfigurationException pce) {}
     133                        try {
     134                            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
     135                        } catch (ParserConfigurationException pce) {}
     136                        try {
     137                            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
     138                        } catch (ParserConfigurationException pce) {}
    120139                        DocumentBuilder builder = factory.newDocumentBuilder();
     140                        builder.setEntityResolver(new BlankingResolver());
    121141                        InputSource inSrc = new InputSource(new NullFilterInputStream(inStream));
    122142                        Document doc = builder.parse(inSrc);
     
    164184                }
    165185        }
     186
     187        /**
     188         *  I2P -
     189         *  http://stackoverflow.com/questions/5883542/disable-xml-validation-based-on-external-dtd-xsd
     190         */
     191        private static class BlankingResolver implements EntityResolver {
     192                private static final byte[] DUMMY = new byte[0];
     193
     194                public InputSource resolveEntity(String arg0, String arg1) {
     195                        return new InputSource(new ByteArrayInputStream(DUMMY));
     196                }
     197        }
    166198}
  • router/java/test/junit/net/i2p/router/crypto/SessionEncryptionTest.java

    rfdb54c3 rc8606746  
    1 package net.i2p.crypto;
     1package net.i2p.router.crypto;
    22/*
    33 * free (adj.): unencumbered; not under the control of others
     
    1414import junit.framework.TestCase;
    1515import net.i2p.I2PAppContext;
     16import net.i2p.crypto.KeyGenerator;
     17import net.i2p.crypto.SessionKeyManager;
     18import net.i2p.crypto.TagSetHandle;
    1619import net.i2p.data.DataHelper;
    1720import net.i2p.data.PrivateKey;
     
    2629 */
    2730public class SessionEncryptionTest extends TestCase{
    28     private static I2PAppContext _context = I2PAppContext.getGlobalContext();
     31    private I2PAppContext _context;
    2932   
    3033    protected void setUp(){
    31         _context = new I2PAppContext();
     34        _context = I2PAppContext.getGlobalContext();
    3235    }
    3336   
     
    4144        PublicKey pubKey = (PublicKey)keys[0];
    4245        PrivateKey privKey = (PrivateKey)keys[1];
    43         SessionKey curKey = _context.sessionKeyManager().createSession(pubKey);
     46        SessionKeyManager skm = new TransientSessionKeyManager(_context);
     47        SessionKey curKey = skm.createSession(pubKey);
    4448       
    4549        byte[] msg = "msg 1".getBytes();
    4650       
    4751        byte emsg[] = _context.elGamalAESEngine().encrypt(msg, pubKey, curKey, 64);
    48         byte dmsg[] = _context.elGamalAESEngine().decrypt(emsg, privKey);
     52        byte dmsg[] = _context.elGamalAESEngine().decrypt(emsg, privKey, skm);
    4953        assertTrue(DataHelper.eq(dmsg, msg));
    5054    }
     
    5458        PublicKey pubKey = (PublicKey)keys[0];
    5559        PrivateKey privKey = (PrivateKey)keys[1];
    56         SessionKey curKey = _context.sessionKeyManager().createSession(pubKey);
     60        SessionKeyManager skm = new TransientSessionKeyManager(_context);
     61        SessionKey curKey = skm.createSession(pubKey);
    5762       
    5863        byte[] msg = "msg 2".getBytes();
    5964       
    6065        byte emsg[] = _context.elGamalAESEngine().encrypt(msg, pubKey, curKey, 64);
    61         byte dmsg[] = _context.elGamalAESEngine().decrypt(emsg, privKey);
     66        byte dmsg[] = _context.elGamalAESEngine().decrypt(emsg, privKey, skm);
    6267        assertTrue(DataHelper.eq(dmsg, msg));
    6368    }
     
    7580        PublicKey pubKey = (PublicKey)keys[0];
    7681        PrivateKey privKey = (PrivateKey)keys[1];
    77         SessionKey curKey = _context.sessionKeyManager().createSession(pubKey);
     82        SessionKeyManager skm = new TransientSessionKeyManager(_context);
     83        SessionKey curKey = skm.createSession(pubKey);
    7884       
    7985        SessionTag tag1 = new SessionTag(true);
     
    98104        byte emsg1[] = _context.elGamalAESEngine().encrypt(msg1, pubKey, curKey, firstTags, 64);
    99105       
    100         byte dmsg1[] = _context.elGamalAESEngine().decrypt(emsg1, privKey);
     106        byte dmsg1[] = _context.elGamalAESEngine().decrypt(emsg1, privKey, skm);
    101107        assertTrue(DataHelper.eq(dmsg1, msg1));
    102108       
    103109       
    104110       
    105         TagSetHandle tsh = _context.sessionKeyManager().tagsDelivered(pubKey, curKey, firstTags);
    106         _context.sessionKeyManager().tagsAcked(pubKey, curKey, tsh);
    107        
    108         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    109         SessionTag curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     111        TagSetHandle tsh = skm.tagsDelivered(pubKey, curKey, firstTags);
     112        skm.tagsAcked(pubKey, curKey, tsh);
     113       
     114        curKey = skm.getCurrentKey(pubKey);
     115        SessionTag curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    110116       
    111117        assertNotNull(curTag);
     
    113119        byte emsg2[] = _context.elGamalAESEngine().encrypt(msg2, pubKey, curKey, null, curTag, 64);
    114120       
    115         byte dmsg2[] = _context.elGamalAESEngine().decrypt(emsg2, privKey);
     121        byte dmsg2[] = _context.elGamalAESEngine().decrypt(emsg2, privKey, skm);
    116122        assertTrue(DataHelper.eq(dmsg2, msg2));
    117123       
     
    119125       
    120126       
    121         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    122         curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     127        curKey = skm.getCurrentKey(pubKey);
     128        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    123129       
    124130        assertNotNull(curTag);
     
    127133        byte emsg3[] = _context.elGamalAESEngine().encrypt(msg3, pubKey, curKey, secondTags, curTag, 64);
    128134       
    129         byte dmsg3[] = _context.elGamalAESEngine().decrypt(emsg3, privKey);
     135        byte dmsg3[] = _context.elGamalAESEngine().decrypt(emsg3, privKey, skm);
    130136        assertTrue(DataHelper.eq(dmsg3, msg3));
    131137       
    132138       
    133139       
    134         tsh = _context.sessionKeyManager().tagsDelivered(pubKey, curKey, secondTags);
    135         _context.sessionKeyManager().tagsAcked(pubKey, curKey, tsh);
    136        
    137         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    138         curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     140        tsh = skm.tagsDelivered(pubKey, curKey, secondTags);
     141        skm.tagsAcked(pubKey, curKey, tsh);
     142       
     143        curKey = skm.getCurrentKey(pubKey);
     144        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    139145       
    140146        assertNotNull(curTag);
     
    143149        byte emsg4[] = _context.elGamalAESEngine().encrypt(msg4, pubKey, curKey, null, curTag, 64);
    144150       
    145         byte dmsg4[] = _context.elGamalAESEngine().decrypt(emsg4, privKey);
     151        byte dmsg4[] = _context.elGamalAESEngine().decrypt(emsg4, privKey, skm);
    146152        assertTrue(DataHelper.eq(dmsg4, msg4));
    147153       
    148154       
    149         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    150         curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     155        curKey = skm.getCurrentKey(pubKey);
     156        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    151157       
    152158        assertNotNull(curTag);
     
    155161        byte emsg5[] = _context.elGamalAESEngine().encrypt(msg5, pubKey, curKey, null, curTag, 64);
    156162       
    157         byte dmsg5[] = _context.elGamalAESEngine().decrypt(emsg5, privKey);
     163        byte dmsg5[] = _context.elGamalAESEngine().decrypt(emsg5, privKey, skm);
    158164        assertTrue(DataHelper.eq(dmsg5, msg5));
    159165       
     
    173179        PublicKey pubKey = (PublicKey)keys[0];
    174180        PrivateKey privKey = (PrivateKey)keys[1];
    175         SessionKey curKey = _context.sessionKeyManager().createSession(pubKey);
     181        SessionKeyManager skm = new TransientSessionKeyManager(_context);
     182        SessionKey curKey = skm.createSession(pubKey);
    176183        SessionKey nextKey = KeyGenerator.getInstance().generateSessionKey();
    177184       
     
    197204        byte emsg1[] = _context.elGamalAESEngine().encrypt(msg1, pubKey, curKey, firstTags, 64);
    198205       
    199         byte dmsg1[] = _context.elGamalAESEngine().decrypt(emsg1, privKey);
     206        byte dmsg1[] = _context.elGamalAESEngine().decrypt(emsg1, privKey, skm);
    200207        assertTrue(DataHelper.eq(dmsg1, msg1));
    201208       
    202209       
    203210       
    204         TagSetHandle tsh = _context.sessionKeyManager().tagsDelivered(pubKey, curKey, firstTags);
    205         _context.sessionKeyManager().tagsAcked(pubKey, curKey, tsh);
    206        
    207         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    208         SessionTag curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     211        TagSetHandle tsh = skm.tagsDelivered(pubKey, curKey, firstTags);
     212        skm.tagsAcked(pubKey, curKey, tsh);
     213       
     214        curKey = skm.getCurrentKey(pubKey);
     215        SessionTag curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    209216       
    210217        assertNotNull(curTag);
     
    212219        byte emsg2[] = _context.elGamalAESEngine().encrypt(msg2, pubKey, curKey, null, curTag, 64);
    213220       
    214         byte dmsg2[] = _context.elGamalAESEngine().decrypt(emsg2, privKey);
     221        byte dmsg2[] = _context.elGamalAESEngine().decrypt(emsg2, privKey, skm);
    215222        assertTrue(DataHelper.eq(dmsg2, msg2));
    216223       
    217224       
    218225       
    219         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    220         curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     226        curKey = skm.getCurrentKey(pubKey);
     227        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    221228       
    222229        assertNotNull(curTag);
     
    225232        byte emsg3[] = _context.elGamalAESEngine().encrypt(msg3, pubKey, curKey, secondTags, curTag, nextKey, 64);
    226233       
    227         byte dmsg3[] = _context.elGamalAESEngine().decrypt(emsg3, privKey);
     234        byte dmsg3[] = _context.elGamalAESEngine().decrypt(emsg3, privKey, skm);
    228235        assertTrue(DataHelper.eq(dmsg3, msg3));
    229236       
    230237       
    231238       
    232         tsh = _context.sessionKeyManager().tagsDelivered(pubKey, nextKey, secondTags); // note nextKey not curKey
    233         _context.sessionKeyManager().tagsAcked(pubKey, nextKey, tsh);
    234        
    235         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    236         curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     239        tsh = skm.tagsDelivered(pubKey, nextKey, secondTags); // note nextKey not curKey
     240        skm.tagsAcked(pubKey, nextKey, tsh);
     241       
     242        curKey = skm.getCurrentKey(pubKey);
     243        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    237244       
    238245        assertNotNull(curTag);
     
    241248        byte emsg4[] = _context.elGamalAESEngine().encrypt(msg4, pubKey, curKey, null, curTag, 64);
    242249       
    243         byte dmsg4[] = _context.elGamalAESEngine().decrypt(emsg4, privKey);
     250        byte dmsg4[] = _context.elGamalAESEngine().decrypt(emsg4, privKey, skm);
    244251        assertTrue(DataHelper.eq(dmsg4, msg4));
    245252       
    246253       
    247254       
    248         curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    249         curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
     255        curKey = skm.getCurrentKey(pubKey);
     256        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
    250257       
    251258        assertNotNull(curTag);
     
    254261        byte emsg5[] = _context.elGamalAESEngine().encrypt(msg5, pubKey, curKey, null, curTag, 64);
    255262       
    256         byte dmsg5[] = _context.elGamalAESEngine().decrypt(emsg5, privKey);
     263        byte dmsg5[] = _context.elGamalAESEngine().decrypt(emsg5, privKey, skm);
    257264        assertTrue(DataHelper.eq(dmsg5, msg5));
    258265       
     
    269276        PublicKey pubKey = (PublicKey)keys[0];
    270277        PrivateKey privKey = (PrivateKey)keys[1];
    271         SessionKey curKey = _context.sessionKeyManager().createSession(pubKey);
     278        SessionKeyManager skm = new TransientSessionKeyManager(_context);
     279        SessionKey curKey = skm.createSession(pubKey);
    272280       
    273281        for (int i = 0; i < 1000; i++) {
    274282            Set<SessionTag> tags = null;
    275283            SessionKey nextKey = null;
    276             curKey = _context.sessionKeyManager().getCurrentKey(pubKey);
    277             SessionTag curTag = _context.sessionKeyManager().consumeNextAvailableTag(pubKey, curKey);
    278            
    279             int availTags = _context.sessionKeyManager().getAvailableTags(pubKey, curKey);
     284            curKey = skm.getCurrentKey(pubKey);
     285            SessionTag curTag = skm.consumeNextAvailableTag(pubKey, curKey);
     286           
     287            int availTags = skm.getAvailableTags(pubKey, curKey);
    280288            if ((availTags < 1)) {
    281289                tags = generateNewTags(50);
     
    288296            byte emsg[] = _context.elGamalAESEngine().encrypt(msg, pubKey, curKey, tags, curTag, nextKey, 64);
    289297           
    290             byte dmsg[] = _context.elGamalAESEngine().decrypt(emsg, privKey);
     298            byte dmsg[] = _context.elGamalAESEngine().decrypt(emsg, privKey, skm);
    291299            assertTrue(DataHelper.eq(dmsg, msg));
    292300           
    293301            if ( (tags != null) && (tags.size() > 0) ) {
    294302                if (nextKey == null) {
    295                     TagSetHandle tsh = _context.sessionKeyManager().tagsDelivered(pubKey, curKey, tags);
    296                     _context.sessionKeyManager().tagsAcked(pubKey, curKey, tsh);
     303                    TagSetHandle tsh = skm.tagsDelivered(pubKey, curKey, tags);
     304                    skm.tagsAcked(pubKey, curKey, tsh);
    297305                } else {
    298                     TagSetHandle tsh = _context.sessionKeyManager().tagsDelivered(pubKey, nextKey, tags);
    299                     _context.sessionKeyManager().tagsAcked(pubKey, nextKey, tsh);
     306                    TagSetHandle tsh = skm.tagsDelivered(pubKey, nextKey, tags);
     307                    skm.tagsAcked(pubKey, nextKey, tsh);
    300308                }
    301309            }
Note: See TracChangeset for help on using the changeset viewer.