Skip to content

Commit

Permalink
Merge pull request #566 from stephenegriffin/u/sgriffin/props
Browse files Browse the repository at this point in the history
Interesting property filter
  • Loading branch information
stephenegriffin authored Mar 27, 2023
2 parents 13be8c3 + e8a4135 commit ddb4fb2
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 135 deletions.
27 changes: 19 additions & 8 deletions MrMapi/MMContents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ void DumpContentsTable(
_In_opt_ LPSRestriction lpRes)
{
output::DebugPrint(
output::dbgLevel::Generic,
output::dbgLevel::Console,
L"DumpContentsTable: Outputting folder %ws from profile %ws to %ws\n",
lpszFolder,
lpszProfile,
lpszDir);
if (cli::switchContents.isSet())
output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: Outputting Contents\n");
output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: Outputting Contents\n");
if (cli::switchAssociatedContents.isSet())
output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: Outputting Associated Contents\n");
output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: Outputting Associated Contents\n");
if (cli::switchMSG.isSet())
output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: Outputting as MSG\n");
output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: Outputting as MSG\n");
if (cli::switchMoreProperties.isSet())
output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: Will retry stream properties\n");
output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: Will retry stream properties\n");
if (cli::switchSkip.isSet())
output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: Will skip attachments\n");
if (cli::switchList.isSet()) output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: List only mode\n");
output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: Will skip attachments\n");
if (cli::switchList.isSet()) output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: List only mode\n");
if (ulCount)
output::DebugPrint(output::dbgLevel::Generic, L"DumpContentsTable: Limiting output to %u messages.\n", ulCount);
output::DebugPrint(output::dbgLevel::Console, L"DumpContentsTable: Limiting output to %u messages.\n", ulCount);

if (lpFolder)
{
Expand All @@ -43,6 +43,17 @@ void DumpContentsTable(
MyDumpStore.InitFolder(lpFolder);
MyDumpStore.InitFolderPathRoot(lpszDir);
MyDumpStore.InitFolderContentsRestriction(lpRes);
// If any properties passed in, pass them along.
if (cli::switchFindProperty.size() != 0)
{
MyDumpStore.InitProperties(cli::switchFindProperty);
}

if (cli::switchFindNamedProperty.size() != 0)
{
MyDumpStore.InitNamedProperties(cli::switchFindNamedProperty);
}

if (cli::switchMSG.isSet()) MyDumpStore.EnableMSG();
if (cli::switchList.isSet()) MyDumpStore.EnableList();
if (ulCount)
Expand Down
92 changes: 3 additions & 89 deletions MrMapi/MMPropTag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,91 +4,11 @@
#include <core/interpret/guid.h>
#include <core/smartview/SmartView.h>
#include <core/utility/strings.h>
#include <core/addin/addin.h>
#include <core/addin/mfcmapi.h>
#include <core/utility/output.h>
#include <core/interpret/proptags.h>
#include <core/interpret/proptype.h>

// Searches a NAMEID_ARRAY_ENTRY array for a target dispid.
// Exact matches are those that match
// If no hits, then ulNoMatch should be returned for lpulFirstExact
void FindNameIDArrayMatches(
_In_ LONG lTarget,
_In_count_(ulMyArray) NAMEID_ARRAY_ENTRY* MyArray,
_In_ ULONG ulMyArray,
_Out_ ULONG* lpulNumExacts,
_Out_ ULONG* lpulFirstExact) noexcept
{
ULONG ulLowerBound = 0;
auto ulUpperBound = ulMyArray - 1; // ulMyArray-1 is the last entry
auto ulMidPoint = (ulUpperBound + ulLowerBound) / 2;
ULONG ulFirstMatch = cache::ulNoMatch;
ULONG ulLastMatch = cache::ulNoMatch;

if (lpulNumExacts) *lpulNumExacts = 0;
if (lpulFirstExact) *lpulFirstExact = cache::ulNoMatch;

// find A match
while (ulUpperBound - ulLowerBound > 1)
{
if (lTarget == MyArray[ulMidPoint].lValue)
{
ulFirstMatch = ulMidPoint;
break;
}

if (lTarget < MyArray[ulMidPoint].lValue)
{
ulUpperBound = ulMidPoint;
}
else if (lTarget > MyArray[ulMidPoint].lValue)
{
ulLowerBound = ulMidPoint;
}
ulMidPoint = (ulUpperBound + ulLowerBound) / 2;
}

// When we get down to two points, we may have only checked one of them
// Make sure we've checked the other
if (lTarget == MyArray[ulUpperBound].lValue)
{
ulFirstMatch = ulUpperBound;
}
else if (lTarget == MyArray[ulLowerBound].lValue)
{
ulFirstMatch = ulLowerBound;
}

// Check that we got a match
if (cache::ulNoMatch != ulFirstMatch)
{
ulLastMatch = ulFirstMatch; // Remember the last match we've found so far

// Scan backwards to find the first match
while (ulFirstMatch > 0 && lTarget == MyArray[ulFirstMatch - 1].lValue)
{
ulFirstMatch = ulFirstMatch - 1;
}

// Scan forwards to find the real last match
// Last entry in the array is ulPropTagArray-1
while (ulLastMatch + 1 < ulMyArray && lTarget == MyArray[ulLastMatch + 1].lValue)
{
ulLastMatch = ulLastMatch + 1;
}

ULONG ulNumMatches = 0;

if (cache::ulNoMatch != ulFirstMatch)
{
ulNumMatches = ulLastMatch - ulFirstMatch + 1;
}

if (lpulNumExacts) *lpulNumExacts = ulNumMatches;
if (lpulFirstExact) *lpulFirstExact = ulFirstMatch;
}
}
#include <core/mapi/cache/namedProps.h>

// prints the type of a prop tag
// no pretty stuff or \n - calling function gets to do that
Expand Down Expand Up @@ -382,8 +302,7 @@ void PrintDispIDFromNum(_In_ ULONG ulDispID) noexcept

wprintf(L"Dispid tag 0x%04lX:\n", ulDispID);

FindNameIDArrayMatches(
ulDispID, NameIDArray.data(), static_cast<ULONG>(NameIDArray.size()), &ulNumExacts, &ulFirstExactMatch);
cache::FindNameIDArrayMatches(ulDispID, &ulNumExacts, &ulFirstExactMatch);

if (ulNumExacts > 0 && cache::ulNoMatch != ulFirstExactMatch)
{
Expand Down Expand Up @@ -412,12 +331,7 @@ void PrintDispIDFromName(_In_opt_z_ LPCWSTR lpszDispIDName) noexcept
ULONG ulNumExacts = NULL;
ULONG ulFirstExactMatch = cache::ulNoMatch;

FindNameIDArrayMatches(
NameIDArray[ulExactMatch].lValue,
NameIDArray.data(),
static_cast<ULONG>(NameIDArray.size()),
&ulNumExacts,
&ulFirstExactMatch);
cache::FindNameIDArrayMatches(NameIDArray[ulExactMatch].lValue, &ulNumExacts, &ulFirstExactMatch);

// We're gonna skip at least one, so only print if we have more than one
if (ulNumExacts > 1)
Expand Down
4 changes: 3 additions & 1 deletion MrMapi/MrMAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ int wmain(_In_ int argc, _In_count_(argc) wchar_t* argv[])
registry::useGetPropList = true;
registry::parseNamedProps = true;
registry::cacheNamedProps = true;
registry::debugTag = 0;
registry::debugTag =
static_cast<DWORD>(output::dbgLevel::Console); // Any debug logging with Console will print to the console now

output::initStubCallbacks();

SetDllDirectory(_T(""));
Expand Down
15 changes: 14 additions & 1 deletion MrMapi/mmcli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ namespace cli
option switchAccounts{L"Accounts", cmdmodeEnumAccounts, 0, 0, OPT_INITALL | OPT_PROFILE};
option switchIterate{L"Iterate", cmdmodeEnumAccounts, 0, 0, OPT_NOOPT};
option switchWizard{L"Wizard", cmdmodeEnumAccounts, 0, 0, OPT_NOOPT};
option switchFindProperty{L"FindProperty", cmdmodeContents, 1, USHRT_MAX, OPT_INITALL};
option switchFindNamedProperty{L"FindNamedProperty", cmdmodeContents, 1, USHRT_MAX, OPT_INITALL};

// If we want to add aliases for any switches, add them here
option switchHelpAlias{L"Help", cmdmodeHelpFull, 0, 0, OPT_INITMFC};
Expand Down Expand Up @@ -129,6 +131,8 @@ namespace cli
&switchAccounts,
&switchIterate,
&switchWizard,
&switchFindProperty,
&switchFindNamedProperty,
// If we want to add aliases for any switches, add them here
&switchHelpAlias,
};
Expand Down Expand Up @@ -191,11 +195,14 @@ namespace cli
switchProfile.name(),
switchFolder.name());
wprintf(
L" MrMAPI -%ws | -%ws [-%ws <profile>] [-%ws <folder>] [-%ws <output directory>]\n",
L" MrMAPI -%ws | -%ws [-%ws <profile>] [-%ws <folder>] [-%ws <property names>] [-%ws <dispid names>] "
L"[-%ws <output directory>]\n",
switchContents.name(),
switchAssociatedContents.name(),
switchProfile.name(),
switchFolder.name(),
switchFindProperty.name(),
switchFindNamedProperty.name(),
switchOutput.name());
wprintf(
L" [-%ws <subject>] [-%ws <message class>] [-%ws] [-%ws] [-%ws <count>] [-%ws]\n",
Expand Down Expand Up @@ -336,6 +343,12 @@ namespace cli
wprintf(L" -Ms (or -%ws) Output as .MSG instead of XML.\n", switchMSG.name());
wprintf(L" -L (or -%ws) List details to screen and do not output files.\n", switchList.name());
wprintf(L" -Re (or -%ws) Restrict output to the 'count' most recent messages.\n", switchRecent.name());
wprintf(
L" -FindP (or -%ws) Restrict output to messages which contain given properties.\n",
switchFindProperty.name());
wprintf(
L" -FindN (or -%ws) Restrict output to messages which contain given named properties.\n",
switchFindNamedProperty.name());
wprintf(L"\n");
wprintf(L" Child Folders:\n");
wprintf(L" -Chi (or -%ws) Display child folders of selected folder.\n", switchChildFolders.name());
Expand Down
2 changes: 2 additions & 0 deletions MrMapi/mmcli.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ namespace cli
extern option switchAccounts;
extern option switchIterate;
extern option switchWizard;
extern option switchFindProperty;
extern option switchFindNamedProperty;

extern std::vector<option*> g_options;

Expand Down
110 changes: 110 additions & 0 deletions core/mapi/cache/namedProps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ namespace cache

MAPIFreeBuffer(lpProp);

// If we don't have a signature, bypass the cache
if (sig.empty())
{
return directMapi::GetIDsFromNames(lpMAPIProp, nameIDs, ulFlags);
}

return namedPropCache::GetIDsFromNames(lpMAPIProp, sig, nameIDs, ulFlags);
}

Expand Down Expand Up @@ -503,4 +509,108 @@ namespace cache
return namedPropCacheEntry::empty();
}
}

// Searches a NAMEID_ARRAY_ENTRY array for a target dispid.
// Exact matches are those that match
// If no hits, then ulNoMatch should be returned for lpulFirstExact
void FindNameIDArrayMatches(
_In_ LONG lTarget,
_In_count_(ulMyArray) NAMEID_ARRAY_ENTRY* MyArray,
_In_ ULONG ulMyArray,
_Out_ ULONG* lpulNumExacts,
_Out_ ULONG* lpulFirstExact) noexcept
{
ULONG ulLowerBound = 0;
auto ulUpperBound = ulMyArray - 1; // ulMyArray-1 is the last entry
auto ulMidPoint = (ulUpperBound + ulLowerBound) / 2;
ULONG ulFirstMatch = cache::ulNoMatch;
ULONG ulLastMatch = cache::ulNoMatch;

if (lpulNumExacts) *lpulNumExacts = 0;
if (lpulFirstExact) *lpulFirstExact = cache::ulNoMatch;

// find A match
while (ulUpperBound - ulLowerBound > 1)
{
if (lTarget == MyArray[ulMidPoint].lValue)
{
ulFirstMatch = ulMidPoint;
break;
}

if (lTarget < MyArray[ulMidPoint].lValue)
{
ulUpperBound = ulMidPoint;
}
else if (lTarget > MyArray[ulMidPoint].lValue)
{
ulLowerBound = ulMidPoint;
}
ulMidPoint = (ulUpperBound + ulLowerBound) / 2;
}

// When we get down to two points, we may have only checked one of them
// Make sure we've checked the other
if (lTarget == MyArray[ulUpperBound].lValue)
{
ulFirstMatch = ulUpperBound;
}
else if (lTarget == MyArray[ulLowerBound].lValue)
{
ulFirstMatch = ulLowerBound;
}

// Check that we got a match
if (cache::ulNoMatch != ulFirstMatch)
{
ulLastMatch = ulFirstMatch; // Remember the last match we've found so far

// Scan backwards to find the first match
while (ulFirstMatch > 0 && lTarget == MyArray[ulFirstMatch - 1].lValue)
{
ulFirstMatch = ulFirstMatch - 1;
}

// Scan forwards to find the real last match
// Last entry in the array is ulPropTagArray-1
while (ulLastMatch + 1 < ulMyArray && lTarget == MyArray[ulLastMatch + 1].lValue)
{
ulLastMatch = ulLastMatch + 1;
}

ULONG ulNumMatches = 0;

if (cache::ulNoMatch != ulFirstMatch)
{
ulNumMatches = ulLastMatch - ulFirstMatch + 1;
}

if (lpulNumExacts) *lpulNumExacts = ulNumMatches;
if (lpulFirstExact) *lpulFirstExact = ulFirstMatch;
}
}

void FindNameIDArrayMatches(_In_ LONG lTarget, _Out_ ULONG* lpulNumExacts, _Out_ ULONG* lpulFirstExact) noexcept
{
FindNameIDArrayMatches(
lTarget, NameIDArray.data(), static_cast<ULONG>(NameIDArray.size()), lpulNumExacts, lpulFirstExact);
}

// Search for properties matching lpszDispIDName on a substring
_Check_return_ LPNAMEID_ARRAY_ENTRY GetDispIDFromName(_In_z_ LPCWSTR lpszDispIDName)
{
if (!lpszDispIDName) return nullptr;

const auto entry = find_if(begin(NameIDArray), end(NameIDArray), [&](NAMEID_ARRAY_ENTRY& nameID) noexcept {
if (0 == wcscmp(nameID.lpszName, lpszDispIDName))
{
// PSUNKNOWN is used as a placeholder in NameIDArray - don't return matching entries
if (!IsEqualGUID(*nameID.lpGuid, guid::PSUNKNOWN)) return true;
}

return false;
});

return entry != end(NameIDArray) ? &(*entry) : nullptr;
}
} // namespace cache
5 changes: 5 additions & 0 deletions core/mapi/cache/namedProps.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once
// Named Property Cache
#include <core/addin/addin.h>
#include <core/addin/MFCMAPI.h>

namespace cache
{
Expand Down Expand Up @@ -108,4 +110,7 @@ namespace cache
_Check_return_ std::shared_ptr<namedPropCacheEntry> find(
const std::vector<std::shared_ptr<namedPropCacheEntry>>& list,
const std::function<bool(const std::shared_ptr<namedPropCacheEntry>&)>& compare);

void FindNameIDArrayMatches(_In_ LONG lTarget, _Out_ ULONG* lpulNumExacts, _Out_ ULONG* lpulFirstExact) noexcept;
_Check_return_ LPNAMEID_ARRAY_ENTRY GetDispIDFromName(_In_z_ LPCWSTR lpszDispIDName);
} // namespace cache
1 change: 1 addition & 0 deletions core/mapi/mapiOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,5 +697,6 @@ namespace output
}

Output(ulDbgLvl, fFile, true, strings::StripCarriage(property::RestrictionToString(lpRes, lpObj)));
Output(ulDbgLvl, fFile, true, L"\n");
}
} // namespace output
Loading

0 comments on commit ddb4fb2

Please sign in to comment.