Windows-Server-2003/ds/security/gina/proquota/proquota.c

2068 lines
55 KiB
C

//*************************************************************
// 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 <objbase.h>
#include <strsafe.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")
#define MAX_MESSAGE_LENGTH 256
HINSTANCE hInst;
HWND hwndMain;
HWND g_hQuotaDlg = NULL;
BOOL g_bHideSmallItems;
BOOL g_bShowReg = FALSE;
HANDLE g_hThread;
HANDLE g_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 + 2]; // "User exclusion list;Policy exclusion list"
TCHAR* g_lpQuotaMessage = NULL;
DWORD g_cbQuotaMessage = 0;
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, UINT cchBuffer, LPTSTR lpTop, HWND hLV, LPTSTR lpExcludeList);
BOOL EnumerateProfile (HWND hLV);
BOOL CheckSemicolon (LPTSTR lpDir, UINT cchBuffer);
LPTSTR CheckSlash (LPTSTR lpDir, UINT cchBuffer, UINT* pcchRemaining);
LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList);
BOOL GetDisplayName(LPCTSTR lpDir, LPTSTR lpTop, LPTSTR lpDisplayName, DWORD cchDisplayName);
//*************************************************************
//
// 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 = NULL;
int iRet = 0;
//
// 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...")));
goto Exit;
}
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()));
goto Exit;
}
//
// Get the quota settings
//
if (!ReadRegistry()) {
DebugMsg((DM_VERBOSE, TEXT("WinMain: ReadRegistry returned FALSE. Exiting...")));
goto Exit;
}
//
// 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
//
__try {
if(!InitializeCriticalSectionAndSpinCount(&g_cs, 0x80000000)) {
DebugMsg((DM_WARNING, TEXT("WinMain: InitializeCriticalSectionAndSpinCount failed with %d"), GetLastError()));
goto Exit;
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DebugMsg((DM_WARNING, TEXT("WinMain: InitializeCriticalSection failed")));
goto Exit;
}
InitCommonControls();
CoInitialize(NULL);
LoadString (hInst, IDS_SIZEFMT, szSizeFormat, ARRAYSIZE(szSizeFormat));
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = 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()));
goto Exit;
}
//
// 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()));
goto Exit;
}
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
iRet = (int)(msg.wParam);
Exit:
DebugMsg((DM_VERBOSE, TEXT("WinMain: Leaving...")));
if (hEvent) {
CloseHandle (hEvent);
}
if (g_hQuotaDlgEvent) {
CloseHandle(g_hQuotaDlgEvent);
}
if (g_lpQuotaMessage) {
LocalFree(g_lpQuotaMessage);
g_lpQuotaMessage = NULL;
}
return iRet;
}
//*************************************************************
//
// 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));
g_hExitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!g_hExitEvent) {
DebugMsg((DM_WARNING, TEXT("ProQuotaWndProc: Failed to create exit event with error %d"), GetLastError()));
return -1;
}
g_hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) QuotaThread,
(LPVOID) hWnd, CREATE_SUSPENDED, &dwThreadId);
if (!g_hThread) {
DebugMsg((DM_WARNING, TEXT("ProQuotaWndProc: Failed to create thread with error %d"), GetLastError()));
CloseHandle (g_hExitEvent);
return -1;
}
SetThreadPriority (g_hThread, THREAD_PRIORITY_IDLE);
ResumeThread (g_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, ARRAYSIZE(szTitle));
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 (g_hExitEvent);
WaitForSingleObject (g_hThread, INFINITE);
CloseHandle (g_hExitEvent);
CloseHandle (g_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[MAX_MESSAGE_LENGTH];
HANDLE hFileChange = INVALID_HANDLE_VALUE;
HANDLE hRegChange = NULL;
HANDLE hWaitHandles[4];
BOOL bFirst = TRUE;
HICON hOk = NULL, hWarning = NULL, hBad = NULL;
DWORD dwDelta;
HKEY hKeySystem = NULL;
LONG lResult;
DWORD dwResult;
HRESULT hr;
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Entering...")));
//
// Load the status icons
//
hOk = LoadImage (hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON,
16, 16, LR_DEFAULTCOLOR);
if (!hOk) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to load OK icon. Error %d"), GetLastError()));
goto Exit;
}
hWarning = LoadImage (hInst, MAKEINTRESOURCE(IDI_CAUTION), IMAGE_ICON,
16, 16, LR_DEFAULTCOLOR);
if (!hWarning) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to load Warning icon. Error %d"), GetLastError()));
goto Exit;
}
hBad = LoadImage (hInst, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON,
16, 16, LR_DEFAULTCOLOR);
if (!hBad) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to load stop icon. Error %d"), GetLastError()));
goto Exit;
}
//
// Get the profile directory
//
szProfile[0] = TEXT('\0');
GetEnvironmentVariable (TEXT("USERPROFILE"), szProfile, ARRAYSIZE(szProfile));
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()));
goto Exit;
}
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));
goto Exit;
}
hRegChange = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hRegChange) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup reg event for change notification. %d"),
GetLastError()));
goto Exit;
}
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));
goto Exit;
}
hWaitHandles[0] = g_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, ARRAYSIZE(szMessage));
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, ARRAYSIZE(szMessage));
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, ARRAYSIZE(szMessage));
dwDelta = g_dwMaxProfileSize - g_dwProfileSize;
if (g_bWarnUser && g_bWarningTimerRunning) {
KillTimer (hwndMain, 1);
g_bWarningTimerRunning = FALSE;
}
}
hr = StringCchPrintf(nid.szTip, ARRAYSIZE(nid.szTip), szMessage, dwDelta);
if (FAILED(hr)) {
DebugMsg((DM_WARNING, TEXT("QuotaThread: Insufficient buffer for message. Error 0x%x"), hr));
continue;
}
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:
if (hOk) {
DestroyIcon(hOk);
}
if (hWarning) {
DestroyIcon(hWarning);
}
if (hBad) {
DestroyIcon(hBad);
}
if (hKeySystem) {
RegCloseKey (hKeySystem);
}
if (hRegChange) {
CloseHandle (hRegChange);
}
if (INVALID_HANDLE_VALUE != hFileChange) {
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+1];
TCHAR szExcludeList1[MAX_PATH+1];
HKEY hKey;
DWORD dwSize, dwType;
HRESULT hr;
//
// 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);
if (RegQueryValueEx (hKey,
TEXT("ExcludeProfileDirs"),
NULL,
&dwType,
(LPBYTE) szExcludeList1,
&dwSize) != ERROR_SUCCESS) {
// ignore user exclusion list
szExcludeList1[0] = TEXT('\0');
}
RegCloseKey (hKey);
}
szExcludeList2[0] = TEXT('\0');
if (RegOpenKeyEx (HKEY_CURRENT_USER,
SYSTEM_POLICIES_KEY,
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
dwSize = sizeof(szExcludeList2);
if (RegQueryValueEx (hKey,
TEXT("ExcludeProfileDirs"),
NULL,
&dwType,
(LPBYTE) szExcludeList2,
&dwSize) != ERROR_SUCCESS) {
// ignore policy exclusion list
szExcludeList2[0] = TEXT('\0');
}
RegCloseKey (hKey);
}
//
// Merge the user preferences and policy together
//
g_szExcludeList[0] = TEXT('\0');
if (szExcludeList1[0] != TEXT('\0')) {
hr = StringCchCopy(g_szExcludeList, ARRAYSIZE(g_szExcludeList), szExcludeList1);
if (FAILED(hr)) {
DebugMsg((DM_WARNING, TEXT("ReadExclusionList: Fail to copy user exclusion list. Error 0x%x"), hr));
return FALSE;
}
if (!CheckSemicolon(g_szExcludeList, ARRAYSIZE(g_szExcludeList))) {
return FALSE;
}
}
if (szExcludeList2[0] != TEXT('\0')) {
hr = StringCchCat(g_szExcludeList, ARRAYSIZE(g_szExcludeList), szExcludeList2);
if (FAILED(hr)) {
DebugMsg((DM_WARNING, TEXT("ReadExclusionList: Fail to copy policy exclusion list. Error 0x%x"), hr));
return FALSE;
}
}
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;
BOOL bLoadDefault = FALSE;
if (!g_lpQuotaMessage) {
g_cbQuotaMessage = MAX_PATH * sizeof(TCHAR);
g_lpQuotaMessage = LocalAlloc (LPTR, g_cbQuotaMessage);
if (!g_lpQuotaMessage) {
g_cbQuotaMessage = 0;
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to allocate memory for msg with %d."), GetLastError()));
return FALSE;
}
}
//
// Query the size for quota message
//
dwErr = RegQueryValueEx (hKey, TEXT("ProfileQuotaMessage"), NULL,
&dwType, NULL, &dwSize);
if (dwErr != ERROR_SUCCESS) {
//
// Load the default message otherwise
//
bLoadDefault = TRUE;
goto Load_Default;
}
// Ensure proper size
if (dwSize > g_cbQuotaMessage) {
if (g_lpQuotaMessage) {
LocalFree(g_lpQuotaMessage);
}
g_cbQuotaMessage = dwSize;
g_lpQuotaMessage = LocalAlloc(LPTR, g_cbQuotaMessage);
if (!g_lpQuotaMessage) {
g_cbQuotaMessage = 0;
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to allocate memory for msg with %d."), GetLastError()));
return FALSE;
}
}
dwSize = g_cbQuotaMessage;
dwErr = RegQueryValueEx (hKey, TEXT("ProfileQuotaMessage"), NULL,
&dwType, (LPBYTE) g_lpQuotaMessage, &dwSize);
if (dwErr != ERROR_SUCCESS) {
//
// Load the default message otherwise
//
bLoadDefault = TRUE;
goto Load_Default;
}
//
// if there is any message expand the environment variables in it.
//
if (*g_lpQuotaMessage) {
DWORD cchSize;
LPTSTR lpTemp;
//
// Get the size of expanded string buffer including NULL terminator
//
cchSize = ExpandEnvironmentStrings (g_lpQuotaMessage, NULL, 0);
if (cchSize == 0) {
//
// Load the default message otherwise
//
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to expand env var"), GetLastError()));
bLoadDefault = TRUE;
goto Load_Default;
}
lpTemp = LocalAlloc (LPTR, cchSize * sizeof(TCHAR));
if (lpTemp) {
cchSize = ExpandEnvironmentStrings (g_lpQuotaMessage, lpTemp, cchSize);
if (cchSize == 0) {
//
// Load the default message otherwise
//
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to expand env var"), GetLastError()));
bLoadDefault = TRUE;
goto Load_Default;
}
if (cchSize * sizeof(TCHAR) > g_cbQuotaMessage) {
LocalFree(g_lpQuotaMessage);
g_lpQuotaMessage = lpTemp;
g_cbQuotaMessage = cchSize * sizeof(TCHAR);
}
else {
if (FAILED(StringCchCopy(g_lpQuotaMessage, g_cbQuotaMessage/sizeof(TCHAR), lpTemp))) {
bLoadDefault = TRUE;
}
LocalFree(lpTemp);
}
}
else {
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to allocate memory for tmp buffer with %d.Not expanding env var"), GetLastError()));
}
}
Load_Default:
if (bLoadDefault) {
//
// Load the default message in case of error
//
LoadString (hInst, IDS_DEFAULTMSG, g_lpQuotaMessage, g_cbQuotaMessage/sizeof(TCHAR));
}
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
// cchBuffer - Buffer size in char
// pcchRemaining - buffer remaining after adding '\',
// can be NULL if not required
//
// Return: Pointer to the end of the string
//
// Comments:
//
// History: Date Author Comment
// 6/19/95 ericflo Created
//
//*************************************************************
LPTSTR CheckSlash (LPTSTR lpDir, UINT cchBuffer, UINT* pcchRemaining)
{
UINT cchDir = lstrlen(lpDir);
LPTSTR lpEnd;
lpEnd = lpDir + cchDir;
if (pcchRemaining) {
*pcchRemaining = cchBuffer - cchDir - 1;
}
if (*(lpEnd - 1) != TEXT('\\')) {
if (cchDir + 1 >= cchBuffer) { // No space to put \, should never happen
return NULL;
}
*lpEnd = TEXT('\\');
lpEnd++;
*lpEnd = TEXT('\0');
if (pcchRemaining)
*pcchRemaining -= 1;
}
return lpEnd;
}
//*************************************************************
//
// CheckSemicolon()
//
// Purpose: Checks for an ending slash and adds one if
// it is missing.
//
// Parameters: lpDir - directory
// cchSize - buffer size in character
//
// Return: TRUE - success
// FALSE - Insufficient buffer space
//
// Comments:
//
// History: Date Author Comment
// 6/19/95 ericlfo Created
//
//*************************************************************
BOOL CheckSemicolon (LPTSTR lpDir, UINT cchBuffer)
{
UINT cchDir = lstrlen(lpDir);
LPTSTR lpEnd;
lpEnd = lpDir + cchDir;
if (*(lpEnd - 1) != TEXT(';')) {
if (cchDir + 1 >= cchBuffer) {
return FALSE; // No space to put ;, should never happen
}
*lpEnd = TEXT(';');
lpEnd++;
*lpEnd = TEXT('\0');
}
return TRUE;
}
//*************************************************************
//
// RecurseDirectory()
//
// Purpose: Recurses through the subdirectories counting the size.
//
// Parameters: lpDir - Directory
// cchBuffer - Buffer size in char
// 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, UINT cchBuffer, LPTSTR lpTop, HWND hLV, LPTSTR lpExcludeList)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fd;
LPTSTR lpEnd, lpTemp;
BOOL bResult = TRUE;
BOOL bSkip;
UINT cchRemaining;
HRESULT hr;
//
// Setup the ending pointer
//
lpEnd = CheckSlash (lpDir, cchBuffer, &cchRemaining);
if (!lpEnd) {
return FALSE;
}
//
// Append *.* to the source directory
//
hr = StringCchCopy(lpEnd, cchRemaining, TEXT("*.*"));
if (FAILED(hr)) {
bResult = FALSE;
goto RecurseDir_Exit;
}
//
// 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 ((UINT)(1+lstrlen(fd.cFileName)+lstrlen(lpDir)+lstrlen(TEXT("\\*.*"))) >= cchBuffer) {
continue;
}
hr = StringCchCopy(lpEnd, cchRemaining, fd.cFileName);
if (FAILED(hr)) {
bResult = FALSE;
goto RecurseDir_Exit;
}
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//
// Check for "." and ".."
//
if (!lstrcmpi(fd.cFileName, TEXT("."))) {
continue;
}
if (!lstrcmpi(fd.cFileName, TEXT(".."))) {
continue;
}
//
// Check for reparse point
//
if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: Found a reparse point <%s>, skip it!"), lpDir));
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, cchBuffer, lpTop, hLV, lpExcludeList))
{
// Ignore error and continue
DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> due to error."), lpDir));
}
} 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];
TCHAR szDisplayName[MAX_PATH*3];
DWORD dwFileSize;
INT iItem;
if (fd.nFileSizeLow <= 1024) {
dwFileSize = 1;
} else {
dwFileSize = fd.nFileSizeLow / 1024;
}
hr = StringCchPrintf(szSize, ARRAYSIZE(szSize), szSizeFormat, dwFileSize);
if (FAILED(hr)) {
bResult = FALSE;
goto RecurseDir_Exit;
}
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 = GetDisplayName(lpDir, lpTop, szDisplayName, ARRAYSIZE(szDisplayName)) ? szDisplayName : lpTop;
lvi.lParam = fd.nFileSizeLow;
iItem = ListView_InsertItem (hLV, &lvi);
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;
HRESULT hr;
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, ARRAYSIZE(szBuffer));
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, ARRAYSIZE(szBuffer));
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
//
hr = StringCchPrintf(szSize, ARRAYSIZE(szSize), szSizeFormat, g_dwProfileSize);
if (FAILED(hr))
break;
SetDlgItemText (hDlg, IDC_QUOTA_SIZE, szSize);
hr = StringCchPrintf(szSize, ARRAYSIZE(szSize), szSizeFormat, g_dwMaxProfileSize);
if (FAILED(hr))
break;
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);
SetEvent(g_hQuotaDlgEvent);
}
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;
UINT cchRemaining;
HRESULT hr;
//
// Setup a temp buffer to work with
//
hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), lpSourceDir);
if (FAILED(hr)) {
return NULL;
}
lpEnd = CheckSlash (szTemp, ARRAYSIZE(szTemp), &cchRemaining);
if (!lpEnd) {
return NULL;
}
//
// 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 && *lpDir == TEXT(' ')) {
lpDir++;
}
//
// Check whether the entry is empty
//
if (lpDir == lpTemp) {
// 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;
continue;
}
//
// Put the directory name on the temp buffer
//
*lpEnd = TEXT('\0');
hr = StringCchCatN(lpEnd, cchRemaining, lpDir, (int)(lpTemp - lpDir));
if (FAILED(hr)) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy 0x%x"), hr));
LocalFree (lpExcludeList);
lpExcludeList = NULL;
goto Exit;
}
//
// 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;
hr = StringCchCopy(lpInsert, dwStrLen, szTemp);
if (FAILED(hr)) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy 0x%x"), hr));
LocalFree (lpExcludeList);
lpExcludeList = NULL;
goto Exit;
}
} 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;
}
hr = StringCchCopy(lpExcludeList, dwSize, szTemp);
if (FAILED(hr)) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy 0x%x"), hr));
LocalFree (lpExcludeList);
lpExcludeList = NULL;
goto Exit;
}
}
//
// 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, ARRAYSIZE(szProfile), NULL);
if (!lpEnd) {
ExitThread (0);
}
//
// 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, ARRAYSIZE(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;
}
//*************************************************************
//
// GetDisplayName()
//
// Purpose: Get display name from shell for specific directory
// to display on the dialog box list view
//
// Parameters: IN lpDir : full directory name which display name is required
// IN lpTop : relative directory name from %USERPROFILE%
// OUT lpDisplayName : buffer to retrieve the display name
// IN cchDisplayName : size of display name buffer
//
// Return: TRUE if success, else FALSE
//
// Remark: This function loops through each directory level of
// the lpDir and get the shell display name of it, append to the
// display name buffer.
//
//*************************************************************
BOOL GetDisplayName(LPCTSTR lpDir, LPTSTR lpTop, LPTSTR lpDisplayName, DWORD cchDisplayName)
{
SHFILEINFO Info;
BOOL bFirstSlash = TRUE;
HRESULT hr;
hr = StringCchCopy(lpDisplayName, cchDisplayName, TEXT(""));
if (FAILED(hr))
return FALSE;
for ( ; lpTop[0]; lpTop++)
{
if (lpTop[0] == TEXT('\\'))
{
lpTop[0] = TEXT('\0');
if (!SHGetFileInfo(lpDir, 0, &Info, sizeof(Info), SHGFI_DISPLAYNAME))
{
DebugMsg((DM_WARNING, TEXT("SHGetFileInfo failed, err = %d"), GetLastError()));
lpTop[0] = TEXT('\\');
return FALSE;
}
lpTop[0] = TEXT('\\');
if (bFirstSlash)
{
bFirstSlash = FALSE;
}
else
{
hr = StringCchCat(lpDisplayName, cchDisplayName, TEXT("\\"));
if (FAILED(hr))
return FALSE;
}
hr = StringCchCat(lpDisplayName, cchDisplayName, Info.szDisplayName);
if (FAILED(hr))
return FALSE;
}
}
if (!SHGetFileInfo(lpDir, 0, &Info, sizeof(Info), SHGFI_DISPLAYNAME))
{
DebugMsg((DM_WARNING, TEXT("SHGetFileInfo failed, err = %d"), GetLastError()));
return FALSE;
}
hr = StringCchCat(lpDisplayName, cchDisplayName, TEXT("\\"));
if (FAILED(hr))
return FALSE;
hr = StringCchCat(lpDisplayName, cchDisplayName, Info.szDisplayName);
if (FAILED(hr))
return FALSE;
return TRUE;
}