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

LibraryBuilder.cpp

Go to the documentation of this file.
00001 //
00002 // LibraryBuilder.cpp
00003 //
00004 // Copyright (c) Shareaza Development Team, 2002-2005.
00005 // This file is part of SHAREAZA (www.shareaza.com)
00006 //
00007 // Shareaza is free software; you can redistribute it
00008 // and/or modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2 of
00010 // the License, or (at your option) any later version.
00011 //
00012 // Shareaza is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with Shareaza; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //
00021 
00022 #include "StdAfx.h"
00023 #include "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 // CLibraryBuilder construction
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 // CLibraryBuilder add and remove
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 // CLibraryBuilder clear
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 // CLibraryBuilder thread control
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 // CLibraryBuilder priority control
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 // CLibraryBuilder sanity check
00239 /*
00240 BOOL CLibraryBuilder::SanityCheck()
00241 {
00242         if ( ! m_bThread || ! m_tActive ) return TRUE;
00243 
00244         CSingleLock pLock( &m_pSection );
00245 
00246         if ( pLock.Lock( 50 ) )
00247         {
00248                 if ( ! m_tActive || GetTickCount() - m_tActive < 180000 ) return TRUE;
00249 
00250                 theApp.Message( MSG_ERROR, _T("CLibraryBuilder sanity check: stuck on \"%s\" (%lu)."),
00251                         (LPCTSTR)m_sPath, ( GetTickCount() - m_tActive ) / 1000 );
00252         }
00253 
00254         return FALSE;
00255 }
00256 */
00258 // CLibraryBuilder thread run (threaded)
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                                 // Plugin got it
00347                         }
00348                         else if ( m_pInternals->ExtractMetadata( m_sPath, hFile, &pSHA1 ) )
00349                         {
00350                                 // Internal got it
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 // CLibraryBuilder file hashing (threaded)
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 // CLibraryBuilder metadata submission (threaded)
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 // CLibraryBuilder bogus/corrupted state submission (threaded)
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 // CLibraryBuilder virtual file detection (threaded)
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 }

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