//--ruleedit.cpp---------------------------------------------------------------
//
// Folder rules editing utility.
//
// Copyright (C) Microsoft Corp., 1986-1996. All rights reserved.
//
//-----------------------------------------------------------------------------
//
// Begin program documentation
//
#if0
Command line arguments: See Usage(), or invoke ruleedit.exe with no command
line arguments to get a usage note.
#endif
//
// End program documentation
//
#include "edk.h"
#include "errno.h"
#include "ruleedit.h"
//
// Manifest constants
//
const UINT cbMaxIniLine =1024;
const UINT cMaxActions =16;
//
// Enumeration, structure, and other type definitions
//
enum EDITCMD// ec
{
EDITCMD_DELETE,
EDITCMD_DISABLE,
EDITCMD_ENABLE,
EDITCMD_HELP,
EDITCMD_INSERT,
EDITCMD_LIST,
EDITCMD_INVALID
};
struct INIFILEPARSEINFO// ifpi
{
CHAR * pszKeyword;
VOID (*ParseFunction)(INCHAR *pch);
};
enum INIFILESECTIONTYPE// ifst
{
IFST_INITIALIZATION,
IFST_COMMAND,
IFST_UNKNOWN
};
enum LINEREADSTATUS// lrs
{
LRS_OKAY, // Line was read and is available.
LRS_EOF, // We are at end of file; no more lines.
LRS_IOERROR, // There was an I/O error in the last read.
LRS_LINETOOLONG // Last line too long to return.
};
enum STORETYPE// st
{
ST_UNKNOWN,// Store type not determined.
ST_EXCHANGEMAILBOX,// Microsoft Exchange Mailbox Store.
ST_EXCHANGEPUBLIC,// Microsoft Exchange Public Store.
ST_NONEXCHANGE// Store not provided by Exchange.
};
//
// Forward function declarations.
//
static
BOOL
bParseCmdLine(
IN INT argc,
IN CHAR * argv[]
);
static
VOID
DoDelete(VOID);
static
VOID
DoDisable(VOID);
static
VOID
DoEnable(VOID);
static
VOID
DoHelp(VOID);
static
VOID
DoInsert(VOID);
static
VOID
DoList(
INLPSTRlpszProvider
);
static
VOID
EventLogHexErrorMsg(
INDWORDdwEvent,
INHRESULThr
);
static
HRESULT
HrGetDefaultStoreName(
INLPMAPISESSIONlpSession,
OUTLPSTR FAR *lppDisplayName
);
static
HRESULT
HrGetStoreType(
INLPMAPISESSIONlpSession,
INLPMDBlpMDB,
INLPSTRlpDisplayName,
OUTSTORETYPE *lpStoreType
);
static
HRESULT
HrProcessCommandSection(VOID);
static
HRESULT
HrProcessInitializationSection(VOID);
static
BOOL
IsSectionLine(
INCHAR * pch
);
static
LINEREADSTATUS
lrsGetNextLine(VOID);
static
VOID
ParseLine(VOID);
static
VOID
ParseSectionLine(
IN CHAR * pch
);
static
VOID
ParseEntryLine(
IN CHAR * pch
);
static
VOID
ParseActionValue(
IN CHAR * pch
);
static
VOID
ParseCommandValue(
IN CHAR * pch
);
static
VOID
ParseConditionValue(
IN CHAR * pch
);
static
VOID
ParseFlagsValue(
IN CHAR * pch
);
static
VOID
ParsePositionValue(
IN CHAR * pch
);
static
VOID
ParseFolderValue(
IN CHAR * pch
);
static
VOID
ParseProfileValue(
IN CHAR * pch
);
static
VOID
ParseProviderValue(
IN CHAR * pch
);
static
VOID
PrintActions(
INLPACTIONSlpActs
);
static
VOID
PrintErr(
INCHAR *Format
...
);
static
VOID
PrintIniFileErr(
INCHAR *Format
...
);
static
VOID
PrintStateFlags(
INLONGlStateFlags
);
static
VOID
Usage(VOID);
//
// Static storage
//
// All the keywords used by the program:
staticCHARszActionKeyword[] ="action";
staticCHARszCommandKeyword[] ="command";
staticCHARszConditionKeyword[] ="condition";
staticCHARszDeleteKeyword[] ="delete";
staticCHARszDisableKeyword[] ="disable";
staticCHARszDisabledKeyword[] ="disabled";
staticCHARszEnableKeyword[] ="enable";
staticCHARszEnabledKeyword[] ="enabled";
staticCHARszErrorKeyword[] ="error";
staticCHARszExitLevelKeyword[] ="exitlevel";
staticCHARszFileKeyword[] ="file";
staticCHARszFlagsKeyword[] ="flags";
staticCHARszFolderKeyword[] ="folder";
staticCHARszHelpKeyword[] ="help";
staticCHARszInboxKeyword[] ="inbox";
staticCHARszInitializationKeyword[] ="initialization";
staticCHARszInsertKeyword[] ="insert";
staticCHARszKeepOOFHistKeyword[] ="keepoofhist";
staticCHARszListKeyword[] ="list";
staticCHARszPositionKeyword[] ="position";
staticCHARszProfileKeyword[] ="profile";
staticCHARszProviderKeyword[] ="provider";
staticCHARszQuestionKeyword[] ="?";
staticCHARszUserOOFKeyword[] ="useroof";
// Command line arg flags, and array used by _HrExpandCommandLineArgument():
staticCHAR *rgpszArgArray[] ={
szFileKeyword,
szFolderKeyword,
szHelpKeyword,
szListKeyword,
szProfileKeyword,
szProviderKeyword,
szQuestionKeyword
};
// Storage used to input and parse ini file lines and store values:
// Ini file ptr:
static FILE *IniFile =NULL;
// Current input line:
static CHARachIniLine[cbMaxIniLine] ={0};
// Parser tables for entries:
static
INIFILEPARSEINFO aifpiInitializationEntry[] = {
szProfileKeyword,
ParseProfileValue,
szFolderKeyword,
ParseFolderValue,
szProviderKeyword,
ParseProviderValue
};
static
INIFILEPARSEINFO aifpiCommandEntry[] = {
szCommandKeyword,
ParseCommandValue,
szPositionKeyword,
ParsePositionValue,
szConditionKeyword,
ParseConditionValue,
szActionKeyword,
ParseActionValue,
szFlagsKeyword,
ParseFlagsValue
};
// Parser state variables:
static INIFILESECTIONTYPEifstCurSection =IFST_UNKNOWN;
staticCHARszProfile[MAX_PATH + 1] ={0};
staticBOOLfSkipProfileArg =FALSE;
staticLPMAPISESSIONlpSession =NULL;
staticCHARszStore[MAX_PATH + 1] ={0};
staticLPMDBlpStore =NULL;
staticULONGcbEIDStore = 0;
staticLPENTRYIDlpEIDStore =NULL;
staticSTORETYPEstStoreType =ST_UNKNOWN;
staticCHARszFolder[MAX_PATH + 1] ={0};
staticLPMAPIFOLDERlpFolder =NULL;
staticULONGcbEIDFolder =0;
staticLPENTRYIDlpEIDFolder =NULL;
static
IExchangeFolderRules *lpFRules =NULL;
staticBOOLfInboxArgFnd =FALSE;
staticBOOLfSkipFolderArg =FALSE;
staticCHARszProvider[MAX_PATH + 1] ={0};
staticBOOLfSkipProviderArg =FALSE;
staticBOOLfProviderArgFnd =FALSE;
staticULONGcProviders =0;
staticLPSTR FAR *lppszProviders =NULL;
staticEDITCMDEditCmd =EDITCMD_INVALID;
staticLONGlCursor =0;
staticBOOLfPositionArgFnd =FALSE;
staticLPACTIONSlpActions =NULL;
staticLPSRestrictionlpRes =NULL;
staticLONGlStateFlags =ST_ENABLED;
staticBOOLfFlagsArgFnd =FALSE;
// Error handling variables:
staticULONGcErrs =0;
staticULONGcPriorErrs =0;// Used in determining if a section should
// be executed.
static DWORDiLine =1;
static DWORDiSection =1;
//
// Functions
//
//$--main----------------------------------------------------------------------
//
// DESCRIPTION: ruleedit.exe main() routine.
//
// INPUT:Standard argc and argv.
//
// RETURNS:integer from edkcode.h
//
//-----------------------------------------------------------------------------
INT
main( // RETURNS: INT
IN INT argc, // argument count
IN CHAR * argv[] // array of command line arguments
)
{
BOOLbMAPIInitialized =FALSE;
BOOLbPrintCompletion =TRUE;
BOOLbEventLoggingEnabled =FALSE;
HRESULThr =NOERROR;
LINEREADSTATUSlrs =LRS_OKAY;
DEBUGPUBLIC("main()\n");
// Initialize event logging.
hr = HrEventOpenLog("EDKRuleEdit", NULL, NULL, NULL, NULL, NULL);
if (FAILED(hr))
{
PrintErr("Event logging could not be enabled; error code=%#x", hr);
hr = HR_LOG(E_FAIL);
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 < 2)
{
Usage();
bPrintCompletion = FALSE;
goto cleanup;
}
hr = MAPIInitialize(0);
if (FAILED(hr))
{
PrintErr("MAPI initialization failed");
EventLogHexErrorMsg(RULEEDIT_MAPIINITIALIZE_FAILED, hr);
hr = HR_LOG(E_FAIL);
goto cleanup;
}
bMAPIInitialized = TRUE;
if (!bParseCmdLine(argc, argv))
{
Usage();
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
if (EditCmd == EDITCMD_HELP)
{
DoHelp();
bPrintCompletion = FALSE;
goto cleanup;
}
ifstCurSection = IFST_UNKNOWN;
// /List can be specified on the cmd line instead of specifying an
// ini file to process. If this is the case, we handle it here.
if (EditCmd == EDITCMD_LIST)
{
if (!fSkipProfileArg)
{
PrintErr("/PROFILE must be specified on command line with /LIST");
hr = HR_LOG(E_FAIL);
goto cleanup;
}
else if (!fSkipFolderArg)
{
PrintErr("/FOLDER must be specified on command line with /LIST");
hr = HR_LOG(E_FAIL);
goto cleanup;
}
hr = HrProcessInitializationSection();
if (FAILED(hr))
goto cleanup;
if (fProviderArgFnd)
{
DoList(szProvider);
}
else
{
ULONGi = 0;
hr = HrFolderRulesGetProviders(lpStore,
cbEIDFolder,
lpEIDFolder,
&cProviders,
&lppszProviders);
if (FAILED(hr))
{
PrintErr("Folder providers could not be determined");
EventLogHexErrorMsg(RULEEDIT_FOLDERRULESGETPROVIDERS_FAILED,hr);
goto cleanup;
}
if (cProviders == 0)
printf("Rules table is empty.\n\n");
else
for (i = 0; i < cProviders; i++)
DoList(lppszProviders[i]);
}
// Note that execution always ends here.
goto cleanup;
}
while (TRUE)
{
lrs = lrsGetNextLine();
if (lrs == LRS_OKAY)
{
if (IsSectionLine(achIniLine))
{
if (ifstCurSection == IFST_COMMAND)
hr = HrProcessCommandSection();
else if (ifstCurSection == IFST_INITIALIZATION)
hr = HrProcessInitializationSection();
if (FAILED(hr))
goto cleanup;
}
ParseLine();
}
else if (lrs == LRS_LINETOOLONG)
{
PrintErr("Line %u too long in Ini File", iLine);
}
else
{
break;
}
iLine++;
}
if (lrs == LRS_EOF)
{
if (ifstCurSection == IFST_COMMAND)
hr = HrProcessCommandSection();
else if (ifstCurSection == IFST_INITIALIZATION)
hr = HrProcessInitializationSection();
if (FAILED(hr))
goto cleanup;
}
cleanup:
MAPIFREEBUFFER(lpActions);
MAPIFREEBUFFER(lpRes);
MAPIFREEBUFFER(lpEIDFolder);
MAPIFREEBUFFER(lpEIDStore);
MAPIFREEBUFFER(lppszProviders);
ULRELEASE(lpFRules);
ULRELEASE(lpFolder);
ULRELEASE(lpStore);
if (lpSession)
{
lpSession->Logoff(0, 0, 0);
ULRELEASE(lpSession);
}
if (bMAPIInitialized)
(VOID)MAPIUninitialize();
if (bPrintCompletion)
{
EDKEVENTCOUNTEvents = {0,0,0};
HrEventGetCounts(&Events);
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();
if (SUCCEEDED(hr) && cErrs > 0)
hr = HR_LOG(E_FAIL);
return _nEcFromHr(hr);
}
//$--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 brc = 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 == szFileKeyword)
{
if (IniFile != NULL)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}
if (pszArgData == NULL || pszArgData[0] == '\0')
{
pszArgData = "ruleedit.ini";
}
if ((IniFile = fopen(pszArgData, "r")) == NULL)
{
CHARszTmpFileName[MAX_PATH + 1];
if (strchr(pszArgData, '.') != NULL||
strlen(pszArgData) + 4 >= sizeof(szTmpFileName))
{
PrintErr("Ini file %s could not be opened", pszArgData);
goto cleanup;
}
// Try appending ".ini".
strcpy(szTmpFileName, pszArgData);
strcat(szTmpFileName, ".ini");
if ((IniFile = fopen(szTmpFileName, "r")) == NULL)
{
PrintErr("Ini file %s could not be opened", szTmpFileName);
goto cleanup;
}
}
}
else if (pszFlagName == szFolderKeyword)
{
if (szFolder[0] != '\0' || fInboxArgFnd)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}
if (pszArgData == NULL || pszArgData[0] == '\0')
{
PrintErr("Command line %s argument requires a value",
szFolderKeyword);
goto cleanup;
}
if (!stricmp(pszArgData, szInboxKeyword))
{
// It is the special name "inbox", which is interpreted as
// appropriate.
fInboxArgFnd = TRUE;
}
else
{
// Split the store and folder names and check their lengths.
CHAR *pch = strchr(pszArgData, '\\');
if (pch == NULL)
{
PrintErr("Command line %s argument invalid",
szFolderKeyword);
goto cleanup;
}
*pch = '\0';
pch++;
if (*pszArgData == '\0')
{
PrintErr("Command line %s argument invalid",
szFolderKeyword);
goto cleanup;
}
if (strlen(pszArgData) >= sizeof(szStore))
{
PrintErr("Command line %s argument too long",
szFolderKeyword);
goto cleanup;
}
strcpy(szStore, pszArgData);
if (*pch == '\0')
{
PrintErr("Command line %s argument invalid",
szFolderKeyword);
goto cleanup;
}
if (strlen(pch) >= sizeof(szFolder))
{
PrintErr("Command line %s argument too long",
szFolderKeyword);
goto cleanup;
}
strcpy(szFolder, pch);
}
fSkipFolderArg = TRUE;
}
else if (pszFlagName == szHelpKeyword||
pszFlagName == szQuestionKeyword)
{
EditCmd = EDITCMD_HELP;
// If we encounter /help or /?, we pretty much ignore everything
// else and produce a help msg.
brc = TRUE;
goto cleanup;
}
else if (pszFlagName == szListKeyword)
{
if (EditCmd == EDITCMD_LIST)
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}
EditCmd = EDITCMD_LIST;
}
else if (pszFlagName == szProfileKeyword)
{
if (szProfile[0] != '\0')
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}
if (pszArgData == NULL || pszArgData[0] == '\0')
{
PrintErr("Command line %s argument requires a value",
szProfileKeyword);
goto cleanup;
}
if (strlen(pszArgData) >= sizeof(szProfile))
{
PrintErr("Command line %s argument too long",
szProfileKeyword);
goto cleanup;
}
strcpy(szProfile, pszArgData);
fSkipProfileArg = TRUE;// Ignore ini file value.
}
else if (pszFlagName == szProviderKeyword)
{
if (szProvider[0] != '\0')
{
PrintErr("Duplicate command line arguments");
goto cleanup;
}
if (pszArgData == NULL || pszArgData[0] == '\0')
{
PrintErr("Command line %s argument requires a value",
szProviderKeyword);
goto cleanup;
}
if (strlen(pszArgData) >= sizeof(szProvider))
{
PrintErr("Command line %s argument too long",
szProviderKeyword);
goto cleanup;
}
strcpy(szProvider, pszArgData);
fSkipProviderArg =TRUE;// Ignore ini file value.
fProviderArgFnd =TRUE;
}
else
{
PrintErr("Command line argument '%s' is invalid", *argv);
goto cleanup;
}
argc--;
argv++;
}
// Check for required arguments.
if (IniFile == NULL)
{
if (EditCmd != EDITCMD_LIST)
{
if ((IniFile = fopen("ruleedit.ini", "r")) == NULL)
{
PrintErr("Ini file ruleedit.ini could not be opened");
goto cleanup;
}
}
}
else
{
if (EditCmd == EDITCMD_LIST)
{
PrintErr("/list and /file parameters may not both be specified");
goto cleanup;
}
}
brc = TRUE;
cleanup:
return brc;
}
//$--DoDelete------------------------------------------------------------------
//
// DESCRIPTION:Execute the DELETE command.
//
// INPUT:None.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
DoDelete(VOID)// RETURNS: VOID
{
HRESULThr =NOERROR;
LONGlCurCursor =0;
DEBUGPRIVATE("DoDelete()\n");
hr = lpFRules->HrSeek(lCursor);
if (FAILED(hr))
{
PrintErr("Delete command at line %u failed.\n\n", iSection);
EventLogHexErrorMsg(RULEEDIT_SEEK_FAILED, hr);
goto cleanup;
}
// We do prior check of cursor location to be able to cleanly specify
// the error.
hr = lpFRules->HrTell(&lCurCursor);
if (FAILED(hr))
{
PrintErr("Delete command at line %u failed.\n\n", iSection);
EventLogHexErrorMsg(RULEEDIT_TELL_FAILED, hr);
goto cleanup;
}
if (lCurCursor == RULE_PAST_END)
{
PrintErr("Delete command at line %u failed; "
"rule at position %d does not exist", iSection, lCursor);
goto cleanup;
}
hr = lpFRules->HrDelete();
if (FAILED(hr))
{
PrintErr("Delete command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_DELETE_FAILED, hr);
goto cleanup;
}
printf("Delete command at line %u succeeded.\n\n", iSection);
cleanup:
return;
}
//$--DoDisable-----------------------------------------------------------------
//
// DESCRIPTION:Execute the DISABLE command.
//
// INPUT:None.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
DoDisable(VOID)// RETURNS:VOID
{
HRESULThr;
LONGlCurCursor;
DEBUGPRIVATE("DoDisable()\n");
hr = lpFRules->HrSeek(lCursor);
if (FAILED(hr))
{
PrintErr("Disable command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_SEEK_FAILED, hr);
goto cleanup;
}
// We do prior check of cursor location to be able to cleanly specify
// the error.
hr = lpFRules->HrTell(&lCurCursor);
if (FAILED(hr))
{
PrintErr("Disable command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_TELL_FAILED, hr);
goto cleanup;
}
if (lCurCursor == RULE_PAST_END)
{
PrintErr("Disable command at line %u failed; "
"rule at position %d does not exist", iSection, lCursor);
goto cleanup;
}
hr = lpFRules->HrDisable();
if (FAILED(hr))
{
PrintErr("Disable command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_DISABLE_FAILED, hr);
goto cleanup;
}
printf("Disable command at line %u succeeded.\n\n", iSection);
cleanup:
return;
}
//$--DoEnable------------------------------------------------------------------
//
// DESCRIPTION:Execute the ENABLE command.
//
// INPUT:None.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
DoEnable(VOID)// RETURNS: VOID
{
HRESULThr =NOERROR;
LONGlCurCursor =0;
DEBUGPRIVATE("DoEnable()\n");
hr = lpFRules->HrSeek(lCursor);
if (FAILED(hr))
{
PrintErr("Enable command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_SEEK_FAILED, hr);
goto cleanup;
}
// We do prior check of cursor location to be able to cleanly specify
// the error.
hr = lpFRules->HrTell(&lCurCursor);
if (FAILED(hr))
{
PrintErr("Enable command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_TELL_FAILED, hr);
goto cleanup;
}
if (lCurCursor == RULE_PAST_END)
{
PrintErr("Enable command at line %u failed; "
"rule at position %d does not exist", iSection, lCursor);
goto cleanup;
}
hr = lpFRules->HrEnable();
if (FAILED(hr))
{
PrintErr("Enable command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_ENABLE_FAILED, hr);
goto cleanup;
}
printf("Enable command at line %u succeeded.\n\n", iSection);
cleanup:
return;
}
//$--DoHelp--------------------------------------------------------------------
//
// DESCRIPTION:Execute the HELP command.
//
// INPUT:None.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
DoHelp(VOID) // RETURNS: VOID
{
printf(
"\n"
"USAGE: RULEEDIT [/Profile=<ProfileName>]"
"\n"
" [<Folder>]"
"\n"
" [/List | /File=<IniFile>]"
"\n"
" [/Provider=<ProviderName>]"
"\n"
"\n"
"<Folder> ::= /Folder=<StoreName>\\<FolderPath> | /Folder=Inbox"
"\n"
"\n"
);
}
//$--DoInsert------------------------------------------------------------------
//
// DESCRIPTION:Execute the INSERT command.
//
// INPUT:None.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
DoInsert(VOID)// RETURNS: VOID
{
HRESULThr =NOERROR;
ULONGi =0;
DEBUGPRIVATE("DoInsert()\n");
hr = lpFRules->HrSeek(lCursor);
if (FAILED(hr))
{
PrintErr("Insert command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_SEEK_FAILED, hr);
goto cleanup;
}
hr = lpFRules->HrInsert(lStateFlags, lpRes, lpActions, 0, "");
if (FAILED(hr))
{
PrintErr("Insert command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_INSERT_FAILED, hr);
goto cleanup;
}
printf("Insert command at line %u succeeded.\n\n", iSection);
cleanup:
return;
}
//$--DoList--------------------------------------------------------------------
//
// DESCRIPTION:Execute the LIST command.
//
// INPUT:
//
//[lpszProvider]-- Provider to list rules for.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
DoList(// RETURNS: VOID
INLPSTRlpszProvider// Provider name
)
{
HRESULThr =NOERROR;
LONGlCurCursor =0;
LPACTIONSlpActs =NULL;
LPSRestrictionlpRes =NULL;
LPSTRlpszRes =NULL;
LPSTRlpszRuleName =NULL;
LPFOLDERRULESlpFRulesLst =NULL;
DEBUGPRIVATE("DoList()\n");
if (!fPositionArgFnd)
lCursor = 0;
// If the provider being listed is not the provider that the global
// rules folder ptr has been opened for, we have to get a local rules
// folder ptr for that provider.
if (stricmp(lpszProvider, szProvider))
{
hr = HrFolderRulesOpen(lpStore,
cbEIDFolder,
lpEIDFolder,
lpszProvider,
&lpFRulesLst);
// An error is highly unexpected here, since this code should never
// execute without the global folder rules ptr having been opened.
if (FAILED(hr))
{
hr = HR_LOG(E_UNEXPECTED);
PrintErr("Unexpected internal error in ruleedit");
EventLogHexErrorMsg(RULEEDIT_INTERNAL_ERROR, hr);
goto cleanup;
}
}
else
{
lpFRulesLst = lpFRules;// AddRef() not necessary due to scope.
}
hr = lpFRulesLst->HrSeek(lCursor);
if (FAILED(hr))
{
PrintErr("List command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_SEEK_FAILED, hr);
goto cleanup;
}
// Check for empty table or invalid cursor position.
hr = lpFRulesLst->HrTell(&lCurCursor);
if (FAILED(hr))
{
PrintErr("List command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_TELL_FAILED, hr);
goto cleanup;
}
printf("Listing of rules for provider '%s'\n"
"in folder '%s' in store '%s':\n\n",
lpszProvider, szFolder, szStore);
if (lCurCursor == RULE_PAST_END)
{
if (fPositionArgFnd)
PrintErr("List command at line %u failed; "
"Rule at specified position (%d) does not exist",
iSection, lCursor);
else
printf("There are no rules for provider '%s'\n\n", lpszProvider);
goto cleanup;
}
while (TRUE)
{
LONGlStateFlags;
LONGlLevel;
hr = lpFRulesLst->HrGet(&lStateFlags,
&lpRes,
&lpActs,
&lLevel,
&lpszRuleName);
if (FAILED(hr))
{
HRESULThrSaved = hr;
hr = lpFRulesLst->HrTell(&lCurCursor);
if (FAILED(hr))
{
PrintErr("List command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_TELL_FAILED, hr);
goto cleanup;
}
if (lCurCursor != RULE_PAST_END || hrSaved != E_FAIL)
{
PrintErr("List command at line %u failed", iSection);
EventLogHexErrorMsg(RULEEDIT_GET_FAILED, hrSaved);
goto cleanup;
}
break;
}
else
{
// Print listing:
printf("Row number = %d\n", lCurCursor);
if (lpRes != NULL)
{
hr = HrRestrictionToString(lpRes, &lpszRes);
if (FAILED(hr))
{
printf("String representation of rule condition "
"could not be determined.\n");
cErrs++;
EventLogHexErrorMsg(RULEEDIT_RESTRICTIONTOSTRING_FAILED, hr);
}
else
{
printf("Rule Condition = %s\n", lpszRes);
}
}
else
{
printf("Rule Condition is not defined (NULL)\n");
}
PrintActions(lpActs);
PrintStateFlags(lStateFlags);
printf("Rule Level = %u\n", lLevel);
printf("Rule Name = %s\n", lpszRuleName);
printf("\n");
lCurCursor++;
}
// Only print one record if cursor position was specified.
if (fPositionArgFnd)
break;
// Loop repeats; clean up allocations so ptrs can be reused.
// Note that MAPIFREEBUFFER() is a macro that nulls the ptr.
MAPIFREEBUFFER(lpRes);
MAPIFREEBUFFER(lpszRes);
MAPIFREEBUFFER(lpActs);
MAPIFREEBUFFER(lpszRuleName);
}
cleanup:
printf("\n");
// Clean up allocations:
MAPIFREEBUFFER(lpRes);
MAPIFREEBUFFER(lpszRes);
MAPIFREEBUFFER(lpActs);
MAPIFREEBUFFER(lpszRuleName);
if (lpFRulesLst != lpFRules)
ULRELEASE(lpFRulesLst);
return;
}
//$--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);
}
//$--HrGetDefaultStoreName-----------------------------------------------------
//
// DESCRIPTION: Get the display name for the default store.
//
// INPUT:
//
//[lpSession]-- Ptr to MAPI session.
//
// OUTPUT:
//
//[lppDisplayName]-- Ptr to ptr that will be set to point at store
// display name on successful return.
//
// RETURNS: NOERRORif successful;
//E_*if call failed.
//-----------------------------------------------------------------------------
HRESULT
HrGetDefaultStoreName(// RETURNS:HRESULT
INLPMAPISESSIONlpSession,// MAPI session ptr
OUTLPSTR FAR *lppDisplayName// Display name buffer ptr
)
{
static
SizedSPropTagArray(2, Columns) = {2,{PR_DEFAULT_STORE,PR_DISPLAY_NAME}};
ULONGcchDisplayName =0;
HRESULThr =NOERROR;
LPSTRlpDisplayName =NULL;
LPSRowSetlpRows =NULL;
LPMAPITABLElpTable =NULL;
DEBUGPRIVATE("HrGetDefaultStoreName()\n");
hr = lpSession->GetMsgStoresTable(0, &lpTable);
if (FAILED(hr))
goto cleanup;
hr = lpTable->SetColumns((LPSPropTagArray)&Columns, 0);
if (FAILED(hr))
goto cleanup;
while (TRUE)
{
LPSPropValuelpProp;
hr = lpTable->QueryRows(1, 0, &lpRows);
if (FAILED(hr) || lpRows->cRows != 1)
break;
lpProp = &lpRows->aRow[0].lpProps[0];
if (lpProp->ulPropTag == PR_DEFAULT_STORE && lpProp->Value.b)
break;
FREEPROWS(lpRows);
}
if (FAILED(hr))
goto cleanup;
if (lpRows == NULL||
lpRows->cRows != 1||
lpRows->aRow[0].lpProps[1].ulPropTag != PR_DISPLAY_NAME||
lpRows->aRow[0].lpProps[1].Value.lpszA == NULL)
{
hr = HR_LOG(MAPI_E_NOT_FOUND);
goto cleanup;
}
// Allocate space for the display name to be returned and copy it out.
lpDisplayName = lpRows->aRow[0].lpProps[1].Value.lpszA;
cchDisplayName = strlen(lpDisplayName) + 1;
hr = MAPIAllocateBuffer(cchDisplayName, (LPVOID FAR *)lppDisplayName);
if (FAILED(hr))
goto cleanup;
strcpy(*lppDisplayName, lpDisplayName);
cleanup:
if (lpRows)
FREEPROWS(lpRows);
ULRELEASE(lpTable);
RETURN(hr);
}
//$--HrGetStoreType------------------------------------------------------------
//
// DESCRIPTION: Determine the store type of a given message store.
//
// INPUT:
//
//[lpSession]-- Ptr to MAPI session.
//[lpMDB]-- Ptr to MAPI store.
//[lpDisplayName]-- Ptr to display name of store to be evaluated.
//
// OUTPUT:
//
//[lpStoreType]-- Ptr to store type that will be set on successful return.
//
// RETURNS: NOERRORif successful;
//E_*if call failed.
//-----------------------------------------------------------------------------
HRESULT
HrGetStoreType(// RETURNS:HRESULT
INLPMAPISESSIONlpSession,// MAPI session ptr
INLPMDBlpMDB,// store ptr
INLPSTRlpDisplayName,// store display name ptr
OUTSTORETYPE *lpStoreType// store type ptr
)
{
// Note - There may be a problem with the following name changing.
// Unfortunately, there is not a good other solution for
// distinguishing between an exchange vs a non-exchange store
// at present.
static CHARszExchangeName[] = "Microsoft Exchange Server";
static
SizedSPropTagArray(2, Columns) = {2,{PR_DISPLAY_NAME,PR_PROVIDER_DISPLAY}};
HRESULThr =NOERROR;
LPSRowSetlpRows =NULL;
LPMAPITABLElpTable =NULL;
DEBUGPRIVATE("HrGetStoreType()\n");
hr = lpSession->GetMsgStoresTable(0, &lpTable);
if (FAILED(hr))
goto cleanup;
hr = lpTable->SetColumns((LPSPropTagArray)&Columns, 0);
if (FAILED(hr))
goto cleanup;
while (TRUE)
{
LPSPropValuelpProp;
hr = lpTable->QueryRows(1, 0, &lpRows);
if (FAILED(hr) || lpRows->cRows != 1)
break;
lpProp = &lpRows->aRow[0].lpProps[0];
if (lpProp->ulPropTag == PR_DISPLAY_NAME)
{
if (!stricmp(lpProp->Value.lpszA, lpDisplayName))
break;
}
FREEPROWS(lpRows);
}
if (FAILED(hr))
goto cleanup;
if (lpRows == NULL||
lpRows->cRows != 1||
lpRows->aRow[0].lpProps[1].ulPropTag != PR_PROVIDER_DISPLAY)
{
hr = HR_LOG(MAPI_E_NOT_FOUND);
goto cleanup;
}
if (!memcmp(lpRows->aRow[0].lpProps[1].Value.lpszA,
szExchangeName, sizeof(szExchangeName) - 1))
{
if (FIsPublicStore(lpMDB))
*lpStoreType = ST_EXCHANGEPUBLIC;
else
*lpStoreType = ST_EXCHANGEMAILBOX;
}
else
{
*lpStoreType = ST_NONEXCHANGE;
}
cleanup:
if (lpRows)
FREEPROWS(lpRows);
ULRELEASE(lpTable);
RETURN(hr);
}
//$--HrProcessCommandSection---------------------------------------------------
//
// DESCRIPTION: Process the information parsed in a command section, and
//execute the command.
//
// INPUT: None.
//
// RETURNS: HRESULT -- NOERROR if execution should continue;
// E_FAIL if there is an initialization error;
//E_UNEXPECTED if there is an internal error.
//
// Notes:This function logs or prints errors associated with
//failure of an individual command, but does not return an
//error code. If a more major error occurs that should
//stop execution, then an error code is returned.
//
//-----------------------------------------------------------------------------
HRESULT
HrProcessCommandSection(VOID) // RETURNS: HRESULT
{
BOOLfSkipExec =FALSE;
HRESULThr =NOERROR;
DEBUGPRIVATE("HrProcessCommandSection()\n");
// Confirm that initialization has been done.
if (lpFRules == NULL)
{
hr = HR_LOG(E_FAIL);
PrintErr("Initialization section missing, or not at start of file");
fSkipExec = TRUE;
goto cleanup;
}
// Confirm that the command section has the required section entries.
// If anything past this point fails, we report or log the error, but
// return NOERROR and keep on going.
if (EditCmd == EDITCMD_INVALID)
{
PrintErr("Command must include a command argument");
fSkipExec = TRUE;
goto cleanup;
}
else if (EditCmd != EDITCMD_LIST)
{
if (!fPositionArgFnd)
{
if (EditCmd == EDITCMD_INSERT)
{
lCursor = RULE_PAST_END;
}
else
{
PrintErr("Command specified requires %s argument",
szPositionKeyword);
fSkipExec = TRUE;
goto cleanup;
}
}
if (EditCmd == EDITCMD_INSERT)
{
if (lpRes == NULL)
{
PrintErr("Insert command requires %s argument",
szConditionKeyword);
fSkipExec = TRUE;
goto cleanup;
}
else if (lpActions == NULL)
{
PrintErr("Insert command requires %s argument",
szActionKeyword);
fSkipExec = TRUE;
goto cleanup;
}
{
LPACTIONlpAction =lpActions->lpAction;
LPACTIONlpActionInv =lpActions->lpAction +
lpActions->cActions;
while (lpAction < lpActionInv)
{
if (lpAction->acttype == OP_MOVE||
lpAction->acttype == OP_COPY)
{
if (stStoreType == ST_EXCHANGEPUBLIC)
{
PrintErr("Move and Copy actions are only supported"
" by Exchange mailboxes");
fSkipExec = TRUE;
goto cleanup;
}
if (lpAction->actMoveCopy.cbStoreEntryId !=
cbEIDStore||
memcmp(lpAction->actMoveCopy.lpStoreEntryId,
lpEIDStore,
cbEIDStore))
{
PrintErr("Move and Copy actions are only supported"
" to destinations in the same store");
fSkipExec = TRUE;
goto cleanup;
}
}
else if (lpAction->acttype == OP_BOUNCE)
{
if (stStoreType == ST_EXCHANGEMAILBOX)
{
PrintErr("Bounce actions are only supported"
" by Exchange public stores");
fSkipExec = TRUE;
goto cleanup;
}
}
else if (lpAction->acttype == OP_DELEGATE)
{
if (stStoreType == ST_EXCHANGEPUBLIC)
{
PrintErr("Delegate actions are only supported"
" by Exchange mailboxes");
fSkipExec = TRUE;
goto cleanup;
}
}
lpAction++;
}
}
}
}
// Only process the section if there were no errors in parsing it.
if (cErrs > cPriorErrs)
{
fSkipExec = TRUE;
goto cleanup;
}
switch (EditCmd)
{
case EDITCMD_LIST:
if (fProviderArgFnd)
{
DoList(szProvider);
}
else
{
ULONGi = 0;
// We have to redetermine the providers every time we do a list
// because RuleEdit may have inserted rules in the table.
MAPIFREEBUFFER(lppszProviders);
hr = HrFolderRulesGetProviders(lpStore,
cbEIDFolder,
lpEIDFolder,
&cProviders,
&lppszProviders);
if (FAILED(hr))
{
PrintErr("Folder providers could not be determined");
EventLogHexErrorMsg(RULEEDIT_FOLDERRULESGETPROVIDERS_FAILED,hr);
// We treat this as a killer error.
goto cleanup;
}
if (cProviders == 0)
printf("Rules table is empty.\n\n");
else
for (i = 0; i < cProviders; i++)
DoList(lppszProviders[i]);
}
break;
case EDITCMD_INSERT:
DoInsert();
break;
case EDITCMD_DELETE:
DoDelete();
break;
case EDITCMD_ENABLE:
DoEnable();
break;
case EDITCMD_DISABLE:
DoDisable();
break;
// Can only occur if the code gets screwed up in maintenance:
default:
hr = HR_LOG(E_UNEXPECTED);
PrintErr("Unexpected internal error in ruleedit");
EventLogHexErrorMsg(RULEEDIT_INTERNAL_ERROR, hr);
goto cleanup;
break;
}
cleanup:
if (fSkipExec)
{
printf("Execution of command at line %u not attempted "
"due to errors.\n\n", iSection);
}
RETURN(hr);
}
//$--HrProcessInitializationSection--------------------------------------------
//
// DESCRIPTION: Process the information parsed in an initialization section,
//logging onto a MAPI profile session and opening the message
//store, its folder, and rules table. If profile or folder info
//was provided on the command line, that is processed instead.
//
// INPUT: None.
//
// RETURNS: HRESULT -- NOERROR if successful,
// E_* otherwise.
//
// Notes:If this function fails, the error should be treated as a
//fatal error.
//
//-----------------------------------------------------------------------------
HRESULT
HrProcessInitializationSection(VOID) // RETURNS: HRESULT
{
HRESULThr =NOERROR;
LPSTRpszStore =NULL;
ULONGulObjType =0;
DEBUGPRIVATE("HrProcessInitializationSection()\n");
// Confirm that this is the first Initialization section we have
// encountered.
if (lpFRules != NULL)
{
if (!fSkipProfileArg || !fSkipFolderArg)
{
PrintErr("Multiple Initialization sections in Ini File");
hr = HR_LOG(E_FAIL);
}
// It is okay to find an initialization section after initialization
// if all args were specified on the command line.
goto cleanup;
}
// Only process the section if there were no errors in parsing it.
if (cErrs > cPriorErrs)
goto cleanup;
if (szProfile[0] == '\0')
{
PrintErr("Profile not specified on command line or in Ini File");
hr = HR_LOG(E_FAIL);
goto cleanup;
}
if (szFolder[0] == '\0' && !fInboxArgFnd)
{
PrintErr("Folder not specified on command line or in Ini File");
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Use default provider value if one was not specified.
if (!fProviderArgFnd)
strcpy(szProvider, "MSFT:RuleEdit");
hr = MAPILogonEx(0,
szProfile,
NULL,
MAPI_EXTENDED | MAPI_NEW_SESSION,
&lpSession);
if (FAILED(hr))
{
PrintErr("MAPI Logon failed");
EventLogHexErrorMsg(RULEEDIT_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))
{
PrintErr("Default message store could not be determined");
EventLogHexErrorMsg(RULEEDIT_FINDDEFAULTMSGSTORE_FAILED, hr);
goto cleanup;
}
hr = HrGetDefaultStoreName(lpSession, &pszStore);
if (FAILED(hr))
{
PrintErr("Default message store name could not be determined");
EventLogHexErrorMsg(RULEEDIT_GETDEFAULTSTORENAME_FAILED, hr);
goto cleanup;
}
if (strlen(pszStore) >= sizeof(szStore))
{
PrintErr("Default store name too long"); // Highly unexpected.
goto cleanup;
}
strcpy(szStore, pszStore);
}
else
{
hr = HrMAPIFindStore(lpSession, szStore, &cbEIDStore, &lpEIDStore);
if (FAILED(hr))
{
PrintErr("Message store not found");
EventLogHexErrorMsg(RULEEDIT_FINDSTORE_FAILED, hr);
goto cleanup;
}
}
hr = lpSession->OpenMsgStore(0,
cbEIDStore,
lpEIDStore,
NULL,
MAPI_DEFERRED_ERRORS | MAPI_BEST_ACCESS,
&lpStore);
if (FAILED(hr))
{
PrintErr("Message store could not be opened");
EventLogHexErrorMsg(RULEEDIT_OPENMSGSTORE_FAILED, hr);
goto cleanup;
}
// Confirm that this is an Exchange store. We stop processing if it
// is not.
hr = HrGetStoreType(lpSession, lpStore, szStore, &stStoreType);
if (FAILED(hr))
{
PrintErr("Message store provider type could not be determined");
EventLogHexErrorMsg(RULEEDIT_GETSTORETYPE_FAILED, hr);
goto cleanup;
}
if (stStoreType == ST_NONEXCHANGE)
{
PrintErr("Rules are only supported by Exchange Server stores");
hr = HR_LOG(E_FAIL);
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))
{
PrintErr("Inbox not found");
EventLogHexErrorMsg(RULEEDIT_GETRECEIVEFOLDER_FAILED, hr);
goto cleanup;
}
// BUGBUG - We may want to look this up instead of hardwiring it.
strcpy(szFolder, "Top of Information Store\\Inbox");
}
else
{
hr = HrMAPIFindFolderEx(lpStore,
'\\',
szFolder,
&cbEIDFolder,
&lpEIDFolder);
if (FAILED(hr))
{
PrintErr("Specified folder not found");
EventLogHexErrorMsg(RULEEDIT_FINDFOLDER_FAILED, hr);
goto cleanup;
}
}
// Get a folder pointer. This is required for HrStringToAction().
hr = lpStore->OpenEntry(cbEIDFolder,
lpEIDFolder,
NULL,
MAPI_BEST_ACCESS |
MAPI_DEFERRED_ERRORS,
&ulObjType,
(LPUNKNOWN FAR *)&lpFolder);
if (FAILED(hr))
{
PrintErr("Specified folder could not be opened");
EventLogHexErrorMsg(RULEEDIT_OPENENTRY_FAILED, hr);
goto cleanup;
}
// Get a folder rules interface ptr for the specified folder. This ptr
// will be used for all commands with the exception of the list command
// when no provider has been specified. In that instance, the list
// command has to get a ptr for each provider.
hr = HrFolderRulesOpen(lpStore,
cbEIDFolder,
lpEIDFolder,
szProvider,
&lpFRules);
if (FAILED(hr))
{
if (hr == E_NOINTERFACE)
{
PrintErr("Specified folder does not support rules");
EventLogMsg(RULEEDIT_NORULESTABLE, 0, 0);
}
else
{
PrintErr("Specified folder could not be opened");
EventLogHexErrorMsg(RULEEDIT_FOLDERRULESOPEN_FAILED, hr);
}
goto cleanup;
}
cleanup:
MAPIFREEBUFFER(pszStore);
RETURN(hr);
}
//$--IsSectionLine-------------------------------------------------------------
//
// DESCRIPTION: Determine if an input line is a section header (ie., first
//non-whitespace char is '[').
//
// INPUT: [pch]-- Ptr to input line.
//
// RETURNS: TRUE if input line is a section line; FALSE otherwise.
//-----------------------------------------------------------------------------
BOOL
IsSectionLine(// RETURNS: BOOL
INCHAR * pch// input line
)
{
DEBUGPRIVATE("IsSectionLine()\n");
while (isspace(*pch))// Skip leading line whitespace.
pch++;
return (*pch == '[');
}
//$--lrsGetNextLine------------------------------------------------------------
//
// DESCRIPTION: Get the next input line from the ini file and deposit it in
//the input buffer (achIniLine).
//
// INPUT:None.
//
// RETURNS:
//
// LRS_OKAY if line was read and is available.
// LRS_EOF if we are at end of file; no more lines.
// LRS_IOERROR if there was an I/O error in the last read.
// LRS_LINETOOLONG if last line was too long to fit in the buffer.
//-----------------------------------------------------------------------------
LINEREADSTATUS
lrsGetNextLine(VOID)// RETURNS: LINEREADSTATUS
{
INT ch =0;
LINEREADSTATUSlrs =LRS_OKAY;
CHAR * pch = &achIniLine[0];
CHAR * pchInv = &achIniLine[cbMaxIniLine];
DEBUGPRIVATE("lrsGetNextLine()\n");
while (TRUE)
{
if (pch == pchInv)
{
// Line is too long.
lrs = LRS_LINETOOLONG;
achIniLine[0] = '\0';
while ((ch = getc(IniFile)) != EOF && (ch != '\n'));
break;
}
ch = getc(IniFile);
if (ch == '\n')
{
// Just read end of this line.
lrs = LRS_OKAY;
*pch = '\0';
break;
}
if (ch == EOF)
{
// We have read last line in file.
// We want to process the last line, if any.
*pch = '\0';
if (pch == &achIniLine[0])
{
// The last line was properly terminated with a newline
// and we are truly at EOF.
lrs = feof(IniFile) ? LRS_EOF : LRS_IOERROR;
}
else
{
// The last line was not properly terminated and has data.
lrs = feof(IniFile) ? LRS_OKAY : LRS_IOERROR;
}
break;
}
*pch = ch;
pch++;
}
return lrs;
}
//$--ParseLine-----------------------------------------------------------------
//
// DESCRIPTION: Parse an ini file line, and store the value if it is a section
//entry line.
//
// INPUT: None.
//
// RETURNS: Nothing.
//
//-----------------------------------------------------------------------------
VOID
ParseLine(VOID)// RETURNS: VOID
{
CHAR * pch =&achIniLine[0];
DEBUGPRIVATE("ParseLine()\n");
while (isspace(*pch)) // Skip leading line whitespace.
pch++;
if (*pch == '\0') // It is a blank line.
{
;
}
else if (*pch == ';') // It is a comment.
{
;
}
else
{
if ( *pch == '[' ) // It is a section, or is invalid
{
iSection = iLine;
cPriorErrs = cErrs;
ParseSectionLine(pch + 1);
}
else // It is a section entry, or is invalid.
{
ParseEntryLine(pch);
}
}
}
//$--ParseSectionLine----------------------------------------------------------
//
// DESCRIPTION: Parse an ini file section header line.
//
// INPUT: [pch]-- Ptr to section keyword.
//
// RETURNS: Nothing.
//
//-----------------------------------------------------------------------------
VOID
ParseSectionLine(// RETURNS:VOID
IN CHAR * pch// input line
)
{
CHAR chTmp = '\0';
BOOLfBadSyntax =FALSE;
CHAR * pchKeyword = NULL;
DEBUGPRIVATE("ParseSectionLine()\n");
// Save ptr to keyword.
pchKeyword = pch;
// Scan for end of keyword and delimit keyword, saving replaced char.
while (!isspace(*pch) && *pch != ']' && *pch != '\0')
pch++;
if (*pch == '\0')
{
fBadSyntax = TRUE;
goto cleanup;
}
chTmp = *pch;
*pch = '\0';
// Compare putative keyword against valid section keywords.
if (!stricmp(pchKeyword, szCommandKeyword))
{
// Numeric value may follow "command" keyword. We don't use it,
// but we do a rough check that what we are seeing is consistent
// with what we expect.
*pch = chTmp;
while (isspace(*pch))
pch++;
if (!isdigit(*pch))
{
fBadSyntax = TRUE;
goto cleanup;
}
pch++;
while (isdigit(*pch))
pch++;
// Check for proper section entry termination.
if (*pch != ']' || *(pch + 1) != '\0')
{
fBadSyntax = TRUE;
goto cleanup;
}
ifstCurSection = IFST_COMMAND;
// Initialize command section parser state.
EditCmd =EDITCMD_INVALID;
fPositionArgFnd = FALSE;
MAPIFREEBUFFER(lpActions);
MAPIFREEBUFFER(lpRes);
lStateFlags = ST_ENABLED;
fFlagsArgFnd = FALSE;
}
else if (!stricmp(pchKeyword, szInitializationKeyword))
{
// Check for proper section entry termination.
// Initialization sections don't have a value.
*pch = chTmp;
if (*pch != ']' || *(pch + 1) != '\0')
{
fBadSyntax = TRUE;
goto cleanup;
}
ifstCurSection = IFST_INITIALIZATION;
}
else
{
PrintIniFileErr("Invalid keyword");
ifstCurSection = IFST_UNKNOWN;// Invalid section keyword.
*pch = chTmp;
goto cleanup;
}
fBadSyntax = FALSE;
cleanup:
if (fBadSyntax)
PrintIniFileErr("Bad syntax");
return;
}
//$--ParseEntryLine------------------------------------------------------------
//
// DESCRIPTION: Parse an ini file section entry line to determine what kind of
//entry it is, and store the value. Section entry lines are the
//lines that follow the section header.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//
//-----------------------------------------------------------------------------
VOID
ParseEntryLine(// RETURNS: VOID
IN CHAR * pch// input line
)
{
INIFILEPARSEINFO * aifpiEntry =NULL;
CHAR chTmp ='\0';
INT cKeywords =0;
INT i =0;
CHAR * pchKeyword =NULL;
DEBUGPRIVATE("ParseEntryLine()\n");
if (ifstCurSection == IFST_COMMAND)
{
aifpiEntry = aifpiCommandEntry;
cKeywords = sizeof(aifpiCommandEntry) /
sizeof(aifpiCommandEntry[0]);
}
else if (ifstCurSection == IFST_INITIALIZATION)
{
aifpiEntry = aifpiInitializationEntry;
cKeywords = sizeof(aifpiInitializationEntry) /
sizeof(aifpiInitializationEntry[0]);
}
else
{
PrintIniFileErr("Entry line not in a valid section");
goto cleanup;
}
// Save ptr to keyword.
pchKeyword = pch;
// Scan for end of keyword and delimit keyword, saving replaced char.
while (*pch != '=' && *pch != ':' && !isspace(*pch) && *pch != '\0')
pch++;
chTmp = *pch;
*pch = '\0';
// Compare putative keyword against valid section keywords.
for (i = 0; i < cKeywords; i++)
if (!stricmp(pchKeyword, aifpiEntry[i].pszKeyword))
break;
*pch = chTmp;
if (i == cKeywords)
{
PrintIniFileErr("Invalid keyword");
goto cleanup;
}
while (isspace(*pch))
pch++;
aifpiEntry[i].ParseFunction(*pch == '\0' ? NULL : pch + 1);
cleanup:
return;
}
//$--ParseActionValue----------------------------------------------------------
//
// DESCRIPTION: Parse an Action value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//
// Notes:In the case of an OP_MOVE or OP_COPY action, it is necessary
//to have a MAPI session to properly set up the action struct.
//If a session has not been established, we don't report an error
//even though the action could not have been completed, because
//the error will be caught as a fatal error in
//HrProcessCommandSection().
//-----------------------------------------------------------------------------
VOID
ParseActionValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
static CHARszCopyKeyword[] ="copy";
static CHARszMoveKeyword[] ="move";
static CHARszReplyKeyword[] ="reply";
HRESULThr =NOERROR;
DEBUGPRIVATE("ParseActionValue()\n");
if (pch == NULL|| *pch == '\0')
{
PrintIniFileErr("Action entry must have a value");
goto cleanup;
}
if (lpActions == NULL)
{
hr = MAPIAllocateBuffer(sizeof(ACTIONS), (LPVOID FAR *)&lpActions);
if (FAILED(hr))
{
PrintErr("Memory allocation failed");
EventLogHexErrorMsg(RULEEDIT_ALLOCATION_FAILED, hr);
goto cleanup;
}
lpActions->ulVersion =EDK_RULES_VERSION;
lpActions->cActions =0;
lpActions->lpAction =NULL;
hr = MAPIAllocateMore(sizeof(ACTION) * cMaxActions,
lpActions,
(LPVOID FAR *)&lpActions->lpAction);
if (FAILED(hr))
{
PrintErr("Memory allocation failed");
EventLogHexErrorMsg(RULEEDIT_ALLOCATION_FAILED, hr);
goto cleanup;
}
memset(lpActions->lpAction, 0, sizeof(ACTION) * cMaxActions);
}
else
{
if (lpActions->cActions > cMaxActions)
{
PrintIniFileErr("Maximum count of rule actions exceeded");
goto cleanup;
}
}
// Check if the session ptr is NULL. If so, further check to see if
// this is a move, copy or reply action. In that case we know
// HrStringToAction() would fail, and simply return. The fact that the
// session was not established will be reported by other code
// (see note above).
if (lpSession == NULL)
{
if (!memicmp(szCopyKeyword, pch, sizeof(szCopyKeyword) - 1)||
!memicmp(szMoveKeyword, pch, sizeof(szMoveKeyword) - 1)||
!memicmp(szReplyKeyword, pch, sizeof(szReplyKeyword) - 1))
goto cleanup;
}
hr = HrStringToAction(lpSession,
lpFolder,
pch,
lpActions,
&lpActions->lpAction[lpActions->cActions]);
if (FAILED(hr))
{
if (hr == E_INVALIDARG)
PrintIniFileErr("Bad syntax or value in action value");
else
PrintErr("Action creation failed for action at line %u of ini file;"
"Error code = %#x", iLine, hr);
goto cleanup;
}
lpActions->cActions++;
cleanup:
return;
}
//$--ParseCommandValue---------------------------------------------------------
//
// DESCRIPTION: Parse a Command value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParseCommandValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
DEBUGPRIVATE("ParseCommandValue()\n");
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Command entry must have a value");
goto cleanup;
}
if (EditCmd != EDITCMD_INVALID)
{
PrintIniFileErr("Multiple command entries");
goto cleanup;
}
if (!stricmp(pch, szDeleteKeyword))
{
EditCmd = EDITCMD_DELETE;
}
else if (!stricmp(pch, szDisableKeyword))
{
EditCmd = EDITCMD_DISABLE;
}
else if (!stricmp(pch, szEnableKeyword))
{
EditCmd = EDITCMD_ENABLE;
}
else if (!stricmp(pch, szInsertKeyword))
{
EditCmd = EDITCMD_INSERT;
}
else if (!stricmp(pch, szListKeyword))
{
EditCmd = EDITCMD_LIST;
}
else
{
PrintIniFileErr("Command '%s' is not valid", pch);
}
cleanup:
return;
}
//$--ParseConditionValue-------------------------------------------------------
//
// DESCRIPTION: Parse a Condition value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParseConditionValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
HRESULThr =NOERROR;
DEBUGPRIVATE("ParseConditionValue()\n");
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Condition entry must have a value");
goto cleanup;
}
if (lpRes != NULL)
{
PrintIniFileErr("Multiple condition entries");
goto cleanup;
}
hr = HrStringToRestriction(pch, NULL, &lpRes);
if (FAILED(hr))
{
if (hr == E_INVALIDARG)
PrintIniFileErr("Bad syntax or value in condition expression");
else
PrintErr("Restriction creation failed for condition at line %u "
"of ini file; Error code = %#x", iLine, hr);
goto cleanup;
}
cleanup:
return;
}
//$--ParseFlagsValue-----------------------------------------------------------
//
// DESCRIPTION: Parse a Flags value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParseFlagsValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
CHARchTmp ='\0';
BOOLfBadSyntax =FALSE;
BOOLfDisabledKeywordFnd =FALSE;
CHAR *pchEnd =NULL;
DEBUGPRIVATE("ParseFlagsValue()\n");
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Flags entry must have a value");
goto cleanup;
}
if (fFlagsArgFnd)
{
PrintIniFileErr("Multiple Flags entries");
goto cleanup;
}
while (isspace(*pch))// Skip leading line whitespace.
pch++;
if (*pch == '\0')
{
fBadSyntax = TRUE;
goto cleanup;
}
// We override the default of ST_ENABLED since we have a flags= entry.
lStateFlags = ST_DISABLED;
while(TRUE)
{
pchEnd = pch;
while (isalpha(*pchEnd))
pchEnd++;
chTmp = *pchEnd;
*pchEnd = '\0';
if (!stricmp(pch, szEnabledKeyword))
{
lStateFlags |=ST_ENABLED;
}
else if (!stricmp(pch, szDisabledKeyword))
{
fDisabledKeywordFnd = TRUE;
}
else if (!stricmp(pch, szUserOOFKeyword))
{
lStateFlags |=ST_ONLY_WHEN_OOF;
}
else if (!stricmp(pch, szErrorKeyword))
{
lStateFlags |=ST_ERROR;
}
else if (!stricmp(pch, szKeepOOFHistKeyword))
{
lStateFlags |=ST_KEEP_OOF_HIST;
}
else if (!stricmp(pch, szExitLevelKeyword))
{
lStateFlags |=ST_EXIT_LEVEL;
}
else
{
*pchEnd = chTmp;
fBadSyntax = TRUE;
goto cleanup;
}
*pchEnd = chTmp;
pch = pchEnd;
while (isspace(*pch))
pch++;
if (*pch == '\0')
break;
if (*pch == ',')
{
pch++;
}
else
{
fBadSyntax = TRUE;
goto cleanup;
}
while (isspace(*pch))
pch++;
}
if ((lStateFlags & ST_ENABLED) && fDisabledKeywordFnd)
{
PrintIniFileErr("Conflicting flags in Flags entry");
goto cleanup;
}
fFlagsArgFnd = TRUE;
cleanup:
if (fBadSyntax)
{
PrintIniFileErr("Bad syntax in Flags entry");
goto cleanup;
}
return;
}
//$--ParsePositionValue--------------------------------------------------------
//
// DESCRIPTION: Parse a position value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParsePositionValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
DEBUGPRIVATE("ParsePositionValue()\n");
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Position entry must have a value");
goto cleanup;
}
if (fPositionArgFnd)
{
PrintIniFileErr("Multiple position entries");
goto cleanup;
}
if (!stricmp(pch, "END"))
{
lCursor = RULE_PAST_END;
}
else
{
errno = 0;
lCursor = strtol(pch, (CHAR **)NULL, 10);
if ((lCursor == LONG_MAX || lCursor == LONG_MIN) &&
errno == ERANGE)
{
PrintIniFileErr("Position value invalid");
goto cleanup;
}
}
fPositionArgFnd = TRUE;
cleanup:
return;
}
//$--ParseFolderValue----------------------------------------------------------
//
// DESCRIPTION: Parse a Folder value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParseFolderValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
DEBUGPRIVATE("ParseFolderValue()\n");
if (fSkipFolderArg)
goto cleanup;
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Folder entry must have a value");
goto cleanup;
}
if (szFolder[0] != '\0' || fInboxArgFnd)
{
PrintIniFileErr("Multiple folder entries");
goto cleanup;
}
if (!stricmp(pch, szInboxKeyword))
{
// It is the special name "inbox", which is interpreted as
// appropriate.
fInboxArgFnd = TRUE;
}
else
{
// Split the store and folder names and check their lengths.
CHAR *pszFolder = strchr(pch, '\\');
if (pszFolder == NULL)
{
PrintIniFileErr("Invalid folder name");
goto cleanup;
}
*pszFolder = '\0';
pszFolder++;
if (*pch == '\0')
{
PrintIniFileErr("Invalid folder name");
goto cleanup;
}
if (strlen(pch) >= sizeof(szStore))
{
PrintIniFileErr("Folder name too long");
goto cleanup;
}
strcpy(szStore, pch);
if (*pszFolder == '\0')
{
PrintIniFileErr("Invalid folder name");
goto cleanup;
}
if (strlen(pszFolder) >= sizeof(szFolder))
{
PrintIniFileErr("Folder name too long");
goto cleanup;
}
strcpy(szFolder, pszFolder);
}
cleanup:
return;
}
//$--ParseProfileValue---------------------------------------------------------
//
// DESCRIPTION: Parse a Profile value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParseProfileValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
DEBUGPRIVATE("ParseProfileValue()\n");
if (fSkipProfileArg)
goto cleanup;
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Profile entry must have a value");
goto cleanup;
}
if (szProfile[0] != '\0')
{
PrintIniFileErr("Multiple profile entries");
goto cleanup;
}
if (strlen(pch) >= sizeof(szProfile))
{
PrintIniFileErr("Profile name too long");
goto cleanup;
}
strcpy(szProfile, pch);
cleanup:
return;
}
//$--ParseProviderValue--------------------------------------------------------
//
// DESCRIPTION: Parse a Provider value entry line.
//
// INPUT: [pch]-- Ptr to input string.
//
// RETURNS: Nothing.
//-----------------------------------------------------------------------------
VOID
ParseProviderValue(// RETURNS: VOID
IN CHAR * pch// input line
)
{
DEBUGPRIVATE("ParseProviderValue()\n");
if (fSkipProviderArg)
goto cleanup;
if (pch == NULL || *pch == '\0')
{
PrintIniFileErr("Provider entry must have a value");
goto cleanup;
}
if (szProvider[0] != '\0')
{
PrintIniFileErr("Multiple provider entries");
goto cleanup;
}
if (strlen(pch) >= sizeof(szProvider))
{
PrintIniFileErr("Provider name too long");
goto cleanup;
}
strcpy(szProvider, pch);
fProviderArgFnd = TRUE;
cleanup:
return;
}
//$--PrintActions--------------------------------------------------------------
//
// DESCRIPTION: Print the rule actions.
//
// INPUT:
//
//[lpActs]-- Rule actions to print.
//
// RETURNS:VOID
//
//-----------------------------------------------------------------------------
VOID
PrintActions( // RETURNS: VOID
INLPACTIONSlpActs // actions ptr
)
{
HRESULThr =NOERROR;
ULONGi;
LPACTIONlpAct =NULL;
LPSTRlpszAct =NULL;
DEBUGPRIVATE("PrintActions()\n");
if (lpActs->cActions <= 0 || lpActs->lpAction == NULL)
{
printf("Rule Actions structure is invalid.\n");
goto cleanup;
}
for (i = 0; i < lpActs->cActions; i++)
{
lpAct =&(lpActs->lpAction[i]);
hr = HrActionToString(lpSession, lpAct, &lpszAct);
if (FAILED(hr))
{
printf("String representation of rule action %u "
"could not be determined.\n", i);
cErrs++;
EventLogHexErrorMsg(RULEEDIT_ACTIONTOSTRING_FAILED, hr);
}
else
{
printf("Rule Action %u = %s\n", i, lpszAct);
}
MAPIFREEBUFFER(lpszAct);
}
cleanup:
return;
}
//$--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;
}
//$--PrintIniFileErr-----------------------------------------------------------
//
// DESCRIPTION:Print an error message to stdout in a standard format.
//This is intended primarily for use in reporting ini file
//input errors.
//
// INPUT:
//
//[Format]-- Printf-style format string.
//[...]-- Variable argument parameters for format string.
//
// RETURNS:Nothing.
//
//-----------------------------------------------------------------------------
VOID
PrintIniFileErr( // RETURNS: VOID
INCHAR *Format // error message format
...
)
{
va_listargs;
DEBUGPRIVATE("PrintIniFileErr()\n");
cErrs++;
printf("ERROR: ");
va_start(args, Format);
vprintf(Format, args);
va_end(args);
printf(" in line %u of Ini File.\n\n", iLine);
return;
}
//$--PrintStateFlags-----------------------------------------------------------
//
// DESCRIPTION: Print the rule state flags.
//
// INPUT:
//
//[lStateFlags]-- Rule state flags to print.
//
// RETURNS:VOID
//
//-----------------------------------------------------------------------------
VOID
PrintStateFlags( // RETURNS: VOID
INLONGlStateFlags// state flags
)
{
DEBUGPRIVATE("PrintStateFlags()\n");
printf("Rule state flags =");
if (lStateFlags & ST_ENABLED)
printf(" Enabled");
else
printf(" Disabled");
if (lStateFlags & ST_ERROR)
printf(", Error");
if (lStateFlags & ST_ONLY_WHEN_OOF)
printf(", UserOOF");
if (lStateFlags & ST_KEEP_OOF_HIST)
printf(", KeepOOFHist");
if (lStateFlags & ST_EXIT_LEVEL)
printf(", ExitLevel");
printf("\n");
return;
}
//$--Usage---------------------------------------------------------------------
//
// DESCRIPTION:Print a usage message to the console.
//
// INPUT:None.
//
// RETURNS:Nothing.
//-----------------------------------------------------------------------------
VOID
Usage(VOID) // RETURNS: VOID
{
DEBUGPRIVATE("Usage()\n");
printf(
"\n"
"USAGE: RULEEDIT [/Profile=<ProfileName>]"
"\n"
" [<Folder>]"
"\n"
" [/List | /File=<IniFile>]"
"\n"
" [/Provider=<ProviderName>]"
"\n"
"\n"
"<Folder> ::= /Folder=<StoreName>\\<FolderPath> | /Folder=Inbox"
"\n"
"\n"
"Enter RULEEDIT /? for more details."
"\n"
"\n"
);
}