ACLEDIT.CPP

//--acledit.cpp---------------------------------------------------------------- 
//
// Folder ACLs editing utility.
//
// Copyright (C) Microsoft Corp., 1986-1996. All rights reserved.
//
//-----------------------------------------------------------------------------

//
// Begin program documentation
//

#if0

Command line arguments: See Usage(), or invoke acledit.exe with no command
line arguments to get a usage note.

#endif

//
// End program documentation
//

#include "edk.h"
#include "errno.h"
#include "acledit.h"

//
// Enumeration, structure, and other type definitions
//

// ACLs editing commands:

enum EDITCMD// ec
{
EDITCMD_LIST,
EDITCMD_INSERT,
EDITCMD_DELETE,
EDITCMD_MODIFY,
EDITCMD_HELP,
EDITCMD_INVALID
};

structRIGHTSINFO// ri
{
LPSTRpszKeyWord;
ULONGcchKeyWord;
LONGlRights;
};

//
// Forward function declarations.
//

static
BOOL
bDoDelete(VOID);

static
BOOL
bDoInsert(VOID);

static
BOOL
bDoList(VOID);

static
BOOL
bDoModify(VOID);

static
BOOL
bParseCmdLine(
IN INT argc,
IN CHAR * argv[]
);

static
BOOL
bParseRightsLst(VOID);

static
VOID
DoHelp(VOID);

static
VOID
EventLogHexErrorMsg(
INDWORDdwEvent,
INHRESULThr
);

static
VOID
PrintRightsString(
INLONGlRights
);

static
VOID
PrintErr(
INCHAR *Format
...
);

static
VOID
Usage(VOID);

//
// Static storage
//

// Rights information array. This is layed out in a specific manner, with
// all single bit rights first. The order should not be changed. The
// manifest constant NONEINDX is used to limit lookup of keywords to the
// range of single bit values, since the other keywords are special cases.

staticRIGHTSINFOria[] =
{
"ReadAny", sizeof("ReadAny") - 1, frightsReadAny,
"Create", sizeof("Create") - 1, frightsCreate,
"EditOwned", sizeof("EditOwned") - 1, frightsEditOwned,
"DeleteOwned", sizeof("DeleteOwned") - 1, frightsDeleteOwned,
"EditAny", sizeof("EditAny") - 1, frightsEditAny,
"DeleteAny", sizeof("DeleteAny") - 1, frightsDeleteAny,
"CreateSubfolder", sizeof("CreateSubfolder") - 1, frightsCreateSubfolder,
"Owner", sizeof("Owner") - 1, frightsOwner,
"Contact", sizeof("Contact") - 1, frightsContact,
"None", sizeof("None") - 1, rightsNone,
"ReadOnly", sizeof("ReadOnly") - 1, rightsReadOnly,
"ReadWrite", sizeof("ReadWrite") - 1, rightsReadWrite,
"All", sizeof("All") - 1, rightsAll
};

#defineNONEINDX9

staticLPSTRpszProfile =NULL;
staticLPSTRpszStore =NULL;
staticLPSTRpszFolder =NULL;
staticLPSTRpszRights =NULL;
staticLPSTRpszUser =NULL;

staticBOOLfInboxArgFnd =FALSE;

staticEDITCMDEditCmd =EDITCMD_INVALID;

staticBOOLfPositionFnd =FALSE;
staticLONGlCursor = 0;

staticLONGlRights =0;

staticLPMAPISESSIONlpSession =NULL;

staticLPMDBlpStore =NULL;

staticULONGcbEIDStore = 0;

staticLPENTRYIDlpEIDStore =NULL;

staticULONGcbEIDFolder = 0;

staticLPENTRYIDlpEIDFolder =NULL;

staticLPFOLDERACLSlpFolderACLs =NULL;

// Command line argument flags, and array used by
// _HrExpandCommandLineArgument():

staticCHARszProfileArg[] ="profile";

staticCHARszStoreArg[] ="store";
staticCHARszFolderArg[] ="folder";
staticCHARszInboxArg[] ="inbox";

staticCHARszListArg[] ="list";
staticCHARszInsertArg[] ="insert";
staticCHARszModifyArg[] ="modify";
staticCHARszDeleteArg[] ="delete";

staticCHARszPositionArg[] ="position";

staticCHARszRightsArg[] ="rights";

staticCHARszUserArg[] ="user";

staticCHARszHelpArg[] ="help";

staticCHARszQuestionArg[] ="?";

staticCHAR *rgpszArgArray[] ={
szProfileArg,
szStoreArg,
szFolderArg,
szInboxArg,
szListArg,
szInsertArg,
szModifyArg,
szDeleteArg,
szPositionArg,
szRightsArg,
szUserArg,
szHelpArg,
szQuestionArg
};

staticULONGcErrs =0;

//
// Functions
//

// $--bDoDelete----------------------------------------------------------------
//
// DESCRIPTION:Execute the DELETE command.
//
// INPUT:None.
//
// RETURNS:TRUE on success; FALSE otherwise.
//
//-----------------------------------------------------------------------------

BOOL
bDoDelete(VOID) // RETURNS: BOOL
{
HRESULThr =NOERROR;
BOOLbrc =FALSE;
LONGlCurCursor =0;

DEBUGPRIVATE("bDoDelete()\n");

hr = lpFolderACLs->HrSeek(lCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_SEEK_FAILED, hr);
goto cleanup;
}

// We do prior check of cursor location to be able to cleanly specify
// the error.

hr = lpFolderACLs->HrTell(&lCurCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_TELL_FAILED, hr);
goto cleanup;
}

if (lCurCursor == ACL_PAST_END)
{
PrintErr("ACL at position END does not exist");
goto cleanup;
}

hr = lpFolderACLs->HrDelete();

if (FAILED(hr))
{
PrintErr("Deletion failed. If the ACL specified is the default ACL or\n"
"is the ACL for the current user on a public folder, "
"it may not be deleted");
cErrs--;
EventLogHexErrorMsg(ACLEDIT_DELETE_FAILED, hr);
goto cleanup;
}

brc = TRUE;

cleanup:

return brc;
}


// $--bDoInsert----------------------------------------------------------------
//
// DESCRIPTION:Execute the INSERT command.
//
// INPUT:None.
//
// RETURNS:TRUE on success; FALSE otherwise.
//
//-----------------------------------------------------------------------------

BOOL
bDoInsert(VOID) // RETURNS: BOOL
{
LPSPropValueaPropVals = NULL;
BOOLbrc =FALSE;
ULONGcbEIDUser =0;
HRESULThr =NOERROR;
ULONGi =0;
LONGlCurCursor =0;
LONGlNewRights =0;
LPADRBOOKlpAdrBook =NULL;
LPADRENTRYlpAdrEntry = NULL;
LPADRLISTlpAdrList =NULL;
LPENTRYIDlpEIDUser =NULL;
LPSTRlpszDisplayName =NULL;

DEBUGPRIVATE("bDoInsert()\n");

hr = lpFolderACLs->HrSeek(lCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_SEEK_FAILED, hr);
goto cleanup;
}

// We do prior check of cursor location to be able to cleanly specify
// the error.

hr = lpFolderACLs->HrTell(&lCurCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_TELL_FAILED, hr);
goto cleanup;
}

// Open the address book and get the user entryid.

hr = lpSession->OpenAddressBook(0, NULL, AB_NO_DIALOG, &lpAdrBook);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_OPENADDRESSBOOK_FAILED, hr);
goto cleanup;
}

hr = MAPIAllocateBuffer(CbNewADRLIST(1), (LPVOID FAR *)&lpAdrList);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_ALLOCATION_FAILED, hr);
goto cleanup;
}

lpAdrEntry = lpAdrList->aEntries;

lpAdrList->cEntries = 1;

lpAdrEntry->ulReserved1 =0;
lpAdrEntry->cValues =1;
lpAdrEntry->rgPropVals =NULL;

hr = MAPIAllocateBuffer(sizeof(SPropValue), (LPVOID FAR *)&aPropVals);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_ALLOCATION_FAILED, hr);
goto cleanup;
}

lpAdrEntry->rgPropVals =aPropVals;

aPropVals[0].ulPropTag =PR_DISPLAY_NAME;
aPropVals[0].Value.lpszA =NULL;

hr = MAPIAllocateMore(strlen(pszUser) + 1,
lpAdrEntry->rgPropVals,
(LPVOID FAR *)&lpAdrEntry->rgPropVals[0].Value.lpszA);
if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_ALLOCATION_FAILED, hr);
goto cleanup;
}

strcpy(lpAdrEntry->rgPropVals[0].Value.lpszA, pszUser);

hr = lpAdrBook->ResolveName(0, 0, NULL, lpAdrList);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_RESOLVENAME_FAILED, hr);
goto cleanup;
}

// Pick up ptr values again, assuming there was reallocation.

lpAdrEntry = lpAdrList->aEntries;
aPropVals =lpAdrEntry->rgPropVals;

for (i = 0; i < lpAdrEntry->cValues; i++)
{
if (aPropVals[i].ulPropTag == PR_ENTRYID)
{
cbEIDUser = aPropVals[i].Value.bin.cb;
lpEIDUser = (LPENTRYID)aPropVals[i].Value.bin.lpb;

break;
}
}

for (i = 0; i < lpAdrEntry->cValues; i++)
{
if (aPropVals[i].ulPropTag == PR_DISPLAY_NAME)
{
lpszDisplayName = aPropVals[i].Value.lpszA;
break;
}
}

if (lpEIDUser == NULL || lpszDisplayName == NULL)
{
hr = MAPI_E_NOT_FOUND;
EventLogHexErrorMsg(ACLEDIT_RESOLVENAME_FAILED, hr);
goto cleanup;

}

// Finally do the insert.

hr = lpFolderACLs->HrInsert(lRights,
lpszDisplayName,
cbEIDUser,
lpEIDUser,
&lNewRights);
if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_INSERT_FAILED, hr);

if (hr == E_FAIL)
{
PrintErr("Insertion failed. "
"If ACL for user already exists, use modify");
cErrs--;
}
else
{
PrintErr("Insertion failed");
cErrs--;
}

goto cleanup;
}

// If the rights obtained are not exactly what is expected (which due to
// certain Exchange vagaries may be the case), the modified rights
// will be printed out.

if (lRights != lNewRights)
{
printf("Rights actually set are '");

PrintRightsString(lNewRights);

printf("'\n\n");
}

brc = TRUE;

cleanup:

FREEPADRLIST(lpAdrList);
ULRELEASE(lpAdrBook);

return brc;
}


// $--bDoList------------------------------------------------------------------
//
// DESCRIPTION:Execute the LIST command.
//
// INPUT:None.
//
// RETURNS:TRUE on success; FALSE otherwise.
//
//---------------------------------------------------------------------------

BOOL
bDoList(VOID) // RETURNS: BOOL
{
HRESULThr =NOERROR;
BOOLbrc =FALSE;
HANDLEhConsole =GetStdHandle(STD_OUTPUT_HANDLE);
LONGlCurCursor =0;

DEBUGPRIVATE("bDoList()\n");

if (!fPositionFnd)
lCursor = 0;

hr = lpFolderACLs->HrSeek(lCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_SEEK_FAILED, hr);
goto cleanup;
}

// Check for empty table or invalid cursor position.

hr = lpFolderACLs->HrTell(&lCurCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_TELL_FAILED, hr);
goto cleanup;
}

if (pszStore == NULL)
pszStore = "default";

if (!fPositionFnd)
{
printf("Listing of ACL table in folder \\%s in %s store:\n\n",
pszFolder, pszStore);
}
else if (lCurCursor != ACL_PAST_END)
{
printf("Listing of ACL table record %d in folder \\%s in %s store:\n\n",
lCurCursor, pszFolder, pszStore);
}

if (lCurCursor == ACL_PAST_END)
{
if (fPositionFnd)
{
PrintErr("ACL at position %d does not exist", lCursor);
}
else
{
printf("ACL table is empty.\n");
brc = TRUE;
}
goto cleanup;
}

while (TRUE)
{
ULONGcbentryid;
LONGlRights;
LPSTRlpszDisplayName =NULL;
LPENTRYIDlpentryid =NULL;

hr = lpFolderACLs->HrGet(&lRights,
&lpszDisplayName,
&cbentryid,
&lpentryid);
if (FAILED(hr))
{
HRESULThrSaved = hr;

hr = lpFolderACLs->HrTell(&lCurCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_TELL_FAILED, hr);
goto cleanup;
}

if (lCurCursor != ACL_PAST_END || hrSaved != E_FAIL)
{
EventLogHexErrorMsg(ACLEDIT_GET_FAILED, hrSaved);
goto cleanup;
}
break;
}
else
{
// Print listing:

printf("For record at cursor position %d:\n\n", lCurCursor);

printf(" User Name is '%s'\n", lpszDisplayName);

printf(" Rights are '");

PrintRightsString(lRights);

printf("'\n\n");

lCurCursor++;
}

// Clean up allocations:

MAPIFREEBUFFER(lpszDisplayName);
MAPIFREEBUFFER(lpentryid);

// Only dump one record if cursor position was specified.

if (fPositionFnd)
break;
}

brc = TRUE;

cleanup:

return brc;
}


// $--bDoModify----------------------------------------------------------------
//
// DESCRIPTION:Execute the MODIFY command.
//
// INPUT:None.
//
// RETURNS:TRUE on success; FALSE otherwise.
//
//-----------------------------------------------------------------------------

BOOL
bDoModify(VOID) // RETURNS: BOOL
{
HRESULThr =NOERROR;
BOOLbrc =FALSE;
LONGlCurCursor =0;
LONGlNewRights =0;

DEBUGPRIVATE("bDoModify()\n");

hr = lpFolderACLs->HrSeek(lCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_SEEK_FAILED, hr);
goto cleanup;
}

// We do prior check of cursor location to be able to cleanly specify
// the error.

hr = lpFolderACLs->HrTell(&lCurCursor);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_TELL_FAILED, hr);
goto cleanup;
}

if (lCurCursor == ACL_PAST_END)
{
PrintErr("ACL at position END does not exist");
goto cleanup;
}

hr = lpFolderACLs->HrModify(lRights, &lNewRights);

if (FAILED(hr))
{
PrintErr("Modification failed");
cErrs--;
EventLogHexErrorMsg(ACLEDIT_MODIFY_FAILED, hr);
goto cleanup;
}

// If the rights obtained are not exactly what is expected (which due to
// certain Exchange vagaries may be the case), the modified rights
// will be printed out. For instance the Owner right is always retained
// for the current user ACL on a public folder, and setting EditAny will
// also cause EditOwned to be set.

if (lRights != lNewRights)
{
printf("Rights actually set are '");

PrintRightsString(lNewRights);

printf("'\n\n");
}

brc = TRUE;

cleanup:

return brc;
}


// $--bParseCmdLine------------------------------------------------------------
//
// DESCRIPTION: Parse the command line.
//
// INPUT:
//
//[argc]-- Count of arguments from command line.
//[argv]-- Array of arguments from command line.
//
// RETURNS:TRUE on success; FALSE otherwise.
//
//-----------------------------------------------------------------------------

BOOL
bParseCmdLine(// RETURNS: BOOL
IN INT argc, // argument count
IN CHAR * argv[] // array of arguments
)
{
BOOL bRetVal = FALSE;
HRESULThr =NOERROR;
CHAR *pszArgData =NULL;
CHAR *pszFlagName =NULL;

DEBUGPRIVATE("bParseCmdLine()\n");

argc--;// Get rid of command argument.
argv++;

if (argc == 0)
goto cleanup;

while (argc > 0)
{
hr =_HrExpandCommandLineArgument(
*argv,
rgpszArgArray,
sizeof(rgpszArgArray)/sizeof(rgpszArgArray[0]),
NULL,
&pszFlagName,
&pszArgData
);

if (FAILED(hr))
{
PrintErr("Invalid command line argument");
goto cleanup;
}

if (pszFlagName == szProfileArg)
{
if (pszProfile != NULL)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

pszProfile = pszArgData;
}
else if (pszFlagName == szStoreArg)
{
if (pszStore != NULL)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

pszStore = pszArgData;
}
else if (pszFlagName == szFolderArg)
{
if (pszFolder != NULL)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

pszFolder = pszArgData;
}
else if (pszFlagName == szInboxArg)
{
// Note that you can put "/inbox" on the cmd line as many times
// as you like without creating a problem! :-)

if (pszStore != NULL || pszFolder != NULL)
{
PrintErr("Conflicting command line arguments");
goto cleanup;
}

if (pszArgData != NULL)
{
PrintErr("The /%s argument does not take a value", szInboxArg);
goto cleanup;
}

fInboxArgFnd = TRUE;
}
else if (pszFlagName == szListArg)
{
if (EditCmd != EDITCMD_INVALID)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

if (pszArgData != NULL)
{
PrintErr("The /%s argument does not take a value", szListArg);
goto cleanup;
}

EditCmd = EDITCMD_LIST;
}
else if (pszFlagName == szInsertArg)
{
if (EditCmd != EDITCMD_INVALID)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

if (pszArgData != NULL)
{
PrintErr("The /%s argument does not take a value",szInsertArg);
goto cleanup;
}

EditCmd = EDITCMD_INSERT;
}
else if (pszFlagName == szModifyArg)
{
if (EditCmd != EDITCMD_INVALID)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

if (pszArgData != NULL)
{
PrintErr("The /%s argument does not take a value",szInsertArg);
goto cleanup;
}

EditCmd = EDITCMD_MODIFY;
}
else if (pszFlagName == szDeleteArg)
{
if (EditCmd != EDITCMD_INVALID)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

if (pszArgData != NULL)
{
PrintErr("The /%s argument does not take a value",szDeleteArg);
goto cleanup;
}

EditCmd = EDITCMD_DELETE;
}
else if (pszFlagName == szPositionArg)
{
LPSTRpszValue = pszArgData;

if (fPositionFnd)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

if (pszValue == NULL || *pszValue == '\0')
{
PrintErr("The /%s argument requires a value", szPositionArg);
goto cleanup;
}

if (!_stricmp(pszValue, "END"))
{
lCursor = RULE_PAST_END;
}
else
{
errno = 0;

lCursor = strtol(pszValue, (CHAR **)NULL, 10);

if ((lCursor == LONG_MAX || lCursor == LONG_MIN) &&
errno == ERANGE)
{
PrintErr("Command line argument '%s' is invalid", *argv);
goto cleanup;
}
}

fPositionFnd = TRUE;
}
else if (pszFlagName == szRightsArg)
{
if (pszRights != NULL)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

pszRights = pszArgData;

if (pszRights == NULL || *pszRights == '\0')
{
PrintErr("The /%s argument requires a value", szRightsArg);
goto cleanup;
}

if (!bParseRightsLst())
goto cleanup;
}
else if (pszFlagName == szUserArg)
{
if (pszUser != NULL)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}

pszUser = pszArgData;

if (pszUser == NULL || *pszUser == '\0')
{
PrintErr("The /%s argument requires a value", szRightsArg);
goto cleanup;
}
}
else if (pszFlagName == szHelpArg||
pszFlagName == szQuestionArg)
{
EditCmd = EDITCMD_HELP;

// If we encounter /help or /?, we pretty much ignore everything
// else and produce a help msg.

bRetVal = TRUE;
goto cleanup;
}
else
{
PrintErr("Command line argument '%s' is invalid", *argv);
goto cleanup;
}

argc--;
argv++;
}

// Check that all args needed have been provided.

if (pszProfile == NULL)
{
PrintErr("Command line must specify %s argument", szProfileArg);
goto cleanup;
}

if (!fInboxArgFnd &&
(pszStore == NULL || *pszStore == '\0'||
pszFolder == NULL || *pszFolder == '\0'))
{
PrintErr("Command line must specify %s or %s and %s arguments",
szInboxArg, szStoreArg, szFolderArg);

goto cleanup;
}

if (EditCmd == EDITCMD_INVALID)
{
PrintErr("Command line must specify a command argument");
goto cleanup;
}
else if (EditCmd != EDITCMD_LIST && EditCmd != EDITCMD_INSERT)
{
if (!fPositionFnd)
{
PrintErr("Command argument specified requires %s argument",
szPositionArg);

goto cleanup;
}
}

if (EditCmd == EDITCMD_INSERT)
{
if (!fPositionFnd)
{
// Defaults to end of list.

fPositionFnd =TRUE;
lCursor =ACL_PAST_END;
}

if (pszRights == NULL)
{
PrintErr("Insert command requires %s argument", szRightsArg);
goto cleanup;
}
else if (pszUser == NULL)
{
PrintErr("Insert command requires %s argument", szUserArg);
goto cleanup;
}
}
else if (EditCmd == EDITCMD_MODIFY)
{
if (pszRights == NULL)
{
PrintErr("Modify command requires %s argument", szRightsArg);
goto cleanup;
}
}
else
{
if (pszRights != NULL)
{
PrintErr("A %s argument is only used with the insert or modify "
"command", szRightsArg);

goto cleanup;
}
else if (pszUser != NULL)
{
PrintErr("A %s argument is only used with the insert command",
szUserArg);

goto cleanup;
}
}

bRetVal = TRUE;

cleanup:

return bRetVal;
}


// $--bParseRightsLst----------------------------------------------------------
//
// DESCRIPTION:Parse the rights list, storing the result in the global lRights.
//
// RETURNS:TRUE on success; FALSE otherwise.
//
// Notes:The rights keywords are case insensitive, but otherwise must
//match exactly. There is no checking for keyword redundancy or
//conflict.
//-----------------------------------------------------------------------------

BOOL
bParseRightsLst(VOID)
{
BOOLbrc =FALSE;
ULONGcch =0;
ULONGi =0;
CHAR *pchBegin =pszRights;
CHAR *pchEnd =NULL;

while (*pchBegin != '\0')
{
while (isspace(*pchBegin))
pchBegin++;

pchEnd = pchBegin;

do
{
pchEnd++;
}while (!isspace(*pchEnd) && *pchEnd != '\0');

cch = pchEnd - pchBegin;

brc = FALSE;

for (i = 0; i < sizeof(ria)/sizeof(ria[0]); i++)
{
if (cch == ria[i].cchKeyWord&&
!memicmp(pchBegin, ria[i].pszKeyWord, cch))
{
lRights |= ria[i].lRights;
brc = TRUE;
break;
}
}

if (!brc)
{
PrintErr("Invalid keyword '%.*s' in the rights list",
cch, pchBegin);

goto cleanup;
}

pchBegin = pchEnd;
}

cleanup:

return brc;
}


// $--DoHelp------------------------------------------------------------------
//
// DESCRIPTION:Execute the HELP command.
//
// INPUT:None.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------

VOID
DoHelp(VOID) // RETURNS: VOID
{
printf(
"\nUSAGE: ACLEDIT /Profile=<ProfileName> <Folder> <Command>\n\n"
"<Folder> ::= /Store=<NameOfStore> /Folder=<FolderPath> ||\n"
" /Inbox\n\n"
"<Command> ::= /List [/Position=<Pos>] ||\n"
" /Insert /Rights=<RightLst> /User=<UserName> [/Position=<Pos>] ||\n"
" /Modify /Position=<Pos> /Rights=<RightLst> ||\n"
" /Delete /Position=<Pos> ||\n"
"<Position> ::= <ACL Number> ||\n"
" END\n\n"
"<RightLst> ::= <Right>...\n"
"<Right> ::= ReadAny ||\n"
" Create ||\n"
" EditOwned ||\n"
" DeleteOwned ||\n"
" EditAny ||\n"
" DeleteAny ||\n"
" CreateSubfolder ||\n"
" Owner ||\n"
" Contact ||\n"
" None ||\n"
" ReadOnly ||\n"
" ReadWrite ||\n"
" All ||\n"
"<UserName> ::= Display name of user against which ACL should be applied\n"
);
}


//$--EventLogHexErrorMsg-------------------------------------------------------
//
// DESCRIPTION: Write a message to the event log that contains a hex error code.
//This function is used for simple error messages that have the
//HRESULT (or other 4 byte error code) as the only insert.
//
// INPUT:
//
//[dwEvent]-- Event code for message string.
//[hr]-- HRESULT that will be printed in hex as the insert in the
// message string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------

VOID
EventLogHexErrorMsg(
INDWORDdwEvent,// event code for message string
INHRESULThr// HRESULT insert in the message string
)
{
CHARszErrorCode[11];

sprintf(szErrorCode, "%#.8x", hr);

EventLogMsg(dwEvent, 1, szErrorCode, 0);
}


//$--PrintErr------------------------------------------------------------------
//
// DESCRIPTION:Print an error message to stdout in a standard format.
//
// INPUT:
//
//[Format]-- Printf-style format string.
//[...]-- Variable argument parameters for format string.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------

VOID
PrintErr( // RETURNS: VOID
INCHAR *Format // error message format
...
)
{
va_listargs;

DEBUGPRIVATE("PrintErr()\n");

cErrs++;

printf("ERROR: ");

va_start(args, Format);

vprintf(Format, args);

va_end(args);

printf(".\n\n");

return;
}


// $--PrintRightsString--------------------------------------------------------
//
// DESCRIPTION:Print a readable representation of the rights.
//
// INPUT:
//
//[lRights]-- Rights to be printed.
//
// RETURNS: Nothing.
//
//-----------------------------------------------------------------------------

VOID
PrintRightsString(
INLONGlRights
)
{
BOOLbSpaceNeeded =FALSE;
ULONGi =0;

// We must plow through the special cases and then hit everything
// else. We express 'ReadWrite' as 'ReadAny EditAny' since they are
// equivalent, and 'ReadOnly' as 'ReadAny' since they are equivalent.

if (lRights == rightsAll)
{
printf("All");
}
else if (lRights == rightsNone)
{
printf("None");
}
else
{
for (i = 0; i < NONEINDX; i++)
{
if (lRights & ria[i].lRights)
{
if (bSpaceNeeded)
printf(" %s", ria[i].pszKeyWord);
else
printf("%s", ria[i].pszKeyWord);

bSpaceNeeded = TRUE;
}
}
}
}


// $--Usage--------------------------------------------------------------------
//
// DESCRIPTION:Print a usage message to the console.
//
// INPUT:None.
//
// RETURNS:VOID
//-----------------------------------------------------------------------------

VOID
Usage(VOID) // RETURNS: VOID
{
DEBUGPRIVATE("Usage()\n");
printf(
"\nUSAGE: ACLEDIT /Profile=<ProfileName> <Folder> <Command>\n\n"
"Enter ACLEDIT /? for details.\n"
);
}


// $--main---------------------------------------------------------------------
//
// DESCRIPTION: acledit.exe main() routine.
//
// INPUT: Standard argc and argv.
//
// RETURNS: NOERROR on success;
// E_INVALIDARG if bad input,
// E_OUTOFMEMORY if memory problems.
// E_FAIL on any execution error.
//
//-----------------------------------------------------------------------------

HRESULT
main( // RETURNS: HRESULT

IN  INT     argc,       // argument count 
IN CHAR * argv[] // array of command line arguments
)
{
HRESULThr =NOERROR;
BOOLbMAPIInitialized =FALSE;
BOOLbPrintCompletion =TRUE;
BOOLbEventLoggingEnabled =FALSE;

DEBUGPUBLIC("main()\n");

// Initialize event logging.

hr = HrEventOpenLog("EDKAclEdit", NULL, NULL, NULL, NULL, NULL);

if (FAILED(hr))
{
PrintErr("Event logging could not be enabled; error code=%#x\n",hr);
goto cleanup;
}
else
{
bEventLoggingEnabled = TRUE;
}

// NOTE: Must initialize MAPI before parsing command line,
// as the parsing of the command line requires MAPI memory
// allocation.

if (argc == 1)
{
Usage();

bPrintCompletion = FALSE;

goto cleanup;
}

hr = MAPIInitialize(0);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_MAPIINITIALIZE_FAILED, hr);
goto cleanup;
}

bMAPIInitialized = TRUE;

if (!bParseCmdLine(argc, argv))
{
printf(" Enter ACLEDIT /? for help.\n");

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

if (EditCmd == EDITCMD_HELP)
{
DoHelp();

hr = NOERROR;

bPrintCompletion = FALSE;

goto cleanup;
}

hr = MAPILogonEx(0,
pszProfile,
NULL,
MAPI_EXTENDED | MAPI_NEW_SESSION,
&lpSession);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_MAPILOGON_FAILED, hr);
goto cleanup;
}

// Get the entry id of the specified store and open it.

if (fInboxArgFnd)
{
hr = HrMAPIFindDefaultMsgStore(lpSession, &cbEIDStore, &lpEIDStore);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_FINDDEFAULTMSGSTORE_FAILED, hr);
goto cleanup;
}

pszFolder = "Top of Information Store\\Inbox";
}
else
{
hr = HrMAPIFindStore(lpSession, pszStore, &cbEIDStore, &lpEIDStore);

if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_FINDSTORE_FAILED, hr);
goto cleanup;
}
}

hr = lpSession->OpenMsgStore(0,
cbEIDStore,
lpEIDStore,
NULL,
MAPI_BEST_ACCESS|
MAPI_DEFERRED_ERRORS,
&lpStore);
if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_OPENMSGSTORE_FAILED, hr);
goto cleanup;
}

// Get the entry id of the specified folder and open it.

if (fInboxArgFnd)
{
LPSTRlpszExplicitClass = "";

hr = lpStore->GetReceiveFolder(NULL,
0,
&cbEIDFolder,
&lpEIDFolder,
&lpszExplicitClass);
if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_GETRECEIVEFOLDER_FAILED, hr);
goto cleanup;
}

pszFolder = "Inbox";
}
else
{
hr = HrMAPIFindFolderEx(lpStore,
TEXT('\\'),
pszFolder,
&cbEIDFolder,
&lpEIDFolder);
if (FAILED(hr))
{
EventLogHexErrorMsg(ACLEDIT_FINDFOLDER_FAILED, hr);
goto cleanup;
}
}

// Get a folder ACL interface ptr for the specified folder.

hr = HrFolderACLsOpen(lpSession, lpStore, cbEIDFolder, lpEIDFolder, &lpFolderACLs);

if (FAILED(hr))
{
if (hr == E_NOINTERFACE)
EventLogMsg(ACLEDIT_NOACLTABLE, 0, 0);
else
EventLogHexErrorMsg(ACLEDIT_FOLDERACLSOPEN_FAILED, hr);
goto cleanup;
}

switch (EditCmd)
{
case EDITCMD_LIST:
if (!bDoList())
{
hr = E_FAIL;
goto cleanup;
}
break;

case EDITCMD_INSERT:
if (!bDoInsert())
{
hr = E_FAIL;
goto cleanup;
}
break;

case EDITCMD_MODIFY:
if (!bDoModify())
{
hr = E_FAIL;
goto cleanup;
}
break;

case EDITCMD_DELETE:
if (!bDoDelete())
{
hr = E_FAIL;
goto cleanup;
}
break;
}

cleanup:

MAPIFREEBUFFER(lpEIDFolder);
MAPIFREEBUFFER(lpEIDStore);

ULRELEASE(lpFolderACLs);
ULRELEASE(lpStore);

if (lpSession)
{
lpSession->Logoff(0, 0, 0);
ULRELEASE(lpSession);
}

if (bMAPIInitialized)
(VOID)MAPIUninitialize();

if (bPrintCompletion)
{
EDKEVENTCOUNTEvents = {0,0,0};

HrEventGetCounts(&Events);

cErrs += Events.cError;

if (cErrs == 0)
{
printf("Execution completed with no errors.\n");
}
else
{
if (cErrs == 1)
{
printf("1 error encountered during execution.\n");
}
else
{
printf("%u errors encountered during execution.\n", cErrs);
}

if (Events.cError == 1)
{
printf("1 error written to NT Event Log.\n");
}
else if (Events.cError > 1)
{
printf("%u errors written to NT Event Log.\n", Events.cError);
}
}
}

if (bEventLoggingEnabled)
HrEventCloseLog();

return _nEcFromHr(hr);
}