/////////////////////////////////////////////////////////////////////////////// // // RulesMgr.cpp // /////////////////////////////////////////////////////////////////////////////// #include #include "rulesmgr.h" #include "ruleutil.h" #include "rule.h" #include "junkrule.h" #include #include #include #include "demand.h" CRulesManager::CRulesManager() : m_cRef(0), m_dwState(STATE_LOADED_INIT), m_pMailHead(NULL), m_pNewsHead(NULL), m_pFilterHead(NULL), m_pIRuleSenderMail(NULL),m_pIRuleSenderNews(NULL), m_pIRuleJunk(NULL) { // Thread Safety InitializeCriticalSection(&m_cs); } CRulesManager::~CRulesManager() { AssertSz(m_cRef == 0, "Somebody still has a hold of us!!"); if (NULL != m_pMailHead) { _HrFreeRules(RULE_TYPE_MAIL); } if (NULL != m_pNewsHead) { _HrFreeRules(RULE_TYPE_NEWS); } if (NULL != m_pFilterHead) { _HrFreeRules(RULE_TYPE_FILTER); } SafeRelease(m_pIRuleSenderMail); SafeRelease(m_pIRuleSenderNews); SafeRelease(m_pIRuleJunk); // Thread Safety DeleteCriticalSection(&m_cs); } STDMETHODIMP_(ULONG) CRulesManager::AddRef() { return ::InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CRulesManager::Release() { LONG cRef = 0; cRef = ::InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return cRef; } return cRef; } STDMETHODIMP CRulesManager::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_IOERulesManager)) { *ppvObject = static_cast(this); } else { hr = E_NOINTERFACE; goto exit; } reinterpret_cast(*ppvObject)->AddRef(); hr = S_OK; exit: return hr; } STDMETHODIMP CRulesManager::Initialize(DWORD dwFlags) { HRESULT hr = S_OK; // Check the incoming params if (0 != dwFlags) { hr = E_INVALIDARG; goto exit; } // Set the proper return value hr = S_OK; exit: return hr; } STDMETHODIMP CRulesManager::GetRule(RULEID ridRule, RULE_TYPE type, DWORD dwFlags, IOERule ** ppIRule) { HRESULT hr = S_OK; RULENODE * pNodeWalk = NULL; IOERule * pIRule = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Check the incoming params if (RULEID_INVALID == ridRule) { hr = E_INVALIDARG; goto exit; } // Initialize the ougoing params if (NULL != ppIRule) { *ppIRule = NULL; } // Check to see if we already loaded the rules hr = _HrLoadRules(type); if (FAILED(hr)) { goto exit; } // Check the special types if (RULEID_SENDERS == ridRule) { if (RULE_TYPE_MAIL == type) { pIRule = m_pIRuleSenderMail; } else if (RULE_TYPE_NEWS == type) { pIRule = m_pIRuleSenderNews; } else { hr = E_INVALIDARG; goto exit; } } else if (RULEID_JUNK == ridRule) { if (RULE_TYPE_MAIL != type) { hr = E_INVALIDARG; goto exit; } pIRule = m_pIRuleJunk; } else { // Walk the proper list if (RULE_TYPE_MAIL == type) { pNodeWalk = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { pNodeWalk = m_pNewsHead; } else if (RULE_TYPE_FILTER == type) { pNodeWalk = m_pFilterHead; } else { hr = E_INVALIDARG; goto exit; } for (; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext) { if (ridRule == pNodeWalk->ridRule) { pIRule = pNodeWalk->pIRule; break; } } } // Did we find something? if (NULL == pIRule) { hr = E_FAIL; goto exit; } // Set the outgoing param if (NULL != ppIRule) { *ppIRule = pIRule; (*ppIRule)->AddRef(); } // Set the proper return value hr = S_OK; exit: // Thread Safety LeaveCriticalSection(&m_cs); return hr; } STDMETHODIMP CRulesManager::FindRule(LPCSTR pszRuleName, RULE_TYPE type, IOERule ** ppIRule) { HRESULT hr = S_OK; RULENODE * pNodeWalk = NULL; PROPVARIANT propvar; ZeroMemory(&propvar, sizeof(propvar)); // Thread Safety EnterCriticalSection(&m_cs); // Check the incoming params if ((NULL == pszRuleName) || (NULL == ppIRule)) { hr = E_INVALIDARG; goto exit; } // Initialize the ougoing params *ppIRule = NULL; // Check to see if we already loaded the rules hr = _HrLoadRules(type); if (FAILED(hr)) { goto exit; } // Walk the proper list if (RULE_TYPE_MAIL == type) { pNodeWalk = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { pNodeWalk = m_pNewsHead; } else if (RULE_TYPE_FILTER == type) { pNodeWalk = m_pFilterHead; } else { hr = E_INVALIDARG; goto exit; } while (NULL != pNodeWalk) { // Check to see if the name of the rule is the same hr = pNodeWalk->pIRule->GetProp(RULE_PROP_NAME , 0, &propvar); if (FAILED(hr)) { continue; } if (0 == lstrcmpi(propvar.pszVal, pszRuleName)) { *ppIRule = pNodeWalk->pIRule; (*ppIRule)->AddRef(); break; } // Move to the next one PropVariantClear(&propvar); pNodeWalk = pNodeWalk->pNext; } // Set the proper return value if (NULL == pNodeWalk) { hr = E_FAIL; } else { hr = S_OK; } exit: PropVariantClear(&propvar); // Thread Safety LeaveCriticalSection(&m_cs); return hr; } STDMETHODIMP CRulesManager::GetRules(DWORD dwFlags, RULE_TYPE typeRule, RULEINFO ** ppinfoRule, ULONG * pcpinfoRule) { HRESULT hr = S_OK; ULONG cpinfoRule = 0; RULEINFO * pinfoRuleAlloc = NULL; IOERule * pIRuleSender = NULL; RULENODE * prnodeList = NULL; RULENODE * prnodeWalk = NULL; ULONG ulIndex = 0; // Thread Safety EnterCriticalSection(&m_cs); // Check the incoming params if (NULL == ppinfoRule) { hr = E_INVALIDARG; goto exit; } // Initialize the outgoing param *ppinfoRule = NULL; if (NULL != pcpinfoRule) { *pcpinfoRule = 0; } // Check to see if we already loaded the rules hr = _HrLoadRules(typeRule); if (FAILED(hr)) { goto exit; } // Figure out which type of rules to work on switch (typeRule) { case RULE_TYPE_MAIL: prnodeList = m_pMailHead; break; case RULE_TYPE_NEWS: prnodeList = m_pNewsHead; break; case RULE_TYPE_FILTER: prnodeList = m_pFilterHead; break; default: hr = E_INVALIDARG; goto exit; } // Count the number of rules prnodeWalk = prnodeList; for (cpinfoRule = 0; NULL != prnodeWalk; prnodeWalk = prnodeWalk->pNext) { // Check to see if we should add this item if (RULE_TYPE_FILTER == typeRule) { if (0 != (dwFlags & GETF_POP3)) { if (RULEID_VIEW_DOWNLOADED == prnodeWalk->ridRule) { continue; } } } cpinfoRule++; } // Allocate space to hold the rules if (0 != cpinfoRule) { hr = HrAlloc((VOID **) &pinfoRuleAlloc, cpinfoRule * sizeof(*pinfoRuleAlloc)); if (FAILED(hr)) { goto exit; } // Initialize it to known values ZeroMemory(pinfoRuleAlloc, cpinfoRule * sizeof(*pinfoRuleAlloc)); // Fill up the info for (ulIndex = 0, prnodeWalk = prnodeList; NULL != prnodeWalk; prnodeWalk = prnodeWalk->pNext) { // Check to see if we should add this item if (RULE_TYPE_FILTER == typeRule) { if (0 != (dwFlags & GETF_POP3)) { if (RULEID_VIEW_DOWNLOADED == prnodeWalk->ridRule) { continue; } } } pinfoRuleAlloc[ulIndex].ridRule = prnodeWalk->ridRule; pinfoRuleAlloc[ulIndex].pIRule = prnodeWalk->pIRule; pinfoRuleAlloc[ulIndex].pIRule->AddRef(); ulIndex++; } } // Set the outgoing values *ppinfoRule = pinfoRuleAlloc; pinfoRuleAlloc = NULL; if (NULL != pcpinfoRule) { *pcpinfoRule = cpinfoRule; } // Set the proper return type hr = S_OK; exit: SafeMemFree(pinfoRuleAlloc); // Thread Safety LeaveCriticalSection(&m_cs); return S_OK; } STDMETHODIMP CRulesManager::SetRules(DWORD dwFlags, RULE_TYPE typeRule, RULEINFO * pinfoRule, ULONG cpinfoRule) { HRESULT hr = S_OK; ULONG ulIndex = 0; IOERule * pIRuleSender = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Check the incoming params if ((NULL == pinfoRule) && (0 != cpinfoRule)) { hr = E_INVALIDARG; goto exit; } // Check to see if we already loaded the rules hr = _HrLoadRules(typeRule); if (FAILED(hr)) { goto exit; } // Do we have to free all the current rules if (0 != (dwFlags & SETF_SENDER)) { if (RULE_TYPE_MAIL == typeRule) { SafeRelease(m_pIRuleSenderMail); m_pIRuleSenderMail = pinfoRule->pIRule; if (NULL != m_pIRuleSenderMail) { m_pIRuleSenderMail->AddRef(); } } else if (RULE_TYPE_NEWS == typeRule) { SafeRelease(m_pIRuleSenderNews); m_pIRuleSenderNews = pinfoRule->pIRule; if (NULL != m_pIRuleSenderNews) { m_pIRuleSenderNews->AddRef(); } } else { hr = E_INVALIDARG; goto exit; } } else if (0 != (dwFlags & SETF_JUNK)) { if (RULE_TYPE_MAIL != typeRule) { hr = E_INVALIDARG; goto exit; } SafeRelease(m_pIRuleJunk); m_pIRuleJunk = pinfoRule->pIRule; if (NULL != m_pIRuleJunk) { m_pIRuleJunk->AddRef(); } } else { if (0 != (dwFlags & SETF_CLEAR)) { _HrFreeRules(typeRule); } // for each new rule for (ulIndex = 0; ulIndex < cpinfoRule; ulIndex++) { if (0 != (dwFlags & SETF_REPLACE)) { // Add the rule to the list hr = _HrReplaceRule(pinfoRule[ulIndex].ridRule, pinfoRule[ulIndex].pIRule, typeRule); if (FAILED(hr)) { goto exit; } } else { // Add the rule to the list hr = _HrAddRule(pinfoRule[ulIndex].ridRule, pinfoRule[ulIndex].pIRule, typeRule); if (FAILED(hr)) { goto exit; } } } } // Save the rules hr = _HrSaveRules(typeRule); if (FAILED(hr)) { goto exit; } if ((0 == (dwFlags & SETF_SENDER)) && (0 == (dwFlags & SETF_JUNK))) { // Fix up the rule ids hr = _HrFixupRuleInfo(typeRule, pinfoRule, cpinfoRule); if (FAILED(hr)) { goto exit; } } // Set the proper return value hr = S_OK; exit: // Thread Safety LeaveCriticalSection(&m_cs); return hr; } STDMETHODIMP CRulesManager::EnumRules(DWORD dwFlags, RULE_TYPE type, IOEEnumRules ** ppIEnumRules) { HRESULT hr = S_OK; CEnumRules * pEnumRules = NULL; RULENODE rnode; RULENODE * prnode = NULL; IOERule * pIRuleSender = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Check the incoming params if (NULL == ppIEnumRules) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params *ppIEnumRules = NULL; // Check to see if we already loaded the rules hr = _HrLoadRules(type); if (FAILED(hr)) { goto exit; } // Create the rules enumerator object pEnumRules = new CEnumRules; if (NULL == pEnumRules) { hr = E_OUTOFMEMORY; goto exit; } // Initialize the rules enumerator if (0 != (dwFlags & ENUMF_SENDER)) { if (RULE_TYPE_MAIL == type) { pIRuleSender = m_pIRuleSenderMail; } else if (RULE_TYPE_NEWS == type) { pIRuleSender = m_pIRuleSenderNews; } else { hr = E_INVALIDARG; goto exit; } if (NULL != pIRuleSender) { ZeroMemory(&rnode, sizeof(rnode)); rnode.pIRule = pIRuleSender; prnode = &rnode; } else { prnode = NULL; } } else { if (RULE_TYPE_MAIL == type) { prnode = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { prnode = m_pNewsHead; } else if (RULE_TYPE_FILTER == type) { prnode = m_pFilterHead; } else { hr = E_INVALIDARG; goto exit; } } hr = pEnumRules->_HrInitialize(0, type, prnode); if (FAILED(hr)) { goto exit; } // Get the rules enumerator interface hr = pEnumRules->QueryInterface(IID_IOEEnumRules, (void **) ppIEnumRules); if (FAILED(hr)) { goto exit; } pEnumRules = NULL; hr = S_OK; exit: if (NULL != pEnumRules) { delete pEnumRules; } // Thread Safety LeaveCriticalSection(&m_cs); return hr; } STDMETHODIMP CRulesManager::ExecRules(DWORD dwFlags, RULE_TYPE type, IOEExecRules ** ppIExecRules) { HRESULT hr = S_OK; CExecRules * pExecRules = NULL; RULENODE rnode; RULENODE * prnodeList = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Check the incoming params if (NULL == ppIExecRules) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params *ppIExecRules = NULL; // Check to see if we already loaded the rules hr = _HrLoadRules(type); if (FAILED(hr)) { goto exit; } // Create the rules enumerator object pExecRules = new CExecRules; if (NULL == pExecRules) { hr = E_OUTOFMEMORY; goto exit; } if (RULE_TYPE_MAIL == type) { prnodeList = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { prnodeList = m_pNewsHead; } else { hr = E_INVALIDARG; goto exit; } // Initialize the rules enumerator hr = pExecRules->_HrInitialize(ERF_ONLY_ENABLED | ERF_ONLY_VALID, prnodeList); if (FAILED(hr)) { goto exit; } // Get the rules enumerator interface hr = pExecRules->QueryInterface(IID_IOEExecRules, (void **) ppIExecRules); if (FAILED(hr)) { goto exit; } pExecRules = NULL; hr = S_OK; exit: if (NULL != pExecRules) { delete pExecRules; } // Thread Safety LeaveCriticalSection(&m_cs); return hr; } STDMETHODIMP CRulesManager::ExecuteRules(RULE_TYPE typeRule, DWORD dwFlags, HWND hwndUI, IOEExecRules * pIExecRules, MESSAGEINFO * pMsgInfo, IMessageFolder * pFolder, IMimeMessage * pIMMsg) { HRESULT hr = S_OK; ULONG ulIndex = 0; RULENODE * prnodeHead = NULL; PROPVARIANT propvar = {0}; ACT_ITEM * pActions = NULL; ULONG cActions = 0; ACT_ITEM * pActionsList = NULL; ULONG cActionsList = 0; ACT_ITEM * pActionsNew = NULL; ULONG cActionsNew = 0; BOOL fStopProcessing = FALSE; BOOL fMatch = FALSE; // Thread Safety EnterCriticalSection(&m_cs); // Check incoming params if ((NULL == pIExecRules) || (NULL == pMsgInfo)) { hr = E_INVALIDARG; goto exit; } // Check to see if we already loaded the rules hr = _HrLoadRules(typeRule); if (FAILED(hr)) { goto exit; } // Figure out which list to use switch (typeRule) { case RULE_TYPE_MAIL: prnodeHead = m_pMailHead; break; case RULE_TYPE_NEWS: prnodeHead = m_pNewsHead; break; default: Assert(FALSE); hr = E_INVALIDARG; goto exit; } // For each rule for (; NULL != prnodeHead; prnodeHead = prnodeHead->pNext) { // Skip if we don't have a rule if (NULL == prnodeHead) { continue; } // Skip if it isn't enabled hr = prnodeHead->pIRule->GetProp(RULE_PROP_DISABLED, 0, &propvar); Assert(VT_BOOL == propvar.vt); if (FAILED(hr) || (FALSE != propvar.boolVal)) { continue; } // Execute rule hr = prnodeHead->pIRule->Evaluate(pMsgInfo->pszAcctId, pMsgInfo, pFolder, NULL, pIMMsg, pMsgInfo->cbMessage, &pActions, &cActions); if (FAILED(hr)) { goto exit; } // Did we have a match if (S_OK == hr) { // We've matched at least once fMatch = TRUE; // If these are server actions if ((1 == cActions) && ((ACT_TYPE_DELETESERVER == pActions[ulIndex].type) || (ACT_TYPE_DONTDOWNLOAD == pActions[ulIndex].type))) { // If this is our only action if (0 == cActionsList) { // Save the action pActionsList = pActions; pActions = NULL; cActionsList = cActions; // We are done fStopProcessing = TRUE; } else { // We already have to do something with it // so skip over this action RuleUtil_HrFreeActionsItem(pActions, cActions); SafeMemFree(pActions); continue; } } else { // Should we stop after merging these? for (ulIndex = 0; ulIndex < cActions; ulIndex++) { if (ACT_TYPE_STOP == pActions[ulIndex].type) { fStopProcessing = TRUE; break; } } // Merge these items with the previous ones hr = RuleUtil_HrMergeActions(pActionsList, cActionsList, pActions, cActions, &pActionsNew, &cActionsNew); if (FAILED(hr)) { goto exit; } // Free up the previous ones RuleUtil_HrFreeActionsItem(pActionsList, cActionsList); SafeMemFree(pActionsList); RuleUtil_HrFreeActionsItem(pActions, cActions); SafeMemFree(pActions); // Save off the new ones pActionsList = pActionsNew; pActionsNew = NULL; cActionsList = cActionsNew; } // Should we continue... if (FALSE != fStopProcessing) { break; } } } // Apply the actions if need be if ((FALSE != fMatch) && (NULL != pActionsList) && (0 != cActionsList)) { if (FAILED(RuleUtil_HrApplyActions(hwndUI, pIExecRules, pMsgInfo, pFolder, pIMMsg, (RULE_TYPE_MAIL != typeRule) ? DELETE_MESSAGE_NOTRASHCAN : 0, pActionsList, cActionsList, NULL, NULL))) { hr = E_FAIL; goto exit; } } // Set the return value hr = (FALSE != fMatch) ? S_OK : S_FALSE; exit: // Thread Safety RuleUtil_HrFreeActionsItem(pActionsNew, cActionsNew); SafeMemFree(pActionsNew); RuleUtil_HrFreeActionsItem(pActions, cActions); SafeMemFree(pActions); RuleUtil_HrFreeActionsItem(pActionsList, cActionsList); SafeMemFree(pActionsList); LeaveCriticalSection(&m_cs); return hr; } HRESULT CRulesManager::_HrLoadRules(RULE_TYPE type) { HRESULT hr = S_OK; LPCSTR pszSubKey = NULL; LPSTR pszOrderAlloc = NULL; LPSTR pszOrder = NULL; LPSTR pszWalk = NULL; HKEY hkeyRoot = NULL; DWORD dwDisp = 0; LONG lErr = 0; ULONG cbData = 0; IOERule * pIRule = NULL; DWORD dwData = 0; CHAR rgchRulePath[MAX_PATH]; ULONG cchRulePath = 0; PROPVARIANT propvar = {0}; RULEID ridRule = RULEID_INVALID; CHAR rgchTagBuff[CCH_INDEX_MAX + 2]; // Check to see if we're already initialized if (RULE_TYPE_MAIL == type) { if (0 != (m_dwState & STATE_LOADED_MAIL)) { hr = S_FALSE; goto exit; } // Make sure we loaded the sender rules _HrLoadSenders(); // Make sure we loaded the junk rule if (0 != (g_dwAthenaMode & MODE_JUNKMAIL)) { _HrLoadJunk(); } // Set the key path pszSubKey = c_szRulesMail; } else if (RULE_TYPE_NEWS == type) { if (0 != (m_dwState & STATE_LOADED_NEWS)) { hr = S_FALSE; goto exit; } // Make sure we loaded the sender rules _HrLoadSenders(); // Set the key path pszSubKey = c_szRulesNews; } else if (RULE_TYPE_FILTER == type) { if (0 != (m_dwState & STATE_LOADED_FILTERS)) { hr = S_FALSE; goto exit; } // Set the key path pszSubKey = c_szRulesFilter; } else { hr = E_INVALIDARG; goto exit; } // Check to see if the Rule node already exists lErr = AthUserCreateKey(pszSubKey, KEY_ALL_ACCESS, &hkeyRoot, &dwDisp); if (ERROR_SUCCESS != lErr) { hr = HRESULT_FROM_WIN32(lErr); goto exit; } // Check the current version cbData = sizeof(dwData); lErr = RegQueryValueEx(hkeyRoot, c_szRulesVersion, NULL, NULL, (BYTE *) &dwData, &cbData); if (ERROR_SUCCESS != lErr) { // Push out the correct rules manager version dwData = RULESMGR_VERSION; lErr = RegSetValueEx(hkeyRoot, c_szRulesVersion, 0, REG_DWORD, (CONST BYTE *) &dwData, sizeof(dwData)); if (ERROR_SUCCESS != lErr) { hr = HRESULT_FROM_WIN32(lErr); goto exit; } } Assert(RULESMGR_VERSION == dwData); // Create the default rules if needed hr = RuleUtil_HrUpdateDefaultRules(type); if (FAILED(hr)) { goto exit; } // Figure out the size of the order lErr = AthUserGetValue(pszSubKey, c_szRulesOrder, NULL, NULL, &cbData); if ((ERROR_SUCCESS != lErr) && (ERROR_FILE_NOT_FOUND != lErr)) { hr = E_FAIL; goto exit; } if (ERROR_FILE_NOT_FOUND != lErr) { // Allocate the space to hold the order hr = HrAlloc((void **) &pszOrderAlloc, cbData); if (FAILED(hr)) { goto exit; } // Get the order from the registry lErr = AthUserGetValue(pszSubKey, c_szRulesOrder, NULL, (LPBYTE) pszOrderAlloc, &cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Build up the rule registry path StrCpyN(rgchRulePath, pszSubKey, ARRAYSIZE(rgchRulePath)); StrCatBuff(rgchRulePath, g_szBackSlash, ARRAYSIZE(rgchRulePath)); cchRulePath = lstrlen(rgchRulePath); // Initialize the rule tag buffer rgchTagBuff[0] = '0'; rgchTagBuff[1] = 'X'; // Parse the order string to create the rules pszOrder = pszOrderAlloc; while ('\0' != *pszOrder) { SafeRelease(pIRule); // Create a new rule hr = HrCreateRule(&pIRule); if (FAILED(hr)) { goto exit; } // Find the name of the new rule pszWalk = StrStr(pszOrder, g_szSpace); if (NULL != pszWalk) { *pszWalk = '\0'; pszWalk++; } // Build the path to the rule StrCpyN(rgchRulePath + cchRulePath, pszOrder, ARRAYSIZE(rgchRulePath) - cchRulePath); // Load the rule hr = pIRule->LoadReg(rgchRulePath); if (SUCCEEDED(hr)) { // Build the correct hex string StrCpyN(rgchTagBuff + 2, pszOrder, ARRAYSIZE(rgchTagBuff) - 2); // Get the new rule handle ridRule = ( ( RULEID ) 0); SideAssert(FALSE != StrToIntEx(rgchTagBuff, STIF_SUPPORT_HEX, (INT *) &ridRule)); // Add the new rule to the manager hr = _HrAddRule(ridRule, pIRule, type); if (FAILED(hr)) { goto exit; } } // Move to the next item in the order if (NULL == pszWalk) { pszOrder += lstrlen(pszOrder); } else { pszOrder = pszWalk; } } } // We've loaded the rules successfully if (RULE_TYPE_MAIL == type) { m_dwState |= STATE_LOADED_MAIL; } else if (RULE_TYPE_NEWS == type) { m_dwState |= STATE_LOADED_NEWS; } else { m_dwState |= STATE_LOADED_FILTERS; } // Set the return value hr = S_OK; exit: SafeMemFree(pszOrderAlloc); SafeRelease(pIRule); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } HRESULT CRulesManager::_HrLoadSenders(VOID) { HRESULT hr = S_OK; HKEY hkeyRoot = NULL; DWORD dwDisp = 0; DWORD dwData = 0; LONG lErr = 0; ULONG cbData = 0; IOERule * pIRule = NULL; CHAR rgchSenderPath[MAX_PATH]; // Do we have anything to do? if (0 != (m_dwState & STATE_LOADED_SENDERS)) { hr = S_FALSE; goto exit; } // Let's get access to the sender root key lErr = AthUserCreateKey(c_szSenders, KEY_ALL_ACCESS, &hkeyRoot, &dwDisp); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Are the senders the correct version? cbData = sizeof(dwData); lErr = RegQueryValueEx(hkeyRoot, c_szSendersVersion, 0, NULL, (BYTE *) &dwData, &cbData); if ((ERROR_SUCCESS != lErr) && (ERROR_FILE_NOT_FOUND != lErr)) { hr = E_FAIL; goto exit; } if (ERROR_FILE_NOT_FOUND == lErr) { dwData = RULESMGR_VERSION; cbData = sizeof(dwData); lErr = RegSetValueEx(hkeyRoot, c_szSendersVersion, 0, REG_DWORD, (BYTE *) &dwData, cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } } Assert(dwData == RULESMGR_VERSION); // Is there anything to do? if (REG_CREATED_NEW_KEY != dwDisp) { // Create the path to the sender StrCpyN(rgchSenderPath, c_szSenders, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, g_szBackSlash, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, c_szMailDir, ARRAYSIZE(rgchSenderPath)); // Create the mail sender rule hr = RuleUtil_HrLoadSender(rgchSenderPath, 0, &pIRule); if (FAILED(hr)) { goto exit; } // Save the loaded rule if (S_OK == hr) { m_pIRuleSenderMail = pIRule; pIRule = NULL; } // Create the path to the sender StrCpyN(rgchSenderPath, c_szSenders, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, g_szBackSlash, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, c_szNewsDir, ARRAYSIZE(rgchSenderPath)); // Create the news sender rule hr = RuleUtil_HrLoadSender(rgchSenderPath, 0, &pIRule); if (FAILED(hr)) { goto exit; } // Save the loaded rule if (S_OK == hr) { m_pIRuleSenderNews = pIRule; pIRule = NULL; } } // Note that we've loaded the senders m_dwState |= STATE_LOADED_SENDERS; // Set the return value hr = S_OK; exit: SafeRelease(pIRule); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } HRESULT CRulesManager::_HrLoadJunk(VOID) { HRESULT hr = S_OK; HKEY hkeyRoot = NULL; DWORD dwDisp = 0; DWORD dwData = 0; ULONG cbData = 0; LONG lErr = 0; IOERule * pIRule = NULL; // Do we have anything to do? if (0 != (m_dwState & STATE_LOADED_JUNK)) { hr = S_FALSE; goto exit; } // Let's get access to the Junk mail root key lErr = AthUserCreateKey(c_szRulesJunkMail, KEY_ALL_ACCESS, &hkeyRoot, &dwDisp); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Is the junk mail the correct version? cbData = sizeof(dwData); lErr = RegQueryValueEx(hkeyRoot, c_szRulesVersion, 0, NULL, (BYTE *) &dwData, &cbData); if ((ERROR_SUCCESS != lErr) && (ERROR_FILE_NOT_FOUND != lErr)) { hr = E_FAIL; goto exit; } if (ERROR_FILE_NOT_FOUND == lErr) { dwData = RULESMGR_VERSION; cbData = sizeof(dwData); lErr = RegSetValueEx(hkeyRoot, c_szRulesVersion, 0, REG_DWORD, (BYTE *) &dwData, cbData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } } Assert(dwData == RULESMGR_VERSION); // Create the rule hr = HrCreateJunkRule(&pIRule); if (FAILED(hr)) { goto exit; } // Load the junk rule hr = pIRule->LoadReg(c_szRulesJunkMail); if (FAILED(hr)) { goto exit; } m_pIRuleJunk = pIRule; pIRule = NULL; // Note that we've loaded the junk rule m_dwState |= STATE_LOADED_JUNK; // Set the return value hr = S_OK; exit: SafeRelease(pIRule); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } HRESULT CRulesManager::_HrSaveRules(RULE_TYPE type) { HRESULT hr = S_OK; LPCSTR pszRegPath = NULL; DWORD dwData = 0; LONG lErr = 0; RULENODE * pRuleNode = NULL; RULENODE * pNodeWalk = NULL; ULONG cpIRule = 0; LPSTR pszOrder = NULL; HKEY hkeyRoot = NULL; DWORD dwIndex = 0; CHAR rgchOrder[CCH_INDEX_MAX]; ULONG cchOrder = 0; CHAR rgchRulePath[MAX_PATH]; ULONG cchRulePath = 0; BOOL fNewRule = FALSE; ULONG ulRuleID = 0; HKEY hkeyDummy = NULL; LONG cSubKeys = 0; // Make sure we loaded the sender rules _HrSaveSenders(); // Make sure we loaded the junk rules if (0 != (g_dwAthenaMode & MODE_JUNKMAIL)) { _HrSaveJunk(); } // Check to see if we have anything to save if (RULE_TYPE_MAIL == type) { if (0 == (m_dwState & STATE_LOADED_MAIL)) { hr = S_FALSE; goto exit; } // Set the key path pszRegPath = c_szRulesMail; pRuleNode = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { if (0 == (m_dwState & STATE_LOADED_NEWS)) { hr = S_FALSE; goto exit; } // Set the key path pszRegPath = c_szRulesNews; pRuleNode = m_pNewsHead; } else if (RULE_TYPE_FILTER == type) { if (0 == (m_dwState & STATE_LOADED_FILTERS)) { hr = S_FALSE; goto exit; } // Set the key path pszRegPath = c_szRulesFilter; pRuleNode = m_pFilterHead; } else { hr = E_INVALIDARG; goto exit; } lErr = AthUserCreateKey(pszRegPath, KEY_ALL_ACCESS, &hkeyRoot, &dwData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Save out the rules version dwData = RULESMGR_VERSION; lErr = RegSetValueEx(hkeyRoot, c_szRulesVersion, 0, REG_DWORD, (CONST BYTE *) &dwData, sizeof(dwData)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Get the number of rules cpIRule = 0; for (pNodeWalk = pRuleNode; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext) { cpIRule++; } // Allocate space to hold the order DWORD cchSize = ((cpIRule * CCH_INDEX_MAX) + 1); hr = HrAlloc((void **) &pszOrder, cchSize); if (FAILED(hr)) { goto exit; } pszOrder[0] = '\0'; // Delete all the old rules lErr = SHQueryInfoKey(hkeyRoot, (LPDWORD) (&cSubKeys), NULL, NULL, NULL); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Delete all the old rules for (cSubKeys--; cSubKeys >= 0; cSubKeys--) { cchOrder = sizeof(rgchOrder); lErr = SHEnumKeyEx(hkeyRoot, cSubKeys, rgchOrder, &cchOrder); if (ERROR_NO_MORE_ITEMS == lErr) { break; } if (ERROR_SUCCESS != lErr) { continue; } SHDeleteKey(hkeyRoot, rgchOrder); } // Delete the old order string RegDeleteValue(hkeyRoot, c_szRulesOrder); // Build up the rule registry path StrCpyN(rgchRulePath, pszRegPath, ARRAYSIZE(rgchRulePath)); StrCatBuff(rgchRulePath, g_szBackSlash, ARRAYSIZE(rgchRulePath)); cchRulePath = lstrlen(rgchRulePath); // Write out the rules with good tags for (dwIndex = 0, pNodeWalk = pRuleNode; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext, dwIndex++) { if (RULEID_INVALID == pNodeWalk->ridRule) { fNewRule = TRUE; continue; } // Get a new index from the order wnsprintf(rgchOrder, ARRAYSIZE(rgchOrder), "%03X", pNodeWalk->ridRule); // Build the path to the rule StrCpyN(rgchRulePath + cchRulePath, rgchOrder, ARRAYSIZE(rgchRulePath) - cchRulePath); // Save the rule hr = pNodeWalk->pIRule->SaveReg(rgchRulePath, TRUE); if (FAILED(hr)) { goto exit; } } // Fill in the new tags if (FALSE != fNewRule) { ulRuleID = 0; // Write out the updated rule for (dwIndex = 0, pNodeWalk = pRuleNode; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext, dwIndex++) { if (RULEID_INVALID != pNodeWalk->ridRule) { continue; } // Find the first open entry for (; ulRuleID < PtrToUlong(RULEID_JUNK); ulRuleID++) { // Get a new index from the order wnsprintf(rgchOrder, ARRAYSIZE(rgchOrder), "%03X", ulRuleID); lErr = RegOpenKeyEx(hkeyRoot, rgchOrder, 0, KEY_READ, &hkeyDummy); if (ERROR_SUCCESS == lErr) { RegCloseKey(hkeyDummy); } else { break; } } if (ERROR_FILE_NOT_FOUND != lErr) { hr = E_FAIL; goto exit; } // Set the rule tag pNodeWalk->ridRule = (RULEID) IntToPtr(ulRuleID); // Build the path to the rule StrCpyN(rgchRulePath + cchRulePath, rgchOrder, ARRAYSIZE(rgchRulePath) - cchRulePath); // Save the rule hr = pNodeWalk->pIRule->SaveReg(rgchRulePath, TRUE); if (FAILED(hr)) { goto exit; } } } // Write out the new order string for (dwIndex = 0, pNodeWalk = pRuleNode; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext, dwIndex++) { // Get a new index from the order wnsprintf(rgchOrder, ARRAYSIZE(rgchOrder), "%03X", pNodeWalk->ridRule); // Add rule to the order if ('\0' != pszOrder[0]) { StrCatBuff(pszOrder, g_szSpace, cchSize); } StrCatBuff(pszOrder, rgchOrder, cchSize); } // Save the order string if (ERROR_SUCCESS != AthUserSetValue(pszRegPath, c_szRulesOrder, REG_SZ, (CONST BYTE *) pszOrder, lstrlen(pszOrder) + 1)) { hr = E_FAIL; goto exit; } // Set the return value hr = S_OK; exit: SafeMemFree(pszOrder); if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } HRESULT CRulesManager::_HrSaveSenders(VOID) { HRESULT hr = S_OK; DWORD dwData = 0; LONG lErr = 0; HKEY hkeyRoot = NULL; DWORD dwIndex = 0; CHAR rgchSenderPath[MAX_PATH]; // Check to see if we have anything to save if (0 == (m_dwState & STATE_LOADED_SENDERS)) { hr = S_FALSE; goto exit; } lErr = AthUserCreateKey(c_szSenders, KEY_ALL_ACCESS, &hkeyRoot, &dwData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Save out the senders version dwData = RULESMGR_VERSION; lErr = RegSetValueEx(hkeyRoot, c_szSendersVersion, 0, REG_DWORD, (CONST BYTE *) &dwData, sizeof(dwData)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Delete the old sender list SHDeleteKey(hkeyRoot, c_szMailDir); // Build up the sender registry path StrCpyN(rgchSenderPath, c_szSenders, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, g_szBackSlash, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, c_szMailDir, ARRAYSIZE(rgchSenderPath)); // Save the rule if (NULL != m_pIRuleSenderMail) { hr = m_pIRuleSenderMail->SaveReg(rgchSenderPath, TRUE); if (FAILED(hr)) { goto exit; } } // Delete the old sender list SHDeleteKey(hkeyRoot, c_szNewsDir); // Build up the sender registry path StrCpyN(rgchSenderPath, c_szSenders, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, g_szBackSlash, ARRAYSIZE(rgchSenderPath)); StrCatBuff(rgchSenderPath, c_szNewsDir, ARRAYSIZE(rgchSenderPath)); // Save the rule if (NULL != m_pIRuleSenderNews) { hr = m_pIRuleSenderNews->SaveReg(rgchSenderPath, TRUE); if (FAILED(hr)) { goto exit; } } // Set the return value hr = S_OK; exit: if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } HRESULT CRulesManager::_HrSaveJunk(VOID) { HRESULT hr = S_OK; DWORD dwData = 0; LONG lErr = 0; HKEY hkeyRoot = NULL; DWORD dwIndex = 0; CHAR rgchSenderPath[MAX_PATH]; // Check to see if we have anything to save if (0 == (m_dwState & STATE_LOADED_JUNK)) { hr = S_FALSE; goto exit; } lErr = AthUserCreateKey(c_szRulesJunkMail, KEY_ALL_ACCESS, &hkeyRoot, &dwData); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Save out the senders version dwData = RULESMGR_VERSION; lErr = RegSetValueEx(hkeyRoot, c_szRulesVersion, 0, REG_DWORD, (CONST BYTE *) &dwData, sizeof(dwData)); if (ERROR_SUCCESS != lErr) { hr = E_FAIL; goto exit; } // Save the rule if (NULL != m_pIRuleJunk) { hr = m_pIRuleJunk->SaveReg(c_szRulesJunkMail, TRUE); if (FAILED(hr)) { goto exit; } } // Set the return value hr = S_OK; exit: if (NULL != hkeyRoot) { RegCloseKey(hkeyRoot); } return hr; } HRESULT CRulesManager::_HrFreeRules(RULE_TYPE type) { HRESULT hr = S_OK; RULENODE * pNodeWalk = NULL; RULENODE * pNodeNext = NULL; // Initialize the params if (RULE_TYPE_MAIL == type) { pNodeWalk = m_pMailHead; pNodeNext = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { pNodeWalk = m_pNewsHead; pNodeNext = m_pNewsHead; } else if (RULE_TYPE_FILTER == type) { pNodeWalk = m_pFilterHead; pNodeNext = m_pFilterHead; } else { hr = E_FAIL; goto exit; } // Walk the list and free each item while (NULL != pNodeWalk) { // Save off the next item pNodeNext = pNodeWalk->pNext; // Release the rule AssertSz(NULL != pNodeWalk->pIRule, "Where the heck is the rule???"); pNodeWalk->pIRule->Release(); // Free up the node delete pNodeWalk; // Move to the next item pNodeWalk = pNodeNext; } // Clear out the list head if (RULE_TYPE_MAIL == type) { m_pMailHead = NULL; } else if (RULE_TYPE_NEWS == type) { m_pNewsHead = NULL; } else { m_pFilterHead = NULL; } exit: // Set the return param return hr; } HRESULT CRulesManager::_HrAddRule(RULEID ridRule, IOERule * pIRule, RULE_TYPE type) { HRESULT hr = S_OK; RULENODE * pRuleNode = NULL; RULENODE * pNodeWalk = NULL; // Check incoming params if (NULL == pIRule) { hr = E_INVALIDARG; goto exit; } // Create a new rule node pRuleNode = new RULENODE; if (NULL == pRuleNode) { hr = E_OUTOFMEMORY; goto exit; } // Initialize the node pRuleNode->pNext = NULL; pRuleNode->ridRule = ridRule; pRuleNode->pIRule = pIRule; pRuleNode->pIRule->AddRef(); // Add the node to the proper list if (RULE_TYPE_MAIL == type) { pNodeWalk = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { pNodeWalk = m_pNewsHead; } else { pNodeWalk = m_pFilterHead; } if (NULL == pNodeWalk) { if (RULE_TYPE_MAIL == type) { m_pMailHead = pRuleNode; } else if (RULE_TYPE_NEWS == type) { m_pNewsHead = pRuleNode; } else { m_pFilterHead = pRuleNode; } pRuleNode = NULL; } else { while (NULL != pNodeWalk->pNext) { pNodeWalk = pNodeWalk->pNext; } pNodeWalk->pNext = pRuleNode; pRuleNode = NULL; } // Set return values hr = S_OK; exit: if (NULL != pRuleNode) { pRuleNode->pIRule->Release(); delete pRuleNode; } return hr; } HRESULT CRulesManager::_HrReplaceRule(RULEID ridRule, IOERule * pIRule, RULE_TYPE type) { HRESULT hr = S_OK; RULENODE * pNodeWalk = NULL; RULENODE * pNodePrev = NULL; // Nothing to do if we don't have a rule if (NULL == pIRule) { hr = E_FAIL; goto exit; } // Initialize the params if (RULE_TYPE_MAIL == type) { pNodeWalk = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { pNodeWalk = m_pNewsHead; } else { pNodeWalk = m_pFilterHead; } // Walk the list and free each item for (; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext) { if (pNodeWalk->ridRule == ridRule) { // We found it break; } } // Couldn't find the rule in the list if (NULL == pNodeWalk) { hr = E_FAIL; goto exit; } // Replace the rule SafeRelease(pNodeWalk->pIRule); pNodeWalk->pIRule = pIRule; pNodeWalk->pIRule->AddRef(); // Set the return param hr = S_OK; exit: return hr; } HRESULT CRulesManager::_HrRemoveRule(IOERule * pIRule, RULE_TYPE type) { HRESULT hr = S_OK; RULENODE * pNodeWalk = NULL; RULENODE * pNodePrev = NULL; // Initialize the params if (RULE_TYPE_MAIL == type) { pNodeWalk = m_pMailHead; } else if (RULE_TYPE_NEWS == type) { pNodeWalk = m_pNewsHead; } else { pNodeWalk = m_pFilterHead; } // Walk the list and free each item pNodePrev = NULL; while (NULL != pNodeWalk) { if (pNodeWalk->pIRule == pIRule) { // We found it break; } // Save off the next item pNodePrev = pNodeWalk; // Move to the next item pNodeWalk = pNodeWalk->pNext; } // Couldn't find the rule in the list if (NULL == pNodeWalk) { hr = E_FAIL; goto exit; } if (NULL == pNodePrev) { // Clear out the list head if (RULE_TYPE_MAIL == type) { m_pMailHead = pNodeWalk->pNext; } else if (RULE_TYPE_NEWS == type) { m_pNewsHead = pNodeWalk->pNext; } else { m_pFilterHead = pNodeWalk->pNext; } } else { pNodePrev->pNext = pNodeWalk->pNext; } // Free up the node pNodeWalk->pIRule->Release(); pNodeWalk->pNext = NULL; delete pNodeWalk; // Set the return param hr = S_OK; exit: return hr; } HRESULT CRulesManager::_HrFixupRuleInfo(RULE_TYPE typeRule, RULEINFO * pinfoRule, ULONG cpinfoRule) { HRESULT hr = S_OK; ULONG ulIndex = 0; RULENODE * pNodeHead = NULL; RULENODE * pNodeWalk = NULL; // Check incoming args if ((NULL == pinfoRule) && (0 != cpinfoRule)) { hr = E_INVALIDARG; goto exit; } // Walk the proper list if (RULE_TYPE_MAIL == typeRule) { pNodeHead = m_pMailHead; } else if (RULE_TYPE_NEWS == typeRule) { pNodeHead = m_pNewsHead; } else if (RULE_TYPE_FILTER == typeRule) { pNodeHead = m_pFilterHead; } else { hr = E_INVALIDARG; goto exit; } // Search the rule info list for an unknown ruleid for (ulIndex = 0; ulIndex < cpinfoRule; ulIndex++) { // If the rule id is invalid try to find it if (RULEID_INVALID == pinfoRule[ulIndex].ridRule) { for (pNodeWalk = pNodeHead; NULL != pNodeWalk; pNodeWalk = pNodeWalk->pNext) { // Check to see if the rule is the same if (pNodeWalk->pIRule == pinfoRule[ulIndex].pIRule) { pinfoRule[ulIndex].ridRule = pNodeWalk->ridRule; break; } } if (NULL == pNodeWalk) { hr = E_FAIL; goto exit; } } } // Set the return value hr = S_OK; exit: return hr; } CEnumRules::CEnumRules() { m_cRef = 0; m_pNodeHead = NULL; m_pNodeCurr = NULL; m_dwFlags = 0; m_typeRule = RULE_TYPE_MAIL; } CEnumRules::~CEnumRules() { RULENODE * pNodeNext = NULL; AssertSz(m_cRef == 0, "Somebody still has a hold of us!!"); // Walk the list and free each item while (NULL != m_pNodeHead) { // Save off the next item pNodeNext = m_pNodeHead->pNext; // Release the rule AssertSz(NULL != m_pNodeHead->pIRule, "Where the heck is the rule???"); m_pNodeHead->pIRule->Release(); // Free up the node delete m_pNodeHead; // Move to the next item m_pNodeHead = pNodeNext; } } STDMETHODIMP_(ULONG) CEnumRules::AddRef() { return ::InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CEnumRules::Release() { LONG cRef = 0; cRef = ::InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return cRef; } return cRef; } STDMETHODIMP CEnumRules::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_IOEEnumRules)) { *ppvObject = static_cast(this); } else { hr = E_NOINTERFACE; goto exit; } reinterpret_cast(*ppvObject)->AddRef(); hr = S_OK; exit: return hr; } STDMETHODIMP CEnumRules::Next(ULONG cpIRule, IOERule ** rgpIRule, ULONG * pcpIRuleFetched) { HRESULT hr = S_OK; ULONG cpIRuleRet = 0; // Check incoming params if (NULL == rgpIRule) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params *rgpIRule = NULL; if (NULL != pcpIRuleFetched) { *pcpIRuleFetched = 0; } // If we're at the end then just return if (NULL == m_pNodeCurr) { hr = S_FALSE; goto exit; } for (cpIRuleRet = 0; cpIRuleRet < cpIRule; cpIRuleRet++) { rgpIRule[cpIRuleRet] = m_pNodeCurr->pIRule; (rgpIRule[cpIRuleRet])->AddRef(); m_pNodeCurr = m_pNodeCurr->pNext; if (NULL == m_pNodeCurr) { cpIRuleRet++; break; } } // Set outgoing params if (NULL != pcpIRuleFetched) { *pcpIRuleFetched = cpIRuleRet; } // Set return value hr = (cpIRuleRet == cpIRule) ? S_OK : S_FALSE; exit: return hr; } STDMETHODIMP CEnumRules::Skip(ULONG cpIRule) { HRESULT hr = S_OK; ULONG cpIRuleWalk = 0; for (cpIRuleWalk = 0; cpIRuleWalk < cpIRule; cpIRuleWalk++) { if (NULL == m_pNodeCurr) { break; } m_pNodeCurr = m_pNodeCurr->pNext; } hr = (cpIRuleWalk == cpIRule) ? S_OK : S_FALSE; return hr; } STDMETHODIMP CEnumRules::Reset(void) { HRESULT hr = S_OK; m_pNodeCurr = m_pNodeHead; return hr; } STDMETHODIMP CEnumRules::Clone(IOEEnumRules ** ppIEnumRules) { HRESULT hr = S_OK; CEnumRules * pEnumRules = NULL; // Check incoming params if (NULL == ppIEnumRules) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing params *ppIEnumRules = NULL; pEnumRules = new CEnumRules; if (NULL == pEnumRules) { hr = E_OUTOFMEMORY; goto exit; } // Initialize the rules enumerator hr = pEnumRules->_HrInitialize(m_dwFlags, m_typeRule, m_pNodeHead); if (FAILED(hr)) { goto exit; } // Set the state of the new one to match the current one pEnumRules->m_pNodeCurr = m_pNodeHead; // Get the rules enumerator interface hr = pEnumRules->QueryInterface(IID_IOEEnumRules, (void **) ppIEnumRules); if (FAILED(hr)) { goto exit; } pEnumRules = NULL; hr = S_OK; exit: if (NULL != pEnumRules) { delete pEnumRules; } return hr; } HRESULT CEnumRules::_HrInitialize(DWORD dwFlags, RULE_TYPE typeRule, RULENODE * pNodeHead) { HRESULT hr = S_OK; RULENODE * pNodeNew = NULL; RULENODE * pNodeWalk = NULL; if (NULL == pNodeHead) { hr = S_FALSE; goto exit; } m_dwFlags = dwFlags; m_typeRule = typeRule; for (pNodeWalk = m_pNodeHead; NULL != pNodeHead; pNodeHead = pNodeHead->pNext) { // Check to see if we should add this item if (RULE_TYPE_FILTER == m_typeRule) { if (0 != (dwFlags & ENUMF_POP3)) { if (RULEID_VIEW_DOWNLOADED == pNodeHead->ridRule) { continue; } } } pNodeNew = new RULENODE; if (NULL == pNodeNew) { hr = E_OUTOFMEMORY; goto exit; } // Initialize new node pNodeNew->pNext = NULL; pNodeNew->pIRule = pNodeHead->pIRule; pNodeNew->pIRule->AddRef(); // Add the new node to the list if (NULL == pNodeWalk) { m_pNodeHead = pNodeNew; pNodeWalk = pNodeNew; } else { pNodeWalk->pNext = pNodeNew; pNodeWalk = pNodeNew; } pNodeNew = NULL; } // Set the current to the front of the chain m_pNodeCurr = m_pNodeHead; // Set return value hr = S_OK; exit: if (pNodeNew) delete pNodeNew; return hr; } // The Rule Executor object CExecRules::~CExecRules() { RULENODE * pNodeNext = NULL; AssertSz(m_cRef == 0, "Somebody still has a hold of us!!"); // Walk the list and free each item while (NULL != m_pNodeHead) { // Save off the next item pNodeNext = m_pNodeHead->pNext; // Release the rule AssertSz(NULL != m_pNodeHead->pIRule, "Where the heck is the rule???"); m_pNodeHead->pIRule->Release(); // Free up the node delete m_pNodeHead; // Move to the next item m_pNodeHead = pNodeNext; } // Free up the cached objects _HrReleaseFolderObjects(); _HrReleaseFileObjects(); _HrReleaseSoundFiles(); // Free the folder list SafeMemFree(m_pRuleFolder); m_cRuleFolderAlloc = 0; // Free the file list SafeMemFree(m_pRuleFile); m_cRuleFileAlloc = 0; // Free the file list SafeMemFree(m_ppszSndFile); m_cpszSndFileAlloc = 0; } STDMETHODIMP_(ULONG) CExecRules::AddRef() { return ::InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CExecRules::Release() { LONG cRef = 0; cRef = ::InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return cRef; } return cRef; } STDMETHODIMP CExecRules::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_IOEExecRules)) { *ppvObject = static_cast(this); } else { hr = E_NOINTERFACE; goto exit; } reinterpret_cast(*ppvObject)->AddRef(); hr = S_OK; exit: return hr; } STDMETHODIMP CExecRules::GetState(DWORD * pdwState) { HRESULT hr = S_OK; // Check incoming params if (NULL == pdwState) { hr = E_INVALIDARG; goto exit; } *pdwState = m_dwState; hr = S_OK; exit: return hr; } STDMETHODIMP CExecRules::ExecuteRules(DWORD dwFlags, LPCSTR pszAcct, MESSAGEINFO * pMsgInfo, IMessageFolder * pFolder, IMimePropertySet * pIMPropSet, IMimeMessage * pIMMsg, ULONG cbMsgSize, ACT_ITEM ** ppActions, ULONG * pcActions) { HRESULT hr = S_OK; ULONG ulIndex = 0; RULENODE * pNodeWalk = NULL; ACT_ITEM * pActions = NULL; ULONG cActions = 0; ACT_ITEM * pActionsList = NULL; ULONG cActionsList = 0; ACT_ITEM * pActionsNew = NULL; ULONG cActionsNew = 0; BOOL fStopProcessing = FALSE; BOOL fMatch = FALSE; DWORD dwState = 0; // Check incoming params if (((NULL == pMsgInfo) && (NULL == pIMPropSet)) || (0 == cbMsgSize) || (NULL == ppActions) || (NULL == pcActions)) { hr = E_INVALIDARG; goto exit; } // Initialize outgoing param *ppActions = NULL; *pcActions = 0; // Should we skip partial messages? if ((NULL != pIMPropSet) && (S_OK == pIMPropSet->IsContentType(STR_CNT_MESSAGE, STR_SUB_PARTIAL)) && (0 != (dwFlags & ERF_SKIPPARTIALS))) { hr = S_FALSE; goto exit; } // Walk the list of rules executing each one pNodeWalk = m_pNodeHead; while (NULL != pNodeWalk) { Assert(NULL != pNodeWalk->pIRule); // If we are only checking server rules // Bail if we need more information... if (0 != (dwFlags & ERF_ONLYSERVER)) { hr = pNodeWalk->pIRule->GetState(&dwState); if (FAILED(hr)) { goto exit; } // Do we need more information... if (0 != (dwState & CRIT_STATE_ALL)) { hr = S_FALSE; break; } } // Evaluate the rule hr = pNodeWalk->pIRule->Evaluate(pszAcct, pMsgInfo, pFolder, pIMPropSet, pIMMsg, cbMsgSize, &pActions, &cActions); if (FAILED(hr)) { goto exit; } // Did we have a match if (S_OK == hr) { // We've matched at least once fMatch = TRUE; ulIndex = 0; // If these are server actions if ((1 == cActions) && ((ACT_TYPE_DELETESERVER == pActions[ulIndex].type) || (ACT_TYPE_DONTDOWNLOAD == pActions[ulIndex].type))) { // If this is our only action if (0 == cActionsList) { // Save the action pActionsList = pActions; pActions = NULL; cActionsList = cActions; // We are done fStopProcessing = TRUE; } else { // We already have to do something with it // so skip over this action RuleUtil_HrFreeActionsItem(pActions, cActions); SafeMemFree(pActions); // Move to the next rule pNodeWalk = pNodeWalk->pNext; continue; } } else { // Should we stop after merging these? for (ulIndex = 0; ulIndex < cActions; ulIndex++) { if (ACT_TYPE_STOP == pActions[ulIndex].type) { fStopProcessing = TRUE; break; } } // Merge these items with the previous ones hr = RuleUtil_HrMergeActions(pActionsList, cActionsList, pActions, cActions, &pActionsNew, &cActionsNew); if (FAILED(hr)) { goto exit; } // Free up the previous ones RuleUtil_HrFreeActionsItem(pActionsList, cActionsList); SafeMemFree(pActionsList); RuleUtil_HrFreeActionsItem(pActions, cActions); SafeMemFree(pActions); // Save off the new ones pActionsList = pActionsNew; pActionsNew = NULL; cActionsList = cActionsNew; } // Should we continue... if (FALSE != fStopProcessing) { break; } } // Move to the next rule pNodeWalk = pNodeWalk->pNext; } // Set outgoing param *ppActions = pActionsList; pActionsList = NULL; *pcActions = cActionsList; // Set the return value hr = (FALSE != fMatch) ? S_OK : S_FALSE; exit: RuleUtil_HrFreeActionsItem(pActionsNew, cActionsNew); SafeMemFree(pActionsNew); RuleUtil_HrFreeActionsItem(pActions, cActions); SafeMemFree(pActions); RuleUtil_HrFreeActionsItem(pActionsList, cActionsList); SafeMemFree(pActionsList); return hr; } STDMETHODIMP CExecRules::ReleaseObjects(VOID) { // Free up the folders _HrReleaseFolderObjects(); // Free up the files _HrReleaseFileObjects(); // Free up the sound files _HrReleaseSoundFiles(); return S_OK; } STDMETHODIMP CExecRules::GetRuleFolder(FOLDERID idFolder, DWORD_PTR * pdwFolder) { HRESULT hr = S_OK; ULONG ulIndex = 0; RULE_FOLDER * pRuleFolderWalk = NULL; IMessageFolder *pFolder = NULL; // Check incoming param if ((FOLDERID_INVALID == idFolder) || (NULL == pdwFolder)) { hr =E_INVALIDARG; goto exit; } // Initialize outgoing param *pdwFolder = NULL; // Let's search for the folder for (ulIndex = 0; ulIndex < m_cRuleFolder; ulIndex++) { pRuleFolderWalk = &(m_pRuleFolder[ulIndex]); if (idFolder == pRuleFolderWalk->idFolder) { Assert(NULL != pRuleFolderWalk->pFolder); break; } } // If we didn't find it then let's open it and lock it... if (ulIndex >= m_cRuleFolder) { // Do we need to alloc any more spaces if (m_cRuleFolder >= m_cRuleFolderAlloc) { hr = HrRealloc((LPVOID *) &m_pRuleFolder, sizeof(*m_pRuleFolder) * (m_cRuleFolderAlloc + RULE_FOLDER_ALLOC)); if (FAILED(hr)) { goto exit; } // Initialize the new rule folders ZeroMemory(m_pRuleFolder + m_cRuleFolderAlloc, sizeof(*m_pRuleFolder) * RULE_FOLDER_ALLOC); for (ulIndex = m_cRuleFolderAlloc; ulIndex < (m_cRuleFolderAlloc + RULE_FOLDER_ALLOC); ulIndex++) { m_pRuleFolder[ulIndex].idFolder = FOLDERID_INVALID; } m_cRuleFolderAlloc += RULE_FOLDER_ALLOC; } // Open the folder hr = g_pStore->OpenFolder(idFolder, NULL, NOFLAGS, &pFolder); if (FAILED(hr)) { goto exit; } m_pRuleFolder[m_cRuleFolder].idFolder = idFolder; m_pRuleFolder[m_cRuleFolder].pFolder = pFolder; pFolder = NULL; pRuleFolderWalk = &(m_pRuleFolder[m_cRuleFolder]); m_cRuleFolder++; } *pdwFolder = (DWORD_PTR) (pRuleFolderWalk->pFolder); // Set the proper return value hr = S_OK; exit: if (NULL != pFolder) pFolder->Release(); return hr; } STDMETHODIMP CExecRules::GetRuleFile(LPCSTR pszFile, IStream ** ppstmFile, DWORD * pdwType) { HRESULT hr = S_OK; ULONG ulIndex = 0; RULE_FILE * pRuleFileWalk = NULL; IStream * pIStmFile = NULL; LPSTR pszExt = NULL; DWORD dwType = RFT_FILE; // Check incoming param if ((NULL == pszFile) || (NULL == ppstmFile) || (NULL == pdwType)) { hr =E_INVALIDARG; goto exit; } // Initialize outgoing param *ppstmFile = NULL; *pdwType = NULL; // Let's search for the file for (ulIndex = 0; ulIndex < m_cRuleFile; ulIndex++) { pRuleFileWalk = &(m_pRuleFile[ulIndex]); if (0 == lstrcmpi(pRuleFileWalk->pszFile, pszFile)) { Assert(NULL != pRuleFileWalk->pstmFile); break; } } // If we didn't find it then let's open it... if (ulIndex >= m_cRuleFile) { // Do we need to alloc any more space if (m_cRuleFile >= m_cRuleFileAlloc) { hr = HrRealloc((LPVOID *) &m_pRuleFile, sizeof(*m_pRuleFile) * (m_cRuleFileAlloc + RULE_FILE_ALLOC)); if (FAILED(hr)) { goto exit; } // Initialize the new rule file ZeroMemory(m_pRuleFile + m_cRuleFileAlloc, sizeof(*m_pRuleFile) * RULE_FILE_ALLOC); m_cRuleFileAlloc += RULE_FILE_ALLOC; } // Open a stream on the file hr = CreateStreamOnHFile((LPTSTR) pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL, &pIStmFile); if (FAILED(hr)) { goto exit; } // Lets split the file name and get the extension pszExt = PathFindExtension(pszFile); if ((0 == lstrcmpi(pszExt, c_szHtmExt)) || (0 == lstrcmpi(pszExt, c_szHtmlExt))) { dwType = RFT_HTML; } // Text File... else if (0 == lstrcmpi(pszExt, c_szTxtExt)) { dwType = RFT_TEXT; } // Else .nws or .eml file... else if ((0 == lstrcmpi(pszExt, c_szEmlExt)) || (0 == lstrcmpi(pszExt, c_szNwsExt))) { dwType = RFT_MESSAGE; } // Otherwise, its an attachment else { dwType = RFT_FILE; } // Save off the info m_pRuleFile[m_cRuleFile].pszFile = PszDupA(pszFile); if (NULL == m_pRuleFile[m_cRuleFile].pszFile) { hr = E_OUTOFMEMORY; goto exit; } m_pRuleFile[m_cRuleFile].pstmFile = pIStmFile; pIStmFile = NULL; m_pRuleFile[m_cRuleFile].dwType = dwType; pRuleFileWalk = &(m_pRuleFile[m_cRuleFile]); m_cRuleFile++; } *ppstmFile = pRuleFileWalk->pstmFile; (*ppstmFile)->AddRef(); *pdwType = pRuleFileWalk->dwType; // Set the proper return value hr = S_OK; exit: SafeRelease(pIStmFile); return hr; } STDMETHODIMP CExecRules::AddSoundFile(DWORD dwFlags, LPCSTR pszSndFile) { HRESULT hr = S_OK; ULONG ulIndex = 0; // Check incoming param if (NULL == pszSndFile) { hr = E_INVALIDARG; goto exit; } // Let's search for the file for (ulIndex = 0; ulIndex < m_cpszSndFile; ulIndex++) { Assert(NULL != m_ppszSndFile[ulIndex]); if (0 == lstrcmpi(m_ppszSndFile[ulIndex], pszSndFile)) { break; } } // If we didn't find it then let's open it... if (ulIndex >= m_cpszSndFile) { // Do we need to alloc any more space if (m_cpszSndFile >= m_cpszSndFileAlloc) { hr = HrRealloc((LPVOID *) &m_ppszSndFile, sizeof(*m_ppszSndFile) * (m_cpszSndFileAlloc + SND_FILE_ALLOC)); if (FAILED(hr)) { goto exit; } // Initialize the new rule file ZeroMemory(m_ppszSndFile + m_cpszSndFileAlloc, sizeof(*m_ppszSndFile) * SND_FILE_ALLOC); m_cpszSndFileAlloc += SND_FILE_ALLOC; } // Save off the info m_ppszSndFile[m_cpszSndFile] = PszDupA(pszSndFile); if (NULL == m_ppszSndFile[m_cpszSndFile]) { hr = E_OUTOFMEMORY; goto exit; } // Should we play it? if (0 != (dwFlags & ASF_PLAYIFNEW)) { sndPlaySound(m_ppszSndFile[m_cpszSndFile], SND_NODEFAULT | SND_SYNC); } m_cpszSndFile++; } // Set the proper return value hr = S_OK; exit: return hr; } STDMETHODIMP CExecRules::PlaySounds(DWORD dwFlags) { HRESULT hr = S_OK; ULONG ulIndex = 0; // Let's search for the file for (ulIndex = 0; ulIndex < m_cpszSndFile; ulIndex++) { Assert(NULL != m_ppszSndFile[ulIndex]); sndPlaySound(m_ppszSndFile[ulIndex], SND_NODEFAULT | SND_SYNC); } return hr; } HRESULT CExecRules::_HrReleaseFolderObjects(VOID) { RULE_FOLDER * pRuleFolder = NULL; ULONG ulIndex = 0; for (ulIndex = 0; ulIndex < m_cRuleFolder; ulIndex++) { pRuleFolder = &(m_pRuleFolder[ulIndex]); Assert(FOLDERID_INVALID != pRuleFolder->idFolder); // If we have the folder opened then close it SafeRelease(pRuleFolder->pFolder); // Reset the folder list pRuleFolder->idFolder = FOLDERID_INVALID; } // Let's clear out the number of messages m_cRuleFolder = 0; return S_OK; } HRESULT CExecRules::_HrReleaseFileObjects(VOID) { RULE_FILE * pRuleFile = NULL; ULONG ulIndex = 0; for (ulIndex = 0; ulIndex < m_cRuleFile; ulIndex++) { pRuleFile = &(m_pRuleFile[ulIndex]); Assert(NULL != pRuleFile->pszFile); // If we have the file opened then close it SafeRelease(pRuleFile->pstmFile); // Clear the file SafeMemFree(pRuleFile->pszFile); pRuleFile->dwType = RFT_FILE; } // Let's clear out the number of file m_cRuleFile = 0; return S_OK; } HRESULT CExecRules::_HrReleaseSoundFiles(VOID) { ULONG ulIndex = 0; for (ulIndex = 0; ulIndex < m_cpszSndFile; ulIndex++) { Assert(NULL != m_ppszSndFile[ulIndex]); // Clear the file SafeMemFree(m_ppszSndFile[ulIndex]); } // Let's clear out the number of file m_cpszSndFile = 0; return S_OK; } HRESULT CExecRules::_HrInitialize(DWORD dwFlags, RULENODE * pNodeHead) { HRESULT hr = S_OK; RULENODE * pNodeNew = NULL; RULENODE * pNodeWalk = NULL; DWORD dwState = 0; PROPVARIANT propvar; if (NULL == pNodeHead) { hr = S_FALSE; goto exit; } for (pNodeWalk = m_pNodeHead; NULL != pNodeHead; pNodeHead = pNodeHead->pNext) { // Skip rules that are disabled if (0 != (dwFlags & ERF_ONLY_ENABLED)) { hr = pNodeHead->pIRule->GetProp(RULE_PROP_DISABLED, 0, &propvar); Assert(VT_BOOL == propvar.vt); if (FAILED(hr) || (FALSE != propvar.boolVal)) { continue; } } // Skip rules that are invalid if (0 != (dwFlags & ERF_ONLY_VALID)) { hr = pNodeHead->pIRule->Validate(dwFlags); if (FAILED(hr) || (S_FALSE == hr)) { continue; } } pNodeNew = new RULENODE; if (NULL == pNodeNew) { hr = E_OUTOFMEMORY; goto exit; } // Initialize new node pNodeNew->pNext = NULL; pNodeNew->pIRule = pNodeHead->pIRule; pNodeNew->pIRule->AddRef(); // Add the new node to the list if (NULL == pNodeWalk) { m_pNodeHead = pNodeNew; pNodeWalk = pNodeNew; } else { pNodeWalk->pNext = pNodeNew; pNodeWalk = pNodeNew; } pNodeNew = NULL; // Calculate state from message if (SUCCEEDED(pNodeWalk->pIRule->GetState(&dwState))) { // Let's set the proper Criteria state if ((m_dwState & CRIT_STATE_MASK) < (dwState & CRIT_STATE_MASK)) { m_dwState = (m_dwState & ~CRIT_STATE_MASK) | (dwState & CRIT_STATE_MASK); } // Let's set the proper Action state if (0 != (dwState & ACT_STATE_MASK)) { m_dwState |= (dwState & ACT_STATE_MASK); } } } // Set return value hr = S_OK; exit: if (pNodeNew) delete pNodeNew; return hr; }