Read.cpp
Read.cpp
Important This sample code may not fully verify that strings passed to it are in fact null-terminated, nor that the referenced string buffers are large enough to store the generated contents. Your production code should always verify the validity and size of data passed as null-terminated strings before using, copying or adding to them. Using more-safe versions of the standard C string-handling functions is also recommended.
//------------------------------------------------------------
//
// File: read.cpp, Implementation of Cread
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Contents: Read Class Implements IMailTransportCategorize, IEventIsCacheable
//
// Classes: Cread
//
// Functions:
// CAttrEntry::operator new
// CAttrList::AddEntry
// Cread::IsCacheable
// Cread::CheckIfSinkHasInitalised
// Cread::HrReadRegParams
// Cread::Register
// Cread::ProcessItem
// Cread::AllocAndSetPropIdInfo
// Cread::SaveAttributeValueOnPropIdInfo
// ConvertStringtoCLSID
// ShowMessage
//
//-------------------------------------------------------------
#include "stdafx.h"
#include "CatReader.h"
#include "mailmsgprops.h"
#include "read.h"
/////////////////////////////////////////////////////////////////////////////
//
// Cread
//
// Function: Cread::IsCacheable
//
// Synopsis: Implementation of method for IEventIsCacheable
// If S_OK is returned, the sink is cacheable (the instance
// will not be loaded and unloaded every time).
//
// Arguments:
// None
//
// Returns:
// S_OK if successful
//
//-------------------------------------------------------------
HRESULT Cread::IsCacheable()
{
return S_OK;
}
//-------------------------------------------------------------
//
// Function: Cread::CheckIfSinkHasInitalised
//
// Synopsis: Function that checks the initialization state. If initialization
// is going to be attempted for the first time, the function tries
// to initialize the sink. It remembers the state, if it succeeded or
// not, and returns it.
//
//
// Arguments:
// None
//
// Returns:
// S_OK The sink initialization succeeded
// S_FALSE The sink initialization failed
//
//-------------------------------------------------------------
HRESULT Cread::CheckIfSinkHasInitalised()
{
PCFunctEnterEx((LPARAM)this, "Cread::CheckIfSinkHasInitalised");
HRESULT hr = S_OK;
BOOL fAcquireCS = FALSE;
// Initialization is attempted only once.
if(m_fTrySinkInit == TRUE)
goto CLEANUP;
EnterCriticalSection(&m_cs);
fAcquireCS = TRUE;
if(m_fTrySinkInit == FALSE) {
hr = HrReadRegParams(&m_attrList);
m_fSinkInitSuccess = SUCCEEDED(hr);
m_fTrySinkInit= TRUE;
if(FAILED(hr)) {
PCErrorTraceEx((LPARAM)this,
"CheckIfSinkHasInitalised failed with 0x%08lx",
hr);
goto CLEANUP;
}
}
CLEANUP:
if(fAcquireCS)
LeaveCriticalSection(&m_cs);
PCFunctLeaveEx((LPARAM)this);
return m_fSinkInitSuccess ? S_OK : S_FALSE;
}
//-------------------------------------------------------------
//
// Function: Cread::Register
//
// Synopsis: Register is a Categorizer event. Register is a method of the
// IMailTransportCategorize interface. This is where you request
// the attributes that you would like the resolver code (known
// as the Categorizer) to return. This involves traversing through
// your list and doing a request for each attribute.
//
// Arguments:
// [in] ICategorizerParameters * pICatParams
// (see definition in smtpevent.idl)
//
// Returns:
// S_OK if the function succeeded
//
//-------------------------------------------------------------
HRESULT Cread::Register(
ICategorizerParameters * pICatParams)
{
HRESULT hr = S_OK;
PCFunctEnterEx((LPARAM)this, "CPhatCat::Register");
CAttrEntry *pCurrent = NULL;
if ( S_OK != CheckIfSinkHasInitalised() ) {
PCErrorTraceEx( (LPARAM) this, "CatReader Sink Not Initialized! CatReader Sink skipping ...");
goto CLEANUP;
}
pCurrent = m_attrList.GetHeadEntry();
while(NULL != pCurrent) {
hr = pICatParams->RequestAttributeA(
m_attrList.GetAttrName(pCurrent));
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this, "RequestAttributeA Failed with 0x%08lx",
hr);
goto CLEANUP;
}
PCDebugTraceEx( (LPARAM) this, "Successfully Requested Client's Attribute %s",
m_attrList.GetAttrName(pCurrent));
pCurrent = m_attrList.GetNextEntry(pCurrent);
}
CLEANUP:
PCDebugTraceEx((LPARAM)this, "result %08lx", hr);
PCFunctLeaveEx((LPARAM)this);
return SUCCEEDED(hr) ? S_OK : hr;
}
//-------------------------------------------------------------
//
// Function: Cread::ProcessItem
//
// Synopsis: First completion event for an ICategorizerItem after its directory
// service (DS) lookup (matched with an ICategorizerItemAttributes
// on success). This item can be the sender or one of the recipients
// of the message. Cread makes use of this event by intercepting
// the completion, checking to see if any of the requested attributes
// were found on the item. If so, it requests a set of mailmsg property
// IDs and saves the attribute's values on them. This is done so that
// any client that uses this sink to retrieve its attributes can write
// another postcat sink (see the catreadex example, postcatreader.cpp)
// to request back the same property set and therefore access its
// attribute's values.
//
// Arguments:
// [in] ICategorizerParameters * pICatParams
// (see the definition in smtpevent.idl)
// [in] ICategorizerItem * pICatItem
// (see the definition in smtpevent.idl)
//
// Returns:
// S_OK if the function succeeded.
//
//-------------------------------------------------------------
HRESULT Cread::ProcessItem(
ICategorizerParameters * pICatParams,
ICategorizerItem * pICatItem)
{
HRESULT hr = S_OK;
IMailMsgProperties * pIMailMsgProperties = NULL;
IMailMsgRecipientsAdd * pIMailMsgRecipientsAdd = NULL;
ICategorizerItemAttributes * pICategorizerItemAttributes = NULL;
ICategorizerItemRawAttributes * pIRawAttributes = NULL;
DWORD dwRecipIndex;
CAttrEntry *pCurrent = NULL;
eSourceType SourceType;
PCFunctEnterEx((LPARAM)this, "CPhatCat::ProcessItem");
if ( S_OK != CheckIfSinkHasInitalised() ) {
PCErrorTraceEx( (LPARAM) this, "CatReader Sink Not Initialized! CatReader Sink skipping ...");
goto CLEANUP;
}
// Determine whether this pICatItem is a sender or a recipient.
hr = pICatItem->GetDWORD(
ICATEGORIZERITEM_SOURCETYPE,
(DWORD *) &SourceType);
if (FAILED(hr))
{
PCErrorTraceEx( (LPARAM) this, "Failed pICatItem->GetDWORD 0x%08lx",
hr);
goto CLEANUP;
}
hr = pICatItem->GetIMailMsgProperties(
ICATEGORIZERITEM_IMAILMSGPROPERTIES,
&pIMailMsgProperties);
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this, "Failed pICatItem->GetIMailMsgProperties 0x%08lx",
hr);
goto CLEANUP;
}
// If the pICatItem is the sender, then operations are done on the
// IMailMsgProperties interface.
// For message recipients, operations are done on the IMailMsgRecipientsAdd
// interface.
if (SourceType == SOURCE_SENDER)
{
PCDebugTraceEx((LPARAM)this,"Item processed is the Sender ");
ShowMessage("\n\n");
ShowMessage("==========================================================================================");
ShowMessage("Message");
ShowMessage("*******");
TCHAR szSenderSmtpAddress[MAX_EMAIL_LENGTH];
hr = pIMailMsgProperties->GetStringA(
IMMPID_MP_SENDER_ADDRESS_SMTP,
MAX_EMAIL_LENGTH,
(TCHAR *)szSenderSmtpAddress);
if(FAILED(hr) && hr != MAILMSG_E_PROPNOTFOUND) {
PCErrorTraceEx((LPARAM)this, "pIMailMsgProperties->GetStringA failed with 0x%08lx",
hr);
goto CLEANUP;
}
// For debugging, output the sender.
if (SUCCEEDED(hr))
ShowMessage("CatReader Detected Sender: %s", szSenderSmtpAddress);
}
else if (SourceType == SOURCE_RECIPIENT) {
TCHAR szRecipAddress[MAX_EMAIL_LENGTH];
PCDebugTraceEx((LPARAM)this,"Item processed is a Recipient");
hr = pICatItem->GetIMailMsgRecipientsAdd(
ICATEGORIZERITEM_IMAILMSGRECIPIENTSADD,
&pIMailMsgRecipientsAdd);
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this, "Failed pICatItem->GetIMailMsgRecipientsAdd 0x%08lx",
hr);
goto CLEANUP;
}
// Find the index for the recipient in the recipient list.
hr = pICatItem->GetDWORD(
ICATEGORIZERITEM_IMAILMSGRECIPIENTSADDINDEX,
&dwRecipIndex);
if (FAILED(hr))
{
PCErrorTraceEx((LPARAM)this, "Failed GetDWORD for getting ICatItem hr %08lx", hr);
goto CLEANUP;
}
ShowMessage("Recipient");
ShowMessage("*******");
hr = pIMailMsgRecipientsAdd->GetStringA(
dwRecipIndex,
IMMPID_RP_ADDRESS_SMTP,
MAX_EMAIL_LENGTH,
(LPSTR) szRecipAddress);
if (FAILED(hr) && hr != MAILMSG_E_PROPNOTFOUND)
{
PCErrorTraceEx((LPARAM)this, "Failed pIMailMsgRecipientsAdd->GetStringA hr %08lx", hr);
goto CLEANUP;
}
// For debugging, output the recipient of the message.
if (SUCCEEDED(hr))
ShowMessage("CatReader Detected Recipient: %s", szRecipAddress);
}
// All of the attributes for pICatItem can be accessed here.
hr = pICatItem->GetICategorizerItemAttributes(
ICATEGORIZERITEM_ICATEGORIZERITEMATTRIBUTES,
&pICategorizerItemAttributes);
if (FAILED(hr)) {
// This could mean that the object (sender/recipient) was not found in the DS.
// That means there are no attributes to save.
ShowMessage("Categorizer did not find this object in the DS");
hr = S_OK;
goto CLEANUP;
}
// ICategorizerItemRawAttributes stores the values as bervals.
hr = pICategorizerItemAttributes->QueryInterface(
IID_ICategorizerItemRawAttributes,
(PVOID *)&pIRawAttributes);
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this,
"Failed pICategorizerItemAttributes->QueryInterface 0x%08lx",
hr);
goto CLEANUP;
}
// Traverse through the list of requested attributes and check to see if
// the DS lookup found anything.
pCurrent = m_attrList.GetHeadEntry();
while(NULL != pCurrent) {
hr = FindAndProcessAttributeFromCat(
SourceType,
pIMailMsgProperties,
pIMailMsgRecipientsAdd,
pIRawAttributes,
dwRecipIndex,
pCurrent);
if(FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this,
"Failed FindAndProcessAttributeFromCat 0x%08lx",
hr);
goto CLEANUP;
}
pCurrent = m_attrList.GetNextEntry(pCurrent);
}
CLEANUP:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
if (pIMailMsgRecipientsAdd)
pIMailMsgRecipientsAdd->Release();
if (pIRawAttributes)
pIRawAttributes->Release();
if (pICategorizerItemAttributes)
pICategorizerItemAttributes->Release();
PCDebugTraceEx((LPARAM)this, "result %08lx", hr);
PCFunctLeaveEx((LPARAM)this);
return SUCCEEDED(hr) ? S_OK : hr;
}
//-------------------------------------------------------------
//
// Function: Cread::FindAndProcessAttributeFromCat
//
// Synopsis: Finds the requested attribute from Cat. If it is found, it will allocate
// the required number of property IDs and save the attribute values
// on them.
//
// Arguments:
// [in] eSourceType SourceType
// [in] IMailMsgProperties *pIMailMsgProperties
// [in] IMailMsgRecipientsAdd *pIMailMsgRecipientsAdd
// [in] ICategorizerItemRawAttributes * pIRawAttributes
// [in] DWORD dwRecipIndex
// [in] CAttrEntry * pCurrent
//
// Returns:
// S_OK if the function succeeded
//
//-------------------------------------------------------------
HRESULT Cread::FindAndProcessAttributeFromCat(eSourceType SourceType,
IMailMsgProperties *pIMailMsgProperties,
IMailMsgRecipientsAdd *pIMailMsgRecipientsAdd,
ICategorizerItemRawAttributes * pIRawAttributes,
DWORD dwRecipIndex,
CAttrEntry * pCurrent)
{
HRESULT hr = S_OK;
ATTRIBUTE_ENUMERATOR enumerator;
DWORD dwAttrValueCount;
DWORD dwcbAttributeValue;
PVOID pvAttributeValue = NULL;
DWORD dwPropIdOffset;
DWORD dwCount;
BOOL fEnumerator = FALSE;
PCFunctEnterEx((LPARAM)this, "CPhatCat::ProcessAttributesFromCat");
// Start by getting the enumerator for the requested attribute.
hr = pIRawAttributes->BeginRawAttributeEnumeration(
m_attrList.GetAttrName(pCurrent),
&enumerator);
if(FAILED(hr)) {
// The Categorizer did not find the attribute on this recipient/sender. This
// is NOT a failure.
PCDebugTraceEx((LPARAM)this,
"Categorizer did not Find attribute %s. BeginRawAttributeEnumeration returned 0x%08lx",
m_attrList.GetAttrName(pCurrent),
hr);
// Set AttrValueCount to 0 and look up the client's other attributes.
dwAttrValueCount = 0;
hr = S_OK;
}
else {
// The requested attribute was found. Determine how many values it has.
fEnumerator = TRUE;
hr = pIRawAttributes->CountRawAttributeValues(
&enumerator,
&dwAttrValueCount);
if(FAILED(hr)) {
PCErrorTraceEx((LPARAM)this,
"Failed pIRawAttributes->CountAttributeValues %08lx",
hr);
goto CLEANUP;
}
}
PCDebugTraceEx((LPARAM)this,
"CatReader Found %ld for attribute %s",
dwAttrValueCount,
m_attrList.GetAttrName(pCurrent));
// The requested attribute(s) were found on this recipent/sender.
// Now request a set of mailmsg property IDs. This function handles multivalued
// attributes, and cases where the attribute was not found.
hr = AllocAndSetPropIdInfo(
SourceType,
pIMailMsgProperties,
pIMailMsgRecipientsAdd,
dwRecipIndex,
pCurrent,
dwAttrValueCount,
&dwPropIdOffset);
if(FAILED(hr)) {
PCErrorTraceEx((LPARAM)this,
"Failed AllocAndSetPropIdInfo %08lx",
hr);
goto CLEANUP;
}
PCDebugTraceEx((LPARAM)this,
"Found Attribute %s - Allocated %ld propids from offset starting %ld",
m_attrList.GetAttrName(pCurrent),
dwAttrValueCount,
dwPropIdOffset);
ShowMessage("CatReader %s Attribute %s - Allocated %ld propids from offset starting %ld",
dwAttrValueCount? "Found" : "Did Not Find",
m_attrList.GetAttrName(pCurrent),
dwAttrValueCount,
dwPropIdOffset);
// Loop in to save all of the attribute values.
for(dwCount = 0; dwCount < dwAttrValueCount; dwCount++) {
// Get the next value pointed by the enumerator. Because the exact count
// of values is known, this method cannot fail.
hr = pIRawAttributes->GetNextRawAttributeValue(
&enumerator,
&dwcbAttributeValue,
&pvAttributeValue);
if(FAILED(hr)) {
PCErrorTraceEx((LPARAM)this,
"Failed GetNextRawAttributeValue hr %08lx", hr);
goto CLEANUP;
}
// Save the attribute value on the allocated mailmsg property ID.
hr = SaveAttributeValueOnPropIdInfo(
SourceType,
pIMailMsgProperties,
pIMailMsgRecipientsAdd,
dwRecipIndex,
dwPropIdOffset+dwCount,
dwcbAttributeValue,
pvAttributeValue);
if(FAILED(hr)) {
PCErrorTraceEx((LPARAM)this, "Failed SaveAttributeValuesOnPropIdInfo hr %08lx", hr);
goto CLEANUP;
}
PCDebugTraceEx((LPARAM)this,
"Attribute %s - Saved Value#%ld at Propid %ld",
m_attrList.GetAttrName(pCurrent),
dwCount+1,
dwPropIdOffset+dwCount);
ShowMessage("CatReader Attribute %s - Saved Value#%ld at PropId %ld",
m_attrList.GetAttrName(pCurrent),
dwCount+1,
dwPropIdOffset+dwCount);
}
CLEANUP:
if (fEnumerator) {
// After starting the enumeration, call EndRawAttributeEnumeration to
// free memory.
hr = pIRawAttributes->EndRawAttributeEnumeration(&enumerator);
if(FAILED(hr))
PCErrorTraceEx((LPARAM)this,
"Failed EndRawAttributeEnumeration hr %08lx", hr);
}
PCFunctLeaveEx((LPARAM)this);
return hr;
}
//-------------------------------------------------------------
//
// Function: Cread::AllocAndSetPropIdInfo
//
// Synopsis: This function starts by allocating and requesting a set of only two mailmsg
// property IDs. The first is always used to persist the number of values.
// If the requested attribute is single-valued, this function will return
// the second property ID as the placeholder to save the value. If the
// attribute is multivalued, this function creates a new GUID and, based
// on the new GUID, it requests a second set of mailmsg property IDs.
// The function then returns this set as a placeholder to save the multiple
// values. The client postcategorizer implementation must walk through the
// same steps to retrieve the values. (See the catreadex example,
// postcatreader.cpp.)
//
// Arguments:
// [in]
// eSourceType SourceType Source type indicates whether the ICatItem
// processed is a sender or a recipient //
// IMailMsgProperties *pIMailMsgProperties
// IMailMsgRecipientsAdd *pIMailMsgRecipientsAdd
// DWORD dwRecipIndex
// CAttrEntry * pCurrent
// DWORD dwAttrValueCount Number of attribute Values
//
// [out]
// DWORD *pdwPropIdOffset Starting offset of available property ID
//
// Returns:
// S_OK if the function succeeded
//
//-------------------------------------------------------------
HRESULT Cread::AllocAndSetPropIdInfo(
eSourceType SourceType,
IMailMsgProperties *pIMailMsgProperties,
IMailMsgRecipientsAdd *pIMailMsgRecipientsAdd,
DWORD dwRecipIndex,
CAttrEntry * pCurrent,
DWORD dwAttrValueCount,
DWORD *pdwPropIdOffset)
{
IMailMsgPropertyManagement *pIMailMsgPropertyManagement = NULL;
DWORD dwStart;
HRESULT hr = S_OK;
PCFunctEnterEx((LPARAM)this, "CPhatCat::AllocAndGetPropIdInfo");
*pdwPropIdOffset = 0;
hr = pIMailMsgProperties->QueryInterface(
IID_IMailMsgPropertyManagement,
(PVOID *)&pIMailMsgPropertyManagement);
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this, "Failed pIMailMsgProperties->QueryInterface 0x%08lx",
hr);
goto CLEANUP;
}
// Start by requesting two mailmsg property IDs.
hr = pIMailMsgPropertyManagement->AllocPropIDRange(
m_attrList.GetAttrGuid(pCurrent),
2,
&dwStart);
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this, "Failed pIMailMsgProperties->QueryInterface 0x%08lx",
hr);
goto CLEANUP;
}
// Save the attribute value count in the first allocated property ID.
if(SourceType == SOURCE_RECIPIENT)
hr = pIMailMsgRecipientsAdd->PutDWORD(
dwRecipIndex,
dwStart,
dwAttrValueCount);
else
hr = pIMailMsgProperties->PutDWORD(
dwStart,
dwAttrValueCount);
if (FAILED(hr)) {
PCErrorTraceEx((LPARAM)this,"Failed %s->PutDWORD hr %08lx",
SourceType == SOURCE_RECIPIENT ? "pIMailMsgRecipientsAdd" : "pIMailMsgProperties",
hr);
goto CLEANUP;
}
// Check if the attribute is single-valued or multivalued.
if (dwAttrValueCount > 1) {
// The attribute is multivalued. Create a unique GUID.
GUID gNewGuid;
hr = CoCreateGuid(
&gNewGuid);
if (FAILED(hr))
{
PCErrorTraceEx((LPARAM)this,"Failed CoCreateGuid hr %08lx", hr);
goto CLEANUP;
}
// Use the next allocated property ID to store your unique GUID.
if(SourceType == SOURCE_RECIPIENT)
hr = pIMailMsgRecipientsAdd->PutProperty(
dwRecipIndex,
dwStart+1,
sizeof(GUID),
(BYTE *) &gNewGuid);
else
hr = pIMailMsgProperties->PutProperty(
dwStart+1,
sizeof(GUID),
(BYTE *) &gNewGuid);
if (FAILED(hr))
{
PCErrorTraceEx((LPARAM)this,"Failed %s PutProperty hr %08lx",
SourceType == SOURCE_RECIPIENT ? "pIMailMsgRecipientsAdd" : "pIMailMsgProperties",
hr);
goto CLEANUP;
}
// Using the new GUID, request the second set of property IDs.
// The number you request equals the number of attribute values.
hr = pIMailMsgPropertyManagement->AllocPropIDRange(
gNewGuid,
dwAttrValueCount,
pdwPropIdOffset);
if (FAILED(hr)) {
PCErrorTraceEx( (LPARAM) this, "Failed pIMailMsgProperties->QueryInterface 0x%08lx",
hr);
goto CLEANUP;
}
}
else
// The attribute is single-valued, therefore return the next
// available property ID in order to store the value itself.
*pdwPropIdOffset = dwStart+1;
CLEANUP:
if (pIMailMsgPropertyManagement)
pIMailMsgPropertyManagement->Release();
PCDebugTraceEx((LPARAM)this, "result %08lx", hr);
PCFunctLeaveEx((LPARAM)this);
return hr;
}
//-------------------------------------------------------------
//
// Function: Cread::SaveAttributeValueOnPropIdInfo
//
// Synopsis: Saves the attribute value onto the mailmsg property ID.
// For simplicity in this example, all property values are
// stored as binary properties. Types like strings are not
// NULL terminated.
//
// Arguments:
// [in]
// eSourceType SourceType,
// IMailMsgProperties *pIMailMsgProperties,
// IMailMsgRecipientsAdd * pIMailMsgRecipientsAdd,
// DWORD dwRecipIndex,
// DWORD dwPropIdOffset,
// DWORD dwcbAttributeValue, - Size of Value
// PVOID pvAttributeValue) - Pointer to Value
//
//
// Returns:
// S_OK if the function succeeded
//
//-------------------------------------------------------------
HRESULT Cread::SaveAttributeValueOnPropIdInfo(
eSourceType SourceType,
IMailMsgProperties *pIMailMsgProperties,
IMailMsgRecipientsAdd * pIMailMsgRecipientsAdd,
DWORD dwRecipIndex,
DWORD dwPropIdOffset,
DWORD dwcbAttributeValue,
PVOID pvAttributeValue)
{
HRESULT hr = S_OK;
PCFunctEnterEx((LPARAM)this, "CPhatCat::AllocAndGetPropIdInfo");
if(SourceType == SOURCE_RECIPIENT)
hr = pIMailMsgRecipientsAdd->PutProperty(
dwRecipIndex,
dwPropIdOffset,
dwcbAttributeValue,
(BYTE *) pvAttributeValue);
else
hr = pIMailMsgProperties->PutProperty(
dwPropIdOffset,
dwcbAttributeValue,
(BYTE *) pvAttributeValue);
if (FAILED(hr))
{
PCErrorTraceEx((LPARAM)this,"Failed %s->PutProperty hr %08lx",
SourceType == SOURCE_RECIPIENT ? "pIMailMsgRecipientsAdd" : "pIMailMsgProperties",
hr);
goto CLEANUP;
}
CLEANUP:
PCDebugTraceEx((LPARAM)this, "result %08lx", hr);
PCFunctLeaveEx((LPARAM)this);
return hr;
}