Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1804 lines
46 KiB

//*************************************************************
// File name: PROQUOTA.C
//
// Description: Profile quota management
//
// Microsoft Confidential
// Copyright (c) Microsoft Corporation 1996
// All rights reserved
//
//*************************************************************
#include <windows.h>
#include <wchar.h>
#include <aclapi.h>
#include <shellapi.h>
#include <commctrl.h>
#include "proquota.h"
#include "debug.h"
#define WINLOGON_KEY TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon")
#define SYSTEM_POLICIES_KEY TEXT("Software\\Policies\\Microsoft\\Windows\\System")
HINSTANCE hInst;
HWND hwndMain;
HWND g_hQuotaDlg = NULL;
BOOL g_bHideSmallItems;
BOOL g_bShowReg = FALSE;
HANDLE hThread;
HANDLE hExitEvent;
HANDLE g_hQuotaDlgEvent;
DWORD g_dwProfileSize = 0;
DWORD g_dwProfileSizeTemp = 0;
DWORD g_dwMaxProfileSize = 10240; //KB
CRITICAL_SECTION g_cs;
HICON hIconGood, hIconCaution, hIconStop;
BOOL g_bQueryEndSession;
TCHAR g_szExcludeList[2*MAX_PATH];
TCHAR *g_lpQuotaMessage=NULL;
TCHAR szClassName[] = TEXT("proquota");
TCHAR szEventName[] = TEXT("proquota instance event");
TCHAR szSizeFormat[40];
BOOL g_bWarnUser = FALSE;
DWORD g_dwWarnUserTimeout = 15; // minutes
BOOL g_bWarningTimerRunning = FALSE;
BOOL g_bWarningDisplayed = FALSE;
//
// Function prototypes
//
LRESULT CALLBACK ProQuotaWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK QuotaDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL SetSecurity (void);
BOOL ReadRegistry (void);
BOOL ReadExclusionList();
VOID QuotaThread(HWND hWnd);
BOOL RecurseDirectory (LPTSTR lpDir, LPTSTR lpTop, HWND hLV, LPTSTR lpExcludeList);
BOOL EnumerateProfile (HWND hLV);
LPTSTR CheckSemicolon (LPTSTR lpDir);
LPTSTR CheckSlash (LPTSTR lpDir);
LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList);
//*************************************************************
//
// WinMain()
//
// Purpose: Entry point
//
// Parameters: hInstance - Instance handle
// hPrevInstance - Previous Instance
// lpCmdLine - Command line
// nCmdShow - ShowWindow flag
//
// Return: int
//
//*************************************************************
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, INT nCmdShow)
{
MSG msg;
WNDCLASS wc;
HANDLE hEvent;
//
// Verbose output
//
#if DBG
InitDebugSupport();
DebugMsg((DM_VERBOSE, TEXT("WinMain: Entering...")));
#endif
hInst = hInstance;
//
// Check if this app is already running
//
hEvent = OpenEvent (EVENT_ALL_ACCESS, FALSE, szEventName);
if (hEvent) {
DebugMsg((DM_VERBOSE, TEXT("WinMain: Proquota already running. Exiting...")));
CloseHandle (hEvent);
return 0;
}
hEvent = CreateEvent (NULL, TRUE, TRUE, szEventName);
g_hQuotaDlgEvent = CreateEvent (NULL, FALSE, TRUE, NULL);
if (!g_hQuotaDlgEvent) {
DebugMsg((DM_VERBOSE, TEXT("WinMain: Proquota Couldn't get prowquota dlg event, error %d..."), GetLastError()));
CloseHandle (hEvent);
return 0;
}
//
// Get the quota settings
//
if (!ReadRegistry()) {
DebugMsg((DM_VERBOSE, TEXT("WinMain: ReadRegistry returned FALSE. Exiting...")));
CloseHandle (hEvent);
return 0;
}
//
// Munge the access mask on the process token so taskmgr
// can't kill this app.
//
SetSecurity();
//
// Make sure proquota is the first one that is attempted to be shutdown
//
SetProcessShutdownParameters(0x3ff, 0);
//
// Initialize
//
InitializeCriticalSection (&g_cs);
InitCommonControls();
LoadString (hInst, IDS_SIZEFMT, szSizeFormat, 40);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)ProQuotaWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szClassName;
if (!RegisterClass(&wc)) {
DebugMsg((DM_WARNING, TEXT("WinMain: RegisterClass failed with %d"), GetLastError()));
CloseHandle (hEvent);
return 0;
}
//
// Create a hidden top level window so we get
// broadcasted messages.
//
hwndMain = CreateWindow(szClassName, NULL, WS_OVERLAPPED, 0, 0, 0, 0,
NULL, NULL, hInstance, NULL);
if (!hwndMain) {
DebugMsg((DM_WARNING, TEXT("WinMain: CreateWindow failed with %d"), GetLastError()));
CloseHandle (hEvent);
return 0;
}
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DebugMsg((DM_VERBOSE, TEXT("WinMain: Leaving...")));
CloseHandle (hEvent);
if (g_lpQuotaMessage) {
LocalFree(g_lpQuotaMessage);
g_lpQuotaMessage = NULL;
}
return (int)(msg.wParam);
}
//*************************************************************
//
// ProQuotaWndProc()
//
// Purpose: Window procedure
//
// Parameters: hWnd - Window handle
// message - Window message
// wParam - WPARAM
// lParam - LPARAM
//
//
// Return: LRESULT
//
//*************************************************************
LRESULT CALLBACK ProQuotaWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
DWORD dwThreadId;
switch (message) {
case WM_CREATE:
hIconGood = LoadIcon (hInst, MAKEINTRESOURCE(IDI_ICON));
hIconCaution = LoadIcon (hInst, MAKEINTRESOURCE(IDI_CAUTION));
hIconStop = LoadIcon (hInst, MAKEINTRESOURCE(IDI_STOP));
hExitEvent = CreateEvent (NULL, FALSE, FALSE, TEXT("PROQUOTA Exit Event"));
if (!hExitEvent) {
DebugMsg((DM_WARNING, TEXT("ProQuotaWndProc: Failed to create exit event with error %d"), GetLastError()));
return -1;
}
hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) QuotaThread,
(LPVOID) hWnd, CREATE_SUSPENDED, &dwThreadId);
if (!hThread) {
DebugMsg((DM_WARNING, TEXT("ProQuotaWndProc: Failed to create thread with error %d"), GetLastError()));
CloseHandle (hExitEvent);
return -1;
}
SetThreadPriority (hThread, THREAD_PRIORITY_IDLE);
ResumeThread (hThread);
break;
case WM_USER:
if (lParam == WM_LBUTTONDBLCLK) {
PostMessage (hWnd, WM_QUOTADLG, 0, 0);
}
#if DBG
if (lParam == WM_RBUTTONUP) {
DestroyWindow (hWnd);
}
#endif
break;
case WM_QUERYENDSESSION:
{
BOOL bLogoff;
//EnterCriticalSection (&g_cs);
bLogoff = (g_dwProfileSize <= g_dwMaxProfileSize);
//LeaveCriticalSection (&g_cs);
//
// If it is zero assume that it has not yet finished enumerating..
//
if (g_dwProfileSize == 0) {
bLogoff = FALSE;
DebugMsg((DM_VERBOSE, TEXT("ProQuotaWndProc: Recd QueryEnd Message before enumerating.")));
}
DebugMsg((DM_VERBOSE, TEXT("ProQuotaWndProc: Recd QueryEnd Message. Returning %s"), bLogoff?TEXT("TRUE"):TEXT("FALSE")));
if (bLogoff) {
return TRUE;
}
PostMessage (hWnd, WM_QUOTADLG, 1, 0);
}
return FALSE;
case WM_QUOTADLG:
if (!g_hQuotaDlg) {
if (wParam) {
g_bQueryEndSession = TRUE;
} else {
g_bQueryEndSession = FALSE;
}
DialogBox (hInst, MAKEINTRESOURCE(IDD_QUOTA), hwndMain, QuotaDlgProc);
g_hQuotaDlg = NULL;
}
break;
case WM_WARNUSER:
if (!g_bWarningDisplayed) {
TCHAR szTitle[100];
g_bWarningDisplayed = TRUE;
LoadString (hInst, IDS_MSGTITLE, szTitle, 100);
MessageBox(hWnd, g_lpQuotaMessage, szTitle, MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL);
g_bWarningDisplayed = FALSE;
}
break;
case WM_TIMER:
if (g_dwWarnUserTimeout > 0) {
PostMessage (hWnd, WM_WARNUSER, 0, 0);
}
break;
case WM_EXITWINDOWS:
ExitWindowsDialog(NULL);
break;
case WM_DESTROY:
{
NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = 1;
Shell_NotifyIcon (NIM_DELETE, &nid);
SetEvent (hExitEvent);
WaitForSingleObject (hThread, INFINITE);
CloseHandle (hExitEvent);
CloseHandle (hThread);
PostQuitMessage(0);
}
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return FALSE;
}
//*************************************************************
//
// QuotaThread()
//
// Purpose: Initializes the tray icon
//
// Parameters: hWnd - main window handle
//
//
// Return: TRUE if successful
// FALSE if an error occurs
//
//*************************************************************
VOID QuotaThread (HWND hWnd)
{
NOTIFYICONDATA nid;
TCHAR szProfile[MAX_PATH];
TCHAR szMessage[64];
HANDLE hFileChange;
HANDLE hRegChange;
HANDLE hWaitHandles[4];
BOOL bFirst = TRUE;
HICON hOk, hWarning, hBad;
DWORD dwDelta;
HKEY hKeySystem;
LONG lResult;
DWORD dwResult;
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Entering...")));
//
// Load the status icons
//
hOk = LoadImage (hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON,
16, 16, LR_DEFAULTCOLOR);
hWarning = LoadImage (hInst, MAKEINTRESOURCE(IDI_CAUTION), IMAGE_ICON,
16, 16, LR_DEFAULTCOLOR);
hBad = LoadImage (hInst, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON,
16, 16, LR_DEFAULTCOLOR);
//
// Get the profile directory
//
szProfile[0] = TEXT('\0');
GetEnvironmentVariable (TEXT("USERPROFILE"), szProfile, MAX_PATH);
if (szProfile[0] == TEXT('\0')) {
ExitThread (0);
}
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User's profile: <%s>"), szProfile));
//
// Setup change notify
//
hFileChange = FindFirstChangeNotification (szProfile, TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE);
if (hFileChange == INVALID_HANDLE_VALUE) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup file change notification. %d"),
GetLastError()));
ExitThread (0);
}
lResult = RegOpenKeyEx (HKEY_CURRENT_USER,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
0, KEY_READ, &hKeySystem);
if (lResult != ERROR_SUCCESS) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to open registry key. %d"), lResult));
ExitThread (0);
}
hRegChange = CreateEvent (NULL, FALSE, FALSE, TEXT("PROQUOTA reg change event"));
if (!hRegChange) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup reg event for change notification. %d"),
GetLastError()));
RegCloseKey (hKeySystem);
FindCloseChangeNotification (hFileChange);
ExitThread (0);
}
lResult = RegNotifyChangeKeyValue(hKeySystem, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
hRegChange, TRUE);
if (lResult != ERROR_SUCCESS) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup RegNotifyChangeKeyValue. %d"),
lResult));
CloseHandle (hRegChange);
RegCloseKey (hKeySystem);
FindCloseChangeNotification (hFileChange);
ExitThread (0);
}
hWaitHandles[0] = hExitEvent;
hWaitHandles[1] = hFileChange;
hWaitHandles[2] = hRegChange;
hWaitHandles[3] = g_hQuotaDlgEvent;
while (TRUE) {
//
// Calculate the profile size
//
if (g_hQuotaDlg) {
DebugMsg((DM_VERBOSE, TEXT("QuotaTHread: Enumerating profile and refreshing dialog")));
if (!EnumerateProfile (GetDlgItem (g_hQuotaDlg, IDC_QUOTA_FILELIST))) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: EnumerateProfile failed with Dlg Item.")));
break;
}
}
else {
if (!EnumerateProfile (NULL)) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: EnumerateProfile failed.")));
break;
}
}
//
// Update the status icon
//
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_USER;
szMessage[0] = TEXT('\0');
if (g_dwProfileSize > g_dwMaxProfileSize) {
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User has exceeded their profile quota.")));
nid.hIcon = hBad;
LoadString (hInst, IDS_SIZEBAD, szMessage, 64);
dwDelta = g_dwProfileSize - g_dwMaxProfileSize;
if (g_bWarnUser && !g_bWarningTimerRunning) {
g_bWarningTimerRunning = TRUE;
SetTimer (hwndMain, 1, g_dwWarnUserTimeout * 60000, NULL);
PostMessage (hwndMain, WM_WARNUSER, 0, 0);
}
} else if ( (g_dwMaxProfileSize - g_dwProfileSize) < (g_dwProfileSize * .10)) {
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User is within 10% of their profile quota.")));
nid.hIcon = hWarning;
LoadString (hInst, IDS_SIZEWARN, szMessage, 64);
dwDelta = g_dwMaxProfileSize - g_dwProfileSize;
if (g_bWarnUser && g_bWarningTimerRunning) {
KillTimer (hwndMain, 1);
g_bWarningTimerRunning = FALSE;
}
} else {
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User has space available in their profile quota.")));
nid.hIcon = hOk;
LoadString (hInst, IDS_SIZEOK, szMessage, 64);
dwDelta = g_dwMaxProfileSize - g_dwProfileSize;
if (g_bWarnUser && g_bWarningTimerRunning) {
KillTimer (hwndMain, 1);
g_bWarningTimerRunning = FALSE;
}
}
_snwprintf (nid.szTip, ARRAYSIZE(nid.szTip), szMessage, dwDelta);
if (bFirst) {
if (Shell_NotifyIcon (NIM_ADD, &nid)) {
bFirst = FALSE;
}
} else {
Shell_NotifyIcon (NIM_MODIFY, &nid);
}
//
// Notify the dialog if it's present
//
if (g_hQuotaDlg) {
PostMessage (g_hQuotaDlg, WM_REFRESH, 0, 0);
}
//
// Clean up and wait for the next change
//
FindNextChangeNotification (hFileChange);
dwResult = WaitForMultipleObjects (4, hWaitHandles, FALSE, INFINITE);
if (dwResult == WAIT_FAILED) {
break;
}
switch (dwResult - WAIT_OBJECT_0) {
case 0:
goto Exit;
break;
case 2:
EnterCriticalSection (&g_cs);
if (!ReadRegistry()) {
PostMessage (hwndMain, WM_DESTROY, 0, 0);
goto Exit;
}
LeaveCriticalSection (&g_cs);
RegNotifyChangeKeyValue(hKeySystem, FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
hRegChange, TRUE);
// fall through
case 1:
Sleep (2000);
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Running background enumeration.")));
break;
case 3:
break;
}
}
Exit:
RegCloseKey (hKeySystem);
CloseHandle (hRegChange);
FindCloseChangeNotification (hFileChange);
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Leaving...")));
ExitThread (0);
}
//*************************************************************
//
// SetSecurity()
//
// Purpose: Removes TERMINATE_PROCESS access to this process
// so taskman can't blow us away.
//
// Parameters:
//
// Return: TRUE if successful
// FALSE if an error occurs
//
//*************************************************************
BOOL SetSecurity (void)
{
HANDLE hProcess;
PACL pDACL;
PSECURITY_DESCRIPTOR pSD;
WORD wIndex;
ACE_HEADER * lpAceHeader;
ACCESS_ALLOWED_ACE * lpAce;
DWORD dwResult;
hProcess = GetCurrentProcess();
if (GetSecurityInfo (hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, &pDACL, NULL, &pSD) != ERROR_SUCCESS) {
return FALSE;
}
for (wIndex = 0; wIndex < pDACL->AceCount; wIndex++) {
if (GetAce(pDACL, wIndex, &lpAceHeader)) {
if (lpAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE) {
lpAce = (ACCESS_ALLOWED_ACE *) lpAceHeader;
lpAce->Mask &= ~(PROCESS_TERMINATE | WRITE_DAC | WRITE_OWNER);
}
}
}
dwResult = SetSecurityInfo (hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, pDACL, NULL);
LocalFree (pSD);
if (dwResult != ERROR_SUCCESS) {
return FALSE;
}
return TRUE;
}
//*************************************************************
//
// ReadExclusionList()
//
// Purpose: Checks if the profile quota policy is set,
// and if so gets the max profile size.
//
// Parameters: void
//
// Return: TRUE if profile quota is enabled
// FALSE if not
//
//*************************************************************
BOOL ReadExclusionList()
{
TCHAR szExcludeList2[MAX_PATH];
TCHAR szExcludeList1[MAX_PATH];
HKEY hKey;
DWORD dwSize, dwType;
//
// Check for a list of directories to exclude both user preferences
// and user policy
//
szExcludeList1[0] = TEXT('\0');
if (RegOpenKeyEx (HKEY_CURRENT_USER,
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
dwSize = sizeof(szExcludeList1);
RegQueryValueEx (hKey,
TEXT("ExcludeProfileDirs"),
NULL,
&dwType,
(LPBYTE) szExcludeList1,
&dwSize);
RegCloseKey (hKey);
}
szExcludeList2[0] = TEXT('\0');
if (RegOpenKeyEx (HKEY_CURRENT_USER,
SYSTEM_POLICIES_KEY,
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
dwSize = sizeof(szExcludeList2);
RegQueryValueEx (hKey,
TEXT("ExcludeProfileDirs"),
NULL,
&dwType,
(LPBYTE) szExcludeList2,
&dwSize);
RegCloseKey (hKey);
}
//
// Merge the user preferences and policy together
//
g_szExcludeList[0] = TEXT('\0');
if (szExcludeList1[0] != TEXT('\0')) {
CheckSemicolon(szExcludeList1);
lstrcpy (g_szExcludeList, szExcludeList1);
}
if (szExcludeList2[0] != TEXT('\0')) {
lstrcat (g_szExcludeList, szExcludeList2);
}
return TRUE;
}
//*************************************************************
//
// ReadQuotaMsg()
//
// Purpose: Reads the msg that needs to be displayed.
//
// Parameters: hKey - Handle to the open policy
//
// Return: TRUE if mesg could be read
// FALSE otherwise
//
//*************************************************************
BOOL ReadQuotaMsg(HKEY hKey)
{
DWORD dwType, dwSize, dwValue, dwErr;
if (g_lpQuotaMessage) {
LocalFree(g_lpQuotaMessage);
g_lpQuotaMessage = NULL;
}
dwSize = sizeof(TCHAR)*500;
g_lpQuotaMessage = LocalAlloc (LPTR, dwSize);
if (!g_lpQuotaMessage) {
DebugMsg((DM_WARNING, TEXT("ReadRegistry: Failed to allocate memory for msg with %d."), GetLastError()));
return FALSE;
}
dwErr = RegQueryValueEx (hKey, TEXT("ProfileQuotaMessage"), NULL,
&dwType, (LPBYTE) g_lpQuotaMessage, &dwSize);
if (dwErr == ERROR_MORE_DATA) {
LPTSTR lpTemp1;
//
// Go in again with a larger buffer
//
lpTemp1 = LocalReAlloc(g_lpQuotaMessage, dwSize,
LMEM_MOVEABLE | LMEM_ZEROINIT);
if (!lpTemp1) {
DebugMsg((DM_WARNING, TEXT("ReadRegistry: Failed to reallocate memory for msg with %d."), GetLastError()));
LocalFree(g_lpQuotaMessage);
g_lpQuotaMessage = NULL;
return FALSE;
}
g_lpQuotaMessage = lpTemp1;
dwErr = RegQueryValueEx (hKey, TEXT("ProfileQuotaMessage"), NULL,
&dwType, (LPBYTE) g_lpQuotaMessage, &dwSize);
}
//
// Load the default message otherwise
//
if (dwErr != ERROR_SUCCESS) {
dwSize = sizeof(TCHAR)*500;
LoadString (hInst, IDS_DEFAULTMSG, g_lpQuotaMessage, 500);
}
//
// if there is any message expand the environment variables in it.
//
//
if (*g_lpQuotaMessage) {
LPTSTR lpTemp1, lpTemp2;
dwSize = sizeof(TCHAR)*500;
lpTemp1 = LocalAlloc (LPTR, dwSize);
if (lpTemp1) {
dwSize = ExpandEnvironmentStrings (g_lpQuotaMessage, lpTemp1, 500);
if (dwSize <= 500)
lstrcpy (g_lpQuotaMessage, lpTemp1);
else {
lpTemp2 = LocalReAlloc(lpTemp1, dwSize*sizeof(TCHAR),
LMEM_MOVEABLE | LMEM_ZEROINIT);
if (lpTemp2) {
lpTemp1 = lpTemp2;
//
// go in with a larger buffer
//
ExpandEnvironmentStrings (g_lpQuotaMessage, lpTemp1, dwSize);
lpTemp2 = LocalReAlloc(g_lpQuotaMessage, dwSize*sizeof(TCHAR),
LMEM_MOVEABLE | LMEM_ZEROINIT);
if (lpTemp2) {
g_lpQuotaMessage = lpTemp2;
lstrcpy (g_lpQuotaMessage, lpTemp1);
} else {
DebugMsg((DM_WARNING, TEXT("ReadRegistry: Failed to resize msg buffer with %d.Not expanding env var"), GetLastError()));
}
}
else {
DebugMsg((DM_WARNING, TEXT("ReadRegistry: Failed to reallocate memory for tmp buffer with %d.Not expanding env var"), GetLastError()));
}
}
LocalFree (lpTemp1);
} else {
DebugMsg((DM_WARNING, TEXT("ReadRegistry: Failed to allocate memory for tmp buffer with %d.Not expanding env var"), GetLastError()));
}
}
return TRUE;
}
//*************************************************************
//
// ReadRegistry()
//
// Purpose: Checks if the profile quota policy is set,
// and if so gets the max profile size.
//
// Parameters: void
//
// Return: TRUE if profile quota is enabled
// FALSE if not
//
//*************************************************************
BOOL ReadRegistry (void)
{
LONG lResult;
HKEY hKey;
DWORD dwType, dwSize, dwValue, dwErr;
lResult = RegOpenKeyEx (HKEY_CURRENT_USER,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
0, KEY_READ, &hKey);
if (lResult == ERROR_SUCCESS) {
dwSize = sizeof(dwValue);
lResult = RegQueryValueEx (hKey, TEXT("EnableProfileQuota"), NULL,
&dwType, (LPBYTE) &dwValue, &dwSize);
if (lResult == ERROR_SUCCESS) {
if (dwValue) {
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Profile quotas are enabled.")));
dwSize = sizeof(g_dwMaxProfileSize);
RegQueryValueEx (hKey, TEXT("MaxProfileSize"), NULL,
&dwType, (LPBYTE) &g_dwMaxProfileSize, &dwSize);
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Max Profile Size: %d"), g_dwMaxProfileSize));
dwSize = sizeof(g_bShowReg);
RegQueryValueEx (hKey, TEXT("IncludeRegInProQuota"), NULL,
&dwType, (LPBYTE) &g_bShowReg, &dwSize);
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Show registry in file list: %s"),
g_bShowReg ? TEXT("TRUE") : TEXT("FALSE")));
dwSize = sizeof(g_bWarnUser);
RegQueryValueEx (hKey, TEXT("WarnUser"), NULL,
&dwType, (LPBYTE) &g_bWarnUser, &dwSize);
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Warn user when quota exceeded: %s"),
g_bWarnUser ? TEXT("TRUE") : TEXT("FALSE")));
if (g_bWarnUser) {
dwSize = sizeof(g_dwWarnUserTimeout);
if (RegQueryValueEx (hKey, TEXT("WarnUserTimeout"), NULL,
&dwType, (LPBYTE) &g_dwWarnUserTimeout, &dwSize) == ERROR_SUCCESS) {
if (g_dwWarnUserTimeout > 1440) {
g_dwWarnUserTimeout = 1440;
}
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: User warning reminder timeout: %d"), g_dwWarnUserTimeout));
}
}
//
// Now read the message that needs to be displayed
//
if (!ReadQuotaMsg(hKey)) {
RegCloseKey (hKey);
return FALSE;
}
if (ReadExclusionList()) {
RegCloseKey (hKey);
return TRUE;
}
else {
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Failed to read the ExclusionList")));
}
} else {
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Profile quotas are DISABLED.")));
}
} else {
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Failed to query EnableProfileQuota with error %d."), lResult));
}
RegCloseKey (hKey);
} else {
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Failed to open System policy key with error %d."), lResult));
}
return FALSE;
}
//*************************************************************
//
// CheckSlash()
//
// Purpose: Checks for an ending slash and adds one if
// it is missing.
//
// Parameters: lpDir - directory
//
// Return: Pointer to the end of the string
//
// Comments:
//
// History: Date Author Comment
// 6/19/95 ericflo Created
//
//*************************************************************
LPTSTR CheckSlash (LPTSTR lpDir)
{
DWORD dwStrLen;
LPTSTR lpEnd;
lpEnd = lpDir + lstrlen(lpDir);
if (*(lpEnd - 1) != TEXT('\\')) {
*lpEnd = TEXT('\\');
lpEnd++;
*lpEnd = TEXT('\0');
}
return lpEnd;
}
//*************************************************************
//
// CheckSemicolon()
//
// Purpose: Checks for an ending slash and adds one if
// it is missing.
//
// Parameters: lpDir - directory
//
// Return: Pointer to the end of the string
//
// Comments:
//
// History: Date Author Comment
// 6/19/95 ericlfo Created
//
//*************************************************************
LPTSTR CheckSemicolon (LPTSTR lpDir)
{
LPTSTR lpEnd;
lpEnd = lpDir + lstrlen(lpDir);
if (*(lpEnd - 1) != TEXT(';')) {
*lpEnd = TEXT(';');
lpEnd++;
*lpEnd = TEXT('\0');
}
return lpEnd;
}
//*************************************************************
//
// RecurseDirectory()
//
// Purpose: Recurses through the subdirectories counting the size.
//
// Parameters: lpDir - Directory
// lpTop - Top of the display name
// hLV - Listview window handle (optional)
// lpExcludeList - Null-termed list of dirs to be skipped (optional)
//
// Return: TRUE if successful
// FALSE if an error occurs
//
// Comments:
//
// History: Date Author Comment
// 1/30/96 ericflo Created
// 12/22/98 ushaji Added exclusionlist support
// Notes:
// The buffer size expected is MAX_PATH+4 for some internal processing
// We should fix this to be better post Win 2K.
//*************************************************************
BOOL RecurseDirectory (LPTSTR lpDir, LPTSTR lpTop, HWND hLV, LPTSTR lpExcludeList)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fd;
LPTSTR lpEnd, lpTemp;
BOOL bResult = TRUE;
BOOL bSkip;
//
// Setup the ending pointer
//
lpEnd = CheckSlash (lpDir);
//
// Append *.* to the source directory
//
lstrcpy(lpEnd, TEXT("*.*"));
//
// Search through the source directory
//
hFile = FindFirstFile(lpDir, &fd);
if (hFile == INVALID_HANDLE_VALUE) {
if ( (GetLastError() == ERROR_FILE_NOT_FOUND) ||
(GetLastError() == ERROR_PATH_NOT_FOUND) ) {
//
// bResult is already initialized to TRUE, so
// just fall through.
//
} else {
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: FindFirstFile for <%s> failed with %d."),
lpDir, GetLastError()));
bResult = FALSE;
}
goto RecurseDir_Exit;
}
do {
//
// Append the file / directory name to the working buffer
//
// skip the file if the path > MAX_PATH
if ((1+lstrlen(fd.cFileName)+lstrlen(lpDir)+lstrlen(TEXT("\\*.*"))) >= 2*MAX_PATH) {
continue;
}
lstrcpy (lpEnd, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//
// Check for "." and ".."
//
if (!lstrcmpi(fd.cFileName, TEXT("."))) {
continue;
}
if (!lstrcmpi(fd.cFileName, TEXT(".."))) {
continue;
}
//
// Check if this directory should be excluded
//
if (lpExcludeList) {
bSkip = FALSE;
lpTemp = lpExcludeList;
while (*lpTemp) {
if (lstrcmpi (lpTemp, lpDir) == 0) {
bSkip = TRUE;
break;
}
lpTemp += lstrlen (lpTemp) + 1;
}
if (bSkip) {
DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> due to exclusion list."),
lpDir));
continue;
}
}
//
// Found a directory.
//
// 1) Change into that subdirectory on the source drive.
// 2) Recurse down that tree.
// 3) Back up one level.
//
//
// Recurse the subdirectory
//
if (!RecurseDirectory(lpDir, lpTop, hLV, lpExcludeList)) {
bResult = FALSE;
goto RecurseDir_Exit;
}
} else {
//
// Found a file, add the filesize and put in the listview
// if appropriate.
//
g_dwProfileSizeTemp += fd.nFileSizeLow;
DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Profile Size <%d> after <%s> "), g_dwProfileSizeTemp,
fd.cFileName));
if (hLV) {
LV_ITEM lvi;
BOOL bAddItem = TRUE;
if ((lstrlen(fd.cFileName) >= 6) &&
(CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE,
TEXT("ntuser"), 6,
fd.cFileName, 6) == 2)) {
bAddItem = (g_bShowReg ? TRUE : FALSE);
}
if (bAddItem && g_bHideSmallItems && (fd.nFileSizeLow <= 2048)) {
bAddItem = FALSE;
}
if (bAddItem) {
TCHAR szSize[40];
DWORD dwFileSize;
INT iItem;
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
lvi.iItem = 0;
lvi.iSubItem = 0;
lvi.state = 0;
lvi.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
lvi.pszText = lpTop;
lvi.lParam = fd.nFileSizeLow;
iItem = ListView_InsertItem (hLV, &lvi);
if (fd.nFileSizeLow <= 1024) {
dwFileSize = 1;
} else {
dwFileSize = fd.nFileSizeLow / 1024;
}
_snwprintf (szSize, ARRAYSIZE(szSize), szSizeFormat, dwFileSize);
lvi.mask = LVIF_TEXT | LVIF_STATE;
lvi.iItem = iItem;
lvi.iSubItem = 1;
lvi.state = 0;
lvi.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
lvi.pszText = szSize;
lvi.lParam = fd.nFileSizeLow;
ListView_SetItem (hLV, &lvi);
}
}
}
//
// Find the next entry
//
} while (FindNextFile(hFile, &fd));
RecurseDir_Exit:
//
// Remove the file / directory name appended above
//
*lpEnd = TEXT('\0');
//
// Close the search handle
//
if (hFile != INVALID_HANDLE_VALUE) {
FindClose(hFile);
}
return bResult;
}
//*************************************************************
//
// CenterWindow()
//
// Purpose: Centers a window on the screen
//
// Parameters: hwnd - window handle to center
//
// Return: void
//
// Comments:
//
// History: Date Author Comment
// 2/21/96 ericflo Ported
//
//*************************************************************
void CenterWindow (HWND hwnd)
{
RECT rect;
LONG dx, dy;
LONG dxParent, dyParent;
LONG Style;
//
// Get window rect
//
GetWindowRect(hwnd, &rect);
dx = rect.right - rect.left;
dy = rect.bottom - rect.top;
//
// Get parent rect
//
Style = GetWindowLong(hwnd, GWL_STYLE);
if ((Style & WS_CHILD) == 0) {
//
// Return the desktop windows size (size of main screen)
//
dxParent = GetSystemMetrics(SM_CXSCREEN);
dyParent = GetSystemMetrics(SM_CYSCREEN);
} else {
HWND hwndParent;
RECT rectParent;
hwndParent = GetParent(hwnd);
if (hwndParent == NULL) {
hwndParent = GetDesktopWindow();
}
GetWindowRect(hwndParent, &rectParent);
dxParent = rectParent.right - rectParent.left;
dyParent = rectParent.bottom - rectParent.top;
}
//
// Center the child in the parent
//
rect.left = (dxParent - dx) / 2;
rect.top = (dyParent - dy) / 3;
//
// Move the child into position
//
SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, 0, 0, SWP_NOSIZE);
}
//*************************************************************
//
// QuotaDlgProc()
//
// Purpose: Quota dialog box
//
// Parameters: hDlg - Window handle
// message - Window message
// wParam - WPARAM
// lParam - LPARAM
//
// Return: TRUE if successful
// FALSE if an error occurs
//
//*************************************************************
LRESULT CALLBACK QuotaDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
TCHAR szBuffer[40];
TCHAR szSize[40];
HWND hLV;
LV_COLUMN col;
RECT rect;
INT cx;
HKEY hKey;
DWORD dwSize, dwType;
LPTSTR lpMessage;
switch (message) {
case WM_INITDIALOG:
hLV = GetDlgItem (hDlg, IDC_QUOTA_FILELIST);
//
// Add the columns to the listview
//
GetClientRect (hLV, &rect);
cx = (rect.right * 31) / 40;
LoadString (hInst, IDS_COLUMN1, szBuffer, 40);
col.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = cx;
col.pszText = szBuffer;
col.iSubItem = 0;
ListView_InsertColumn (hLV, 0, &col);
LoadString (hInst, IDS_COLUMN2, szBuffer, 40);
col.cx = rect.right - cx - GetSystemMetrics(SM_CYHSCROLL);
col.fmt = LVCFMT_RIGHT;
col.iSubItem = 1;
ListView_InsertColumn (hLV, 1, &col);
//
// Hide small items by default
//
g_bHideSmallItems = TRUE;
CheckDlgButton (hDlg, IDC_QUOTA_HIDESMALL, BST_CHECKED);
CenterWindow (hDlg);
SetForegroundWindow (hDlg);
// EnumerateProfile (GetDlgItem (hDlg, IDC_QUOTA_FILELIST));
dwSize = 500 * sizeof(TCHAR);
lpMessage = LocalAlloc (LPTR, dwSize);
if (!lpMessage)
break;
LoadString (hInst ,IDS_QUOTAENUMMSG, lpMessage, 500);
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, lpMessage);
if (g_dwProfileSize > g_dwMaxProfileSize) {
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconStop, 0);
} else if ( (g_dwMaxProfileSize - g_dwProfileSize) < (g_dwProfileSize * .10)) {
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconCaution, 0);
} else {
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconGood, 0);
}
//
// Setting the global value at the end QuotaThread is not trying
// to refresh the dialog etc. at the same time.
//
g_hQuotaDlg = hDlg;
SetEvent(g_hQuotaDlgEvent);
LocalFree (lpMessage);
break;
case WM_REFRESH:
//
// Popuplate the listview
//
//
// Set the size information
//
_snwprintf (szSize, ARRAYSIZE(szSize), szSizeFormat, g_dwProfileSize);
SetDlgItemText (hDlg, IDC_QUOTA_SIZE, szSize);
_snwprintf (szSize, ARRAYSIZE(szSize), szSizeFormat, g_dwMaxProfileSize);
SetDlgItemText (hDlg, IDC_QUOTA_MAXSIZE, szSize);
dwSize = 500 * sizeof(TCHAR);
lpMessage = LocalAlloc (LPTR, dwSize);
if (!lpMessage) {
break;
}
if (g_dwProfileSize > g_dwMaxProfileSize) {
//
// This messge is already read
//
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, g_lpQuotaMessage);
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconStop, 0);
} else if ( (g_dwMaxProfileSize - g_dwProfileSize) < (g_dwProfileSize * .10)) {
LoadString (hInst, IDS_CAUTION, lpMessage, 500);
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, lpMessage);
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconCaution, 0);
} else {
LoadString (hInst, IDS_LOGOFFOK, lpMessage, 500);
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, lpMessage);
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconGood, 0);
}
LocalFree (lpMessage);
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
g_hQuotaDlg = NULL;
if ((g_dwProfileSize < g_dwMaxProfileSize) && (g_bQueryEndSession) && (g_dwProfileSize != 0)) {
PostMessage (hwndMain, WM_EXITWINDOWS, 0, 0);
}
EndDialog(hDlg, TRUE);
return TRUE;
}
if (LOWORD(wParam) == IDC_QUOTA_HIDESMALL) {
g_bHideSmallItems = IsDlgButtonChecked (hDlg, IDC_QUOTA_HIDESMALL);
EnumerateProfile (GetDlgItem(hDlg, IDC_QUOTA_FILELIST));
}
break;
}
return FALSE;
}
//*************************************************************
//
// ListViewSortCallback()
//
// Purpose: List view callback function for sorting
//
// Parameters: lParam1 - lParam1
// lParam2 - lParam2
// lParamSort - Column id
//
// Return: -1, 0, 1
//
//*************************************************************
INT CALLBACK ListViewSortCallback (LPARAM lParam1, LPARAM lParam2,
LPARAM lParamSort)
{
if (lParam1 < lParam2) {
return 1;
} else if (lParam1 == lParam2) {
return 0;
} else {
return -1;
}
}
//*************************************************************
//
// ConvertExclusionList()
//
// Purpose: Converts the semi-colon profile relative exclusion
// list to fully qualified null terminated exclusion
// list
//
// Parameters: lpSourceDir - Profile root directory
// lpExclusionList - List of directories to exclude
//
// Return: List if successful
// NULL if an error occurs
//
//*************************************************************
LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList)
{
LPTSTR lpExcludeList = NULL, lpInsert, lpEnd, lpTempList;
LPCTSTR lpTemp, lpDir;
TCHAR szTemp[MAX_PATH];
DWORD dwSize = 2; // double null terminator
DWORD dwStrLen;
//
// Setup a temp buffer to work with
//
lstrcpy (szTemp, lpSourceDir);
lpEnd = CheckSlash (szTemp);
//
// Loop through the list
//
lpTemp = lpDir = lpExclusionList;
while (*lpTemp) {
//
// Look for the semicolon separator
//
while (*lpTemp && ((*lpTemp) != TEXT(';'))) {
lpTemp++;
}
//
// Remove any leading spaces
//
while (*lpDir == TEXT(' ')) {
lpDir++;
}
//
// Put the directory name on the temp buffer
//
lstrcpyn (lpEnd, lpDir, (int)(lpTemp - lpDir + 1));
//
// Add the string to the exclusion list
//
if (lpExcludeList) {
dwStrLen = lstrlen (szTemp) + 1;
dwSize += dwStrLen;
lpTempList = LocalReAlloc (lpExcludeList, dwSize * sizeof(TCHAR),
LMEM_MOVEABLE | LMEM_ZEROINIT);
if (!lpTempList) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to realloc memory with %d"), GetLastError()));
LocalFree (lpExcludeList);
lpExcludeList = NULL;
goto Exit;
}
lpExcludeList = lpTempList;
lpInsert = lpExcludeList + dwSize - dwStrLen - 1;
lstrcpy (lpInsert, szTemp);
} else {
dwSize += lstrlen (szTemp);
lpExcludeList = LocalAlloc (LPTR, dwSize * sizeof(TCHAR));
if (!lpExcludeList) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to alloc memory with %d"), GetLastError()));
goto Exit;
}
lstrcpy (lpExcludeList, szTemp);
}
//
// If we are at the end of the exclusion list, we're done
//
if (!(*lpTemp)) {
goto Exit;
}
//
// Prep for the next entry
//
lpTemp++;
lpDir = lpTemp;
}
Exit:
return lpExcludeList;
}
//*************************************************************
//
// EnumerateProfile()
//
// Purpose: Enumerates the profile for size and names
//
// Parameters: hLV - listview window handle (optional)
//
// Return: TRUE if successful
// FALSE if an error occurs
//
//*************************************************************
BOOL EnumerateProfile (HWND hLV)
{
TCHAR szProfile[2*MAX_PATH];
LPTSTR lpEnd;
BOOL bRetVal = FALSE;
LPTSTR lpExcludeList = NULL;
LVITEM item;
//
// Get the profile directory
//
szProfile[0] = TEXT('\0');
GetEnvironmentVariable (TEXT("USERPROFILE"), szProfile, MAX_PATH);
if (szProfile[0] == TEXT('\0')) {
ExitThread (0);
}
lpEnd = CheckSlash (szProfile);
//
// Claim the critical section
//
EnterCriticalSection (&g_cs);
if (hLV) {
ListView_DeleteAllItems (hLV);
}
//
// Get current profile size
//
g_dwProfileSizeTemp = 0;
//
// Convert the exclusionlist read from the registry to a Null terminated list
// readable by recursedirectory.
//
if (g_szExcludeList[0] != TEXT('\0'))
lpExcludeList = ConvertExclusionList (szProfile, g_szExcludeList);
else
lpExcludeList = NULL;
if (!RecurseDirectory (szProfile, lpEnd, hLV, lpExcludeList)) {
SendMessage (hLV, WM_SETREDRAW, TRUE, 0);
goto Exit;
}
g_dwProfileSize = g_dwProfileSizeTemp;
//
// Sort by size
//
ListView_SortItems (hLV, ListViewSortCallback, 1);
//
// Select the next item
//
item.mask = LVIF_STATE;
item.iItem = 0;
item.iSubItem = 0;
item.state = LVIS_SELECTED | LVIS_FOCUSED;
item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
SendMessage (hLV, LVM_SETITEMSTATE, 0, (LPARAM) &item);
//
// Convert to K
//
if (g_dwProfileSize < 1024) {
g_dwProfileSize = 1;
} else {
g_dwProfileSize /= 1024;
}
bRetVal = TRUE;
Exit:
//
// Release the critical section
//
LeaveCriticalSection (&g_cs);
return bRetVal;
}