Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

DownloadTransferBT.cpp

Go to the documentation of this file.
00001 //
00002 // DownloadTransferBT.cpp
00003 //
00004 // Copyright (c) Shareaza Development Team, 2002-2005.
00005 // This file is part of SHAREAZA (www.shareaza.com)
00006 //
00007 // Shareaza is free software; you can redistribute it
00008 // and/or modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2 of
00010 // the License, or (at your option) any later version.
00011 //
00012 // Shareaza is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with Shareaza; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //
00021 
00022 #include "StdAfx.h"
00023 #include <limits>
00024 #include "Shareaza.h"
00025 #include "Settings.h"
00026 #include "BTClients.h"
00027 #include "BTClient.h"
00028 #include "BTPacket.h"
00029 #include "Download.h"
00030 #include "Downloads.h"
00031 #include "DownloadSource.h"
00032 #include "DownloadTransferBT.h"
00033 #include "FragmentedFile.h"
00034 #include "Network.h"
00035 #include "Buffer.h"
00036 #include "BENode.h"
00037 
00038 #ifdef _DEBUG
00039 #undef THIS_FILE
00040 static char THIS_FILE[]=__FILE__;
00041 #define new DEBUG_NEW
00042 #endif
00043 
00045 // CDownloadTransferBT construction
00046 
00047 CDownloadTransferBT::CDownloadTransferBT(CDownloadSource* pSource, CBTClient* pClient) : CDownloadTransfer( pSource, PROTOCOL_BT )
00048 {
00049         ASSERT( m_pDownload->m_bBTH );
00050         ASSERT( m_pDownload->m_nSize != SIZE_UNKNOWN );
00051         
00052         m_pClient                       = pClient;
00053         m_nState                        = pClient ? dtsConnecting : dtsNull;
00054         m_sUserAgent            = _T("BitTorrent");
00055         
00056         m_bChoked                       = TRUE;
00057         m_bInterested           = FALSE;
00058         
00059         m_pAvailable            = NULL;
00060         
00061         m_tRunThrottle          = 0;
00062         m_tSourceRequest        = GetTickCount();
00063 }
00064 
00065 CDownloadTransferBT::~CDownloadTransferBT()
00066 {
00067         ASSERT( m_pClient == NULL );
00068         if ( m_pAvailable != NULL ) delete [] m_pAvailable;
00069 }
00070 
00072 // CDownloadTransferBT initiate
00073 
00074 BOOL CDownloadTransferBT::Initiate()
00075 {
00076         ASSERT( m_pClient == NULL );
00077         ASSERT( m_nState == dtsNull );
00078         m_pClient = new CBTClient();
00079         if ( ! m_pClient->Connect( this ) )
00080         {
00081                 delete m_pClient;
00082                 m_pClient = NULL;
00083                 Close( TS_FALSE );
00084                 return FALSE;
00085         }
00086         SetState( dtsConnecting );
00087         m_tConnected    = GetTickCount();
00088         m_pHost                 = m_pClient->m_pHost;
00089         m_sAddress              = m_pClient->m_sAddress;
00090         
00091         return TRUE;
00092 }
00093 
00095 // CDownloadTransferBT close
00096 
00097 void CDownloadTransferBT::Close(TRISTATE bKeepSource)
00098 {
00099         if ( m_pClient != NULL )
00100         {
00101                 m_pClient->m_pDownloadTransfer = NULL;
00102                 if ( m_pClient->IsOnline() )
00103                 {
00104                         m_pClient->Send( CBTPacket::New( BT_PACKET_NOT_INTERESTED ) );
00105                 }
00106                 else
00107                 {
00108                         m_pClient->Close();
00109                 }
00110                 m_pClient = NULL;
00111         }
00112         CDownloadTransfer::Close( bKeepSource );
00113 }
00114 
00116 // CDownloadTransferBT bandwidth control
00117 
00118 void CDownloadTransferBT::Boost()
00119 {
00120         if ( m_pClient == NULL ) return;
00121         m_pClient->m_mInput.pLimit = NULL;
00122 }
00123 
00124 DWORD CDownloadTransferBT::GetAverageSpeed()
00125 {
00126         return m_pSource->m_nSpeed = GetMeasuredSpeed();
00127 }
00128 
00129 DWORD CDownloadTransferBT::GetMeasuredSpeed()
00130 {
00131         if ( m_pClient == NULL ) return 0;
00132         m_pClient->Measure();
00133         return m_pClient->m_mInput.nMeasure;
00134 }
00135 
00136 CString CDownloadTransferBT::GetStateText(BOOL bLong)
00137 {
00138         if ( m_nState == dtsTorrent )
00139         {
00140                 CString str;
00141                 if ( ! m_bInterested ) LoadString( str, IDS_STATUS_UNINTERESTED );
00142                 else if ( m_bChoked ) LoadString( str, IDS_STATUS_CHOKED );
00143                 else LoadString( str, IDS_STATUS_REQUESTING );
00144                 return str;
00145         }
00146         return CDownloadTransfer::GetStateText( bLong );
00147 }
00148 
00150 // CDownloadTransferBT send packet helper
00151 
00152 void CDownloadTransferBT::Send(CBTPacket* pPacket, BOOL bRelease)
00153 {
00154         ASSERT( m_pClient != NULL );
00155         m_pClient->Send( pPacket, bRelease );
00156 }
00157 
00159 // CDownloadTransferBT run event
00160 
00161 BOOL CDownloadTransferBT::OnRun()
00162 {
00163         DWORD tNow = GetTickCount();
00164         if ( tNow - m_tRunThrottle >= 2000 )
00165         {
00166                 m_tRunThrottle = tNow;
00167                 ShowInterest();
00168                 if ( m_nState == dtsTorrent || m_nState == dtsRequesting || m_nState == dtsDownloading )
00169                 {
00170                         if ( ! SendRequests() ) return FALSE;
00171                 }
00172         }
00173         if ( m_pClient->m_bExchange && tNow - m_tSourceRequest >= Settings.BitTorrent.SourceExchangePeriod * 60000 )
00174         {
00175                 Send( CBTPacket::New( BT_PACKET_SOURCE_REQUEST ) );
00176                 m_tSourceRequest = tNow;
00177         }
00178         return CDownloadTransfer::OnRun();
00179 }
00180 
00182 // CDownloadTransferBT connection established event
00183 
00184 BOOL CDownloadTransferBT::OnConnected()
00185 {
00186         ASSERT( m_pClient != NULL );
00187         ASSERT( m_pSource != NULL );
00188 
00189         if( m_pDownload->IsCompleted() )
00190         {
00191                 // This source is only here to push start torrent uploads. (We don't want to download)
00192                 theApp.Message( MSG_DEFAULT, _T("Initiated push start for upload to %s"), (LPCTSTR)m_sAddress );
00193                 Close( TS_FALSE );
00194                 return FALSE;
00195         }
00196         else
00197         {
00198                 // Regular download
00199                 SetState( dtsTorrent );
00200                 m_pHost         = m_pClient->m_pHost;
00201                 m_sAddress      = m_pClient->m_sAddress;
00202                 m_pSource->SetLastSeen();
00203                 m_pClient->m_mInput.pLimit = &Downloads.m_nLimitGeneric;
00204                 theApp.Message( MSG_DEFAULT, IDS_DOWNLOAD_CONNECTED, (LPCTSTR)m_sAddress );
00205                 if ( ! m_pDownload->PrepareFile() )
00206                 {
00207                         Close( TS_TRUE );
00208                         return FALSE;
00209                 }
00210                 return TRUE;
00211         }
00212 }
00213 
00215 // CDownloadTransferBT bitfields
00216 
00217 BOOL CDownloadTransferBT::OnBitfield(CBTPacket* pPacket)
00218 {
00219         QWORD nBlockSize        = m_pDownload->m_pTorrent.m_nBlockSize;
00220         DWORD nBlockCount       = m_pDownload->m_pTorrent.m_nBlockCount;
00221         
00222         m_pSource->m_oAvailable.clear();
00223         
00224         if ( m_pAvailable != NULL ) delete [] m_pAvailable;
00225         m_pAvailable = NULL;
00226         
00227         if ( nBlockSize == 0 || nBlockCount == 0 ) return TRUE;
00228         
00229         m_pAvailable = new BYTE[ nBlockCount ];
00230         ZeroMemory( m_pAvailable, nBlockCount );
00231         
00232         for ( DWORD nBlock = 0 ; nBlock < nBlockCount && pPacket->GetRemaining() ; )
00233         {
00234                 BYTE nByte = pPacket->ReadByte();
00235                 
00236                 for ( int nBit = 7 ; nBit >= 0 && nBlock < nBlockCount ; nBit--, nBlock++ )
00237                 {
00238                         if ( nByte & ( 1 << nBit ) )
00239                         {
00240                                 QWORD nOffset = nBlockSize * nBlock;
00241                                 QWORD nLength = min( nBlockSize, m_pDownload->m_nSize - nOffset );
00242                 m_pSource->m_oAvailable.insert( m_pSource->m_oAvailable.end(),
00243                     FF::SimpleFragment( nOffset, nOffset + nLength ) );
00244                                 m_pAvailable[ nBlock ] = TRUE;
00245                         }
00246                 }
00247         }
00248         
00249         ShowInterest();
00250         return TRUE;
00251 }
00252 
00254 // CDownloadTransferBT have block updates
00255 
00256 void CDownloadTransferBT::SendFinishedBlock(DWORD nBlock)
00257 {
00258         if ( m_pClient == NULL || ! m_pClient->IsOnline() ) return;
00259         CBTPacket* pPacket = CBTPacket::New( BT_PACKET_HAVE );
00260         pPacket->WriteLongBE( nBlock );
00261         Send( pPacket );
00262 }
00263 
00264 BOOL CDownloadTransferBT::OnHave(CBTPacket* pPacket)
00265 {
00266         if ( pPacket->GetRemaining() != sizeof(int) ) return TRUE;
00267         QWORD nBlockSize        = m_pDownload->m_pTorrent.m_nBlockSize;
00268         DWORD nBlockCount       = m_pDownload->m_pTorrent.m_nBlockCount;
00269         DWORD nBlock            = pPacket->ReadLongBE();
00270         if ( nBlock >= nBlockCount ) return TRUE;
00271         QWORD nOffset = nBlockSize * nBlock;
00272         QWORD nLength = min( nBlockSize, m_pDownload->m_nSize - nOffset );
00273     m_pSource->m_oAvailable.insert( FF::SimpleFragment( nOffset, nOffset + nLength ) );
00274         
00275         if ( m_pAvailable == NULL )
00276         {
00277                 m_pAvailable = new BYTE[ nBlockCount ];
00278                 ZeroMemory( m_pAvailable, nBlockCount );
00279         }
00280         
00281         m_pAvailable[ nBlock ] = TRUE;
00282         ShowInterest();
00283         return TRUE;
00284 }
00285 
00287 // CDownloadTransferBT interest control
00288 
00289 void CDownloadTransferBT::ShowInterest()
00290 {
00291         BOOL bInterested = FALSE;
00292         
00293         // TODO: Use an algorithm similar to CDownloadWithTiger::FindNext.., rather
00294         // than relying on that algorithm to complete verifications here.
00295         
00296         if ( m_pAvailable == NULL )
00297         {
00298                 // Never interested if we don't know what they have
00299                 // bInterested = m_pDownload->GetVolumeRemaining() != 0;
00300         }
00301         else if ( QWORD nBlockSize = m_pDownload->m_pTorrent.m_nBlockSize )
00302         {
00303         for ( FF::SimpleFragmentList::ConstIterator pFragment
00304             = m_pDownload->GetEmptyFragmentList().begin();
00305             !bInterested && pFragment != m_pDownload->GetEmptyFragmentList().end();
00306             ++pFragment )
00307         {
00308             DWORD nBlock = DWORD( pFragment->begin() / nBlockSize );
00309             
00310                         for ( DWORD nEnd = DWORD( ( pFragment->end() - 1 ) / nBlockSize );
00311                 nBlock <= nEnd; ++nBlock )
00312                         {
00313                                 if ( m_pAvailable[ nBlock ] )
00314                                 {
00315                                         bInterested = TRUE;
00316                                         break;
00317                                 }
00318                         }
00319                 }
00320         }
00321         
00322         if ( bInterested != m_bInterested )
00323         {
00324                 m_bInterested = bInterested;
00325                 Send( CBTPacket::New( bInterested ? BT_PACKET_INTERESTED : BT_PACKET_NOT_INTERESTED ) );
00326                 
00327                 if ( ! bInterested )
00328                 {
00329             m_oRequested.clear();
00330                 }
00331         }
00332 }
00333 
00335 // CDownloadTransferBT choking
00336 
00337 BOOL CDownloadTransferBT::OnChoked(CBTPacket* pPacket)
00338 {
00339         if ( m_bChoked ) return TRUE;
00340         m_bChoked = TRUE;
00341         SetState( dtsTorrent );
00342         theApp.Message( MSG_DEBUG, _T("Download from %s was choked."), (LPCTSTR)m_sAddress );
00343     for ( FF::SimpleFragmentQueue::ConstIterator pFragment = m_oRequested.begin();
00344         pFragment != m_oRequested.end() ; ++pFragment )
00345         {
00346                 CBTPacket* pPacket = CBTPacket::New( BT_PACKET_CANCEL );
00347                 pPacket->WriteLongBE( (DWORD)( pFragment->begin() / m_pDownload->m_pTorrent.m_nBlockSize ) );
00348                 pPacket->WriteLongBE( (DWORD)( pFragment->begin() % m_pDownload->m_pTorrent.m_nBlockSize ) );
00349                 pPacket->WriteLongBE( (DWORD)pFragment->length() );
00350                 Send( pPacket );
00351         }
00352         m_oRequested.clear();
00353         return TRUE;
00354 }
00355 
00356 BOOL CDownloadTransferBT::OnUnchoked(CBTPacket* pPacket)
00357 {
00358         m_bChoked = FALSE;
00359         SetState( dtsTorrent );
00360         m_oRequested.clear();
00361         
00362         theApp.Message( MSG_DEBUG, _T("Download from %s was Unchoked."), (LPCTSTR)m_sAddress );
00363         
00364         return SendRequests();
00365 }
00366 
00368 // CDownloadTransferBT request pipe
00369 
00370 BOOL CDownloadTransferBT::SendRequests()
00371 {
00372         ASSERT( m_nState == dtsTorrent || m_nState == dtsRequesting || m_nState == dtsDownloading );
00373         if ( m_bChoked || ! m_bInterested )
00374         {
00375                 if ( m_oRequested.empty() ) SetState( dtsTorrent );
00376                 return TRUE;
00377         }
00378         if ( m_oRequested.size() >= (int)Settings.BitTorrent.RequestPipe )
00379         {
00380                 if ( m_nState != dtsDownloading ) SetState( dtsRequesting );
00381                 return TRUE;
00382         }
00383         QWORD nBlockSize = m_pDownload->m_pTorrent.m_nBlockSize;
00384         ASSERT( nBlockSize != 0 );
00385         if ( nBlockSize == 0 ) return TRUE;
00386         
00387     FF::SimpleFragmentList oPossible( m_pDownload->GetEmptyFragmentList() );
00388         
00389         if ( ! m_pDownload->m_bTorrentEndgame )
00390         {
00391                 for ( CDownloadTransfer* pTransfer = m_pDownload->GetFirstTransfer() ; pTransfer && !oPossible.empty() ; pTransfer = pTransfer->m_pDlNext )
00392                 {
00393                         pTransfer->SubtractRequested( oPossible );
00394                 }
00395         }
00396         while ( m_oRequested.size() < (int)Settings.BitTorrent.RequestPipe )
00397         {
00398                 QWORD nOffset, nLength;
00399                 if ( SelectFragment( oPossible, nOffset, nLength ) )
00400                 {
00401                         ChunkifyRequest( &nOffset, &nLength, Settings.BitTorrent.RequestSize, FALSE );
00402                         
00403             FF::SimpleFragment Selected( nOffset, nOffset + nLength );
00404             oPossible.erase( Selected );
00405                         
00406             m_oRequested.pushBack( Selected );
00407                         
00408                         int nType       = ( m_nDownloaded == 0 || ( nOffset % nBlockSize ) == 0 )
00409                                                 ? MSG_DEFAULT : MSG_DEBUG;
00410                         theApp.Message( nType, IDS_DOWNLOAD_FRAGMENT_REQUEST,
00411                                 nOffset, nOffset + nLength - 1,
00412                                 (LPCTSTR)m_pDownload->GetDisplayName(), (LPCTSTR)m_sAddress );
00413 #ifdef _DEBUG
00414                         DWORD ndBlock1 = (DWORD)( nOffset / nBlockSize );
00415                         DWORD ndBlock2 = (DWORD)( ( nOffset + nLength - 1 ) / nBlockSize );
00416                         ASSERT( ndBlock1 < m_pDownload->m_pTorrent.m_nBlockCount );
00417                         ASSERT( ndBlock1 == ndBlock2 );
00418                         ASSERT( nLength <= nBlockSize );
00419 #endif
00420                         CBTPacket* pPacket = CBTPacket::New( BT_PACKET_REQUEST );
00421                         pPacket->WriteLongBE( (DWORD)( nOffset / nBlockSize ) );
00422                         pPacket->WriteLongBE( (DWORD)( nOffset % nBlockSize ) );
00423                         pPacket->WriteLongBE( (DWORD)nLength );
00424                         Send( pPacket );
00425                 }
00426                 else
00427                 {
00428                         break;
00429                 }
00430         }
00431         // If there are no more possible chunks to request, and endgame is available but not active
00432         if ( oPossible.empty() && Settings.BitTorrent.Endgame && ! m_pDownload->m_bTorrentEndgame )
00433         {
00434                 // And the torrent is at least 95% complete
00435                 if ( m_pDownload->GetProgress() > 0.95 )
00436                 {
00437                         // Then activate endgame
00438                         m_pDownload->m_bTorrentEndgame = TRUE;
00439                         theApp.Message( MSG_DEBUG, _T("Torrent EndGame mode activated for %s"), m_pDownload->m_sLocalName );
00440                 }
00441         }
00442         
00443         if ( !m_oRequested.empty() && m_nState != dtsDownloading ) SetState( dtsRequesting );
00444         if ( m_oRequested.empty() ) SetState( dtsTorrent );
00445         return TRUE;
00446 }
00447 
00449 // CDownloadTransferBT fragment selection
00450 
00451 BOOL CDownloadTransferBT::SelectFragment(const FF::SimpleFragmentList& oPossible, QWORD& nOffset, QWORD& nLength)
00452 {
00453     FF::SimpleFragment oSelection( selectBlock( oPossible,
00454         m_pDownload->m_pTorrent.m_nBlockSize, m_pAvailable ) );
00455 
00456     if ( oSelection.end() == ::std::numeric_limits< FF::SimpleFragment::SizeType >::max() ) return FALSE;
00457 
00458     nOffset = oSelection.begin();
00459     nLength = oSelection.length();
00460 
00461     return TRUE;
00462 }
00463 
00465 // CDownloadTransferBT multi-source fragment handling
00466 
00467 BOOL CDownloadTransferBT::SubtractRequested(FF::SimpleFragmentList& ppFragments)
00468 {
00469         if ( m_oRequested.empty() || m_bChoked ) return FALSE;
00470         ppFragments.erase( m_oRequested.begin(), m_oRequested.end() );
00471         return TRUE;
00472 }
00473 
00474 BOOL CDownloadTransferBT::UnrequestRange(QWORD nOffset, QWORD nLength)
00475 {
00476         if ( m_oRequested.empty() ) return FALSE;
00477         ASSERT( m_pDownload->m_pTorrent.m_nBlockSize != 0 );
00478         if ( m_pDownload->m_pTorrent.m_nBlockSize == 0 ) return FALSE;
00479 
00480     FF::SimpleFragmentQueue oUnrequests = extractRange( m_oRequested,
00481         FF::SimpleFragment( nOffset, nOffset + nLength ) );
00482 
00483     for ( FF::SimpleFragmentQueue::ConstIterator pFragment
00484         = oUnrequests.begin(); pFragment != oUnrequests.end(); ++pFragment )
00485     {
00486                 CBTPacket* pPacket = CBTPacket::New( BT_PACKET_CANCEL );
00487                 pPacket->WriteLongBE( (DWORD)( pFragment->begin() / m_pDownload->m_pTorrent.m_nBlockSize ) );
00488                 pPacket->WriteLongBE( (DWORD)( pFragment->begin() % m_pDownload->m_pTorrent.m_nBlockSize ) );
00489                 pPacket->WriteLongBE( (DWORD)pFragment->length() );
00490                 Send( pPacket );
00491     }
00492 
00493         return !oUnrequests.empty();
00494 }
00495 
00497 // CDownloadTransferBT piece reception
00498 
00499 BOOL CDownloadTransferBT::OnPiece(CBTPacket* pPacket)
00500 {
00501         ASSERT( m_pClient != NULL );
00502         if ( pPacket->GetRemaining() < 8 ) return TRUE;
00503         if ( m_nState != dtsRequesting && m_nState != dtsDownloading ) return TRUE;
00504         SetState( dtsDownloading );
00505         DWORD nBlock    = pPacket->ReadLongBE();
00506         QWORD nOffset   = pPacket->ReadLongBE();
00507         QWORD nLength   = pPacket->GetRemaining();
00508         nOffset += (QWORD)nBlock * m_pDownload->m_pTorrent.m_nBlockSize;
00509         m_nDownloaded += nLength;
00510         m_pDownload->m_nTorrentDownloaded += nLength;
00511         m_pSource->AddFragment( nOffset, nLength );
00512         m_pSource->SetValid();
00513     m_oRequested.erase( FF::SimpleFragment( nOffset, nOffset + nLength ) );
00514         
00515         m_pDownload->SubmitData( nOffset,
00516                 pPacket->m_pBuffer + pPacket->m_nPosition, nLength );
00517         // TODO: SendRequests and ShowInterest could be combined.. SendRequests
00518         // is probably going to tell us if we are interested or not
00519         ShowInterest();
00520         return SendRequests();
00521 }
00522 
00524 // CDownloadTransferBT source exchange
00525 
00526 BOOL CDownloadTransferBT::OnSourceResponse(CBTPacket* pPacket)
00527 {
00528         CBuffer pInput;
00529         pInput.Add( pPacket->m_pBuffer, pPacket->GetRemaining() );
00530         CBENode* pRoot = CBENode::Decode( &pInput );
00531         if ( pRoot == NULL ) return TRUE;
00532         CBENode* pPeers = pRoot->GetNode( "peers" );
00533         if ( ! pPeers->IsType( CBENode::beList ) )
00534         {
00535                 delete pRoot;
00536                 return TRUE;
00537         }
00538         
00539         int nCount = 0;
00540         
00541         for ( int nPeer = 0 ; nPeer < pPeers->GetCount() ; nPeer++ )
00542         {
00543                 CBENode* pPeer = pPeers->GetNode( nPeer );
00544                 if ( ! pPeer->IsType( CBENode::beDict ) ) continue;
00545                 
00546                 CBENode* pURL = pPeer->GetNode( "url" );
00547                 
00548                 if ( pURL->IsType( CBENode::beString ) )
00549                 {
00550                         nCount += m_pDownload->AddSourceURL( pURL->GetString(), TRUE );
00551                 }
00552                 else
00553                 {
00554                         CBENode* pID = pPeer->GetNode( "peer id" );
00555                         if ( ! pID->IsType( CBENode::beString ) || pID->m_nValue != sizeof(SHA1) ) continue;
00556                         
00557                         CBENode* pIP = pPeer->GetNode( "ip" );
00558                         if ( ! pIP->IsType( CBENode::beString ) ) continue;
00559                         
00560                         CBENode* pPort = pPeer->GetNode( "port" );
00561                         if ( ! pPort->IsType( CBENode::beInt ) ) continue;
00562                         
00563                         SOCKADDR_IN saPeer;
00564                         if ( ! Network.Resolve( pIP->GetString(), (int)pPort->GetInt(), &saPeer ) ) continue;
00565                         
00566                         theApp.Message( MSG_DEBUG, _T("CDownloadTransferBT::OnSourceResponse(): %s: %s:%i"),
00567                                 (LPCTSTR)m_sAddress,
00568                                 (LPCTSTR)CString( inet_ntoa( saPeer.sin_addr ) ), htons( saPeer.sin_port ) );
00569                         
00570                         nCount += m_pDownload->AddSourceBT( (SHA1*)pID->m_pValue,
00571                                 &saPeer.sin_addr, htons( saPeer.sin_port ) );
00572                 }
00573         }
00574         
00575         delete pRoot;
00576         
00577         theApp.Message( MSG_DEFAULT, IDS_BT_CLIENT_EXCHANGE, nCount, (LPCTSTR)m_sAddress );
00578         
00579         return TRUE;
00580 }

Generated on Thu Dec 15 10:39:40 2005 for Shareaza 2.2.1.0 by  doxygen 1.4.2