#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <stdarg.h>
#include <time.h>
#include "network.h"
#include "idw_dbg.h"
#include "server.h"

/*++

   Filename :  idw_dbg.cpp

   Description: Contains the idwlog1.dbg idwlog2.dbg error logging functions.
   
   Created by:  Wally Ho

   History:     Created on 31/01/2000.
                Modified to TCHAR from my implementation in the MPK test suite.

	09.19.2001	Joe Holman	fixes for idwlog bugs 409338, 399178, and 352810
	10.03.2001	Joe Holman	make the log file report a 'w' instead of a 'a' fopen call.
	11.02.2001	Joe Holman	Added code to make a connection to ntburnlab2 with particular user so we
					can authenticate when a machine is not in the same domain thus allowing 
					the file copy of logs to succeed.
	11.12.2001	Joe Holman	Added language value to the output log.


   Contains these functions:

   1. VOID OpenIdwlogDebugFile(DWORD dwPart);
   2. VOID CloseIdwlogDebugFile(VOID);
   3. VOID RemoveIdwlogDebugFile(DWORD dwPart);
   4. VOID Idwlog (LPTSTR szMessage,...);

--*/

// Global
FILE* fpIdwlogDebugFile;

CONST DWORD MAX_SIZE_OF_DEBUG_FILE = 4000000;
 
static CONST LPTSTR IDWLOG_LOG          = TEXT("Idwlog.log");
static CONST LPTSTR IDWLOGSERVICE_LOG   = TEXT("IdwlogService.log");

TCHAR szSuiteMask[MAX_PATH*2];
TCHAR szProductType[MAX_PATH*2];

#define CLEAR_LOG	TRUE
#define APPEND_LOG	FALSE


VOID
MyLogger ( TCHAR * String, DWORD dwBuild, BOOL bClearLog, DWORD dwDelta ) 

/*++

Routine Description:
   This function does the following:

	- get's the computer name
	- tries to create the log directory for the build
	- opens the log file in append mode or overwrites
	- writes out the specified string
	- closes the log file

Arguments:

   String - this is the text that we want to copy over to the server
   dwBuild - the build # for the machine
   bClearLog - if TRUE, we fopen with "w" (zero out), if FALSE, we fopen with "a" (append)

Return Value:

   NONE

Notes:

   NONE

Author: Joe Holman (joehol) 09.20.2001

--*/

{ 
     
	FILE * stream;
        TCHAR szName[MAX_PATH];
        TCHAR szComputerName [ MAX_COMPUTERNAME_LENGTH + MAX_PATH] = "DefCompName";
        DWORD dwSize;


        dwSize = sizeof ( szComputerName );

        if ( GetComputerName ( szComputerName, &dwSize ) ) {

	    // Try to make the directory name.  This will only be succussful on the first instance for
	    // for this build, but we need to do the operation always to make sure it gets created.
	    //

	    if ( dwBuild == 2600 ) {	// For Service Pack builds we want to use minor and major build # also to differentiate.


		//	We are going to use the format of:
		//
		//		2600.1001   (for internal use, we don't need to worry about having the major and minor data.
		//

		_stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld.%ld"), dwBuild, dwDelta );

		CreateDirectory ( szName, NULL );

	    	_stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld.%ld\\%s"), dwBuild, dwDelta, szComputerName );

	    }
	    else {

	    	_stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld"), dwBuild );

		CreateDirectory ( szName, NULL );

	    	_stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld\\%s"), dwBuild, szComputerName );
            }

	    
	//    Idwlog ( TEXT("MyLogger szName = %s.\n"), szName );


            if ( (stream = _tfopen ( szName, (bClearLog?TEXT("w"):TEXT("a")) )) != NULL ) {  

                TCHAR szBuf[2*MAX_PATH];

                _stprintf ( szBuf, TEXT("%s"), String ); 

                if ( fwrite ( szBuf, 1, _tcsclen(szBuf), stream ) < 1 ) {
	
			Idwlog ( TEXT("MyLogger ERROR fwrite had an error writing.\n") );

		}

                fclose ( stream );

            } 
	    else {
                Idwlog ( TEXT("MyLogger ERROR fopen had a problem on (%s).\n"), szName );
	    }
        }
        else {

	 	Idwlog ( TEXT("MyLogger ERROR GetComputerName gle = %ld\n"), GetLastError() );
	}
}

TCHAR * ShowProductType ( DWORD dwProductType )

/*++

Routine Description:
   This function does the following:

	- determines what the product type is and returns it in string format

Arguments:

   dwProductType - the bit mask to examine

Return Value:

   Pointer to global string to display.

Notes:

   NONE

Author: Joe Holman (joehol) 09.20.2001

--*/
 {

	

	switch ( dwProductType ) {

	case  VER_NT_WORKSTATION :
		_tcscpy ( szProductType, TEXT("VER_NT_WORKSTATION") );
		break;

	case  VER_NT_DOMAIN_CONTROLLER :
		_tcscpy ( szProductType, TEXT("VER_NT_DOMAIN_CONTROLLER") );
		break;

	case  VER_NT_SERVER :
		_tcscpy ( szProductType, TEXT("VER_NT_SERVER") );
		break;

	
	default :

		strcpy ( szProductType, "ERRORUNKNOWNPRODUCTTYPE" );

	}

	return ( szProductType );

}


TCHAR * ShowSuiteMask ( DWORD dwSuiteMask ) 

/*++

Routine Description:
   This function does the following:

 	- examines the suite mask provided and appends each suite characteristic to the global string.

Arguments:

   dwSuiteMask - suite mask to do bit compares against.

Return Value:

   Pointer to global string to display.

Notes:

   NONE

Author: Joe Holman (joehol) 09.20.2001

--*/

{


	_tcscpy ( szSuiteMask, "  " );



	if ( dwSuiteMask & VER_SUITE_SMALLBUSINESS ) {

		_tcscat( szSuiteMask, TEXT("Small Business, ") );

	}

	if ( dwSuiteMask & VER_SUITE_BACKOFFICE ) {

		_tcscat( szSuiteMask, TEXT("BackOffice, ") );

	}

	if ( dwSuiteMask & VER_SUITE_COMMUNICATIONS ) {

		_tcscat( szSuiteMask, TEXT("Communications, ") );

	}

	if ( dwSuiteMask & VER_SUITE_TERMINAL ) {

		_tcscat( szSuiteMask, TEXT("Terminal, ") );

	}

	if ( dwSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED ) {

		_tcscat( szSuiteMask, TEXT("Small Business Restricted, ") );

	}

	if ( dwSuiteMask & VER_SUITE_EMBEDDEDNT ) {

		_tcscat( szSuiteMask, TEXT("Embedded NT, ") );

	}

	if ( dwSuiteMask & VER_SUITE_SINGLEUSERTS ) {

		_tcscat( szSuiteMask, TEXT("Supports Single User Terminal Service, ") );

	}

	if ( dwSuiteMask & VER_SUITE_PERSONAL ) {

		_tcscat( szSuiteMask, TEXT("Home Edition, ") );

	}

	if ( dwSuiteMask & VER_SUITE_DATACENTER ) {

		_tcscat( szSuiteMask, TEXT("Data Center, ") );

	}

	if ( dwSuiteMask & VER_SUITE_ENTERPRISE ) {

		_tcscat( szSuiteMask, TEXT("Enterprise(old Advanced Server), ") );

	}

	if ( dwSuiteMask & VER_SUITE_BLADE ) {

		_tcscat( szSuiteMask, TEXT("Blade, ") );

	}

	return ( szSuiteMask );
}



typedef struct _tagLANGINFO {
    LANGID LangID;
    INT    Count;
} LANGINFO,*PLANGINFO;

BOOL
CALLBACK
EnumLangProc(
    HANDLE hModule,     // resource-module handle
    LPCTSTR lpszType,   // pointer to resource type
    LPCTSTR lpszName,   // pointer to resource name
    WORD wIDLanguage,   // resource language identifier
    LONG_PTR lParam     // application-defined parameter
   )
/*++

Routine Description:

    Callback that counts versions stamps.

Arguments:

    Details of version enumerated version stamp. (Ignore.)

Return Value:

    Indirectly thru lParam: count, langID

--*/
{
    PLANGINFO LangInfo;

    LangInfo = (PLANGINFO) lParam;

    LangInfo->Count++;

    //
    // for localized build contains multiple resource, 
    // it usually contains 0409 as backup lang.
    //
    // if LangInfo->LangID != 0 means we already assigned an ID to it
    //
    // so when wIDLanguage == 0x409, we keep the one we got from last time 
    //
    if ((wIDLanguage == 0x409) && (LangInfo->LangID != 0)) {
        return TRUE;
    }

    LangInfo->LangID  = wIDLanguage;

    return TRUE;        // continue enumeration
}




VOID
CopySetupErrorLog ( LPINSTALL_DATA pID ) 

/*++

Routine Description:
   This function copies the machines SetupError.log file
   to one of our specified servers for later analysis.

   It will copy it to the following directory:

	\\ntburnlab2\joehol\logs\<build#>\<machinename>

Arguments:
   
   NONE

Return Value:

   NONE

Notes:

   This function should silently fail gracefully if any error
   is encountered.


Author: Joe Holman (joehol) 09.20.2001

--*/
{

	TCHAR szLog[2*MAX_PATH];
	TCHAR szWindowsDirectory[MAX_PATH];
	TCHAR Line[2*MAX_PATH];
	UINT  ui;
	FILE* fp;
	OSVERSIONINFOEX osv;
	BOOL  b;
	DWORD dwBuild = 0;
	NETRESOURCE NetResource;
	TCHAR       szRemoteName [MAX_PATH];
	TCHAR       szPassWord  [ MAX_PATH ];
	TCHAR       szUserId    [ MAX_PATH ];
	DWORD	dwError=0;
	LPCTSTR Type = (LPCTSTR) RT_VERSION;
	LPCTSTR Name = (LPCTSTR) 1;
	LANGINFO LangInfo;

	

	Idwlog ( TEXT("Entering CopySetupErrorLog.\n") );

	Idwlog ( TEXT("CopySetupErrorLog pID.szSystemBuildSourceLocation = %s\n"),     pID->szSystemBuildSourceLocation );
	Idwlog ( TEXT("CopySetupErrorLog pID.szInstallingBuildSourceLocation = %s\n"), pID->szInstallingBuildSourceLocation );

	Idwlog ( TEXT("CopySetupErrorLog g_InstallData.dwInstallingBuildDelta = %ld\n"), g_InstallData.dwInstallingBuildDelta );


	//	Get the windows directory for the machine.
	//

	ui = GetWindowsDirectory ( szWindowsDirectory, MAX_PATH );

	if ( ui == 0 ) {

		Idwlog ( TEXT("CopySetupErrorLog ERROR - GetWindowsDirectory gle = %ld\n"), GetLastError());
		return;	
	}
	

	//	Gain access to the machine via a user and password since a lot of machines are NOT
	//	setup on the same domain and thus cannot authenticate unless we specify this.
	//	We will log an error if this doesn't work, but we won't stop.
	//

	_stprintf(szRemoteName, TEXT("%s"), SETUPLOGS_MACH);
	_stprintf(szPassWord,   TEXT("%s"), SETUPLOGS_PW);
	_stprintf(szUserId,     TEXT("%s"), SETUPLOGS_USER);

	// Setup the memory for the connection.
	ZeroMemory( &NetResource, sizeof( NetResource ) );
	NetResource.dwType         = RESOURCETYPE_DISK ;
	NetResource.lpLocalName    = NULL;
	NetResource.lpRemoteName   = szRemoteName;
	NetResource.lpProvider     = NULL;

	_stprintf(szUserId, TEXT("%s"), SETUPLOGS_USER );

	dwError = WNetAddConnection2( &NetResource, szPassWord, szUserId, 0 );

	//Idwlog(TEXT("WNetAddConnection2 %s [dwError=%ld] using:  %s, %s, %s\n"), (dwError==NO_ERROR)?TEXT("OK"):TEXT("ERROR"), dwError, szRemoteName, szUserId, szPassWord );

	Idwlog(TEXT("WNetAddConnection2 %s [dwError=%ld] using:  %s\n"), (dwError==NO_ERROR)?TEXT("OK"):TEXT("ERROR"), dwError, szRemoteName );
    
	

	//	Write the header out to the log file.
	//
	//	Note: the first writes clear the log file.
	//

	ZeroMemory (&osv, sizeof(osv) ); 
    	osv.dwOSVersionInfoSize = sizeof ( OSVERSIONINFOEX );
    	b = GetVersionEx ( (OSVERSIONINFO * ) &osv );

    	if ( !b ) {

        	Idwlog ( TEXT("CopySetupErrorLog ERROR GetVersionEx FAILed, gle = %ld\n"), GetLastError());
		dwBuild = 0;
        	MyLogger ( TEXT("IdwLog Header (GetVersionEx ERROR.)\n"), dwBuild, CLEAR_LOG, g_InstallData.dwInstallingBuildDelta );
    	}
	else {

		dwBuild = osv.dwBuildNumber;

		Idwlog ( TEXT("CopySetupErrorLog retreived os version info. dwBuild = %ld\n"), dwBuild );
    
     		_stprintf ( szLog, TEXT ("IdwLog\nosv.dwOSVersionInfoSize = %x\nosv.dwMajorVersion = %d\nosv.dwMinorVersion = %d\nosv.dwBuildNumber = %d\nosv.dwPlatformId = %x\nosv.szCSDVersion = %s\nosv.wServicePackMajor = %x\nosv.wServicePackMinor = %x\nosv.wSuiteMask = %x (%s)\nosv.wProductType = %x (%s)\nszSystemBuildSource = %s\nszInstallingBuildSource = %s\n"), 
        	osv.dwOSVersionInfoSize, 
        	osv.dwMajorVersion, 
        	osv.dwMinorVersion, 
        	osv.dwBuildNumber, 
        	osv.dwPlatformId, 
        	osv.szCSDVersion, 
        	osv.wServicePackMajor, 
        	osv.wServicePackMinor, 
        	osv.wSuiteMask, 
		ShowSuiteMask (osv.wSuiteMask),
        	osv.wProductType,
		ShowProductType (osv.wProductType),
		pID->szSystemBuildSourceLocation,
		pID->szInstallingBuildSourceLocation
		  );

		MyLogger ( szLog, dwBuild, CLEAR_LOG, g_InstallData.dwInstallingBuildDelta) ;

	}


	//	Write out the language information.
	//
	//



	ZeroMemory(&LangInfo,sizeof(LangInfo));

	EnumResourceLanguages(
        	GetModuleHandle(TEXT("ntdll.dll")),
        	Type,
        	Name,
        	(ENUMRESLANGPROC) EnumLangProc,
        	(LONG_PTR) &LangInfo );


	_stprintf ( szLog, "LangInfo.LangID = %X\n\n", LangInfo.LangID );
	MyLogger ( szLog, dwBuild, APPEND_LOG, g_InstallData.dwInstallingBuildDelta );



	
	//	Open the setup error log.
	//

	_stprintf ( szLog, TEXT("%s\\setuperr.log"), szWindowsDirectory );

    	fp = _tfopen ( szLog, TEXT("r") );
    
        if ( fp == NULL ) {

		Idwlog ( TEXT("CopySetupErrorLog  ERROR Couldn't open log file:  %s\n"), szLog );
		return;
    	}
	
    	Idwlog ( TEXT("CopySetupErrorLog opened local setuperr.log file.\n") );

       	while ( _fgetts ( Line, MAX_PATH, fp ) ) {


		//	Prepend our tag text and write to the Server.
		//

            	_stprintf ( szLog, TEXT("SetupErr.Log ERROR:  %s"), Line );
            	MyLogger ( szLog, dwBuild, APPEND_LOG, g_InstallData.dwInstallingBuildDelta );
           

       	}

        
       	fclose ( fp );


	Idwlog ( TEXT("CopySetupErrorLog finished.\n") );


}

VOID
OpenIdwlogDebugFile(DWORD dwPart)
/*++

Routine Description:
   This will open a logfile for the dbg output for the idwlog
   The parameter it takes will let it write a *.log file for either
   Part1 or Part two of the tools execution.

Arguments:
   1 for service otherwise its idwlog.log.

Return Value:
   NONE

Author: Wally Ho (wallyho) Jan 31st, 2000

--*/
{

   TCHAR sztimeClock[128];
   TCHAR sztimeDate[128];
   TCHAR szmsg[MAX_PATH];
   TCHAR szIdwlogFile[30];
   TCHAR szIdwlogFileAndPath[100];
   TCHAR szSystemDrive[4];
   BOOL  bUseSysDrive;
   HANDLE hTestExistence;
   WIN32_FIND_DATA ffd;
   UINT    i;
   TCHAR szLogDirectoryToCreate[100] = TEXT("c:\\idwlog");


   fpIdwlogDebugFile = NULL;

   // Determine which part is the one we want.
   if (1 == dwPart){
      _tcscpy(szIdwlogFile,IDWLOGSERVICE_LOG );
   }
   else {
      _tcscpy(szIdwlogFile, IDWLOG_LOG );
   }

   // Do a look for where we wrote the file first.
   // The case is this: we install with a system on C drive.
   // We install to d: drive. D drive boots up; we write the dbg
   // on system root d:. This splits it from the initial write.
   // This will find it if its on C.
   // if it doesn't find it then we default to system drive.
   

   bUseSysDrive = TRUE;
   
   for (i= TEXT('c'); i <= TEXT('z'); i++){

      _stprintf ( szIdwlogFileAndPath, 
                  TEXT("%c:\\idwlog\\Idwlo*.dbg"), i);

      hTestExistence = FindFirstFile(szIdwlogFileAndPath, &ffd);

      if (INVALID_HANDLE_VALUE != hTestExistence){

         FindClose(hTestExistence);

         // Delete Old DBG files.
         //
         _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i, IDWLOGSERVICE_LOG );
         SetFileAttributes(szIdwlogFileAndPath,FILE_ATTRIBUTE_NORMAL);
         DeleteFile( szIdwlogFileAndPath);
         _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i, IDWLOG_LOG );
         SetFileAttributes(szIdwlogFileAndPath,FILE_ATTRIBUTE_NORMAL);
         DeleteFile( szIdwlogFileAndPath);
      }
   }
   
   for (i= TEXT('c'); i <= TEXT('z'); i++){

      _stprintf ( szIdwlogFileAndPath, 
                  TEXT("%c:\\idwlog\\%s"), i,szIdwlogFile);

      hTestExistence = FindFirstFile(szIdwlogFileAndPath, &ffd);
      
      if (INVALID_HANDLE_VALUE != hTestExistence){

	 //	We found a log file in this case here.
	 //

         bUseSysDrive = FALSE;
         FindClose(hTestExistence);
 
         _stprintf ( szIdwlogFileAndPath, 
                     TEXT("%c:\\idwlog\\%s"), i, szIdwlogFile);
         
         // Check for FileSize if Greater that 500,000 bytes then delete it.
         if (ffd.nFileSizeLow >= MAX_SIZE_OF_DEBUG_FILE ) {
            SetFileAttributes(szIdwlogFileAndPath,FILE_ATTRIBUTE_NORMAL);
            DeleteFile( szIdwlogFileAndPath);
         }
         break;
      }
   }

   if (TRUE == bUseSysDrive){

      //  Get the system Drive
      //
      if ( 0 == GetEnvironmentVariable(TEXT("SystemDrive"),szSystemDrive, 4)) {
         //
         // Default to C: (we're probably running on Win9x where there is
         // no SystemDrive envinronment variable)
         //
         _tcscpy(szSystemDrive, TEXT("C:"));
      }
      _stprintf(szIdwlogFileAndPath,TEXT("%s\\idwlog\\%s"),
                szSystemDrive,szIdwlogFile);


      _stprintf( szLogDirectoryToCreate, TEXT("%s\\idwlog"), szSystemDrive );	// new
   }
   else {
      _stprintf( szLogDirectoryToCreate, TEXT("%c\\idwlog"), szIdwlogFileAndPath[0] );	// new, szIdwlogFileAndPath filled out above.	
   }

   // We want to store the logs in the our idwlog directory from the root, in order to fix bug #352810 - on logon, non-admin can't write log.
   CreateDirectory ( szLogDirectoryToCreate, NULL );  // don't check return code since it will fail in some cases if exist.



   fpIdwlogDebugFile = _tfopen(szIdwlogFileAndPath,TEXT("a"));

   if(NULL == fpIdwlogDebugFile) {

      // nothing we can do if the logfile is not formed?

      //_tprintf ( TEXT("ERROR - Could not open log file:  %s\n"), szIdwlogFileAndPath );
      ExitProcess(GetLastError());
   } 

   _tstrtime(sztimeClock);
   _tstrdate(sztimeDate);
   _stprintf(szmsg,TEXT("[Started on %s %s]\n"), sztimeDate, sztimeClock); 
   _ftprintf( fpIdwlogDebugFile,TEXT("%s"), szmsg);

/***	This is too annoying to have it hidden, so I'm removing it.  JoeHol 09.17.2001
   if(FALSE == SetFileAttributes(szIdwlogFileAndPath, FILE_ATTRIBUTE_HIDDEN)) {
      Idwlog(TEXT("OpenIdwlogDebugFile ERROR - Could not set the debug file to Hidden.\n"));
   }
***/

   return; 
}

VOID
CloseIdwlogDebugFile(VOID)
/*++

Routine Description:
   This will close the logfile for the dbg output for the idwlog

Arguments:
   NONE

Return Value:
   NONE

Author: Wally Ho (wallyho) Jan 31st, 2000


--*/
{
    if ( NULL != fpIdwlogDebugFile){
      fclose(fpIdwlogDebugFile);
      fpIdwlogDebugFile = NULL;
    }
      
}


VOID
RemoveIdwlogDebugFile(DWORD dwPart)
/*++

Routine Description:
   This will remove the logfile for the dbg output for the idwlog

Arguments:
   NONE

Return Value:
   NONE

Author: Wally Ho (wallyho) Jan 31st, 2000


--*/
{

   TCHAR szIdwlogFile[50];
   TCHAR szIdwlogFileAndPath[100];
   HANDLE hTestExistence;
   WIN32_FIND_DATA ffd;
   UINT  i; 

   // Determine which part is the one we want.
   if (1 == dwPart){
      _tcscpy(szIdwlogFile, IDWLOGSERVICE_LOG );
   }
   else
      _tcscpy(szIdwlogFile, IDWLOG_LOG );


   // Search all drives and kill the idwlog.dbg file
   for (i= TEXT('c'); i <= TEXT('z'); i++){
      _stprintf ( szIdwlogFileAndPath, 
                  TEXT("%c:\\idwlog\\%s"), i,szIdwlogFile);

      hTestExistence = FindFirstFile(szIdwlogFileAndPath, &ffd);
      if (INVALID_HANDLE_VALUE != hTestExistence){
         FindClose(hTestExistence);
         _stprintf ( szIdwlogFileAndPath, 
                     TEXT("%c:\\idwlog\\%s"), i,szIdwlogFile);
         // Make sure the file is removed so we have a fresh file everytime.
         _tremove(szIdwlogFile);
         break;
      }
   }

}
  


VOID
Idwlog (LPTSTR szMessage,...)
/*++

Routine Description:
   This is the logging function for the idwlog.
   It behaves much like printf.

Arguments:
   same as printf.

Return Value:
   NONE

Author: Wally Ho (wallyho) Jan 31st, 2000

--*/
{
   va_list vaArgs;
   time_t t;
   TCHAR szTimeBuffer[30];

   if ( NULL != fpIdwlogDebugFile) {

      //  Write the time to the log.
      time(&t); 
      _stprintf ( szTimeBuffer, TEXT("%s"), ctime(&t) );

      // ctime addes a new line to the buffer. Erase it here.
      szTimeBuffer[_tcslen(szTimeBuffer) - 1] = TEXT('\0');

      _ftprintf( fpIdwlogDebugFile, TEXT("[%s] "),szTimeBuffer);  


      //  Write the formatted string to the log.
      va_start( vaArgs, szMessage );
      _vftprintf( fpIdwlogDebugFile, szMessage, vaArgs );
      va_end  ( vaArgs );
      // Flush the stream
      fflush(fpIdwlogDebugFile);
   }
}