Changeset 7602999


Ignore:
Timestamp:
Dec 20, 2010 12:05:03 AM (9 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
ebe7f3b
Parents:
4899a6d
Message:

more prep and stubs for no metainfo

Location:
apps/i2psnark/java/src/org/klomp/snark
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java

    r4899a6d r7602999  
    3333import net.i2p.util.Translate;
    3434
     35import org.klomp.snark.dht.KRPC;
     36
    3537/**
    3638 * I2P specific helpers for I2PSnark
     
    5759    private File _tmpDir;
    5860    private int _startupDelay;
     61    private KRPC _krpc;
    5962
    6063    public static final int DEFAULT_STARTUP_DELAY = 3;
     
    6568    public static final int DEFAULT_MAX_UP_BW = 8;  //KBps
    6669    public static final int MAX_CONNECTIONS = 16; // per torrent
     70    private static final boolean ENABLE_DHT = true;
     71
    6772    public I2PSnarkUtil(I2PAppContext ctx) {
    6873        _context = ctx;
     
    186191            _manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
    187192        }
     193        // FIXME this only instantiates krpc once, left stuck with old manager
     194        if (ENABLE_DHT && _manager != null && _krpc == null)
     195            _krpc = new KRPC(_context, _manager.getSession());
    188196        return (_manager != null);
    189197    }
    190198   
     199    /**
     200     * @return null if disabled or not started
     201     * @since 0.8.4
     202     */
     203    public KRPC getDHT() { return _krpc; }
     204
    191205    public boolean connected() { return _manager != null; }
     206
    192207    /**
    193208     * Destroy the destination itself
  • apps/i2psnark/java/src/org/klomp/snark/Peer.java

    r4899a6d r7602999  
    4040
    4141  private final byte[] my_id;
     42  private final byte[] infohash;
    4243  final MetaInfo metainfo;
    4344
     
    7172   * Creates a disconnected peer given a PeerID, your own id and the
    7273   * relevant MetaInfo.
    73    */
    74   public Peer(PeerID peerID, byte[] my_id, MetaInfo metainfo)
     74   * @param metainfo null if in magnet mode
     75   */
     76  public Peer(PeerID peerID, byte[] my_id, byte[] infohash, MetaInfo metainfo)
    7577  {
    7678    this.peerID = peerID;
    7779    this.my_id = my_id;
     80    this.infohash = infohash;
    7881    this.metainfo = metainfo;
    7982    _id = ++__id;
     
    8992   * the connect() method.
    9093   *
     94   * @param metainfo null if in magnet mode
    9195   * @exception IOException when an error occurred during the handshake.
    9296   */
    93   public Peer(final I2PSocket sock, InputStream in, OutputStream out, byte[] my_id, MetaInfo metainfo)
     97  public Peer(final I2PSocket sock, InputStream in, OutputStream out, byte[] my_id, byte[] infohash, MetaInfo metainfo)
    9498    throws IOException
    9599  {
    96100    this.my_id = my_id;
     101    this.infohash = infohash;
    97102    this.metainfo = metainfo;
    98103    this.sock = sock;
     
    313318    dout.writeLong(OPTION_EXTENSION | OPTION_DHT);
    314319    // Handshake write - metainfo hash
    315     byte[] shared_hash = metainfo.getInfoHash();
    316     dout.write(shared_hash);
     320    dout.write(infohash);
    317321    // Handshake write - peer id
    318322    dout.write(my_id);
     
    342346    bs = new byte[20];
    343347    din.readFully(bs);
    344     if (!Arrays.equals(shared_hash, bs))
     348    if (!Arrays.equals(infohash, bs))
    345349      throw new IOException("Unexpected MetaInfo hash");
    346350
  • apps/i2psnark/java/src/org/klomp/snark/PeerAcceptor.java

    r4899a6d r7602999  
    8989    if (coordinator != null) {
    9090        // single torrent capability
    91         MetaInfo meta = coordinator.getMetaInfo();
    92         if (DataHelper.eq(meta.getInfoHash(), peerInfoHash)) {
     91        if (DataHelper.eq(coordinator.getInfoHash(), peerInfoHash)) {
    9392            if (coordinator.needPeers())
    9493              {
    9594                Peer peer = new Peer(socket, in, out, coordinator.getID(),
    96                                      coordinator.getMetaInfo());
     95                                     coordinator.getInfoHash(), coordinator.getMetaInfo());
    9796                coordinator.addPeer(peer);
    9897              }
     
    102101          // its for another infohash, but we are only single torrent capable.  b0rk.
    103102            throw new IOException("Peer wants another torrent (" + Base64.encode(peerInfoHash)
    104                                   + ") while we only support (" + Base64.encode(meta.getInfoHash()) + ")");
     103                                  + ") while we only support (" + Base64.encode(coordinator.getInfoHash()) + ")");
    105104        }
    106105    } else {
     
    108107        for (Iterator iter = coordinators.iterator(); iter.hasNext(); ) {
    109108            PeerCoordinator cur = (PeerCoordinator)iter.next();
    110             MetaInfo meta = cur.getMetaInfo();
    111            
    112             if (DataHelper.eq(meta.getInfoHash(), peerInfoHash)) {
     109
     110            if (DataHelper.eq(cur.getInfoHash(), peerInfoHash)) {
    113111                if (cur.needPeers())
    114112                  {
    115113                    Peer peer = new Peer(socket, in, out, cur.getID(),
    116                                          cur.getMetaInfo());
     114                                         cur.getInfoHash(), cur.getMetaInfo());
    117115                    cur.addPeer(peer);
    118116                    return;
  • apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java

    r4899a6d r7602999  
    9494
    9595  private final byte[] id;
     96  private final byte[] infohash;
    9697
    9798  /** The wanted pieces. We could use a TreeSet but we'd have to clear and re-add everything
     
    109110  private static final Random _random = I2PAppContext.getGlobalContext().random();
    110111 
    111   public PeerCoordinator(I2PSnarkUtil util, byte[] id, MetaInfo metainfo, Storage storage,
     112  /**
     113   *  @param metainfo null if in magnet mode
     114   *  @param storage null if in magnet mode
     115   */
     116  public PeerCoordinator(I2PSnarkUtil util, byte[] id, byte[] infohash, MetaInfo metainfo, Storage storage,
    112117                         CoordinatorListener listener, Snark torrent)
    113118  {
    114119    _util = util;
    115120    this.id = id;
     121    this.infohash = infohash;
    116122    this.metainfo = metainfo;
    117123    this.storage = storage;
     
    150156  public void setWantedPieces()
    151157  {
     158    if (metainfo == null || storage == null)
     159        return;
    152160    // Make a list of pieces
    153161      synchronized(wantedPieces) {
     
    189197  public boolean completed()
    190198  {
     199    // FIXME return metainfo complete status
     200    if (storage == null)
     201        return false;
    191202    return storage.complete();
    192203  }
     
    205216  /**
    206217   * Returns how many bytes are still needed to get the complete file.
     218   * @return -1 if in magnet mode
    207219   */
    208220  public long getLeft()
    209221  {
     222    if (metainfo == null | storage == null)
     223        return -1;
    210224    // XXX - Only an approximation.
    211225    return ((long) storage.needed()) * metainfo.getPieceLength(0);
     
    292306  }
    293307
     308  /** @since 0.8.4 */
     309  public byte[] getInfoHash()
     310  {
     311    return infohash;
     312  }
     313
    294314  public boolean needPeers()
    295315  {
     
    302322   */
    303323  private int getMaxConnections() {
     324    if (metainfo == null)
     325        return 6;
    304326    int size = metainfo.getPieceLength(0);
    305327    int max = _util.getMaxConnections();
     
    376398        else
    377399          {
    378             if (_log.shouldLog(Log.INFO))
    379               _log.info("New connection to peer: " + peer + " for " + metainfo.getName());
     400            if (_log.shouldLog(Log.INFO)) {
     401                // just for logging
     402                String name;
     403                if (metainfo == null)
     404                    name = "Magnet";
     405                else
     406                    name = metainfo.getName();
     407               _log.info("New connection to peer: " + peer + " for " + metainfo.getName());
     408            }
    380409
    381410            // Add it to the beginning of the list.
     
    436465    if (need_more)
    437466      {
    438         if (_log.shouldLog(Log.DEBUG))
    439             _log.debug("Adding a peer " + peer.getPeerID().toString() + " for " + metainfo.getName(), new Exception("add/run"));
    440 
     467        if (_log.shouldLog(Log.DEBUG)) {
     468            // just for logging
     469            String name;
     470            if (metainfo == null)
     471                name = "Magnet";
     472            else
     473                name = metainfo.getName();
     474            _log.debug("Adding a peer " + peer.getPeerID().toString() + " for " + name, new Exception("add/run"));
     475        }
    441476        // Run the peer with us as listener and the current bitfield.
    442477        final PeerListener listener = this;
    443         final BitField bitfield = storage.getBitField();
     478        final BitField bitfield;
     479        if (storage != null)
     480            bitfield = storage.getBitField();
     481        else
     482            bitfield = null;
    444483        Runnable r = new Runnable()
    445484          {
     
    505544          }
    506545        interestedAndChoking = count;
    507   }
    508 
    509   public byte[] getBitMap()
    510   {
    511     return storage.getBitField().getFieldBytes();
    512546  }
    513547
     
    680714   */
    681715  public void updatePiecePriorities() {
     716      if (storage == null)
     717          return;
    682718      int[] pri = storage.getPiecePriorities();
    683719      if (pri == null) {
     
    746782    if (halted)
    747783      return null;
     784    if (metainfo == null || storage == null)
     785        return null;
    748786
    749787    try
     
    788826  public boolean gotPiece(Peer peer, int piece, byte[] bs)
    789827  {
     828    if (metainfo == null || storage == null)
     829        return true;
    790830    if (halted) {
    791831      _log.info("Got while-halted piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
     
    9841024   */
    9851025  public PartialPiece getPartialPiece(Peer peer, BitField havePieces) {
     1026      if (metainfo == null)
     1027          return null;
    9861028      synchronized(wantedPieces) {
    9871029          // sorts by remaining bytes, least first
  • apps/i2psnark/java/src/org/klomp/snark/PeerState.java

    r4899a6d r7602999  
    7272  private final static int MAX_PARTSIZE = 64*1024; // Don't let anybody request more than this
    7373
     74  /**
     75   * @param metainfo null if in magnet mode
     76   */
    7477  PeerState(Peer peer, PeerListener listener, MetaInfo metainfo,
    7578            PeerConnectionIn in, PeerConnectionOut out)
     
    133136    if (_log.shouldLog(Log.DEBUG))
    134137      _log.debug(peer + " rcv have(" + piece + ")");
     138    // FIXME we will lose these until we get the metainfo
     139    if (metainfo == null)
     140        return;
    135141    // Sanity check
    136142    if (piece < 0 || piece >= metainfo.getPieces())
     
    170176       
    171177        // XXX - Check for weird bitfield and disconnect?
    172         bitfield = new BitField(bitmap, metainfo.getPieces());
    173       }
     178        // FIXME will have to regenerate the bitfield after we know exactly
     179        // how many pieces there are, as we don't know how many spare bits there are.
     180        if (metainfo == null)
     181            bitfield = new BitField(bitmap, bitmap.length * 8);
     182        else
     183            bitfield = new BitField(bitmap, metainfo.getPieces());
     184      }
     185    if (metainfo == null)
     186        return;
    174187    boolean interest = listener.gotBitField(peer, bitfield);
    175188    setInteresting(interest);
     
    189202      _log.debug(peer + " rcv request("
    190203                  + piece + ", " + begin + ", " + length + ") ");
     204    if (metainfo == null)
     205        return;
    191206    if (choking)
    192207      {
     
    606621    // no bitfield yet? nothing to request then.
    607622    if (bitfield == null)
     623        return;
     624    if (metainfo == null)
    608625        return;
    609626    boolean more_pieces = true;
  • apps/i2psnark/java/src/org/klomp/snark/Snark.java

    r4899a6d r7602999  
    318318    activity = "Network setup";
    319319
    320     // "Taking Three as the subject to reason about--
    321     // A convenient number to state--
    322     // We add Seven, and Ten, and then multiply out
    323     // By One Thousand diminished by Eight.
    324     //
    325     // "The result we proceed to divide, as you see,
    326     // By Nine Hundred and Ninety Two:
    327     // Then subtract Seventeen, and the answer must be
    328     // Exactly and perfectly true.
    329 
    330     // Create a new ID and fill it with something random.  First nine
    331     // zeros bytes, then three bytes filled with snark and then
    332     // sixteen random bytes.
    333     byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
    334     id = new byte[20];
    335     Random random = I2PAppContext.getGlobalContext().random();
    336     int i;
    337     for (i = 0; i < 9; i++)
    338       id[i] = 0;
    339     id[i++] = snark;
    340     id[i++] = snark;
    341     id[i++] = snark;
    342     while (i < 20)
    343       id[i++] = (byte)random.nextInt(256);
    344 
     320    id = generateID();
    345321    debug("My peer id: " + PeerID.idencode(id), Snark.INFO);
    346322
     
    469445        startTorrent();
    470446  }
     447
     448  /**
     449   *  multitorrent, magnet
     450   *
     451   *  @param torrent a fake name for now (not a file name)
     452   *  @param ih 20-byte info hash
     453   *  @since 0.8.4
     454   */
     455  public Snark(I2PSnarkUtil util, String torrent, byte[] ih,
     456        CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
     457        ConnectionAcceptor connectionAcceptor, boolean start, String rootDir)
     458  {
     459    completeListener = complistener;
     460    _util = util;
     461    _peerCoordinatorSet = peerCoordinatorSet;
     462    acceptor = connectionAcceptor;
     463    this.torrent = torrent;
     464    this.infoHash = ih;
     465    this.rootDataDir = rootDir;
     466    stopped = true;
     467    id = generateID();
     468
     469    // All we have is an infoHash
     470    // meta remains null
     471    // storage remains null
     472
     473    if (start)
     474        startTorrent();
     475  }
     476
     477  private static byte[] generateID() {
     478    // "Taking Three as the subject to reason about--
     479    // A convenient number to state--
     480    // We add Seven, and Ten, and then multiply out
     481    // By One Thousand diminished by Eight.
     482    //
     483    // "The result we proceed to divide, as you see,
     484    // By Nine Hundred and Ninety Two:
     485    // Then subtract Seventeen, and the answer must be
     486    // Exactly and perfectly true.
     487
     488    // Create a new ID and fill it with something random.  First nine
     489    // zeros bytes, then three bytes filled with snark and then
     490    // sixteen random bytes.
     491    byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
     492    byte[] rv = new byte[20];
     493    Random random = I2PAppContext.getGlobalContext().random();
     494    int i;
     495    for (i = 0; i < 9; i++)
     496      rv[i] = 0;
     497    rv[i++] = snark;
     498    rv[i++] = snark;
     499    rv[i++] = snark;
     500    while (i < 20)
     501      rv[i++] = (byte)random.nextInt(256);
     502    return rv;
     503  }
     504
    471505  /**
    472506   * Start up contacting peers and querying the tracker
     
    485519        debug("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient", NOTICE);
    486520        activity = "Collecting pieces";
    487         coordinator = new PeerCoordinator(_util, id, meta, storage, this, this);
     521        coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
    488522        if (_peerCoordinatorSet != null) {
    489523            // multitorrent
     
    508542        if (_peerCoordinatorSet != null)
    509543            _peerCoordinatorSet.remove(coordinator);
    510         PeerCoordinator newCoord = new PeerCoordinator(_util, id, meta, storage, this, this);
     544        PeerCoordinator newCoord = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
    511545        if (_peerCoordinatorSet != null)
    512546            _peerCoordinatorSet.add(newCoord);
     
    517551        trackerclient.start();
    518552    } else if (trackerclient.halted() || coordinatorChanged) {
    519         try
    520           {
    521             storage.reopen(rootDataDir);
    522           }
    523         catch (IOException ioe)
    524           {
    525             try { storage.close(); } catch (IOException ioee) {
    526                 ioee.printStackTrace();
    527             }
    528             fatal("Could not reopen storage", ioe);
    529           }
    530         TrackerClient newClient = new TrackerClient(_util, coordinator.getMetaInfo(), coordinator, this);
     553        if (storage != null) {
     554            try {
     555                 storage.reopen(rootDataDir);
     556             }   catch (IOException ioe) {
     557                 try { storage.close(); } catch (IOException ioee) {
     558                     ioee.printStackTrace();
     559                 }
     560                 fatal("Could not reopen storage", ioe);
     561             }
     562        }
     563        TrackerClient newClient = new TrackerClient(_util, meta, coordinator, this);
    531564        if (!trackerclient.halted())
    532565            trackerclient.halt();
     
    602635     */
    603636    public byte[] getInfoHash() {
     637        // should always be the same
    604638        if (meta != null)
    605639            return meta.getInfoHash();
  • apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java

    r4899a6d r7602999  
    580580   
    581581    /**
     582     * Add a torrent with the info hash alone (magnet / maggot)
     583     *
     584     * @param name hex or b32 name from the magnet link
     585     * @param ih 20 byte info hash
     586     * @since 0.8.4
     587     */
     588    public void addMagnet(String name, byte[] ih) {
     589        Snark torrent = new Snark(_util, name, ih, this,
     590                                  _peerCoordinatorSet, _connectionAcceptor,
     591                                  false, getDataDir().getPath());
     592
     593        // TODO tell the dir monitor not to delete us
     594        synchronized (_snarks) {
     595            _snarks.put(name, torrent);
     596        }
     597        if (shouldAutoStart()) {
     598            torrent.startTorrent();
     599            addMessage(_("Fetching {0}", name));
     600            boolean haveSavedPeers = false;
     601            if ((!util().connected()) && !haveSavedPeers) {
     602                addMessage(_("We have no saved peers and no other torrents are running. " +
     603                             "Fetch of {0} will not succeed until you start another torrent.", name));
     604            }
     605        } else {
     606            addMessage(_("Adding {0}", name));
     607      }
     608    }
     609
     610    /**
     611     * Delete a torrent with the info hash alone (magnet / maggot)
     612     *
     613     * @param ih 20 byte info hash
     614     * @since 0.8.4
     615     */
     616    public void deleteMagnet(byte[] ih) {
     617    }
     618
     619    /**
    582620     * Get the timestamp for a torrent from the config file
    583621     */
  • apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java

    r4899a6d r7602999  
    3939import net.i2p.util.Log;
    4040
     41import org.klomp.snark.dht.KRPC;
    4142
    4243/**
     
    7475  private List trackers;
    7576
     77  /**
     78   * @param meta null if in magnet mode
     79   */
    7680  public TrackerClient(I2PSnarkUtil util, MetaInfo meta, PeerCoordinator coordinator, Snark snark)
    7781  {
     
    174178        stop = true;
    175179        _log.error("No valid trackers for infoHash: " + infoHash);
     180        // FIXME keep going if DHT enabled
    176181        return;
    177182    }
     
    193198        while(!stop)
    194199          {
     200            // Local DHT tracker announce
     201            if (_util.getDHT() != null)
     202                _util.getDHT().announce(snark.getInfoHash());
    195203            try
    196204              {
     
    320328            }  // *** end of trackers loop here
    321329
     330            // Get peers from DHT
     331            // FIXME this needs to be in its own thread
     332            if (_util.getDHT() != null && !stop) {
     333                int numwant;
     334                if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
     335                    numwant = 1;
     336                else
     337                    numwant = _util.getMaxConnections();
     338                List<Hash> hashes = _util.getDHT().getPeers(snark.getInfoHash(), numwant, 2*60*1000);
     339                _util.debug("Got " + hashes + " from DHT", Snark.INFO);
     340                // announce  ourselves while the token is still good
     341                // FIXME this needs to be in its own thread
     342                if (!stop) {
     343                    int good = _util.getDHT().announce(snark.getInfoHash(), 8, 5*60*1000);
     344                    _util.debug("Sent " + good + " good announces to DHT", Snark.INFO);
     345                }
     346
     347                // now try these peers
     348                if ((!stop) && !hashes.isEmpty()) {
     349                    List<Peer> peers = new ArrayList(hashes.size());
     350                    for (Hash h : hashes) {
     351                        PeerID pID = new PeerID(h.getData());
     352                        peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), meta));
     353                    }
     354                    Collections.shuffle(peers, r);
     355                    Iterator<Peer> it = peers.iterator();
     356                    while ((!stop) && it.hasNext()) {
     357                        Peer cur = it.next();
     358                        if (coordinator.addPeer(cur)) {
     359                            int delay = DELAY_MUL;
     360                            delay *= ((int)cur.getPeerID().getAddress().calculateHash().toBase64().charAt(0)) % 10;
     361                            delay += DELAY_MIN;
     362                            try { Thread.sleep(delay); } catch (InterruptedException ie) {}
     363                         }
     364                    }
     365                }
     366            }
     367
     368
    322369            // we could try and total the unique peers but that's too hard for now
    323370            snark.setTrackerSeenPeers(maxSeenPeers);
     
    334381    finally
    335382      {
     383        // Local DHT tracker unannounce
     384        if (_util.getDHT() != null)
     385            _util.getDHT().unannounce(snark.getInfoHash());
    336386        try
    337387          {
  • apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java

    r4899a6d r7602999  
    145145            }
    146146        }
    147         peers.add(new Peer(peerID, my_id, metainfo));
     147        peers.add(new Peer(peerID, my_id, metainfo.getInfoHash(), metainfo));
    148148      }
    149149
     
    173173            continue;
    174174        }
    175         peers.add(new Peer(peerID, my_id, metainfo));
     175        peers.add(new Peer(peerID, my_id, metainfo.getInfoHash(), metainfo));
    176176      }
    177177
  • apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java

    r4899a6d r7602999  
    6262   
    6363    public static final String PROP_CONFIG_FILE = "i2psnark.configFile";
     64    /** BEP 9 */
     65    private static final String MAGNET = "magnet:?xt=urn:btih:";
     66    /** http://sponge.i2p/files/maggotspec.txt */
     67    private static final String MAGGOT = "maggot://";
    6468 
    6569    @Override
     
    456460                    I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add", true);
    457461                    fetch.start();
     462                } else if (newURL.startsWith(MAGNET) || newURL.startsWith(MAGGOT)) {
     463                    addMagnet(newURL);
    458464                } else {
    459                     _manager.addMessage(_("Invalid URL - must start with http://"));
     465                    _manager.addMessage(_("Invalid URL - must start with http://, {0} or {1}", MAGNET, MAGGOT));
    460466                }
    461467            } else {
     
    504510                            MetaInfo meta = snark.getMetaInfo();
    505511                            if (meta == null) {
     512                                // magnet
     513                                _manager.deleteMagnet(snark.getInfoHash());
    506514                                return;
    507515                            }
     
    528536                            MetaInfo meta = snark.getMetaInfo();
    529537                            if (meta == null) {
     538                                // magnet
     539                                _manager.deleteMagnet(snark.getInfoHash());
    530540                                return;
    531541                            }
     
    13151325    }
    13161326
     1327    /**
     1328     *  @param url in base32 or hex, xt must be first magnet param
     1329     *  @since 0.8.4
     1330     */
     1331    private void addMagnet(String url) {
     1332        String ihash;
     1333        String name;
     1334        if (url.startsWith(MAGNET)) {
     1335            ihash = url.substring(MAGNET.length()).trim();
     1336            int amp = ihash.indexOf('&');
     1337            if (amp >= 0)
     1338                ihash = url.substring(0, amp);
     1339            name = "Magnet " + ihash;
     1340        } else if (url.startsWith(MAGGOT)) {
     1341            ihash = url.substring(MAGGOT.length()).trim();
     1342            int col = ihash.indexOf(':');
     1343            if (col >= 0)
     1344                ihash = url.substring(0, col);
     1345            name = "Maggot " + ihash;
     1346        } else {
     1347            return;
     1348        }
     1349        byte[] ih = null;
     1350        if (ihash.length() == 32) {
     1351            ih = Base32.decode(ihash);
     1352        } else if (ihash.length() == 40) {
     1353            ih = new byte[20];
     1354            try {
     1355                for (int i = 0; i < 20; i++) {
     1356                    ih[i] = (byte) (Integer.parseInt(ihash.substring(i*2, (i*2) + 2), 16) & 0xff);
     1357                }
     1358            } catch (NumberFormatException nfe) {
     1359                ih = null;
     1360            }
     1361        }
     1362        if (ih == null || ih.length != 20) {
     1363            _manager.addMessage(_("Invalid info hash in magnet URL {0}", url));
     1364            return;
     1365        }
     1366        _manager.addMagnet(ihash, ih);
     1367    }
     1368
    13171369    /** copied from ConfigTunnelsHelper */
    13181370    private static final String HOP = "hop";
Note: See TracChangeset for help on using the changeset viewer.