Blame src/Torrent.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h>
Packit 8f70b4
#include <sys/socket.h>
Packit 8f70b4
#include <netinet/in.h>
Packit 8f70b4
#include <arpa/inet.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <sha1.h>
Packit 8f70b4
#include <dirent.h>
Packit 8f70b4
Packit 8f70b4
#include "Torrent.h"
Packit 8f70b4
#include "TorrentTracker.h"
Packit 8f70b4
#include "DHT.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "plural.h"
Packit 8f70b4
CDECL_BEGIN
Packit 8f70b4
#include "human.h"
Packit 8f70b4
CDECL_END
Packit 8f70b4
Packit 8f70b4
static ResType torrent_vars[] = {
Packit 8f70b4
   {"torrent:port-range", "6881-6889", ResMgr::RangeValidate, ResMgr::NoClosure},
Packit 8f70b4
   {"torrent:max-peers", "60", ResMgr::UNumberValidate},
Packit 8f70b4
   {"torrent:save-metadata", "yes", ResMgr::BoolValidate, ResMgr::NoClosure},
Packit 8f70b4
   {"torrent:stop-min-ppr", "1.4", ResMgr::FloatValidate},
Packit 8f70b4
   {"torrent:stop-on-ratio", "2.0", ResMgr::FloatValidate},
Packit 8f70b4
   {"torrent:seed-max-time", "30d", ResMgr::TimeIntervalValidate},
Packit 8f70b4
   {"torrent:seed-min-peers", "3", ResMgr::UNumberValidate},
Packit 8f70b4
   {"torrent:ip", "", ResMgr::IPv4AddrValidate, ResMgr::NoClosure},
Packit 8f70b4
   {"torrent:retracker", ""},
Packit 8f70b4
   {"torrent:use-dht", "yes", ResMgr::BoolValidate, ResMgr::NoClosure},
Packit 8f70b4
   {"torrent:timeout", "7d", ResMgr::TimeIntervalValidate, ResMgr::NoClosure},
Packit 8f70b4
#if INET6
Packit 8f70b4
   {"torrent:ipv6", "", ResMgr::IPv6AddrValidate, ResMgr::NoClosure},
Packit 8f70b4
#endif
Packit 8f70b4
   {0}
Packit 8f70b4
};
Packit 8f70b4
static ResDecls torrent_vars_register(torrent_vars);
Packit 8f70b4
Packit 8f70b4
#if INET6
Packit 8f70b4
#ifdef HAVE_IFADDRS_H
Packit 8f70b4
#include <ifaddrs.h>
Packit 8f70b4
#endif
Packit 8f70b4
static const char *FindGlobalIPv6Address()
Packit 8f70b4
{
Packit 8f70b4
#ifdef HAVE_IFADDRS_H
Packit 8f70b4
   struct ifaddrs *ifaddrs=0;
Packit 8f70b4
   getifaddrs(&ifaddrs);
Packit 8f70b4
   for(struct ifaddrs *ifa=ifaddrs; ifa; ifa=ifa->ifa_next) {
Packit 8f70b4
      if(ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET6) {
Packit 8f70b4
	 struct in6_addr *addr=&((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr;
Packit 8f70b4
	 if(!IN6_IS_ADDR_UNSPECIFIED(addr) && !IN6_IS_ADDR_LOOPBACK(addr)
Packit 8f70b4
	 && !IN6_IS_ADDR_LINKLOCAL(addr) && !IN6_IS_ADDR_SITELOCAL(addr)
Packit 8f70b4
	 && !IN6_IS_ADDR_MULTICAST(addr)) {
Packit 8f70b4
	    char *buf=xstring::tmp_buf(INET6_ADDRSTRLEN);
Packit 8f70b4
	    inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN);
Packit 8f70b4
	    freeifaddrs(ifaddrs);
Packit 8f70b4
	    return buf;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   freeifaddrs(ifaddrs);
Packit 8f70b4
#endif
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
void Torrent::ClassInit()
Packit 8f70b4
{
Packit 8f70b4
   static bool inited;
Packit 8f70b4
   if(inited)
Packit 8f70b4
      return;
Packit 8f70b4
   inited=true;
Packit 8f70b4
Packit 8f70b4
#if INET6
Packit 8f70b4
   const char *ipv6=ResMgr::Query("torrent:ipv6",0);
Packit 8f70b4
   if(!*ipv6)
Packit 8f70b4
   {
Packit 8f70b4
      ipv6=FindGlobalIPv6Address();
Packit 8f70b4
      if(ipv6) {
Packit 8f70b4
	 ProtoLog::LogNote(9,"found IPv6 address: %s",ipv6);
Packit 8f70b4
	 ResMgr::Set("torrent:ipv6",0,ipv6);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring Torrent::my_peer_id;
Packit 8f70b4
xstring Torrent::my_key;
Packit 8f70b4
unsigned Torrent::my_key_num;
Packit 8f70b4
xmap<Torrent*> Torrent::torrents;
Packit 8f70b4
SMTaskRef<TorrentListener> Torrent::listener;
Packit 8f70b4
SMTaskRef<TorrentListener> Torrent::listener_udp;
Packit 8f70b4
SMTaskRef<DHT> Torrent::dht;
Packit 8f70b4
#if INET6
Packit 8f70b4
SMTaskRef<TorrentListener> Torrent::listener_ipv6;
Packit 8f70b4
SMTaskRef<TorrentListener> Torrent::listener_ipv6_udp;
Packit 8f70b4
SMTaskRef<DHT> Torrent::dht_ipv6;
Packit 8f70b4
#endif
Packit 8f70b4
SMTaskRef<FDCache> Torrent::fd_cache;
Packit 8f70b4
Ref<TorrentBlackList> Torrent::black_list;
Packit 8f70b4
Packit 8f70b4
void Torrent::StartDHT()
Packit 8f70b4
{
Packit 8f70b4
   if(!ResMgr::QueryBool("torrent:use-dht",0)) {
Packit 8f70b4
      StopDHT();
Packit 8f70b4
      StopListenerUDP();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(dht)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   StartListenerUDP();
Packit 8f70b4
Packit 8f70b4
   const char *home=get_lftp_cache_dir();
Packit 8f70b4
   const char *nodename=get_nodename();
Packit 8f70b4
Packit 8f70b4
   mkdir(xstring::format("%s/DHT",home),0700);
Packit 8f70b4
Packit 8f70b4
   const char *ip=ResMgr::Query("torrent:ip",0);
Packit 8f70b4
   if(!ip || !ip[0])
Packit 8f70b4
      ip="127.0.0.1";
Packit 8f70b4
   sockaddr_compact ip_packed;
Packit 8f70b4
   ip_packed.get_space(4);
Packit 8f70b4
   inet_pton(AF_INET,ip,ip_packed.get_non_const());
Packit 8f70b4
   ip_packed.set_length(4);
Packit 8f70b4
   xstring node_id;
Packit 8f70b4
   DHT::MakeNodeId(node_id,ip_packed);
Packit 8f70b4
   dht=new DHT(AF_INET,node_id);
Packit 8f70b4
   dht->state_file.setf("%s/DHT/ipv4-%s",home,nodename);
Packit 8f70b4
   if(listener_udp->GetPort())
Packit 8f70b4
      dht->Load();
Packit 8f70b4
Packit 8f70b4
#if INET6
Packit 8f70b4
   ip=ResMgr::Query("torrent:ipv6",0);
Packit 8f70b4
   if(!ip || !ip[0])
Packit 8f70b4
      ip="::1";
Packit 8f70b4
   ip_packed.get_space(16);
Packit 8f70b4
   inet_pton(AF_INET6,ip,ip_packed.get_non_const());
Packit 8f70b4
   ip_packed.set_length(16);
Packit 8f70b4
   DHT::MakeNodeId(node_id,ip_packed);
Packit 8f70b4
   dht_ipv6=new DHT(AF_INET6,node_id);
Packit 8f70b4
   dht_ipv6->state_file.setf("%s/DHT/ipv6-%s",home,nodename);
Packit 8f70b4
   if(listener_ipv6_udp->GetPort())
Packit 8f70b4
      dht_ipv6->Load();
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
void Torrent::StopDHT()
Packit 8f70b4
{
Packit 8f70b4
   if(!dht)
Packit 8f70b4
      return;
Packit 8f70b4
   dht->Save();
Packit 8f70b4
   dht=0;
Packit 8f70b4
#if INET6
Packit 8f70b4
   dht_ipv6->Save();
Packit 8f70b4
   dht_ipv6=0;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::StartListener()
Packit 8f70b4
{
Packit 8f70b4
   if(listener)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   listener=new TorrentListener(AF_INET);
Packit 8f70b4
   listener->Roll(); // try to allocate ipv4 port first
Packit 8f70b4
#if INET6
Packit 8f70b4
   listener_ipv6=new TorrentListener(AF_INET6);
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
void Torrent::StopListener()
Packit 8f70b4
{
Packit 8f70b4
   listener=0;
Packit 8f70b4
#if INET6
Packit 8f70b4
   listener_ipv6=0;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
void Torrent::StartListenerUDP()
Packit 8f70b4
{
Packit 8f70b4
   if(listener_udp)
Packit 8f70b4
      return;
Packit 8f70b4
   listener_udp=new TorrentListener(AF_INET,SOCK_DGRAM);
Packit 8f70b4
#if INET6
Packit 8f70b4
   listener_ipv6_udp=new TorrentListener(AF_INET6,SOCK_DGRAM);
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
void Torrent::StopListenerUDP()
Packit 8f70b4
{
Packit 8f70b4
   listener_udp=0;
Packit 8f70b4
#if INET6
Packit 8f70b4
   listener_ipv6_udp=0;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Torrent::Torrent(const char *mf,const char *c,const char *od)
Packit 8f70b4
   : metainfo_url(mf),
Packit 8f70b4
     pieces_timer(10),
Packit 8f70b4
     cwd(c), output_dir(od), rate_limit(mf),
Packit 8f70b4
     seed_timer("torrent:seed-max-time",0),
Packit 8f70b4
     timeout_timer("torrent:timeout",0),
Packit 8f70b4
     optimistic_unchoke_timer(30), peers_scan_timer(1),
Packit 8f70b4
     am_interested_timer(1), shutting_down_timer(60),
Packit 8f70b4
     dht_announce_timer(10*60),
Packit 8f70b4
     dht_announce_count(0), dht_announce_count_ipv6(0)
Packit 8f70b4
{
Packit 8f70b4
   shutting_down=false;
Packit 8f70b4
   complete=false;
Packit 8f70b4
   end_game=false;
Packit 8f70b4
   is_private=false;
Packit 8f70b4
   validating=false;
Packit 8f70b4
   force_valid=false;
Packit 8f70b4
   build_md=false;
Packit 8f70b4
   stop_if_complete=false;
Packit 8f70b4
   stop_if_known=false;
Packit 8f70b4
   md_saved=false;
Packit 8f70b4
   validate_index=0;
Packit 8f70b4
   metadata_size=0;
Packit 8f70b4
   info=0;
Packit 8f70b4
   pieces=0;
Packit 8f70b4
   piece_length=0;
Packit 8f70b4
   total_pieces=0;
Packit 8f70b4
   last_piece_length=0;
Packit 8f70b4
   total_length=0;
Packit 8f70b4
   total_sent=0;
Packit 8f70b4
   total_recv=0;
Packit 8f70b4
   total_left=0;
Packit 8f70b4
   complete_pieces=0;
Packit 8f70b4
   connected_peers_count=0;
Packit 8f70b4
   active_peers_count=0;
Packit 8f70b4
   complete_peers_count=0;
Packit 8f70b4
   am_interested_peers_count=0;
Packit 8f70b4
   am_not_choking_peers_count=0;
Packit 8f70b4
   max_peers=60;
Packit 8f70b4
   seed_min_peers=3;
Packit 8f70b4
   stop_on_ratio=2;
Packit 8f70b4
   stop_min_ppr=1,
Packit 8f70b4
   last_piece=TorrentPeer::NO_PIECE;
Packit 8f70b4
   min_piece_sources=0;
Packit 8f70b4
   avg_piece_sources=0;
Packit 8f70b4
   pieces_available_pct=0;
Packit 8f70b4
   current_min_ppr=0;
Packit 8f70b4
   current_max_ppr=0;
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
Packit 8f70b4
   if(!my_peer_id) {
Packit 8f70b4
      my_peer_id.set("-lftp47-");
Packit 8f70b4
      my_peer_id.appendf("%04x",(unsigned)getpid() & 0xffff);
Packit 8f70b4
      my_peer_id.appendf("%08x",(unsigned)now.UnixTime());
Packit 8f70b4
      assert(my_peer_id.length()==PEER_ID_LEN);
Packit 8f70b4
   }
Packit 8f70b4
   if(!my_key) {
Packit 8f70b4
      for(int i=0; i<10; i++)
Packit 8f70b4
	 my_key.appendf("%02x",unsigned(random()/13%256));
Packit 8f70b4
      my_key_num=random();
Packit 8f70b4
   }
Packit 8f70b4
   dht_announce_timer.Stop();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Torrent::~Torrent()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Torrent::TrackersDone() const
Packit 8f70b4
{
Packit 8f70b4
   if(shutting_down && shutting_down_timer.Stopped())
Packit 8f70b4
      return true;
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      if(trackers[i]->IsActive())
Packit 8f70b4
	 return false;
Packit 8f70b4
   }
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
int Torrent::Done() const
Packit 8f70b4
{
Packit 8f70b4
   return (shutting_down && TrackersDone());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::ShutdownTrackers() const
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      trackers[i]->Shutdown();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void Torrent::Shutdown()
Packit 8f70b4
{
Packit 8f70b4
   if(shutting_down)
Packit 8f70b4
      return;
Packit 8f70b4
   Enter(this);
Packit 8f70b4
   LogNote(3,"Shutting down...");
Packit 8f70b4
   shutting_down=true;
Packit 8f70b4
   shutting_down_timer.Reset();
Packit 8f70b4
   ShutdownTrackers();
Packit 8f70b4
   DenounceDHT();
Packit 8f70b4
   PrepareToDie();
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::AddTorrent(Torrent *t)
Packit 8f70b4
{
Packit 8f70b4
   if(FindTorrent(t->GetInfoHash()))
Packit 8f70b4
      return;
Packit 8f70b4
   if(GetTorrentsCount()==0) {
Packit 8f70b4
      StartListener();
Packit 8f70b4
      StartDHT();
Packit 8f70b4
   }
Packit 8f70b4
   torrents.add(t->GetInfoHash(),t);
Packit 8f70b4
}
Packit 8f70b4
void Torrent::RemoveTorrent(Torrent *t)
Packit 8f70b4
{
Packit 8f70b4
   if(t!=FindTorrent(t->info_hash))
Packit 8f70b4
      return;
Packit 8f70b4
   torrents.remove(t->GetInfoHash());
Packit 8f70b4
   if(GetTorrentsCount()==0) {
Packit 8f70b4
      StopListener();
Packit 8f70b4
      StopDHT();
Packit 8f70b4
      StopListenerUDP();
Packit 8f70b4
      fd_cache=0;
Packit 8f70b4
      black_list=0;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::PrepareToDie()
Packit 8f70b4
{
Packit 8f70b4
   metainfo_copy=0;
Packit 8f70b4
   building=0;
Packit 8f70b4
   peers.unset();
Packit 8f70b4
   if(info_hash && this==FindTorrent(info_hash))
Packit 8f70b4
      RemoveTorrent(this);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::SetError(Error *e)
Packit 8f70b4
{
Packit 8f70b4
   if(invalid_cause)
Packit 8f70b4
      return;
Packit 8f70b4
   invalid_cause=e;
Packit 8f70b4
   LogError(0,"%s: %s",
Packit 8f70b4
      invalid_cause->IsFatal()?"Fatal error":"Transient error",
Packit 8f70b4
      invalid_cause->Text());
Packit 8f70b4
   Shutdown();
Packit 8f70b4
}
Packit 8f70b4
void Torrent::SetError(const char *msg)
Packit 8f70b4
{
Packit 8f70b4
   SetError(Error::Fatal(msg));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
double Torrent::GetRatio() const
Packit 8f70b4
{
Packit 8f70b4
   if(total_sent==0 || total_length==total_left)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return total_sent/double(total_length-total_left);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::SetDownloader(unsigned piece,unsigned block,const TorrentPeer *o,const TorrentPeer *n)
Packit 8f70b4
{
Packit 8f70b4
   piece_info[piece].set_downloader(block,o,n,BlocksInPiece(piece));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::AccountSend(unsigned p,unsigned len)
Packit 8f70b4
{
Packit 8f70b4
   total_sent+=len;
Packit 8f70b4
   send_rate.Add(len);
Packit 8f70b4
   piece_info[p].add_ratio(float(len)/PieceLength(p));
Packit 8f70b4
}
Packit 8f70b4
void Torrent::AccountRecv(unsigned p,unsigned len)
Packit 8f70b4
{
Packit 8f70b4
   total_recv+=len;
Packit 8f70b4
   recv_rate.Add(len);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
BeNode *Torrent::Lookup(xmap_p<BeNode>& dict,const char *name,BeNode::be_type_t type)
Packit 8f70b4
{
Packit 8f70b4
   BeNode *node=dict.lookup(name);
Packit 8f70b4
   if(!node) {
Packit 8f70b4
      SetError(xstring::format("Meta-data: `%s' key missing",name));
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   if(node->type!=type) {
Packit 8f70b4
      SetError(xstring::format("Meta-data: wrong `%s' type, must be %s",name,BeNode::TypeName(type)));
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   return node;
Packit 8f70b4
}
Packit 8f70b4
void Torrent::InitTranslation()
Packit 8f70b4
{
Packit 8f70b4
   const char *charset="UTF-8"; // default
Packit 8f70b4
   recv_translate_utf8=new DirectedBuffer(DirectedBuffer::GET);
Packit 8f70b4
   recv_translate_utf8->SetTranslation(charset,true);
Packit 8f70b4
   if(metainfo_tree) {
Packit 8f70b4
      BeNode *b_charset=metainfo_tree->lookup("encoding",BeNode::BE_STR);
Packit 8f70b4
      if(b_charset)
Packit 8f70b4
	 charset=b_charset->str;
Packit 8f70b4
   }
Packit 8f70b4
   recv_translate=new DirectedBuffer(DirectedBuffer::GET);
Packit 8f70b4
   recv_translate->SetTranslation(charset,true);
Packit 8f70b4
}
Packit 8f70b4
void Torrent::TranslateString(BeNode *node) const
Packit 8f70b4
{
Packit 8f70b4
   if(node->str_lc)
Packit 8f70b4
      return;
Packit 8f70b4
   recv_translate->ResetTranslation();
Packit 8f70b4
   recv_translate->PutTranslated(node->str);
Packit 8f70b4
   node->str_lc.nset(recv_translate->Get(),recv_translate->Size());
Packit 8f70b4
   recv_translate->Skip(recv_translate->Size());
Packit 8f70b4
}
Packit 8f70b4
void Torrent::TranslateStringFromUTF8(BeNode *node) const
Packit 8f70b4
{
Packit 8f70b4
   if(node->str_lc)
Packit 8f70b4
      return;
Packit 8f70b4
   const Ref<DirectedBuffer>& tr=recv_translate_utf8;
Packit 8f70b4
   tr->ResetTranslation();
Packit 8f70b4
   tr->PutTranslated(node->str);
Packit 8f70b4
   node->str_lc.nset(tr->Get(),tr->Size());
Packit 8f70b4
   tr->Skip(tr->Size());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::SHA1(const xstring& str,xstring& buf)
Packit 8f70b4
{
Packit 8f70b4
   buf.get_space(SHA1_DIGEST_SIZE);
Packit 8f70b4
   sha1_buffer(str.get(),str.length(),buf.get_non_const());
Packit 8f70b4
   buf.set_length(SHA1_DIGEST_SIZE);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::ValidatePiece(unsigned p)
Packit 8f70b4
{
Packit 8f70b4
   const xstring& buf=Torrent::RetrieveBlock(p,0,PieceLength(p));
Packit 8f70b4
   bool valid=false;
Packit 8f70b4
   if(buf.length()==PieceLength(p)) {
Packit 8f70b4
      xstring& sha1=xstring::get_tmp();
Packit 8f70b4
      SHA1(buf,sha1);
Packit 8f70b4
      if(building) {
Packit 8f70b4
	 building->SetPiece(p,sha1);
Packit 8f70b4
	 valid=true;
Packit 8f70b4
      } else {
Packit 8f70b4
	 valid=!memcmp(pieces->get()+p*SHA1_DIGEST_SIZE,sha1,SHA1_DIGEST_SIZE);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(!valid) {
Packit 8f70b4
      if(building) {
Packit 8f70b4
	 SetError("File validation error");
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      if(buf.length()==PieceLength(p))
Packit 8f70b4
	 LogError(11,"piece %u digest mismatch",p);
Packit 8f70b4
      if(my_bitfield->get_bit(p)) {
Packit 8f70b4
	 total_left+=PieceLength(p);
Packit 8f70b4
	 complete_pieces--;
Packit 8f70b4
	 my_bitfield->set_bit(p,0);
Packit 8f70b4
      }
Packit 8f70b4
      SetBlocksAbsent(p);
Packit 8f70b4
   } else {
Packit 8f70b4
      LogNote(11,"piece %u ok",p);
Packit 8f70b4
      if(!my_bitfield->get_bit(p)) {
Packit 8f70b4
	 total_left-=PieceLength(p);
Packit 8f70b4
	 complete_pieces++;
Packit 8f70b4
	 my_bitfield->set_bit(p,1);
Packit 8f70b4
	 piece_info[p].free_block_map();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
template<typename T>
Packit 8f70b4
static inline int cmp(T a,T b)
Packit 8f70b4
{
Packit 8f70b4
   if(a>b)
Packit 8f70b4
      return 1;
Packit 8f70b4
   if(a
Packit 8f70b4
      return -1;
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static Torrent *cmp_torrent;
Packit 8f70b4
int Torrent::PiecesNeededCmp(const unsigned *a,const unsigned *b)
Packit 8f70b4
{
Packit 8f70b4
   int ra=cmp_torrent->piece_info[*a].get_sources_count();
Packit 8f70b4
   int rb=cmp_torrent->piece_info[*b].get_sources_count();
Packit 8f70b4
   int c=cmp(ra,rb);
Packit 8f70b4
   if(c) return c;
Packit 8f70b4
   return cmp(*a,*b);
Packit 8f70b4
}
Packit 8f70b4
int Torrent::PeersCompareActivity(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2)
Packit 8f70b4
{
Packit 8f70b4
   TimeDiff i1((*p1)->activity_timer.TimePassed());
Packit 8f70b4
   TimeDiff i2((*p2)->activity_timer.TimePassed());
Packit 8f70b4
   return cmp(i1.Seconds(),i2.Seconds());
Packit 8f70b4
}
Packit 8f70b4
int Torrent::PeersCompareRecvRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2)
Packit 8f70b4
{
Packit 8f70b4
   float r1((*p1)->peer_recv_rate.Get());
Packit 8f70b4
   float r2((*p2)->peer_recv_rate.Get());
Packit 8f70b4
   int c=cmp(r1,r2);
Packit 8f70b4
   if(c) return c;
Packit 8f70b4
   return PeersCompareSendRate(p1,p2);
Packit 8f70b4
}
Packit 8f70b4
int Torrent::PeersCompareSendRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2)
Packit 8f70b4
{
Packit 8f70b4
   float r1((*p1)->peer_send_rate.Get());
Packit 8f70b4
   float r2((*p2)->peer_send_rate.Get());
Packit 8f70b4
   return cmp(r1,r2);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Torrent::SeededEnough() const
Packit 8f70b4
{
Packit 8f70b4
   return (stop_on_ratio>0 && GetRatio()>=stop_on_ratio
Packit 8f70b4
	   && GetMinPerPieceRatio()>=stop_min_ppr)
Packit 8f70b4
      || seed_timer.Stopped();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::CleanPeers()
Packit 8f70b4
{
Packit 8f70b4
   Enter();
Packit 8f70b4
   // remove uninteresting peers and request more
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      const TorrentPeer *peer=peers[i];
Packit 8f70b4
      if(peer->ActivityTimedOut()) {
Packit 8f70b4
	 LogNote(4,"removing uninteresting peer %s (%s)",peer->GetName(),peers[i]->Status());
Packit 8f70b4
	 BlackListPeer(peer,"2h");
Packit 8f70b4
	 peers.remove(i--);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::StartTrackers()
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      trackers[i]->Start();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Torrent::GetPort()
Packit 8f70b4
{
Packit 8f70b4
   int port=0;
Packit 8f70b4
   if(listener && !port)
Packit 8f70b4
      port=listener->GetPort();
Packit 8f70b4
#if INET6
Packit 8f70b4
   if(listener_ipv6 && !port)
Packit 8f70b4
      port=listener_ipv6->GetPort();
Packit 8f70b4
#endif
Packit 8f70b4
   if(listener_udp && !port)
Packit 8f70b4
      return listener_udp->GetPort();
Packit 8f70b4
#if INET6
Packit 8f70b4
   if(listener_ipv6_udp && !port)
Packit 8f70b4
      port=listener_ipv6_udp->GetPort();
Packit 8f70b4
#endif
Packit 8f70b4
   return port;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::AnnounceDHT()
Packit 8f70b4
{
Packit 8f70b4
   if(is_private)
Packit 8f70b4
      return;
Packit 8f70b4
   CleanPeers();
Packit 8f70b4
   if(dht)
Packit 8f70b4
      dht->AnnouncePeer(this);
Packit 8f70b4
#if INET6
Packit 8f70b4
   if(dht_ipv6)
Packit 8f70b4
      dht_ipv6->AnnouncePeer(this);
Packit 8f70b4
#endif
Packit 8f70b4
   dht_announce_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
void Torrent::DenounceDHT()
Packit 8f70b4
{
Packit 8f70b4
   if(is_private)
Packit 8f70b4
      return;
Packit 8f70b4
   if(dht)
Packit 8f70b4
      dht->DenouncePeer(this);
Packit 8f70b4
#if INET6
Packit 8f70b4
   if(dht_ipv6)
Packit 8f70b4
      dht_ipv6->DenouncePeer(this);
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
void Torrent::DHT_Announced(int af)
Packit 8f70b4
{
Packit 8f70b4
   if(af==AF_INET)
Packit 8f70b4
      dht_announce_count++;
Packit 8f70b4
#if INET6
Packit 8f70b4
   else if(af==AF_INET6)
Packit 8f70b4
      dht_announce_count_ipv6++;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
const char *Torrent::DHT_Status() const
Packit 8f70b4
{
Packit 8f70b4
   if(!HasDHT() || Private())
Packit 8f70b4
      return "";
Packit 8f70b4
   static xstring status;
Packit 8f70b4
   status.nset("",0);
Packit 8f70b4
   if(dht_announce_count || dht_announce_count_ipv6) {
Packit 8f70b4
      status.append(_("announced via "));
Packit 8f70b4
      if(dht_announce_count)
Packit 8f70b4
	 status.appendf("ipv4:%d",dht_announce_count);
Packit 8f70b4
#if INET6
Packit 8f70b4
      if(dht_announce_count_ipv6) {
Packit 8f70b4
	 if(dht_announce_count)
Packit 8f70b4
	    status.append(", ");
Packit 8f70b4
	 status.appendf("ipv6:%d",dht_announce_count_ipv6);
Packit 8f70b4
      }
Packit 8f70b4
#endif
Packit 8f70b4
   }
Packit 8f70b4
   if(!dht_announce_timer.Stopped() && !validating) {
Packit 8f70b4
      if(status.length()>0)
Packit 8f70b4
	 status.append("; ");
Packit 8f70b4
      status.appendf(_("next announce in %s"),
Packit 8f70b4
	 dht_announce_timer.TimeLeft().toString(TimeInterval::TO_STR_TRANSLATE));
Packit 8f70b4
   }
Packit 8f70b4
   return status;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *Torrent::GetMetadataPath() const
Packit 8f70b4
{
Packit 8f70b4
   if(!QueryBool("torrent:save-metadata",0))
Packit 8f70b4
      return NULL;
Packit 8f70b4
   xstring& path=xstring::cat(get_lftp_data_dir(),"/torrent",NULL);
Packit 8f70b4
   mkdir(path,0700);
Packit 8f70b4
   path.append("/md");
Packit 8f70b4
   mkdir(path,0700);
Packit 8f70b4
   path.append('/');
Packit 8f70b4
   info_hash.hexdump_to(path);
Packit 8f70b4
   return path;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Torrent::SaveMetadata() const
Packit 8f70b4
{
Packit 8f70b4
   if(md_saved)
Packit 8f70b4
      return true;  // saved already.
Packit 8f70b4
Packit 8f70b4
   const char *path=GetMetadataPath();
Packit 8f70b4
   if(!path)
Packit 8f70b4
      return false;
Packit 8f70b4
Packit 8f70b4
   int fd=open(path,O_CREAT|O_WRONLY,0600);
Packit 8f70b4
   if(fd<0) {
Packit 8f70b4
      LogError(9,"open(%s): %s",path,strerror(errno));
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int bytes_to_write=metadata.length();
Packit 8f70b4
   int res=write(fd,metadata.get(),bytes_to_write);
Packit 8f70b4
   int saved_errno=errno;
Packit 8f70b4
   ftruncate(fd,bytes_to_write);
Packit 8f70b4
Packit 8f70b4
   close(fd);
Packit 8f70b4
Packit 8f70b4
   if(res==bytes_to_write)
Packit 8f70b4
      return true;  // no error, fd is closed.
Packit 8f70b4
Packit 8f70b4
   if(res<0)
Packit 8f70b4
      LogError(9,"write(%s): %s",path,strerror(saved_errno));
Packit 8f70b4
   else
Packit 8f70b4
      LogError(9,"write(%s): short write (only wrote %d bytes)",path,res);
Packit 8f70b4
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
bool Torrent::LoadMetadata(const char *path)
Packit 8f70b4
{
Packit 8f70b4
   int fd=open(path,O_RDONLY);
Packit 8f70b4
   if(fd<0) {
Packit 8f70b4
      LogError(9,"open(%s): %s",path,strerror(errno));
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   struct stat st;
Packit 8f70b4
   if(fstat(fd,&st)==-1) {
Packit 8f70b4
      close(fd);
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
   int bytes_to_read=st.st_size;
Packit 8f70b4
Packit 8f70b4
   xstring md;
Packit 8f70b4
   int res=read(fd,md.add_space(bytes_to_read),bytes_to_read);
Packit 8f70b4
   int saved_errno=errno;
Packit 8f70b4
Packit 8f70b4
   close(fd);
Packit 8f70b4
Packit 8f70b4
   if(res==bytes_to_read) {
Packit 8f70b4
      // no error, fd is closed.
Packit 8f70b4
      md.add_commit(res);
Packit 8f70b4
Packit 8f70b4
      xstring new_info_hash;
Packit 8f70b4
      SHA1(md,new_info_hash);
Packit 8f70b4
      if(info_hash && info_hash.ne(new_info_hash)) {
Packit 8f70b4
	 LogError(9,"cached metadata does not match info_hash");
Packit 8f70b4
	 return false;
Packit 8f70b4
      }
Packit 8f70b4
      LogNote(9,"got metadata from %s",path);
Packit 8f70b4
      if(SetMetadata(md)) {
Packit 8f70b4
	 md_saved=true;
Packit 8f70b4
	 return true;
Packit 8f70b4
      }
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(res<0)
Packit 8f70b4
      LogError(9,"read(%s): %s",path,strerror(saved_errno));
Packit 8f70b4
   else
Packit 8f70b4
      LogError(9,"read(%s): short read (only read %d bytes)",path,res);
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::FetchMetadataFromURL(const char *url)
Packit 8f70b4
{
Packit 8f70b4
   ParsedURL u(url,true);
Packit 8f70b4
   if(!u.proto) {
Packit 8f70b4
      u.proto.set("file");
Packit 8f70b4
      u.path.set(url);  // undo %xx translation
Packit 8f70b4
   }
Packit 8f70b4
   LogNote(9,"Retrieving meta-data from `%s'...\n",url);
Packit 8f70b4
   FileCopyPeer *metainfo_src=new FileCopyPeerFA(&u,FA::RETRIEVE);
Packit 8f70b4
   FileCopyPeer *metainfo_dst=new FileCopyPeerMemory(10000000);
Packit 8f70b4
   metainfo_copy=new FileCopy(metainfo_src,metainfo_dst,false);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::StartMetadataDownload()
Packit 8f70b4
{
Packit 8f70b4
   const char *path=GetMetadataPath();
Packit 8f70b4
   if(path && access(path,R_OK)>=0) {
Packit 8f70b4
      // we have the metadata cached
Packit 8f70b4
      if(LoadMetadata(path)) {
Packit 8f70b4
	 if(stop_if_known) {
Packit 8f70b4
	    LogNote(2,"found cached metadata, stopping");
Packit 8f70b4
	    Shutdown();
Packit 8f70b4
	 } else {
Packit 8f70b4
	    Startup();
Packit 8f70b4
	 }
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   md_download.nset("",0);
Packit 8f70b4
   // add torrent without metadata to announce it and get peers to get MD from.
Packit 8f70b4
   AddTorrent(this);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::SetTotalLength(off_t tl)
Packit 8f70b4
{
Packit 8f70b4
   total_length=tl;
Packit 8f70b4
Packit 8f70b4
   LogNote(4,"Total length is %llu",total_length);
Packit 8f70b4
   total_left=total_length;
Packit 8f70b4
Packit 8f70b4
   last_piece_length=total_length%piece_length;
Packit 8f70b4
   if(last_piece_length==0)
Packit 8f70b4
      last_piece_length=piece_length;
Packit 8f70b4
Packit 8f70b4
   total_pieces=(total_length+piece_length-1)/piece_length;
Packit 8f70b4
Packit 8f70b4
   my_bitfield=new BitField(total_pieces);
Packit 8f70b4
Packit 8f70b4
   blocks_in_piece=(piece_length+BLOCK_SIZE-1)/BLOCK_SIZE;
Packit 8f70b4
   blocks_in_last_piece=(last_piece_length+BLOCK_SIZE-1)/BLOCK_SIZE;
Packit 8f70b4
Packit 8f70b4
   piece_info=new TorrentPiece[total_pieces]();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::StartValidating()
Packit 8f70b4
{
Packit 8f70b4
   validate_index=0;
Packit 8f70b4
   validating=true;
Packit 8f70b4
   recv_rate.Reset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Torrent::SetMetadata(const xstring& md)
Packit 8f70b4
{
Packit 8f70b4
   metadata.set(md);
Packit 8f70b4
   timeout_timer.Reset();
Packit 8f70b4
Packit 8f70b4
   xstring new_info_hash;
Packit 8f70b4
   SHA1(metadata,new_info_hash);
Packit 8f70b4
   if(info_hash && info_hash.ne(new_info_hash)) {
Packit 8f70b4
      metadata.unset();
Packit 8f70b4
      SetError("metadata does not match info_hash");
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
   info_hash.set(new_info_hash);
Packit 8f70b4
Packit 8f70b4
   if(!info) {
Packit 8f70b4
      int rest;
Packit 8f70b4
      info=BeNode::Parse(metadata,metadata.length(),&rest);
Packit 8f70b4
      if(!info) {
Packit 8f70b4
	 SetError("cannot parse metadata");
Packit 8f70b4
	 return false;
Packit 8f70b4
      }
Packit 8f70b4
      xmap_p<BeNode> d;
Packit 8f70b4
      d.add("info",info);
Packit 8f70b4
      metainfo_tree=new BeNode(&d);
Packit 8f70b4
      InitTranslation();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   BeNode *b_piece_length=Lookup(info,"piece length",BeNode::BE_INT);
Packit 8f70b4
   if(!b_piece_length || b_piece_length->num<1024 || b_piece_length->num>INT_MAX/4) {
Packit 8f70b4
      SetError("Meta-data: invalid piece length");
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
   piece_length=b_piece_length->num;
Packit 8f70b4
   LogNote(4,"Piece length is %u",piece_length);
Packit 8f70b4
Packit 8f70b4
   BeNode *b_name=info->lookup("name",BeNode::BE_STR);
Packit 8f70b4
   BeNode *b_name_utf8=info->lookup("name.utf-8",BeNode::BE_STR);
Packit 8f70b4
   if(b_name_utf8) {
Packit 8f70b4
      TranslateStringFromUTF8(b_name_utf8);
Packit 8f70b4
      name.set(b_name_utf8->str_lc);
Packit 8f70b4
   } else if(b_name) {
Packit 8f70b4
      TranslateString(b_name);
Packit 8f70b4
      name.set(b_name->str_lc);
Packit 8f70b4
   } else {
Packit 8f70b4
      name.truncate();
Packit 8f70b4
      info_hash.hexdump_to(name);
Packit 8f70b4
   }
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
Packit 8f70b4
   BeNode *files=info->lookup("files");
Packit 8f70b4
   if(!files) {
Packit 8f70b4
      BeNode *length=Lookup(info,"length",BeNode::BE_INT);
Packit 8f70b4
      if(!length || length->num<0) {
Packit 8f70b4
	 SetError("Meta-data: invalid or missing length");
Packit 8f70b4
	 return false;
Packit 8f70b4
      }
Packit 8f70b4
      total_length=length->num;
Packit 8f70b4
   } else {
Packit 8f70b4
      if(files->type!=BeNode::BE_LIST) {
Packit 8f70b4
	 SetError("Meta-data: wrong `info/files' type, must be LIST");
Packit 8f70b4
	 return false;
Packit 8f70b4
      }
Packit 8f70b4
      total_length=0;
Packit 8f70b4
      for(int i=0; i<files->list.length(); i++) {
Packit 8f70b4
	 if(files->list[i]->type!=BeNode::BE_DICT) {
Packit 8f70b4
	    SetError(xstring::format("Meta-data: wrong `info/files[%d]' type, must be LIST",i));
Packit 8f70b4
	    return false;
Packit 8f70b4
	 }
Packit 8f70b4
	 BeNode *f=Lookup(files->list[i]->dict,"length",BeNode::BE_INT);
Packit 8f70b4
	 if(!f || f->num<0) {
Packit 8f70b4
	    SetError("Meta-data: invalid or missing file length");
Packit 8f70b4
	    return false;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!Lookup(files->list[i]->dict,"path",BeNode::BE_LIST)) {
Packit 8f70b4
	    SetError("Meta-data: file path missing");
Packit 8f70b4
	    return false;
Packit 8f70b4
	 }
Packit 8f70b4
	 total_length+=f->num;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   this->files=new TorrentFiles(files,this);
Packit 8f70b4
   SetTotalLength(total_length);
Packit 8f70b4
Packit 8f70b4
   BeNode *b_pieces=Lookup(info,"pieces",BeNode::BE_STR);
Packit 8f70b4
   if(!b_pieces) {
Packit 8f70b4
      SetError("Meta-data: `pieces' missing");
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
   pieces=&b_pieces->str;
Packit 8f70b4
   if(pieces->length()!=SHA1_DIGEST_SIZE*total_pieces) {
Packit 8f70b4
      SetError("Meta-data: invalid `pieces' length");
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   is_private=info->lookup_int("private");
Packit 8f70b4
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::Startup()
Packit 8f70b4
{
Packit 8f70b4
   if(!info_hash || !metadata)
Packit 8f70b4
      SetError("missing metadata");
Packit 8f70b4
   if(shutting_down)
Packit 8f70b4
      return;
Packit 8f70b4
   Torrent *other=FindTorrent(info_hash);
Packit 8f70b4
   if(other) {
Packit 8f70b4
      if(other!=this) {
Packit 8f70b4
	 SetError("This torrent is already running");
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
   } else {
Packit 8f70b4
      AddTorrent(this);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!building)
Packit 8f70b4
      md_saved=SaveMetadata();
Packit 8f70b4
Packit 8f70b4
   if(!force_valid && !building) {
Packit 8f70b4
      StartValidating();
Packit 8f70b4
   } else {
Packit 8f70b4
      my_bitfield->set_range(0,total_pieces,1);
Packit 8f70b4
      complete_pieces=total_pieces;
Packit 8f70b4
      total_left=0;
Packit 8f70b4
      complete=true;
Packit 8f70b4
      seed_timer.Reset();
Packit 8f70b4
      dht_announce_timer.Stop();
Packit 8f70b4
   }
Packit 8f70b4
   RestartPeers();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::RestartPeers()
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      peers[i]->Restart();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static int base32_char_to_value(char c)
Packit 8f70b4
{
Packit 8f70b4
   if(c>='a' && c<='z')
Packit 8f70b4
      return c-'a';
Packit 8f70b4
   if(c>='A' && c<='Z')
Packit 8f70b4
      return c-'A';
Packit 8f70b4
   if(c>='2' && c<='7')
Packit 8f70b4
      return c-'2'+26;
Packit 8f70b4
   if(c=='=')
Packit 8f70b4
      return 0;
Packit 8f70b4
   return -1;
Packit 8f70b4
}
Packit 8f70b4
void base32_decode(const char *base32,xstring& out)
Packit 8f70b4
{
Packit 8f70b4
   unsigned data=0;
Packit 8f70b4
   int data_bits=0;
Packit 8f70b4
   int pad_bits=0;
Packit 8f70b4
   while(*base32) {
Packit 8f70b4
      char c=*base32++;
Packit 8f70b4
      if(c=='=' && data_bits<=pad_bits)
Packit 8f70b4
	 return;
Packit 8f70b4
      if(pad_bits>0 && c!='=')
Packit 8f70b4
	 return;
Packit 8f70b4
      int v=base32_char_to_value(c);
Packit 8f70b4
      if(v==-1)
Packit 8f70b4
	 return;
Packit 8f70b4
      data|=((v&31)<<(11-data_bits));
Packit 8f70b4
      data_bits+=5;
Packit 8f70b4
      if(c=='=')
Packit 8f70b4
	 pad_bits+=5;
Packit 8f70b4
      if(data_bits>=8) {
Packit 8f70b4
	 out.append(char((data>>8)&255));
Packit 8f70b4
	 data<<=8;
Packit 8f70b4
	 data_bits-=8;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(data_bits>0)
Packit 8f70b4
      out.append(char((data>>8)&255));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::ParseMagnet(const char *m0)
Packit 8f70b4
{
Packit 8f70b4
   char *m=alloca_strdup(m0);
Packit 8f70b4
   for(char *p=strtok(m,"&";; p; p=strtok(NULL,"&")) {
Packit 8f70b4
      char *v=strchr(p,'=');
Packit 8f70b4
      if(!v)
Packit 8f70b4
	 continue;
Packit 8f70b4
      *v++=0;
Packit 8f70b4
      v=xstring::get_tmp(v).url_decode(URL_DECODE_PLUS).get_non_const();
Packit 8f70b4
      if(!strcmp(p,"xt")) {
Packit 8f70b4
	 if(strncmp(v,"urn:btih:",9)) {
Packit 8f70b4
	    SetError("Only BitTorrent magnet links are supported");
Packit 8f70b4
	    return;
Packit 8f70b4
	 }
Packit 8f70b4
	 v+=9;
Packit 8f70b4
	 xstring& btih=xstring::get_tmp(v);
Packit 8f70b4
	 if(btih.length()==40) {
Packit 8f70b4
	    btih.hex_decode();
Packit 8f70b4
	    if(btih.length()!=20) {
Packit 8f70b4
	       SetError("Invalid value of urn:btih in magnet link");
Packit 8f70b4
	       return;
Packit 8f70b4
	    }
Packit 8f70b4
	    info_hash.move_here(btih);
Packit 8f70b4
	 } else {
Packit 8f70b4
	    info_hash.truncate();
Packit 8f70b4
	    base32_decode(v,info_hash);
Packit 8f70b4
	    if(info_hash.length()!=20) {
Packit 8f70b4
	       info_hash.unset();
Packit 8f70b4
	       SetError("Invalid value of urn:btih in magnet link");
Packit 8f70b4
	       return;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else if(!strcmp(p,"tr")) {
Packit 8f70b4
	 SMTaskRef<TorrentTracker> new_tracker(new TorrentTracker(this,v));
Packit 8f70b4
	 if(!new_tracker->Failed()) {
Packit 8f70b4
	    new_tracker->tracker_no=trackers.count();
Packit 8f70b4
	    trackers.append(new_tracker.borrow());
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else if(!strcmp(p,"dn")) {
Packit 8f70b4
	 name.set(v);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(!info_hash) {
Packit 8f70b4
      SetError("missing urn:btih in magnet link");
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   if(FindTorrent(info_hash)) {
Packit 8f70b4
      SetError("This torrent is already running");
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   StartMetadataDownload();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::CalcPiecesStats()
Packit 8f70b4
{
Packit 8f70b4
   min_piece_sources=INT_MAX;
Packit 8f70b4
   avg_piece_sources=0;
Packit 8f70b4
   pieces_available_pct=0;
Packit 8f70b4
   for(unsigned i=0; i
Packit 8f70b4
      if(my_bitfield->get_bit(i))
Packit 8f70b4
	 continue;
Packit 8f70b4
      unsigned sc=piece_info[i].get_sources_count();
Packit 8f70b4
      if(min_piece_sources>sc)
Packit 8f70b4
	 min_piece_sources=sc;
Packit 8f70b4
      if(sc==0)
Packit 8f70b4
	 continue;
Packit 8f70b4
      pieces_available_pct++;
Packit 8f70b4
      avg_piece_sources+=sc;
Packit 8f70b4
   }
Packit 8f70b4
   avg_piece_sources=avg_piece_sources*256/(total_pieces-complete_pieces);
Packit 8f70b4
   pieces_available_pct=pieces_available_pct*100/(total_pieces-complete_pieces);
Packit 8f70b4
   CalcPerPieceRatio();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::CalcPerPieceRatio()
Packit 8f70b4
{
Packit 8f70b4
   current_min_ppr=1024;
Packit 8f70b4
   current_max_ppr=0;
Packit 8f70b4
   for(unsigned i=0; i
Packit 8f70b4
      float ppr=piece_info[i].get_ratio();
Packit 8f70b4
      if(current_min_ppr>ppr)
Packit 8f70b4
	 current_min_ppr=ppr;
Packit 8f70b4
      if(current_max_ppr
Packit 8f70b4
	 current_max_ppr=ppr;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::RebuildPiecesNeeded()
Packit 8f70b4
{
Packit 8f70b4
   pieces_needed.truncate();
Packit 8f70b4
   bool enter_end_game=true;
Packit 8f70b4
   for(unsigned i=0; i
Packit 8f70b4
      if(!my_bitfield->get_bit(i)) {
Packit 8f70b4
	 if(!piece_info[i].has_a_downloader())
Packit 8f70b4
	    enter_end_game=false;
Packit 8f70b4
	 if(piece_info[i].has_no_sources())
Packit 8f70b4
	    continue;
Packit 8f70b4
	 pieces_needed.append(i);
Packit 8f70b4
      }
Packit 8f70b4
      piece_info[i].cleanup();
Packit 8f70b4
   }
Packit 8f70b4
   if(!end_game && enter_end_game) {
Packit 8f70b4
      LogNote(1,"entering End Game mode");
Packit 8f70b4
      end_game=true;
Packit 8f70b4
   }
Packit 8f70b4
   cmp_torrent=this;
Packit 8f70b4
   pieces_needed.qsort(PiecesNeededCmp);
Packit 8f70b4
   CalcPiecesStats();
Packit 8f70b4
   pieces_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
TorrentBuild::TorrentBuild(const char *path) :
Packit 8f70b4
   top_path(path),
Packit 8f70b4
   name(basename_ptr(path)),
Packit 8f70b4
   done(false),
Packit 8f70b4
   total_length(0),
Packit 8f70b4
   piece_length(0)
Packit 8f70b4
{
Packit 8f70b4
   name.rtrim('/');
Packit 8f70b4
Packit 8f70b4
   struct stat st;
Packit 8f70b4
   if(stat(path,&st)==-1) {
Packit 8f70b4
      error=SysError();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   if(S_ISREG(st.st_mode)) {
Packit 8f70b4
      total_length=st.st_size;
Packit 8f70b4
      LogNote(10,"single file %s, size %lld",path,(long long)st.st_size);
Packit 8f70b4
      Finish();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   if(!S_ISDIR(st.st_mode)) {
Packit 8f70b4
      error=new Error(-1,"Need a plain file or directory",true);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   QueueDir("");
Packit 8f70b4
}
Packit 8f70b4
const char *TorrentBuild::lc_to_utf8(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   if(!translate || !s)
Packit 8f70b4
      return s;
Packit 8f70b4
Packit 8f70b4
   translate->ResetTranslation();
Packit 8f70b4
   translate->PutTranslated(s);
Packit 8f70b4
   int len;
Packit 8f70b4
   translate->Get(&s,&len;;
Packit 8f70b4
   translate->Skip(len);
Packit 8f70b4
   return xstring::get_tmp(s,len);
Packit 8f70b4
}
Packit 8f70b4
void TorrentBuild::Finish()
Packit 8f70b4
{
Packit 8f70b4
   done=true;
Packit 8f70b4
   LogNote(10,"scan finished, total_length=%lld",(long long)total_length);
Packit 8f70b4
Packit 8f70b4
   translate=new DirectedBuffer(DirectedBuffer::PUT);
Packit 8f70b4
   translate->SetTranslation("UTF-8",false);
Packit 8f70b4
Packit 8f70b4
   xmap_p<BeNode> *b_info=new xmap_p<BeNode>();
Packit 8f70b4
   b_info->add("name",new BeNode(lc_to_utf8(name)));
Packit 8f70b4
Packit 8f70b4
   piece_length=16*1024;
Packit 8f70b4
   off_t length_scan=piece_length*2200;
Packit 8f70b4
   while(length_scan<=total_length) {
Packit 8f70b4
      piece_length*=2;
Packit 8f70b4
      length_scan*=2;
Packit 8f70b4
   }
Packit 8f70b4
   b_info->add("piece length",new BeNode(piece_length));
Packit 8f70b4
Packit 8f70b4
   if(files.count()==0) {
Packit 8f70b4
      b_info->add("length",new BeNode(total_length));
Packit 8f70b4
   } else {
Packit 8f70b4
      files.Sort(FileSet::BYNAME);
Packit 8f70b4
      files.rewind();
Packit 8f70b4
      xarray_p<BeNode> *b_files=new xarray_p<BeNode>();
Packit 8f70b4
      for(FileInfo *fi=files.curr(); fi; fi=files.next()) {
Packit 8f70b4
	 xarray_p<BeNode> *path=new xarray_p<BeNode>();
Packit 8f70b4
	 const char *name_utf8=lc_to_utf8(fi->name);
Packit 8f70b4
	 char *p=alloca_strdup(name_utf8);
Packit 8f70b4
	 for(p=strtok(p,"/"); p; p=strtok(NULL,"/"))
Packit 8f70b4
	    path->append(new BeNode(p));
Packit 8f70b4
	 xmap_p<BeNode> *b_file=new xmap_p<BeNode>();
Packit 8f70b4
	 b_file->add("path",new BeNode(path));
Packit 8f70b4
	 b_file->add("length",new BeNode(fi->size));
Packit 8f70b4
	 b_files->append(new BeNode(b_file));
Packit 8f70b4
      }
Packit 8f70b4
      b_info->add("files",new BeNode(b_files));
Packit 8f70b4
   }
Packit 8f70b4
   info=new BeNode(b_info);
Packit 8f70b4
}
Packit 8f70b4
void TorrentBuild::SetPiece(unsigned p,const xstring& sha)
Packit 8f70b4
{
Packit 8f70b4
   assert(pieces.length()==p*20); // require sequential building
Packit 8f70b4
   pieces.append(sha);
Packit 8f70b4
}
Packit 8f70b4
const xstring& TorrentBuild::GetMetadata()
Packit 8f70b4
{
Packit 8f70b4
   info->dict.add("pieces",new BeNode(pieces));
Packit 8f70b4
   return info->Pack();
Packit 8f70b4
}
Packit 8f70b4
const xstring& TorrentBuild::Status() const
Packit 8f70b4
{
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return xstring::get_tmp("");
Packit 8f70b4
   const char *curr=CurrPath();
Packit 8f70b4
   int count=files.count();
Packit 8f70b4
   if(curr[0])
Packit 8f70b4
      return xstring::format(plural("%d file$|s$ found, now scanning %s",count),count,curr);
Packit 8f70b4
   return xstring::format(plural("%d file$|s$ found",count),count);
Packit 8f70b4
}
Packit 8f70b4
int TorrentBuild::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   const char *curr_path=CurrPath();
Packit 8f70b4
   if(!curr_path) {
Packit 8f70b4
      Finish();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   const char *full_path=dir_file(top_path,curr_path);
Packit 8f70b4
   full_path=alloca_strdup(full_path);
Packit 8f70b4
Packit 8f70b4
   DIR *dir=opendir(full_path);
Packit 8f70b4
   if(!dir) {
Packit 8f70b4
      if(NonFatalError(errno))
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(dirs_to_scan.Count()<=1)
Packit 8f70b4
	 error=SysError();
Packit 8f70b4
      else
Packit 8f70b4
	 LogError(0,"opendir(%s): %s",full_path,strerror(errno));
Packit 8f70b4
      NextDir();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   LogNote(10,"scanning %s",full_path);
Packit 8f70b4
   struct dirent *entry;
Packit 8f70b4
   while((entry=readdir(dir))!=0) {
Packit 8f70b4
      if(!strcmp(entry->d_name,".") || !strcmp(entry->d_name,".."))
Packit 8f70b4
	 continue;
Packit 8f70b4
      struct stat st;
Packit 8f70b4
      const char *path=dir_file(full_path,entry->d_name);
Packit 8f70b4
      if(lstat(path,&st)==-1) {
Packit 8f70b4
	 LogError(0,"stat(%s): %s",path,strerror(errno));
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(S_ISREG(st.st_mode))
Packit 8f70b4
	 AddFile(dir_file(curr_path,entry->d_name),&st);
Packit 8f70b4
      else if(S_ISDIR(st.st_mode))
Packit 8f70b4
	 QueueDir(dir_file(curr_path,entry->d_name));
Packit 8f70b4
      else
Packit 8f70b4
	 LogNote(10,"ignoring %s (not a directory nor a plain file)",path);
Packit 8f70b4
   }
Packit 8f70b4
   closedir(dir);
Packit 8f70b4
   NextDir();
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
void TorrentBuild::AddFile(const char *path,struct stat *st)
Packit 8f70b4
{
Packit 8f70b4
   FileInfo *fi=new FileInfo(path);
Packit 8f70b4
   fi->SetSize(st->st_size);
Packit 8f70b4
   files.Add(fi);
Packit 8f70b4
   total_length+=st->st_size;
Packit 8f70b4
   LogNote(10,"adding %s, size %lld",path,(long long)fi->size);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Torrent::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(Done() || shutting_down)
Packit 8f70b4
      return m;
Packit 8f70b4
   if(!complete && !building && timeout_timer.Stopped()) {
Packit 8f70b4
      SetError("timed out with no progress");
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(build_md && !files) {
Packit 8f70b4
      if(!building)
Packit 8f70b4
	 building=new TorrentBuild(metainfo_url);
Packit 8f70b4
      if(!building->Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      if(building->Failed()) {
Packit 8f70b4
	 SetError(building->ErrorText());
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      InitTranslation();
Packit 8f70b4
      name.set(building->name);
Packit 8f70b4
      piece_length=building->piece_length;
Packit 8f70b4
      SetTotalLength(building->total_length);
Packit 8f70b4
      files=new TorrentFiles(building->GetFilesNode(),this);
Packit 8f70b4
      output_dir.set(building->GetBaseDirectory());
Packit 8f70b4
      StartValidating();
Packit 8f70b4
   }
Packit 8f70b4
   if(!metainfo_tree && metainfo_url && !md_download && !build_md) {
Packit 8f70b4
      // retrieve metainfo if don't have already.
Packit 8f70b4
      if(!metainfo_copy) {
Packit 8f70b4
	 if(metainfo_url.begins_with("magnet:?")) {
Packit 8f70b4
	    ParseMagnet(metainfo_url+8);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(metainfo_url.length()==40 && strspn(metainfo_url,"0123456789ABCDEFabcdef")==40
Packit 8f70b4
	 && access(metainfo_url,0)==-1) {
Packit 8f70b4
	    xstring& btih=xstring::get_tmp(metainfo_url);
Packit 8f70b4
	    btih.hex_decode();
Packit 8f70b4
	    assert(btih.length()==20);
Packit 8f70b4
	    info_hash.move_here(btih);
Packit 8f70b4
	    if(FindTorrent(info_hash)) {
Packit 8f70b4
	       SetError("This torrent is already running");
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    StartMetadataDownload();
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 FetchMetadataFromURL(metainfo_url);
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(metainfo_copy->Error()) {
Packit 8f70b4
	 SetError(Error::Fatal(metainfo_copy->ErrorText()));
Packit 8f70b4
	 metainfo_copy=0;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(!metainfo_copy->Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
      LogNote(9,"meta-data EOF\n");
Packit 8f70b4
      int rest;
Packit 8f70b4
      const char *metainfo_buf;
Packit 8f70b4
      int metainfo_len;
Packit 8f70b4
      metainfo_copy->put->Get(&metainfo_buf,&metainfo_len);
Packit 8f70b4
      metainfo_tree=BeNode::Parse(metainfo_buf,metainfo_len,&rest);
Packit 8f70b4
      metainfo_copy=0;
Packit 8f70b4
      if(!metainfo_tree) {
Packit 8f70b4
	 SetError("Meta-data parse error");
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(rest>0) {
Packit 8f70b4
	 SetError("Junk at the end of Meta-data");
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      InitTranslation();
Packit 8f70b4
Packit 8f70b4
      LogNote(10,"Received meta-data:");
Packit 8f70b4
      Log::global->Write(10,metainfo_tree->Format());
Packit 8f70b4
Packit 8f70b4
      if(metainfo_tree->type!=BeNode::BE_DICT) {
Packit 8f70b4
	 SetError("Meta-data: wrong top level type, must be DICT");
Packit 8f70b4
         return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      const char *retracker=ResMgr::Query("torrent:retracker",GetName());
Packit 8f70b4
      int retracker_len=xstrlen(retracker);
Packit 8f70b4
      BeNode *announce_list=metainfo_tree->lookup("announce-list",BeNode::BE_LIST);
Packit 8f70b4
      if(announce_list) {
Packit 8f70b4
	 for(int i=0; i<announce_list->list.length(); i++) {
Packit 8f70b4
	    BeNode *announce_list1=announce_list->list[i];
Packit 8f70b4
	    if(announce_list1->type!=BeNode::BE_LIST)
Packit 8f70b4
	       continue;
Packit 8f70b4
	    SMTaskRef<TorrentTracker> new_tracker;
Packit 8f70b4
	    for(int j=0; j<announce_list1->list.length(); j++) {
Packit 8f70b4
	       BeNode *announce=announce_list1->list[j];
Packit 8f70b4
	       if(announce->type!=BeNode::BE_STR)
Packit 8f70b4
		  continue;
Packit 8f70b4
	       if(retracker_len && !strncmp(retracker,announce->str,retracker_len))
Packit 8f70b4
		  retracker=0, retracker_len=0;
Packit 8f70b4
	       if(!new_tracker)
Packit 8f70b4
		  new_tracker=new TorrentTracker(this,announce->str);
Packit 8f70b4
	       else
Packit 8f70b4
		  new_tracker->AddURL(announce->str);
Packit 8f70b4
	    }
Packit 8f70b4
	    if(new_tracker && !new_tracker->Failed()) {
Packit 8f70b4
	       new_tracker->tracker_no=trackers.count();
Packit 8f70b4
	       trackers.append(new_tracker.borrow());
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(trackers.count()==0) {
Packit 8f70b4
	 const xstring& announce=metainfo_tree->lookup_str("announce");
Packit 8f70b4
	 if(announce) {
Packit 8f70b4
	    SMTaskRef<TorrentTracker> new_tracker(
Packit 8f70b4
	       new TorrentTracker(this,announce));
Packit 8f70b4
	    if(!new_tracker->Failed())
Packit 8f70b4
	       trackers.append(new_tracker.borrow());
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(retracker_len) {
Packit 8f70b4
	 SMTaskRef<TorrentTracker> new_tracker(new TorrentTracker(this,retracker));
Packit 8f70b4
	 if(!new_tracker->Failed()) {
Packit 8f70b4
	    new_tracker->tracker_no=trackers.count();
Packit 8f70b4
            trackers.append(new_tracker.borrow());
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      BeNode *nodes=metainfo_tree->lookup("nodes",BeNode::BE_LIST);
Packit 8f70b4
      if(nodes && dht) {
Packit 8f70b4
	 for(int i=0; i<nodes->list.count(); i++) {
Packit 8f70b4
	    BeNode *n=nodes->list[i];
Packit 8f70b4
	    if(n->type!=BeNode::BE_LIST || n->list.count()<2)
Packit 8f70b4
	       continue;
Packit 8f70b4
	    BeNode *b_host=n->list[0];
Packit 8f70b4
	    BeNode *b_port=n->list[1];
Packit 8f70b4
	    if(b_host->type!=BeNode::BE_STR || b_port->type!=BeNode::BE_INT)
Packit 8f70b4
	       continue;
Packit 8f70b4
	    if(b_port->num<=0 || b_port->num>=65535)
Packit 8f70b4
	       continue;
Packit 8f70b4
	    ParsedURL u;
Packit 8f70b4
	    u.host.set(b_host->str);
Packit 8f70b4
	    u.port.set(xstring::format("%u",(unsigned)b_port->num));
Packit 8f70b4
	    xstring_ca node(u.Combine());
Packit 8f70b4
	    dht->AddBootstrapNode(node);
Packit 8f70b4
#if INET6
Packit 8f70b4
	    dht_ipv6->AddBootstrapNode(node);
Packit 8f70b4
#endif
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      info=Lookup(metainfo_tree,"info",BeNode::BE_DICT);
Packit 8f70b4
      if(!info)
Packit 8f70b4
         return MOVED;
Packit 8f70b4
Packit 8f70b4
      if(SetMetadata(info->str))
Packit 8f70b4
	 Startup();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      if(Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
   }
Packit 8f70b4
   if(peers_scan_timer.Stopped())
Packit 8f70b4
      ScanPeers();
Packit 8f70b4
   if(validating) {
Packit 8f70b4
      ValidatePiece(validate_index++);
Packit 8f70b4
      if(validate_index
Packit 8f70b4
	 recv_rate.Add(piece_length);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      recv_rate.Add(last_piece_length);
Packit 8f70b4
      validating=false;
Packit 8f70b4
      recv_rate.Reset();
Packit 8f70b4
      if(total_left==0) {
Packit 8f70b4
	 complete=true;
Packit 8f70b4
	 seed_timer.Reset();
Packit 8f70b4
	 if(stop_if_complete) {
Packit 8f70b4
	    LogNote(2,"torrent is already complete, stopping");
Packit 8f70b4
	    Shutdown();
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(building) {
Packit 8f70b4
	 if(!complete) {
Packit 8f70b4
	    SetError("File validation error");
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!SetMetadata(building->GetMetadata()))
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 building=0;
Packit 8f70b4
	 xstring magnet("magnet:?xt=urn:btih:");
Packit 8f70b4
	 magnet.append(info_hash.hexdump());
Packit 8f70b4
	 magnet.appendf("&xl=%lld",(long long)total_length);
Packit 8f70b4
	 magnet.append("&dn=");
Packit 8f70b4
	 magnet.append_url_encoded(name,URL_PATH_UNSAFE);
Packit 8f70b4
	 printf("%s\n",magnet.get());
Packit 8f70b4
	 Startup();
Packit 8f70b4
      }
Packit 8f70b4
      RestartPeers();
Packit 8f70b4
      dht_announce_timer.Stop();
Packit 8f70b4
   }
Packit 8f70b4
   if(GetPort())
Packit 8f70b4
      StartTrackers();
Packit 8f70b4
   if(dht_announce_timer.Stopped())
Packit 8f70b4
      AnnounceDHT();
Packit 8f70b4
Packit 8f70b4
   // count peers
Packit 8f70b4
   connected_peers_count=0;
Packit 8f70b4
   active_peers_count=0;
Packit 8f70b4
   complete_peers_count=0;
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      connected_peers_count+=peers[i]->Connected();
Packit 8f70b4
      active_peers_count+=peers[i]->Active();
Packit 8f70b4
      complete_peers_count+=peers[i]->Complete();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!metadata)
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(optimistic_unchoke_timer.Stopped())
Packit 8f70b4
      OptimisticUnchoke();
Packit 8f70b4
Packit 8f70b4
   // rebuild lists of needed pieces
Packit 8f70b4
   if(!complete && (pieces_needed.count()==0 || pieces_timer.Stopped()))
Packit 8f70b4
      RebuildPiecesNeeded();
Packit 8f70b4
Packit 8f70b4
   if(complete) {
Packit 8f70b4
      if(pieces_timer.Stopped()) {
Packit 8f70b4
	 CalcPerPieceRatio();
Packit 8f70b4
	 pieces_timer.Reset();
Packit 8f70b4
      }
Packit 8f70b4
      if(SeededEnough()) {
Packit 8f70b4
	 Shutdown();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::BlackListPeer(const TorrentPeer *peer,const char *timeout)
Packit 8f70b4
{
Packit 8f70b4
   if(peer->IsPassive() || GetTorrentsCount()==0)
Packit 8f70b4
      return;
Packit 8f70b4
   if(!black_list)
Packit 8f70b4
      black_list=new TorrentBlackList();
Packit 8f70b4
   black_list->Add(peer->GetAddress(),timeout);
Packit 8f70b4
}
Packit 8f70b4
bool Torrent::BlackListed(const TorrentPeer *peer)
Packit 8f70b4
{
Packit 8f70b4
   return black_list && black_list->Listed(peer->GetAddress());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Torrent::CanAccept() const
Packit 8f70b4
{
Packit 8f70b4
   return !validating && decline_timer.Stopped();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::Accept(int s,const sockaddr_u *addr,IOBuffer *rb)
Packit 8f70b4
{
Packit 8f70b4
   if(!CanAccept()) {
Packit 8f70b4
      LogNote(4,"declining new connection");
Packit 8f70b4
      Delete(rb);
Packit 8f70b4
      close(s);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   TorrentPeer *p=new TorrentPeer(this,addr,TorrentPeer::TR_ACCEPTED);
Packit 8f70b4
   p->Connect(s,rb);
Packit 8f70b4
   AddPeer(p);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::AddPeer(TorrentPeer *peer)
Packit 8f70b4
{
Packit 8f70b4
   if(BlackListed(peer)) {
Packit 8f70b4
      Delete(peer);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      if(peers[i]->AddressEq(peer)) {
Packit 8f70b4
	 if(peer->Connected() && !peers[i]->Connected()) {
Packit 8f70b4
	    peers[i]=peer;
Packit 8f70b4
	 } else {
Packit 8f70b4
	    Delete(peer);
Packit 8f70b4
	 }
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   peers.append(peer);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Torrent::GetWantedPeersCount() const
Packit 8f70b4
{
Packit 8f70b4
   int numwant=complete?seed_min_peers:max_peers/2;
Packit 8f70b4
   if(numwant>peers.count())
Packit 8f70b4
      numwant-=peers.count();
Packit 8f70b4
   else
Packit 8f70b4
      numwant=0;
Packit 8f70b4
   if(shutting_down)
Packit 8f70b4
      numwant=-1;
Packit 8f70b4
   if(numwant>1) {
Packit 8f70b4
      // count almost ready for request trackers
Packit 8f70b4
      int trackers_count=0;
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 trackers_count+=(trackers[i]->tracker_timer.TimeLeft()<60);
Packit 8f70b4
      // request peers from all trackers evenly (round up).
Packit 8f70b4
      if(trackers_count)
Packit 8f70b4
	 numwant=(numwant+trackers_count-1)/trackers_count;
Packit 8f70b4
   }
Packit 8f70b4
   return numwant;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *Torrent::MakePath(BeNode *p) const
Packit 8f70b4
{
Packit 8f70b4
   BeNode *path=p->lookup("path.utf-8",BeNode::BE_LIST);
Packit 8f70b4
   void (Torrent::*tr)(BeNode*)const=&Torrent::TranslateStringFromUTF8;
Packit 8f70b4
   if(!path) {
Packit 8f70b4
      path=p->lookup("path",BeNode::BE_LIST);
Packit 8f70b4
      tr=&Torrent::TranslateString;
Packit 8f70b4
   }
Packit 8f70b4
   static xstring buf;
Packit 8f70b4
   buf.set(name);
Packit 8f70b4
   if(buf.eq("..") || buf[0]=='/') {
Packit 8f70b4
      buf.set_substr(0,0,"_",1);
Packit 8f70b4
   }
Packit 8f70b4
   for(int i=0; i<path->list.count(); i++) {
Packit 8f70b4
      BeNode *e=path->list[i];
Packit 8f70b4
      if(e->type==BeNode::BE_STR) {
Packit 8f70b4
	 (this->*tr)(e);
Packit 8f70b4
	 buf.append('/');
Packit 8f70b4
	 if(e->str_lc.eq(".."))
Packit 8f70b4
	    buf.append('_');
Packit 8f70b4
	 buf.append(e->str_lc);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
const char *Torrent::FindFileByPosition(unsigned piece,unsigned begin,off_t *f_pos,off_t *f_tail) const
Packit 8f70b4
{
Packit 8f70b4
   off_t target_pos=(off_t)piece*piece_length+begin;
Packit 8f70b4
   TorrentFile *file=files->FindByPosition(target_pos);
Packit 8f70b4
   if(!file)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   *f_pos=target_pos-file->pos;
Packit 8f70b4
   *f_tail=file->length-*f_pos;
Packit 8f70b4
Packit 8f70b4
   return file->path;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
TorrentFiles::TorrentFiles(const BeNode *files,const Torrent *t)
Packit 8f70b4
{
Packit 8f70b4
   if(!files) {
Packit 8f70b4
      grow_space(1);
Packit 8f70b4
      set_length(1);
Packit 8f70b4
      file(0)->set(t->GetName(),0,t->TotalLength());
Packit 8f70b4
   } else {
Packit 8f70b4
      int count=files->list.length();
Packit 8f70b4
      grow_space(count);
Packit 8f70b4
      set_length(count);
Packit 8f70b4
      off_t scan_pos=0;
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 BeNode *node=files->list[i];
Packit 8f70b4
	 off_t file_length=node->lookup_int("length");
Packit 8f70b4
	 file(i)->set(t->MakePath(node),scan_pos,file_length);
Packit 8f70b4
	 scan_pos+=file_length;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   qsort(pos_cmp);
Packit 8f70b4
}
Packit 8f70b4
TorrentFile *TorrentFiles::FindByPosition(off_t pos)
Packit 8f70b4
{
Packit 8f70b4
   int i=0;
Packit 8f70b4
   int j=length()-1;
Packit 8f70b4
   while(i<=j) {
Packit 8f70b4
      // invariant: the target element is in the range [i,j]
Packit 8f70b4
      int m=(i+j)/2;
Packit 8f70b4
      if(file(m)->contains_pos(pos))
Packit 8f70b4
	 return file(m);
Packit 8f70b4
      if(file(m)->pos>pos)
Packit 8f70b4
	 j=m-1;
Packit 8f70b4
      else
Packit 8f70b4
	 i=m+1;
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FDCache::FDCache()
Packit 8f70b4
   : clean_timer(1)
Packit 8f70b4
{
Packit 8f70b4
   max_count=16;
Packit 8f70b4
   max_time=30;
Packit 8f70b4
}
Packit 8f70b4
FDCache::~FDCache()
Packit 8f70b4
{
Packit 8f70b4
   CloseAll();
Packit 8f70b4
}
Packit 8f70b4
void FDCache::Clean()
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i<3; i++) {
Packit 8f70b4
      xmap<FD>& cache=this->cache[i];
Packit 8f70b4
      for(const FD *f=&cache.each_begin(); f->last_used; f=&cache.each_next()) {
Packit 8f70b4
	 if(f->fd==-1) {
Packit 8f70b4
	    if(f->last_used+1
Packit 8f70b4
	       cache.remove(cache.each_key());
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(f->last_used+max_time
Packit 8f70b4
	    ProtoLog::LogNote(9,"closing %s",cache.each_key().get());
Packit 8f70b4
	    close(f->fd);
Packit 8f70b4
	    cache.remove(cache.each_key());
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   while(Count()>max_count && CloseOne())
Packit 8f70b4
      /*empty*/;
Packit 8f70b4
   if(Count()>0)
Packit 8f70b4
      clean_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
int FDCache::Do()
Packit 8f70b4
{
Packit 8f70b4
   if(clean_timer.Stopped())
Packit 8f70b4
      Clean();
Packit 8f70b4
   return STALL;
Packit 8f70b4
}
Packit 8f70b4
void FDCache::Close(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   const xstring &n=xstring::get_tmp(name);
Packit 8f70b4
   for(int i=0; i<3; i++) {
Packit 8f70b4
      const FD& f=cache[i].lookup(n);
Packit 8f70b4
      if(f.last_used!=0) {
Packit 8f70b4
	 if(f.fd!=-1) {
Packit 8f70b4
	    ProtoLog::LogNote(9,"closing %s",name);
Packit 8f70b4
#ifdef HAVE_POSIX_FADVISE
Packit 8f70b4
	    if(i==O_RDONLY) // avoid filling up the cache
Packit 8f70b4
	       posix_fadvise(f.fd,0,0,POSIX_FADV_DONTNEED);
Packit 8f70b4
#endif
Packit 8f70b4
	    close(f.fd);
Packit 8f70b4
	 }
Packit 8f70b4
	 cache[i].remove(n);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void FDCache::CloseAll()
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i<3; i++) {
Packit 8f70b4
      xmap<FD>& cache=this->cache[i];
Packit 8f70b4
      for(const FD *f=&cache.each_begin(); f->last_used; f=&cache.each_next()) {
Packit 8f70b4
	 if(f->fd!=-1) {
Packit 8f70b4
	    ProtoLog::LogNote(9,"closing %s",cache.each_key().get());
Packit 8f70b4
	    close(f->fd);
Packit 8f70b4
	 }
Packit 8f70b4
	 cache.remove(cache.each_key());
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
bool FDCache::CloseOne()
Packit 8f70b4
{
Packit 8f70b4
   int oldest_mode=0;
Packit 8f70b4
   int oldest_fd=-1;
Packit 8f70b4
   int oldest_time=0;
Packit 8f70b4
   const xstring *oldest_key=0;
Packit 8f70b4
   for(int i=0; i<3; i++) {
Packit 8f70b4
      xmap<FD>& cache=this->cache[i];
Packit 8f70b4
      for(const FD *f=&cache.each_begin(); f->last_used; f=&cache.each_next()) {
Packit 8f70b4
	 if(f->fd==-1)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(oldest_key==0 || f->last_used
Packit 8f70b4
	    oldest_key=&cache.each_key();
Packit 8f70b4
	    oldest_time=f->last_used;
Packit 8f70b4
	    oldest_fd=f->fd;
Packit 8f70b4
	    oldest_mode=i;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(!oldest_key)
Packit 8f70b4
      return false;
Packit 8f70b4
   if(oldest_fd!=-1) {
Packit 8f70b4
      ProtoLog::LogNote(9,"closing %s",oldest_key->get());
Packit 8f70b4
      close(oldest_fd);
Packit 8f70b4
   }
Packit 8f70b4
   cache[oldest_mode].remove(*oldest_key);
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
int FDCache::Count() const
Packit 8f70b4
{
Packit 8f70b4
   return cache[0].count()+cache[1].count()+cache[2].count();
Packit 8f70b4
}
Packit 8f70b4
int FDCache::OpenFile(const char *file,int m,off_t size)
Packit 8f70b4
{
Packit 8f70b4
   int ci=m&3;
Packit 8f70b4
   assert(ci<3);
Packit 8f70b4
   FD& f=cache[ci].lookup_Lv(file);
Packit 8f70b4
   if(f.last_used!=0) {
Packit 8f70b4
      if(f.fd!=-1)
Packit 8f70b4
	 f.last_used=now.UnixTime();
Packit 8f70b4
      else
Packit 8f70b4
	 errno=f.saved_errno;
Packit 8f70b4
      return f.fd;
Packit 8f70b4
   }
Packit 8f70b4
   if(ci==O_RDONLY) {
Packit 8f70b4
      // O_RDWR also will do, check if we have it.
Packit 8f70b4
      const FD& f_rw=cache[O_RDWR].lookup(file);
Packit 8f70b4
      if(f_rw.last_used!=0 && f_rw.fd!=-1) {
Packit 8f70b4
	 // don't update last_used to expire it and reopen with proper mode
Packit 8f70b4
	 return f_rw.fd;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   Clean();
Packit 8f70b4
   clean_timer.Reset();
Packit 8f70b4
   ProtoLog::LogNote(9,"opening %s",file);
Packit 8f70b4
   int fd;
Packit 8f70b4
   do {
Packit 8f70b4
      fd=open(file,m,0664);
Packit 8f70b4
   } while(fd==-1 && (errno==EMFILE || errno==ENFILE) && CloseOne());
Packit 8f70b4
   FD new_entry = {fd,errno,now.UnixTime()};
Packit 8f70b4
   cache[ci].add(file,new_entry);
Packit 8f70b4
   if(fd!=-1)
Packit 8f70b4
      fcntl(fd,F_SETFD,FD_CLOEXEC);
Packit 8f70b4
   if(fd==-1 || size==0)
Packit 8f70b4
      return fd;
Packit 8f70b4
   if(ci==O_RDWR && QueryBool("file:use-fallocate",0)) {
Packit 8f70b4
      struct stat st;
Packit 8f70b4
      // check if it is newly created file, then allocate space
Packit 8f70b4
      if(fstat(fd,&st)!=-1 && st.st_size==0) {
Packit 8f70b4
	 if(lftp_fallocate(fd,size)==-1 && errno!=ENOSYS && errno!=EOPNOTSUPP) {
Packit 8f70b4
	    ProtoLog::LogError(9,"space allocation for %s (%lld bytes) failed: %s",
Packit 8f70b4
	       file,(long long)size,strerror(errno));
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
#ifdef HAVE_POSIX_FADVISE
Packit 8f70b4
   if(ci==O_RDONLY) {
Packit 8f70b4
      // validation mode (when validating, size>0)
Packit 8f70b4
      posix_fadvise(fd,0,size,POSIX_FADV_SEQUENTIAL);
Packit 8f70b4
      posix_fadvise(fd,0,size,POSIX_FADV_NOREUSE);
Packit 8f70b4
   }
Packit 8f70b4
#endif//HAVE_POSIX_FADVISE
Packit 8f70b4
   return fd;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Torrent::OpenFile(const char *file,int m,off_t size)
Packit 8f70b4
{
Packit 8f70b4
   bool did_mkdir=false;
Packit 8f70b4
   if(!fd_cache)
Packit 8f70b4
      fd_cache=new FDCache();
Packit 8f70b4
try_again:
Packit 8f70b4
   const char *cf=dir_file(output_dir,file);
Packit 8f70b4
   int fd=fd_cache->OpenFile(cf,m,size);
Packit 8f70b4
   while(fd==-1 && (errno==EMFILE || errno==ENFILE) && peers.count()>0) {
Packit 8f70b4
      peers.chop();  // free an fd
Packit 8f70b4
      fd=fd_cache->OpenFile(cf,m,size);
Packit 8f70b4
   }
Packit 8f70b4
   if(validating)
Packit 8f70b4
      return fd;
Packit 8f70b4
   if(fd==-1)
Packit 8f70b4
      fd_cache->Close(cf); // remove negative cache.
Packit 8f70b4
   if(fd==-1 && errno==ENOENT && !did_mkdir) {
Packit 8f70b4
      LogError(10,"open(%s): %s",cf,strerror(errno));
Packit 8f70b4
      const char *sl=strchr(file,'/');
Packit 8f70b4
      while(sl)
Packit 8f70b4
      {
Packit 8f70b4
	 if(sl>file) {
Packit 8f70b4
	    if(mkdir(cf=dir_file(output_dir,xstring::get_tmp(file,sl-file)),0775)==-1 && errno!=EEXIST)
Packit 8f70b4
	       LogError(9,"mkdir(%s): %s",cf,strerror(errno));
Packit 8f70b4
	 }
Packit 8f70b4
	 sl=strchr(sl+1,'/');
Packit 8f70b4
      }
Packit 8f70b4
      did_mkdir=true;
Packit 8f70b4
      goto try_again;
Packit 8f70b4
   }
Packit 8f70b4
   return fd;
Packit 8f70b4
}
Packit 8f70b4
void Torrent::CloseFile(const char *file) const
Packit 8f70b4
{
Packit 8f70b4
   if(!fd_cache)
Packit 8f70b4
      return;
Packit 8f70b4
   fd_cache->Close(dir_file(output_dir,file));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::SetPieceNotWanted(unsigned piece)
Packit 8f70b4
{
Packit 8f70b4
   for(int j=0; j
Packit 8f70b4
      if(pieces_needed[j]==piece) {
Packit 8f70b4
	 pieces_needed.remove(j);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#define MIN(a,b) ((a)<(b)?(a):(b))
Packit 8f70b4
Packit 8f70b4
void Torrent::StoreBlock(unsigned piece,unsigned begin,unsigned len,const char *buf,TorrentPeer *src_peer)
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      peers[i]->CancelBlock(piece,begin);
Packit 8f70b4
Packit 8f70b4
   unsigned b=begin/BLOCK_SIZE;
Packit 8f70b4
   int bc=(len+BLOCK_SIZE-1)/BLOCK_SIZE;
Packit 8f70b4
Packit 8f70b4
   off_t f_pos=0;
Packit 8f70b4
   off_t f_rest=len;
Packit 8f70b4
   while(len>0) {
Packit 8f70b4
      const char *file=FindFileByPosition(piece,begin,&f_pos,&f_rest);
Packit 8f70b4
      int fd=OpenFile(file,O_RDWR|O_CREAT,f_pos+f_rest);
Packit 8f70b4
      if(fd==-1) {
Packit 8f70b4
	 SetError(xstring::format("open(%s): %s",file,strerror(errno)));
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      int w=pwrite(fd,buf,MIN(f_rest,len),f_pos);
Packit 8f70b4
      int saved_errno=errno;
Packit 8f70b4
      if(w==-1) {
Packit 8f70b4
	 SetError(xstring::format("pwrite(%s): %s",file,strerror(saved_errno)));
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      if(w==0) {
Packit 8f70b4
	 SetError(xstring::format("pwrite(%s): write error - disk full?",file));
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      buf+=w;
Packit 8f70b4
      begin+=w;
Packit 8f70b4
      len-=w;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   while(bc-->0) {
Packit 8f70b4
      SetBlockPresent(piece,b++);
Packit 8f70b4
   }
Packit 8f70b4
   if(AllBlocksPresent(piece) && !my_bitfield->get_bit(piece)) {
Packit 8f70b4
      ValidatePiece(piece);
Packit 8f70b4
      if(!my_bitfield->get_bit(piece)) {
Packit 8f70b4
	 LogError(0,"new piece %u digest mismatch",piece);
Packit 8f70b4
	 src_peer->MarkPieceInvalid(piece);
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      LogNote(3,"piece %u complete",piece);
Packit 8f70b4
      timeout_timer.Reset();
Packit 8f70b4
      SetPieceNotWanted(piece);
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 peers[i]->Have(piece);
Packit 8f70b4
      if(my_bitfield->has_all_set() && !complete) {
Packit 8f70b4
	 complete=true;
Packit 8f70b4
	 seed_timer.Reset();
Packit 8f70b4
	 end_game=false;
Packit 8f70b4
	 ScanPeers();
Packit 8f70b4
	 SendTrackersRequest("completed");
Packit 8f70b4
	 recv_rate.Reset();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void Torrent::SendTrackersRequest(const char *event) const
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      if(!trackers[i]->Failed())
Packit 8f70b4
	 trackers[i]->SendTrackerRequest(event);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const xstring& Torrent::RetrieveBlock(unsigned piece,unsigned begin,unsigned len)
Packit 8f70b4
{
Packit 8f70b4
   static xstring buf;
Packit 8f70b4
   buf.truncate(0);
Packit 8f70b4
   buf.get_space(len);
Packit 8f70b4
Packit 8f70b4
   off_t f_pos=0;
Packit 8f70b4
   off_t f_rest=len;
Packit 8f70b4
   while(len>0) {
Packit 8f70b4
      const char *file=FindFileByPosition(piece,begin,&f_pos,&f_rest);
Packit 8f70b4
      int fd=OpenFile(file,O_RDONLY,validating?f_pos+f_rest:0);
Packit 8f70b4
      if(fd==-1)
Packit 8f70b4
	 return xstring::null;
Packit 8f70b4
      int w=pread(fd,buf.add_space(len),MIN(f_rest,len),f_pos);
Packit 8f70b4
      if(w==-1) {
Packit 8f70b4
	 SetError(xstring::format("pread(%s): %s",file,strerror(errno)));
Packit 8f70b4
	 return xstring::null;
Packit 8f70b4
      }
Packit 8f70b4
      if(w==0) {
Packit 8f70b4
// 	 buf.append_padding(len,'\0');
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      buf.add_commit(w);
Packit 8f70b4
      begin+=w;
Packit 8f70b4
      len-=w;
Packit 8f70b4
      if(validating && w==f_rest)
Packit 8f70b4
	 CloseFile(file);
Packit 8f70b4
   }
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
TorrentPeer *Torrent::FindPeerById(const xstring& p_id)
Packit 8f70b4
{
Packit 8f70b4
   // linear search - peers count<100, called rarely
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      const TorrentPeer *peer=peers[i];
Packit 8f70b4
      if(peer->peer_id.eq(p_id))
Packit 8f70b4
	 return const_cast<TorrentPeer*>(peer);
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::ScanPeers() {
Packit 8f70b4
   // scan existing peers
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      const TorrentPeer *peer=peers[i];
Packit 8f70b4
      const char *blacklist_time="2h";
Packit 8f70b4
      if(peer->Failed()) {
Packit 8f70b4
	 LogError(2,"peer %s failed: %s",peer->GetName(),peer->ErrorText());
Packit 8f70b4
      } else if(peer->Disconnected() && peer->ActivityTimedOut()) {
Packit 8f70b4
	 LogNote(4,"peer %s disconnected",peer->GetName());
Packit 8f70b4
      } else if(peer->myself) {
Packit 8f70b4
	 LogNote(4,"removing myself-connected peer %s",peer->GetName());
Packit 8f70b4
	 blacklist_time="forever";
Packit 8f70b4
      } else if(peer->duplicate) {
Packit 8f70b4
	 LogNote(4,"removing duplicate peer %s",peer->GetName());
Packit 8f70b4
      } else if(complete && peer->Seed()) {
Packit 8f70b4
	 LogNote(4,"removing unneeded peer %s (%s)",peer->GetName(),peers[i]->Status());
Packit 8f70b4
	 blacklist_time="1d";
Packit 8f70b4
      } else {
Packit 8f70b4
	 // keep the peer.
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(blacklist_time)
Packit 8f70b4
	 BlackListPeer(peer,blacklist_time);
Packit 8f70b4
      peers.remove(i--);
Packit 8f70b4
   }
Packit 8f70b4
   ReducePeers();
Packit 8f70b4
   peers_scan_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::ReducePeers()
Packit 8f70b4
{
Packit 8f70b4
   if(max_peers>0 && peers.count()>max_peers) {
Packit 8f70b4
      // remove least interesting peers.
Packit 8f70b4
      peers.qsort(PeersCompareActivity);
Packit 8f70b4
      int to_remove=peers.count()-max_peers;
Packit 8f70b4
      while(to_remove-->0) {
Packit 8f70b4
	 TimeInterval max_idle(peers.last()->activity_timer.TimePassed());
Packit 8f70b4
	 LogNote(3,"removing peer %s (too many; idle:%s)",peers.last()->GetName(),
Packit 8f70b4
	    max_idle.toString(TimeInterval::TO_STR_TERSE+TimeInterval::TO_STR_TRANSLATE));
Packit 8f70b4
	 peers.chop();
Packit 8f70b4
	 if(max_idle<60)
Packit 8f70b4
	    decline_timer.Set(60-max_idle.Seconds());
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   peers.qsort(complete ? PeersCompareSendRate : PeersCompareRecvRate);
Packit 8f70b4
   ReduceUploaders();
Packit 8f70b4
   ReduceDownloaders();
Packit 8f70b4
   UnchokeBestUploaders();
Packit 8f70b4
}
Packit 8f70b4
void Torrent::ReduceUploaders()
Packit 8f70b4
{
Packit 8f70b4
   bool rate_low = RateLow(RateLimit::GET);
Packit 8f70b4
   if(am_interested_peers_count < (rate_low?max_uploaders:min_uploaders+1))
Packit 8f70b4
      return;
Packit 8f70b4
   // make the slowest uninterested
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      TorrentPeer *peer=peers[i].get_non_const();
Packit 8f70b4
      if(peer->am_interested) {
Packit 8f70b4
	 if(peer->interest_timer.TimePassed() <= 30)
Packit 8f70b4
	    break;
Packit 8f70b4
	 peer->SetAmInterested(false);
Packit 8f70b4
	 if(am_interested_peers_count < max_uploaders)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void Torrent::ReduceDownloaders()
Packit 8f70b4
{
Packit 8f70b4
   bool rate_low = RateLow(RateLimit::PUT);
Packit 8f70b4
   if(am_not_choking_peers_count < (rate_low?max_downloaders:min_downloaders+1))
Packit 8f70b4
      return;
Packit 8f70b4
   // choke the slowest
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      TorrentPeer *peer=peers[i].get_non_const();
Packit 8f70b4
      if(!peer->am_choking && peer->peer_interested) {
Packit 8f70b4
	 if(peer->choke_timer.TimePassed() <= 30)
Packit 8f70b4
	    break;
Packit 8f70b4
	 peer->SetAmChoking(true);
Packit 8f70b4
	 if(am_not_choking_peers_count < max_downloaders)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Torrent::NeedMoreUploaders()
Packit 8f70b4
{
Packit 8f70b4
   if(!metadata || validating)
Packit 8f70b4
      return false;
Packit 8f70b4
   return RateLow(RateLimit::GET) && am_interested_peers_count < max_uploaders
Packit 8f70b4
      && am_interested_timer.Stopped();
Packit 8f70b4
}
Packit 8f70b4
bool Torrent::AllowMoreDownloaders()
Packit 8f70b4
{
Packit 8f70b4
   if(!metadata || validating)
Packit 8f70b4
      return false;
Packit 8f70b4
   return RateLow(RateLimit::PUT) && am_not_choking_peers_count < max_downloaders;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::UnchokeBestUploaders()
Packit 8f70b4
{
Packit 8f70b4
   if(!metadata)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   // unchoke 4 best uploaders
Packit 8f70b4
   int limit = 4;
Packit 8f70b4
Packit 8f70b4
   int count=0;
Packit 8f70b4
   for(int i=peers.count()-1; i>=0 && count
Packit 8f70b4
      TorrentPeer *peer=peers[i].get_non_const();
Packit 8f70b4
      if(!peer->Connected())
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(!peer->choke_timer.Stopped())
Packit 8f70b4
	 continue;   // cannot change choke status yet
Packit 8f70b4
      if(!peer->peer_interested)
Packit 8f70b4
	 continue;
Packit 8f70b4
      peer->SetAmChoking(false);
Packit 8f70b4
      count++;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void Torrent::OptimisticUnchoke()
Packit 8f70b4
{
Packit 8f70b4
   xarray<TorrentPeer*> choked_peers;
Packit 8f70b4
   for(int i=peers.count()-1; i>=0; i--) {
Packit 8f70b4
      TorrentPeer *peer=peers[i].get_non_const();
Packit 8f70b4
      if(!peer->Connected())
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(!peer->choke_timer.Stopped())
Packit 8f70b4
	 continue;   // cannot change choke status yet
Packit 8f70b4
      if(peer->am_choking) {
Packit 8f70b4
	 if(!peer->peer_interested) {
Packit 8f70b4
	    peer->SetAmChoking(false);
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
	 choked_peers.append(peer);
Packit 8f70b4
	 if(peer->retry_timer.TimePassed()<60) {
Packit 8f70b4
	    // newly connected is more likely to be unchoked
Packit 8f70b4
	    choked_peers.append(peer);
Packit 8f70b4
	    choked_peers.append(peer);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(choked_peers.count()==0)
Packit 8f70b4
      return;
Packit 8f70b4
   choked_peers[rand()/13%choked_peers.count()]->SetAmChoking(false);
Packit 8f70b4
   optimistic_unchoke_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Torrent::PeerBytesAllowed(const TorrentPeer *peer,RateLimit::dir_t dir)
Packit 8f70b4
{
Packit 8f70b4
   float peer_rate=(dir==RateLimit::GET ? peer->peer_send_rate : peer->peer_recv_rate).Get();
Packit 8f70b4
   float total_rate=(dir==RateLimit::GET ? send_rate : recv_rate).Get();
Packit 8f70b4
   const int min_rate = 1024;
Packit 8f70b4
   // the more is the opposite rate the more rate allowed, with a minimum
Packit 8f70b4
   float bytes = rate_limit.BytesAllowed(dir);
Packit 8f70b4
   bytes *= (peer_rate  + min_rate)
Packit 8f70b4
          / (total_rate + active_peers_count*min_rate);
Packit 8f70b4
   return (int)bytes;
Packit 8f70b4
}
Packit 8f70b4
void Torrent::PeerBytesUsed(int b,RateLimit::dir_t dir)
Packit 8f70b4
{
Packit 8f70b4
   rate_limit.BytesUsed(b,dir);
Packit 8f70b4
}
Packit 8f70b4
void Torrent::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   const char *c=GetName();
Packit 8f70b4
   max_peers=ResMgr::Query("torrent:max-peers",c);
Packit 8f70b4
   seed_min_peers=ResMgr::Query("torrent:seed-min-peers",c);
Packit 8f70b4
   stop_on_ratio=ResMgr::Query("torrent:stop-on-ratio",c);
Packit 8f70b4
   stop_min_ppr=ResMgr::Query("torrent:stop-min-ppr",c);
Packit 8f70b4
   rate_limit.Reconfig(name,metainfo_url);
Packit 8f70b4
   if(listener)
Packit 8f70b4
      StartDHT();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const xstring& Torrent::Status()
Packit 8f70b4
{
Packit 8f70b4
   if(metainfo_copy)
Packit 8f70b4
      return xstring::format(_("Getting meta-data: %s"),metainfo_copy->GetStatus());
Packit 8f70b4
   if(validating) {
Packit 8f70b4
      return xstring::format(_("Validation: %u/%u (%u%%) %s%s"),validate_index,total_pieces,
Packit 8f70b4
	 validate_index*100/total_pieces,recv_rate.GetStrS(),
Packit 8f70b4
	 recv_rate.GetETAStrFromSize((off_t)(total_pieces-validate_index-1)*piece_length+last_piece_length).get());
Packit 8f70b4
   }
Packit 8f70b4
   if(building)
Packit 8f70b4
      return building->Status();
Packit 8f70b4
   if(!metadata && !build_md) {
Packit 8f70b4
      if(md_download.length()>0)
Packit 8f70b4
	 return xstring::format(_("Getting meta-data: %s"),
Packit 8f70b4
	    xstring::format("%u/%u",(unsigned)md_download.length(),(unsigned)metadata_size).get());
Packit 8f70b4
      else
Packit 8f70b4
	 return xstring::get_tmp(_("Waiting for meta-data..."));
Packit 8f70b4
   }
Packit 8f70b4
   if(shutting_down) {
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 if(!trackers[i]->IsActive())
Packit 8f70b4
	    continue;
Packit 8f70b4
	 const char *status=trackers[i]->Status();
Packit 8f70b4
	 if(status[0]) {
Packit 8f70b4
	    xstring &s=xstring::get_tmp(_("Shutting down: "));
Packit 8f70b4
	    if(trackers.count()>1)
Packit 8f70b4
	       s.appendf("%d. ",i+1);
Packit 8f70b4
	    s.append(status);
Packit 8f70b4
	    return s;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      return xstring::get_tmp("");
Packit 8f70b4
   }
Packit 8f70b4
   if(total_length==0)
Packit 8f70b4
      return xstring::get_tmp("");
Packit 8f70b4
Packit 8f70b4
   char dn_buf[LONGEST_HUMAN_READABLE + 1];
Packit 8f70b4
   char up_buf[LONGEST_HUMAN_READABLE + 1];
Packit 8f70b4
   xstring& buf=xstring::format("dn:%s %sup:%s %s",
Packit 8f70b4
      human_readable(total_recv, dn_buf, human_autoscale|human_SI, 1, 1),
Packit 8f70b4
      recv_rate.GetStrS(),
Packit 8f70b4
      human_readable(total_sent, up_buf, human_autoscale|human_SI, 1, 1),
Packit 8f70b4
      send_rate.GetStrS());
Packit 8f70b4
   if(!complete) {
Packit 8f70b4
      buf.appendf("complete:%u/%u (%u%%)",complete_pieces,total_pieces,
Packit 8f70b4
	 unsigned((total_length-total_left)*100/total_length));
Packit 8f70b4
      buf.append(' ');
Packit 8f70b4
      if(min_piece_sources)
Packit 8f70b4
	 buf.append(recv_rate.GetETAStrFromSize(total_left));
Packit 8f70b4
      if(end_game)
Packit 8f70b4
	 buf.append(" end-game");
Packit 8f70b4
   } else {
Packit 8f70b4
      buf.appendf("complete, ratio:%.2f/%.2f/%.2f",
Packit 8f70b4
	 GetMinPerPieceRatio(),GetRatio(),GetMaxPerPieceRatio());
Packit 8f70b4
   }
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
TorrentPeer::TorrentPeer(Torrent *p,const sockaddr_u *a,int t_no)
Packit 8f70b4
   : timeout_timer(360), retry_timer(30), keepalive_timer(120),
Packit 8f70b4
     choke_timer(10), interest_timer(10), activity_timer(300),
Packit 8f70b4
     msg_ext_metadata(0), msg_ext_pex(0), metadata_size(0)
Packit 8f70b4
{
Packit 8f70b4
   parent=p;
Packit 8f70b4
   tracker_no=t_no;
Packit 8f70b4
   addr=*a;
Packit 8f70b4
   sock=-1;
Packit 8f70b4
   udp_port=0;
Packit 8f70b4
   connected=false;
Packit 8f70b4
   passive=false;
Packit 8f70b4
   duplicate=0;
Packit 8f70b4
   myself=false;
Packit 8f70b4
   peer_choking=true;
Packit 8f70b4
   am_choking=true;
Packit 8f70b4
   peer_interested=false;
Packit 8f70b4
   am_interested=false;
Packit 8f70b4
   upload_only=false;
Packit 8f70b4
   peer_complete_pieces=0;
Packit 8f70b4
   retry_timer.Stop();
Packit 8f70b4
   retry_timer.AddRandom(2);
Packit 8f70b4
   choke_timer.Stop();
Packit 8f70b4
   interest_timer.Stop();
Packit 8f70b4
   last_piece=NO_PIECE;
Packit 8f70b4
   if(addr.is_reserved() || addr.is_multicast() || addr.port()==0)
Packit 8f70b4
      SetError("invalid peer address");
Packit 8f70b4
   peer_bytes_pool[0]=peer_bytes_pool[1]=0;
Packit 8f70b4
   peer_recv=peer_sent=0;
Packit 8f70b4
   invalid_piece_count=0;
Packit 8f70b4
}
Packit 8f70b4
TorrentPeer::~TorrentPeer()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::PrepareToDie()
Packit 8f70b4
{
Packit 8f70b4
   Disconnect();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::Connect(int s,IOBuffer *rb)
Packit 8f70b4
{
Packit 8f70b4
   sock=s;
Packit 8f70b4
   recv_buf=rb;
Packit 8f70b4
   connected=true;
Packit 8f70b4
   passive=true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SetError(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   error=Error::Fatal(s);
Packit 8f70b4
   LogError(11,"fatal error: %s",s);
Packit 8f70b4
   Disconnect(s);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::Restart()
Packit 8f70b4
{
Packit 8f70b4
   if(!Connected())
Packit 8f70b4
      return;
Packit 8f70b4
   Disconnect();
Packit 8f70b4
   retry_timer.Stop();
Packit 8f70b4
   retry_timer.AddRandom(2);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::Disconnect(const char *dc)
Packit 8f70b4
{
Packit 8f70b4
   Enter();
Packit 8f70b4
   if(Connected() && !recv_buf->Eof())
Packit 8f70b4
      LogNote(4,"closing connection");
Packit 8f70b4
   recv_queue.empty();
Packit 8f70b4
   ClearSentQueue();
Packit 8f70b4
   if(peer_bitfield) {
Packit 8f70b4
      for(unsigned p=0; p<parent->total_pieces; p++)
Packit 8f70b4
	 SetPieceHaving(p,false);
Packit 8f70b4
      peer_bitfield=0;
Packit 8f70b4
   }
Packit 8f70b4
   peer_id.unset();
Packit 8f70b4
   fast_set.empty();
Packit 8f70b4
   suggested_set.empty();
Packit 8f70b4
   recv_buf=0;
Packit 8f70b4
   send_buf=0;
Packit 8f70b4
   if(sock!=-1) {
Packit 8f70b4
      close(sock);
Packit 8f70b4
      sock=-1;
Packit 8f70b4
      connected=false;
Packit 8f70b4
      last_dc.set(dc);
Packit 8f70b4
   }
Packit 8f70b4
   parent->am_interested_peers_count-=am_interested;
Packit 8f70b4
   am_interested=false;
Packit 8f70b4
   parent->am_not_choking_peers_count-=!am_choking;
Packit 8f70b4
   am_choking=true;
Packit 8f70b4
   peer_interested=false;
Packit 8f70b4
   peer_choking=true;
Packit 8f70b4
   peer_complete_pieces=0;
Packit 8f70b4
   retry_timer.Reset();
Packit 8f70b4
   choke_timer.Stop();
Packit 8f70b4
   interest_timer.Stop();
Packit 8f70b4
   // return to main pool
Packit 8f70b4
   parent->PeerBytesUsed(-peer_bytes_pool[RateLimit::GET],RateLimit::GET);
Packit 8f70b4
   parent->PeerBytesUsed(-peer_bytes_pool[RateLimit::PUT],RateLimit::PUT);
Packit 8f70b4
   peer_bytes_pool[0]=peer_bytes_pool[1]=0;
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SendHandshake()
Packit 8f70b4
{
Packit 8f70b4
   const char *const protocol="BitTorrent protocol";
Packit 8f70b4
   int proto_len=strlen(protocol);
Packit 8f70b4
   send_buf->PackUINT8(proto_len);
Packit 8f70b4
   send_buf->Put(protocol,proto_len);
Packit 8f70b4
   static char extensions[8] = {
Packit 8f70b4
      // extensions[7]&0x01 - DHT Protocol (http://www.bittorrent.org/beps/bep_0005.html)
Packit 8f70b4
      // extensions[7]&0x04 - Fast Extension (http://www.bittorrent.org/beps/bep_0006.html)
Packit 8f70b4
      // extensions[5]&0x10 - Extension Protocol (http://www.bittorrent.org/beps/bep_0010.html)
Packit 8f70b4
      0, 0, 0, 0, 0, 0x10, 0, 0x05,
Packit 8f70b4
   };
Packit 8f70b4
   if(ResMgr::QueryBool("torrent:use-dht",0))
Packit 8f70b4
      extensions[7]|=0x01;
Packit 8f70b4
   else
Packit 8f70b4
      extensions[7]&=~0x01;
Packit 8f70b4
   send_buf->Put(extensions,8);
Packit 8f70b4
   send_buf->Put(parent->info_hash);
Packit 8f70b4
   send_buf->Put(parent->my_peer_id);
Packit 8f70b4
   LogSend(9,"handshake");
Packit 8f70b4
}
Packit 8f70b4
TorrentPeer::unpack_status_t TorrentPeer::RecvHandshake()
Packit 8f70b4
{
Packit 8f70b4
   unsigned proto_len=0;
Packit 8f70b4
   if(recv_buf->Size()>0)
Packit 8f70b4
      proto_len=recv_buf->UnpackUINT8();
Packit 8f70b4
Packit 8f70b4
   if((unsigned)recv_buf->Size()<1+proto_len+8+SHA1_DIGEST_SIZE+Torrent::PEER_ID_LEN)
Packit 8f70b4
      return recv_buf->Eof() ? UNPACK_PREMATURE_EOF : UNPACK_NO_DATA_YET;
Packit 8f70b4
Packit 8f70b4
   int unpacked=1;
Packit 8f70b4
   const char *data=recv_buf->Get();
Packit 8f70b4
Packit 8f70b4
   xstring protocol(data+unpacked,proto_len);
Packit 8f70b4
   unpacked+=proto_len;
Packit 8f70b4
Packit 8f70b4
   memcpy(extensions,data+unpacked,8);
Packit 8f70b4
   unpacked+=8; // 8 bytes are reserved (extensions)
Packit 8f70b4
Packit 8f70b4
   xstring peer_info_hash(data+unpacked,SHA1_DIGEST_SIZE);
Packit 8f70b4
   unpacked+=SHA1_DIGEST_SIZE;
Packit 8f70b4
   if(peer_info_hash.ne(parent->info_hash)) {
Packit 8f70b4
      LogError(0,"got info_hash: %s, wanted: %s",peer_info_hash.hexdump(),parent->info_hash.hexdump());
Packit 8f70b4
      SetError("peer info_hash mismatch");
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const xstring& tmp_peer_id=xstring::get_tmp(recv_buf->Get()+unpacked,Torrent::PEER_ID_LEN);
Packit 8f70b4
   unpacked+=Torrent::PEER_ID_LEN;
Packit 8f70b4
   // if we have already such a peer, then this peer or the other one
Packit 8f70b4
   // much be marked as duplicate and then removed in ScanPeers.
Packit 8f70b4
   duplicate=parent->FindPeerById(tmp_peer_id);
Packit 8f70b4
   if(duplicate && !duplicate->Connected()) {
Packit 8f70b4
      duplicate->duplicate=this;
Packit 8f70b4
      duplicate=0;
Packit 8f70b4
   }
Packit 8f70b4
   peer_id.set(tmp_peer_id);
Packit 8f70b4
Packit 8f70b4
   recv_buf->Skip(unpacked);
Packit 8f70b4
   LogRecv(4,xstring::format("handshake, %s, peer_id: %s, reserved: %02x%02x%02x%02x%02x%02x%02x%02x",
Packit 8f70b4
      protocol.dump(),url::encode(peer_id,"").get(),
Packit 8f70b4
      extensions[0],extensions[1],extensions[2],extensions[3],
Packit 8f70b4
      extensions[4],extensions[5],extensions[6],extensions[7]));
Packit 8f70b4
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::SendExtensions()
Packit 8f70b4
{
Packit 8f70b4
  if(!LTEPExtensionEnabled())
Packit 8f70b4
      return;
Packit 8f70b4
   xmap_p<BeNode> m;
Packit 8f70b4
   m.add("ut_metadata",new BeNode(MSG_EXT_METADATA));
Packit 8f70b4
   m.add("ut_pex",new BeNode(MSG_EXT_PEX));
Packit 8f70b4
   xmap_p<BeNode> ext;
Packit 8f70b4
   ext.add("m",new BeNode(&m);;
Packit 8f70b4
   ext.add("p",new BeNode(parent->GetPort()));
Packit 8f70b4
   ext.add("v",new BeNode(PACKAGE "/" VERSION));
Packit 8f70b4
   ext.add("reqq",new BeNode(MAX_QUEUE_LEN*16));
Packit 8f70b4
   if(parent->Complete())
Packit 8f70b4
      ext.add("upload_only",new BeNode(1));
Packit 8f70b4
   if(parent->metadata)
Packit 8f70b4
      ext.add("metadata_size",new BeNode(parent->metadata.length()));
Packit 8f70b4
Packit 8f70b4
   const char *ip=ResMgr::Query("torrent:ip",0);
Packit 8f70b4
   sockaddr_u sa;
Packit 8f70b4
   socklen_t sa_len=sizeof(sa);
Packit 8f70b4
   if((ip && ip[0] && inet_aton(ip,&sa.in.sin_addr))
Packit 8f70b4
   || (getsockname(sock,&sa.sa,&sa_len)!=-1 && sa.sa.sa_family==AF_INET))
Packit 8f70b4
      ext.add("ipv4",new BeNode((const char*)&sa.in.sin_addr,4));
Packit 8f70b4
Packit 8f70b4
#if INET6
Packit 8f70b4
   const char *ipv6=ResMgr::Query("torrent:ipv6",0);
Packit 8f70b4
   sa_len=sizeof(sa);
Packit 8f70b4
   if((ipv6 && ipv6[0] && inet_pton(AF_INET6,ipv6,&sa.in6.sin6_addr)>0)
Packit 8f70b4
   || (getsockname(sock,&sa.sa,&sa_len)!=-1 && sa.sa.sa_family==AF_INET6))
Packit 8f70b4
      ext.add("ipv6",new BeNode((const char*)&sa.in6.sin6_addr,16));
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
   sa_len=sizeof(sa);
Packit 8f70b4
   if(getpeername(sock,&sa.sa,&sa_len)!=-1) {
Packit 8f70b4
      if(sa.sa.sa_family==AF_INET)
Packit 8f70b4
	 ext.add("yourip",new BeNode((const char*)&sa.in.sin_addr,4));
Packit 8f70b4
#if INET6
Packit 8f70b4
      else if(sa.sa.sa_family==AF_INET6)
Packit 8f70b4
         ext.add("yourip",new BeNode((const char*)&sa.in6.sin6_addr,16));
Packit 8f70b4
#endif
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   PacketExtended pkt(MSG_EXT_HANDSHAKE,new BeNode(&ext));
Packit 8f70b4
   pkt.Pack(send_buf);
Packit 8f70b4
   LogSend(9,xstring::format("extended(%u,%s)",pkt.code,pkt.data->Format1()));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SendDataReply()
Packit 8f70b4
{
Packit 8f70b4
   const PacketRequest *p=recv_queue.next();
Packit 8f70b4
   Enter(parent);
Packit 8f70b4
   const xstring& data=parent->RetrieveBlock(p->index,p->begin,p->req_length);
Packit 8f70b4
   Leave(parent);
Packit 8f70b4
   if(!Connected()) // we can be disconnected by parent
Packit 8f70b4
      return;
Packit 8f70b4
   if(data.length()!=p->req_length) {
Packit 8f70b4
      if(parent->my_bitfield->get_bit(p->index))
Packit 8f70b4
	 parent->SetError(xstring::format("failed to read piece %u",p->index));
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   LogSend(8,xstring::format("piece:%u begin:%u size:%u",p->index,p->begin,p->req_length));
Packit 8f70b4
   PacketPiece(p->index,p->begin,data).Pack(send_buf);
Packit 8f70b4
   peer_sent+=data.length();
Packit 8f70b4
   peer_send_rate.Add(data.length());
Packit 8f70b4
   parent->AccountSend(p->index,data.length());
Packit 8f70b4
   BytesPut(data.length());
Packit 8f70b4
   activity_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int TorrentPeer::SendDataRequests(unsigned p)
Packit 8f70b4
{
Packit 8f70b4
   if(p==NO_PIECE)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(parent->my_bitfield->get_bit(p)
Packit 8f70b4
   || !peer_bitfield || !peer_bitfield->get_bit(p))
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   int sent=0;
Packit 8f70b4
   unsigned blocks=parent->BlocksInPiece(p);
Packit 8f70b4
   unsigned bytes_allowed=BytesAllowed(RateLimit::GET);
Packit 8f70b4
   for(unsigned b=0; b
Packit 8f70b4
      if(parent->BlockPresent(p,b))
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(parent->piece_info[p].downloader_for(b)) {
Packit 8f70b4
	 if(!parent->end_game)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(parent->piece_info[p].downloader_for(b)==this)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(FindRequest(p,b*Torrent::BLOCK_SIZE)>=0)
Packit 8f70b4
	    continue;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      unsigned begin=b*Torrent::BLOCK_SIZE;
Packit 8f70b4
      unsigned len=Torrent::BLOCK_SIZE;
Packit 8f70b4
Packit 8f70b4
      if(b==blocks-1) {
Packit 8f70b4
	 assert(begin<parent->PieceLength(p));
Packit 8f70b4
	 unsigned max_len=parent->PieceLength(p)-begin;
Packit 8f70b4
	 if(len>max_len)
Packit 8f70b4
	    len=max_len;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(bytes_allowed
Packit 8f70b4
	 break;
Packit 8f70b4
Packit 8f70b4
      parent->SetDownloader(p,b,0,this);
Packit 8f70b4
      PacketRequest *req=new PacketRequest(p,b*Torrent::BLOCK_SIZE,len);
Packit 8f70b4
      LogSend(6,xstring::format("request piece:%u begin:%u size:%u",p,b*Torrent::BLOCK_SIZE,len));
Packit 8f70b4
      req->Pack(send_buf);
Packit 8f70b4
      sent_queue.push(req);
Packit 8f70b4
      SetLastPiece(p);
Packit 8f70b4
      sent++;
Packit 8f70b4
      activity_timer.Reset();
Packit 8f70b4
      bytes_allowed-=len;
Packit 8f70b4
      BytesGot(len);
Packit 8f70b4
Packit 8f70b4
      if(sent_queue.count()>=MAX_QUEUE_LEN)
Packit 8f70b4
	 break;
Packit 8f70b4
   }
Packit 8f70b4
   return sent;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool TorrentPeer::InFastSet(unsigned p) const
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      if(fast_set[i]==p)
Packit 8f70b4
	 return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SendDataRequests()
Packit 8f70b4
{
Packit 8f70b4
   assert(am_interested);
Packit 8f70b4
Packit 8f70b4
   if(peer_choking && !FastExtensionEnabled())
Packit 8f70b4
      return;
Packit 8f70b4
   if(sent_queue.count()>=MAX_QUEUE_LEN)
Packit 8f70b4
      return;
Packit 8f70b4
   if(!BytesAllowedToGet(Torrent::BLOCK_SIZE))
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   if(peer_choking) {
Packit 8f70b4
      // try to continue getting last piece if it is in the fast set
Packit 8f70b4
      unsigned last_piece=GetLastPiece();
Packit 8f70b4
      if(last_piece!=NO_PIECE && InFastSet(last_piece) && SendDataRequests(last_piece)>0)
Packit 8f70b4
	 return;
Packit 8f70b4
      // try fast set when choking
Packit 8f70b4
      while(fast_set.count()>0) {
Packit 8f70b4
	 unsigned p=fast_set[0];
Packit 8f70b4
	 if(SendDataRequests(p)>0)
Packit 8f70b4
	    return;
Packit 8f70b4
	 fast_set.next();
Packit 8f70b4
      }
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   // try to continue getting last piece
Packit 8f70b4
   if(SendDataRequests(GetLastPiece())>0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   // try suggested pieces
Packit 8f70b4
   while(suggested_set.count()>0) {
Packit 8f70b4
      unsigned p=suggested_set.next();
Packit 8f70b4
      if(SendDataRequests(p)>0)
Packit 8f70b4
	 return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   // pick a new piece
Packit 8f70b4
   unsigned p=NO_PIECE;
Packit 8f70b4
   for(int i=0; i<parent->pieces_needed.count(); i++) {
Packit 8f70b4
      if(peer_bitfield->get_bit(parent->pieces_needed[i])) {
Packit 8f70b4
	 p=parent->pieces_needed[i];
Packit 8f70b4
	 if(parent->my_bitfield->get_bit(p))
Packit 8f70b4
	    continue;
Packit 8f70b4
	 // add some randomness, so that different instances don't synchronize
Packit 8f70b4
	 if(parent->AllBlocksAbsent(p) && random()/13%16==0)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(SendDataRequests(p)>0)
Packit 8f70b4
	    return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(p==NO_PIECE && interest_timer.Stopped())
Packit 8f70b4
      SetAmInterested(false);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::Have(unsigned p)
Packit 8f70b4
{
Packit 8f70b4
   if(!send_buf)
Packit 8f70b4
      return;
Packit 8f70b4
   Enter();
Packit 8f70b4
   LogSend(9,xstring::format("have(%u)",p));
Packit 8f70b4
   PacketHave(p).Pack(send_buf);
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
int TorrentPeer::FindRequest(unsigned piece,unsigned begin) const
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      const PacketRequest *req=sent_queue[i];
Packit 8f70b4
      if(req->index==piece && req->begin==begin)
Packit 8f70b4
	 return i;
Packit 8f70b4
   }
Packit 8f70b4
   return -1;
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::CancelBlock(unsigned p,unsigned b)
Packit 8f70b4
{
Packit 8f70b4
   if(!send_buf)
Packit 8f70b4
      return;
Packit 8f70b4
   Enter();
Packit 8f70b4
   int i=FindRequest(p,b);
Packit 8f70b4
   if(i>=0) {
Packit 8f70b4
      const PacketRequest *req=sent_queue[i];
Packit 8f70b4
      LogSend(9,xstring::format("cancel(%u,%u)",p,b));
Packit 8f70b4
      PacketCancel(p,b,req->req_length).Pack(send_buf);
Packit 8f70b4
      parent->SetDownloader(p,b/Torrent::BLOCK_SIZE,this,0);
Packit 8f70b4
      sent_queue.remove(i);
Packit 8f70b4
   }
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// mark that peer as having an invalid piece
Packit 8f70b4
void TorrentPeer::MarkPieceInvalid(unsigned p)
Packit 8f70b4
{
Packit 8f70b4
   invalid_piece_count++;
Packit 8f70b4
   SetPieceHaving(p,false);
Packit 8f70b4
   SetAmInterested(am_interested);
Packit 8f70b4
   if(invalid_piece_count>5)
Packit 8f70b4
      parent->BlackListPeer(this,"1d");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::ClearSentQueue(int i)
Packit 8f70b4
{
Packit 8f70b4
   if(i<0)
Packit 8f70b4
      return;
Packit 8f70b4
   if(!FastExtensionEnabled()) {
Packit 8f70b4
      // without Fast Extension we assume sequential packet processing,
Packit 8f70b4
      // thus clear also all sent requests before this one.
Packit 8f70b4
      while(i-->=0) {
Packit 8f70b4
	 const PacketRequest *req=sent_queue.next();
Packit 8f70b4
	 parent->PeerBytesGot(-req->req_length);
Packit 8f70b4
	 parent->SetDownloader(req->index,req->begin/Torrent::BLOCK_SIZE,this,0);
Packit 8f70b4
      }
Packit 8f70b4
   } else {
Packit 8f70b4
      const PacketRequest *req=sent_queue[i];
Packit 8f70b4
      parent->PeerBytesGot(-req->req_length);
Packit 8f70b4
      parent->SetDownloader(req->index,req->begin/Torrent::BLOCK_SIZE,this,0);
Packit 8f70b4
      sent_queue.remove(i);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int TorrentPeer::BytesAllowed(RateLimit::dir_t dir)
Packit 8f70b4
{
Packit 8f70b4
   int a=parent->PeerBytesAllowed(this,dir);
Packit 8f70b4
   int pool_target=Torrent::BLOCK_SIZE*2;
Packit 8f70b4
   if(peer_bytes_pool[dir]
Packit 8f70b4
      int to_pool=pool_target-peer_bytes_pool[dir];
Packit 8f70b4
      if(to_pool>a)
Packit 8f70b4
	 to_pool=a;
Packit 8f70b4
      peer_bytes_pool[dir]+=to_pool;
Packit 8f70b4
      a-=to_pool;
Packit 8f70b4
      parent->PeerBytesUsed(to_pool,dir);
Packit 8f70b4
   }
Packit 8f70b4
   return peer_bytes_pool[dir]+a;
Packit 8f70b4
}
Packit 8f70b4
bool TorrentPeer::BytesAllowed(RateLimit::dir_t dir,unsigned bytes)
Packit 8f70b4
{
Packit 8f70b4
   int a=BytesAllowed(dir);
Packit 8f70b4
   if(bytes<=(unsigned)a)
Packit 8f70b4
      return true;
Packit 8f70b4
   TimeoutS(1);
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::BytesUsed(int b,RateLimit::dir_t dir)
Packit 8f70b4
{
Packit 8f70b4
   if(peer_bytes_pool[dir]>=b)
Packit 8f70b4
      peer_bytes_pool[dir]-=b;
Packit 8f70b4
   else {
Packit 8f70b4
      b-=peer_bytes_pool[dir];
Packit 8f70b4
      peer_bytes_pool[dir]=0;
Packit 8f70b4
      parent->PeerBytesUsed(b,dir);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
unsigned TorrentPeer::GetLastPiece() const
Packit 8f70b4
{
Packit 8f70b4
   if(!peer_bitfield)
Packit 8f70b4
      return NO_PIECE;
Packit 8f70b4
   unsigned p=last_piece;
Packit 8f70b4
   // continue if have any blocks already
Packit 8f70b4
   if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
Packit 8f70b4
   && parent->AnyBlocksPresent(p)
Packit 8f70b4
   && peer_bitfield->get_bit(p))
Packit 8f70b4
      return p;
Packit 8f70b4
   p=parent->last_piece;
Packit 8f70b4
   if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
Packit 8f70b4
   && peer_bitfield->get_bit(p))
Packit 8f70b4
      return p;
Packit 8f70b4
   p=last_piece;
Packit 8f70b4
   if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
Packit 8f70b4
   && peer_bitfield->get_bit(p))
Packit 8f70b4
      return p;
Packit 8f70b4
   return NO_PIECE;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SetLastPiece(unsigned p)
Packit 8f70b4
{
Packit 8f70b4
   if(last_piece==NO_PIECE || parent->my_bitfield->get_bit(last_piece))
Packit 8f70b4
      last_piece=p;
Packit 8f70b4
   if(parent->last_piece==NO_PIECE || parent->my_bitfield->get_bit(parent->last_piece))
Packit 8f70b4
      parent->last_piece=p;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SetAmInterested(bool interest)
Packit 8f70b4
{
Packit 8f70b4
   if(invalid_piece_count>5)
Packit 8f70b4
      interest=false;
Packit 8f70b4
   if(am_interested==interest)
Packit 8f70b4
      return;
Packit 8f70b4
   Enter();
Packit 8f70b4
   LogSend(6,interest?"interested":"uninterested");
Packit 8f70b4
   Packet(interest?MSG_INTERESTED:MSG_UNINTERESTED).Pack(send_buf);
Packit 8f70b4
   parent->am_interested_peers_count+=(interest-am_interested);
Packit 8f70b4
   am_interested=interest;
Packit 8f70b4
   interest_timer.Reset();
Packit 8f70b4
   if(am_interested)
Packit 8f70b4
      parent->am_interested_timer.Reset();
Packit 8f70b4
   (void)BytesAllowed(RateLimit::GET); // draw some bytes from the common pool
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::SetAmChoking(bool c)
Packit 8f70b4
{
Packit 8f70b4
   if(am_choking==c)
Packit 8f70b4
      return;
Packit 8f70b4
   Enter();
Packit 8f70b4
   LogSend(6,c?"choke":"unchoke");
Packit 8f70b4
   Packet(c?MSG_CHOKE:MSG_UNCHOKE).Pack(send_buf);
Packit 8f70b4
   parent->am_not_choking_peers_count-=(c-am_choking);
Packit 8f70b4
   am_choking=c;
Packit 8f70b4
   choke_timer.Reset();
Packit 8f70b4
   if(am_choking) {
Packit 8f70b4
      if(!FastExtensionEnabled()) {
Packit 8f70b4
	 recv_queue.empty();
Packit 8f70b4
      } else {
Packit 8f70b4
	 // send rejects
Packit 8f70b4
	 while(recv_queue.count()>0) {
Packit 8f70b4
	    const PacketRequest *p=recv_queue.next();
Packit 8f70b4
	    LogSend(6,xstring::format("reject-request piece:%u begin:%u size:%u",p->index,p->begin,p->req_length));
Packit 8f70b4
	    PacketRejectRequest(p->index,p->begin,p->req_length).Pack(send_buf);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SetPieceHaving(unsigned p,bool have)
Packit 8f70b4
{
Packit 8f70b4
   int diff = (have - peer_bitfield->get_bit(p));
Packit 8f70b4
   if(!diff)
Packit 8f70b4
      return;
Packit 8f70b4
   parent->piece_info[p].add_sources_count(diff);
Packit 8f70b4
   peer_complete_pieces+=diff;
Packit 8f70b4
   peer_bitfield->set_bit(p,have);
Packit 8f70b4
Packit 8f70b4
   if(parent->piece_info[p].get_sources_count()==0)
Packit 8f70b4
      parent->SetPieceNotWanted(p);
Packit 8f70b4
   if(have && send_buf && !am_interested && !parent->my_bitfield->get_bit(p)
Packit 8f70b4
   && parent->NeedMoreUploaders()) {
Packit 8f70b4
      SetAmInterested(true);
Packit 8f70b4
      SetLastPiece(p);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::HandlePacket(Packet *p)
Packit 8f70b4
{
Packit 8f70b4
   switch(p->GetPacketType())
Packit 8f70b4
   {
Packit 8f70b4
   case MSG_KEEPALIVE: {
Packit 8f70b4
	 LogRecv(5,"keep-alive");
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_CHOKE: {
Packit 8f70b4
	 LogRecv(5,"choke");
Packit 8f70b4
	 peer_choking=true;
Packit 8f70b4
	 ClearSentQueue(); // discard pending requests
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_UNCHOKE: {
Packit 8f70b4
	 LogRecv(5,"unchoke");
Packit 8f70b4
	 peer_choking=false;
Packit 8f70b4
	 if(am_interested)
Packit 8f70b4
	    SendDataRequests();
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_INTERESTED: {
Packit 8f70b4
	 LogRecv(5,"interested");
Packit 8f70b4
	 peer_interested=true;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_UNINTERESTED: {
Packit 8f70b4
	 LogRecv(5,"uninterested");
Packit 8f70b4
	 recv_queue.empty();
Packit 8f70b4
	 peer_interested=false;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_HAVE: {
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 PacketHave *pp=static_cast<PacketHave*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("have(%u)",pp->piece));
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(pp->piece>=parent->total_pieces) {
Packit 8f70b4
	    SetError("invalid piece index");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 SetPieceHaving(pp->piece,true);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_BITFIELD: {
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 PacketBitField *pp=static_cast<PacketBitField*>(p);
Packit 8f70b4
	 if(pp->bitfield->count()<(int)parent->total_pieces/8) {
Packit 8f70b4
	    LogError(9,"bitfield length %d, expected %u",pp->bitfield->count(),parent->total_pieces/8);
Packit 8f70b4
	    SetError("invalid bitfield length");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(pp->bitfield->has_any_set(parent->total_pieces,pp->bitfield->get_bit_length())) {
Packit 8f70b4
	    SetError("bitfield has spare bits set");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 for(unsigned p=0; p<parent->total_pieces; p++)
Packit 8f70b4
	    SetPieceHaving(p,pp->bitfield->get_bit(p));
Packit 8f70b4
	 LogRecv(5,xstring::format("bitfield(%u/%u)",peer_complete_pieces,parent->total_pieces));
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_PORT: {
Packit 8f70b4
	 PacketPort *pp=static_cast<PacketPort*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("port(%u)",pp->port));
Packit 8f70b4
	 udp_port=pp->port;
Packit 8f70b4
	 if(DHT_Enabled() && Torrent::dht) {
Packit 8f70b4
	    sockaddr_u a(addr);
Packit 8f70b4
	    a.set_port(udp_port);
Packit 8f70b4
	    Torrent::GetDHT(a)->SendPing(a);
Packit 8f70b4
	 }
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_HAVE_ALL: {
Packit 8f70b4
	 LogRecv(5,"have-all");
Packit 8f70b4
	 if(!FastExtensionEnabled()) {
Packit 8f70b4
	    SetError("fast extension is disabled");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 for(unsigned p=0; p<parent->total_pieces; p++)
Packit 8f70b4
	    SetPieceHaving(p,1);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_HAVE_NONE: {
Packit 8f70b4
	 LogRecv(5,"have-none");
Packit 8f70b4
	 if(!FastExtensionEnabled()) {
Packit 8f70b4
	    SetError("fast extension is disabled");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 for(unsigned p=0; p<parent->total_pieces; p++)
Packit 8f70b4
	    SetPieceHaving(p,0);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_SUGGEST_PIECE: {
Packit 8f70b4
	 PacketSuggestPiece *pp=static_cast<PacketSuggestPiece*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("suggest-piece:%u",pp->piece));
Packit 8f70b4
	 if(!FastExtensionEnabled()) {
Packit 8f70b4
	    SetError("fast extension is disabled");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(pp->piece>=parent->total_pieces) {
Packit 8f70b4
	    SetError("invalid piece index");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 suggested_set.push(pp->piece);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_ALLOWED_FAST: {
Packit 8f70b4
	 PacketAllowedFast *pp=static_cast<PacketAllowedFast*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("allowed-fast:%u",pp->piece));
Packit 8f70b4
	 if(!FastExtensionEnabled()) {
Packit 8f70b4
	    SetError("fast extension is disabled");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(pp->piece>=parent->total_pieces) {
Packit 8f70b4
	    SetError("invalid piece index");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 fast_set.push(pp->piece);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_REJECT_REQUEST: {
Packit 8f70b4
	 PacketRejectRequest *pp=static_cast<PacketRejectRequest*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("reject-request(%u,%u)",pp->index,pp->begin));
Packit 8f70b4
	 if(!FastExtensionEnabled()) {
Packit 8f70b4
	    SetError("fast extension is disabled");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 int i=FindRequest(pp->index,pp->begin);
Packit 8f70b4
	 if(i>=0)
Packit 8f70b4
	    ClearSentQueue(i);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_EXTENDED: {
Packit 8f70b4
	 PacketExtended *pp=static_cast<PacketExtended*>(p);
Packit 8f70b4
	 LogRecv(9,xstring::format("extended(%u,%s)",pp->code,pp->data->Format1()));
Packit 8f70b4
	 HandleExtendedMessage(pp);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_PIECE: {
Packit 8f70b4
	 PacketPiece *pp=static_cast<PacketPiece*>(p);
Packit 8f70b4
	 LogRecv(7,xstring::format("piece:%u begin:%u size:%u",pp->index,pp->begin,(unsigned)pp->data.length()));
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(pp->index>=parent->total_pieces) {
Packit 8f70b4
	    SetError("invalid piece index");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(pp->begin>=parent->PieceLength(pp->index)) {
Packit 8f70b4
	    SetError("invalid data offset");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(pp->begin+pp->data.length() > parent->PieceLength(pp->index)) {
Packit 8f70b4
	    SetError("invalid data length");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 int i=FindRequest(pp->index,pp->begin);
Packit 8f70b4
	 if(i<0) {
Packit 8f70b4
// 	    SetError("got a piece that was not requested");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 ClearSentQueue(i);
Packit 8f70b4
	 parent->PeerBytesGot(pp->data.length()); // re-take the bytes returned by ClearSentQueue
Packit 8f70b4
	 Enter(parent);
Packit 8f70b4
	 parent->StoreBlock(pp->index,pp->begin,pp->data.length(),pp->data.get(),this);
Packit 8f70b4
	 Leave(parent);
Packit 8f70b4
Packit 8f70b4
	 int len=pp->data.length();
Packit 8f70b4
	 peer_recv+=len;
Packit 8f70b4
	 peer_recv_rate.Add(len);
Packit 8f70b4
	 parent->AccountRecv(pp->index,len);
Packit 8f70b4
Packit 8f70b4
	 // request another block from the same piece
Packit 8f70b4
	 if(am_interested && (!peer_choking || InFastSet(pp->index)))
Packit 8f70b4
	    SendDataRequests(pp->index);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_REQUEST: {
Packit 8f70b4
	 PacketRequest *pp=static_cast<PacketRequest*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("request for piece:%u begin:%u size:%u",pp->index,pp->begin,pp->req_length));
Packit 8f70b4
	 if(pp->req_length>Torrent::BLOCK_SIZE*2) {
Packit 8f70b4
	    SetError("too large request");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!parent->HasMetadata())
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(am_choking)
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(pp->index>=parent->total_pieces) {
Packit 8f70b4
	    SetError("invalid piece index");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(pp->begin>=parent->PieceLength(pp->index)) {
Packit 8f70b4
	    SetError("invalid data offset");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(pp->begin+pp->req_length > parent->PieceLength(pp->index)) {
Packit 8f70b4
	    SetError("invalid data length");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(recv_queue.count()>=MAX_QUEUE_LEN*16) {
Packit 8f70b4
	    SetError("too many requests");
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 recv_queue.push(pp);
Packit 8f70b4
	 activity_timer.Reset();
Packit 8f70b4
	 p=0;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case MSG_CANCEL: {
Packit 8f70b4
	 PacketCancel *pp=static_cast<PacketCancel*>(p);
Packit 8f70b4
	 LogRecv(5,xstring::format("cancel(%u,%u)",pp->index,pp->begin));
Packit 8f70b4
	 for(int i=0; i
Packit 8f70b4
	    const PacketRequest *req=recv_queue[i];
Packit 8f70b4
	    if(req->index==pp->index && req->begin==pp->begin) {
Packit 8f70b4
	       recv_queue.remove(i);
Packit 8f70b4
	       break;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   delete p;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Torrent::MetadataDownloaded()
Packit 8f70b4
{
Packit 8f70b4
   xstring new_info_hash;
Packit 8f70b4
   SHA1(md_download,new_info_hash);
Packit 8f70b4
   if(info_hash && info_hash.ne(new_info_hash)) {
Packit 8f70b4
      LogError(1,"downloaded metadata does not match info_hash, retrying");
Packit 8f70b4
      StartMetadataDownload();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   if(SetMetadata(md_download))
Packit 8f70b4
      Startup();
Packit 8f70b4
   md_download.unset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::SendMetadataRequest()
Packit 8f70b4
{
Packit 8f70b4
   if(!msg_ext_metadata || !parent->md_download || parent->md_download.length()>=metadata_size
Packit 8f70b4
   || parent->md_download.length()%Torrent::BLOCK_SIZE)
Packit 8f70b4
      return;
Packit 8f70b4
   xmap_p<BeNode> req;
Packit 8f70b4
   req.add("msg_type",new BeNode(UT_METADATA_REQUEST));
Packit 8f70b4
   req.add("piece",new BeNode(parent->md_download.length()/Torrent::BLOCK_SIZE));
Packit 8f70b4
   PacketExtended pkt(msg_ext_metadata,new BeNode(&req));
Packit 8f70b4
   LogSend(4,xstring::format("ut_metadata request %s",pkt.data->Format1()));
Packit 8f70b4
   pkt.Pack(send_buf);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TorrentPeer::HandleExtendedMessage(PacketExtended *pp)
Packit 8f70b4
{
Packit 8f70b4
   if(pp->data->type!=BeNode::BE_DICT) {
Packit 8f70b4
      SetError("extended type must be DICT");
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   if(pp->code==MSG_EXT_HANDSHAKE) {
Packit 8f70b4
      BeNode *m=pp->data->lookup("m",BeNode::BE_DICT);
Packit 8f70b4
      if(m) {
Packit 8f70b4
	 msg_ext_metadata=m->lookup_int("ut_metadata");
Packit 8f70b4
	 msg_ext_pex=m->lookup_int("ut_pex");
Packit 8f70b4
      }
Packit 8f70b4
      metadata_size=parent->metadata_size=pp->data->lookup_int("metadata_size");
Packit 8f70b4
      upload_only=pp->data->lookup_int("upload_only");
Packit 8f70b4
Packit 8f70b4
      if(!parent->HasMetadata() && !msg_ext_metadata) {
Packit 8f70b4
	 Disconnect("peer cannot provide metadata");
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      const xstring& v=pp->data->lookup_str("v");
Packit 8f70b4
      if(v)
Packit 8f70b4
	 LogNote(3,"peer version is %s",v.get());
Packit 8f70b4
Packit 8f70b4
      const xstring& myip=pp->data->lookup_str("yourip");
Packit 8f70b4
      if(myip && myip.length()==4) {
Packit 8f70b4
	 char ip[16];
Packit 8f70b4
	 inet_ntop(AF_INET,myip.get(),ip,sizeof(ip));
Packit 8f70b4
	 LogNote(5,"my external IPv4 is %s",ip);
Packit 8f70b4
      }
Packit 8f70b4
      if(passive) {
Packit 8f70b4
	 // use specified port number to connect back
Packit 8f70b4
	 int p=pp->data->lookup_int("p");
Packit 8f70b4
	 if(p && p>=1024 && p<=65535) {
Packit 8f70b4
	    LogNote(9,"using port %d to connect back",p);
Packit 8f70b4
	    addr.set_port(p);
Packit 8f70b4
	    passive=false;
Packit 8f70b4
	    // check the black list
Packit 8f70b4
	    if(Torrent::BlackListed(this)) {
Packit 8f70b4
	       SetError("blacklisted");
Packit 8f70b4
	       return;
Packit 8f70b4
	    }
Packit 8f70b4
	    // check for duplicates
Packit 8f70b4
	    TaskRefArray<TorrentPeer>& peers=parent->peers;
Packit 8f70b4
	    for(int i=0; i
Packit 8f70b4
	       if(peers[i]!=this && peers[i]->AddressEq(this)) {
Packit 8f70b4
		  if(!peers[i]->Connected())
Packit 8f70b4
		     peers[i]->duplicate=this;
Packit 8f70b4
		  else
Packit 8f70b4
		     duplicate=peers[i].get_non_const();
Packit 8f70b4
		  return;
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(msg_ext_metadata && parent->md_download)
Packit 8f70b4
	 SendMetadataRequest();
Packit 8f70b4
   } else if(pp->code==MSG_EXT_METADATA) {
Packit 8f70b4
      BeNode *msg_type=pp->data->lookup("msg_type",BeNode::BE_INT);
Packit 8f70b4
      if(!msg_type) {
Packit 8f70b4
	 SetError("ut_metadata msg_type bad or missing");
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      BeNode *piece=pp->data->lookup("piece",BeNode::BE_INT);
Packit 8f70b4
      if(!piece || piece->num<0 || piece->num>=INT_MAX/Torrent::BLOCK_SIZE) {
Packit 8f70b4
	 SetError("ut_metadata piece bad or missing");
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      size_t offset=piece->num*Torrent::BLOCK_SIZE;
Packit 8f70b4
      xmap_p<BeNode> reply;
Packit 8f70b4
      switch(msg_type->num) {
Packit 8f70b4
	 case UT_METADATA_REQUEST: {
Packit 8f70b4
	    if(offset>parent->metadata.length()) {
Packit 8f70b4
	       reply.add("msg_type",new BeNode(UT_METADATA_REJECT));
Packit 8f70b4
	       reply.add("piece",new BeNode(piece->num));
Packit 8f70b4
	       PacketExtended pkt(msg_ext_metadata,new BeNode(&reply));
Packit 8f70b4
	       LogSend(4,xstring::format("ut_metadata reject %s",pkt.data->Format1()));
Packit 8f70b4
	       pkt.Pack(send_buf);
Packit 8f70b4
	       break;
Packit 8f70b4
	    }
Packit 8f70b4
	    const char *d=parent->metadata+offset;
Packit 8f70b4
	    unsigned len=parent->metadata.length()-offset;
Packit 8f70b4
	    if(len>Torrent::BLOCK_SIZE)
Packit 8f70b4
	       len=Torrent::BLOCK_SIZE;
Packit 8f70b4
	    reply.add("msg_type",new BeNode(UT_METADATA_DATA));
Packit 8f70b4
	    reply.add("piece",new BeNode(piece->num));
Packit 8f70b4
	    reply.add("total_size",new BeNode(parent->metadata.length()));
Packit 8f70b4
	    PacketExtended pkt(msg_ext_metadata,new BeNode(&reply));
Packit 8f70b4
	    LogSend(4,xstring::format("ut_metadata data %s",pkt.data->Format1()));
Packit 8f70b4
	    pkt.SetAppendix(d,len);
Packit 8f70b4
	    pkt.Pack(send_buf);
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 case UT_METADATA_DATA: {
Packit 8f70b4
	    if(parent->md_download) {
Packit 8f70b4
	       if(offset==parent->md_download.length()) {
Packit 8f70b4
		  BeNode *b_size=pp->data->lookup("total_size",BeNode::BE_INT);
Packit 8f70b4
		  if(b_size) {
Packit 8f70b4
		     if(metadata_size && metadata_size!=(size_t)b_size->num) {
Packit 8f70b4
			SetError("metadata_size mismatch with total_size");
Packit 8f70b4
			return;
Packit 8f70b4
		     }
Packit 8f70b4
		     metadata_size=b_size->num;
Packit 8f70b4
		     parent->metadata_size=metadata_size;
Packit 8f70b4
		  }
Packit 8f70b4
		  parent->md_download.append(pp->appendix);
Packit 8f70b4
		  if(pp->appendix.length()
Packit 8f70b4
		     parent->MetadataDownloaded();
Packit 8f70b4
	       }
Packit 8f70b4
	       SendMetadataRequest();
Packit 8f70b4
	    }
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 case UT_METADATA_REJECT:
Packit 8f70b4
	    break;
Packit 8f70b4
	 default:
Packit 8f70b4
	    SetError("ut_metadata msg_type invalid value");
Packit 8f70b4
	    return;
Packit 8f70b4
      }
Packit 8f70b4
   } else if(pp->code==MSG_EXT_PEX) {
Packit 8f70b4
      if(!pex.recv_timer.Stopped())
Packit 8f70b4
	 return;
Packit 8f70b4
      pex.recv_timer.Reset();
Packit 8f70b4
      BeNode *added=pp->data->lookup("added",BeNode::BE_STR);
Packit 8f70b4
      BeNode *added6=pp->data->lookup("added6",BeNode::BE_STR);
Packit 8f70b4
      BeNode *added_f=pp->data->lookup("added.f",BeNode::BE_STR);
Packit 8f70b4
      BeNode *added6_f=pp->data->lookup("added6.f",BeNode::BE_STR);
Packit 8f70b4
      AddPEXPeers(added,added_f,6);
Packit 8f70b4
      AddPEXPeers(added6,added6_f,18);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::AddPEXPeers(BeNode *added,BeNode *added_f,int addr_size)
Packit 8f70b4
{
Packit 8f70b4
   if(!added)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   const char *data=added->str;
Packit 8f70b4
   unsigned n=added->str.length()/addr_size;
Packit 8f70b4
   if(n>50)
Packit 8f70b4
      n=50;
Packit 8f70b4
Packit 8f70b4
   const char *flags=0;
Packit 8f70b4
   if(added_f && added_f->str.length()==n)
Packit 8f70b4
      flags=added_f->str;
Packit 8f70b4
Packit 8f70b4
   int peers_count=0;
Packit 8f70b4
   for(unsigned i=0; i
Packit 8f70b4
      unsigned char f=(flags?flags[i]:pex.CONNECTABLE);
Packit 8f70b4
      if(!(f&pex.CONNECTABLE))
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(parent->Complete() && (f&pex.SEED))
Packit 8f70b4
	 continue;
Packit 8f70b4
      sockaddr_u a;
Packit 8f70b4
      a.set_compact(data,addr_size);
Packit 8f70b4
      if(!a.is_compatible(this->addr))
Packit 8f70b4
	 continue;
Packit 8f70b4
      parent->AddPeer(new TorrentPeer(parent,&a,TR_PEX));
Packit 8f70b4
      peers_count++;
Packit 8f70b4
   }
Packit 8f70b4
   if(peers_count>0)
Packit 8f70b4
      LogNote(4,"%d %s peers added from PEX message",peers_count,addr_size==6?"ipv4":"ipv6");
Packit 8f70b4
}
Packit 8f70b4
void TorrentPeer::SendPEXPeers()
Packit 8f70b4
{
Packit 8f70b4
   pex.send_timer.Reset();
Packit 8f70b4
   if(!msg_ext_pex || parent->Private())
Packit 8f70b4
      return;
Packit 8f70b4
   xmap<char> old_sent;
Packit 8f70b4
   old_sent.move_here(pex.sent);
Packit 8f70b4
   int peer_count=0;
Packit 8f70b4
   xstring added;
Packit 8f70b4
   xstring added6;
Packit 8f70b4
   xstring added_f;
Packit 8f70b4
   xstring added6_f;
Packit 8f70b4
   xstring dropped;
Packit 8f70b4
   xstring dropped6;
Packit 8f70b4
   int a=0,a6=0,d=0,d6=0;
Packit 8f70b4
   for(int i=parent->peers.count(); i>0; i--) {
Packit 8f70b4
      const TorrentPeer *peer=parent->peers[i-1];
Packit 8f70b4
      if(!peer->Connected() || peer->IsPassive() || peer->Failed()
Packit 8f70b4
      || !peer->addr.is_compatible(this->addr) || peer==this || peer->myself)
Packit 8f70b4
	 continue;
Packit 8f70b4
      const xstring& ca=peer->addr.compact();
Packit 8f70b4
      if(old_sent.exists(ca)) {
Packit 8f70b4
	 old_sent.remove(ca);
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      unsigned char f=pex.CONNECTABLE;
Packit 8f70b4
      if(peer->Seed())
Packit 8f70b4
	 f|=pex.SEED;
Packit 8f70b4
      peer_count++;
Packit 8f70b4
      if(peer_count>50)
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(ca.length()==6) {
Packit 8f70b4
	 added.append(ca);
Packit 8f70b4
	 added_f.append(f);
Packit 8f70b4
	 a++;
Packit 8f70b4
      } else {
Packit 8f70b4
	 added6.append(ca);
Packit 8f70b4
	 added6_f.append(f);
Packit 8f70b4
	 a6++;
Packit 8f70b4
      }
Packit 8f70b4
      pex.sent.add(ca,f);
Packit 8f70b4
   }
Packit 8f70b4
   peer_count=0;
Packit 8f70b4
   for(old_sent.each_begin(); !old_sent.each_finished(); old_sent.each_next())
Packit 8f70b4
   {
Packit 8f70b4
      const xstring& ca=old_sent.each_key();
Packit 8f70b4
      peer_count++;
Packit 8f70b4
      if(peer_count>50) {
Packit 8f70b4
	 // drop it later
Packit 8f70b4
	 pex.sent.add(ca,0);
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(ca.length()==6) {
Packit 8f70b4
	 dropped.append(ca);
Packit 8f70b4
	 d++;
Packit 8f70b4
      } else {
Packit 8f70b4
	 dropped6.append(ca);
Packit 8f70b4
	 d6++;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(a+a6+d+d6==0)
Packit 8f70b4
      return;
Packit 8f70b4
   xmap_p<BeNode> req;
Packit 8f70b4
   if(a) {
Packit 8f70b4
      req.add("added",new BeNode(added));
Packit 8f70b4
      req.add("added.f",new BeNode(added_f));
Packit 8f70b4
   }
Packit 8f70b4
   if(a6) {
Packit 8f70b4
      req.add("added6",new BeNode(added6));
Packit 8f70b4
      req.add("added6.f",new BeNode(added6_f));
Packit 8f70b4
   }
Packit 8f70b4
   if(d)
Packit 8f70b4
      req.add("dropped",new BeNode(dropped));
Packit 8f70b4
   if(d6)
Packit 8f70b4
      req.add("dropped6",new BeNode(dropped6));
Packit 8f70b4
   PacketExtended pkt(msg_ext_pex,new BeNode(&req));
Packit 8f70b4
   LogSend(4,xstring::format("ut_pex message: added=[%d,%d], dropped=[%d,%d]",a,a6,d,d6));
Packit 8f70b4
   pkt.Pack(send_buf);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool TorrentPeer::HasNeededPieces()
Packit 8f70b4
{
Packit 8f70b4
   if(!peer_bitfield)
Packit 8f70b4
      return false;
Packit 8f70b4
   if(GetLastPiece()!=NO_PIECE)
Packit 8f70b4
      return true;
Packit 8f70b4
   for(int i=0; i<parent->pieces_needed.count(); i++)
Packit 8f70b4
      if(peer_bitfield->get_bit(parent->pieces_needed[i]))
Packit 8f70b4
	 return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int TorrentPeer::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(error || myself)
Packit 8f70b4
      return m;
Packit 8f70b4
   if(sock==-1) {
Packit 8f70b4
      if(passive)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(!retry_timer.Stopped())
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(parent->IsValidating())
Packit 8f70b4
	 return m;
Packit 8f70b4
      sock=SocketCreateTCP(addr.sa.sa_family,0);
Packit 8f70b4
      if(sock==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 if(NonFatalError(errno))
Packit 8f70b4
	    return m;
Packit 8f70b4
	 SetError(xstring::format(_("cannot create socket of address family %d"),addr.sa.sa_family));
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      LogNote(4,_("Connecting to peer %s port %u"),SocketNumericAddress(&addr),SocketPort(&addr));
Packit 8f70b4
      connected=false;
Packit 8f70b4
   }
Packit 8f70b4
   if(!connected) {
Packit 8f70b4
      int res=SocketConnect(sock,&addr);
Packit 8f70b4
      if(res==-1 && errno!=EINPROGRESS && errno!=EALREADY && errno!=EISCONN)
Packit 8f70b4
      {
Packit 8f70b4
	 int e=errno;
Packit 8f70b4
	 const char *error=strerror(e);
Packit 8f70b4
	 LogError(4,"connect(%s): %s\n",GetName(),error);
Packit 8f70b4
	 Disconnect(error);
Packit 8f70b4
	 if(FA::NotSerious(e) && !ActivityTimedOut())
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 SetError(error);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(res==-1 && errno!=EISCONN) {
Packit 8f70b4
	 Block(sock,POLLOUT);
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      connected=true;
Packit 8f70b4
      timeout_timer.Reset();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(!recv_buf) {
Packit 8f70b4
      recv_buf=new IOBufferFDStream(new FDStream(sock,"<input-socket>"),IOBuffer::GET);
Packit 8f70b4
   }
Packit 8f70b4
   if(!send_buf) {
Packit 8f70b4
      send_buf=new IOBufferFDStream(new FDStream(sock,"<output-socket>"),IOBuffer::PUT);
Packit 8f70b4
      SendHandshake();
Packit 8f70b4
   }
Packit 8f70b4
   if(send_buf->Error())
Packit 8f70b4
   {
Packit 8f70b4
      LogError(2,"send: %s",send_buf->ErrorText());
Packit 8f70b4
      Disconnect(send_buf->ErrorText());
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(recv_buf->Error())
Packit 8f70b4
   {
Packit 8f70b4
      LogError(2,"receive: %s",recv_buf->ErrorText());
Packit 8f70b4
      Disconnect(recv_buf->ErrorText());
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(!peer_id) {
Packit 8f70b4
      // expect handshake
Packit 8f70b4
      unpack_status_t s=RecvHandshake();
Packit 8f70b4
      if(s==UNPACK_NO_DATA_YET)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(s!=UNPACK_SUCCESS) {
Packit 8f70b4
	 if(s==UNPACK_PREMATURE_EOF) {
Packit 8f70b4
	    if(recv_buf->Size()>0) {
Packit 8f70b4
	       LogError(2,_("peer unexpectedly closed connection after %s"),recv_buf->Dump());
Packit 8f70b4
	       Disconnect(_("peer unexpectedly closed connection"));
Packit 8f70b4
	    } else {
Packit 8f70b4
	       LogError(4,_("peer closed connection (before handshake)"));
Packit 8f70b4
	       Disconnect(_("peer closed connection (before handshake)"));
Packit 8f70b4
	    }
Packit 8f70b4
	 } else {
Packit 8f70b4
	    Disconnect(_("invalid peer response format"));
Packit 8f70b4
	 }
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(!parent->HasMetadata() && !LTEPExtensionEnabled()) {
Packit 8f70b4
	 Disconnect("peer cannot provide metadata");
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      timeout_timer.Reset();
Packit 8f70b4
      myself=peer_id.eq(Torrent::my_peer_id);
Packit 8f70b4
      if(myself)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      SendExtensions();
Packit 8f70b4
      if(parent->HasMetadata())
Packit 8f70b4
	 peer_bitfield=new BitField(parent->total_pieces);
Packit 8f70b4
      if(FastExtensionEnabled()) {
Packit 8f70b4
	 if(parent->complete_pieces==0) {
Packit 8f70b4
	    LogSend(5,"have-none");
Packit 8f70b4
	    Packet(MSG_HAVE_NONE).Pack(send_buf);
Packit 8f70b4
	 } else if(parent->complete_pieces==parent->total_pieces) {
Packit 8f70b4
	    LogSend(5,"have-all");
Packit 8f70b4
	    Packet(MSG_HAVE_ALL).Pack(send_buf);
Packit 8f70b4
	 } else {
Packit 8f70b4
	    LogSend(5,"bitfield");
Packit 8f70b4
	    PacketBitField(parent->my_bitfield).Pack(send_buf);
Packit 8f70b4
	 }
Packit 8f70b4
      } else if(parent->my_bitfield && parent->my_bitfield->has_any_set()) {
Packit 8f70b4
	 LogSend(5,"bitfield");
Packit 8f70b4
	 PacketBitField(parent->my_bitfield).Pack(send_buf);
Packit 8f70b4
      }
Packit 8f70b4
      if(Torrent::listener_udp && DHT_Enabled()) {
Packit 8f70b4
	 int udp_port=Torrent::listener_udp->GetPort();
Packit 8f70b4
#if INET6
Packit 8f70b4
	 if(Torrent::listener_ipv6_udp && addr.sa.sa_family==AF_INET6)
Packit 8f70b4
	    udp_port=Torrent::listener_ipv6_udp->GetPort();
Packit 8f70b4
#endif
Packit 8f70b4
	 if(udp_port) {
Packit 8f70b4
	    LogSend(5,xstring::format("port(%d)",udp_port));
Packit 8f70b4
	    PacketPort(udp_port).Pack(send_buf);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      keepalive_timer.Reset();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(keepalive_timer.Stopped()) {
Packit 8f70b4
      LogSend(5,"keep-alive");
Packit 8f70b4
      Packet(MSG_KEEPALIVE).Pack(send_buf);
Packit 8f70b4
      keepalive_timer.Reset();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(send_buf->Size()>(int)Torrent::BLOCK_SIZE*4)
Packit 8f70b4
      recv_buf->Suspend();
Packit 8f70b4
   else
Packit 8f70b4
      recv_buf->Resume();
Packit 8f70b4
Packit 8f70b4
   if(recv_buf->IsSuspended())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   timeout_timer.Reset(send_buf->EventTime());
Packit 8f70b4
   timeout_timer.Reset(recv_buf->EventTime());
Packit 8f70b4
   if(timeout_timer.Stopped()) {
Packit 8f70b4
      LogError(0,_("Timeout - reconnecting"));
Packit 8f70b4
      Disconnect("timed out");
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!am_interested && interest_timer.Stopped()
Packit 8f70b4
   && HasNeededPieces() && parent->NeedMoreUploaders())
Packit 8f70b4
      SetAmInterested(true);
Packit 8f70b4
Packit 8f70b4
   if(am_interested && sent_queue.count()
Packit 8f70b4
      SendDataRequests();
Packit 8f70b4
Packit 8f70b4
   if(peer_interested && am_choking && choke_timer.Stopped()
Packit 8f70b4
   && parent->AllowMoreDownloaders())
Packit 8f70b4
      SetAmChoking(false);
Packit 8f70b4
Packit 8f70b4
   if(recv_queue.count()>0 && send_buf->Size()<(int)Torrent::BLOCK_SIZE*2) {
Packit 8f70b4
      unsigned bytes_allowed=BytesAllowed(RateLimit::PUT);
Packit 8f70b4
      while(bytes_allowed>=recv_queue[0]->req_length) {
Packit 8f70b4
	 bytes_allowed-=recv_queue[0]->req_length;
Packit 8f70b4
	 SendDataReply();
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
	 if(!Connected())
Packit 8f70b4
	    return m;
Packit 8f70b4
	 if(recv_queue.count()==0)
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(send_buf->Size()>=(int)Torrent::BLOCK_SIZE)
Packit 8f70b4
	    m|=send_buf->Do();
Packit 8f70b4
	 if(send_buf->Size()>=(int)Torrent::BLOCK_SIZE*2)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(recv_buf->Eof() && recv_buf->Size()==0) {
Packit 8f70b4
      LogError(4,_("peer closed connection"));
Packit 8f70b4
      Disconnect(_("peer closed connection"));
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(pex.send_timer.Stopped())
Packit 8f70b4
      SendPEXPeers();
Packit 8f70b4
Packit 8f70b4
   Packet *reply=0;
Packit 8f70b4
   unpack_status_t st=UnpackPacket(recv_buf,&reply);
Packit 8f70b4
   if(st==UNPACK_NO_DATA_YET)
Packit 8f70b4
      return m;
Packit 8f70b4
   if(st!=UNPACK_SUCCESS)
Packit 8f70b4
   {
Packit 8f70b4
      if(st==UNPACK_PREMATURE_EOF) {
Packit 8f70b4
	 LogError(2,_("peer unexpectedly closed connection after %s"),recv_buf->Dump());
Packit 8f70b4
	 Disconnect(_("peer unexpectedly closed connection"));
Packit 8f70b4
      } else {
Packit 8f70b4
	 LogError(2,_("invalid peer response format"));
Packit 8f70b4
	 Disconnect(_("invalid peer response format"));
Packit 8f70b4
      }
Packit