www.pudn.com > 44w.rar > AccessEnum.cpp


#define _WIN32_WINNT 0x0400 // WM_MOUSEWHEEL support
#include <windows.h>
#include <stdio.h>
#include <commctrl.h>
#include <tchar.h>
#include <ctype.h>
#include <Aclapi.h>
#include <crtdbg.h> // ASSERT
#include <comdef.h> // bstr_t support
#include <lm.h>
#include <ShlObj.h>


#include "AccessEnum.h"
#include "resource.h"
#include "resizer.h"
#include "listview.h"
#include "Enumeration.h"


PSID DomainAdminSid();
bool IsDomainAdmin();
int GetAccountName( TCHAR * buf, const TCHAR * host, PSID sid );


const TCHAR ALL_DOMAINS[] = _T("<All domains>");

struct _globals {
HINSTANCE hInst;
HWND Abort; // 'abort in progress' window
bool ShowAllFiles;
CShare * ShareList;
long ThreadCount;
HWND hMainDlg;
_bstr_t Exclude;
} g;


const DWORD CFileEnumeration::READ_MASK = GENERIC_ALL|GENERIC_READ|GENERIC_EXECUTE|READ_CONTROL|FILE_READ_ATTRIBUTES|FILE_READ_DATA|FILE_READ_EA;
const DWORD CFileEnumeration::WRITE_MASK = GENERIC_ALL|GENERIC_WRITE|DELETE|WRITE_DAC|WRITE_OWNER|FILE_APPEND_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA|FILE_WRITE_EA;

const DWORD CRegEnumeration::READ_MASK = GENERIC_ALL|GENERIC_READ|GENERIC_EXECUTE|READ_CONTROL|KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS|KEY_NOTIFY;
const DWORD CRegEnumeration::WRITE_MASK = GENERIC_ALL|GENERIC_WRITE|DELETE|WRITE_DAC|WRITE_OWNER|KEY_SET_VALUE|KEY_CREATE_SUB_KEY|KEY_CREATE_LINK;


// Everyone
static const SID SID_EVERYONE = { 1, 1, SECURITY_WORLD_SID_AUTHORITY, SECURITY_NULL_RID };
// creator owner
static const SID SID_CREATOR_OWNER = { 1, 1, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RID };
static const SID SID_CREATOR_GROUP = { 1, 1, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_GROUP_RID };
static const SID SID_CREATOR_OWNERSERVER = { 1, 1, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_SERVER_RID };
static const SID SID_CREATOR_GROUPSERVER = { 1, 1, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_GROUP_SERVER_RID };
// local system
static const SID SID_NTAUTHORITY_SYSTEM = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID };
// alias sids
#if 0
static const SID SID_BUILTIN_ADMIN = { 1, 1, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS };
static const SID SID_BUILTIN_POWERUSERS = { 1, 1, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS };
static const SID SID_BUILTIN_GUESTS = { 1, 1, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS };
static const SID SID_BUILTIN_USERS = { 1, 1, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS };
#endif



static struct LISTVIEW_COLUMN Columns[] =
{
{ TEXT("Path"), 240, DATATYPE_TEXT },
{ TEXT("Read"), 120, DATATYPE_TEXT },
{ TEXT("Write"), 120, DATATYPE_TEXT },
{ TEXT("Deny"), 80, DATATYPE_TEXT },
{ NULL, 0, (DATATYPE)-1 }
};



// Power Users
PSID SidLocalPowerUserAlias()
{
static PSID sid = NULL;
if ( sid == NULL ) {
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
AllocateAndInitializeSid( &amt;SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS,
0, 0, 0, 0, 0, 0, &amt;sid );
}
return sid;
}

// None
PSID SidNone()
{
static PSID sid = NULL;
if ( sid == NULL ) {
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
AllocateAndInitializeSid( &amt;SystemSidAuthority, 5, SECURITY_NT_NON_UNIQUE, 0x56252AE1, 0xAEF28A35, 0x7D8EE907, 0x00000201, 0, 0, 0, &amt;sid );
}
return sid;
}

// Local Administrators
PSID SidLocalAdminAlias()
{
static PSID sid = NULL;
if ( sid == NULL ) {
SID_IDENTIFIER_AUTHORITY ntauth = SECURITY_NT_AUTHORITY;
AllocateAndInitializeSid( &amt;ntauth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &amt;sid );
}
return sid;
}


const TCHAR * FormattedMessage( DWORD status )
{
static TCHAR msg[ 1024 ];
msg[0] = 0;
FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, NULL,
status, 0, msg, sizeof msg/sizeof msg[0], NULL );
if ( msg[0] != 0 ) {
TCHAR * ptr = _tcschr( msg, 0 );
while ( ptr > msg &amt;&amt; isspace( *--ptr ) )
*ptr = 0;
} else {
_stprintf( msg, _T("Error >d"), status );
}
return msg;
}


bool ShowFileProperties( HWND hDlg, const TCHAR * Path )
{
SHELLEXECUTEINFO shellExecuteInfo = { 0 };

shellExecuteInfo.cbSize = sizeof shellExecuteInfo;
shellExecuteInfo.fMask = SEE_MASK_INVOKEIDLIST;
shellExecuteInfo.hwnd = hDlg;
shellExecuteInfo.lpVerb = _T("properties");
shellExecuteInfo.lpFile = Path;

return ShellExecuteEx( &amt;shellExecuteInfo ) != FALSE;
}

bool ExploreFile( HWND hDlg, const TCHAR * Path )
{
if ( _tcsnicmp( Path, _T("HK"), 2 ) == 0 ) {
// registry key

RegeditJump( hDlg, Path );
return true;

} else {
// file
SHELLEXECUTEINFO shellExecuteInfo = { 0 };

shellExecuteInfo.cbSize = sizeof shellExecuteInfo;
shellExecuteInfo.fMask = SEE_MASK_INVOKEIDLIST;
shellExecuteInfo.hwnd = hDlg;
shellExecuteInfo.lpVerb = _T("explore");
shellExecuteInfo.lpFile = Path;

return ShellExecuteEx( &amt;shellExecuteInfo ) != FALSE;
}
}




TCHAR * GetTextualSid( PSID pSid )
{
PSID_IDENTIFIER_AUTHORITY psia;
DWORD dwSubAuthorities;
DWORD dwSidRev=SID_REVISION;
DWORD dwCounter;
DWORD dwSidSize;

// Validate the binary SID.
if ( ! IsValidSid( pSid ) )
return NULL;

// Get the identifier authority value from the SID.
psia = GetSidIdentifierAuthority( pSid );

// Get the number of subauthorities in the SID.
dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

// Compute the buffer length.
// S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL
dwSidSize = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

// Check input buffer length.
// If too small, indicate the proper size and set last error.
TCHAR * TextualSid = new TCHAR[ dwSidSize ];

// Add 'S' prefix and revision number to the string.
dwSidSize = wsprintf( TextualSid, TEXT("S->lu-"), dwSidRev );

// Add SID identifier authority to the string.
if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) ) {
dwSidSize += wsprintf( TextualSid + dwSidSize,
TEXT("0x>02hx>02hx>02hx>02hx>02hx>02hx"),
(USHORT)psia->Value[0],
(USHORT)psia->Value[1],
(USHORT)psia->Value[2],
(USHORT)psia->Value[3],
(USHORT)psia->Value[4],
(USHORT)psia->Value[5]);
} else {
dwSidSize += wsprintf( TextualSid + dwSidSize,
TEXT(">lu"),
(ULONG)(psia->Value[5] ) +
(ULONG)(psia->Value[4] << 8) +
(ULONG)(psia->Value[3] << 16) +
(ULONG)(psia->Value[2] << 24) );
}

// Add SID subauthorities to the string.
//
for ( dwCounter = 0; dwCounter < dwSubAuthorities; dwCounter++ ) {
dwSidSize += wsprintf( TextualSid + dwSidSize, TEXT("->lu"), *GetSidSubAuthority(pSid, dwCounter) );
}

return TextualSid;
}




struct ACCOUNT {
PSID Sid;
const TCHAR * Name;
const GROUP_USERS_INFO_0 * GroupNetList;
DWORD GroupNetCnt;
const LOCALGROUP_USERS_INFO_0 * GroupLocalList;
DWORD GroupLocalCnt;
SID_NAME_USE Use;
};
#define UNDEFINED_ACCOUNT ((ACCOUNT *)-1)


const ACCOUNT * LookupSid( PSID pSid, const TCHAR * host )
{
static ACCOUNT ** AccountList = NULL;
static DWORD AccountCnt = 0;
static DWORD AccountMax = 0;

for ( DWORD idx = 0; idx < AccountCnt; ++idx )
if ( EqualSid( pSid, AccountList[idx]->Sid ) )
return AccountList[idx];

ACCOUNT * act = new ACCOUNT;

// get copy of sid
DWORD sidlen = GetLengthSid( pSid );
act->Sid = new BYTE[ sidlen ];
memcpy( act->Sid, pSid, sidlen );

// Get account name for sid.
TCHAR domainName[MAX_PATH+1+2] = _T("\\\\");
TCHAR userName[MAX_PATH+1];
DWORD userLength = MAX_PATH;
DWORD domainLength = MAX_PATH;
TCHAR fullName[ 2 * MAX_PATH ];

if ( LookupAccountSid( host, pSid, userName, &amt;userLength, domainName+2, &amt;domainLength, &amt;act->Use ) ) {
// found account on remote system
switch ( act->Use ) {
case SidTypeAlias:
_stprintf( fullName, _T(">s"), userName );
break;
case SidTypeUser:
_stprintf( fullName, _T(">s\\>s"), domainName+2, userName );
break;
case SidTypeDeletedAccount:
_stprintf( fullName, _T(">s\\>s(Deleted)"), domainName+2, userName );
break;
default:
_ASSERT(false);
// fall through
case SidTypeGroup:
case SidTypeWellKnownGroup:
if ( domainName[2] )
_stprintf( fullName, _T(">s\\>s"), domainName+2, userName );
else
_stprintf( fullName, _T(">s"), userName );
break;
}
act->Name = _tcsdup( fullName );
} else if ( host &amt;&amt; LookupAccountSid( NULL, pSid, userName, &amt;userLength, domainName+2, &amt;domainLength, &amt;act->Use ) ) {
_ASSERT(false);
// found account on local system
switch ( act->Use ) {
case SidTypeAlias:
_stprintf( fullName, _T(">s"), userName );
break;
case SidTypeUser:
_stprintf( fullName, _T(">s\\>s"), domainName+2, userName );
break;
case SidTypeDeletedAccount:
_stprintf( fullName, _T(">s\\>s(Deleted)"), domainName+2, userName );
break;
default:
_ASSERT(false);
// fall through
case SidTypeGroup:
case SidTypeWellKnownGroup:
if ( domainName[2] )
_stprintf( fullName, _T(">s\\>s"), domainName+2, userName );
else
_stprintf( fullName, _T(">s"), userName );
break;
}
act->Name = _tcsdup( fullName );
} else {
if ( EqualSid( pSid, SidLocalPowerUserAlias() ) ) {
_tcscpy( fullName, _T("Power Users") );
act->Name = _tcsdup( fullName );
} else {
// Use plain SID
act->Name = GetTextualSid( pSid );
act->Use = SidTypeUnknown;
fullName[0] = 0;
}
}

act->GroupNetList = NULL;
act->GroupLocalList = NULL;
act->GroupNetCnt = 0;
act->GroupLocalCnt = 0;

if ( act->Use == SidTypeUser ) {
NET_API_STATUS status;

// see what groups account belongs to
DWORD total = 0;
status = NetUserGetGroups( domainName, userName, 0, (BYTE **)&amt;act->GroupNetList, MAX_PREFERRED_LENGTH, &amt;act->GroupNetCnt, &amt;total );
_ASSERT( status == 0 || status == ERROR_ACCESS_DENIED ); // ERROR_INVALID_FUNCTION, NERR_InvalidComputer

total = 0;
TCHAR server[ MAX_PATH ];
if ( host ) {
_stprintf( server, _T("\\\\>s"), host );
host = server;
}
status = NetUserGetLocalGroups( host, fullName, 0, LG_INCLUDE_INDIRECT, (BYTE **)&amt;act->GroupLocalList, MAX_PREFERRED_LENGTH, &amt;act->GroupLocalCnt, &amt;total );
_ASSERT( status == 0 || status == ERROR_ACCESS_DENIED ); // ERROR_INVALID_FUNCTION, NERR_InvalidComputer
}

// add account to list
if ( AccountCnt >= AccountMax ) {
AccountMax = AccountMax ? 2*AccountMax : 1024;
AccountList = (ACCOUNT **)realloc( AccountList, AccountMax * sizeof AccountList[0] );
}
AccountList[ AccountCnt++ ] = act;

return act;
}


struct ACCOUNT_RIGHTS {
const ACCOUNT * Account;
DWORD AllowMask;
DWORD DenyMask;
};

inline int CompareAccount( const ACCOUNT * pa, const ACCOUNT * pb )
{
if ( pa == pb )
return 0;
return _tcsicmp( pa->Name, pb->Name );
}
int __cdecl CompareAccountRights( const void * pa, const void * pb )
{
return CompareAccount( ((const ACCOUNT_RIGHTS *)pa)->Account, ((const ACCOUNT_RIGHTS *)pb)->Account );
}

class CEnumeration;
bool GetCannonicalRights( const TCHAR * host, const PSECURITY_DESCRIPTOR sd, const CEnumeration * root, const ACCOUNT * Read[], const ACCOUNT * Write[], const ACCOUNT * Deny[] )
{
BOOL present, defaulted;
PACL acl = NULL;

if ( host &amt;&amt; host[0] == 0 )
host = NULL;

// Get DACL
if ( !IsValidSecurityDescriptor( sd ) ||
!GetSecurityDescriptorDacl( sd, &amt;present, &amt;acl, &amt;defaulted ) ||
!present )
{
// can't say anything about the rights
Read[0] = UNDEFINED_ACCOUNT;
Write[0] = UNDEFINED_ACCOUNT;
Deny[0] = UNDEFINED_ACCOUNT;
Read[1] = NULL;
Write[1] = NULL;
Deny[1] = NULL;
return false;
}

if ( acl == NULL ) {
// NULL dacl grants full access to everyone
Read[0] = LookupSid( (PSID)&amt;SID_EVERYONE, host );
Write[0] = Read[0];
Deny[0] = Read[0];
Read[1] = NULL;
Write[1] = NULL;
Deny[1] = NULL;
return true;
}

// First we'll get a list of all accounts and the rights each has.
ACCOUNT_RIGHTS AccountList[ MAX_ACCOUNTS_PER_ACL ];
DWORD AccountCnt = 0;

// Iterate over aces and acculate a list of accounts
for ( int idx = 0; idx < acl->AceCount; ++idx ) {

ACCESS_ALLOWED_ACE * ace;

// Get the ace
GetAce( acl, idx, (void **)&amt;ace );
if ( ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE &amt;&amt;
ace->Header.AceType != ACCESS_DENIED_ACE_TYPE )
{
continue;
}

// Get SID for ace
PSID pSid = &amt;ace->SidStart;
if ( ! IsValidSid( pSid ) )
continue;

// translate CREATOR_OWNER sid to owner sid
if ( EqualSid( pSid, (PSID)&amt;SID_CREATOR_OWNER ) ) {
PSID pOwner = NULL;
BOOL defaulted;
if ( ! GetSecurityDescriptorOwner( sd, &amt;pOwner, &amt;defaulted ) )
continue;
if ( pOwner == NULL )
continue; // no owner, therefore no permissions to report
pSid = pOwner;
} else if ( EqualSid( pSid, (PSID)&amt;SID_CREATOR_GROUP ) ) {
PSID pOwner = NULL;
BOOL defaulted;
if ( ! GetSecurityDescriptorGroup( sd, &amt;pOwner, &amt;defaulted ) )
continue;
if ( pOwner == NULL )
continue; // no owner, therefore no permissions to report
pSid = pOwner;
}

// Skip LocalSystem account
if ( EqualSid( pSid, (PSID)&amt;SID_NTAUTHORITY_SYSTEM ) ) {
// who cares?
continue;
}

// Check if SID already is in our list
for ( DWORD i = 0; i < AccountCnt; ++i )
if ( EqualSid( pSid, AccountList[i].Account->Sid ) )
break;

// Create new list entry if not already present
if ( i >= AccountCnt ) {
if ( AccountCnt+1 >= MAX_ACCOUNTS_PER_ACL ) {
MessageBox( g.hMainDlg, _T("Internal error: Too many accounts in ACL"), APPNAME, MB_OK|MB_ICONSTOP );
return false;
}
++AccountCnt;
AccountList[i].Account = LookupSid( pSid, host );
AccountList[i].AllowMask = 0;
AccountList[i].DenyMask = 0;
}

#if 0
// Skip None group
if ( EqualSid( pSid, SidNone() ) ) {
// who cares?
continue;
}
#endif

// Merge rights for this ace into existing account rights
if ( ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE )
AccountList[i].AllowMask |= ace->Mask;
else
AccountList[i].DenyMask |= ace->Mask;
}

// Convert rights for each account to generic rights. This
// simplifies the bit arithmetic we do later.
for ( DWORD actIdx = 0; actIdx < AccountCnt; ++actIdx ) {
DWORD &amt; mask = AccountList[actIdx].AllowMask;
mask = (root->AllowRead(mask) ? GENERIC_READ : 0)
| (root->AllowWrite(mask) ? GENERIC_WRITE : 0);
}

#if 1
// Check group membership for each account and
// subtract rights granted by group(s) from account rights.
for ( actIdx = 0; actIdx < AccountCnt; ++actIdx ) {

for ( DWORD other = 0; other < AccountCnt; ++other ) {
// make sure other item is a group, and not us
if ( actIdx == other )
continue;
if ( AccountList[other].Account->Use != SidTypeGroup &amt;&amt;
AccountList[other].Account->Use != SidTypeWellKnownGroup &amt;&amt;
AccountList[other].Account->Use != SidTypeAlias )
continue;

// If Everyone is present then always assume membership
if ( EqualSid( AccountList[other].Account->Sid, (PSID)&amt;SID_EVERYONE ) ) {
// Everyone is a member of Everyone
AccountList[actIdx].AllowMask &amt;= ~AccountList[other].AllowMask;
AccountList[actIdx].DenyMask &amt;= ~AccountList[other].DenyMask;
continue;
}

// check each of our groups to see if we belong to other
for ( DWORD actGrp = 0; actGrp < AccountList[actIdx].Account->GroupLocalCnt; ++actGrp ) {
if ( _tcsicmp( AccountList[actIdx].Account->GroupLocalList[actGrp].lgrui0_name,
AccountList[other].Account->Name ) == 0 )
{
// There is another group that we are a member of.
// Subtract any rights they grant from our rights.
AccountList[actIdx].AllowMask &amt;= ~AccountList[other].AllowMask;
AccountList[actIdx].DenyMask &amt;= ~AccountList[other].DenyMask;
}
}
for ( actGrp = 0; actGrp < AccountList[actIdx].Account->GroupNetCnt; ++actGrp ) {
if ( _tcsicmp( AccountList[actIdx].Account->GroupNetList[actGrp].grui0_name,
AccountList[other].Account->Name ) == 0 )
{
// There is another group that we are a member of
// Subtract any rights they grant from our rights.
AccountList[actIdx].AllowMask &amt;= ~AccountList[other].AllowMask;
AccountList[actIdx].DenyMask &amt;= ~AccountList[other].DenyMask;
}
}
}
}
#endif

// sort list of accounts
qsort( AccountList, AccountCnt, sizeof AccountList[0], CompareAccountRights );

// Generate lists of the accounts with Read, Write and Deny rights present.
DWORD ReadCnt = 0;
DWORD WriteCnt = 0;
DWORD DenyCnt = 0;
for ( actIdx = 0; actIdx < AccountCnt; ++actIdx ) {
if ( AccountList[actIdx].AllowMask &amt; GENERIC_READ ) {
Read[ReadCnt++] = AccountList[actIdx].Account;
}
if ( AccountList[actIdx].AllowMask &amt; GENERIC_WRITE ) {
Write[WriteCnt++] = AccountList[actIdx].Account;
}
if ( AccountList[actIdx].DenyMask ) {
Deny[DenyCnt++] = AccountList[actIdx].Account;
}
}

// set null terminator
Read[ReadCnt] = NULL;
Write[WriteCnt] = NULL;
Deny[DenyCnt] = NULL;

return true;
}


// Both account lists must be sorted
bool AccountListEqual( const ACCOUNT * Account1[], const ACCOUNT * Account2[] )
{
if ( Account1[0] == UNDEFINED_ACCOUNT || Account2[0] == UNDEFINED_ACCOUNT ) {
// if either is undefined, then the undefined one is the superset
return Account2[0] == Account1[0];
}

// Compare each account in turn, using the same comparison function used to sort the lists.
for ( int idx = 0; Account1[idx] &amt;&amt; Account2[idx]; ++idx ) {
int diff = CompareAccount( Account1[idx], Account2[idx] );
if ( diff )
return false;
}
return Account1[idx] == Account2[idx];
}

bool AccountListSubset( const ACCOUNT * Sub[], const ACCOUNT * Super[] )
{
// if either is undefined, then the undefined one is the superset
if ( Sub[0] == UNDEFINED_ACCOUNT )
return Super[0] == UNDEFINED_ACCOUNT;
if ( Super[0] == UNDEFINED_ACCOUNT )
return false;

// Compare each account in turn, using the same comparison function used to sort the lists.
for ( int iSub = 0, iSuper = 0; Sub[iSub] &amt;&amt; Super[iSuper];) {
int diff = CompareAccount( Sub[iSub], Super[iSuper] );
if ( diff == 0 ) {
++iSub;
++iSuper;
} else if ( diff > 0 ) {
++iSuper;
} else {
return false;
}
}
return Sub[iSub] == NULL;
}



int AddListviewRow( HWND hListview, const TCHAR * path,
const TCHAR * OtherRead, const TCHAR * OtherWrite,
const TCHAR * deny )
{
CShare * share = new CShare( path, OtherRead, OtherWrite, deny, ',' );
share->InsertInList( &amt;g.ShareList );

// get icon
int iImage;
if ( _tcsnicmp( path, _T("HK"), 2 ) == 0 ) {
// registry key
HIMAGELIST hImageList = ListView_GetImageList( hListview, LVSIL_SMALL );
iImage = ImageList_AddIcon( hImageList, LoadIcon( g.hInst, MAKEINTRESOURCE(IDI_FOLDERCLOSED) ) );
} else {
// file
SHFILEINFO info = { 0 };
SHGetFileInfo( path, 0, &amt;info, sizeof info, SHGFI_ICON|SHGFI_SMALLICON );
HIMAGELIST hImageList = ListView_GetImageList( hListview, LVSIL_SMALL );
iImage = ImageList_AddIcon( hImageList, info.hIcon );
DestroyIcon( info.hIcon );
}

// Add to listview
LVITEM item;
item.mask = LVIF_TEXT | LVIF_IMAGE;
item.iItem = 0x7FFFFFFF;
item.iSubItem = 0;
item.iImage = iImage;
item.pszText = (TCHAR *) path;
item.iItem = ListView_InsertItem( hListview, &amt;item );

// set param to index, so we can look ourself up during sorting
item.mask = LVIF_PARAM;
item.lParam = item.iItem;
ListView_SetItem( hListview, &amt;item );
item.mask = LVIF_TEXT;

item.iSubItem += 1;
item.pszText = (TCHAR *) OtherRead;
ListView_SetItem( hListview, &amt;item );

item.iSubItem += 1;
item.pszText = (TCHAR *) OtherWrite;
ListView_SetItem( hListview, &amt;item );

item.iSubItem += 1;
item.pszText = (TCHAR *) deny;
ListView_SetItem( hListview, &amt;item );

return item.iItem;
}





//==============================================================
//
// EnumerateDomains
//
// Create a list of computer domains accessible to this system
//
//==============================================================

class CWorker {
public:
void Start()
{
InterlockedIncrement( &amt;g.ThreadCount );
UpdateStatus();

#if _MT
DWORD id;
HANDLE hThread = CreateThread( NULL, 0, Work, this, CREATE_SUSPENDED, &amt;id );
SetThreadPriority( hThread, THREAD_PRIORITY_LOWEST );
ResumeThread( hThread );
#else
Work();
#endif
}

virtual ~CWorker()
{
}

protected:
void UpdateStatus()
{
TCHAR * msg = g.ThreadCount ? _T("Searching...") : _T("");
SetWindowText( GetDlgItem( g.hMainDlg, IDC_STATUS ), msg );
}

static int ProcessException( int code, const EXCEPTION_POINTERS * ep )
{
TCHAR buf[ MAX_PATH ];
_stprintf( buf, _T("An internal error occured: Exception #0x>X"), code );
MessageBox( g.hMainDlg, buf, APPNAME, MB_OK );

EXCEPTION_RECORD * ExceptionRecord = ep->ExceptionRecord;
CONTEXT * ContextRecord = ep->ContextRecord;

return EXCEPTION_EXECUTE_HANDLER;
}

static DWORD WINAPI Work( void * This )
{
CWorker * obj = (CWorker *)This;
__try {
obj->Work();
} __except ( ProcessException( GetExceptionCode(), GetExceptionInformation() ) ) {
}
return 0;
}

virtual void Work()
{
if ( InterlockedDecrement( &amt;g.ThreadCount ) == 0 ) {
// We're done enumerating
PostMessage( g.hMainDlg, WM_APP_ENUM_COMPLETE, 0, 0 );
}
UpdateStatus();
delete this;
}
};






class CTraversal : public CWorker {
HWND m_hListview;
const bstr_t m_Host;
CEnumeration * m_Root;

#define MISSING_DACL ((PACL) 0xFFFFFFFF)

inline bool EXPLICIT_DACL( PACL d )
{
return d != NULL &amt;&amt; d != MISSING_DACL;
}

public:
CTraversal( HWND hListview, const TCHAR * Host, CEnumeration * Root ) :
m_hListview( hListview ), m_Host( Host ), m_Root( Root )
{
}

~CTraversal()
{
}

private:

void GetAccountNames( TCHAR * Text, const ACCOUNT * Account[] )
{
// Analyze DACL
if ( Account[0] == UNDEFINED_ACCOUNT ) {
_tcscpy( Text, _T("???") );
} else {
// convert acl to text
TCHAR * TextStart = Text;
for ( int i = 0; Account[i]; ++i ) {
Text += _stprintf( Text, _T(">s, "), Account[i]->Name );
}

// Trim trailing commas in lists
if ( Text == TextStart ) {
Text[0] = 0;
} else {
Text[-2] = 0;
}
}
}

void ShowAccounts( const TCHAR * Path, const ACCOUNT * Read[], const ACCOUNT * Write[], const ACCOUNT * Deny[] )
{
// Get permissions
TCHAR szRead[ MAX_TEXT ];
TCHAR szWrite[ MAX_TEXT ];
TCHAR szDeny[ MAX_TEXT ];

GetAccountNames( szRead, Read );
GetAccountNames( szWrite, Write );
GetAccountNames( szDeny, Deny );

// Add to list view
AddListviewRow( m_hListview, Path, szRead, szWrite, szDeny );
}


void Enumerate( CEnumeration * Root, const ACCOUNT * ParentRead[], const ACCOUNT * ParentWrite[], const ACCOUNT * ParentDeny[] )
{
// If path is a subdirectory of Exclude then skip it
if ( !!g.Exclude ) {
if ( _tcsnicmp( Root->Path(), g.Exclude, _tcslen(g.Exclude) ) == 0 )
return;
else
g.Exclude = (TCHAR *) NULL;
}
#if 0
if ( _tcsicmp( Root->Path(), _T("HKLM\\Software\\Classes\\Installer\\Components") ) == 0 )
__asm int 3;
#endif

// Show progress
SetDlgItemText( GetParent(m_hListview), IDC_PATH, Root->Path() );

#if 0
if ( wcslen( Root->Path() ) > 100 )
Path = (TCHAR *)9999999999;
#endif

// Get security descriptor
PSECURITY_DESCRIPTOR sd = Root->GetSecurity();

// Convert acl to rights list
const ACCOUNT * Read[ MAX_ACCOUNTS_PER_ACL ];
const ACCOUNT * Write[ MAX_ACCOUNTS_PER_ACL ];
const ACCOUNT * Deny[ MAX_ACCOUNTS_PER_ACL ];

GetCannonicalRights( m_Host, sd, Root, Read, Write, Deny );
bool show;
if ( Root->IsDir() || g.ShowAllFiles ) {
// directory permissions must match exactly
show = !AccountListEqual( Read, ParentRead ) ||
!AccountListEqual( Write, ParentWrite ) ||
!AccountListEqual( Deny, ParentDeny );
} else {
// file permissions its okay to be a subset of parent
show = !AccountListSubset( Read, ParentRead ) ||
!AccountListSubset( Write, ParentWrite ) ||
!AccountListSubset( Deny, ParentDeny );
}

if ( show ) {
// Display permissions
ShowAccounts( Root->Path(), Read, Write, Deny );
}

// Recurse if a directory
if ( Root->IsDir() ) {

DWORD status;
CEnumeration * Child = Root->FindNext( &amt;status );

if ( status ) {

// indicate error while reading children
const TCHAR * msg = FormattedMessage( status );
_bstr_t fullpath = Root->Path() + _T("\\*");
AddListviewRow( m_hListview, fullpath, msg, _T(""), _T("") );

} else {

while ( Child ) {
if ( g.Abort )
break;

// Compute full path and evaluate
#if 0
if ( end - Path + _tcslen( info.cFileName ) + 1 >= MAX_UNCPATH ) {
MessageBox( NULL, _T("Fatal error: MAX_UNCPATH exceeded"), APPNAME, MB_ICONSTOP );
exit(1);
}
_tcscpy( end+1, info.cFileName );
#endif

#if 0
static TCHAR oldPath[ 2*MAX_UNCPATH ];
_tcscpy( oldPath, Path );
if ( _tcscmp( oldPath, _T("C:\\WINDOWS\\Help\\SBSI\\Training\\Database\\bookmrk.CDX") ) == 0 )
__asm int 3;
#endif

Enumerate( Child, Read, Write, Deny );

delete Child;
Child = Root->FindNext( &amt;status );
}
}
}

delete sd;
}

void Work()
{
// Enumerate files in the directory
bool isDir = m_Root->IsDir();

// Get permissions
const ACCOUNT * Account[2] = { UNDEFINED_ACCOUNT, NULL };
Enumerate( m_Root, Account, Account, Account );

// restore original path
SetDlgItemText( GetParent(m_hListview), IDC_PATH, m_Root->Path() );

CWorker::Work();
} // Work

}; // CTraversal






bool ExportFile( HWND hListView, const TCHAR * path )
{
HANDLE hFile = CreateFile( path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE )
return false;

LV_ITEM lvi;
DWORD nb;

// Mark file as unicode
#if UNICODE
WORD uni = 0xFEFF;
WriteFile( hFile, &amt;uni, sizeof uni, &amt;nb, NULL );
#endif

// print headers
for ( lvi.iSubItem = 0; lvi.iSubItem < sizeof(Columns)/sizeof(Columns[0])-1; ++lvi.iSubItem ) {
WriteFile( hFile, _T("\""), sizeof(TCHAR), &amt;nb, NULL );
WriteFile( hFile, Columns[lvi.iSubItem].Title, _tcslen(Columns[lvi.iSubItem].Title)*sizeof(TCHAR), &amt;nb, NULL );
WriteFile( hFile, _T("\"\t"), 2*sizeof(TCHAR), &amt;nb, NULL );
}
WriteFile( hFile, _T("\r\n"), 2*sizeof(TCHAR), &amt;nb, NULL );

// Get text
int max = ListView_GetItemCount( hListView );

for ( lvi.iItem = 0; lvi.iItem < max; ++lvi.iItem ) {

// Get item
lvi.mask = TVIF_TEXT;

for ( lvi.iSubItem = 0; lvi.iSubItem < sizeof(Columns)/sizeof(Columns[0])-1; ++lvi.iSubItem ) {

TCHAR Item[ MAX_TEXT ];
lvi.pszText = Item;
lvi.cchTextMax = MAX_TEXT;

if ( ! ListView_GetItem( hListView, &amt;lvi ) )
*Item = 0;

for ( int i = 0; Item[i]; ++i )
if ( Item[i] == ',' &amt;&amt; Item[i+1] == ' ' ) {
Item[i] = ' ';
Item[i+1] = 0x0A;
}

WriteFile( hFile, _T("\""), sizeof(TCHAR), &amt;nb, NULL );
WriteFile( hFile, Item, _tcslen(Item)*sizeof(TCHAR), &amt;nb, NULL );
WriteFile( hFile, _T("\"\t"), 2*sizeof(TCHAR), &amt;nb, NULL );
}

WriteFile( hFile, _T("\r\n"), 2*sizeof(TCHAR), &amt;nb, NULL );
}

CloseHandle( hFile );

return true;
}




void PrintAllRecurse( CEnumeration * parent )
{
PSECURITY_DESCRIPTOR sd = parent->GetSecurity();
delete sd;
for (;;) {
DWORD status;
CEnumeration * child = parent->FindNext( &amt;status );
if ( child == NULL )
break;
PrintAllRecurse( child );
delete child;
}
}
void PrintAll()
{
CFileEnumeration file( _T("C:"), true );
CRegEnumeration reg( _T("SOFTWARE"), true );

PrintAllRecurse( &amt;reg );
PrintAllRecurse( &amt;file );
}








INT_PTR CALLBACK AbortDialog( HWND hDlg, UINT message, UINT wParam, LONG lParam )
{
RECT parentRc, childRc;

switch ( message ) {
case WM_INITDIALOG:
GetWindowRect( GetParent(hDlg), &amt;parentRc );
GetWindowRect( hDlg, &amt;childRc );
parentRc.left = ((parentRc.left + parentRc.right) - (childRc.right - childRc.left)) / 2;
parentRc.top = ((parentRc.top + parentRc.bottom) - (childRc.bottom - childRc.top)) / 2;
MoveWindow( hDlg, parentRc.left, parentRc.top, childRc.right - childRc.left, childRc.bottom - childRc.top, TRUE );
return TRUE;

case WM_CLOSE:
EndDialog( hDlg, 0 );
return TRUE;

default:
break;
}
return FALSE;

}


INT_PTR CALLBACK AboutDialog( HWND hDlg, UINT message, UINT wParam, LONG lParam )
{
RECT parentRc, childRc;
static HWND hLink;
static BOOL underline_link;
static HFONT hFontNormal = NULL;
static HFONT hFontUnderline = NULL;
static HCURSOR hHandCursor = NULL;
static HCURSOR hRegularCursor;
LOGFONT logfont;

switch ( message ) {
case WM_INITDIALOG:
GetWindowRect( GetParent(hDlg), &amt;parentRc );
GetWindowRect( hDlg, &amt;childRc );
parentRc.left += 70;
parentRc.top += 60;
MoveWindow( hDlg, parentRc.left, parentRc.top, childRc.right - childRc.left, childRc.bottom - childRc.top, TRUE );

underline_link = TRUE;
hLink = GetDlgItem( hDlg, IDC_LINK );

// get link fonts
hFontNormal = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
GetObject( hFontNormal, sizeof logfont, &amt;logfont);
logfont.lfUnderline = TRUE;
hFontUnderline = CreateFontIndirect( &amt;logfont );

// get hand
hHandCursor = LoadCursor( g.hInst, TEXT("HAND") );
hRegularCursor = LoadCursor( NULL, IDC_ARROW );
return TRUE;

case WM_CTLCOLORSTATIC:
if ( (HWND)lParam == hLink ) {
HDC hdc = (HDC)wParam;
SetBkMode( hdc, TRANSPARENT );
if ( GetSysColorBrush(26/*COLOR_HOTLIGHT*/) )
SetTextColor( hdc, GetSysColor(26/*COLOR_HOTLIGHT*/) );
else
SetTextColor( hdc, RGB(0,0,255) );
SelectObject( hdc, underline_link ? hFontUnderline : hFontNormal );
return (LONG)GetSysColorBrush( COLOR_BTNFACE );
}
break;

case WM_MOUSEMOVE: {
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
HWND hChild = ChildWindowFromPoint( hDlg, pt );
if ( underline_link == (hChild == hLink) ) {
underline_link = !underline_link;
InvalidateRect( hLink, NULL, FALSE );
}
if ( underline_link )
SetCursor( hRegularCursor );
else
SetCursor( hHandCursor );
break;
}

case WM_LBUTTONDOWN: {
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
HWND hChild = ChildWindowFromPoint( hDlg, pt );
if ( hChild == hLink ) {
ShellExecute( hDlg, TEXT("open"), TEXT("http://www.sysinternals.com"), NULL, NULL, SW_SHOWNORMAL );
}
break;
}

case WM_COMMAND:
switch ( wParam ) {
case IDOK:
case IDCANCEL:
EndDialog( hDlg, 0 );
return TRUE;
}
break;

case WM_CLOSE:
EndDialog( hDlg, 0 );
return TRUE;

default:
break;
}
return FALSE;

}

INT_PTR CALLBACK FilePermDialog( HWND hDlg, UINT message, UINT wParam, LONG lParam )
{
switch ( message ) {
case WM_INITDIALOG:
CheckDlgButton( hDlg, IDYES, g.ShowAllFiles ? BST_CHECKED : BST_UNCHECKED );
CheckDlgButton( hDlg, IDNO, !g.ShowAllFiles ? BST_CHECKED : BST_UNCHECKED );
return true;
case WM_COMMAND:
switch ( LOWORD(wParam) ) {
case IDOK:
g.ShowAllFiles = IsDlgButtonChecked( hDlg, IDYES ) == BST_CHECKED;
case IDCANCEL:
EndDialog( hDlg, 0 );
break;
}
break;

}
return false;
}

INT_PTR CALLBACK HelpDialog( HWND hDlg, UINT message, UINT wParam, LONG lParam )
{
static const TCHAR HelpText[] =
_T("AccessEnum displays who has access to the files and folders within a directory. Although every ")
_T("file/directory is examined, AccessEnum displays only those with permissions that differ from ")
_T("their parent folder, allowing you to quickly determine deviations in your security policy.")
_T("\r\n\r\n")
_T("AccessEnum abstracts Window's access-control model to just Read, Write and Deny permissions. ")
_T("A file is shown as granting Write permission whether it grants just a single write right (such ")
_T("as Write Owner) or the full suite of write rights via Full Control. Read and Deny permissions ")
_T("are handled similarly.")
_T("\r\n\r\n")
_T("When AccessEnum compares a file/folder and its parent to determine whether their permissions ")
_T("are equivalent, it looks only at whether the same set of accounts are granted Read, Write and ")
_T("Deny access respectively. If a file grants just Write Owner access, and its parent just Delete ")
_T("access, the two will still be considered equivalent since both allow some form of writing.")
_T("\r\n\r\n")
_T("AccessEnum condenses the number of accounts displayed as having access to a file/folder by ")
_T("hiding accounts with permissions that are duplicated by a group to which the account belongs. ")
_T("For example, if a file grants Read access to both user Bob and group Marketing, and Bob is a ")
_T("member of the Marketing group, then only Marketing will be shown in the list of accounts having ")
_T("Read access.")
_T("\r\n\r\n")
_T("AccessEnum handles files slightly differently than folders. When comparing the permissions of a ")
_T("file to its parent the file is displayed only when its permissions are less strict than the parent ")
_T("folder. You can change this behavior using the Options menu.");

switch ( message ) {
case WM_INITDIALOG:
SetDlgItemText( hDlg, IDC_EDIT, HelpText );
return true;
case WM_COMMAND:
switch ( LOWORD(wParam) ) {
case IDOK:
case IDCANCEL:
EndDialog( hDlg, 0 );
break;
}
break;

}
return false;
}



_bstr_t GetTreePath( HWND hTree, HTREEITEM hItem )
{
_bstr_t Path;
for (;;) {
TVITEM tvi;
TCHAR Name[ MAX_UNCPATH ];
tvi.mask = TVIF_TEXT;
tvi.hItem = hItem;
tvi.pszText = Name;
tvi.cchTextMax = MAX_UNCPATH;
TreeView_GetItem( hTree, &amt;tvi );
Path = Name + Path;

hItem = TreeView_GetParent( hTree, hItem );
if ( hItem == NULL )
return Path;
Path = _T("\\") + Path;
}
}


INT_PTR CALLBACK BrowseRegDialog( HWND hDlg, UINT message, UINT wParam, LONG lParam )
{
HWND hTree;
TVINSERTSTRUCT tvi;
int i;
NMHDR * nmhdr;
HIMAGELIST hImageList;
static _bstr_t * ReturnPath;

static const TCHAR * Root[] = {
_T("HKEY_CLASSES_ROOT"),
_T("HKEY_CURRENT_USER"),
_T("HKEY_LOCAL_MACHINE"),
_T("HKEY_USERS"),
_T("HKEY_CURRENT_CONFIG")
};

switch ( message ) {
case WM_INITDIALOG:
ReturnPath = (_bstr_t *) lParam;
hTree = GetDlgItem( hDlg, IDC_TREE );
// create images for nodes
hImageList = ImageList_Create( 16, 16, ILC_COLOR | ILC_MASK, 4, 4 );
ImageList_AddIcon( hImageList, LoadIcon( g.hInst, MAKEINTRESOURCE(IDI_FOLDERCLOSED) ) );
ImageList_AddIcon( hImageList, LoadIcon( g.hInst, MAKEINTRESOURCE(IDI_FOLDEROPEN) ) );
TreeView_SetImageList( hTree, hImageList, TVSIL_NORMAL );
// add root entries
for ( i = 0; i < 5; ++i ) {
tvi.hParent = TVI_ROOT;
tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.item.pszText = (TCHAR *) Root[i];
tvi.item.cChildren = I_CHILDRENCALLBACK;
tvi.item.iImage = 0;
tvi.item.iSelectedImage = 1;
tvi.item.lParam = false; // not expanded yet
TreeView_InsertItem( hTree, &amt;tvi );
}
EnableWindow( GetDlgItem( hDlg, IDOK ), false );
return false;

case WM_NOTIFY:
nmhdr = (NMHDR *) lParam;
if ( nmhdr->code == TVN_SELCHANGED ) {
EnableWindow( GetDlgItem( hDlg, IDOK ), true );
}

if ( nmhdr->code == TVN_GETDISPINFO ) {
NMTVDISPINFO * tvnm = (NMTVDISPINFO *) nmhdr;
if ( tvnm->item.mask &amt; TVIF_CHILDREN ) {
// Get number of children in key
_bstr_t Path = GetTreePath( GetDlgItem( hDlg, IDC_TREE ), tvnm->item.hItem );
HKEY hKey = CRegEnumeration::OpenPath( Path, NULL );
tvnm->item.cChildren = 0;
RegQueryInfoKey( hKey, NULL, NULL, NULL, (DWORD *)&amt;tvnm->item.cChildren, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
RegCloseKey( hKey );
}
}

if ( nmhdr->code == TVN_ITEMEXPANDING ) {
NMTREEVIEW * tvnm = (NMTREEVIEW *) nmhdr;

if ( tvnm->action == TVE_EXPAND &amt;&amt; tvnm->itemNew.lParam == 0 ) {
// add children to parent
hTree = GetDlgItem( hDlg, IDC_TREE );
_bstr_t Path = GetTreePath( hTree, tvnm->itemNew.hItem );
HKEY hKey = CRegEnumeration::OpenPath( Path, NULL );
TCHAR Name[ MAX_PATH ];
for ( int i = 0; RegEnumKey( hKey, i, Name, MAX_PATH ) == 0; ++i ) {
TVINSERTSTRUCT tvi;
tvi.hParent = tvnm->itemNew.hItem;
tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.item.pszText = Name;
tvi.item.cChildren = I_CHILDRENCALLBACK;
tvi.item.iImage = 0;
tvi.item.iSelectedImage = 1;
tvi.item.lParam = false; // not expanded yet
TreeView_InsertItem( hTree, &amt;tvi );
}
RegCloseKey( hKey );
// mark parent as expanded
TVITEM tvi;
tvi.mask = TVIF_CHILDREN | TVIF_PARAM;
tvi.hItem = tvnm->itemNew.hItem;
tvi.cChildren = i;
tvi.lParam = true; // expanded
TreeView_SetItem( hTree, &amt;tvi );
}
}
break;

case WM_COMMAND:
switch ( LOWORD(wParam) ) {
case IDOK:
hTree = GetDlgItem( hDlg, IDC_TREE );
*ReturnPath = GetTreePath( hTree, TreeView_GetSelection( hTree ) );
EndDialog( hDlg, 1 );
break;

case IDCANCEL:
EndDialog( hDlg, 0 );
break;
}
break;

}
return false;
}

_bstr_t GetTreeItemText( HWND hTree, int Item, int SubItem )
{
// apparently there is no way to determine the length of the text in a list view
TCHAR tmp[ 64 * 1024 ];

LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = Item;
lvi.iSubItem = SubItem;
lvi.pszText = tmp;
lvi.cchTextMax = sizeof tmp/sizeof tmp[0];
SendMessage( hTree, LVM_GETITEMTEXT, Item, (LPARAM) &amt;lvi );
_bstr_t Text( tmp );
return Text;
}


//----------------------------------------------------------------------
//
// MainDialog
//
// Main interface for editing output
//
//----------------------------------------------------------------------
LRESULT CALLBACK MainDialog( HWND hDlg, UINT message, UINT wParam, LONG lParam )
{
static CResizer resizer;
int idx;
OPENFILENAME open;
static TCHAR exportPath[ MAX_PATH ];
TCHAR * p;
bool ok;
DWORD style;
static bool Starting = true;
static HCURSOR hCursor;

switch ( message ) {

case WM_INITDIALOG:
// set anchor points
resizer.OnInitDialog( hDlg );
resizer.SetHorz( GetDlgItem(hDlg,IDC_REFRESH), ANCHOR_LEFT );
resizer.SetHorz( GetDlgItem(hDlg,IDC_EXPORT), ANCHOR_LEFT );
resizer.SetVert( GetDlgItem(hDlg,IDC_LIST), ANCHOR_BOTH );
resizer.SetHorz( GetDlgItem(hDlg,IDC_PATH), ANCHOR_BOTH );
resizer.SetHorz( GetDlgItem(hDlg,IDC_DESCRIPTION), ANCHOR_LEFT );
resizer.SetHorz( GetDlgItem(hDlg,IDC_STATUS), ANCHOR_BOTH );

// clear status text
SetWindowText( GetDlgItem(hDlg,IDC_STATUS), _T("") );

// Set cursor for window
hCursor = LoadCursor( NULL, IDC_ARROW );
SetClassLong( hDlg, GCL_HCURSOR, (LONG)hCursor );
SetCursor( hCursor );

// Create listview columns
InitListViewColumns( GetDlgItem(hDlg,IDC_LIST), Columns, sizeof Columns/sizeof Columns[0] - 1 );
style = GetWindowLong( GetDlgItem(hDlg,IDC_LIST), GWL_STYLE );
style |= LVS_SHOWSELALWAYS;
SetWindowLong( GetDlgItem(hDlg,IDC_LIST), GWL_STYLE, style );
ListView_SetExtendedListViewStyleEx( GetDlgItem(hDlg,IDC_LIST), LVS_EX_LABELTIP, LVS_EX_LABELTIP );

#if 0
if ( ! IsDomainAdmin() ) {
MessageBox( hDlg, _T("Information may be incomplete because you are not a domain administrator"), APPNAME, MB_OK|MB_ICONWARNING );
}
#endif

{
TCHAR Path[ MAX_PATH ] = _T("");
GetEnvironmentVariable( _T("SYSTEMROOT"), Path, MAX_PATH );
SetDlgItemText( hDlg, IDC_PATH, Path );
}

return TRUE;

case WM_SETCURSOR:
switch ( LOWORD(lParam) ) {
case HTTOP:
case HTBOTTOM:
case HTLEFT:
case HTRIGHT:
case HTTOPLEFT:
case HTTOPRIGHT:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
break;
default:
SetCursor( hCursor );
return true;
}
break;

case WM_APP_ENUM_COMPLETE:
EnableWindow( GetDlgItem( hDlg, IDC_REFRESH ), true );
EnableWindow( GetDlgItem( hDlg, IDC_PATH ), true );
EnableWindow( GetDlgItem( hDlg, IDC_BROWSE_FILE ), true );
EnableWindow( GetDlgItem( hDlg, IDC_BROWSE_REG ), true );
EnableWindow( GetDlgItem( hDlg, IDCANCEL ), false );
EnableWindow( GetDlgItem( hDlg, IDC_EXPORT ), true );
hCursor = LoadCursor( NULL, IDC_ARROW );
SetClassLong( hDlg, GCL_HCURSOR, (LONG)hCursor );
SetCursor( hCursor );
if ( g.Abort ) {
SendMessage( g.Abort, WM_CLOSE, 0, 0 );
// MessageBox( hDlg, _T("Scan cancelled by user"), APPNAME, MB_OK|MB_ICONINFORMATION );
}
break;

case WM_NOTIFY:
if ( LOWORD(wParam) == IDC_LIST ) {
NMLISTVIEW * nm = (NMLISTVIEW *) lParam;
switch( nm->hdr.code ) {
case LVN_COLUMNCLICK:
// sort column
SortListView( nm->hdr.hwndFrom, nm->iSubItem, Columns );
break;
case NM_DBLCLK:
// double click item, so display device properties
idx = ListView_GetSelectionMark( GetDlgItem(hDlg,IDC_LIST) );
if ( idx >= 0 ) {
TCHAR path[ MAX_UNCPATH ];
ListView_GetItemText( GetDlgItem(hDlg,IDC_LIST), idx, 0, path, MAX_UNCPATH );
ShellExecute( NULL, _T("explore"), path, NULL, NULL, SW_SHOWNORMAL );
}
break;
case NM_RCLICK:
// Create pop-up menu
idx = ListView_GetSelectionMark( GetDlgItem(hDlg,IDC_LIST) );
if ( idx >= 0 ) {
TCHAR path[ MAX_UNCPATH ];
ListView_GetItemText( GetDlgItem(hDlg,IDC_LIST), idx, 0, path, MAX_UNCPATH );
idx = _tcsnicmp( path, _T("HK"), 2 ) ? MF_ENABLED : MF_GRAYED;
} else {
idx = MF_ENABLED;
}
{
HMENU hMenu = LoadMenu( g.hInst, _T("POPUP") );
HMENU hSubMenu = GetSubMenu( hMenu, 0 );
POINT pt;
GetCursorPos( &amt;pt );
EnableMenuItem( hSubMenu, IDC_PROPERTIES, MF_BYCOMMAND|idx );
TrackPopupMenu( hSubMenu,
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hDlg, NULL);
DestroyMenu( hMenu );
}
break;
}
}
break;

case WM_COMMAND:
// Normal notifications
switch ( LOWORD(wParam) ) {

case IDC_QUIT:
// quit
SendMessage( hDlg, WM_CLOSE, 0, 0 );
break;

case IDCANCEL:
// is an enumeration in progress?
if ( ! IsWindowEnabled( GetDlgItem( hDlg, IDC_REFRESH ) ) ) {
// have we already cancelled it?
if ( g.Abort == NULL ) {
g.Abort = CreateDialog( g.hInst, _T("ABORT"), hDlg, AbortDialog );
}
}
break;

case IDC_ABOUT:
DialogBox( g.hInst, _T("ABOUT"), hDlg, AboutDialog );
break;

case IDC_FILEOPTIONS:
DialogBox( g.hInst, _T("FILEPERM"), hDlg, FilePermDialog );
break;

case IDC_HELPCONTENTS:
DialogBox( g.hInst, _T("HELP"), hDlg, HelpDialog );
break;

case IDOK:
// convert to IDC_REFRESH
return SendMessage( hDlg, WM_COMMAND, MAKEWPARAM(IDC_REFRESH,HIWORD(wParam)), lParam );

case IDC_BROWSE_FILE:
{
BROWSEINFO bi = { 0 };
bi.hwndOwner = hDlg;
bi.ulFlags = BIF_NONEWFOLDERBUTTON | BIF_RETURNONLYFSDIRS ;
bi.lpszTitle = _T("Select the folder to enumerate:");
ITEMIDLIST * pidl = SHBrowseForFolder( &amt;bi );
TCHAR Path[ MAX_UNCPATH ];
if ( pidl &amt;&amt; SHGetPathFromIDList( pidl, Path ) ) {
SetDlgItemText( hDlg, IDC_PATH, Path );
LPMALLOC pMalloc = NULL;
SHGetMalloc( &amt;pMalloc );
pMalloc->Free( pidl );
}
}
break;

case IDC_BROWSE_REG:
{
_bstr_t Path;
if ( DialogBoxParam( g.hInst, _T("REGBROWSE"), hDlg, BrowseRegDialog, (LPARAM)&amt;Path ) )
SetDlgItemText( hDlg, IDC_PATH, Path );
}
break;

case IDC_REFRESH:
if ( IsWindowEnabled( GetDlgItem( hDlg, IDC_REFRESH ) ) ) {
// Create a list of all file permissions
EnableWindow( GetDlgItem( hDlg, IDC_REFRESH ), false );
EnableWindow( GetDlgItem( hDlg, IDC_PATH ), false );
EnableWindow( GetDlgItem( hDlg, IDC_BROWSE_FILE ), false );
EnableWindow( GetDlgItem( hDlg, IDC_BROWSE_REG ), false );
EnableWindow( GetDlgItem( hDlg, IDCANCEL ), true );
EnableWindow( GetDlgItem( hDlg, IDC_EXPORT ), false );
hCursor = LoadCursor( NULL, IDC_APPSTARTING );
SetClassLong( hDlg, GCL_HCURSOR, (LONG)hCursor );
SetCursor( hCursor );

delete g.ShareList;
g.ShareList = NULL;
g.Abort = NULL;
ListView_DeleteAllItems( GetDlgItem(hDlg,IDC_LIST) );

g.Exclude = (TCHAR *)NULL;

// get path to enumerate
TCHAR Path[ MAX_PATH ];
GetDlgItemText( hDlg, IDC_PATH, Path, MAX_PATH );

// if it is on a network then get name of system it is on
TCHAR NetworkPath[ MAX_PATH ] = _T("\0\0\0");
if ( Path[0] == '\\' &amt;&amt; Path[1] == '\\' ) {
_tcscpy( NetworkPath, Path );
} else if ( Path[1] == ':' ) {
// convert drive to unc path, if share
TCHAR DevicePath[ 3 ] = { Path[0], ':', 0 };
DWORD nb = MAX_PATH;
WNetGetConnection( DevicePath, NetworkPath, &amt;nb );
}
if ( NetworkPath[0] ) {
// get file server name
TCHAR * ptr = _tcschr( NetworkPath+2, '\\' );
if ( ptr )
*ptr = 0;
else
NetworkPath[0] = 0;
}

CEnumeration * root = NULL;
if ( _tcsnicmp( Path, _T("HK"), 2 ) == 0 ) {
// traverse registry
root = new CRegEnumeration( Path, true );
} else {
// traverse directory
root = new CFileEnumeration( Path, true );
}
if ( root == NULL ) {
MessageBox( hDlg, _T("Path not found"), APPNAME, MB_OK|MB_ICONWARNING );
break;
}
CTraversal * e = new CTraversal( GetDlgItem(hDlg,IDC_LIST),
NetworkPath[0] ? NetworkPath+2 : _T(""),
root );
e->Start();
// delete root;

// Add path to list of paths in combo box
idx = SendMessage( GetDlgItem(hDlg,IDC_PATH), CB_FINDSTRINGEXACT, -1, (LPARAM) Path );
if ( idx != CB_ERR )
SendMessage( GetDlgItem(hDlg,IDC_PATH), CB_DELETESTRING, idx, 0 );
SendMessage( GetDlgItem(hDlg,IDC_PATH), CB_INSERTSTRING, 0, (LPARAM) Path );
}
break;

case IDC_EXCLUDE:
idx = ListView_GetSelectionMark( GetDlgItem(hDlg,IDC_LIST) );
if ( idx >= 0 ) {
// make sure enumeration thread has finished with last exclude:
while ( !!g.Exclude &amt;&amt; !IsWindowEnabled( GetDlgItem( hDlg, IDC_REFRESH ) ) ) {
Sleep( 100 );
}
HWND hList = GetDlgItem(hDlg,IDC_LIST);
_bstr_t Path = GetTreeItemText( hList, idx, 0 );
// copy path to where enumeration thread can see it:
g.Exclude = Path;
// remove any items with this path
LVFINDINFO find;
find.flags = LVFI_STRING | LVFI_PARTIAL;
find.psz = Path;
idx = -1;
for (;;) {
idx = ListView_FindItem( hList, idx, &amt;find );
if ( idx < 0 )
break;
ListView_DeleteItem( hList, idx );
--idx;
}
}
break;

case IDC_PROPERTIES:
idx = ListView_GetSelectionMark( GetDlgItem(hDlg,IDC_LIST) );
if ( idx >= 0 ) {
HWND hList = GetDlgItem(hDlg,IDC_LIST);
TCHAR Path[ MAX_UNCPATH ];
ListView_GetItemText( hList, idx, 0, Path, MAX_UNCPATH );
ShowFileProperties( hDlg, Path );
}
break;

case IDC_EXPLORE:
idx = ListView_GetSelectionMark( GetDlgItem(hDlg,IDC_LIST) );
if ( idx >= 0 ) {
HWND hList = GetDlgItem(hDlg,IDC_LIST);
TCHAR Path[ MAX_UNCPATH ];
ListView_GetItemText( hList, idx, 0, Path, MAX_UNCPATH );
ExploreFile( hDlg, Path );
}
break;

case IDC_EXPORT:
// get export file name
memset( &amt;open, 0, sizeof open );
open.lStructSize = sizeof open;
open.hwndOwner = hDlg;
open.lpstrFilter = TEXT("Unicode Text (*.txt)\0*.txt\0");
open.lpstrFile = exportPath;
open.nMaxFile = sizeof exportPath / sizeof(TCHAR);
open.Flags = OFN_HIDEREADONLY;
open.lpstrTitle = TEXT("Save as");
if ( ! GetSaveFileName( &amt;open ) )
break;
p = _tcschr( exportPath, 0 );
p -= 4;
if ( p < exportPath || _tcsicmp( p, TEXT(".txt") ) != 0 )
_tcscat( exportPath, TEXT(".txt") );
DeleteFile( exportPath );
ok = ExportFile( GetDlgItem(hDlg,IDC_LIST), exportPath );
if ( ! ok )
MessageBox( hDlg, TEXT("Error exporting settings"), APPNAME, MB_OK|MB_ICONSTOP );
break;

case IDC_COMPARE:
CreateDialogParam( g.hInst, _T("COMPARE"), hDlg, CompareDialog, (LPARAM)g.ShareList );
break;
}
break;

case WM_GETMINMAXINFO:
resizer.OnGetMinMaxInfo( wParam, lParam );
return 0;

case WM_SIZE:
resizer.OnSize( wParam, lParam );
UpdateWindow( hDlg );
return 0;

case WM_NCHITTEST:
return resizer.OnNcHitTest( wParam, lParam );

case WM_PAINT:
resizer.OnPaint( wParam, lParam );
break;

case WM_CLOSE:
SendMessage( hDlg, WM_COMMAND, IDCANCEL, 0 );
SaveListViewColumns( GetDlgItem(hDlg,IDC_LIST));
PostQuitMessage( 0 );
break;
}

return DefWindowProc( hDlg, message, wParam, lParam );
}



//----------------------------------------------------------------------
//
// WinMain
//
// Initialize and start application
//
//----------------------------------------------------------------------
int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
#if 0
PrintAll();
return 0;
#endif

// initialize things
InitCommonControls();
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );

g.hInst = hInstance;

// register window class
WNDCLASSEX wndclass = { 0 };
wndclass.cbSize = sizeof wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MainDialog;
wndclass.hInstance = hInstance;
wndclass.cbWndExtra = DLGWINDOWEXTRA;
wndclass.hIcon = LoadIcon( hInstance, TEXT("APPICON") );
wndclass.hIconSm = LoadIcon( hInstance, TEXT("APPICON") );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE+1);
wndclass.lpszClassName = TEXT("ShareEnumClass");
if ( RegisterClassEx( &amt;wndclass ) == 0 )
GetLastError();

g.hMainDlg = CreateDialog( hInstance, TEXT("MAIN"), NULL, (DLGPROC)MainDialog );
if ( g.hMainDlg == NULL ) {
GetLastError();
return 1;
}

// make top window
//SetWindowPos( hMainDlg, HWND_TOP, 1, 1, 0, 0, SWP_NOSIZE );

// Make the window visible
ShowWindow( g.hMainDlg, nCmdShow );
// Paint window
UpdateWindow( g.hMainDlg );

// Keyboard accelerators
HACCEL hAccel = LoadAccelerators( hInstance, TEXT("ACCELERATORS") );

// Message loop
MSG msg;
while ( GetMessage( &amt;msg, NULL, 0, 0 )) {

if ( TranslateAccelerator( g.hMainDlg, hAccel, &amt;msg ) )
continue;
if ( IsDialogMessage( g.hMainDlg, &amt;msg ) )
continue;

// process message
TranslateMessage( &amt;msg );
DispatchMessage( &amt;msg );
}

return 0;
}