00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "StdAfx.h"
00023 #include "Shareaza.h"
00024 #include "Settings.h"
00025 #include "Uploads.h"
00026 #include "UploadFile.h"
00027 #include "UploadFiles.h"
00028 #include "UploadQueue.h"
00029 #include "UploadQueues.h"
00030 #include "UploadTransferHTTP.h"
00031 #include "TransferFile.h"
00032 #include "Transfers.h"
00033 #include "Remote.h"
00034 #include "ShellIcons.h"
00035 #include "Statistics.h"
00036 #include "Buffer.h"
00037 #include "Schema.h"
00038 #include "XML.h"
00039
00040 #include "Network.h"
00041 #include "Library.h"
00042 #include "SharedFile.h"
00043 #include "Downloads.h"
00044 #include "Download.h"
00045
00046 #include "LocalSearch.h"
00047 #include "ImageServices.h"
00048 #include "ThumbCache.h"
00049 #include "Neighbours.h"
00050 #include "Neighbour.h"
00051 #include "G2Packet.h"
00052 #include "GProfile.h"
00053 #include "Security.h"
00054
00055 #include "SHA.h"
00056 #include "ED2K.h"
00057 #include "TigerTree.h"
00058
00059 #ifdef _DEBUG
00060 #undef THIS_FILE
00061 static char THIS_FILE[]=__FILE__;
00062 #define new DEBUG_NEW
00063 #endif
00064
00065
00067
00068
00069 CUploadTransferHTTP::CUploadTransferHTTP() : CUploadTransfer( PROTOCOL_HTTP )
00070 {
00071 m_bKeepAlive = TRUE;
00072 m_nGnutella = 0;
00073 m_nReaskMultiplier = 1;
00074 m_bNotShareaza = FALSE;
00075 }
00076
00077 CUploadTransferHTTP::~CUploadTransferHTTP()
00078 {
00079 }
00080
00082
00083
00084 void CUploadTransferHTTP::AttachTo(CConnection* pConnection)
00085 {
00086 CUploadTransfer::AttachTo( pConnection );
00087
00088 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_ACCEPTED, (LPCTSTR)m_sAddress );
00089
00090 m_mInput.pLimit = &Settings.Bandwidth.Request;
00091 m_mOutput.pLimit = &m_nBandwidth;
00092
00093 m_nState = upsRequest;
00094 m_tRequest = m_tConnected;
00095
00096 OnRead();
00097 }
00098
00100
00101
00102 BOOL CUploadTransferHTTP::OnRead()
00103 {
00104 CUploadTransfer::OnRead();
00105
00106 switch ( m_nState )
00107 {
00108 case upsRequest:
00109 case upsQueued:
00110 if ( ! ReadRequest() ) return FALSE;
00111 if ( m_nState != upsHeaders ) break;
00112
00113 case upsHeaders:
00114 return ReadHeaders();
00115
00116 }
00117
00118 return TRUE;
00119 }
00120
00122
00123
00124 BOOL CUploadTransferHTTP::ReadRequest()
00125 {
00126 CString strLine;
00127
00128 if ( ! m_pInput->ReadLine( strLine ) ) return TRUE;
00129 if ( strLine.GetLength() > 512 ) strLine = _T("#LINE_TOO_LONG#");
00130
00131 if ( m_nState == upsQueued && m_pQueue != NULL )
00132 {
00133 DWORD tLimit = Settings.Uploads.QueuePollMin;
00134
00135 tLimit *= m_nReaskMultiplier;
00136
00137 if ( GetTickCount() - m_tRequest < tLimit )
00138 {
00139 theApp.Message( MSG_ERROR, IDS_UPLOAD_BUSY_FAST, (LPCTSTR)m_sAddress );
00140 Close();
00141 return FALSE;
00142 }
00143 }
00144
00145 int nChar = strLine.Find( _T(" HTTP/") );
00146
00147 if ( strLine.GetLength() < 14 || nChar < 5 ||
00148 ( strLine.Left( 4 ) != _T("GET ") && strLine.Left( 5 ) != _T("HEAD ") ) )
00149 {
00150 theApp.Message( MSG_ERROR, IDS_UPLOAD_NOHTTP, (LPCTSTR)m_sAddress );
00151 Close();
00152 return FALSE;
00153 }
00154
00155 ClearRequest();
00156
00157 m_bHead = ( strLine.Left( 5 ) == _T("HEAD ") );
00158 m_bConnectHdr = FALSE;
00159 m_bKeepAlive = TRUE;
00160 m_bHostBrowse = FALSE;
00161 m_bDeflate = FALSE;
00162 m_bBackwards = FALSE;
00163 m_bRange = FALSE;
00164 m_bQueueMe = FALSE;
00165 m_bNotShareaza = FALSE;
00166
00167 m_bMetadata = FALSE;
00168 m_bTigerTree = FALSE;
00169
00170 m_sLocations.Empty();
00171 m_sRanges.Empty();
00172
00173 CString strRequest = strLine.Mid( m_bHead ? 5 : 4, nChar - ( m_bHead ? 5 : 4 ) );
00174
00175 if ( strRequest.GetLength() > 5 && strRequest.Right( 1 ) == _T("/") )
00176 {
00177 strRequest = strRequest.Left( strRequest.GetLength() - 1 );
00178 }
00179
00180 strRequest = URLDecode( strRequest );
00181
00182 if ( strRequest != m_sRequest )
00183 {
00184 if ( m_sRequest.Find( _T("/gnutella/tigertree/") ) < 0 &&
00185 strRequest.Find( _T("/gnutella/tigertree/") ) < 0 &&
00186 m_sRequest.Find( _T("/gnutella/thex/") ) < 0 &&
00187 strRequest.Find( _T("/gnutella/thex/") ) < 0 &&
00188 m_sRequest.Find( _T("/gnutella/metadata/") ) < 0 &&
00189 strRequest.Find( _T("/gnutella/metadata/") ) < 0 )
00190 {
00191 UploadQueues.Dequeue( this );
00192 }
00193
00194 m_sRequest = strRequest;
00195 }
00196
00197 theApp.Message( MSG_DEBUG, _T("%s: UPLOAD PATH: %s"), (LPCTSTR)m_sAddress, (LPCTSTR)m_sRequest );
00198
00199 m_nState = upsHeaders;
00200 m_tRequest = GetTickCount();
00201
00202 return TRUE;
00203 }
00204
00206
00207
00208 BOOL CUploadTransferHTTP::OnHeaderLine(CString& strHeader, CString& strValue)
00209 {
00210 theApp.Message( MSG_DEBUG, _T("%s: UPLOAD HEADER: %s: %s"), (LPCTSTR)m_sAddress, (LPCTSTR)strHeader, strValue );
00211
00212 if ( strHeader.CompareNoCase( _T("Connection") ) == 0 )
00213 {
00214 if ( strValue.CompareNoCase( _T("close") ) == 0 ) m_bKeepAlive = FALSE;
00215 m_bConnectHdr = TRUE;
00216 }
00217 else if ( strHeader.CompareNoCase( _T("Accept") ) == 0 )
00218 {
00219 CharLower( strValue.GetBuffer() );
00220 strValue.ReleaseBuffer();
00221 if ( strValue.Find( _T("application/x-gnutella-packets") ) >= 0 ) m_bHostBrowse = 1;
00222 if ( strValue.Find( _T("application/x-gnutella2") ) >= 0 ) m_bHostBrowse = 2;
00223 if ( strValue.Find( _T("application/x-shareaza") ) >= 0 ) m_bHostBrowse = 2;
00224 }
00225 else if ( strHeader.CompareNoCase( _T("Accept-Encoding") ) == 0 )
00226 {
00227 if ( _tcsistr( strValue, _T("deflate") ) ) m_bDeflate = TRUE;
00228 if ( Settings.Uploads.AllowBackwards && _tcsistr( strValue, _T("backwards") ) ) m_bBackwards = TRUE;
00229 }
00230 else if ( strHeader.CompareNoCase( _T("Range") ) == 0 )
00231 {
00232 QWORD nFrom = 0, nTo = 0;
00233
00234 if ( _stscanf( strValue, _T("bytes=%I64i-%I64i"), &nFrom, &nTo ) == 2 )
00235 {
00236 m_nOffset = nFrom;
00237 m_nLength = nTo + 1 - nFrom;
00238 m_bRange = TRUE;
00239 }
00240 else if ( _stscanf( strValue, _T("bytes=%I64i-"), &nFrom ) == 1 )
00241 {
00242 m_nOffset = nFrom;
00243 m_nLength = SIZE_UNKNOWN;
00244 m_bRange = TRUE;
00245 }
00246 }
00247 else if ( strHeader.CompareNoCase( _T("X-Gnutella-Content-URN") ) == 0 ||
00248 strHeader.CompareNoCase( _T("X-Content-URN") ) == 0 ||
00249 strHeader.CompareNoCase( _T("Content-URN") ) == 0 )
00250 {
00251 HashesFromURN( strValue );
00252 m_nGnutella |= 1;
00253 }
00254 else if ( strHeader.CompareNoCase( _T("X-Gnutella-Alternate-Location") ) == 0 ||
00255 strHeader.CompareNoCase( _T("Alt-Location") ) == 0 ||
00256 strHeader.CompareNoCase( _T("X-Alt") ) == 0 )
00257 {
00258 if ( Settings.Library.SourceMesh )
00259 {
00260 if ( strValue.Find( _T("Zhttp://") ) < 0 ) m_sLocations = strValue;
00261 }
00262 m_nGnutella |= 1;
00263 }
00264 else if ( strHeader.CompareNoCase( _T("X-NAlt") ) == 0 )
00265 {
00266
00267 }
00268 else if ( strHeader.CompareNoCase( _T("X-Node") ) == 0 )
00269 {
00270 m_bNotShareaza = TRUE;
00271 }
00272 else if ( strHeader.CompareNoCase( _T("X-Queue") ) == 0 )
00273 {
00274 m_bQueueMe = TRUE;
00275 m_nGnutella |= 1;
00276 if ( strValue == _T("1.0") ) m_bNotShareaza = TRUE;
00277 }
00278 else if ( strHeader.CompareNoCase( _T("X-Nick") ) == 0 ||
00279 strHeader.CompareNoCase( _T("X-Name") ) == 0 ||
00280 strHeader.CompareNoCase( _T("X-UserName") ) == 0 )
00281 {
00282 m_sNick = URLDecode( strValue );
00283 }
00284 else if ( strHeader.CompareNoCase( _T("X-Features") ) == 0 )
00285 {
00286 if ( _tcsistr( strValue, _T("g2/") ) != NULL ) m_nGnutella |= 2;
00287 if ( _tcsistr( strValue, _T("gnet2/") ) != NULL ) m_nGnutella |= 2;
00288 if ( _tcsistr( strValue, _T("gnutella2/") ) != NULL ) m_nGnutella |= 2;
00289 if ( m_nGnutella == 0 ) m_nGnutella = 1;
00290 }
00291
00292 return CUploadTransfer::OnHeaderLine( strHeader, strValue );
00293 }
00294
00296
00297
00298 BOOL CUploadTransferHTTP::OnHeadersComplete()
00299 {
00300 if ( Uploads.EnforcePerHostLimit( this, TRUE ) ) return FALSE;
00301
00302 if ( _tcsistr( m_sUserAgent, _T("shareaza") ) != NULL )
00303 {
00304
00305 m_nGnutella |= 3;
00306 if ( m_sUserAgent == _T("Shareaza 1.4.0.0") ) m_bQueueMe = TRUE;
00307
00308
00309 if ( m_bNotShareaza )
00310 {
00311 SendResponse( IDR_HTML_FILENOTFOUND );
00312 theApp.Message( MSG_ERROR, _T("Client %s has a spoofed user agent, banning"), (LPCTSTR)m_sAddress );
00313
00314 Security.Ban( &m_pHost.sin_addr, banWeek, FALSE );
00315 Remove( FALSE );
00316 return FALSE;
00317 }
00318 }
00319 else if ( _tcsistr( m_sUserAgent, _T("trustyfiles") ) != NULL ||
00320 _tcsistr( m_sUserAgent, _T("gnucdna") ) != NULL ||
00321 _tcsistr( m_sUserAgent, _T("adagio") ) != NULL )
00322 {
00323
00324 m_nGnutella |= 3;
00325 }
00326 else if ( m_nGnutella & 2 )
00327 {
00328
00329 if ( _tcsistr( m_sUserAgent, _T("phex") ) != NULL )
00330 {
00331
00332
00333 m_nGnutella = 1;
00334
00335 if ( ! Settings.Gnutella1.EnableToday )
00336 {
00337
00338 SendResponse( IDR_HTML_FILENOTFOUND );
00339 theApp.Message( MSG_ERROR, _T("Client %s has a fake G2 header, banning"), (LPCTSTR)m_sAddress );
00340
00341 Security.Ban( &m_pHost.sin_addr, banWeek, FALSE );
00342 Remove( FALSE );
00343 return FALSE;
00344 }
00345 }
00346 }
00347
00348 if ( m_sRequest == _T("/") || StartsWith( m_sRequest, _T("/gnutella/browse/v1") ) )
00349 {
00350
00351
00352 if ( ( m_bHostBrowse == 1 && ! Settings.Community.ServeFiles ) ||
00353 ( m_bHostBrowse == 2 && ! Settings.Community.ServeProfile && ! Settings.Community.ServeFiles ) )
00354 {
00355 theApp.Message( MSG_ERROR, IDS_UPLOAD_BROWSE_DENIED, (LPCTSTR)m_sAddress );
00356 m_bHostBrowse = FALSE;
00357 }
00358
00359 if ( m_bHostBrowse )
00360 {
00361 RequestHostBrowse();
00362 }
00363 else
00364 {
00365 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_ABOUT, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
00366 SendResponse( IDR_HTML_ABOUT );
00367 }
00368
00369 return TRUE;
00370 }
00371 else if ( StartsWith( m_sRequest, _T("/remote") ) || StartsWith( m_sRequest, _T("/favicon.ico") ) )
00372 {
00373
00374
00375
00376 if ( Settings.Remote.Enable )
00377 {
00378 m_pInput->Prefix( "GET /remote/ HTTP/1.0\r\n\r\n" );
00379 new CRemote( this );
00380 Remove( FALSE );
00381 return FALSE;
00382 }
00383 }
00384 else if ( IsAgentBlocked() )
00385 {
00386 if ( m_sFileName.IsEmpty() ) m_sFileName = _T("file");
00387 SendResponse( IDR_HTML_BROWSER );
00388 theApp.Message( MSG_ERROR, IDS_UPLOAD_BROWSER, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00389 Security.Ban( &m_pHost.sin_addr, ban5Mins, FALSE );
00390 if ( m_sUserAgent.Find( _T("Mozilla") ) >= 0 ) return TRUE;
00391 Remove( FALSE );
00392 return FALSE;
00393 }
00394 else if ( IsNetworkDisabled() )
00395 {
00396
00397
00398 if ( StartsWith( m_sRequest, _T("/uri-res/N2R?urn:") ) )
00399 {
00400 LPCTSTR pszURN = (LPCTSTR)m_sRequest + 13;
00401
00402 CSingleLock oLock( &Library.m_pSection );
00403
00404 if ( oLock.Lock( 50 ) )
00405 {
00406 if ( CLibraryFile* pFile = LibraryMaps.LookupFileByURN( pszURN, TRUE, TRUE ) )
00407 {
00408 if ( UploadQueues.CanUpload( PROTOCOL_HTTP, pFile, TRUE ) )
00409 {
00410
00411 SendResponse( IDR_HTML_DISABLED );
00412 theApp.Message( MSG_ERROR, IDS_UPLOAD_DISABLED, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
00413 Security.Ban( &m_pHost.sin_addr, ban2Hours, FALSE );
00414 Remove( FALSE );
00415 return FALSE;
00416 }
00417 }
00418 }
00419
00420 SendResponse( IDR_HTML_FILENOTFOUND );
00421 }
00422 else
00423 {
00424 SendResponse( IDR_HTML_DISABLED );
00425 }
00426 theApp.Message( MSG_ERROR, IDS_UPLOAD_DISABLED, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
00427 Security.Ban( &m_pHost.sin_addr, ban2Hours, FALSE );
00428 Remove( FALSE );
00429 return FALSE;
00430 }
00431 else if ( StartsWith( m_sRequest, _T("/gnutella/metadata/v1?urn:") ) && Settings.Uploads.ShareMetadata )
00432 {
00433 LPCTSTR pszURN = (LPCTSTR)m_sRequest + 22;
00434 CXMLElement* pMetadata = NULL;
00435
00436 CSingleLock oLock( &Library.m_pSection, TRUE );
00437 if ( CLibraryFile* pShared = LibraryMaps.LookupFileByURN( pszURN, TRUE, TRUE ) )
00438 {
00439 if ( pShared->m_pMetadata != NULL )
00440 {
00441 m_sFileName = pShared->m_sName;
00442 pMetadata = pShared->m_pSchema->Instantiate( TRUE );
00443 pMetadata->AddElement( pShared->m_pMetadata->Clone() );
00444 }
00445 oLock.Unlock();
00446 }
00447 else
00448 {
00449 oLock.Unlock();
00450 if ( CDownload* pDownload = Downloads.FindByURN( pszURN ) )
00451 {
00452 if ( pDownload->m_pXML != NULL )
00453 {
00454 m_sFileName = pDownload->m_sRemoteName;
00455 pMetadata = pDownload->m_pXML->Clone();
00456 }
00457 }
00458 }
00459
00460 if ( pMetadata != NULL ) return RequestMetadata( pMetadata );
00461 }
00462 else if ( StartsWith( m_sRequest, _T("/gnutella/tigertree/v3?urn:") ) && Settings.Uploads.ShareTiger )
00463 {
00464 LPCTSTR pszURN = (LPCTSTR)m_sRequest + 23;
00465
00466 {
00467 CQuickLock oLock( Library.m_pSection );
00468 if ( CLibraryFile* pShared = LibraryMaps.LookupFileByURN( pszURN, TRUE, TRUE ) )
00469 {
00470 CTigerTree* pTigerTree = pShared->GetTigerTree();
00471 m_sFileName = pShared->m_sName;
00472 return RequestTigerTreeRaw( pTigerTree, TRUE );
00473 }
00474 }
00475 if ( CDownload* pDownload = Downloads.FindByURN( pszURN ) )
00476 {
00477 if ( pDownload->GetTigerTree() != NULL )
00478 {
00479 m_sFileName = pDownload->m_sRemoteName;
00480 return RequestTigerTreeRaw( pDownload->GetTigerTree(), FALSE );
00481 }
00482 }
00483 }
00484 else if ( StartsWith( m_sRequest, _T("/gnutella/thex/v1?urn:") ) && Settings.Uploads.ShareTiger )
00485 {
00486 LPCTSTR pszURN = (LPCTSTR)m_sRequest + 18;
00487 DWORD nDepth = 0;
00488
00489 if ( LPCTSTR pszDepth = _tcsistr( m_sRequest, _T("depth=") ) )
00490 {
00491 _stscanf( pszDepth + 6, _T("%i"), &nDepth );
00492 }
00493
00494 BOOL bHashset = ( _tcsistr( m_sRequest, _T("ed2k=1") ) != NULL );
00495
00496 {
00497 CQuickLock oLock( Library.m_pSection );
00498 if ( CLibraryFile* pShared = LibraryMaps.LookupFileByURN( pszURN, TRUE, TRUE ) )
00499 {
00500 CTigerTree* pTigerTree = pShared->GetTigerTree();
00501 CED2K* pHashset = bHashset ? pShared->GetED2K() : NULL;
00502 m_sFileName = pShared->m_sName;
00503 m_nFileSize = pShared->GetSize();
00504 return RequestTigerTreeDIME( pTigerTree, nDepth, pHashset, TRUE );
00505 }
00506 }
00507 if ( CDownload* pDownload = Downloads.FindByURN( pszURN ) )
00508 {
00509 if ( pDownload->GetTigerTree() != NULL )
00510 {
00511 m_sFileName = pDownload->m_sRemoteName;
00512 m_nFileSize = pDownload->m_nSize;
00513 return RequestTigerTreeDIME( pDownload->GetTigerTree(), nDepth,
00514 bHashset ? pDownload->GetHashset() : NULL, FALSE );
00515 }
00516 }
00517 }
00518 else if ( StartsWith( m_sRequest, _T("/gnutella/preview/v1?urn:") ) && Settings.Uploads.SharePreviews )
00519 {
00520 LPCTSTR pszURN = (LPCTSTR)m_sRequest + 21;
00521 CSingleLock oLock( &Library.m_pSection, TRUE );
00522 CLibraryFile* pShared = LibraryMaps.LookupFileByURN( pszURN, TRUE, TRUE );
00523 if ( pShared != NULL ) return RequestPreview( pShared, oLock );
00524 }
00525 else if ( StartsWith( m_sRequest, _T("/uri-res/N2R?urn:") ) )
00526 {
00527 LPCTSTR pszURN = (LPCTSTR)m_sRequest + 13;
00528
00529 {
00530 CSingleLock oLock( &Library.m_pSection, TRUE );
00531
00532 if ( CLibraryFile* pShared = LibraryMaps.LookupFileByURN( pszURN, TRUE, TRUE ) )
00533 {
00534 return RequestSharedFile( pShared, oLock );
00535 }
00536 }
00537
00538 CDownload* pDownload = Downloads.FindByURN( pszURN );
00539
00540 if ( pDownload != NULL && pDownload->IsShared() && pDownload->IsStarted() )
00541 {
00542 return RequestPartialFile( pDownload );
00543 }
00544 }
00545 else if ( StartsWith( m_sRequest, _T("/get/") ) )
00546 {
00547 DWORD nIndex = 0;
00548
00549 CString strFile = m_sRequest.Mid( 5 );
00550 int nChar = strFile.Find( '/' );
00551
00552 if ( _stscanf( strFile, _T("%lu/"), &nIndex ) == 1 && nChar > 0 && nChar < strFile.GetLength() - 1 )
00553 {
00554 strFile = strFile.Mid( nChar + 1 );
00555
00556 {
00557 CSingleLock oLock( &Library.m_pSection, TRUE );
00558
00559 CLibraryFile* pFile = Library.LookupFile( nIndex, TRUE, TRUE );
00560
00561 if ( pFile != NULL && pFile->m_sName.CompareNoCase( strFile ) )
00562 {
00563 pFile = NULL;
00564 }
00565
00566 if ( pFile == NULL )
00567 {
00568 pFile = LibraryMaps.LookupFileByName( strFile, TRUE, TRUE );
00569 }
00570
00571 if ( pFile != NULL ) return RequestSharedFile( pFile, oLock );
00572 }
00573 }
00574 else
00575 {
00576 strFile = strFile.Mid( nChar + 1 );
00577 CSingleLock oLock( &Library.m_pSection, TRUE );
00578 CLibraryFile* pFile = LibraryMaps.LookupFileByName( strFile, TRUE, TRUE );
00579 if ( pFile != NULL ) return RequestSharedFile( pFile, oLock );
00580 }
00581 }
00582 else
00583 {
00584 CString strFile = m_sRequest.Mid( 1 );
00585 CSingleLock oLock( &Library.m_pSection, TRUE );
00586 CLibraryFile* pFile = LibraryMaps.LookupFileByName( strFile, TRUE, TRUE );
00587 if ( pFile != NULL ) return RequestSharedFile( pFile, oLock );
00588 }
00589
00590 if ( m_sFileName.IsEmpty() )
00591 {
00592 if ( m_bSHA1 ) m_sFileName = CSHA::HashToString( &m_pSHA1, TRUE );
00593 else m_sFileName = m_sRequest;
00594 }
00595
00596 SendResponse( IDR_HTML_FILENOTFOUND );
00597 theApp.Message( MSG_ERROR, IDS_UPLOAD_FILENOTFOUND, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00598
00599 return TRUE;
00600 }
00601
00602 BOOL CUploadTransferHTTP::IsNetworkDisabled()
00603 {
00604 if ( Settings.Connection.RequireForTransfers == FALSE ) return FALSE;
00605
00606 if ( m_nGnutella == 2 )
00607 {
00608 if ( ! Settings.Gnutella2.EnableToday ) return TRUE;
00609 }
00610 else if ( m_nGnutella == 1 )
00611 {
00612 if ( ! Settings.Gnutella1.EnableToday ) return TRUE;
00613 }
00614 else
00615 {
00616 if ( ! Settings.Gnutella1.EnableToday &&
00617 ! Settings.Gnutella2.EnableToday ) return TRUE;
00618 }
00619
00620 return FALSE;
00621 }
00622
00624
00625
00626 BOOL CUploadTransferHTTP::RequestSharedFile(CLibraryFile* pFile, CSingleLock& oLibraryLock)
00627 {
00628 ASSERT( pFile != NULL );
00629
00630 if ( ! RequestComplete( pFile ) )
00631 {
00632 oLibraryLock.Unlock();
00633 SendResponse( IDR_HTML_HASHMISMATCH );
00634 theApp.Message( MSG_ERROR, IDS_UPLOAD_HASH_MISMATCH, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00635 return TRUE;
00636 }
00637
00638 if ( ! UploadQueues.CanUpload( PROTOCOL_HTTP, pFile ) )
00639 {
00640
00641 if ( m_sFileName.IsEmpty() )
00642 {
00643 if ( m_bSHA1 ) m_sFileName = CSHA::HashToString( &m_pSHA1, TRUE );
00644 }
00645
00646 oLibraryLock.Unlock();
00647 SendResponse( IDR_HTML_FILENOTFOUND );
00648 theApp.Message( MSG_ERROR, IDS_UPLOAD_FILENOTFOUND, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00649 return TRUE;
00650 }
00651
00652 m_bTigerTree = m_bTiger;
00653 m_bMetadata = ( pFile->m_pMetadata != NULL && ( pFile->m_bMetadataAuto == FALSE || pFile->m_nVirtualSize > 0 ) );
00654
00655 if ( ! m_bSHA1 && ! m_bTiger && ! m_bED2K ) m_sLocations.Empty();
00656
00657 if ( m_nLength == SIZE_UNKNOWN ) m_nLength = m_nFileSize - m_nOffset;
00658
00659 if ( m_nOffset >= m_nFileSize || m_nOffset + m_nLength > m_nFileSize )
00660 {
00661 oLibraryLock.Unlock();
00662 SendResponse( IDR_HTML_BADRANGE );
00663 theApp.Message( MSG_ERROR, IDS_UPLOAD_BAD_RANGE, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00664 return TRUE;
00665 }
00666
00667 CString strLocations;
00668 if ( Settings.Library.SourceMesh ) strLocations = pFile->GetAlternateSources( &m_pSourcesSent, 15, PROTOCOL_HTTP );
00669 if ( m_sLocations.GetLength() ) pFile->AddAlternateSources( m_sLocations );
00670 m_sLocations = strLocations;
00671
00672 oLibraryLock.Unlock();
00673
00674 return QueueRequest();
00675 }
00676
00678
00679
00680 BOOL CUploadTransferHTTP::RequestPartialFile(CDownload* pDownload)
00681 {
00682 ASSERT( pDownload != NULL );
00683 ASSERT( pDownload->IsStarted() );
00684
00685 if ( ! RequestPartial( pDownload ) )
00686 {
00687 SendResponse( IDR_HTML_HASHMISMATCH );
00688 theApp.Message( MSG_ERROR, IDS_UPLOAD_HASH_MISMATCH, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00689 return TRUE;
00690 }
00691
00692 ASSERT( m_nFileBase == 0 );
00693
00694 m_bTigerTree = ( m_bTiger && pDownload->GetTigerTree() != NULL );
00695 m_bMetadata = ( pDownload->m_pXML != NULL );
00696
00697 if ( m_sLocations.GetLength() ) pDownload->AddSourceURLs( m_sLocations, TRUE );
00698
00699 if ( Settings.Library.SourceMesh )
00700 {
00701 if ( m_nGnutella == 1 )
00702 m_sLocations = pDownload->GetSourceURLs( &m_pSourcesSent, 15, PROTOCOL_G1, NULL );
00703 else
00704 m_sLocations = pDownload->GetSourceURLs( &m_pSourcesSent, 15, PROTOCOL_HTTP, NULL );
00705 }
00706
00707 m_sRanges = pDownload->GetAvailableRanges();
00708
00709 if ( m_bRange && m_nOffset == 0 && m_nLength == SIZE_UNKNOWN )
00710 {
00711 pDownload->GetRandomRange( m_nOffset, m_nLength );
00712 }
00713
00714 if ( m_nLength == SIZE_UNKNOWN ) m_nLength = m_nFileSize - m_nOffset;
00715
00716 if ( pDownload->ClipUploadRange( m_nOffset, m_nLength ) )
00717 {
00718 return QueueRequest();
00719 }
00720
00721 if ( pDownload->IsMoving() )
00722 {
00723 if ( GetTickCount() - pDownload->m_tCompleted < 30000 )
00724 {
00725 m_pOutput->Print( "HTTP/1.1 503 Range Temporarily Unavailable\r\n" );
00726 }
00727 else
00728 {
00729 SendResponse( IDR_HTML_FILENOTFOUND );
00730 theApp.Message( MSG_ERROR, IDS_UPLOAD_FILENOTFOUND, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00731 return TRUE;
00732 }
00733 }
00734 else if ( pDownload->GetTransferCount() )
00735 {
00736 m_pOutput->Print( "HTTP/1.1 503 Range Temporarily Unavailable\r\n" );
00737 }
00738 else
00739 {
00740 m_pOutput->Print( "HTTP/1.1 416 Requested Range Unavailable\r\n" );
00741 }
00742
00743 SendDefaultHeaders();
00744 SendFileHeaders();
00745
00746 m_pOutput->Print( "Content-Length: 0\r\n" );
00747 m_pOutput->Print( "\r\n" );
00748
00749 StartSending( upsResponse );
00750
00751 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_BAD_RANGE, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
00752
00753 return TRUE;
00754 }
00755
00757
00758
00759 BOOL CUploadTransferHTTP::QueueRequest()
00760 {
00761 SHA1* pSHA1 = NULL;
00762
00763 if ( m_bHead ) return OpenFileSendHeaders();
00764
00765 AllocateBaseFile();
00766
00767 UINT nError = 0;
00768 int nPosition = 0;
00769
00770 if ( m_bStopTransfer )
00771 {
00772
00773 m_tRotateTime = 0;
00774 m_bStopTransfer = FALSE;
00775
00776 CUploadQueue* pQueue = m_pQueue;
00777 if ( pQueue ) pQueue->Dequeue( this );
00778 }
00779
00780 if ( m_bSHA1 )
00781 {
00782 pSHA1 = &m_pSHA1;
00783 }
00784
00785
00786 if ( Uploads.CanUploadFileTo( &m_pHost.sin_addr, pSHA1 ) )
00787 {
00788 if ( ( nPosition = UploadQueues.GetPosition( this, TRUE ) ) >= 0 )
00789 {
00790 ASSERT( m_pQueue != NULL );
00791
00792
00793 if( ! m_pQueue->CanAccept( m_nProtocol, m_sFileName, m_nFileSize, m_bFilePartial, m_sFileTags ) )
00794 {
00795 theApp.Message( MSG_DEBUG, _T("File queue error- Partial may have recently completed") );
00796
00797
00798
00799 }
00800
00801
00802 if ( nPosition == 0 )
00803 {
00804
00805 return OpenFileSendHeaders();
00806 }
00807 else
00808 {
00809
00810 }
00811 }
00812 else if ( UploadQueues.Enqueue( this ) )
00813 {
00814 ASSERT( m_pQueue != NULL );
00815 ASSERT( m_pQueue->CanAccept( m_nProtocol, m_sFileName, m_nFileSize, m_bFilePartial, m_sFileTags ) );
00816
00817 nPosition = UploadQueues.GetPosition( this, TRUE );
00818 ASSERT( nPosition >= 0 );
00819
00820 if ( nPosition == 0 )
00821 {
00822
00823 return OpenFileSendHeaders();
00824 }
00825 else if ( m_bQueueMe )
00826 {
00827
00828 }
00829 else
00830 {
00831
00832 UploadQueues.Dequeue( this );
00833 ASSERT( m_pQueue == NULL );
00834 }
00835 }
00836 else
00837 {
00838
00839 }
00840 }
00841 else
00842 {
00843
00844
00845 UploadQueues.Dequeue( this );
00846 ASSERT( m_pQueue == NULL );
00847
00848 nError = IDS_UPLOAD_BUSY_HOST;
00849 }
00850
00851 if ( m_pQueue != NULL )
00852 {
00853 CString strHeader, strName;
00854
00855 m_pOutput->Print( "HTTP/1.1 503 Busy Queued\r\n" );
00856
00857 SendDefaultHeaders();
00858 SendFileHeaders();
00859
00860 m_nReaskMultiplier=( nPosition <= 9 ) ? ( (nPosition+1) / 2 ) : 5;
00861 DWORD nTimeScale = 1000 / m_nReaskMultiplier;
00862
00863 CSingleLock pLock( &UploadQueues.m_pSection, TRUE );
00864
00865 if ( UploadQueues.Check( m_pQueue ) )
00866 {
00867 strName = m_pQueue->m_sName;
00868 Replace( strName, _T("\""), _T("'") );
00869
00870 strHeader.Format( _T("X-Queue: position=%i,length=%i,limit=%i,pollMin=%lu,pollMax=%lu,id=\"%s\"\r\n"),
00871 nPosition,
00872 m_pQueue->GetQueuedCount(),
00873 m_pQueue->GetTransferCount( TRUE ),
00874 Settings.Uploads.QueuePollMin / nTimeScale,
00875 Settings.Uploads.QueuePollMax / nTimeScale,
00876 (LPCTSTR)strName );
00877
00878 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_QUEUED, (LPCTSTR)m_sFileName,
00879 (LPCTSTR)m_sAddress, nPosition, m_pQueue->GetQueuedCount(),
00880 (LPCTSTR)strName );
00881
00882 }
00883
00884 pLock.Unlock();
00885
00886 m_pOutput->Print( strHeader );
00887 m_pOutput->Print( "Content-Length: 0\r\n" );
00888 m_pOutput->Print( "\r\n" );
00889
00890 StartSending( upsPreQueue );
00891 }
00892 else
00893 {
00894 SendResponse( IDR_HTML_BUSY, TRUE );
00895
00896 if ( ! nError ) nError = m_bQueueMe ? IDS_UPLOAD_BUSY_QUEUE : IDS_UPLOAD_BUSY_OLD;
00897 theApp.Message( MSG_ERROR, nError, (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
00898 }
00899
00900 return TRUE;
00901 }
00902
00904
00905
00906 void CUploadTransferHTTP::SendDefaultHeaders()
00907 {
00908 CString strLine = Settings.SmartAgent();
00909
00910 if ( strLine.GetLength() )
00911 {
00912 strLine = _T("Server: ") + strLine + _T("\r\n");
00913 m_pOutput->Print( strLine );
00914 }
00915
00916 if ( ! m_bInitiated )
00917 {
00918 strLine.Format( _T("Remote-IP: %s\r\n"),
00919 (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) );
00920 m_pOutput->Print( strLine );
00921 }
00922
00923 if ( m_bKeepAlive )
00924 {
00925 m_pOutput->Print( "Connection: Keep-Alive\r\n" );
00926 }
00927 else
00928 {
00929 m_pOutput->Print( "Connection: Close\r\n" );
00930 }
00931
00932 m_pOutput->Print( "Accept-Ranges: bytes\r\n" );
00933
00934 if ( m_nRequests <= 1 )
00935 {
00936 if ( m_bInitiated ) SendMyAddress();
00937 strLine.Format( _T("X-PerHost: %lu\r\n"), Settings.Uploads.MaxPerHost );
00938 m_pOutput->Print( strLine );
00939
00940 strLine = MyProfile.GetNick().Left( 255 );
00941
00942 if ( strLine.GetLength() > 0 )
00943 {
00944 strLine = _T("X-Nick: ") + URLEncode( strLine ) + _T("\r\n");
00945 m_pOutput->Print( strLine );
00946 }
00947 }
00948 }
00949
00951
00952
00953 void CUploadTransferHTTP::SendFileHeaders()
00954 {
00955 CString strHeader;
00956
00957 if ( m_bSHA1 )
00958 {
00959 if ( m_bTiger )
00960 {
00961 strHeader = _T("X-Content-URN: urn:bitprint:")
00962 + CSHA::HashToString( &m_pSHA1, FALSE ) + '.'
00963 + CTigerNode::HashToString( &m_pTiger, FALSE ) + _T("\r\n");
00964 }
00965 else
00966 {
00967 strHeader = _T("X-Content-URN: ") + CSHA::HashToString( &m_pSHA1, TRUE ) + _T("\r\n");
00968 }
00969
00970 m_pOutput->Print( strHeader );
00971 }
00972 else if ( m_bTiger )
00973 {
00974 strHeader = _T("X-Content-URN: ") + CTigerNode::HashToString( &m_pTiger, TRUE ) + _T("\r\n");
00975 m_pOutput->Print( strHeader );
00976 }
00977
00978 if ( m_bED2K )
00979 {
00980 strHeader = _T("X-Content-URN: ") + CED2K::HashToString( &m_pED2K, TRUE ) + _T("\r\n");
00981 m_pOutput->Print( strHeader );
00982 }
00983
00984 if ( m_bTigerTree && Settings.Uploads.ShareTiger )
00985 {
00986 strHeader = _T("X-Thex-URI: /gnutella/thex/v1?")
00987 + CTigerNode::HashToString( &m_pTiger, TRUE )
00988 + _T("&depth=9&ed2k=0;")
00989 + CTigerNode::HashToString( &m_pTiger, FALSE )
00990 + _T("\r\n");
00991 m_pOutput->Print( strHeader );
00992 }
00993
00994 if ( m_bMetadata )
00995 {
00996 strHeader = _T("X-Metadata-Path: /gnutella/metadata/v1?")
00997 + CTigerNode::HashToString( &m_pTiger, TRUE )
00998 + _T("\r\n");
00999 m_pOutput->Print( strHeader );
01000 }
01001
01002 if ( m_sRanges.GetLength() )
01003 {
01004 strHeader = _T("X-Available-Ranges: ") + m_sRanges + _T("\r\n");
01005 m_pOutput->Print( strHeader );
01006 }
01007
01008 if ( m_sLocations.GetLength() )
01009 {
01010 strHeader = _T("Alt-Location: ") + m_sLocations + _T("\r\n");
01011 m_pOutput->Print( strHeader );
01012 }
01013 }
01014
01016
01017
01018 BOOL CUploadTransferHTTP::OpenFileSendHeaders()
01019 {
01020 ASSERT( m_pDiskFile == NULL );
01021
01022 m_pDiskFile = TransferFiles.Open( m_sFilePath, FALSE, FALSE );
01023
01024
01025 if ( m_pDiskFile == NULL )
01026 {
01027 SendResponse( IDR_HTML_FILENOTFOUND );
01028 theApp.Message( MSG_ERROR, IDS_UPLOAD_CANTOPEN, (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01029 return TRUE;
01030 }
01031
01032 CSingleLock pLock( &UploadQueues.m_pSection, TRUE );
01033
01034 if ( m_pQueue != NULL && UploadQueues.Check( m_pQueue ) && m_pQueue->m_bRotate )
01035 {
01036 DWORD nLimit = m_pQueue->m_nRotateChunk;
01037 if ( nLimit == 0 ) nLimit = Settings.Uploads.RotateChunkLimit;
01038 if ( nLimit > 0 ) m_nLength = min( m_nLength, QWORD(nLimit) );
01039 }
01040
01041 pLock.Unlock();
01042
01043 if ( m_nLength != m_nFileSize )
01044 m_pOutput->Print( "HTTP/1.1 206 OK\r\n" );
01045 else
01046 m_pOutput->Print( "HTTP/1.1 200 OK\r\n" );
01047
01048 SendDefaultHeaders();
01049
01050 CString strExt, strResponse;
01051
01052 int nType = m_sFileName.ReverseFind( '.' );
01053 if ( nType > 0 ) strExt = m_sFileName.Mid( nType );
01054 ShellIcons.Lookup( strExt, NULL, NULL, NULL, &strResponse );
01055
01056 if ( strResponse.IsEmpty() )
01057 {
01058 m_pOutput->Print( "Content-Type: application/x-binary\r\n" );
01059 }
01060 else
01061 {
01062 strResponse = _T("Content-Type: ") + strResponse + _T("\r\n");
01063 m_pOutput->Print( strResponse );
01064 }
01065
01066 strResponse.Format( _T("Content-Length: %I64i\r\n"), m_nLength );
01067 m_pOutput->Print( strResponse );
01068
01069 if ( m_nLength != m_nFileSize )
01070 {
01071 strResponse.Format( _T("Content-Range: bytes=%I64i-%I64i/%I64i\r\n"), m_nOffset, m_nOffset + m_nLength - 1, m_nFileSize );
01072 m_pOutput->Print( strResponse );
01073 }
01074
01075 if ( ! m_bHead && m_bBackwards )
01076 {
01077 m_pOutput->Print( "Content-Encoding: backwards\r\n" );
01078 }
01079
01080 if ( m_bSHA1 || m_bTiger || m_bED2K ) SendFileHeaders();
01081
01082 m_pOutput->Print( "\r\n" );
01083
01084 if ( m_bHead )
01085 {
01086 m_pDiskFile->Release( FALSE );
01087 m_pDiskFile = NULL;
01088
01089 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_HEADERS, (LPCTSTR)m_sFileName,
01090 (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
01091
01092 StartSending( upsResponse );
01093 }
01094 else
01095 {
01096 if ( m_pBaseFile->m_nRequests++ == 0 )
01097 {
01098 theApp.Message( MSG_SYSTEM, IDS_UPLOAD_FILE,
01099 (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01100
01101 CQuickLock oLock( Library.m_pSection );
01102 if ( CLibraryFile* pFile = LibraryMaps.LookupFileByPath( m_sFilePath, TRUE, TRUE ) )
01103 {
01104 pFile->m_nUploadsToday++;
01105 pFile->m_nUploadsTotal++;
01106 }
01107 }
01108
01109 theApp.Message( MSG_DEFAULT,
01110 m_sRanges.GetLength() ? IDS_UPLOAD_PARTIAL_CONTENT : IDS_UPLOAD_CONTENT,
01111 m_nOffset, m_nOffset + m_nLength - 1, (LPCTSTR)m_sFileName,
01112 (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
01113
01114 StartSending( upsUploading );
01115 }
01116
01117 OnWrite();
01118
01119 return TRUE;
01120 }
01121
01123
01124
01125 BOOL CUploadTransferHTTP::OnWrite()
01126 {
01127 if ( m_nState == upsUploading && m_pDiskFile != NULL && m_pOutput->m_nLength == 0 )
01128 {
01129 if ( m_nPosition >= m_nLength )
01130 {
01131 OnCompleted();
01132 CUploadTransfer::OnWrite();
01133 return TRUE;
01134 }
01135
01136 QWORD nPacket = min( m_nLength - m_nPosition, (QWORD)Transfers.m_nBuffer );
01137 BYTE* pBuffer = Transfers.m_pBuffer;
01138
01139 if ( m_bBackwards )
01140 {
01141 QWORD nRead = 0;
01142 m_pDiskFile->Read( m_nFileBase + m_nOffset + m_nLength - m_nPosition - nPacket, pBuffer, nPacket, &nRead );
01143 if ( nRead != nPacket ) return TRUE;
01144 m_pOutput->AddReversed( pBuffer, (DWORD)nPacket );
01145 }
01146 else
01147 {
01148 m_pDiskFile->Read( m_nFileBase + m_nOffset + m_nPosition, pBuffer, nPacket, &nPacket );
01149 if ( nPacket == 0 ) return TRUE;
01150 m_pOutput->Add( pBuffer, (DWORD)nPacket );
01151 }
01152
01153 m_nPosition += nPacket;
01154 m_nUploaded += nPacket;
01155
01156 Statistics.Current.Uploads.Volume += ( nPacket / 1024 );
01157 }
01158
01159 CUploadTransfer::OnWrite();
01160
01161 if ( m_nState >= upsResponse && m_pOutput->m_nLength == 0 )
01162 {
01163 m_nState = ( m_nState == upsPreQueue ) ? upsQueued : upsRequest;
01164 m_tRequest = GetTickCount();
01165 }
01166
01167 return TRUE;
01168 }
01169
01170 void CUploadTransferHTTP::OnCompleted()
01171 {
01172 Uploads.SetStable( GetAverageSpeed() );
01173
01174 m_pDiskFile->Release( FALSE );
01175 m_pDiskFile = NULL;
01176 m_nState = upsRequest;
01177 m_tRequest = GetTickCount();
01178
01179 m_pBaseFile->AddFragment( m_nOffset, m_nLength );
01180
01181
01182 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_FINISHED, (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01183 }
01184
01186
01187
01188 BOOL CUploadTransferHTTP::OnRun()
01189 {
01190 CUploadTransfer::OnRun();
01191
01192 DWORD tNow = GetTickCount();
01193
01194 switch ( m_nState )
01195 {
01196 case upsRequest:
01197 if ( ! m_bKeepAlive && m_pOutput->m_nLength == 0 )
01198 {
01199 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_DROPPED, (LPCTSTR)m_sAddress );
01200 Close();
01201 return FALSE;
01202 }
01203
01204 case upsHeaders:
01205 if ( tNow - m_tRequest > Settings.Connection.TimeoutHandshake )
01206 {
01207 theApp.Message( MSG_ERROR, IDS_UPLOAD_REQUEST_TIMEOUT, (LPCTSTR)m_sAddress );
01208 Close();
01209 return FALSE;
01210 }
01211 break;
01212
01213 case upsQueued:
01214 if ( tNow - m_tRequest > ( Settings.Uploads.QueuePollMax * m_nReaskMultiplier ) )
01215 {
01216 theApp.Message( MSG_ERROR, IDS_UPLOAD_REQUEST_TIMEOUT, (LPCTSTR)m_sAddress );
01217 Close();
01218 return FALSE;
01219 }
01220 break;
01221
01222 case upsUploading:
01223 case upsResponse:
01224 case upsBrowse:
01225 case upsTigerTree:
01226 case upsMetadata:
01227 case upsPreview:
01228 case upsPreQueue:
01229 if ( tNow - m_mOutput.tLast > Settings.Connection.TimeoutTraffic )
01230 {
01231 theApp.Message( MSG_ERROR, IDS_UPLOAD_TRAFFIC_TIMEOUT, (LPCTSTR)m_sAddress );
01232 Close();
01233 return FALSE;
01234 }
01235 break;
01236
01237 }
01238
01239 return TRUE;
01240 }
01241
01243
01244
01245 void CUploadTransferHTTP::OnDropped(BOOL bError)
01246 {
01247 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_DROPPED, (LPCTSTR)m_sAddress );
01248
01249 if ( m_nState == upsUploading && m_pBaseFile != NULL )
01250 {
01251 if ( m_bBackwards )
01252 {
01253 m_pBaseFile->AddFragment( m_nOffset + m_nLength - m_nPosition, m_nPosition );
01254 }
01255 else
01256 {
01257 m_pBaseFile->AddFragment( m_nOffset, m_nPosition );
01258 }
01259
01260 m_pBaseFile = NULL;
01261 }
01262
01263 Close();
01264 }
01265
01267
01268
01269 BOOL CUploadTransferHTTP::RequestMetadata(CXMLElement* pMetadata)
01270 {
01271 ASSERT( pMetadata != NULL );
01272 CString strXML = pMetadata->ToString( TRUE, TRUE );
01273 delete pMetadata;
01274
01275 int nXML = WideCharToMultiByte( CP_UTF8, 0, strXML, strXML.GetLength(), NULL, 0, NULL, NULL );
01276 LPSTR pszXML = new CHAR[ nXML ];
01277 WideCharToMultiByte( CP_UTF8, 0, strXML, strXML.GetLength(), pszXML, nXML, NULL, NULL );
01278
01279 m_pOutput->Print( "HTTP/1.1 200 OK\r\n" );
01280 SendDefaultHeaders();
01281 m_pOutput->Print( "Content-Type: text/xml\r\n" );
01282
01283 CString strHeader;
01284 strHeader.Format( _T("Content-Length: %lu\r\n"), nXML );
01285 m_pOutput->Print( strHeader );
01286 m_pOutput->Print( "\r\n" );
01287
01288 if ( ! m_bHead ) m_pOutput->Add( pszXML, nXML );
01289 delete [] pszXML;
01290
01291 StartSending( upsMetadata );
01292
01293 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_METADATA_SEND,
01294 (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01295
01296 return TRUE;
01297 }
01298
01300
01301
01302 BOOL CUploadTransferHTTP::RequestTigerTreeRaw(CTigerTree* pTigerTree, BOOL bDelete)
01303 {
01304 if ( pTigerTree == NULL )
01305 {
01306 ClearHashes();
01307 m_sLocations.Empty();
01308
01309 SendResponse( IDR_HTML_FILENOTFOUND, TRUE );
01310 theApp.Message( MSG_ERROR, IDS_UPLOAD_FILENOTFOUND, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
01311
01312 return TRUE;
01313 }
01314
01315 BYTE* pSerialTree;
01316 DWORD nSerialTree;
01317
01318 pTigerTree->ToBytes( &pSerialTree, &nSerialTree );
01319 if ( bDelete ) delete pTigerTree;
01320
01321 if ( m_bRange )
01322 {
01323 if ( m_nOffset >= nSerialTree ) m_nLength = SIZE_UNKNOWN;
01324 else m_nLength = min( m_nLength, nSerialTree - m_nOffset );
01325 }
01326 else
01327 {
01328 m_nOffset = 0;
01329 m_nLength = nSerialTree;
01330 }
01331
01332 if ( m_nLength <= nSerialTree )
01333 {
01334 CString strHeader;
01335
01336 if ( m_nLength != nSerialTree )
01337 m_pOutput->Print( "HTTP/1.1 206 OK\r\n" );
01338 else
01339 m_pOutput->Print( "HTTP/1.1 200 OK\r\n" );
01340
01341 SendDefaultHeaders();
01342
01343 m_pOutput->Print( "Content-Type: application/tigertree-breadthfirst\r\n" );
01344 strHeader.Format( _T("Content-Length: %I64i\r\n"), m_nLength );
01345 m_pOutput->Print( strHeader );
01346
01347 if ( m_nLength != nSerialTree )
01348 {
01349 strHeader.Format( _T("Content-Range: %I64i-%I64i\r\n"), m_nOffset, m_nOffset + m_nLength - 1 );
01350 m_pOutput->Print( strHeader );
01351 }
01352
01353 m_pOutput->Print( "\r\n" );
01354
01355 if ( ! m_bHead ) m_pOutput->Add( pSerialTree + m_nOffset, (DWORD)m_nLength );
01356
01357 StartSending( upsTigerTree );
01358
01359 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_TIGER_SEND,
01360 (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01361 }
01362 else
01363 {
01364 m_sRanges.Format( _T("0-%I64i"), (QWORD)nSerialTree - 1 );
01365 ClearHashes();
01366 m_sLocations.Empty();
01367
01368 SendResponse( IDR_HTML_BADRANGE, TRUE );
01369 theApp.Message( MSG_ERROR, IDS_UPLOAD_BAD_RANGE, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
01370 }
01371
01372 delete [] pSerialTree;
01373
01374 return TRUE;
01375 }
01376
01378
01379
01380 BOOL CUploadTransferHTTP::RequestTigerTreeDIME(CTigerTree* pTigerTree, int nDepth, CED2K* pHashset, BOOL bDelete)
01381 {
01382 if ( pTigerTree == NULL )
01383 {
01384 ClearHashes();
01385 m_sLocations.Empty();
01386
01387 SendResponse( IDR_HTML_FILENOTFOUND, TRUE );
01388 theApp.Message( MSG_ERROR, IDS_UPLOAD_FILENOTFOUND, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
01389
01390 if ( pHashset != NULL && bDelete ) delete pHashset;
01391
01392 return TRUE;
01393 }
01394
01395 DWORD nSerialTree;
01396 BYTE* pSerialTree;
01397 CBuffer pDIME;
01398
01399 if ( nDepth < 1 ) nDepth = pTigerTree->GetHeight();
01400 else if ( nDepth > (int)pTigerTree->GetHeight() ) nDepth = pTigerTree->GetHeight();
01401
01402 pTigerTree->ToBytes( &pSerialTree, &nSerialTree, nDepth );
01403 if ( bDelete ) delete pTigerTree;
01404
01405 CString strUUID, strXML;
01406 GUID pUUID;
01407
01408 Network.CreateID( *(GGUID*)&pUUID );
01409 strUUID.Format( _T("uuid:%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x"),
01410 pUUID.Data1, pUUID.Data2, pUUID.Data3,
01411 pUUID.Data4[0], pUUID.Data4[1], pUUID.Data4[2], pUUID.Data4[3],
01412 pUUID.Data4[4], pUUID.Data4[5], pUUID.Data4[6], pUUID.Data4[7] );
01413
01414 strXML.Format( _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n")
01415 _T("<!DOCTYPE hashtree SYSTEM \"http://open-content.net/spec/thex/thex.dtd\">\r\n")
01416 _T("<hashtree>\r\n")
01417 _T("\t<file size=\"%I64i\" segmentsize=\"1024\"/>\r\n")
01418 _T("\t<digest algorithm=\"http://open-content.net/spec/digest/tiger\" outputsize=\"24\"/>\r\n")
01419 _T("\t<serializedtree depth=\"%i\" type=\"http://open-content.net/spec/thex/breadthfirst\" uri=\"%s\"/>\r\n")
01420 _T("</hashtree>"),
01421 m_nFileSize, nDepth, (LPCTSTR)strUUID );
01422
01423 int nXML = WideCharToMultiByte( CP_UTF8, 0, strXML, -1, NULL, 0, NULL, NULL );
01424 LPSTR pszXML = new CHAR[ nXML ];
01425 WideCharToMultiByte( CP_UTF8, 0, strXML, -1, pszXML, nXML, NULL, NULL );
01426 int nUUID = WideCharToMultiByte( CP_ACP, 0, strUUID, -1, NULL, 0, NULL, NULL );
01427 LPSTR pszUUID = new CHAR[ nUUID ];
01428 WideCharToMultiByte( CP_ACP, 0, strUUID, -1, pszUUID, nUUID, NULL, NULL );
01429
01430 pDIME.WriteDIME( 1, "", "text/xml", pszXML, strlen(pszXML) );
01431 pDIME.WriteDIME( pHashset ? 0 : 2, pszUUID, "http://open-content.net/spec/thex/breadthfirst", pSerialTree, nSerialTree );
01432 delete [] pSerialTree;
01433
01434 delete [] pszUUID;
01435 delete [] pszXML;
01436
01437 if ( pHashset != NULL )
01438 {
01439 pHashset->ToBytes( &pSerialTree, &nSerialTree );
01440 if ( bDelete ) delete pHashset;
01441
01442 pDIME.WriteDIME( 2, "", "http://edonkey2000.com/spec/md4-hashset", pSerialTree, nSerialTree );
01443 delete [] pSerialTree;
01444 }
01445
01446 if ( m_bRange )
01447 {
01448 if ( m_nOffset >= (QWORD)pDIME.m_nLength ) m_nLength = SIZE_UNKNOWN;
01449 else m_nLength = min( m_nLength, (QWORD)pDIME.m_nLength - m_nOffset );
01450 }
01451 else
01452 {
01453 m_nOffset = 0;
01454 m_nLength = (QWORD)pDIME.m_nLength;
01455 }
01456
01457 if ( m_nLength <= pDIME.m_nLength )
01458 {
01459 CString strHeader;
01460
01461 if ( m_nLength != pDIME.m_nLength )
01462 m_pOutput->Print( "HTTP/1.1 206 OK\r\n" );
01463 else
01464 m_pOutput->Print( "HTTP/1.1 200 OK\r\n" );
01465
01466 SendDefaultHeaders();
01467
01468 m_pOutput->Print( "Content-Type: application/dime\r\n" );
01469 strHeader.Format( _T("Content-Length: %I64i\r\n"), m_nLength );
01470 m_pOutput->Print( strHeader );
01471
01472 if ( m_nLength != pDIME.m_nLength )
01473 {
01474 strHeader.Format( _T("Content-Range: %I64i-%I64i\r\n"), m_nOffset, m_nOffset + m_nLength - 1 );
01475 m_pOutput->Print( strHeader );
01476 }
01477
01478 m_pOutput->Print( "\r\n" );
01479
01480 if ( ! m_bHead )
01481 {
01482 m_pOutput->Add( pDIME.m_pBuffer + m_nOffset, (DWORD)m_nLength );
01483 }
01484
01485 StartSending( upsTigerTree );
01486
01487 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_TIGER_SEND,
01488 (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01489 }
01490 else
01491 {
01492 m_sRanges.Format( _T("0-%I64i"), (QWORD)pDIME.m_nLength - 1 );
01493 ClearHashes();
01494 m_sLocations.Empty();
01495
01496 SendResponse( IDR_HTML_BADRANGE, TRUE );
01497 theApp.Message( MSG_ERROR, IDS_UPLOAD_BAD_RANGE, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
01498 }
01499
01500 return TRUE;
01501 }
01502
01504
01505
01506 BOOL CUploadTransferHTTP::RequestPreview(CLibraryFile* pFile, CSingleLock& oLibraryLock)
01507 {
01508 ASSERT( pFile != NULL );
01509
01510 m_sFileName = pFile->m_sName;
01511 m_sFilePath = pFile->GetPath();
01512 m_bSHA1 = pFile->m_bSHA1;
01513 m_pSHA1 = pFile->m_pSHA1;
01514 m_bTiger = pFile->m_bTiger;
01515 m_pTiger = pFile->m_pTiger;
01516 m_bED2K = pFile->m_bED2K;
01517 m_pED2K = pFile->m_pED2K;
01518 DWORD nIndex = pFile->m_nIndex;
01519 BOOL bCached = pFile->m_bCachedPreview;
01520
01521 oLibraryLock.Unlock();
01522
01523 int nExisting = Uploads.GetCount( this, upsPreview );
01524
01525 if ( nExisting >= (int)Settings.Uploads.PreviewTransfers )
01526 {
01527 theApp.Message( MSG_ERROR, IDS_UPLOAD_PREVIEW_BUSY, (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01528 m_pOutput->Print( "HTTP/1.1 503 Busy\r\n" );
01529 SendDefaultHeaders();
01530 StartSending( upsResponse );
01531 return TRUE;
01532 }
01533
01534 CImageServices pServices;
01535 CImageFile pImage( &pServices );
01536 CThumbCache pCache;
01537 CSize szThumb( 0, 0 );
01538
01539 if ( pCache.Load( m_sFilePath, &szThumb, nIndex, &pImage ) )
01540 {
01541
01542 }
01543 else if ( Settings.Uploads.DynamicPreviews && pImage.LoadFromFile( m_sFilePath, FALSE, TRUE ) && pImage.EnsureRGB() )
01544 {
01545 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_PREVIEW_DYNAMIC, (LPCTSTR)m_sFileName, (LPCTSTR)m_sAddress );
01546
01547 int nSize = szThumb.cy * pImage.m_nWidth / pImage.m_nHeight;
01548
01549 if ( nSize > szThumb.cx )
01550 {
01551 nSize = szThumb.cx * pImage.m_nHeight / pImage.m_nWidth;
01552 pImage.Resample( szThumb.cx, nSize );
01553 }
01554 else
01555 {
01556 pImage.Resample( nSize, szThumb.cy );
01557 }
01558
01559 pCache.Store( m_sFilePath, &szThumb, nIndex, &pImage );
01560 }
01561 else
01562 {
01563 theApp.Message( MSG_ERROR, IDS_UPLOAD_PREVIEW_EMPTY, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
01564 SendResponse( IDR_HTML_FILENOTFOUND );
01565 return TRUE;
01566 }
01567
01568 if ( ! bCached )
01569 {
01570 CQuickLock oLock( Library.m_pSection );
01571 if ( pFile = Library.LookupFile( nIndex ) )
01572 {
01573 pFile->m_bCachedPreview = TRUE;
01574 Library.Update();
01575 }
01576 }
01577
01578 BYTE* pBuffer = NULL;
01579 DWORD nLength = 0;
01580
01581 int nQuality = Settings.Uploads.PreviewQuality;
01582
01583 if ( LPCTSTR pszQuality = _tcsistr( m_sRequest, _T("&quality=") ) )
01584 {
01585 _stscanf( pszQuality + 9, _T("%i"), &nQuality );
01586 nQuality = max( 1, min( 100, nQuality ) );
01587 }
01588
01589 if ( ! pImage.SaveToMemory( _T(".jpg"), nQuality, &pBuffer, &nLength ) )
01590 {
01591 theApp.Message( MSG_ERROR, IDS_UPLOAD_PREVIEW_EMPTY, (LPCTSTR)m_sAddress, (LPCTSTR)m_sFileName );
01592 SendResponse( IDR_HTML_FILENOTFOUND );
01593 return TRUE;
01594 }
01595
01596 pServices.Cleanup();
01597
01598 m_pOutput->Print( "HTTP/1.1 200 OK\r\n" );
01599 SendDefaultHeaders();
01600
01601 CString strHeader;
01602
01603 if ( m_bSHA1 )
01604 {
01605 strHeader.Format( _T("X-Previewed-URN: %s\r\n"),
01606 (LPCTSTR)CSHA::HashToString( &m_pSHA1, TRUE ) );
01607 }
01608 else if ( m_bTiger )
01609 {
01610 strHeader.Format( _T("X-Previewed-URN: %s\r\n"),
01611 (LPCTSTR)CTigerNode::HashToString( &m_pTiger, TRUE ) );
01612 }
01613 else if ( m_bED2K )
01614 {
01615 strHeader.Format( _T("X-Previewed-URN: %s\r\n"),
01616 (LPCTSTR)CED2K::HashToString( &m_pED2K, TRUE ) );
01617 }
01618
01619 m_pOutput->Print( strHeader );
01620
01621 m_pOutput->Print( "Content-Type: image/jpeg\r\n" );
01622
01623 strHeader.Format( _T("Content-Length: %lu\r\n"), nLength );
01624 m_pOutput->Print( strHeader );
01625
01626 m_pOutput->Print( "\r\n" );
01627
01628 if ( ! m_bHead )
01629 {
01630 m_pOutput->Add( pBuffer, nLength );
01631 }
01632
01633 delete [] pBuffer;
01634
01635 StartSending( upsPreview );
01636
01637 theApp.Message( MSG_DEFAULT, IDS_UPLOAD_PREVIEW_SEND, (LPCTSTR)m_sFileName,
01638 (LPCTSTR)m_sAddress );
01639
01640 return TRUE;
01641 }
01642
01644
01645
01646 BOOL CUploadTransferHTTP::RequestHostBrowse()
01647 {
01648 CBuffer pBuffer;
01649
01650 int nExisting = Uploads.GetCount( this, upsBrowse );
01651
01652 if ( nExisting >= (int)Settings.Uploads.PreviewTransfers )
01653 {
01654 theApp.Message( MSG_ERROR, IDS_UPLOAD_BROWSE_BUSY, (LPCTSTR)m_sAddress );
01655 m_pOutput->Print( "HTTP/1.1 503 Busy\r\n" );
01656 SendDefaultHeaders();
01657 StartSending( upsResponse );
01658 return TRUE;
01659 }
01660
01661 if ( m_bHostBrowse < 2 )
01662 {
01663 if ( Settings.Community.ServeFiles )
01664 {
01665 CLocalSearch pSearch( NULL, &pBuffer, PROTOCOL_G1 );
01666 pSearch.Execute( 0 );
01667 }
01668 }
01669 else
01670 {
01671 if ( Settings.Community.ServeProfile && MyProfile.IsValid() )
01672 {
01673 CG2Packet* pProfile = CG2Packet::New( G2_PACKET_PROFILE_DELIVERY, TRUE );
01674 CString strXML = MyProfile.GetXML()->ToString( TRUE );
01675 pProfile->WritePacket( "XML", pProfile->GetStringLen( strXML ) );
01676 pProfile->WriteString( strXML, FALSE );
01677 pProfile->ToBuffer( &pBuffer );
01678 pProfile->Release();
01679 }
01680
01681 if ( Settings.Community.ServeFiles )
01682 {
01683 CLocalSearch pSearch( NULL, &pBuffer, PROTOCOL_G2 );
01684 pSearch.Execute( 0 );
01685 pSearch.WriteVirtualTree();
01686 }
01687
01688 if ( Settings.Community.ServeProfile && MyProfile.IsValid() )
01689 {
01690 if ( CG2Packet* pAvatar = MyProfile.CreateAvatar() )
01691 {
01692 pAvatar->ToBuffer( &pBuffer );
01693 pAvatar->Release();
01694 }
01695 }
01696 }
01697
01698 m_pOutput->Print( "HTTP/1.1 200 OK\r\n" );
01699 SendDefaultHeaders();
01700
01701 if ( m_bHostBrowse < 2 )
01702 {
01703 m_pOutput->Print( "Content-Type: application/x-gnutella-packets\r\n" );
01704 }
01705 else
01706 {
01707 m_pOutput->Print( "Content-Type: application/x-gnutella2\r\n" );
01708 }
01709
01710 m_bDeflate = m_bDeflate && pBuffer.Deflate( TRUE );
01711
01712 if ( m_bDeflate ) m_pOutput->Print( "Content-Encoding: deflate\r\n" );
01713
01714 CString strLength;
01715 strLength.Format( _T("Content-Length: %lu\r\n\r\n"), pBuffer.m_nLength );
01716 m_pOutput->Print( strLength );
01717
01718 if ( ! m_bHead ) m_pOutput->AddBuffer( &pBuffer );
01719
01720 StartSending( upsBrowse );
01721
01722 theApp.Message( MSG_SYSTEM, IDS_UPLOAD_BROWSE, (LPCTSTR)m_sAddress, (LPCTSTR)m_sUserAgent );
01723
01724 CTransfer::OnWrite();
01725
01726 return TRUE;
01727 }
01728
01730
01731
01732 void CUploadTransferHTTP::SendResponse(UINT nResourceID, BOOL bFileHeaders)
01733 {
01734 CString strBody, strResponse;
01735
01736 HMODULE hModule = GetModuleHandle( NULL );
01737 HRSRC hRes = FindResource( hModule, MAKEINTRESOURCE( nResourceID ), MAKEINTRESOURCE( 23 ) );
01738
01739 if ( hRes != NULL )
01740 {
01741 DWORD nSize = SizeofResource( hModule, hRes );
01742 HGLOBAL hMemory = LoadResource( hModule, hRes );
01743 LPTSTR pszOutput = strBody.GetBuffer( nSize + 1 );
01744 LPCSTR pszInput = (LPCSTR)LockResource( hMemory );
01745
01746 while ( nSize-- ) *pszOutput++ = *pszInput++;
01747 *pszOutput++ = 0;
01748
01749 strBody.ReleaseBuffer();
01750 }
01751
01752 int nBreak = strBody.Find( _T("\r\n") );
01753 strResponse = strBody.Left( nBreak + 2 );
01754 strBody = strBody.Mid( nBreak + 2 );
01755
01756 while ( TRUE )
01757 {
01758 int nStart = strBody.Find( _T("<%") );
01759 if ( nStart < 0 ) break;
01760
01761 int nEnd = strBody.Find( _T("%>") );
01762 if ( nEnd < nStart ) break;
01763
01764 CString strReplace = strBody.Mid( nStart + 2, nEnd - nStart - 2 );
01765
01766 strReplace.TrimLeft();
01767 strReplace.TrimRight();
01768
01769 if ( strReplace.CompareNoCase( _T("Name") ) == 0 )
01770 strReplace = m_sFileName;
01771 else if ( strReplace.CompareNoCase( _T("SHA1") ) == 0 )
01772 strReplace = CSHA::HashToString( &m_pSHA1 );
01773 else if ( strReplace.CompareNoCase( _T("URN") ) == 0 )
01774 strReplace = CSHA::HashToString( &m_pSHA1, TRUE );
01775 else if ( strReplace.CompareNoCase( _T("Version") ) == 0 )
01776 strReplace = theApp.m_sVersion;
01777 else if ( strReplace.CompareNoCase( _T("Neighbours") ) == 0 )
01778 GetNeighbourList( strReplace );
01779 else if ( strReplace.CompareNoCase( _T("ListenIP") ) == 0 )
01780 {
01781 if ( Network.IsListening() )
01782 {
01783 strReplace.Format( _T("%s:%i"),
01784 (LPCTSTR)CString( inet_ntoa( Network.m_pHost.sin_addr ) ),
01785 htons( Network.m_pHost.sin_port ) );
01786 }
01787 else strReplace.Empty();
01788 }
01789
01790 strBody = strBody.Left( nStart ) + strReplace + strBody.Mid( nEnd + 2 );
01791 }
01792
01793 m_pOutput->Print( _T("HTTP/1.1 ") + strResponse );
01794 SendDefaultHeaders();
01795 if ( bFileHeaders ) SendFileHeaders();
01796 m_pOutput->Print( "Content-Type: text/html\r\n" );
01797
01798 int nBody = WideCharToMultiByte( CP_UTF8, 0, strBody, strBody.GetLength(), NULL, 0, NULL, NULL );
01799 LPSTR pszBody = new CHAR[ nBody ];
01800 WideCharToMultiByte( CP_UTF8, 0, strBody, strBody.GetLength(), pszBody, nBody, NULL, NULL );
01801
01802 strResponse.Format( _T("Content-Length: %lu\r\n\r\n"), nBody );
01803 m_pOutput->Print( strResponse );
01804
01805 if ( ! m_bHead ) m_pOutput->Add( pszBody, nBody );
01806
01807 delete [] pszBody;
01808
01809 StartSending( upsResponse );
01810 }
01811
01812 void CUploadTransferHTTP::GetNeighbourList(CString& strOutput)
01813 {
01814 static LPCTSTR pszModes[4][3] =
01815 {
01816 { _T("Handshake"), _T("Handshake"), _T("Handshake") },
01817 { _T("G1 Peer"), _T("G1 Ultrapeer"), _T("G1 Leaf") },
01818 { _T("G2 Peer"), _T("G2 Hub"), _T("G2 Leaf") },
01819 { _T("eDonkey2000"), _T("eDonkey2000"), _T("eDonkey2000") }
01820 };
01821
01822 strOutput.Empty();
01823
01824 CSingleLock pLock( &Network.m_pSection );
01825 if ( ! pLock.Lock( 100 ) ) return;
01826
01827 DWORD tNow = GetTickCount();
01828
01829 for ( POSITION pos = Neighbours.GetIterator() ; pos ; )
01830 {
01831 CNeighbour* pNeighbour = Neighbours.GetNext( pos );
01832
01833 if ( pNeighbour->m_nState == nrsConnected )
01834 {
01835 CString strNode;
01836
01837 DWORD nTime = ( tNow - pNeighbour->m_tConnected ) / 1000;
01838
01839 strNode.Format( _T("<tr><td class=\"fi\"><a href=\"gnutella:host:%s:%lu\">%s:%lu</a></td><td class=\"fi\" align=\"center\">%i:%.2i:%.2i</td><td class=\"fi\">%s</td><td class=\"fi\">%s</td><td class=\"fi\"><a href=\"http://%s:%lu/\">Browse</a></td></tr>\r\n"),
01840 (LPCTSTR)pNeighbour->m_sAddress, htons( pNeighbour->m_pHost.sin_port ),
01841 (LPCTSTR)pNeighbour->m_sAddress, htons( pNeighbour->m_pHost.sin_port ),
01842 nTime / 3600, ( nTime % 3600 ) / 60, nTime % 60,
01843 pszModes[ pNeighbour->m_nProtocol ][ pNeighbour->m_nNodeType ],
01844 (LPCTSTR)pNeighbour->m_sUserAgent,
01845 (LPCTSTR)pNeighbour->m_sAddress, htons( pNeighbour->m_pHost.sin_port ) );
01846
01847 strOutput += strNode;
01848 }
01849 }
01850 }