00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "StdAfx.h"
00027 #include "Shareaza.h"
00028 #include "Settings.h"
00029 #include "Download.h"
00030 #include "Downloads.h"
00031 #include "DownloadTask.h"
00032 #include "DownloadGroups.h"
00033 #include "Transfers.h"
00034 #include "Uploads.h"
00035 #include "LibraryFolders.h"
00036
00037 #ifdef _DEBUG
00038 #define new DEBUG_NEW
00039 #undef THIS_FILE
00040 static char THIS_FILE[] = __FILE__;
00041 #endif
00042
00043 IMPLEMENT_DYNAMIC(CDownloadTask, CWinThread)
00044
00045 BEGIN_MESSAGE_MAP(CDownloadTask, CWinThread)
00046
00047
00048 END_MESSAGE_MAP()
00049
00050 #define METHOD_BUFFERED 0x00000000
00051 #define FILE_DEVICE_FILE_SYSTEM 0x00000009
00052 #define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
00053 #define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA)
00054 #define BUFFER_SIZE (2*1024*1024)
00055
00056
00058
00059
00060 CDownloadTask::CDownloadTask(CDownload* pDownload, int nTask)
00061 {
00062 ASSERT( pDownload->m_pTask == NULL );
00063 pDownload->m_pTask = this;
00064
00065 m_nTask = nTask;
00066 m_pDownload = pDownload;
00067 m_bSuccess = FALSE;
00068 m_nTorrentFile = 0;
00069 m_nSize = pDownload->m_nSize;
00070 m_sName = pDownload->m_sRemoteName;
00071 m_sFilename = pDownload->m_sLocalName;
00072 m_sPath = DownloadGroups.GetCompletedPath( pDownload );
00073
00074 int nExt = m_sFilename.ReverseFind( '.' );
00075 if ( nExt >= 2 )
00076 {
00077 CString sExtention = m_sFilename.Mid( nExt );
00078 CharLower( sExtention.GetBuffer() );
00079 sExtention.ReleaseBuffer();
00080
00081 if( ( sExtention == ".collection" ) || ( sExtention == ".co" ) )
00082 {
00083 m_sPath = Settings.Downloads.CollectionPath;
00084 CreateDirectory( Settings.Downloads.CollectionPath, NULL );
00085 LibraryFolders.AddFolder( Settings.Downloads.CollectionPath );
00086 }
00087 else if( sExtention == ".torrent" )
00088 {
00089 m_sPath = Settings.Downloads.TorrentPath;
00090 CreateDirectory( Settings.Downloads.TorrentPath, NULL );
00091 LibraryFolders.AddFolder( Settings.Downloads.TorrentPath, FALSE );
00092 }
00093 }
00094
00095
00096 if ( m_nTask == dtaskCopySimple && m_pDownload->m_pTorrent.m_nFiles > 1 )
00097 {
00098 m_nTask = dtaskCopyTorrent;
00099 m_pTorrent.Copy( &m_pDownload->m_pTorrent );
00100 }
00101
00102 m_pEvent = NULL;
00103 m_bAutoDelete = TRUE;
00104
00105 CreateThread();
00106 SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
00107 }
00108
00109 CDownloadTask::~CDownloadTask()
00110 {
00111 Transfers.m_pSection.Lock();
00112
00113 if ( Downloads.Check( m_pDownload ) )
00114 {
00115 m_pDownload->OnTaskComplete( this );
00116 ASSERT( m_pDownload->m_pTask != this );
00117 }
00118
00119 CEvent* pEvent = m_pEvent;
00120 Transfers.m_pSection.Unlock();
00121 if ( pEvent != NULL ) pEvent->SetEvent();
00122 }
00123
00125
00126
00127 void CDownloadTask::Abort()
00128 {
00129 CEvent* pEvent = m_pEvent = new CEvent();
00130 Transfers.m_pSection.Unlock();
00131 WaitForSingleObject( *pEvent, INFINITE );
00132 Transfers.m_pSection.Lock();
00133 delete pEvent;
00134 }
00135
00136 BOOL CDownloadTask::WasAborted()
00137 {
00138 return m_pEvent != NULL;
00139 }
00140
00142
00143
00144 BOOL CDownloadTask::InitInstance()
00145 {
00146 return TRUE;
00147 }
00148
00149 int CDownloadTask::Run()
00150 {
00151 switch ( m_nTask )
00152 {
00153 case dtaskAllocate:
00154 RunAllocate();
00155 break;
00156 case dtaskCopySimple:
00157 RunCopySimple();
00158 if ( m_bSuccess == FALSE && m_pEvent == NULL )
00159 {
00160 if ( m_sPath != Settings.Downloads.CompletePath )
00161 {
00162 m_sPath = Settings.Downloads.CompletePath;
00163 RunCopySimple();
00164 }
00165 }
00166 break;
00167 case dtaskCopyTorrent:
00168 RunCopyTorrent();
00169 if ( m_bSuccess == FALSE && m_pEvent == NULL )
00170 {
00171 if ( m_sPath != Settings.Downloads.CompletePath )
00172 {
00173 m_sPath = Settings.Downloads.CompletePath;
00174 RunCopyTorrent();
00175 }
00176 }
00177 break;
00178 }
00179
00180 return 0;
00181 }
00182
00184
00185
00186 void CDownloadTask::RunAllocate()
00187 {
00188 HANDLE hFile = CreateFile( m_sFilename, GENERIC_WRITE,
00189 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
00190 FILE_ATTRIBUTE_NORMAL, NULL );
00191
00192 if ( hFile == INVALID_HANDLE_VALUE ) return;
00193
00194 if ( GetFileSize( hFile, NULL ) != 0 )
00195 {
00196 CloseHandle( hFile );
00197 return;
00198 }
00199
00200 DWORD dwOut = 0;
00201 DeviceIoControl( hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwOut, NULL );
00202
00203 if ( m_nSize > 0 )
00204 {
00205 DWORD nOffsetLow = (DWORD)( ( m_nSize - 1 ) & 0x00000000FFFFFFFF );
00206 DWORD nOffsetHigh = (DWORD)( ( ( m_nSize - 1 ) & 0xFFFFFFFF00000000 ) >> 32 );
00207 SetFilePointer( hFile, nOffsetLow, (PLONG)&nOffsetHigh, FILE_BEGIN );
00208
00209 BYTE bZero = 0;
00210 DWORD nSize = 0;
00211 WriteFile( hFile, &bZero, 1, &nSize, NULL );
00212 }
00213
00214 CloseHandle( hFile );
00215 m_bSuccess = TRUE;
00216 }
00217
00219
00220
00221 void CDownloadTask::RunCopySimple()
00222 {
00223 CString strTarget;
00224 CString strSafeName = SafeFilename( m_sName );
00225
00226 int nExt = strSafeName.ReverseFind( '.' );
00227
00228 CString strName( nExt > 0 ? strSafeName.Left( nExt ) : strSafeName );
00229 CString strExt( nExt > 0 ? strSafeName.Mid( nExt ) : _T( "" ) );
00230
00231
00232 if ( m_sFilename[ 0 ] == m_sPath[ 0 ] && m_sFilename[ 0 ] != '\\' )
00233 {
00234 TCHAR szOpFrom[MAX_PATH] = { 0 };
00235 _tcsncpy( szOpFrom, m_sFilename, MAX_PATH - 2 );
00236 TCHAR szOpTo[MAX_PATH] = { 0 };
00237 SHFILEOPSTRUCT pOp = { 0 };
00238
00239 pOp.wFunc = FO_MOVE;
00240 pOp.pFrom = szOpFrom;
00241 pOp.pTo = szOpTo;
00242 pOp.fFlags = FOF_MULTIDESTFILES|FOF_NOERRORUI|FOF_SILENT;
00243
00244 CTransfers::Lock oLock;
00245
00246
00247 Uploads.OnRename( m_sFilename );
00248
00249 for ( int nCopy = 0 ; nCopy < 10 ; nCopy++ )
00250 {
00251 if ( nCopy )
00252 {
00253 strTarget.Format( _T("%s\\%s (%i)%s"),
00254 (LPCTSTR)m_sPath, (LPCTSTR)strName, nCopy, (LPCTSTR)strExt );
00255 }
00256 else
00257 {
00258 strTarget.Format( _T("%s\\%s%s"),
00259 (LPCTSTR)m_sPath, (LPCTSTR)strName, (LPCTSTR)strExt );
00260 }
00261
00262 theApp.Message( MSG_DEBUG, _T("Moving \"%s\" to \"%s\"..."),
00263 (LPCTSTR)m_sFilename, (LPCTSTR)strTarget );
00264
00265 _tcsncpy( szOpTo, strTarget, MAX_PATH - 2 );
00266
00267 if ( GetFileAttributes( strTarget ) == 0xFFFFFFFF &&
00268 SHFileOperation( &pOp ) == 0 )
00269 {
00270 Uploads.OnRename( m_sFilename, strTarget );
00271 m_bSuccess = TRUE;
00272 m_sFilename = strTarget;
00273 return;
00274 }
00275 }
00276
00277
00278 Uploads.OnRename( m_sFilename, m_sFilename );
00279 }
00280
00281
00282 HANDLE hSource = CreateFile( m_sFilename, GENERIC_READ,
00283 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
00284 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL );
00285
00286 if ( hSource == INVALID_HANDLE_VALUE ) return;
00287
00288 for ( int nCopy = 0; !m_bSuccess && nCopy < 10; ++nCopy )
00289 {
00290 if ( nCopy )
00291 {
00292 strTarget.Format( _T("%s\\%s (%i)%s"),
00293 (LPCTSTR)m_sPath, (LPCTSTR)strName, nCopy, (LPCTSTR)strExt );
00294 }
00295 else
00296 {
00297 strTarget.Format( _T("%s\\%s%s"),
00298 (LPCTSTR)m_sPath, (LPCTSTR)strName, (LPCTSTR)strExt );
00299 }
00300
00301 theApp.Message( MSG_DEBUG, _T("Copying \"%s\" to \"%s\"..."),
00302 (LPCTSTR)m_sFilename, (LPCTSTR)strTarget );
00303
00304 m_bSuccess = CopyFile( hSource, strTarget, m_nSize );
00305 }
00306
00307 CloseHandle( hSource );
00308
00309 if ( !m_bSuccess )
00310 {
00311
00312 strTarget.Format( _T( "%s\\%s%s" ), LPCTSTR( m_sFilename.Left( m_sFilename.ReverseFind( '\\' ) ) ),
00313 LPCTSTR( strName ), LPCTSTR( strExt ) );
00314 TCHAR szOpFrom[MAX_PATH] = { 0 };
00315 _tcsncpy( szOpFrom, m_sFilename, MAX_PATH - 2 );
00316 TCHAR szOpTo[MAX_PATH] = { 0 };
00317 _tcsncpy( szOpTo, strTarget, MAX_PATH - 2 );
00318
00319 SHFILEOPSTRUCT pOp = { 0 };
00320 pOp.wFunc = FO_MOVE;
00321 pOp.pFrom = szOpFrom;
00322 pOp.pTo = szOpTo;
00323 pOp.fFlags = FOF_MULTIDESTFILES|FOF_NOERRORUI|FOF_SILENT;
00324
00325 CTransfers::Lock oLock;
00326
00327 Uploads.OnRename( m_sFilename );
00328
00329 if ( SHFileOperation( &pOp ) == 0 )
00330 {
00331 Uploads.OnRename( m_sFilename, strTarget );
00332 m_bSuccess = TRUE;
00333 m_sFilename = strTarget;
00334 return;
00335 }
00336
00337 Uploads.OnRename( m_sFilename, m_sFilename );
00338 }
00339
00340 if ( m_bSuccess )
00341 {
00342 CTransfers::Lock(),
00343 Uploads.OnRename( m_sFilename, NULL ),
00344 Uploads.OnRename( m_sFilename, strTarget );
00345
00346 if ( ! DeleteFile( m_sFilename ) )
00347 theApp.WriteProfileString( _T("Delete"), m_sFilename, _T("") );
00348 m_sFilename = strTarget;
00349 }
00350 }
00351
00353
00354
00355 void CDownloadTask::RunCopyTorrent()
00356 {
00357 ASSERT( m_pTorrent.IsAvailable() );
00358 ASSERT( m_pTorrent.m_nFiles > 1 );
00359
00360 HANDLE hSource = CreateFile( m_sFilename, GENERIC_READ,
00361 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
00362 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL );
00363
00364 if ( hSource == INVALID_HANDLE_VALUE ) return;
00365
00366 m_bSuccess = FALSE;
00367
00368
00369 if ( ! Downloads.IsSpaceAvailable( m_pTorrent.m_nTotalSize, Downloads.dlPathComplete ) ) return;
00370
00371 QWORD nOffset = 0;
00372
00373 for ( ; m_nTorrentFile < m_pTorrent.m_nFiles ; ++m_nTorrentFile )
00374 {
00375 const CBTInfo::CBTFile& rFile = m_pTorrent.m_pFiles[ m_nTorrentFile ];
00376
00377 DWORD nOffsetLow = (DWORD)( nOffset & 0x00000000FFFFFFFF );
00378 DWORD nOffsetHigh = (DWORD)( ( nOffset & 0xFFFFFFFF00000000 ) >> 32 );
00379 SetFilePointer( hSource, nOffsetLow, (PLONG)&nOffsetHigh, FILE_BEGIN );
00380 nOffset += rFile.m_nSize;
00381
00382 CString strPath;
00383 strPath.Format( _T("%s\\%s"), (LPCTSTR)m_sPath, (LPCTSTR)rFile.m_sPath );
00384 CreatePathForFile( m_sPath, rFile.m_sPath );
00385
00386 theApp.Message( MSG_DEBUG, _T("Extracting %s..."), (LPCTSTR)strPath );
00387
00388 if ( !CopyFile( hSource, strPath, rFile.m_nSize ) )
00389 {
00390
00391 strPath.Format( _T("%s\\%s"), LPCTSTR( m_sFilename.Left( m_sFilename.ReverseFind( '\\' ) ) ),
00392 LPCTSTR( rFile.m_sPath ) );
00393 theApp.Message( MSG_DEBUG, _T("Extraction failed, trying %s..."), LPCTSTR( strPath ) );
00394 CreatePathForFile( m_sFilename.Left( m_sFilename.ReverseFind( '\\' ) ), rFile.m_sPath );
00395
00396 if ( !CopyFile( hSource, strPath, rFile.m_nSize ) )
00397 {
00398 CloseHandle( hSource );
00399 return;
00400 }
00401 }
00402
00403 if ( m_pEvent != NULL )
00404 {
00405 CloseHandle( hSource );
00406 return;
00407 }
00408 }
00409
00410 CloseHandle( hSource );
00411 m_bSuccess = TRUE;
00412 }
00413
00415
00416
00417 BOOL CDownloadTask::CopyFile(HANDLE hSource, LPCTSTR pszTarget, QWORD nLength)
00418 {
00419 HANDLE hTarget = CreateFile( pszTarget, GENERIC_WRITE,
00420 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
00421
00422 if ( hTarget == INVALID_HANDLE_VALUE ) return FALSE;
00423
00424 BYTE* pBuffer = new BYTE[ BUFFER_SIZE ];
00425
00426 while ( nLength )
00427 {
00428 DWORD nBuffer = (DWORD)min( nLength, QWORD(BUFFER_SIZE) );
00429 DWORD nSuccess = 0;
00430 DWORD tStart = GetTickCount();
00431
00432 if ( !ReadFile( hSource, pBuffer, nBuffer, &nBuffer, NULL )
00433 || !nBuffer
00434 || !WriteFile( hTarget, pBuffer, nBuffer, &nSuccess, NULL )
00435 || nSuccess != nBuffer ) break;
00436
00437 nLength -= nBuffer;
00438
00439 if ( m_pEvent != NULL ) break;
00440 tStart = ( GetTickCount() - tStart ) / 2;
00441 Sleep( min( tStart, DWORD(50) ) );
00442 if ( m_pEvent != NULL ) break;
00443 }
00444
00445 delete [] pBuffer;
00446
00447 CloseHandle( hTarget );
00448 if ( nLength > 0 ) DeleteFile( pszTarget );
00449
00450 return ( nLength == 0 );
00451 }
00452
00454
00455
00456 CString CDownloadTask::SafeFilename(LPCTSTR pszName)
00457 {
00458 static LPCTSTR pszValid = _T(" `~!@#$%^&()-_=+[]{}';.,");
00459 CString strName = pszName;
00460
00461 for ( int nChar = 0 ; nChar < strName.GetLength() ; nChar++ )
00462 {
00463 TCHAR cChar = strName.GetAt( nChar );
00464
00465 if ( (DWORD)cChar > 128 )
00466 {
00467 if ( theApp.m_bNT ) continue;
00468 }
00469 else
00470 {
00471 if ( IsCharacter( cChar ) ) continue;
00472 if ( _tcschr( pszValid, cChar ) != NULL ) continue;
00473 }
00474
00475 strName.SetAt( nChar, '_' );
00476 }
00477
00478 LPCTSTR pszExt = _tcsrchr( strName, '.' );
00479 if ( pszExt )
00480 {
00481 if ( _tcsicmp( pszExt, _T(".sd") ) == 0 )
00482 strName += _T("x");
00483 }
00484
00485
00486
00487 int nMaxFilenameLength = 256 - 1 - Settings.Downloads.IncompletePath.GetLength() - 47;
00488 if ( strName.GetLength() > nMaxFilenameLength )
00489 {
00490 int nExtLen;
00491 if ( pszExt ) nExtLen = _tcslen( pszExt );
00492 else nExtLen = 0;
00493 strName = strName.Left( nMaxFilenameLength - nExtLen ) + strName.Right( nExtLen );
00494 }
00495
00496 return strName;
00497 }
00498
00500
00501
00502 void CDownloadTask::CreatePathForFile(const CString& strBase, const CString& strPath)
00503 {
00504 CreateDirectory( strBase, NULL );
00505
00506 for ( int nPos = 0 ; nPos < strPath.GetLength() ; nPos++ )
00507 {
00508 if ( strPath.GetAt( nPos ) == '\\' )
00509 {
00510 CString strFolder = strBase + '\\' + strPath.Left( nPos );
00511 CreateDirectory( strFolder, NULL );
00512 }
00513 }
00514 }