//************************************************************* // File name: PROQUOTA.C // // Description: Profile quota management // // Microsoft Confidential // Copyright (c) Microsoft Corporation 1996 // All rights reserved // //************************************************************* #include #include #include #include #include #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; }