/*---------------------------------------------------------------------------
  File: Monitor.cpp  

  Comments: Functions to monitor the status of the DCT Agents.

  This involves spawning a thread which periodically reads the dispatch log,
  and scans the result directory for result files.

  (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  Proprietary and confidential to Mission Critical Software, Inc.

  REVISION LOG ENTRY
  Revision By: Christy Boles
  Revised on 03/15/99 15:43:35

 ---------------------------------------------------------------------------
*/


#include "StdAfx.h"
#include "Resource.h"
#include "Common.hpp"
#include "Err.hpp"
#include "UString.hpp"
#include "TNode.hpp"
#include "ServList.hpp"
#include "Globals.h"
#include "Monitor.h"
#include "ResStr.h"
#include <lm.h>  // to remove result share


//#import "\bin\McsVarSetMin.tlb" no_namespace , named_guids
//#import "\bin\DBManager.tlb" no_namespace, named_guids
#import "VarSet.tlb" no_namespace , named_guids rename("property", "aproperty")
#import "DBMgr.tlb" no_namespace, named_guids

//#include "..\Common\Include\McsPI.h"
#include "McsPI.h"
#include "McsPI_i.c"

#include "afxdao.h"

void LookForResults(WCHAR * dir = NULL);
void WaitForMoreResults(WCHAR * dir);
void ProcessResults(TServerNode * pServer, WCHAR const * directory, WCHAR const * filename);

GlobalData        gData;

DWORD __stdcall ResultMonitorFn(void * arg)
{
   WCHAR            logdir[MAX_PATH] = L"";
   BOOL             bFirstPassDone;

   CoInitialize(NULL);
   
   gData.GetFirstPassDone(&bFirstPassDone);

   // wait until the other monitoring thread has  a chance to build the server list,
   // so we can check for pre-existing input files before using the changenotify mechanism
   
   while ( ! bFirstPassDone || !*logdir )
   {
      Sleep(500);
      gData.GetFirstPassDone(&bFirstPassDone);
      gData.GetResultDir(logdir);
   }
   LookForResults(logdir);
   WaitForMoreResults(logdir);

   CoUninitialize();

   return 0;
}

void WaitForMoreResults(WCHAR * logdir)
{
   WCHAR                     resultWC[MAX_PATH];
   HANDLE                    hFind;
   DWORD                     rc = 0;
   BOOL                      bDone;
   long                      nIntervalSeconds;

   safecopy(resultWC,logdir);
      
   hFind = FindFirstChangeNotification(resultWC,FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);
   if ( hFind == INVALID_HANDLE_VALUE )
   {
      rc = GetLastError();
      return;
   }
   
   gData.GetDone(&bDone);
   gData.GetWaitInterval(&nIntervalSeconds);
   while (! bDone && !rc)
   {
      if ( WAIT_OBJECT_0 == WaitForSingleObject(hFind,nIntervalSeconds * 1000 ) )
      {
         LookForResults(logdir);
      }
      else
      {
         LookForResults(logdir);
      }
      if ( ! FindNextChangeNotification(hFind) )
      {
         rc = GetLastError();
      }
      gData.GetDone(&bDone);
      gData.GetWaitInterval(&nIntervalSeconds);
   }
   
   FindCloseChangeNotification(hFind);
}


void LookForResults(WCHAR * arglogdir)
{
   TNodeListEnum             e;
   TServerNode             * s;
   DWORD                     nInstalled = 0;
   DWORD                     nRunning = 0;
   DWORD                     nFinished = 0;
   DWORD                     nError = 0;
   HWND                      gListWnd;
   HWND                      gSummaryWnd;
   WCHAR                     logdir[MAX_PATH];

   if ( ! (arglogdir && *arglogdir) )
   {
      gData.GetResultDir(logdir);
   }
   else
   {
      safecopy(logdir,arglogdir);
   }
   
   for ( s = (TServerNode*)e.OpenFirst(gData.GetUnsafeServerList()) ; s ; gData.Lock(),s = (TServerNode*)e.Next(),gData.Unlock() )
   {
      if ( s->IsInstalled() )
         nInstalled++;
      if ( s->IsFinished() )
         nFinished++;
      if ( s->HasFailed() )
         nError++;

      
      // Check  jobs that were running but not finished
      if ( *s->GetJobFile() && s->IsRunning() )
      {
         nRunning++;
         // Look for results 
         WCHAR               resultWC[MAX_PATH];
         HANDLE              hFind;
         WIN32_FIND_DATA     fdata;
         WCHAR               sTime[32];

         if ( logdir[UStrLen(logdir)-1] == L'\\' )
         {
            swprintf(resultWC,L"%s%s.result",logdir,s->GetJobFile());
         }
         else
         {
            swprintf(resultWC,L"%s\\%s.result",logdir,s->GetJobFile());
         }
         hFind = FindFirstFile(resultWC,&fdata);
         
         s->SetTimeStamp(gTTime.FormatIsoLcl( gTTime.Now( NULL ), sTime ));
         
         if ( hFind != INVALID_HANDLE_VALUE )
         {
            // found something
            gData.Lock();
            if ( ! s->IsFinished() )
            {
               gData.Unlock();
               ProcessResults(s,logdir,fdata.cFileName);
            }
            else
            {
               gData.Unlock();
            }
            nRunning--;
            nFinished++;
            FindClose(hFind);
         }
         gData.GetListWindow(&gListWnd);
//         SendMessage(gListWnd,DCT_UPDATE_ENTRY,NULL,(long)s);
         SendMessage(gListWnd,DCT_UPDATE_ENTRY,NULL,(LPARAM)s);
      }
   }
   e.Close();
   // Update the summary window
   ComputerStats        stat;

   // get the total servers number
   gData.GetComputerStats(&stat);
   stat.numError = nError;
   stat.numFinished = nFinished;
   stat.numRunning = nRunning;
   stat.numInstalled = nInstalled;
   
   gData.SetComputerStats(&stat);

   gData.GetSummaryWindow(&gSummaryWnd);
   
//   SendMessage(gSummaryWnd,DCT_UPDATE_COUNTS,0,(long)&stat);
   SendMessage(gSummaryWnd,DCT_UPDATE_COUNTS,0,(LPARAM)&stat);
}

BOOL                                       // ret- TRUE if successful
   ReadResults(
      TServerNode          * pServer,      // in - pointer to server node containing server name 
      WCHAR          const * directory,    // in - directory where results files are stored
      WCHAR          const * filename,     // in - filename for this agent's job
      DetailStats          * pStats,       // out- counts of items processed by the agent
      CString              & plugInText,   // out- text results from plug-ins
      BOOL                   bStore        // in - bool, whether to store plug-in text
   )
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   WCHAR                     path[MAX_PATH];
   HRESULT                   hr = S_OK;
   BOOL                      bSuccess = FALSE;

   if ( directory[UStrLen(directory)-1] == '\\' )
   {
      swprintf(path,L"%ls%ls",directory,filename);
   }
   else
   {
      swprintf(path,L"%ls\\%ls",directory,filename);
   }

   // Read the varset data from the file
   IVarSetPtr             pVarSet;
   IStoragePtr            store = NULL;

   // Try to create the COM objects
   hr = pVarSet.CreateInstance(CLSID_VarSet);
   if ( SUCCEEDED(hr) )
   {
      
      // Read the VarSet from the data file
      int tries = 0;
      do 
      {
         tries++;
         hr = StgOpenStorage(path,NULL,STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,NULL,0,&store);
         if ( SUCCEEDED(hr) )
         {                  
            // Load the data into a new varset
            hr = OleLoad(store,IID_IUnknown,NULL,(void **)&pVarSet);
         }
         if ( tries > 2 ) 
            break;
         Sleep(500);
      } while ( hr == STG_E_SHAREVIOLATION || hr == STG_E_LOCKVIOLATION );

   }

   if ( SUCCEEDED(hr) )
   {
      pStats->directoriesChanged = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Directories_Changed));
      pStats->directoriesExamined = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Directories_Examined));
      pStats->directoriesUnchanged = (pStats->directoriesExamined - pStats->directoriesChanged);

      pStats->filesChanged = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Files_Changed));
      pStats->filesExamined = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Files_Examined));
      pStats->filesUnchanged = (pStats->filesExamined - pStats->filesChanged );

      pStats->sharesChanged = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Shares_Changed));
      pStats->sharesExamined = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Shares_Examined));
      pStats->sharesUnchanged = (pStats->sharesExamined - pStats->sharesChanged );

      pStats->membersChanged = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Members_Changed));
      pStats->membersExamined = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_Members_Examined));
      pStats->membersUnchanged = (pStats->membersExamined - pStats->membersChanged );

      pStats->rightsChanged = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_UserRights_Changed));
      pStats->rightsExamined = (long)pVarSet->get(GET_BSTR(DCTVS_Stats_UserRights_Examined));
      pStats->rightsUnchanged = (pStats->rightsExamined - pStats->rightsChanged );

      
      long           level = pVarSet->get(GET_BSTR(DCTVS_Results_ErrorLevel));
      _bstr_t        logfile = pVarSet->get(GET_BSTR(DCTVS_Results_LogFile));

      if ( level > 2 )
      {
         CString message;

         message.FormatMessage(IDS_SeeLogForAgentErrors_S,(WCHAR*)logfile);

         pServer->SetMessageText(message.GetBuffer(0));
      }
      pServer->SetSeverity(level);
         
      // build the UNC path for the log file
      WCHAR             logPath[MAX_PATH];
      
      swprintf(logPath,L"\\\\%s\\%c$\\%s",pServer->GetServer(),((WCHAR*)logfile)[0],((WCHAR*)logfile) + 3);

      pServer->SetLogPath(logPath);
      bSuccess = TRUE;
      
      // Try to get information from any plug-ins that ran
      // create the COM object for each plug-in
      _bstr_t                   bStrGuid;
      WCHAR                     key[300];
      CLSID                     clsid;

      for ( int i = 0 ; ; i++ )
      {
         swprintf(key,L"Plugin.%ld",i);
         bStrGuid = pVarSet->get(key);
      
         if ( bStrGuid.length() == 0 )
            break;

         IMcsDomPlugIn        * pPlugIn = NULL;
      
         hr = CLSIDFromString(bStrGuid,&clsid);
         if ( SUCCEEDED(hr) )
         {
            hr = CoCreateInstance(clsid,NULL,CLSCTX_ALL,IID_IMcsDomPlugIn,(void**)&pPlugIn);
            if ( SUCCEEDED(hr) )
            {
               BSTR           name = NULL;
               BSTR           result = NULL;
               
               hr = pPlugIn->GetName(&name);
               if ( SUCCEEDED(hr) )
               {
                  hr = pPlugIn->GetResultString(pVarSet,&result);
                  if ( SUCCEEDED(hr) )
                  {
                     plugInText += (WCHAR*)name;
                     plugInText += L"\n";
                     plugInText += (WCHAR*)result;
                     plugInText += L"\n\n";
                     SysFreeString(result);
                  }
                  SysFreeString(name);
                  if ( bStore )
                  {
                     pVarSet->put(L"LocalServer",pServer->GetServer());
                     pPlugIn->StoreResults(pVarSet);
                  }
               }
               pPlugIn->Release();
            }
         }
      }

   }
   else
   {
      CString  message;
      CString  title;

      if ( hr != STG_E_SHAREVIOLATION && hr != STG_E_LOCKVIOLATION )
      {
         message.FormatMessage(IDS_FailedToLoadResults,filename,hr);
         title.LoadString(IDS_MessageTitle);   
         if ( hr != STG_E_FILENOTFOUND )
            MessageBox(NULL,message,title,MB_OK | MB_ICONERROR);
      }
      else
      {
         // the agent has still not finished writing its results file, for some reason
         // we'll check it again later
         pServer->SetStatus(pServer->GetStatus() & ~Agent_Status_Finished);
      }
   }
   return bSuccess;
}

void 
   ProcessSecRefs(
      TServerNode          * pServer,
      WCHAR          const * directory,
      WCHAR          const * filename
   )
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   WCHAR                     path[MAX_PATH];
   DWORD                     rc = 0;
   BOOL                      bSuccess = FALSE;
   FILE                    * pFile;
   WCHAR                   * pDot;

   if ( directory[UStrLen(directory)-1] == '\\' )
   {
      swprintf(path,L"%ls%ls",directory,filename);
   }
   else
   {
      swprintf(path,L"%ls\\%ls",directory,filename);
   }
   // check to see if a secrefs file was written
   pDot = wcsrchr(path,L'.');
   if ( pDot )
   {
      UStrCpy(pDot,L".secrefs");
      pFile = _wfopen(path,L"rb");
      if ( pFile )
      {
         IIManageDBPtr        pDB;

         rc = pDB.CreateInstance(CLSID_IManageDB);
         if ( SUCCEEDED(rc) )
         {
            // there are some secrefs here, load them into the database
            WCHAR                account[300] = L"";
            WCHAR                type[100] = L"";
            DWORD                nOwner = 0;
            DWORD                nGroup = 0;
            DWORD                nDacl = 0;
            DWORD                nSacl = 0;
            WCHAR                domPart[300];
            WCHAR                acctPart[300];
            WCHAR                acctSid[300] = L"";
            WCHAR              * slash;
            CString              typeString;


			   //move past the UNICODE Byte Order Mark
			fgetwc(pFile);

			   //get entries
            while ( 7 == fwscanf(pFile,L"%[^,],%[^,],%[^,],%ld,%ld,%ld,%ld\r\n",account,acctSid,type,&nOwner,&nGroup,&nDacl,&nSacl) )
            {
         
               safecopy(domPart,account);
               slash = wcschr(domPart,L'\\');
               if ( slash )
               {
                  *slash = 0;
                  UStrCpy(acctPart,slash+1);
               }
               else
               {
                  domPart[0] = 0;
                  safecopy(acctPart,account);
               }

			      //for sIDs with no resolvable account, change domain and account to (Unknown)
			   if ((wcsstr(account, L"S-") == account) && (domPart[0] == 0))
			   {
				  wcscpy(acctPart, GET_STRING(IDS_UnknownSid));
				  wcscpy(domPart, GET_STRING(IDS_UnknownSid));
			   }

               typeString.FormatMessage(IDS_OwnerRef_S,type);
               rc = pDB->raw_AddAcctRef(domPart,acctPart,acctSid,pServer->GetServer(),nOwner,typeString.AllocSysString());
               
               typeString.FormatMessage(IDS_GroupRef_S,type);
               rc = pDB->raw_AddAcctRef(domPart,acctPart,acctSid,pServer->GetServer(),nGroup,typeString.AllocSysString());

                  //since local group members are not referenced in DACL, but we use that
			      //field to keep track of reference, use a different type string
			   if (!UStrCmp(type, GET_STRING(IDS_STReference_Member)))
			      typeString.FormatMessage(IDS_MemberRef_S);
			   else
			      typeString.FormatMessage(IDS_DACLRef_S,type);
               rc = pDB->raw_AddAcctRef(domPart,acctPart,acctSid,pServer->GetServer(),nDacl,typeString.AllocSysString());

               typeString.FormatMessage(IDS_SACLRef_S,type);
               rc = pDB->raw_AddAcctRef(domPart,acctPart,acctSid,pServer->GetServer(),nSacl,typeString.AllocSysString());

               // make sure there's not any data left over in these
               account[0] = 0;
               type[0] = 0;
			   acctSid[0] = 0;
               nOwner = 0;
               nGroup = 0;
               nDacl = 0;
               nSacl = 0;
            }
         }
         fclose(pFile);
      }
   }

}

void 
   ProcessResults(
      TServerNode          * pServer,
      WCHAR          const * directory,
      WCHAR          const * filename
   )
{
   HRESULT                   hr = S_OK;
   DetailStats               stats;
   HWND                      hWnd;
   CString                   PLText;

   memset(&stats,0,(sizeof stats));

   if ( ReadResults(pServer,directory,filename,&stats,PLText,TRUE) )
   {
      pServer->SetFinished();
      if ( ! pServer->HasFailed() && ! pServer->GetSeverity() )
      {
         pServer->SetMessageText(L"");
      }
      gData.AddDetailStats(&stats);
      gData.GetSummaryWindow(&hWnd);
     // get the stats for this job, and send them to the summary window
//      SendMessage(hWnd, DCT_UPDATE_TOTALS, 0, (long)&stats);
      SendMessage(hWnd, DCT_UPDATE_TOTALS, 0, (LPARAM)&stats);
      // also get import the security references
      ProcessSecRefs(pServer,directory,filename);
   }
}