/*++ Copyright (c) 1996 Microsoft Corporation Module Name : lcmgr.cpp Abstract: Link checker manager class implementation. This class provides the interfaces for creating and customizing the worker thread (link checking thread). NOTE: You should only have a aingle instance of CLinkCheckerMgr. Author: Michael Cheuk (mcheuk) Project: Link Checker Revision History: --*/ #include "stdafx.h" #include "lcmgr.h" #include "enumdir.h" #include "proglog.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // Constants (TODO: put this in resource) const CString strParsing_c(_T("Parsing")); const CString strLoading_c(_T("Loading")); //------------------------------------------------------------------ // Global fucntion for retrieve the link checker manager // // Global link checker manager pointer CLinkCheckerMgr* g_pLinkCheckerMgr = NULL; CLinkCheckerMgr& GetLinkCheckerMgr( ) /*++ Routine Description: Global fucntion for retrieve the link checker manager Arguments: N/A Return Value: CLinkCheckMgr& - reference to the link checker manager --*/ { ASSERT(g_pLinkCheckerMgr); return *g_pLinkCheckerMgr; } //------------------------------------------------------------------ // CLinkCheckerMgr implementation // CLinkCheckerMgr::CLinkCheckerMgr( ) /*++ Routine Description: Constructor. Arguments: N/A Return Value: N/A --*/ { ASSERT(g_pLinkCheckerMgr == NULL); g_pLinkCheckerMgr = this; m_fWininetLoaded = FALSE; m_fInitialized = FALSE; m_lWorkerThreadRunning = -1; m_lTerminatingThread = -1; m_hWorkerThread = NULL; m_pProgressLog = NULL; } // CLinkCheckerMgr::CLinkCheckerMgr CLinkCheckerMgr::~CLinkCheckerMgr( ) /*++ Routine Description: Destructor. Arguments: N/A Return Value: N/A --*/ { // The worker must be terminated ASSERT(!IsWorkerThreadRunning()); // Nuke the global pointer ASSERT(g_pLinkCheckerMgr); g_pLinkCheckerMgr = NULL; } // CLinkCheckerMgr::~CLinkCheckerMgr BOOL CLinkCheckerMgr::LoadWininet( ) /*++ Routine Description: Load wininet.dll. This must be called before initialize() Arguments: N/A Return Value: BOOL - TRUE if success. FALSE otherwise. --*/ { // Make sure LoadWininet() only call once ASSERT(!m_fWininetLoaded); if(m_fWininetLoaded) { return FALSE; } m_fWininetLoaded = TRUE; return m_Wininet.Load(); } // CLinkCheckerMgr::LoadWininet BOOL CLinkCheckerMgr::Initialize( CProgressLog* pProgressLog ) /*++ Routine Description: Initialize the link checker manager. The link checker manager will initialize the link loader, link parser, ...etc Arguments: pProgressLog - pointer to an instance of progress logging object Return Value: BOOL - TRUE if success. FALSE otherwise. --*/ { // Make sure Initialize() only call once ASSERT(!m_fInitialized); if(m_fInitialized) { return FALSE; } m_fInitialized = TRUE; // pProgressLog is ok to be NULL m_pProgressLog = pProgressLog; // Create the link loader if(!m_Loader.Create(_T(""), _T(""))) { return FALSE; } // Create the error log if(!m_ErrLog.Create()) { return FALSE; } // Set the local host name in the paser m_Parser.SetLocalHostName(GetUserOptions().GetHostName()); return TRUE; } // CLinkCheckerMgr::Initialize BOOL CLinkCheckerMgr::BeginWorkerThread( ) /*++ Routine Description: Begin the link checking thread Arguments: N/A Return Value: BOOL - TRUE if success. FALSE otherwise. --*/ { // Start 1 thread only if(IsWorkerThreadRunning()) { return FALSE; } CWinThread* pWorkerThread = ::AfxBeginThread((AFX_THREADPROC)WorkerThreadForwarder, NULL); if(pWorkerThread == NULL) { return FALSE; } else { m_hWorkerThread = pWorkerThread->m_hThread; return TRUE; } } // CLinkCheckerMgr::BeginWorkerThread void CLinkCheckerMgr::SignalWorkerThreadToTerminate( ) /*++ Routine Description: Signal the worker thread to terminate Arguments: N/A Return Value: N/A --*/ { if(IsWorkerThreadRunning() && !IsThreadTerminating()) { InterlockedIncrement(&m_lTerminatingThread); } } // CLinkCheckerMgr::SignalWorkerThreadToTerminate UINT CLinkCheckerMgr::WorkerThreadForwarder( LPVOID pParam ) /*++ Routine Description: Worker thread entry point Arguments: pParam - unused Return Value: UINT - unsed --*/ { // Now IsWorkerThreadRunnig() return TRUE InterlockedIncrement(&GetLinkCheckerMgr().m_lWorkerThreadRunning); UINT nRet = GetLinkCheckerMgr().WorkerThread(pParam); // Now IsWorkerThreadRunnig() return FLASE InterlockedDecrement(&GetLinkCheckerMgr().m_lWorkerThreadRunning); // Notify the progress log, the worker thread is completed if(GetLinkCheckerMgr().m_pProgressLog) { // Possible deadlock. Use message instead ? GetLinkCheckerMgr().m_pProgressLog->WorkerThreadComplete(); } return nRet; } // CLinkCheckerMgr::WorkerThreadForwarder UINT CLinkCheckerMgr::WorkerThread( LPVOID pParam ) /*++ Routine Description: Actual worker thread function Arguments: pParam - unused Return Value: UINT - unsed --*/ { UNUSED_ALWAYS(pParam); // Write the error log header m_ErrLog.WriteHeader(); // Go thru all the combination of browser & language POSITION PosBrowser; CBrowserInfo BrowserInfo; POSITION PosLanguage; CLanguageInfo LanguageInfo; PosBrowser = GetUserOptions().GetAvailableBrowsers().GetHeadSelectedPosition(); do { // Get the next browser BrowserInfo = GetUserOptions().GetAvailableBrowsers().GetNextSelected(PosBrowser); m_ErrLog.SetBrowser(BrowserInfo.GetName()); // Reset language position PosLanguage = GetUserOptions().GetAvailableLanguages().GetHeadSelectedPosition(); do { // Get the language LanguageInfo = GetUserOptions().GetAvailableLanguages().GetNextSelected(PosLanguage); m_ErrLog.SetLanguage(LanguageInfo.GetName()); // Change the loader properties CString strAdditionalHeaders; strAdditionalHeaders.Format(_T("Accept: */*\r\nAccept-Language: %s"), LanguageInfo.GetAcceptName()); if(!m_Loader.ChangeProperties(BrowserInfo.GetUserAgent(), strAdditionalHeaders)) { return 1; } // Remove everything in the look up table m_Lookup.RemoveAll(); // *EITHER* We are checking for virtual directories const CVirtualDirInfoList& DirInfoList = GetUserOptions().GetDirectoryList(); int iSize = DirInfoList.GetCount(); if(DirInfoList.GetCount() > 0) { POSITION Pos = DirInfoList.GetHeadPosition(); // For each user input directory for(int i=0; !IsThreadTerminating() && i 0) { POSITION Pos = URLList.GetHeadPosition(); for(int i=0; !IsThreadTerminating() && iLog(strLog); TRACE(_T("%s\n"), strLog); } // Load it ( with ReadFile ) int iRet = m_Loader.Load(Link, TRUE); // Set the load time in the object Link.SetTime(CTime::GetCurrentTime()); // Update the lookup table with this link m_Lookup.Add(Link.GetURL(), Link); } ASSERT(Link.GetState() != CLink::eUnit); // If the link is invalid, write to error log & return if(Link.GetState() == CLink::eInvalidHTTP || Link.GetState() == CLink::eInvalidWininet) { m_ErrLog.Write(Link); return; } // If the link is not a text file, nothing // to parse if(Link.GetContentType() != CLink::eText) { return; } if(m_pProgressLog) { CString strLog; strLog.Format(_T("%s %s"), strParsing_c, Link.GetURL()); m_pProgressLog->Log(strLog); TRACE(_T("%s\n"), strLog); } // Add the links in this html to the stack CLinkPtrList List; m_Parser.Parse(Link.GetData(), Link.GetURL(), List); // While the link stack is not empty while(!IsThreadTerminating() && List.GetCount() > 0) { // Pop a new link CLink* pLink = List.GetHead(); List.RemoveHead(); // If not found in the lookup table if(!m_Lookup.Get(pLink->GetURL(), *pLink)) { if(m_pProgressLog) { CString strLog; strLog.Format(_T("%s %s"), strLoading_c, pLink->GetURL()); m_pProgressLog->Log(strLog); TRACE(_T("%s\n"), strLog); } // Load it m_Loader.Load(*pLink, FALSE); // Set the load time in the object pLink->SetTime(CTime::GetCurrentTime()); // Update the lookup table with this link m_Lookup.Add(pLink->GetURL(), *pLink); } // Make sure all the links were initialized ASSERT(pLink->GetState() != CLink::eUnit); // If the link is invalid, write to error log & return if(pLink->GetState() == CLink::eInvalidHTTP || pLink->GetState() == CLink::eInvalidWininet) { m_ErrLog.Write(*pLink); } delete pLink; } } // CLinkCheckerMgr::CheckThisURL void CLinkCheckerMgr::ChangeBackSlash( LPTSTR lpsz ) /*++ Routine Description: Static functions for changing '\' to '/' in string Arguments: lpsz - input string pointer Return Value: N/A --*/ { lpsz = _tcschr(lpsz, _TUCHAR('\\')); while(lpsz != NULL) { lpsz[0] = _TCHAR('/'); lpsz = _tcschr(lpsz, _TUCHAR('\\')); } } // CLinkCheckerMgr::ChangeBackSlash void CLinkCheckerMgr::ChangeBackSlash( CString& str ) /*++ Routine Description: Static functions for changing '\' to '/' in string Arguments: str - input string Return Value: N/A --*/ { LPTSTR lpsz = str.GetBuffer(str.GetLength()); ChangeBackSlash(lpsz); str.ReleaseBuffer(); } // CLinkCheckerMgr::ChangeBackSlash