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 "SharedFile.h"
00026 #include "Library.h"
00027 #include "LibraryBuilder.h"
00028 #include "LibraryBuilderInternals.h"
00029 #include "LibraryBuilderPlugins.h"
00030 #include "HashDatabase.h"
00031
00032 #include "XML.h"
00033 #include "Packet.h"
00034 #include "Schema.h"
00035 #include "SchemaCache.h"
00036 #include "ID3.h"
00037
00038 #include "SHA.h"
00039 #include "TigerTree.h"
00040 #include "MD5.h"
00041 #include "ED2K.h"
00042
00043 #ifdef _DEBUG
00044 #undef THIS_FILE
00045 static char THIS_FILE[]=__FILE__;
00046 #define new DEBUG_NEW
00047 #endif
00048
00049 CLibraryBuilder LibraryBuilder;
00050
00051
00053
00054
00055 CLibraryBuilder::CLibraryBuilder()
00056 {
00057 m_pInternals = new CLibraryBuilderInternals( this );
00058 m_pPlugins = new CLibraryBuilderPlugins( this );
00059
00060 m_hThread = NULL;
00061 m_bThread = FALSE;
00062 m_bPriority = FALSE;
00063 m_nHashSleep = 100;
00064 m_nIndex = 0;
00065 m_tActive = 0;
00066 m_pBuffer = NULL;
00067 }
00068
00069 CLibraryBuilder::~CLibraryBuilder()
00070 {
00071 StopThread();
00072 Clear();
00073
00074 delete m_pPlugins;
00075 delete m_pInternals;
00076 if ( m_pBuffer ) delete [] m_pBuffer;
00077 }
00078
00080
00081
00082 void CLibraryBuilder::Add(CLibraryFile* pFile)
00083 {
00084 CSingleLock pLock( &m_pSection, TRUE );
00085
00086 POSITION pos = m_pFiles.Find( (LPVOID)pFile->m_nIndex );
00087 if ( pos == NULL ) m_pFiles.AddHead( (LPVOID)pFile->m_nIndex );
00088
00089 if ( ! m_bThread ) StartThread();
00090 }
00091
00092 void CLibraryBuilder::Remove(CLibraryFile* pFile)
00093 {
00094 m_pSection.Lock();
00095
00096 if ( POSITION pos = m_pFiles.Find( (LPVOID)pFile->m_nIndex ) )
00097 {
00098 m_pFiles.RemoveAt( pos );
00099
00100 if ( pos = m_pPriority.Find( pFile->GetPath() ) )
00101 {
00102 m_pPriority.RemoveAt( pos );
00103 }
00104 }
00105
00106 m_pSection.Unlock();
00107 }
00108
00109 int CLibraryBuilder::GetRemaining()
00110 {
00111 m_pSection.Lock();
00112 int nCount = m_pFiles.GetCount();
00113 if ( m_bThread ) nCount ++;
00114 m_pSection.Unlock();
00115 return nCount;
00116 }
00117
00118 CString CLibraryBuilder::GetCurrentFile()
00119 {
00120 m_pSection.Lock();
00121 CString str = m_sPath;
00122 if ( ! m_bThread ) str.Empty();
00123 m_pSection.Unlock();
00124 return str;
00125 }
00126
00127 void CLibraryBuilder::UpdateStatus(CString* pStr, int* pRemaining )
00128 {
00129 m_pSection.Lock();
00130
00131 if ( pRemaining != NULL )
00132 {
00133 *pRemaining = m_pFiles.GetCount();
00134 if ( m_bThread ) *pRemaining ++;
00135 }
00136
00137 if ( pStr != NULL )
00138 {
00139 *pStr = m_sPath;
00140 if ( ! m_bThread ) pStr->Empty();
00141 }
00142 m_pSection.Unlock();
00143
00144 }
00145
00146 void CLibraryBuilder::RequestPriority(LPCTSTR pszPath)
00147 {
00148 CSingleLock pLock( &m_pSection, TRUE );
00149
00150 POSITION pos = m_pPriority.Find( pszPath );
00151 if ( pos == NULL ) m_pPriority.AddTail( pszPath );
00152 }
00153
00155
00156
00157 void CLibraryBuilder::Clear()
00158 {
00159 m_pSection.Lock();
00160 m_pFiles.RemoveAll();
00161 m_pPriority.RemoveAll();
00162 m_pSection.Unlock();
00163 }
00164
00166
00167
00168 BOOL CLibraryBuilder::StartThread()
00169 {
00170 if ( m_hThread != NULL && m_bThread ) return TRUE;
00171
00172 m_pSection.Lock();
00173 BOOL bWorkToDo = m_pFiles.GetCount() > 0;
00174 m_pSection.Unlock();
00175
00176 if ( ! bWorkToDo ) return FALSE;
00177
00178 m_pInternals->LoadSettings();
00179
00180 m_bThread = TRUE;
00181 m_tActive = 0;
00182
00183 CWinThread* pThread = AfxBeginThread( ThreadStart, this, m_bPriority ?
00184 THREAD_PRIORITY_NORMAL : THREAD_PRIORITY_BELOW_NORMAL );
00185
00186 m_hThread = pThread->m_hThread;
00187
00188 return TRUE;
00189 }
00190
00191 void CLibraryBuilder::StopThread()
00192 {
00193 if ( m_hThread == NULL ) return;
00194
00195 m_bThread = FALSE;
00196
00197 int nAttempt = 20;
00198 for ( ; nAttempt > 0 ; nAttempt-- )
00199 {
00200 DWORD nCode;
00201 if ( ! GetExitCodeThread( m_hThread, &nCode ) ) break;
00202 if ( nCode != STILL_ACTIVE ) break;
00203 Sleep( 150 );
00204 }
00205
00206 if ( nAttempt == 0 )
00207 {
00208 TerminateThread( m_hThread, 0 );
00209 theApp.Message( MSG_DEBUG, _T("WARNING: Terminating CLibraryBuilder thread.") );
00210 Sleep( 100 );
00211 }
00212
00213 m_hThread = NULL;
00214 m_tActive = 0;
00215 }
00216
00218
00219
00220 void CLibraryBuilder::BoostPriority(BOOL bPriority)
00221 {
00222 if ( m_bPriority == bPriority ) return;
00223 m_bPriority = bPriority;
00224
00225 if ( m_bThread && m_hThread != NULL )
00226 {
00227 SetThreadPriority( m_hThread, m_bPriority ?
00228 THREAD_PRIORITY_NORMAL : THREAD_PRIORITY_BELOW_NORMAL );
00229 }
00230 }
00231
00232 BOOL CLibraryBuilder::GetBoostPriority()
00233 {
00234 return m_bPriority;
00235 }
00236
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00258
00259
00260 UINT CLibraryBuilder::ThreadStart(LPVOID pParam)
00261 {
00262 CLibraryBuilder* pBuilder = (CLibraryBuilder*)pParam;
00263 pBuilder->OnRun();
00264 return 0;
00265 }
00266
00267 void CLibraryBuilder::OnRun()
00268 {
00269 if ( m_pBuffer == NULL ) m_pBuffer = new BYTE[20480];
00270
00271 while ( m_bThread )
00272 {
00273 if ( m_pSection.Lock() )
00274 {
00275 m_nIndex = 0;
00276 m_tActive = 0;
00277 m_sPath.Empty();
00278
00279 if ( m_pFiles.IsEmpty() )
00280 {
00281 m_pSection.Unlock();
00282 break;
00283 }
00284
00285 m_nIndex = (DWORD)m_pFiles.RemoveHead();
00286
00287 m_pSection.Unlock();
00288 }
00289
00290 if ( m_nIndex == 0 )
00291 {
00292 Sleep( 250 );
00293 continue;
00294 }
00295
00296 {
00297 CQuickLock oLock( Library.m_pSection );
00298 if ( CLibraryFile* pFile = Library.LookupFile( m_nIndex ) )
00299 {
00300 m_sPath = pFile->GetPath();
00301 }
00302 else
00303 {
00304 m_nIndex = 0;
00305 continue;
00306 }
00307 }
00308
00309 BOOL bPriority = FALSE;
00310
00311 if ( m_pSection.Lock() )
00312 {
00313 if ( POSITION pos = m_pPriority.Find( m_sPath ) )
00314 {
00315 bPriority = TRUE;
00316 m_pPriority.RemoveAt( pos );
00317 }
00318
00319 m_pSection.Unlock();
00320 }
00321
00322 HANDLE hFile = CreateFile( m_sPath, GENERIC_READ,
00323 FILE_SHARE_READ, NULL, OPEN_EXISTING,
00324 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL );
00325
00326 if ( hFile == INVALID_HANDLE_VALUE )
00327 {
00328 m_pSection.Lock();
00329 if ( m_pFiles.Find( NULL ) == NULL ) m_pFiles.AddTail( (LPVOID)0 );
00330 m_pFiles.AddTail( (LPVOID)m_nIndex );
00331 m_pSection.Unlock();
00332 continue;
00333 }
00334
00335 theApp.Message( MSG_DEBUG, _T("Hashing: %s"), (LPCTSTR)m_sPath );
00336
00337 SHA1 pSHA1;
00338
00339 if ( HashFile( hFile, bPriority, &pSHA1 ) )
00340 {
00341 SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00342 m_tActive = GetTickCount();
00343
00344 if ( m_pPlugins->ExtractMetadata( m_sPath, hFile ) )
00345 {
00346
00347 }
00348 else if ( m_pInternals->ExtractMetadata( m_sPath, hFile, &pSHA1 ) )
00349 {
00350
00351 }
00352 }
00353
00354 CloseHandle( hFile );
00355 }
00356
00357 m_pPlugins->Cleanup();
00358
00359 delete [] m_pBuffer;
00360 m_pBuffer = NULL;
00361
00362 m_nIndex = 0;
00363 m_tActive = 0;
00364 m_bThread = FALSE;
00365 m_sPath.Empty();
00366
00367 theApp.Message( MSG_DEBUG, _T("CLibraryBuilder shutting down.") );
00368 }
00369
00371
00372
00373 BOOL CLibraryBuilder::HashFile(HANDLE hFile, BOOL bPriority, SHA1* pOutSHA1)
00374 {
00375 DWORD nSizeHigh = 0;
00376 DWORD nSizeLow = GetFileSize( hFile, &nSizeHigh );
00377 QWORD nFileSize = (QWORD)nSizeLow | ( (QWORD)nSizeHigh << 32 );
00378 QWORD nFileBase = 0;
00379
00380 BOOL bVirtual = FALSE;
00381
00382 if ( Settings.Library.VirtualFiles )
00383 bVirtual = DetectVirtualFile( hFile, nFileBase, nFileSize );
00384
00385 nSizeLow = (DWORD)( nFileBase & 0xFFFFFFFF );
00386 nSizeHigh = (DWORD)( nFileBase >> 32 );
00387 SetFilePointer( hFile, nSizeLow, (PLONG)&nSizeHigh, FILE_BEGIN );
00388
00389 CTigerTree pTiger;
00390 CED2K pED2K;
00391 CSHA pSHA1;
00392 CMD5 pMD5;
00393
00394 pTiger.BeginFile( Settings.Library.TigerHeight, nFileSize );
00395 pED2K.BeginFile( nFileSize );
00396
00397 for ( QWORD nLength = nFileSize ; nLength > 0 ; )
00398 {
00399 DWORD nBlock = (DWORD)min( nLength, QWORD(20480) );
00400 DWORD nTime = GetTickCount();
00401
00402 ReadFile( hFile, m_pBuffer, nBlock, &nBlock, NULL );
00403
00404 pSHA1.Add( m_pBuffer, nBlock );
00405 pMD5.Add( m_pBuffer, nBlock );
00406 pTiger.AddToFile( m_pBuffer, nBlock );
00407 pED2K.AddToFile( m_pBuffer, nBlock );
00408
00409 nLength -= nBlock;
00410
00411 if ( ! m_bPriority && ! bPriority )
00412 {
00413 if ( nBlock == 20480 ) m_nHashSleep = ( GetTickCount() - nTime ) * 2;
00414 m_nHashSleep = max( m_nHashSleep, DWORD(20) );
00415 Sleep( m_nHashSleep );
00416 }
00417
00418 if ( ! m_bThread ) return FALSE;
00419 }
00420
00421 pSHA1.Finish();
00422 pMD5.Finish();
00423 pTiger.FinishFile();
00424 pED2K.FinishFile();
00425
00426 {
00427 CQuickLock oLock( Library.m_pSection );
00428 CLibraryFile* pFile = Library.LookupFile( m_nIndex );
00429 if ( pFile == NULL ) return FALSE;
00430
00431 Library.RemoveFile( pFile );
00432
00433 pFile->m_bBogus = FALSE;
00434 pFile->m_nVirtualBase = bVirtual ? nFileBase : 0;
00435 pFile->m_nVirtualSize = bVirtual ? nFileSize : 0;
00436
00437 pFile->m_bSHA1 = TRUE;
00438 pSHA1.GetHash( &pFile->m_pSHA1 );
00439 if ( pOutSHA1 != NULL ) *pOutSHA1 = pFile->m_pSHA1;
00440
00441 pFile->m_bMD5 = TRUE;
00442 pMD5.GetHash( &pFile->m_pMD5 );
00443
00444 pFile->m_bTiger = TRUE;
00445 pTiger.GetRoot( &pFile->m_pTiger );
00446
00447 pFile->m_bED2K = TRUE;
00448 pED2K.GetRoot( &pFile->m_pED2K );
00449
00450 LibraryMaps.CullDeletedFiles( pFile );
00451 Library.AddFile( pFile );
00452 Library.Update();
00453 }
00454
00455 LibraryHashDB.StoreTiger( m_nIndex, &pTiger );
00456 LibraryHashDB.StoreED2K( m_nIndex, &pED2K );
00457
00458 return TRUE;
00459 }
00460
00462
00463
00464 BOOL CLibraryBuilder::SubmitMetadata(LPCTSTR pszSchemaURI, CXMLElement*& pXML)
00465 {
00466 CSchema* pSchema = SchemaCache.Get( pszSchemaURI );
00467
00468 if ( pSchema == NULL )
00469 {
00470 delete pXML;
00471 return FALSE;
00472 }
00473
00474 CXMLElement* pBase = pSchema->Instantiate( TRUE );
00475 pBase->AddElement( pXML );
00476
00477 if ( ! pSchema->Validate( pBase, TRUE ) )
00478 {
00479 delete pBase;
00480 return FALSE;
00481 }
00482
00483 pXML->Detach();
00484 delete pBase;
00485
00486 CQuickLock oLock( Library.m_pSection );
00487 if ( CLibraryFile* pFile = Library.LookupFile( m_nIndex ) )
00488 {
00489 if ( pFile->m_pMetadata == NULL )
00490 {
00491 Library.RemoveFile( pFile );
00492
00493 pFile->m_pSchema = pSchema;
00494 pFile->m_pMetadata = pXML;
00495 pFile->m_bMetadataAuto = TRUE;
00496
00497 Library.AddFile( pFile );
00498 Library.Update();
00499
00500 return TRUE;
00501 }
00502
00503 }
00504
00505 delete pXML;
00506
00507 return FALSE;
00508 }
00509
00511
00512
00513 BOOL CLibraryBuilder::SubmitCorrupted()
00514 {
00515 CQuickLock oLock( Library.m_pSection );
00516 if ( CLibraryFile* pFile = Library.LookupFile( m_nIndex ) )
00517 {
00518 pFile->m_bBogus = TRUE;
00519 return TRUE;
00520 }
00521
00522 return FALSE;
00523 }
00524
00526
00527
00528 BOOL CLibraryBuilder::DetectVirtualFile(HANDLE hFile, QWORD& nOffset, QWORD& nLength)
00529 {
00530 BOOL bVirtual = FALSE;
00531
00532 if ( _tcsistr( m_sPath, _T(".mp3") ) != NULL )
00533 {
00534 bVirtual |= DetectVirtualID3v2( hFile, nOffset, nLength );
00535 bVirtual |= DetectVirtualID3v1( hFile, nOffset, nLength );
00536 }
00537
00538 return bVirtual;
00539 }
00540
00541 BOOL CLibraryBuilder::DetectVirtualID3v1(HANDLE hFile, QWORD& nOffset, QWORD& nLength)
00542 {
00543 ID3V1 pInfo;
00544 DWORD nRead;
00545
00546 if ( nLength <= 128 ) return FALSE;
00547
00548 LONG nPosLow = (LONG)( ( nOffset + nLength - 128 ) & 0xFFFFFFFF );
00549 LONG nPosHigh = (LONG)( ( nOffset + nLength - 128 ) >> 32 );
00550 SetFilePointer( hFile, nPosLow, &nPosHigh, FILE_BEGIN );
00551
00552 ReadFile( hFile, &pInfo, sizeof(pInfo), &nRead, NULL );
00553
00554 if ( nRead != sizeof(pInfo) ) return FALSE;
00555 if ( memcmp( pInfo.szTag, ID3V1_TAG, 3 ) ) return FALSE;
00556
00557 nLength -= 128;
00558
00559 return TRUE;
00560 }
00561
00562 BOOL CLibraryBuilder::DetectVirtualID3v2(HANDLE hFile, QWORD& nOffset, QWORD& nLength)
00563 {
00564 ID3V2_HEADER pHeader;
00565 DWORD nRead;
00566
00567 LONG nPosLow = (LONG)( ( nOffset ) & 0xFFFFFFFF );
00568 LONG nPosHigh = (LONG)( ( nOffset ) >> 32 );
00569 SetFilePointer( hFile, nPosLow, &nPosHigh, FILE_BEGIN );
00570
00571 ReadFile( hFile, &pHeader, sizeof(pHeader), &nRead, NULL );
00572 if ( nRead != sizeof(pHeader) ) return FALSE;
00573
00574 if ( strncmp( pHeader.szTag, ID3V2_TAG, 3 ) ) return FALSE;
00575 if ( pHeader.nMajorVersion < 2 || pHeader.nMajorVersion > 4 ) return FALSE;
00576 if ( pHeader.nFlags & ~ID3V2_KNOWNMASK ) return FALSE;
00577 if ( pHeader.nFlags & ID3V2_UNSYNCHRONISED ) return FALSE;
00578
00579 DWORD nTagSize = SWAP_LONG( pHeader.nSize );
00580 ID3_DESYNC_SIZE( nTagSize );
00581
00582 if ( pHeader.nFlags & ID3V2_FOOTERPRESENT ) nTagSize += 10;
00583 nTagSize += sizeof(pHeader);
00584
00585 if ( nLength <= nTagSize ) return FALSE;
00586
00587 nOffset += nTagSize;
00588 nLength -= nTagSize;
00589
00590 return TRUE;
00591 }