func (c *Conn) AddSeeder(t *storage.Torrent, p *storage.Peer) error { c.torrentsM.Lock() defer c.torrentsM.Unlock() torrent, ok := c.torrents[t.Infohash] if !ok { return tracker.ErrMissingResource } torrent.Leechers[storage.PeerMapKey(p)] = *p t.Leechers[storage.PeerMapKey(p)] = *p return nil }
func (c *Conn) RemoveSeeder(t *storage.Torrent, p *storage.Peer) error { c.torrentsM.Lock() defer c.torrentsM.Unlock() torrent, ok := c.torrents[t.Infohash] if !ok { return tracker.ErrMissingResource } delete(torrent.Seeders, storage.PeerMapKey(p)) delete(t.Seeders, storage.PeerMapKey(p)) return nil }
func (c *Conn) LeecherFinished(t *storage.Torrent, p *storage.Peer) error { c.torrentsM.Lock() defer c.torrentsM.Unlock() torrent, ok := c.torrents[t.Infohash] if !ok { return tracker.ErrMissingResource } torrent.Seeders[storage.PeerMapKey(p)] = *p delete(torrent.Leechers, storage.PeerMapKey(p)) t.Seeders[storage.PeerMapKey(p)] = *p delete(t.Leechers, storage.PeerMapKey(p)) return nil }
func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) { // Parse the required parameters off of a query compact, numWant, infohash, peerID, event, ip, port, uploaded, downloaded, left, err := s.validateAnnounceQuery(r) if err != nil { fail(err, w, r) return } // Get a connection to the tracker db conn, err := s.dbConnPool.Get() if err != nil { log.Panicf("server: %s", err) } // Validate the user's passkey passkey, _ := path.Split(r.URL.Path) user, err := validateUser(conn, passkey) if err != nil { fail(err, w, r) return } // Check if the user's client is whitelisted whitelisted, err := conn.ClientWhitelisted(parsePeerID(peerID)) if err != nil { log.Panicf("server: %s", err) } if !whitelisted { fail(errors.New("Your client is not approved"), w, r) return } // Find the specified torrent torrent, exists, err := conn.FindTorrent(infohash) if err != nil { log.Panicf("server: %s", err) } if !exists { fail(errors.New("This torrent does not exist"), w, r) return } // If the torrent was pruned and the user is seeding, unprune it if !torrent.Active && left == 0 { err := conn.MarkActive(torrent) if err != nil { log.Panicf("server: %s", err) } } // Create a new peer object from the request peer := &storage.Peer{ ID: peerID, UserID: user.ID, TorrentID: torrent.ID, IP: ip, Port: port, Uploaded: uploaded, Downloaded: downloaded, Left: left, LastAnnounce: time.Now().Unix(), } // Look for the user in in the pool of seeders and leechers _, seeder := torrent.Seeders[storage.PeerMapKey(peer)] _, leecher := torrent.Leechers[storage.PeerMapKey(peer)] switch { // Guarantee that no user is in both pools case seeder && leecher: if left == 0 { err := conn.RemoveLeecher(torrent, peer) if err != nil { log.Panicf("server: %s", err) } leecher = false } else { err := conn.RemoveSeeder(torrent, peer) if err != nil { log.Panicf("server: %s", err) } seeder = false } case seeder: // Update the peer with the stats from the request err := conn.SetSeeder(torrent, peer) if err != nil { log.Panicf("server: %s", err) } case leecher: // Update the peer with the stats from the request err := conn.SetLeecher(torrent, peer) if err != nil { log.Panicf("server: %s", err) } default: if left == 0 { // Save the peer as a new seeder err := conn.AddSeeder(torrent, peer) if err != nil { log.Panicf("server: %s", err) } } else { err = conn.AddLeecher(torrent, peer) if err != nil { log.Panicf("server: %s", err) } } } // Handle any events in the request switch { case event == "stopped" || event == "paused": if seeder { err := conn.RemoveSeeder(torrent, peer) if err != nil { log.Panicf("server: %s", err) } } if leecher { err := conn.RemoveLeecher(torrent, peer) if err != nil { log.Panicf("server: %s", err) } } case event == "completed": err := conn.RecordSnatch(user, torrent) if err != nil { log.Panicf("server: %s", err) } if leecher { err := conn.LeecherFinished(torrent, peer) if err != nil { log.Panicf("server: %s", err) } } case leecher && left == 0: // A leecher completed but the event was never received err := conn.LeecherFinished(torrent, peer) if err != nil { log.Panicf("server: %s", err) } } if ip != peer.IP || port != peer.Port { peer.Port = port peer.IP = ip } // Generate the response seedCount := len(torrent.Seeders) leechCount := len(torrent.Leechers) writeBencoded(w, "d") writeBencoded(w, "complete") writeBencoded(w, seedCount) writeBencoded(w, "incomplete") writeBencoded(w, leechCount) writeBencoded(w, "interval") writeBencoded(w, s.conf.Announce.Duration) writeBencoded(w, "min interval") writeBencoded(w, s.conf.MinAnnounce.Duration) if numWant > 0 && event != "stopped" && event != "paused" { writeBencoded(w, "peers") var peerCount, count int if compact { if left > 0 { peerCount = minInt(numWant, leechCount) } else { peerCount = minInt(numWant, leechCount+seedCount-1) } writeBencoded(w, strconv.Itoa(peerCount*6)) writeBencoded(w, ":") } else { writeBencoded(w, "l") } if left > 0 { // If they're seeding, give them only leechers count += writeLeechers(w, user, torrent, numWant, compact) } else { // If they're leeching, prioritize giving them seeders count += writeSeeders(w, user, torrent, numWant, compact) count += writeLeechers(w, user, torrent, numWant-count, compact) } if compact && peerCount != count { log.Panicf("Calculated peer count (%d) != real count (%d)", peerCount, count) } if !compact { writeBencoded(w, "e") } } writeBencoded(w, "e") }