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

DownloadTask.cpp

Go to the documentation of this file.
00001 //
00002 // DownloadTask.cpp
00003 //
00004 //      Date:                   "$Date: 2005/07/20 19:06:38 $"
00005 //      Revision:               "$Revision: 1.20 $"
00006 //  Last change by:     "$Author: spooky23 $"
00007 //
00008 // Copyright (c) Shareaza Development Team, 2002-2005.
00009 // This file is part of SHAREAZA (www.shareaza.com)
00010 //
00011 // Shareaza is free software; you can redistribute it
00012 // and/or modify it under the terms of the GNU General Public License
00013 // as published by the Free Software Foundation; either version 2 of
00014 // the License, or (at your option) any later version.
00015 //
00016 // Shareaza is distributed in the hope that it will be useful,
00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 // GNU General Public License for more details.
00020 //
00021 // You should have received a copy of the GNU General Public License
00022 // along with Shareaza; if not, write to the Free Software
00023 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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         //{{AFX_MSG_MAP(CDownloadTask)
00047         //}}AFX_MSG_MAP
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 // CDownloadTask construction
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 // CDownloadTask aborting
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 // CDownloadTask run
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 // CDownloadTask allocate
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 // CDownloadTask simple copy
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         // Only try to move if the drive letters match else we copy always
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                 // Disconnect all uploads for that file (i.e. close the file handle)
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                 // Moving failed, so we allow uploads using the old filename for the time being
00278                 Uploads.OnRename( m_sFilename, m_sFilename );
00279         }
00280         
00281         // Moving failed or the destination drive differs from the source drive
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                 // rename in place
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 // CDownloadTask torrent copy
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         // Check for space before copying torrent
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                         // try to copy in place
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; // try again later ( m_bSuccess is still FALSE )
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 // CDownloadTask copy file
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 // CDownloadTask filename processor
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         // Maximum filepath length is
00486         // <Windows limit = 256 - 1> - <length of path to download directory> - <length of hash = 39(tiger)> -<space = 1> - <length of ".sd.sav" = 7>
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 // CDownloadTask path creator
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 }

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