//////////////////////////////////////////////////////////////////////////////
//
// TASKS.CPP / Tuneup
//
//  Microsoft Confidential
//  Copyright (c) Microsoft Corporation 1998
//  All rights reserved
//
//  7/98 - Jason Cohen (JCOHEN)
//
//////////////////////////////////////////////////////////////////////////////


// Internal include file(s).
//
#include "main.h"
#include "scm.h"


// Internal defince value(s).
//
#define TUNEUP_TASK_CI	0x00000001
#define SERVICE_CISVC	_T("CISVC")


// Internal data structure(s).
//
typedef struct _DEFTASKS
{
	INT		nPageID;			// Resource ID for the dialog used in the wizard page.
	LPTSTR	lpAppName;			// File name of the task to schedule.
	LPTSTR	lpParameters;		// Parameters for the application.
	LPTSTR	lpSetParam;			// Command line to execute for the settings button.
	DWORD	dwFlags;			// Default flags to use for the job.
	DWORD	dwOptions;			// Default tuneup options for the task.
	INT		nSchedule;			// The task schedule.
	INT		nTitle;				// Resource ID for the title of the wizard page.
	INT		nSubTitle;			// Resource ID for the subtitle of the wizard.
	INT		nDescription;		// Resource ID for the description on the wizard page.
	INT		nJobName;			// Resource ID for the string used for the job name of the task.
	INT		nJobComment;		// Resource ID for the string used for the job comment of the task.
	INT		nYesAction;			// Resource ID for the string used in the Yes option button.  Only for IDD_TASKS pages.
	INT		nNoAction;			// Resource ID for the string used in the No option button.  Only for IDD_TASKS pages.
	INT		nSummary;			// Resource ID for the string used in on the summary page.
	DWORD	nSpecial;			// Special identifier for tasks that require specific code.
} DEFTASKS, *PDEFTASKS, *LPDEFTASKS;


// Internal function prtotype(s).
//
static BOOL CALLBACK	ThirdPartyAddOn(HKEY, LPTSTR, LPARAM);
static LPTASKDATA		AllocateTaskData(LPTASKDATA *);



//
// External function(s).
//


LPTASKDATA CreateTasks(VOID)
{
	LPDEFTASKS	lpDefTask;
	LPTASKDATA	lpCurrent,
				lpHead = NULL;
	BOOL		bAdd;
	TCHAR		szPathBuffer[MAX_PATH];

	// This is how we now what the default tasks are.  You can simply
	// add any tasks to this list that you want.
	//
	DEFTASKS DefaultTasks[] =
	{
		//{ IDD_TASK,       _T("SMTIDY.EXE"),    _T(""),                _T(""),            DEFAULT_TASK_FLAG2,  0,            TASK_MONTHLY,    IDS_TITLE_SMTIDY,     IDS_SUBTITLE_SMTIDY,     IDS_DESC_SMTIDY,    IDS_TASK_SMTIDY,    IDS_CMT_SMTIDY,    IDS_TEXT_SMTIDY},
		{ IDD_TASK,       _T("SCANDISK.EXE"),  _T("/sagerun:0"),      _T("/sageset:0"),  DEFAULT_TASK_FLAG,   0,            TASK_WEEKLY,     IDS_TITLE_CHKDSK,     IDS_SUBTITLE_CHKDSK,     IDS_DESC_CHKDSK,    IDS_TASK_CHKDSK,    IDS_CMT_CHKDSK,    IDS_YES_CHKDSK,		IDS_NO_CHKDSK,		IDS_SUM_CHKDSK,    0},
		{ IDD_CLEANUP,    _T("CLEANMGR.EXE"),  _T("/sagerun:0"),      _T("/sageset:0"),  DEFAULT_TASK_FLAG2,  TASK_NOIDLE,  TASK_MONTHLY,    IDS_TITLE_CLEANUP,    IDS_SUBTITLE_CLEANUP,    IDS_DESC_CLEANUP,   IDS_TASK_CLEANUP,   IDS_CMT_CLEANUP,   0,					0,					IDS_SUM_CLEANUP,   0},
		{ IDD_TASK,       _T("TUNEUP.EXE"),    _T("/service:cisvc"),  NULL,              DEFAULT_TASK_FLAG,   0,            TASK_ONCE,       IDS_TITLE_CIDAEMON,   IDS_SUBTITLE_CIDAEMON,   IDS_DESC_CIDAEMON,  IDS_TASK_CIDAEMON,  IDS_CMT_CIDAEMON,  IDS_YES_CIDAEMON,	IDS_NO_CIDAEMON,	IDS_SUM_CIDAEMON,  TUNEUP_TASK_CI},
		//{ IDD_TASK,       _T("NTBACKUP.EXE"),  _T(""),                _T(""),            DEFAULT_TASK_FLAG,   0,            TASK_MONTHLY,    IDS_TITLE_BACKUP,     IDS_SUBTITLE_BACKUP,     IDS_TASK_BACKUP,    IDS_CMT_BACKUP,    IDS_TEXT_BACKUP},
		{ 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  // The first field of the last item must be zero.
	};

	// Loop through all the default tasks.
	//
	for (lpDefTask = DefaultTasks; lpDefTask->nPageID != 0; lpDefTask++)
	{
		// First make sure we need this page in case there is a condition where we
		// shouldn't display it (just for content indexing now).
		//
		switch (lpDefTask->nSpecial)
		{
			case TUNEUP_TASK_CI:
				bAdd = (IsUserAdmin() && !ServiceRunning(SERVICE_CISVC));
				break;
			default:
				bAdd = TRUE;
		}

		// Allocate the memory for this task's data structure.
		//
		if ( bAdd && (lpCurrent = AllocateTaskData(&lpHead)) )
		{
			//
			// Now we need to setup all the settings we need
			// to create this task's job in Task Scheduler.
			//
			// These structure items must be filled in:
			// lpFullPathName, lpParameters, lpSetParam,
			// lpJobName, lpComment, nSchedule, dwFlags,
			// dwOptions.
			//			

			// Get the full path and app name (lpFullPathName).  The default path for all the
			// default tasks is the system32 directory.
			//
			if ( GetSystemDirectory(szPathBuffer, sizeof(szPathBuffer)) )
			{
				lstrcat(szPathBuffer, _T("\\"));
				lstrcat(szPathBuffer, lpDefTask->lpAppName);
				if ( lpCurrent->lpFullPathName = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(szPathBuffer) + 1)) )
					lstrcpy(lpCurrent->lpFullPathName, szPathBuffer);
			}

			// Get the app name (lpParameters) from the default task structure.
			//
			if ( lpCurrent->lpParameters = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(lpDefTask->lpParameters) + 1)) )
				lstrcpy(lpCurrent->lpParameters, lpDefTask->lpParameters);


			// Get the settings button parameters.  We leave the settings name to null because
			// the default tasks use the same exe as the task, just with different command line parameters.
			//
			if ( ( lpDefTask->lpSetParam ) &&
			     ( lpCurrent->lpSetParam = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(lpDefTask->lpSetParam) + 1)) ) )
				lstrcpy(lpCurrent->lpSetParam, lpDefTask->lpSetParam);

			// Get the job name (lpJobName) from the string table.
			//
			lpCurrent->lpJobName = AllocateString(NULL, lpDefTask->nJobName);

			// Get the name of the job comment (lpComment) from the string table.
			//
			lpCurrent->lpComment = AllocateString(NULL, lpDefTask->nJobComment);

			// Set the schedule for this job (nSchedule).
			//
			lpCurrent->nSchedule = lpDefTask->nSchedule;

			// Set the default flags for this job (dwFlags).
			//
			lpCurrent->dwFlags = lpDefTask->dwFlags;

			// Set the default tuneup options for this job (dwOptions).
			//
			lpCurrent->dwOptions = lpDefTask->dwOptions;


			//
			// Now we are going to setup all the data to create
			// this task's wizard page in Tuneup.  All the task
			// structure items for the wizard must be filled in.
			//

			// Get the resource ID for the dialog.
			//
			lpCurrent->nPageID = lpDefTask->nPageID;

			// Get the other strings needed by the wizard.
			//
			lpCurrent->lpTitle = AllocateString(NULL, lpDefTask->nTitle);
			lpCurrent->lpSubTitle = AllocateString(NULL, lpDefTask->nSubTitle);
			lpCurrent->lpDescription = AllocateString(NULL, lpDefTask->nDescription);
			lpCurrent->lpYesAction = AllocateString(NULL, lpDefTask->nYesAction);
			lpCurrent->lpNoAction = AllocateString(NULL, lpDefTask->nNoAction);
			lpCurrent->lpSummary = AllocateString(NULL, lpDefTask->nSummary);
		}
	}

	// Now loop through all the third-party tasks.
	//
	RegEnumKeys(HKLM, g_szRegKeyAddOns, ThirdPartyAddOn, (LPARAM) &lpHead, FALSE);

	// Return the head of this list.
	//
	return lpHead;
}


VOID FreeTasks(LPTASKDATA lpTask)
{
	// Don't free NULL data (this is the last item).
	//
	if ( lpTask != NULL )
	{
		// First free the next guy in the list.
		//
		FreeTasks(lpTask->lpNext);

		// Free all the job data.  The FREE macro
		// will make sure that it isn't freeing NULL.
		//
		FREE(lpTask->lpFullPathName);
		FREE(lpTask->lpParameters);
		FREE(lpTask->lpSetName);
		FREE(lpTask->lpSetParam);
		FREE(lpTask->lpJobName);
		FREE(lpTask->lpComment);

		// Free all the wizard data.
		//
		FREE(lpTask->lpTitle);
		FREE(lpTask->lpSubTitle);
		FREE(lpTask->lpDescription);
		FREE(lpTask->lpYesAction);
		FREE(lpTask->lpNoAction);
		FREE(lpTask->lpSummary);

		// Now finally free the actuall task item.
		//
		FREE(lpTask);
	}
}


DWORD TasksScheduled(LPTASKDATA lpTasks)
{
	DWORD dwFound = 0;

	while ( lpTasks )
	{
		if ( !(g_dwFlags & TUNEUP_CUSTOM) || (lpTasks->dwOptions & TASK_SCHEDULED) )
			dwFound++;
		lpTasks = lpTasks->lpNext;
	}

	return dwFound;
}


static BOOL CALLBACK ThirdPartyAddOn(HKEY hKey, LPTSTR lpKey, LPARAM lParam)
{
	LPTASKDATA			lpCurrent;
	LPTSTR				lpBuffer,
						lpString;

	// Allocate the memory for this task's data structure.
	//
	if ( lpCurrent = AllocateTaskData((LPTASKDATA *) lParam) )
	{
		//
		// Now we need to setup all the settings we need
		// to create this task's job in Task Scheduler.
		//
		// These structure items must be filled in:
		// lpFullPathName, lpParameters, lpSetParam,
		// lpJobName, lpComment, nSchedule, dwFlags,
		// dwOptions.
		//			

		// Get the full path and app name (lpFullPathName).
		//
		lpCurrent->lpFullPathName = RegGetString(hKey, NULL, g_szRegValProgram);

		// Get the app name (lpParameters) from the registry.
		//
		lpCurrent->lpParameters = RegGetString(hKey, NULL, g_szRegValProgParam);

		// Get the settings button exe and parameters.
		//
		lpCurrent->lpSetName = RegGetString(hKey, NULL, g_szRegValSettings);
		lpCurrent->lpSetParam = RegGetString(hKey, NULL, g_szRegValSetParam);

		// Get the job name (lpJobName) from the registry.
		//
		lpCurrent->lpJobName = RegGetString(hKey, NULL, g_szRegValJobName);

		// Get the name of the job comment (lpComment) from the registry.
		//
		lpCurrent->lpComment = RegGetString(hKey, NULL, g_szRegValComment);

		// Set the schedule for this job (nSchedule).
		//
		if ( lpBuffer = RegGetString(hKey, NULL, g_szRegValSchedule) )
		{
			lpCurrent->nSchedule = *lpBuffer - _T('0');
			FREE(lpBuffer);
		}

		// Set the default flags for this job (dwFlags).
		//
		lpCurrent->dwFlags = DEFAULT_TASK_FLAG;		

		//
		// Now we are going to setup all the data to create
		// this task's wizard page in Tuneup.  All the task
		// structure items for the wizard must be filled in.
		//

		// Set the resource ID for the dialog.
		//
		lpCurrent->nPageID = IDD_TASK;

		// Get the other strings needed by the wizard.
		//
		lpCurrent->lpTitle = RegGetString(hKey, NULL, g_szRegValTitle);
		lpCurrent->lpSubTitle = RegGetString(hKey, NULL, g_szRegValSubtitle);
		lpCurrent->lpDescription = RegGetString(hKey, NULL, g_szRegValDescription);
		lpCurrent->lpSummary = RegGetString(hKey, NULL, g_szRegValSummary);

		// Get and hold the action text in case we need it.
		//
		lpString = RegGetString(hKey, NULL, REG_VAL_ACTION);

		// Get the yes action option text for the wizard page.
		//
		if ( (lpCurrent->lpYesAction = RegGetString(hKey, NULL, g_szRegValYesAction)) == NULL )
		{
			// Since the no specific registry key wasn't there, we can create the
			// string from the action text and the '&Yes, ' prefix.
			//
			if ( lpString && (lpBuffer = AllocateString(NULL, IDS_YES)) )
			{
				if ( lpCurrent->lpYesAction = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(lpString) + lstrlen(lpBuffer) + 1)) )
					wsprintf(lpCurrent->lpYesAction, _T("%s%s"), lpBuffer, lpString);
				FREE(lpBuffer);
			}
		}

		// Get the no action option text for the wizard page.
		//
		if ( (lpCurrent->lpNoAction = RegGetString(hKey, NULL, g_szRegValNoAction)) == NULL )
		{
			// Since the no specific registry key wasn't there, we can create the
			// string from the action text and the 'No, &don't ' prefix.
			//
			if ( lpString && (lpBuffer = AllocateString(NULL, IDS_NO)) )
			{
				if ( lpCurrent->lpNoAction = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(lpString) + lstrlen(lpBuffer) + 1)) )
					wsprintf(lpCurrent->lpNoAction, _T("%s%s"), lpBuffer, lpString);
				FREE(lpBuffer);
			}
		}

		// Free the action text if we allocated it (FREE macro checks for us).
		//
		FREE(lpString);

		// Now lets make sure there is enough info for this item.  If not we
		// need to free it so we don't display the page.
		//
		if ( ( lpCurrent->lpFullPathName == NULL ) ||
		     ( !(EXIST(lpCurrent->lpFullPathName)) ) ||
		     ( lpCurrent->nSchedule < TASK_ONCE ) ||
		     ( lpCurrent->nSchedule > TASK_YEARLY ) ||
		     ( lpCurrent->lpJobName == NULL) ||
		     ( lpCurrent->lpTitle == NULL ) ||
		     ( lpCurrent->lpYesAction == NULL ) ||
		     ( lpCurrent->lpNoAction == NULL ) )
		{
			// Passing in NULL to this function will cause it to
			// free the last item allocated.
			//
			AllocateTaskData(NULL);
		}
	}

	// Always return true so the enum will continue.
	//
	return TRUE;
}


static LPTASKDATA AllocateTaskData(LPTASKDATA * lpHead)
{
	// The current pointer needs to be static so that we can setup
	// the the next and back pointers after allocating the next
	// task data item.
	//
	static LPTASKDATA	lpCurrent	= NULL;
	LPTASKDATA			lpBuffer	= NULL;

	// lpHead can not be NULL.  If it is, then we want to free
	// the last page allocated (used so if we decide that the third
	// party program doesn't have enough info in the registry for
	// us to use it, we can remove it).
	//
	if ( lpHead == NULL )
	{
		// Make sure there is a last item to free.
		//
		if ( lpCurrent )
		{
			// Record the current lpCurrent pointer so we can free
			// it later.
			//
			lpBuffer = lpCurrent;

			// Back up the lpCurrent pointer to the previous item
			// and Null out its next pointer so that it doesn't
			// think that this item still exists.
			//
			lpCurrent = lpCurrent->lpBack;
			lpCurrent->lpNext = NULL;

			// Now free the orphaned last item.
			//
			FreeTasks(lpBuffer);
		}
		return NULL;
	}

	// Allocate the memory for this task's data structure.
	//
	if ( lpBuffer = (LPTASKDATA) MALLOC(sizeof(TASKDATA)) )
	{
		//
		// We need to setup the next and back pointers
		// for the doubly linked list.
		//

		// Record the new pointer in the next of the pervious (current)
		// data.
		//
		if ( *lpHead && lpCurrent )
		{
			lpCurrent->lpNext = lpBuffer;
			lpBuffer->lpBack = lpCurrent;
		}
		// Or the head if this is the first item.
		//
		else
			*lpHead = lpBuffer;

		// Then set the current pointer to the new buffer.
		//
		lpCurrent = lpBuffer;
	}

	return lpBuffer;
}


BOOL InitGenericTask(HWND hDlg)
{
	// Setup the "Yes" and "No" option string.
	//
	SetDlgItemText(hDlg, IDC_YES, g_CurrentTask->lpYesAction);
	SetDlgItemText(hDlg, IDC_DENY, g_CurrentTask->lpNoAction);

	// We can free these now, because we don't need them anymore.
	// The FREE macro also sets the pointer to NULL so we don't free
	// it again when we close the program.
	//
	FREE(g_CurrentTask->lpYesAction);
	FREE(g_CurrentTask->lpNoAction);

	// Disable the settings button if there is nothing for it to do.
	//
	if ( ( g_CurrentTask->lpSetName == NULL ) &&
	     ( g_CurrentTask->lpSetParam == NULL) )
		EnableWindow(GetDlgItem(hDlg, IDC_SETTING), FALSE);

	// Always return false.
	//
	return FALSE;
}