Changeset 1058

Show
Ignore:
Timestamp:
05/05/08 15:16:26 (4 years ago)
Author:
rakshasa
Message:

* Initial seeding support added. Patch by Josef Drexler.

Location:
trunk
Files:
2 added
20 modified

Legend:

Unmodified
Added
Removed
  • trunk/libtorrent/src/download/download_main.cc

    r1032 r1058  
    4343#include "protocol/extensions.h" 
    4444#include "protocol/handshake_manager.h" 
     45#include "protocol/initial_seed.h" 
    4546#include "protocol/peer_connection_base.h" 
     47#include "protocol/peer_factory.h" 
    4648#include "tracker/tracker_manager.h" 
     49#include "torrent/download.h" 
    4750#include "torrent/exceptions.h" 
    4851#include "torrent/data/file_list.h" 
     
    5760#include "download_info.h" 
    5861#include "download_main.h" 
     62#include "download_manager.h" 
     63#include "download_wrapper.h" 
    5964 
    6065namespace torrent { 
     
    6873  m_chunkStatistics(new ChunkStatistics), 
    6974 
     75  m_initialSeeding(NULL), 
    7076  m_uploadThrottle(NULL), 
    7177  m_downloadThrottle(NULL) { 
     
    192198 
    193199  m_slotStopHandshakes(this); 
    194  
    195200  connection_list()->erase_remaining(connection_list()->begin(), ConnectionList::disconnect_available); 
    196201 
     202  delete m_initialSeeding; 
     203 
    197204  priority_queue_erase(&taskScheduler, &m_taskTrackerRequest); 
     205} 
     206 
     207bool 
     208DownloadMain::start_initial_seeding() { 
     209  if (!file_list()->is_done()) 
     210    return false; 
     211 
     212  m_initialSeeding = new InitialSeeding(this); 
     213  return true; 
     214} 
     215 
     216void 
     217DownloadMain::initial_seeding_done(PeerConnectionBase* pcb) { 
     218  if (m_initialSeeding == NULL) 
     219    throw internal_error("DownloadMain::initial_seeding_done called when not initial seeding."); 
     220 
     221  // Close all connections but the currently active one (pcb). 
     222  // That one will be closed by throw close_connection() later. 
     223  if (m_connectionList->size() > 1) { 
     224    ConnectionList::iterator itr = std::find(m_connectionList->begin(), m_connectionList->end(), pcb); 
     225    if (itr == m_connectionList->end()) 
     226      throw internal_error("DownloadMain::initial_seeding_done could not find current connection."); 
     227 
     228    std::iter_swap(m_connectionList->begin(), itr); 
     229    m_connectionList->erase_remaining(m_connectionList->begin() + 1, ConnectionList::disconnect_available); 
     230  } 
     231 
     232  // Switch to normal seeding. 
     233  DownloadManager::iterator itr = manager->download_manager()->find(m_info); 
     234  (*itr)->set_connection_type(Download::CONNECTION_SEED); 
     235  m_connectionList->slot_new_connection(&createPeerConnectionSeed); 
     236  delete m_initialSeeding; 
     237  m_initialSeeding = NULL; 
     238 
     239  // And close the current connection. 
     240  throw close_connection(); 
    198241} 
    199242 
  • trunk/libtorrent/src/download/download_main.h

    r1016 r1058  
    6666class DownloadInfo; 
    6767class ThrottleList; 
     68class InitialSeeding; 
    6869 
    6970class DownloadMain { 
     
    9596 
    9697  have_queue_type*    have_queue()                               { return &m_haveQueue; } 
     98 
     99  InitialSeeding*     initial_seeding()                          { return m_initialSeeding; } 
     100  bool                start_initial_seeding(); 
     101  void                initial_seeding_done(PeerConnectionBase* pcb); 
    97102 
    98103  ConnectionList*     connection_list()                          { return m_connectionList; } 
     
    158163  Delegator           m_delegator; 
    159164  have_queue_type     m_haveQueue; 
     165  InitialSeeding*     m_initialSeeding; 
    160166 
    161167  ConnectionList*     m_connectionList; 
  • trunk/libtorrent/src/protocol/Makefile.am

    r1043 r1058  
    1111        handshake_manager.cc \ 
    1212        handshake_manager.h \ 
     13        initial_seed.cc \ 
     14        initial_seed.h \ 
    1315        peer_chunks.h \ 
    1416        peer_connection_base.cc \ 
  • trunk/libtorrent/src/protocol/handshake.cc

    r1034 r1058  
    527527  // The download is just starting so we're not sending any 
    528528  // bitfield. Pretend we wrote it already. 
    529   if (m_download->file_list()->bitfield()->is_all_unset()) 
     529  if (m_download->file_list()->bitfield()->is_all_unset() || m_download->initial_seeding() != NULL) { 
    530530    m_writePos = m_download->file_list()->bitfield()->size_bytes(); 
    531   else 
     531    m_writeBuffer.write_32(0); 
     532 
     533    if (m_encryption.info()->is_encrypted()) 
     534      m_encryption.info()->encrypt(m_writeBuffer.end() - 4, 4); 
     535 
     536  } else { 
    532537    prepare_bitfield(); 
     538  } 
    533539 
    534540  m_state = READ_MESSAGE; 
     
    622628  // bitfield to send, we need to send a keep-alive now. 
    623629  if (m_writePos == m_download->file_list()->bitfield()->size_bytes()) 
    624     prepare_post_handshake(m_download->file_list()->bitfield()->is_all_unset()); 
     630    prepare_post_handshake(m_download->file_list()->bitfield()->is_all_unset() || m_download->initial_seeding() != NULL); 
    625631 
    626632  if (m_writeDone) 
  • trunk/libtorrent/src/protocol/handshake_manager.cc

    r1010 r1058  
    195195      // We need to make libtorrent more selective in the clients it 
    196196      // connects to, and to move this somewhere else. 
    197       (!download->file_list()->is_done() || !handshake->bitfield()->is_all_set()) && 
     197      (!download->file_list()->is_done() || !handshake->bitfield()->is_all_set() || download->initial_seeding() != NULL) && 
    198198 
    199199      (pcb = download->connection_list()->insert(handshake->peer_info(), 
  • trunk/libtorrent/src/protocol/peer_connection_base.h

    r1032 r1058  
    137137  void                cancel_transfer(BlockTransfer* transfer); 
    138138 
     139  // Insert into the poll unless we're blocking for throttling etc. 
     140  void                read_insert_poll_safe(); 
     141  void                write_insert_poll_safe(); 
     142 
    139143protected: 
    140144  static const uint32_t extension_must_encrypt = ~uint32_t(); 
     
    176180 
    177181  bool                send_pex_message(); 
    178  
    179   // Insert into the poll unless we're blocking for throttling etc. 
    180   void                read_insert_poll_safe(); 
    181   void                write_insert_poll_safe(); 
    182182 
    183183  DownloadMain*       m_download; 
  • trunk/libtorrent/src/protocol/peer_connection_leech.cc

    r1043 r1058  
    5151 
    5252#include "extensions.h" 
     53#include "initial_seed.h" 
    5354#include "peer_connection_leech.h" 
    5455 
     
    6667void 
    6768PeerConnection<type>::initialize_custom() { 
     69  if (type == Download::CONNECTION_INITIAL_SEED) { 
     70    if (m_download->initial_seeding() == NULL) { 
     71      // Can't throw close_connection or network_error here, we're still 
     72      // initializing. So close the socket and let that kill it later. 
     73      get_fd().close(); 
     74      return; 
     75    } 
     76 
     77    m_download->initial_seeding()->new_peer(this); 
     78  } 
    6879//   if (m_download->content()->chunks_completed() != 0) { 
    6980//     m_up->write_bitfield(m_download->file_list()->bitfield()->size_bytes()); 
     
    7889void 
    7990PeerConnection<type>::update_interested() { 
    80   if (type == Download::CONNECTION_SEED) 
     91  if (type != Download::CONNECTION_LEECH) 
    8192    return; 
    8293 
     
    118129  } 
    119130 
    120   if (type == Download::CONNECTION_SEED) 
     131  if (type != Download::CONNECTION_LEECH) 
    121132    return true; 
    122133 
     
    193204  switch (buf->read_8()) { 
    194205  case ProtocolBase::CHOKE: 
    195     if (type == Download::CONNECTION_SEED) 
     206    if (type != Download::CONNECTION_LEECH) 
    196207      return true; 
    197208 
     
    213224 
    214225  case ProtocolBase::UNCHOKE: 
    215     if (type == Download::CONNECTION_SEED) 
     226    if (type != Download::CONNECTION_LEECH) 
    216227      return true; 
    217228 
     
    259270 
    260271  case ProtocolBase::PIECE: 
    261     if (type == Download::CONNECTION_SEED) 
     272    if (type != Download::CONNECTION_LEECH) 
    262273      throw communication_error("Received a piece but the connection is strictly for seeding."); 
    263274 
     
    382393 
    383394      case ProtocolRead::READ_PIECE: 
    384         if (type == Download::CONNECTION_SEED) 
     395        if (type != Download::CONNECTION_LEECH) 
    385396          return; 
    386397 
     
    402413 
    403414      case ProtocolRead::READ_SKIP_PIECE: 
    404         if (type == Download::CONNECTION_SEED) 
     415        if (type != Download::CONNECTION_LEECH) 
    405416          return; 
    406417 
     
    490501  // request has been received while uninterested. The problem arises 
    491502  // as they send unchoke before receiving interested. 
    492   if (type != Download::CONNECTION_SEED && m_sendInterested && m_up->can_write_interested()) { 
     503  if (type == Download::CONNECTION_LEECH && m_sendInterested && m_up->can_write_interested()) { 
    493504    m_up->write_interested(m_downInterested); 
    494505    m_sendInterested = false; 
    495506  } 
    496507 
    497   if (type != Download::CONNECTION_SEED && m_tryRequest) { 
     508  if (type == Download::CONNECTION_LEECH && m_tryRequest) { 
    498509    if (!(m_tryRequest = !should_request()) && 
    499510        !(m_tryRequest = try_request_pieces()) && 
     
    509520  DownloadMain::have_queue_type* haveQueue = m_download->have_queue(); 
    510521 
    511   if (type != Download::CONNECTION_SEED &&  
     522  if (type == Download::CONNECTION_LEECH &&  
    512523      !haveQueue->empty() && 
    513524      m_peerChunks.have_timer() <= haveQueue->front().first && 
     
    523534  } 
    524535 
    525   while (type != Download::CONNECTION_SEED && !m_peerChunks.cancel_queue()->empty() && m_up->can_write_cancel()) { 
     536  if (type == Download::CONNECTION_INITIAL_SEED && m_up->can_write_have()) 
     537    offer_chunk(); 
     538 
     539  while (type == Download::CONNECTION_LEECH && !m_peerChunks.cancel_queue()->empty() && m_up->can_write_cancel()) { 
    526540    m_up->write_cancel(m_peerChunks.cancel_queue()->front()); 
    527541    m_peerChunks.cancel_queue()->pop_front(); 
     
    534548  } else if (!m_upChoke.choked() && 
    535549             !m_peerChunks.upload_queue()->empty() && 
    536              m_up->can_write_piece()) { 
     550             m_up->can_write_piece() && 
     551             (type != Download::CONNECTION_INITIAL_SEED || should_upload())) { 
    537552    write_prepare_piece(); 
    538553  } 
     
    639654  m_download->chunk_statistics()->received_have_chunk(&m_peerChunks, index, m_download->file_list()->chunk_size()); 
    640655 
     656  if (type == Download::CONNECTION_INITIAL_SEED) 
     657    m_download->initial_seeding()->chunk_seen(index, this); 
     658 
     659  // Disconnect seeds when we are seeding (but not for initial seeding 
     660  // so that we keep accurate chunk statistics until that is done). 
    641661  if (m_peerChunks.bitfield()->is_all_set()) { 
    642     if (type == Download::CONNECTION_SEED || m_download->file_list()->is_done()) 
     662    if (type == Download::CONNECTION_SEED ||  
     663        (type != Download::CONNECTION_INITIAL_SEED && m_download->file_list()->is_done())) 
    643664      throw close_connection(); 
    644665 
     
    646667  } 
    647668 
    648   if (type == Download::CONNECTION_SEED || m_download->file_list()->is_done()) 
     669  if (type != Download::CONNECTION_LEECH || m_download->file_list()->is_done()) 
    649670    return; 
    650671 
     
    677698} 
    678699 
    679 // Explicit instatiation of the member functions and vtable. 
     700template<> 
     701void 
     702PeerConnection<Download::CONNECTION_INITIAL_SEED>::offer_chunk() { 
     703  // If bytes left to send in this chunk minus bytes about to be sent is zero, 
     704  // assume the peer will have got the chunk completely. In that case we may 
     705  // get another one to offer if not enough other peers are interested even 
     706  // if the peer would otherwise still be blocked. 
     707  uint32_t bytesLeft = m_data.bytesLeft; 
     708  if (!m_peerChunks.upload_queue()->empty() && m_peerChunks.upload_queue()->front().index() == m_data.lastIndex) 
     709    bytesLeft -= m_peerChunks.upload_queue()->front().length(); 
     710 
     711  uint32_t index = m_download->initial_seeding()->chunk_offer(this, bytesLeft == 0 ? m_data.lastIndex : InitialSeeding::no_offer); 
     712 
     713  if (index == InitialSeeding::no_offer || index == m_data.lastIndex) 
     714    return; 
     715 
     716  m_up->write_have(index); 
     717  m_data.lastIndex = index; 
     718  m_data.bytesLeft = m_download->file_list()->chunk_index_size(index); 
     719} 
     720 
     721template<> 
     722bool 
     723PeerConnection<Download::CONNECTION_INITIAL_SEED>::should_upload() { 
     724  // For initial seeding, check if chunk is well seeded now, and if so 
     725  // remove it from the queue to better use our bandwidth on rare chunks. 
     726  while (!m_peerChunks.upload_queue()->empty() && 
     727         !m_download->initial_seeding()->should_upload(m_peerChunks.upload_queue()->front().index())) 
     728    m_peerChunks.upload_queue()->pop_front(); 
     729 
     730  // If queue ends up empty, choke peer to let it know that it 
     731  // shouldn't wait for the cancelled pieces to be sent. 
     732  if (m_peerChunks.upload_queue()->empty()) { 
     733    m_download->upload_choke_manager()->set_not_queued(this, &m_upChoke); 
     734    m_download->upload_choke_manager()->set_queued(this, &m_upChoke); 
     735 
     736  // If we're sending the chunk we last offered, adjust bytes left in it. 
     737  } else if (m_peerChunks.upload_queue()->front().index() == m_data.lastIndex) { 
     738    m_data.bytesLeft -= m_peerChunks.upload_queue()->front().length(); 
     739 
     740    if (!m_data.bytesLeft) 
     741      m_data.lastIndex = InitialSeeding::no_offer; 
     742  } 
     743 
     744  return !m_peerChunks.upload_queue()->empty(); 
     745} 
     746 
     747// Explicit instantiation of the member functions and vtable. 
    680748template class PeerConnection<Download::CONNECTION_LEECH>; 
    681749template class PeerConnection<Download::CONNECTION_SEED>; 
    682  
    683 } 
     750template class PeerConnection<Download::CONNECTION_INITIAL_SEED>; 
     751 
     752} 
  • trunk/libtorrent/src/protocol/peer_connection_leech.h

    r1043 r1058  
    4444namespace torrent { 
    4545 
     46// Type-specific data. 
     47template<Download::ConnectionType type> struct PeerConnectionData; 
     48 
     49template<> struct PeerConnectionData<Download::CONNECTION_LEECH> { }; 
     50 
     51template<> struct PeerConnectionData<Download::CONNECTION_SEED> { }; 
     52 
     53template<> struct PeerConnectionData<Download::CONNECTION_INITIAL_SEED> { 
     54  PeerConnectionData() : lastIndex(~uint32_t()) { } 
     55  uint32_t lastIndex; 
     56  uint32_t bytesLeft; 
     57}; 
     58 
    4659template<Download::ConnectionType type> 
    4760class PeerConnection : public PeerConnectionBase { 
     
    6073  void                read_have_chunk(uint32_t index); 
    6174 
     75  void                offer_chunk(); 
     76  bool                should_upload(); 
     77 
    6278  inline void         fill_write_buffer(); 
     79 
     80  PeerConnectionData<type> m_data; 
    6381}; 
    6482 
  • trunk/libtorrent/src/protocol/peer_factory.cc

    r1043 r1058  
    5656} 
    5757 
     58PeerConnectionBase* 
     59createPeerConnectionInitialSeed(bool encrypted) { 
     60  PeerConnectionBase* pc = new PeerConnection<Download::CONNECTION_INITIAL_SEED>; 
     61 
     62  return pc; 
    5863} 
     64 
     65} 
  • trunk/libtorrent/src/protocol/peer_factory.h

    r939 r1058  
    4444PeerConnectionBase* createPeerConnectionDefault(bool encrypted); 
    4545PeerConnectionBase* createPeerConnectionSeed(bool encrypted); 
     46PeerConnectionBase* createPeerConnectionInitialSeed(bool encrypted); 
    4647 
    4748} 
  • trunk/libtorrent/src/torrent/download.cc

    r1035 r1058  
    107107  file_list()->open(flags & ~FileList::open_no_create); 
    108108 
     109  if (m_ptr->connection_type() == CONNECTION_INITIAL_SEED) { 
     110    if (!m_ptr->main()->start_initial_seeding())  
     111      set_connection_type(CONNECTION_SEED); 
     112  } 
     113 
    109114  m_ptr->main()->start(); 
    110115  m_ptr->main()->tracker_manager()->set_active(true); 
     
    502507    m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionSeed); 
    503508    break; 
     509  case CONNECTION_INITIAL_SEED: 
     510    if (is_active() && m_ptr->main()->initial_seeding() == NULL) 
     511      throw input_error("Can't switch to initial seeding: download is active."); 
     512    m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionInitialSeed); 
     513    break; 
    504514  default: 
    505515    throw input_error("torrent::Download::set_connection_type(...) received an unknown type."); 
  • trunk/libtorrent/src/torrent/download.h

    r1035 r1058  
    181181  typedef enum { 
    182182    CONNECTION_LEECH, 
    183     CONNECTION_SEED 
     183    CONNECTION_SEED, 
     184    CONNECTION_INITIAL_SEED, 
    184185  } ConnectionType; 
    185186 
  • trunk/libtorrent/src/torrent/peer/peer_info.cc

    r991 r1058  
    7070    throw internal_error("PeerInfo::~PeerInfo() m_transferCounter != 0."); 
    7171 
     72  if (is_blocked()) 
     73    throw internal_error("PeerInfo::~PeerInfo() peer is blocked."); 
     74 
    7275  delete rak::socket_address::cast_from(m_address); 
    7376} 
  • trunk/libtorrent/src/torrent/peer/peer_info.h

    r1013 r1058  
    4848  friend class Handshake; 
    4949  friend class HandshakeManager; 
     50  friend class InitialSeeding; 
    5051  friend class PeerList; 
    5152  friend class ProtocolExtension; 
     
    5455  static const int flag_incoming  = (1 << 1); 
    5556  static const int flag_handshake = (1 << 2); 
     57  static const int flag_blocked   = (1 << 3);   // For initial seeding. 
     58  static const int flag_restart   = (1 << 4); 
    5659 
    5760  PeerInfo(const sockaddr* address); 
     
    6164  bool                is_incoming() const                   { return m_flags & flag_incoming; } 
    6265  bool                is_handshake() const                  { return m_flags & flag_handshake; } 
     66  bool                is_blocked() const                    { return m_flags & flag_blocked; } 
     67  bool                is_restart() const                    { return m_flags & flag_restart; } 
    6368 
    6469  int                 flags() const                         { return m_flags; } 
  • trunk/libtorrent/src/torrent/peer/peer_list.cc

    r1012 r1058  
    307307        itr->second->last_connection() >= timer || 
    308308 
    309         (flags & cull_keep_interesting && itr->second->failed_counter() != 0)) { 
     309        (flags & cull_keep_interesting &&  
     310         (itr->second->failed_counter() != 0 || itr->second->is_blocked()))) { 
    310311      itr++; 
    311312      continue; 
  • trunk/rtorrent/doc/rtorrent.1.xml

    r1045 r1058  
    155155Set the 'create/resize queued' flags on all files in a torrent. This 
    156156is necessary if the underlying files in a torrent have been deleted or 
    157 truncates, and thus rtorrent much recreate them. 
     157truncated, and thus rtorrent must recreate them. 
    158158          </para></listitem> 
    159159        </varlistentry> 
     
    178178          <listitem><para> 
    179179Call commands or change settings. 
     180          </para></listitem> 
     181        </varlistentry> 
     182 
     183        <varlistentry> 
     184          <term>^B</term> 
     185          <listitem><para> 
     186Set download to perform initial seeding. Only use when 
     187you are the first and only seeder so far for the download. 
    180188          </para></listitem> 
    181189        </varlistentry> 
  • trunk/rtorrent/src/command_download.cc

    r1049 r1058  
    170170  else if (name == "seed") 
    171171    connType = torrent::Download::CONNECTION_SEED; 
     172  else if (name == "initial_seed") 
     173    connType = torrent::Download::CONNECTION_INITIAL_SEED; 
    172174  else 
    173175    throw torrent::input_error("Unknown peer connection type selected."); 
     
    193195  case torrent::Download::CONNECTION_SEED: 
    194196    return "seed"; 
     197  case torrent::Download::CONNECTION_INITIAL_SEED: 
     198    return "initial_seed"; 
    195199  default: 
    196200    return "unknown"; 
     
    493497 
    494498  ADD_CD_STRING_BI("connection_current", std::ptr_fun(&apply_d_connection_type), std::ptr_fun(&retrieve_d_connection_type)); 
     499  ADD_CD_VARIABLE_STRING("connection_leech",      "rtorrent", "connection_leech"); 
     500  ADD_CD_VARIABLE_STRING("connection_seed",       "rtorrent", "connection_seed"); 
    495501 
    496502  ADD_CD_VALUE_BI("hashing_failed",      std::mem_fun(&core::Download::set_hash_failed), std::mem_fun(&core::Download::is_hash_failed)); 
     
    499505  // logging support. 
    500506  ADD_CD_STRING_BI("message",            std::mem_fun(&core::Download::set_message), std::mem_fun(&core::Download::message)); 
    501  
    502   add_copy_to_download("get_connection_leech", "d.get_connection_leech"); 
    503   add_copy_to_download("set_connection_leech", "d.set_connection_leech"); 
    504   add_copy_to_download("get_connection_seed", "d.get_connection_seed"); 
    505   add_copy_to_download("set_connection_seed", "d.set_connection_seed"); 
    506507 
    507508  ADD_CD_VALUE_MEM_BI("max_file_size", &core::Download::file_list, &torrent::FileList::set_max_file_size, &torrent::FileList::max_file_size); 
  • trunk/rtorrent/src/core/download_factory.cc

    r1049 r1058  
    196196  if (!rtorrent->has_key_string("custom5")) rtorrent->insert_key("custom5", std::string()); 
    197197 
    198   // Move to 'rtorrent'. 
    199   rpc::call_command("d.set_connection_leech", m_variables["connection_leech"], rpc::make_target(download)); 
    200   rpc::call_command("d.set_connection_seed",  m_variables["connection_seed"], rpc::make_target(download)); 
    201  
    202198  rpc::call_command("d.set_uploads_max",      rpc::call_command_void("get_max_uploads"), rpc::make_target(download)); 
    203199  rpc::call_command("d.set_peers_min",        rpc::call_command_void("get_min_peers"), rpc::make_target(download)); 
     
    353349 
    354350  rtorrent->insert_preserve_copy("ignore_commands", (int64_t)0); 
    355 } 
    356  
    357 } 
     351 
     352  rtorrent->insert_preserve_type("connection_leech", m_variables["connection_leech"]); 
     353  rtorrent->insert_preserve_type("connection_seed", m_variables["connection_seed"]); 
     354} 
     355 
     356} 
  • trunk/rtorrent/src/core/download_list.cc

    r1051 r1058  
    399399    rpc::call_command("d.set_state_counter", rpc::call_command_value("d.get_state_counter", rpc::make_target(download)), rpc::make_target(download)); 
    400400 
     401    // If initial seeding is complete, don't try it again when restarting. 
     402    if (download->is_done()) 
     403      rpc::call_command("d.set_connection_seed", rpc::call_command_void("d.get_connection_current", rpc::make_target(download)), rpc::make_target(download)); 
     404 
    401405    // Save the state after all the slots, etc have been called so we 
    402406    // include the modifications they may make. 
  • trunk/rtorrent/src/ui/element_download_list.cc

    r1050 r1058  
    7777                                  "{d.set_ignore_commands=0, print=\"Torrent set to heed commands.\"}," 
    7878                                  "{d.set_ignore_commands=1, print=\"Torrent set to ignore commands.\"}"); 
     79  m_bindings['B'-'@']= sigc::bind(sigc::mem_fun(*this, &ElementDownloadList::receive_command), 
     80                                  "branch=d.is_active=," 
     81                                  "{print=\"Cannot enable initial seeding on an active download.\"}," 
     82                                  "{d.set_connection_seed=initial_seed, print=\"Enabled initial seeding for the selected download.\"}"); 
    7983 
    8084  m_bindings['U']    = sigc::bind(sigc::mem_fun(*this, &ElementDownloadList::receive_command), "d.delete_tied=; print=\"Cleared tied to file association for the selected download.\"");