/////////////////////////////////////////////////////////////////////////////// // // Actions.cpp // /////////////////////////////////////////////////////////////////////////////// #include #include "actions.h" #include "storutil.h" #include "ruleutil.h" static const int ACT_GROW = 16; /////////////////////////////////////////////////////////////////////////////// // // HrCreateActions // // This creates an actions container. // // ppIActions - pointer to return the criteria container // // Returns: S_OK, on success // E_OUTOFMEMORY, if can't create the IOEActions object // /////////////////////////////////////////////////////////////////////////////// HRESULT HrCreateActions(IOEActions ** ppIActions) { COEActions * pActions = NULL; HRESULT hr = S_OK; // Check the incoming params if (NULL == ppIActions) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params *ppIActions = NULL; // Create the rules manager object pActions = new COEActions; if (NULL == pActions) { hr = E_OUTOFMEMORY; goto exit; } // Get the rules manager interface hr = pActions->QueryInterface(IID_IOEActions, (void **) ppIActions); if (FAILED(hr)) { goto exit; } pActions = NULL; // Set the proper return value hr = S_OK; exit: if (NULL != pActions) { delete pActions; } return hr; } COEActions::~COEActions() { AssertSz(m_cRef == 0, "Somebody still has a hold of us!!"); Reset(); } STDMETHODIMP_(ULONG) COEActions::AddRef() { return ::InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) COEActions::Release() { LONG cRef = 0; cRef = ::InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return cRef; } return cRef; } STDMETHODIMP COEActions::QueryInterface(REFIID riid, void ** ppvObject) { HRESULT hr = S_OK; // Check the incoming params if (NULL == ppvObject) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing param *ppvObject = NULL; if ((riid == IID_IUnknown) || (riid == IID_IOEActions)) { *ppvObject = static_cast(this); } else if ((riid == IID_IPersistStream) || (riid == IID_IPersist)) { *ppvObject = static_cast(this); } else { hr = E_NOINTERFACE; goto exit; } reinterpret_cast(*ppvObject)->AddRef(); hr = S_OK; exit: return hr; } STDMETHODIMP COEActions::Reset(void) { HRESULT hr = S_OK; // See if there is something to do if (0 == m_cItems) { Assert(NULL == m_rgItems); hr = S_OK; goto exit; } RuleUtil_HrFreeActionsItem(m_rgItems, m_cItems); SafeMemFree(m_rgItems); m_cItems = 0; m_cItemsAlloc = 0; exit: return hr; } STDMETHODIMP COEActions::GetState(DWORD * pdwState) { HRESULT hr = S_OK; DWORD dwState = ACT_STATE_NULL; ULONG ulIndex = 0; // Check incoming params if (NULL == pdwState) { hr = E_INVALIDARG; goto exit; } // Init the outgoing param *pdwState = ACT_STATE_NULL; // See if there is something to do if (0 == m_cItems) { Assert(NULL == m_rgItems); hr = S_OK; goto exit; } // Walk through the actions to figure out the state for (ulIndex = 0; ulIndex < m_cItems; ulIndex++) { if ((ACT_STATE_NULL == dwState) && ((ACT_TYPE_DELETESERVER == m_rgItems[ulIndex].type) || (ACT_TYPE_DONTDOWNLOAD == m_rgItems[ulIndex].type))) { dwState = ACT_STATE_SERVER; } else { dwState = ACT_STATE_LOCAL; } } // Set the outgoing param *pdwState = dwState; // Set the proper return value hr = S_OK; exit: return hr; } STDMETHODIMP COEActions::GetActions(DWORD dwFlags, PACT_ITEM * ppItem, ULONG * pcItem) { HRESULT hr = S_OK; ACT_ITEM * pItemNew = NULL; // Check incoming params if ((NULL == ppItem) || (0 != dwFlags)) { hr = E_INVALIDARG; goto exit; } // Initialize the out params *ppItem = NULL; if (NULL != pcItem) { *pcItem = 0; } // If we don't have any criteria, then return if (0 == m_cItems) { hr = E_FAIL; goto exit; } // Allocate space for the criteria hr = RuleUtil_HrDupActionsItem(m_rgItems, m_cItems, &pItemNew); if (FAILED(hr)) { goto exit; } // Save the criteria *ppItem = pItemNew; pItemNew = NULL; if (NULL != pcItem) { *pcItem = m_cItems; } exit: RuleUtil_HrFreeActionsItem(pItemNew, m_cItems); SafeMemFree(pItemNew); return hr; } STDMETHODIMP COEActions::SetActions(DWORD dwFlags, ACT_ITEM * pItem, ULONG cItem) { HRESULT hr = S_OK; ACT_ITEM * pItemNew = NULL; // Check incoming params if ((NULL == pItem) || (0 == cItem) || (0 != dwFlags)) { hr = E_INVALIDARG; goto exit; } // If we have any criteria already, then reset if (0 != m_cItems) { Reset(); } // Allocate space for the criteria hr = RuleUtil_HrDupActionsItem(pItem, cItem, &pItemNew); if (FAILED(hr)) { goto exit; } // Save the criteria m_rgItems = pItemNew; pItemNew = NULL; m_cItems = cItem; m_cItemsAlloc = cItem; exit: RuleUtil_HrFreeActionsItem(pItemNew, cItem); SafeMemFree(pItemNew); return hr; } /////////////////////////////////////////////////////////////////////////////// // // ValidateActions // // This verifies each of the action values // // Returns: S_OK, if the actions were valid // S_FALSE, otherwise // /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP COEActions::Validate(DWORD dwFlags) { HRESULT hr = S_OK; ULONG ulIndex = 0; LPSTR pszText = NULL; FOLDERINFO Folder={0}; RULEFOLDERDATA * prfdData = NULL; // If we don't have any actions, then we must be valid if (0 == m_cItems) { hr = S_OK; goto exit; } for (ulIndex = 0; ulIndex < m_cItems; ulIndex++) { switch(m_rgItems[ulIndex].type) { // These ones are always valid case ACT_TYPE_DELETESERVER: case ACT_TYPE_DONTDOWNLOAD: case ACT_TYPE_FLAG: case ACT_TYPE_READ: case ACT_TYPE_MARKDOWNLOAD: case ACT_TYPE_DELETE: case ACT_TYPE_JUNKMAIL: case ACT_TYPE_STOP: if (VT_EMPTY != m_rgItems[ulIndex].propvar.vt) { hr = S_FALSE; goto exit; } break; case ACT_TYPE_HIGHLIGHT: if (VT_UI4 != m_rgItems[ulIndex].propvar.vt) { hr = S_FALSE; goto exit; } break; case ACT_TYPE_WATCH: case ACT_TYPE_SHOW: if (VT_UI4 != m_rgItems[ulIndex].propvar.vt) { hr = S_FALSE; goto exit; } if (ACT_DATA_NULL == m_rgItems[ulIndex].propvar.ulVal) { hr = S_FALSE; goto exit; } break; case ACT_TYPE_COPY: case ACT_TYPE_MOVE: if ((VT_BLOB != m_rgItems[ulIndex].propvar.vt) || (0 == m_rgItems[ulIndex].propvar.blob.cbSize)) { hr = S_FALSE; goto exit; } // Make life simpler prfdData = (RULEFOLDERDATA *) (m_rgItems[ulIndex].propvar.blob.pBlobData); // Validate the rule folder data if (S_OK != RuleUtil_HrValidateRuleFolderData(prfdData)) { hr = S_FALSE; goto exit; } hr = g_pStore->GetFolderInfo(prfdData->idFolder, &Folder); if (FAILED(hr)) { hr = S_FALSE; goto exit; } else g_pStore->FreeRecord(&Folder); break; case ACT_TYPE_REPLY: case ACT_TYPE_NOTIFYSND: if ((VT_LPSTR != m_rgItems[ulIndex].propvar.vt) || (NULL == m_rgItems[ulIndex].propvar.pszVal)) { hr = S_FALSE; goto exit; } Assert(lstrlen(m_rgItems[ulIndex].propvar.pszVal) <= MAX_PATH) if (0xFFFFFFFF == GetFileAttributes(m_rgItems[ulIndex].propvar.pszVal)) { hr = S_FALSE; goto exit; } break; case ACT_TYPE_FWD: { LPWSTR pwszVal = NULL, pwszText = NULL; if ((VT_LPSTR != m_rgItems[ulIndex].propvar.vt) || (NULL == m_rgItems[ulIndex].propvar.pszVal)) { AssertSz(VT_LPWSTR != m_rgItems[ulIndex].propvar.vt, "We are getting UNICODE here."); hr = S_FALSE; goto exit; } // Update the display string pwszVal = PszToUnicode(CP_ACP, m_rgItems[ulIndex].propvar.pszVal); if (!pwszVal) { hr = S_FALSE; goto exit; } hr = RuleUtil_HrParseEmailString(pwszVal, 0, &pwszText, NULL); MemFree(pwszText); MemFree(pwszVal); if (FAILED(hr)) { hr = S_FALSE; goto exit; } // If either always encrypt or always sign is turned on // we can't do anything if ((0 != DwGetOption(OPT_MAIL_DIGSIGNMESSAGES)) || (0 != DwGetOption(OPT_MAIL_ENCRYPTMESSAGES))) { hr = S_FALSE; goto exit; } break; } default: hr = S_FALSE; goto exit; break; } } // If we got here, the we must be AOK hr = S_OK; exit: SafeMemFree(pszText); return hr; } STDMETHODIMP COEActions::AppendActions(DWORD dwFlags, ACT_ITEM * pItem, ULONG cItem, ULONG * pcItemAppended) { HRESULT hr = S_OK; ACT_ITEM * pItemNew = NULL; // Check incoming parameters if ((0 != dwFlags) || (NULL == pItem) || (0 == cItem)) { hr = E_INVALIDARG; goto exit; } // Let's init our outgoing parameters if (NULL != pcItemAppended) { *pcItemAppended = 0; } // Do we have to add more items? if (m_cItems == m_cItemsAlloc) { hr = HrRealloc((LPVOID *) &m_rgItems, sizeof(ACT_ITEM) * (m_cItemsAlloc + ACT_GROW)); if (FAILED(hr)) { goto exit; } ZeroMemory(m_rgItems + m_cItemsAlloc, sizeof(ACT_ITEM) * ACT_GROW); m_cItemsAlloc += ACT_GROW; } // Let's duplicate the items that need to be added hr = RuleUtil_HrDupActionsItem(pItem, cItem, &pItemNew); if (FAILED(hr)) { goto exit; } // Let's add them to the criteria array CopyMemory(m_rgItems + m_cItems, pItemNew, sizeof(ACT_ITEM) * cItem); m_cItems += cItem; // Set the proper outgoing parameter if (NULL != pcItemAppended) { *pcItemAppended = cItem; } // Set the proper return value hr = S_OK; exit: SafeMemFree(pItemNew); return hr; } /////////////////////////////////////////////////////////////////////////////// // // LoadReg // // This loads in the actions from the registry. It loads in the actions // order from the Order value. The string contains space delimitied values // and each value contains the subkey name for each action. Each action // is loaded in the order that is contained in the Order value. The actions // are loaded with the Actions Type. The Actions Value Type is loaded if it exists. // If an Action Value Type exists, then the corresponding Action Value is loaded in. // // pszRegPath - the path to load the actions from // // Returns: S_OK, if the actions were loaded without problems // E_OUTOFMEMORY, if we couldn't allocate memory to hold the actions // E_FAIL, otherwise // /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP COEActions::LoadReg(LPCSTR pszRegPath) { HRESULT hr = S_OK; LONG lErr = 0; HKEY hkeyRoot = NULL; ULONG cbData = 0; LPSTR pszOrder = NULL; ULONG cOrder = 0; LPSTR pszWalk = NULL; ACT_ITEM * pItems = NULL; LPSTR pszNext = NULL; ULONG ulOrder = 0; HKEY hkeyAction = NULL; ACT_TYPE typeAct; PROPVARIANT propvar = {0}; DWORD dwType = 0; BYTE * pbData = NULL; DWORD dwFlags = ACT_FLAG_DEFAULT; // Check incoming param if (NULL == pszRegPath) { hr = E_INVALIDARG; goto exit; } // Should we fail if we're already loaded? AssertSz(0 == (m_dwState & ACT_STATE_LOADED), "We're already loaded!!!"); // Open the reg key from the path lErr = AthUserOpenKey(pszRegPath, KEY_ALL_ACCESS, &hkeyRoot); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Get the actions order hr = RuleUtil_HrGetRegValue(hkeyRoot, c_szActionsOrder, NULL, (BYTE **) &pszOrder, &cbData); if (FAILED(hr)) { goto exit; } // Make sure we actually have something to load if ('\0' == *pszOrder) { AssertSz(FALSE, "The order string for the actions is mis-formatted in the registry"); hr = E_FAIL; goto exit; } // Convert the actions string to a more useful format pszWalk = pszOrder; cOrder = 1; for (pszWalk = StrStr(pszOrder, g_szSpace); NULL != pszWalk; pszWalk = StrStr(pszWalk, g_szSpace)) { // Terminate the order item *pszWalk = '\0'; pszWalk++; cOrder++; } // Allocate the space to hold all the actions cbData = cOrder * sizeof(ACT_ITEM); hr = HrAlloc((void **) &pItems, cbData); if (FAILED(hr)) { goto exit; } // Initialize it to a known value ZeroMemory(pItems, cbData); // For each action in the order string pszWalk = pszOrder; for (ulOrder = 0, pszWalk = pszOrder; ulOrder < cOrder; ulOrder++, pszWalk += lstrlen(pszWalk) + 1) { // Open up the action reg key lErr = RegOpenKeyEx(hkeyRoot, pszWalk, 0, KEY_READ, &hkeyAction); if (ERROR_SUCCESS != lErr) { AssertSz(FALSE, "Part of the criteria is mis-formatted in the registry"); hr = E_FAIL; goto exit; } // Get the action type cbData = sizeof(typeAct); lErr = RegQueryValueEx(hkeyAction, c_szActionsType, 0, NULL, (BYTE *) &(typeAct), &cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Get the action flags cbData = sizeof(dwFlags); lErr = RegQueryValueEx(hkeyAction, c_szActionsFlags, 0, NULL, (BYTE *) &(dwFlags), &cbData); if ((ERROR_SUCCESS != lErr) && (ERROR_FILE_NOT_FOUND != lErr)) { hr = E_FAIL; goto exit; } // If it didn't exist then assign it to the default if (ERROR_FILE_NOT_FOUND == lErr) { dwFlags = ACT_FLAG_DEFAULT; } // Does a action value type exist lErr = RegQueryValueEx(hkeyAction, c_szActionsValueType, 0, NULL, NULL, &cbData); if ((ERROR_SUCCESS == lErr) && (0 != cbData)) { // Load the action value in cbData = sizeof(dwType); lErr = RegQueryValueEx(hkeyAction, c_szActionsValueType, 0, NULL, (BYTE *) &dwType, &cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } propvar.vt = (VARTYPE) dwType; switch (propvar.vt) { case VT_UI4: // Get the action value cbData = sizeof(propvar.ulVal); lErr = RegQueryValueEx(hkeyAction, c_szActionsValue, 0, NULL, (BYTE * ) &(propvar.ulVal), &cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } break; case VT_LPSTR: case VT_BLOB: // Get the criteria value hr = RuleUtil_HrGetRegValue(hkeyAction, c_szActionsValue, NULL, (BYTE **) &pbData, &cbData); if (FAILED(hr)) { goto exit; } // Save the space so we can free it if (VT_LPSTR == propvar.vt) { propvar.pszVal = (LPSTR) pbData; } else { propvar.blob.cbSize = cbData; propvar.blob.pBlobData = pbData; } pbData = NULL; break; default: AssertSz(FALSE, "Why are we loading in an invalid action type?"); hr = E_FAIL; goto exit; break; } } // Save the value into the criteria array pItems[ulOrder].type = typeAct; pItems[ulOrder].dwFlags = dwFlags; pItems[ulOrder].propvar = propvar; ZeroMemory(&propvar, sizeof(propvar)); // Close the action SideAssert(ERROR_SUCCESS == RegCloseKey(hkeyAction)); hkeyAction = NULL; } // Free up the current actions SafeMemFree(m_rgItems); // Save the new values m_rgItems = pItems; pItems = NULL; m_cItems = cOrder; // Make sure we clear the dirty bit m_dwState &= ~ACT_STATE_DIRTY; // Note that we have been loaded m_dwState |= ACT_STATE_LOADED; // Set the return value hr = S_OK; exit: SafeMemFree(pbData); PropVariantClear(&propvar); RuleUtil_HrFreeActionsItem(pItems, cOrder); SafeMemFree(pItems); SafeMemFree(pszOrder); if (NULL != hkeyAction) { RegCloseKey(hkeyAction); } if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } STDMETHODIMP COEActions::SaveReg(LPCSTR pszRegPath, BOOL fClearDirty) { HRESULT hr = S_OK; LONG lErr = 0; HKEY hkeyRoot = NULL; DWORD dwDisp = 0; LPSTR pszOrder = NULL; ULONG ulIndex = 0; ACT_ITEM * pItem = NULL; CHAR rgchTag[CCH_ACT_ORDER]; HKEY hkeyAction = NULL; ULONG cbData = 0; BYTE * pbData = NULL; // Check incoming param if (NULL == pszRegPath) { hr = E_INVALIDARG; goto exit; } // If there's nothing to save, then fail if (NULL == m_rgItems) { hr = E_FAIL; goto exit; } // Let's make sure we clear out the key first AthUserDeleteKey(pszRegPath); // Create the reg key from the path lErr = AthUserCreateKey(pszRegPath, KEY_ALL_ACCESS, &hkeyRoot, &dwDisp); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } Assert(REG_CREATED_NEW_KEY == dwDisp); Assert(m_cItems < ACT_COUNT_MAX); // Allocate space to hold the order DWORD cchSize = (m_cItems * CCH_ACT_ORDER); hr = HrAlloc((void **) &pszOrder, cchSize * sizeof(*pszOrder)); if (FAILED(hr)) { goto exit; } pszOrder[0] = '\0'; // Write out each of the actions for (ulIndex = 0, pItem = m_rgItems; ulIndex < m_cItems; ulIndex++, pItem++) { // Get the new action tag wnsprintf(rgchTag, ARRAYSIZE(rgchTag), "%03X", ulIndex); // Add the new tag to the order if (0 != ulIndex) { StrCatBuff(pszOrder, g_szSpace, cchSize); } StrCatBuff(pszOrder, rgchTag, cchSize); // Create the new action lErr = RegCreateKeyEx(hkeyRoot, rgchTag, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyAction, &dwDisp); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } Assert(REG_CREATED_NEW_KEY == dwDisp); // Write out the action type lErr = RegSetValueEx(hkeyAction, c_szActionsType, 0, REG_DWORD, (BYTE *) &(pItem->type), sizeof(pItem->type)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Write out the action type lErr = RegSetValueEx(hkeyAction, c_szActionsFlags, 0, REG_DWORD, (BYTE *) &(pItem->dwFlags), sizeof(pItem->dwFlags)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Do we have an action value? if (VT_EMPTY != pItem->propvar.vt) { // Write out the criteria value type dwDisp = pItem->propvar.vt; lErr = RegSetValueEx(hkeyAction, c_szActionsValueType, 0, REG_DWORD, (BYTE *) &dwDisp, sizeof(dwDisp)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Write out the action value switch (pItem->propvar.vt) { case VT_UI4: dwDisp = REG_DWORD; pbData = (BYTE * ) &(pItem->propvar.ulVal); cbData = sizeof(pItem->propvar.ulVal); break; case VT_LPSTR: dwDisp = REG_SZ; pbData = (BYTE * ) (pItem->propvar.pszVal); cbData = lstrlen(pItem->propvar.pszVal) + 1; break; case VT_BLOB: dwDisp = REG_BINARY; pbData = pItem->propvar.blob.pBlobData; cbData = pItem->propvar.blob.cbSize; break; default: AssertSz(FALSE, "Why are we trying to save in an invalid action type?"); hr = E_FAIL; goto exit; break; } // Write out the action value lErr = RegSetValueEx(hkeyAction, c_szActionsValue, 0, dwDisp, pbData, cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } } // Close the action SideAssert(ERROR_SUCCESS == RegCloseKey(hkeyAction)); hkeyAction = NULL; } // Write out the order string. lErr = RegSetValueEx(hkeyRoot, c_szActionsOrder, 0, REG_SZ, (BYTE *) pszOrder, lstrlen(pszOrder) + 1); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Should we clear the dirty bit? if (FALSE != fClearDirty) { m_dwState &= ~ACT_STATE_DIRTY; } // Set the return value hr = S_OK; exit: if (NULL != hkeyAction) { RegCloseKey(hkeyAction); } SafeMemFree(pszOrder); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } STDMETHODIMP COEActions::Clone(IOEActions ** ppIActions) { HRESULT hr = S_OK; COEActions * pActions = NULL; // Check incoming params if (NULL == ppIActions) { hr = E_INVALIDARG; goto exit; } // Initialize the outgoing params *ppIActions = NULL; // Create a new actions pActions = new COEActions; if (NULL == pActions) { hr = E_OUTOFMEMORY; goto exit; } // Copy over the list of actions hr = pActions->SetActions(0, m_rgItems, m_cItems); if (FAILED(hr)) { goto exit; } // Get the actions interface hr = pActions->QueryInterface(IID_IOEActions, (void **) ppIActions); if (FAILED(hr)) { goto exit; } pActions = NULL; // Set the proper return value hr = S_OK; exit: if (NULL != pActions) { delete pActions; } return hr; } STDMETHODIMP COEActions::GetClassID(CLSID * pclsid) { HRESULT hr = S_OK; if (NULL == pclsid) { hr = E_INVALIDARG; goto exit; } *pclsid = CLSID_OEActions; // Set the proper return value hr = S_OK; exit: return hr; } STDMETHODIMP COEActions::IsDirty(void) { HRESULT hr = S_OK; hr = (ACT_STATE_DIRTY == (m_dwState & ACT_STATE_DIRTY)) ? S_OK : S_FALSE; return hr; } STDMETHODIMP COEActions::Load(IStream * pStm) { HRESULT hr = S_OK; ULONG cbData = 0; ULONG cbRead = 0; DWORD dwData = 0; ULONG cItems = 0; ACT_ITEM * pItems = NULL; ULONG ulIndex = 0; ACT_ITEM * pItem = NULL; ACT_TYPE typeAct; PROPVARIANT propvar = {0}; BYTE * pbData = NULL; DWORD dwFlags = ACT_FLAG_DEFAULT; // Check incoming param if (NULL == pStm) { hr = E_INVALIDARG; goto exit; } // Verify we have the correct version hr = pStm->Read(&dwData, sizeof(dwData), &cbRead); if (FAILED(hr)) { goto exit; } if ((cbRead != sizeof(dwData)) || (dwData != ACT_VERSION)) { hr = E_FAIL; goto exit; } // Get the number of actions hr = pStm->Read(&cItems, sizeof(cItems), &cbRead); if (FAILED(hr)) { goto exit; } if ((cbRead != sizeof(cItems)) || (0 == cItems)) { hr = E_FAIL; goto exit; } // Allocate space to hold all the actions hr = HrAlloc( (void **) &pItems, cItems * sizeof(*pItems)); if (FAILED(hr)) { goto exit; } // Initialize the actions to a known value ZeroMemory(pItems, cItems * sizeof(*pItems)); // for each action for (ulIndex = 0, pItem = pItems; ulIndex < cItems; ulIndex++, pItem++) { // Read in the action type hr = pStm->Read(&typeAct, sizeof(typeAct), &cbRead); if (FAILED(hr)) { goto exit; } if (cbRead != sizeof(typeAct)) { hr = E_FAIL; goto exit; } // Read in the action flags hr = pStm->Read(&dwFlags, sizeof(dwFlags), &cbRead); if ((FAILED(hr)) || (cbRead != sizeof(dwFlags))) { goto exit; } // Read in the action value type hr = pStm->Read(&dwData, sizeof(dwData), &cbRead); if ((FAILED(hr)) || (cbRead != sizeof(dwData))) { goto exit; } propvar.vt = (VARTYPE) dwData; if (VT_EMPTY != propvar.vt) { // Get the size of the action value hr = pStm->Read(&cbData, sizeof(cbData), &cbRead); if (FAILED(hr)) { goto exit; } if (cbRead != sizeof(cbData)) { hr = E_FAIL; goto exit; } // Allocate space to hold the action value data switch (propvar.vt) { case VT_UI4: pbData = (BYTE * ) &(propvar.ulVal); break; case VT_BLOB: case VT_LPSTR: hr = HrAlloc((void **) &pbData, cbData); if (FAILED(hr)) { goto exit; } if (VT_LPSTR == propvar.vt) { propvar.pszVal = (LPSTR) pbData; } else { propvar.blob.cbSize = cbData; propvar.blob.pBlobData = pbData; } break; default: AssertSz(FALSE, "Why are we trying to save in a invalid action type?"); hr = E_FAIL; goto exit; break; } // Read in the action value hr = pStm->Read(pbData, cbData, &cbRead); if (FAILED(hr)) { goto exit; } if (cbRead != cbData) { hr = E_FAIL; goto exit; } } pItem->type = typeAct; pItem->dwFlags = dwFlags; pItem->propvar = propvar; ZeroMemory(&propvar, sizeof(propvar)); } // Free up the current actions SafeMemFree(m_rgItems); // Save the new values m_rgItems = pItems; pItems = NULL; m_cItems = cItems; // Make sure we clear the dirty bit m_dwState &= ~ACT_STATE_DIRTY; // Note that we have been loaded m_dwState |= ACT_STATE_LOADED; // Set the return value hr = S_OK; exit: PropVariantClear(&propvar); RuleUtil_HrFreeActionsItem(pItems, cItems); SafeMemFree(pItems); return hr; } STDMETHODIMP COEActions::Save(IStream * pStm, BOOL fClearDirty) { HRESULT hr = S_OK; ULONG cbData = 0; ULONG cbWritten = 0; DWORD dwData = 0; ULONG ulIndex = 0; ACT_ITEM * pItem = NULL; BYTE * pbData = NULL; // Check incoming param if (NULL == pStm) { hr = E_INVALIDARG; goto exit; } // Write out the version dwData = ACT_VERSION; hr = pStm->Write(&dwData, sizeof(dwData), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(dwData)); // Write out the count of actions hr = pStm->Write(&m_cItems, sizeof(m_cItems), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(m_cItems)); // Loop through each of the actions for (ulIndex = 0, pItem = m_rgItems; ulIndex < m_cItems; ulIndex++, pItem++) { // Write out the action type hr = pStm->Write(&(pItem->type), sizeof(pItem->type), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(pItem->type)); // Write out the actions flags hr = pStm->Write(&(pItem->dwFlags), sizeof(pItem->dwFlags), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(pItem->dwFlags)); // Write out the value type dwData = pItem->propvar.vt; hr = pStm->Write(&(dwData), sizeof(dwData), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(dwData)); if (VT_EMPTY == pItem->propvar.vt) { continue; } // Figure out the size of the action value switch (pItem->propvar.vt) { case VT_UI4: pbData = (BYTE * ) &(pItem->propvar.ulVal); cbData = sizeof(pItem->propvar.ulVal); break; case VT_LPSTR: pbData = (BYTE * ) (pItem->propvar.pszVal); cbData = lstrlen(pItem->propvar.pszVal) + 1; break; case VT_BLOB: pbData = pItem->propvar.blob.pBlobData; cbData = pItem->propvar.blob.cbSize; break; default: AssertSz(FALSE, "Why are we trying to save in a invalid action type?"); hr = E_FAIL; goto exit; break; } // Write out the action value size hr = pStm->Write(&cbData, sizeof(cbData), &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == sizeof(cbData)); // Write out the action value hr = pStm->Write(pbData, cbData, &cbWritten); if (FAILED(hr)) { goto exit; } Assert(cbWritten == cbData); } // Should we clear out the dirty bit if (FALSE != fClearDirty) { m_dwState &= ~ACT_STATE_DIRTY; } // Set the return value hr = S_OK; exit: return hr; }