#include "private.h" #include "subsmgrp.h" #include "offl_cpp.h" #include "helper.h" #include "propshts.h" #include "apithk.h" #include #undef TF_THISMODULE #define TF_THISMODULE TF_WEBCHECKCORE extern HRESULT LoadSubscription(LPCTSTR url, LPMYPIDL *); extern TCHAR szInternetSettings[]; extern void PropagateGeneralProp(HWND, POOEBuf); extern INT_PTR CALLBACK EnableScreenSaverDlgProc(HWND, UINT, WPARAM, LPARAM); extern HRESULT CreateSubscriptionFromOOEBuf(POOEBuf, LPMYPIDL *); extern BOOL CALLBACK _AddOnePropSheetPage(HPROPSHEETPAGE, LPARAM); extern TCHAR g_szDontAskScreenSaver[]; // from WIZARDS.CPP #define MAX_STR_LENGTH 200 extern DWORD aHelpIDs[]; extern TCHAR c_szHelpFile[]; typedef struct { CSubscriptionMgr* pMgr; LPCWSTR pwszName; LPCWSTR pwszUrl; SUBSCRIPTIONINFO* pSubsInfo; SUBSCRIPTIONTYPE subsType; DWORD dwFlags; } SUBSCRIBE_ADI_INFO; static const TCHAR SUBSCRIBEADIPROP[] = TEXT("SADIP"); INT_PTR CALLBACK SummarizeDesktopSubscriptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); //////////////////////////////////////////////////////////////////////////////// // Private imports from shdocvw (AddToFavorites API) // STDAPI SHAddSubscribeFavorite (HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO* pInfo); //////////////////////////////////////////////////////////////////////////////// void UpdateSubsInfoFromOOE (SUBSCRIPTIONINFO* pInfo, POOEBuf pooe); HRESULT CreateBSTRFromTSTR(BSTR * pBstr, LPCTSTR sz) { int i = lstrlen(sz) + 1; *pBstr = SysAllocStringLen(NULL, i); if(NULL == *pBstr) return E_OUTOFMEMORY; MyStrToOleStrN(*pBstr, i, sz); return S_OK; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // Subsctiption Manager // ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // constructor / destructor // CSubscriptionMgr::CSubscriptionMgr(void) { ASSERT(NULL == _pidl); m_cRef = 1; m_eInitSrc = _INIT_FROM_URL; // Default. m_oldType = SUBSTYPE_URL; // Default. DllAddRef(); } CSubscriptionMgr::~CSubscriptionMgr() { if (_pidl) { COfflineFolderEnum::FreePidl(_pidl); _pidl = NULL; } SAFELOCALFREE(m_pBuf); SAFERELEASE(m_pUIHelper); DllRelease(); } // // IUnknown members // STDMETHODIMP CSubscriptionMgr::QueryInterface(REFIID riid, void ** ppv) { *ppv=NULL; // Validate requested interface if ((IID_IUnknown == riid) || (IID_ISubscriptionMgr == riid) || (IID_ISubscriptionMgr2 == riid)) { *ppv=(ISubscriptionMgr2 *)this; } else if(IID_ISubscriptionMgrPriv == riid) { *ppv=(ISubscriptionMgrPriv *)this; } else if(IID_IShellExtInit == riid) { *ppv=(IShellExtInit *)this; } else if(IID_IShellPropSheetExt == riid) { *ppv=(IShellPropSheetExt *)this; } else { return E_NOINTERFACE; } ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CSubscriptionMgr::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) CSubscriptionMgr::Release(void) { if( 0L != --m_cRef ) return m_cRef; delete this; return 0L; } HRESULT CSubscriptionMgr::RemovePages(HWND hDlg) { HRESULT hr; ASSERT(NULL != m_pUIHelper); if (NULL == m_pUIHelper) { return E_UNEXPECTED; } ISubscriptionAgentShellExt *psase; hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase); if (SUCCEEDED(hr)) { hr = psase->RemovePages(hDlg); psase->Release(); } return hr; } HRESULT CSubscriptionMgr::SaveSubscription() { HRESULT hr; ASSERT(NULL != m_pUIHelper); if (NULL == m_pUIHelper) { return E_UNEXPECTED; } DWORD dwMaxCount = SHRestricted2W(REST_MaxSubscriptionCount, NULL, 0); DWORD dwCount; SUBSCRIPTIONTYPE subsType = (m_eInitSrc == _INIT_FROM_INTSHCUT) ? SUBSTYPE_URL : SUBSTYPE_CHANNEL; if ((dwMaxCount > 0) && SUCCEEDED(CountSubscriptions(subsType, &dwCount)) && (dwCount >= dwMaxCount)) { SGMessageBox(GetForegroundWindow(), IDS_RESTRICTED, MB_OK); return E_ACCESSDENIED; } ISubscriptionAgentShellExt *psase; hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase); if (SUCCEEDED(hr)) { hr = psase->SaveSubscription(); psase->Release(); } return hr; } HRESULT CSubscriptionMgr::URLChange(LPCWSTR pwszNewURL) { return E_NOTIMPL; } HRESULT GetInfoFromDataObject(IDataObject *pido, TCHAR *pszPath, DWORD cchPath, TCHAR *pszFriendlyName, DWORD cchFriendlyName, TCHAR *pszURL, DWORD cchURL, INIT_SRC_ENUM *peInitSrc) { STGMEDIUM stgmed; FORMATETC fmtetc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; HRESULT hr = pido->GetData(&fmtetc, &stgmed); if (hr == S_OK) { TCHAR szTempURL[INTERNET_MAX_URL_LENGTH]; TCHAR *pszSection; TCHAR *pszEntry; TCHAR szTempPath[MAX_PATH]; TCHAR szIniPath[MAX_PATH]; if (DragQueryFile((HDROP)stgmed.hGlobal, 0, szTempPath, ARRAYSIZE(szTempPath))) { // save path if (NULL != pszPath) { StrCpyN(pszPath, szTempPath, cchPath); } StrCpyN(szIniPath, szTempPath, ARRAYSIZE(szIniPath)); // make friendly name from path if (NULL != pszFriendlyName) { PathStripPath(szTempPath); PathRemoveExtension(szTempPath); StrCpyN(pszFriendlyName, szTempPath, cchFriendlyName); } if ((NULL != pszURL) || (NULL != peInitSrc)) { if (PathIsDirectory(szIniPath)) { PathAppend(szIniPath, TEXT("desktop.ini")); pszSection = TEXT("Channel"); pszEntry = TEXT("CDFURL"); if (NULL != peInitSrc) *peInitSrc = _INIT_FROM_CHANNEL; } else { pszSection = TEXT("InternetShortcut"); pszEntry = TEXT("URL"); if (NULL != peInitSrc) *peInitSrc = _INIT_FROM_INTSHCUT; } if (NULL != pszURL) { // canonicalize url if (SHGetIniString(pszSection, pszEntry, szTempURL, INTERNET_MAX_URL_LENGTH, szIniPath)) { if(!InternetCanonicalizeUrl(szTempURL, pszURL, &cchURL, ICU_NO_ENCODE)) { // failed - use non-canonical version StrCpyN(pszURL, szTempURL, cchURL); } } else { hr = E_FAIL; } } } } else { hr = E_FAIL; } ReleaseStgMedium(&stgmed); } return hr; } // // IShellExtInit / IShellPropSheetExt members // STDMETHODIMP CSubscriptionMgr::Initialize(LPCITEMIDLIST pcidlFolder, IDataObject * pido, HKEY hkeyProgID) { HRESULT hr; ISubscriptionItem *psi; CLSID clsid; SUBSCRIPTIONCOOKIE cookie; hr = GetInfoFromDataObject(pido, m_pszPath, ARRAYSIZE(m_pszPath), m_pszFriendly, ARRAYSIZE(m_pszFriendly), m_pszURL, ARRAYSIZE(m_pszURL), &m_eInitSrc); if (SUCCEEDED(hr)) { hr = DoGetItemFromURL(m_pszURL, &psi); if (SUCCEEDED(hr)) { SUBSCRIPTIONITEMINFO sii; sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO); hr = psi->GetSubscriptionItemInfo(&sii); if (SUCCEEDED(hr)) { clsid = sii.clsidAgent; } psi->GetCookie(&cookie); psi->Release(); } if (FAILED(hr)) { // New subscription hr = S_OK; CreateCookie(&cookie); switch (m_eInitSrc) { case _INIT_FROM_INTSHCUT: clsid = CLSID_WebCrawlerAgent; break; case _INIT_FROM_CHANNEL: clsid = CLSID_ChannelAgent; break; default: hr = E_FAIL; break; } } if (SUCCEEDED(hr)) { // HACKHACK: // We call coinit and uninit and keep an object pointer around. // This ain't cool but will work as long as the agents are in // webcheck. Need to fix this for multi-cast handler. hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pUIHelper); if (SUCCEEDED(hr)) { ISubscriptionAgentShellExt *psase; hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase); if (SUCCEEDED(hr)) { WCHAR wszURL[ARRAYSIZE(m_pszURL)]; WCHAR wszName[MAX_NAME + 1]; MyStrToOleStrN(wszURL, ARRAYSIZE(wszURL), m_pszURL); MyStrToOleStrN(wszName, ARRAYSIZE(wszName), m_pszFriendly); hr = psase->Initialize(&cookie, wszURL, wszName, (clsid == CLSID_ChannelAgent) ? SUBSTYPE_CHANNEL : SUBSTYPE_URL); psase->Release(); } } CoUninitialize(); } } } return hr; } STDMETHODIMP CSubscriptionMgr::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) { HRESULT hr; if (SHRestricted2W(REST_NoEditingSubscriptions, NULL, 0)) return E_FAIL; ASSERT(NULL != m_pUIHelper); if (NULL == m_pUIHelper) { return E_UNEXPECTED; } IShellPropSheetExt *pspse; hr = m_pUIHelper->QueryInterface(IID_IShellPropSheetExt, (void **)&pspse); if (SUCCEEDED(hr)) { hr = pspse->AddPages(lpfnAddPage, lParam); pspse->Release(); } return hr; } STDMETHODIMP CSubscriptionMgr::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplacePage, LPARAM lParam) { return E_NOTIMPL; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // ISubscriptionMgr members // STDMETHODIMP CSubscriptionMgr::IsSubscribed(LPCWSTR pURL, BOOL * pFSub) { HRESULT hr; ASSERT (pURL && pFSub); MyOleStrToStrN(m_pszURL, INTERNET_MAX_URL_LENGTH, pURL); * pFSub = FALSE; if (!_pidl) { hr = LoadSubscription(m_pszURL, &_pidl); if ((hr != S_OK) || (!_pidl)) return S_OK; } else if (UrlCompare(URL(&(_pidl->ooe)), m_pszURL, TRUE)) { COfflineFolderEnum::FreePidl(_pidl); _pidl = NULL; hr = LoadSubscription(m_pszURL, &_pidl); if ((hr != S_OK) || (!_pidl)) return S_OK; } * pFSub = TRUE; return S_OK; } STDMETHODIMP CSubscriptionMgr::DeleteSubscription(LPCWSTR pURL, HWND hwnd) { ASSERT(pURL); MyOleStrToStrN(m_pszURL, INTERNET_MAX_URL_LENGTH, pURL); if (!_pidl) { HRESULT hr; hr = LoadSubscription(m_pszURL, &_pidl); if ((hr != S_OK) || (!_pidl)) return E_FAIL; } // This is a restricted action. The restriction // is checked in ConfirmDelete. If you remove this call, // you must add the restriction check here. if (!ConfirmDelete(hwnd, 1, &_pidl)) return E_FAIL; HRESULT hr = DoDeleteSubscription(&(_pidl->ooe)); if (SUCCEEDED(hr)) { TraceMsg(TF_ALWAYS, "%s(URL:%s) deleted", NAME(&(_pidl->ooe)), URL(&(_pidl->ooe))); _GenerateEvent(SHCNE_DELETE, (LPITEMIDLIST)_pidl, NULL); COfflineFolderEnum::FreePidl(_pidl); _pidl = NULL; } return hr; } STDMETHODIMP CSubscriptionMgr::ShowSubscriptionProperties(LPCWSTR pURL, HWND hwnd) { HRESULT hr = S_OK; LPMYPIDL oldPidl = NULL, newPidl = NULL; ASSERT(pURL); MyOleStrToStrN(m_pszURL, INTERNET_MAX_URL_LENGTH, pURL); if (!m_pBuf) { m_pBuf = (OOEBuf *)MemAlloc(LPTR, sizeof(OOEBuf)); if (NULL == m_pBuf) { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr)) { GetDefaultOOEBuf(m_pBuf, SUBSTYPE_URL); if(SUCCEEDED(LoadSubscription(m_pszURL, &oldPidl))) { POOEntry pooe = &(oldPidl->ooe); StrCpyN(m_pszFriendly, NAME(&(oldPidl->ooe)), ARRAYSIZE(m_pszFriendly)); CopyToOOEBuf(&(oldPidl->ooe), m_pBuf); m_pBuf->m_dwPropSheetFlags = PSF_IS_ALREADY_SUBSCRIBED; } else { CreateCookie(&m_pBuf->m_Cookie); StrCpyN(m_pszFriendly, m_pszURL, ARRAYSIZE(m_pszFriendly)); StrCpyN(m_pBuf->m_URL, m_pszURL, ARRAYSIZE(m_pBuf->m_URL)); StrCpyN(m_pBuf->m_Name, m_pszURL, ARRAYSIZE(m_pBuf->m_Name)); } hr = CoCreateInstance(*(&m_pBuf->clsidDest), NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&m_pUIHelper); if (SUCCEEDED(hr)) { ISubscriptionAgentShellExt *psase; hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase); if (SUCCEEDED(hr)) { WCHAR wszURL[MAX_URL + 1]; WCHAR wszName[MAX_NAME + 1]; MyStrToOleStrN(wszURL, ARRAYSIZE(wszURL), m_pBuf->m_URL); MyStrToOleStrN(wszName, ARRAYSIZE(wszName), m_pBuf->m_Name); hr = psase->Initialize(&m_pBuf->m_Cookie, wszURL, wszName, (SUBSCRIPTIONTYPE)-1); psase->Release(); } if (SUCCEEDED(hr)) { PROPSHEETHEADER psh = { 0 } ; HPROPSHEETPAGE hPropPage[MAX_PROP_PAGES]; // initialize propsheet header. psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_PROPTITLE; psh.hwndParent = hwnd; psh.pszCaption = m_pszFriendly; psh.hInstance = g_hInst; psh.nPages = 0; psh.nStartPage = 0; psh.phpage = hPropPage; PROPSHEETPAGE psp; psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_DEFAULT; psp.hInstance = MLGetHinst(); psp.pszIcon = NULL; psp.pszTitle = NULL; psp.lParam = (LPARAM)this; psp.pszTemplate = MAKEINTRESOURCE(IDD_SUBSPROPS_SUMMARY); psp.pfnDlgProc = SummaryPropDlgProc; psh.phpage[psh.nPages++] = Whistler_CreatePropertySheetPageW(&psp); if (NULL != hPropPage[0]) { if (m_pBuf->m_dwPropSheetFlags & PSF_IS_ALREADY_SUBSCRIBED) { hr = AddPages(_AddOnePropSheetPage, (LPARAM)&psh); } if (SUCCEEDED(hr)) { INT_PTR iRet = PropertySheet(&psh); if (iRet < 0) { hr = E_FAIL; } else { hr = LoadSubscription(m_pszURL, &newPidl); if (SUCCEEDED(hr)) { if (_pidl) { COfflineFolderEnum::FreePidl(_pidl); } _pidl = newPidl; } } } } } } if (NULL != oldPidl) { COfflineFolderEnum::FreePidl(oldPidl); } } return hr; } // // // void CSubscriptionMgr::ChangeSubscriptionValues ( OOEBuf *pCurrent, SUBSCRIPTIONINFO *pNew ) { // // Channel flags // if (SUBSINFO_CHANNELFLAGS & pNew->fUpdateFlags) { pCurrent->fChannelFlags = pNew->fChannelFlags; } // // The subscription schedule. // if (SUBSINFO_SCHEDULE & pNew->fUpdateFlags) { switch (pNew->schedule) { case SUBSSCHED_DAILY: case SUBSSCHED_MANUAL: case SUBSSCHED_WEEKLY: LoadGroupCookie(&pCurrent->groupCookie, pNew->schedule); break; case SUBSSCHED_CUSTOM: pCurrent->groupCookie = pNew->customGroupCookie; break; case SUBSSCHED_AUTO: { // FEATURE. We should look at subType; memset(&pCurrent->groupCookie, 0, sizeof(pCurrent->groupCookie)); //t-mattgi so it will look at the trigger PTASK_TRIGGER pNewTrigger = ((PTASK_TRIGGER)pNew->pTrigger); if (pNewTrigger && pNewTrigger->cbTriggerSize == sizeof(TASK_TRIGGER)) { pCurrent->m_Trigger = *pNewTrigger; } else //bad trigger; use daily as default { pCurrent->m_Trigger.cbTriggerSize = 0; pCurrent->groupCookie = NOTFCOOKIE_SCHEDULE_GROUP_DAILY; } } pCurrent->fChannelFlags |= CHANNEL_AGENT_DYNAMIC_SCHEDULE; break; default: ASSERT(FALSE); break; } } // // Recurse levels. // if (SUBSINFO_RECURSE & pNew->fUpdateFlags) pCurrent->m_RecurseLevels = pNew->dwRecurseLevels; // // Webcrawler flags. Note: The flags are not or'd with the current flags. // The caller must set all of the webcrawler flags they want to use. // if (SUBSINFO_WEBCRAWL & pNew->fUpdateFlags) pCurrent->m_RecurseFlags = pNew->fWebcrawlerFlags; // // Mail notification. // if (SUBSINFO_MAILNOT & pNew->fUpdateFlags) pCurrent->bMail = pNew->bMailNotification; else pCurrent->bMail = FALSE; // // Need password. // if (SUBSINFO_NEEDPASSWORD & pNew->fUpdateFlags) pCurrent->bNeedPassword = pNew->bNeedPassword; else pCurrent->bNeedPassword = FALSE; // // User name. // if (SUBSINFO_USER & pNew->fUpdateFlags) { if (pNew->bstrUserName) { MyOleStrToStrN(pCurrent->username, MAX_USERNAME, pNew->bstrUserName); } pCurrent->bNeedPassword = pNew->bNeedPassword; } // // Password. // if (SUBSINFO_PASSWORD & pNew->fUpdateFlags) { if (pNew->bstrPassword) { MyOleStrToStrN(pCurrent->password, MAX_PASSWORD, pNew->bstrPassword); } pCurrent->bNeedPassword = pNew->bNeedPassword; } // // Friendly Name. // if (SUBSINFO_FRIENDLYNAME & pNew->fUpdateFlags) { if (pNew->bstrFriendlyName) { MyOleStrToStrN(pCurrent->m_Name, MAX_NAME, pNew->bstrFriendlyName); } } // // Gleam // if (SUBSINFO_GLEAM & pNew->fUpdateFlags) { pCurrent->bGleam = pNew->bGleam; } // // Changes only (notification only) // if (SUBSINFO_CHANGESONLY & pNew->fUpdateFlags) { pCurrent->bChangesOnly = pNew->bChangesOnly; } // // dwMaxSizeKB // if (SUBSINFO_MAXSIZEKB & pNew->fUpdateFlags) { pCurrent->m_SizeLimit = pNew->dwMaxSizeKB; } // // Task flags // if (SUBSINFO_TASKFLAGS & pNew->fUpdateFlags) { pCurrent->grfTaskTrigger = pNew->fTaskFlags; } return; } // // CSubscriptionMgr::CountSubscriptions // FEATURE: We could make this public if other people need it. An enumerator // would be more useful though... // HRESULT CSubscriptionMgr::CountSubscriptions(SUBSCRIPTIONTYPE subType, PDWORD pdwCount) { HRESULT hr; IEnumSubscription *pes; ASSERT(NULL != pdwCount); *pdwCount = 0; hr = EnumSubscriptions(0, &pes); if (SUCCEEDED(hr)) { SUBSCRIPTIONCOOKIE cookie; while (S_OK == pes->Next(1, &cookie, NULL)) { ISubscriptionItem *psi; DWORD dwRet; if (SUCCEEDED(SubscriptionItemFromCookie(FALSE, &cookie, &psi))) { if (SUCCEEDED(ReadDWORD(psi, c_szPropChannel, &dwRet)) && dwRet) { if (SUBSTYPE_CHANNEL == subType) (*pdwCount)++; } else if (SUCCEEDED(ReadDWORD(psi, c_szPropDesktopComponent, &dwRet)) && dwRet) { if (SUBSTYPE_DESKTOPURL == subType || SUBSTYPE_DESKTOPCHANNEL == subType) (*pdwCount)++; } else { if (SUBSTYPE_URL == subType) (*pdwCount)++; } psi->Release(); } } } return hr; } // // CSubscriptionMgr::IsValidSubscriptionInfo // #define SUBSCRIPTIONSCHEDULE_MAX 4 BOOL CSubscriptionMgr::IsValidSubscriptionInfo(SUBSCRIPTIONTYPE subType, SUBSCRIPTIONINFO *pSI) { if (pSI->cbSize != sizeof(SUBSCRIPTIONINFO)) { return FALSE; } else if (pSI->fUpdateFlags & ~SUBSINFO_ALLFLAGS) { return FALSE; } else if (pSI->pTrigger && ((TASK_TRIGGER*)(pSI->pTrigger))->cbTriggerSize && (subType == SUBSTYPE_URL || subType == SUBSTYPE_DESKTOPURL)) // || pSI->schedule != SUBSSCHED_AUTO)) { return FALSE; } else if (pSI->fUpdateFlags & SUBSINFO_SCHEDULE) { if (pSI->schedule > SUBSCRIPTIONSCHEDULE_MAX) { return FALSE; } if (pSI->schedule == SUBSSCHED_CUSTOM && pSI->customGroupCookie == CLSID_NULL) { return FALSE; } } return TRUE; } //////////////////////////////////////////////////////////////////////////////// // // *** RemoveURLMapping *** // // Description: // Removes the cache and registry settings that wininet uses to map the // given url to a local file. // //////////////////////////////////////////////////////////////////////////////// #define PRELOAD_REG_KEY \ TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Preload") void RemoveURLMapping(LPCWSTR pszURL) { BYTE cei[MY_MAX_CACHE_ENTRY_INFO]; DWORD cbcei = sizeof(cei); LPINTERNET_CACHE_ENTRY_INFOW pcei = (LPINTERNET_CACHE_ENTRY_INFOW)cei; // // Look up the url in the cache // if (GetUrlCacheEntryInfoExW(pszURL, pcei, &cbcei, NULL, 0, NULL, 0)) { // // see if it has a mapping because it is a preinstalled cache entry // if (pcei->CacheEntryType & INSTALLED_CACHE_ENTRY) { // // Clear the flag // pcei->CacheEntryType &= ~INSTALLED_CACHE_ENTRY; SetUrlCacheEntryInfoW(pszURL, pcei, CACHE_ENTRY_ATTRIBUTE_FC); // // Now remove the mapping from the registry // HKEY hk; if (RegOpenKeyEx(HKEY_CURRENT_USER, PRELOAD_REG_KEY, 0, KEY_WRITE, &hk) == ERROR_SUCCESS) { RegDeleteValueW(hk, pszURL); RegCloseKey(hk); } } } } // // CSubscriptionMgr::CreateSubscription // entry point for creating a subscription // flags: // CREATESUBS_FROMFAVORITES -- already exists in favorites, use alternate summary dialog // that doesn't do AddToFavorites. Valid only for channel or url. // CREATESUBS_INACTIVEPLATINUM -- when creating a channel subscription, show Activate Channel dialog // valid only for channel subscriptions with CREATESUBS_FROMFAVORITES // CREATESUBS_ADDTOFAVORITES -- display summary dialog before wizard // default summary: for channel or url, use AddToFavorites from shdocvw // for desktop item, just a confirmation dialog // for other, no summary -- straight to wizard // CREATESUBS_NOUI -- totally silent // CREATESUBS_NOSAVE -- update subscription in memory buffer, not on disk (pInfo must be non-NULL) // STDMETHODIMP CSubscriptionMgr::CreateSubscription ( HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszFriendlyName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO *pInfo ) { HRESULT hr = E_INVALIDARG; BOOL bAlready; if (IsFlagSet(dwFlags, CREATESUBS_NOUI) || !IsFlagSet (dwFlags, CREATESUBS_ADDTOFAVORITES)) { //no UI, so skip ATF dialog hr = CreateSubscriptionNoSummary (hwnd, pwszURL, pwszFriendlyName, dwFlags, subsType, pInfo); if (hr == S_OK) { // // The user successfully subscribed to this URL so remove // mappings used for preinstalled content as this URL is now // "Activated" // RemoveURLMapping(pwszURL); } } else { switch (subsType) { case SUBSTYPE_URL: case SUBSTYPE_CHANNEL: hr = SHAddSubscribeFavorite (hwnd, pwszURL, pwszFriendlyName, dwFlags, subsType, pInfo); break; case SUBSTYPE_DESKTOPCHANNEL: case SUBSTYPE_DESKTOPURL: hr = IsSubscribed (pwszURL, &bAlready); if (SUCCEEDED (hr) && bAlready) break; //don't display summary dialog since it has nothing useful for this case hr = CreateDesktopSubscription (hwnd, pwszURL, pwszFriendlyName, dwFlags, subsType, pInfo); break; default: //SUBSTYPE_EXTERNAL -- don't know what kind of summary to show hr = CreateSubscriptionNoSummary (hwnd, pwszURL, pwszFriendlyName, dwFlags, subsType, pInfo); break; } } return hr; } // // CSubscriptionMgr::CreateSubscriptionNoSummary // modify a SUBSCRIPTIONINFO interactively, using the wizard // persists info to Subscriptions folder, unless SUBSINFO_NOSAVE passed // STDMETHODIMP CSubscriptionMgr::CreateSubscriptionNoSummary ( HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszFriendlyName, DWORD dwFlags, SUBSCRIPTIONTYPE subType, SUBSCRIPTIONINFO *pInfo ) { HRESULT hr = S_OK; // // Validate the parameters. // if (!IS_VALID_SUBSCRIPTIONTYPE(subType) || !pwszURL || !pwszFriendlyName || (!pInfo && (dwFlags & CREATESUBS_NOSAVE)) || (pInfo && !IsValidSubscriptionInfo(subType, pInfo))) { ASSERT(FALSE); return E_INVALIDARG; } // // Fail if already subscribed and we aren't in no save or no UI mode. // Caller is responsible for UI. // BOOL fAlreadySubscribed; if ((FAILED(IsSubscribed(pwszURL, &fAlreadySubscribed)) || fAlreadySubscribed) && (!(dwFlags & (CREATESUBS_NOSAVE | CREATESUBS_NOUI)))) { return E_FAIL; } // // Fail if restrictions are in place. // FEATURE: Currently cdfview is handling channel restrictions for // but we should probably do it here. // Should we have a flag parameter to override this? // if (SUBSTYPE_URL == subType) { DWORD dwMaxCount = SHRestricted2W(REST_MaxSubscriptionCount, NULL, 0); DWORD dwCount; if (SHRestricted2W(REST_NoAddingSubscriptions, pwszURL, 0) || ((dwMaxCount > 0) && SUCCEEDED(CountSubscriptions(subType, &dwCount)) && (dwCount >= dwMaxCount))) { if (!IsFlagSet(dwFlags, CREATESUBS_NOUI)) SGMessageBox(hwnd, IDS_RESTRICTED, MB_OK); return E_ACCESSDENIED; } } // // Get the subscription defaults and merge in the caller's info // OOEBuf subProps; GetDefaultOOEBuf(&subProps, subType); //this is (intentionally) duplicated below... it needs to be after the ChangeSubscriptionValues() //call, but we need to grab the url first to make sure it's subscribable. MyOleStrToStrN(subProps.m_URL, INTERNET_MAX_URL_LENGTH, pwszURL); // Does this mean we can't support plugin protocols? if (/*(subType != SUBSTYPE_EXTERNAL) &&*/ !IsHTTPPrefixed(subProps.m_URL)) { return E_INVALIDARG; } if (pInfo) { ChangeSubscriptionValues(&subProps, pInfo); if (fAlreadySubscribed) { ReadCookieFromInetDB(subProps.m_URL, &subProps.m_Cookie); subProps.m_dwPropSheetFlags |= PSF_IS_ALREADY_SUBSCRIBED; } } // Disallow password caching if restriction is in place. This both // skips the wizard page and prevents the caller's password from // being saved. if (SHRestricted2W(REST_NoSubscriptionPasswords, NULL, 0)) { subProps.bNeedPassword = FALSE; subProps.username[0] = 0; subProps.password[0] = 0; subProps.dwFlags &= ~(PROP_WEBCRAWL_UNAME | PROP_WEBCRAWL_PSWD); } //the passed-in name and url override whatever's in the info buffer MyOleStrToStrN(subProps.m_URL, INTERNET_MAX_URL_LENGTH, pwszURL); MyOleStrToStrN(subProps.m_Name, MAX_NAME_QUICKLINK, pwszFriendlyName); // // If we're in UI mode, initialize the wizard // if (!IsFlagSet(dwFlags, CREATESUBS_NOUI)) { hr = CreateWizard(hwnd, subType, &subProps); } // !NOUI // // If we're not in NOSAVE mode, then create/save the subscription // if (SUCCEEDED(hr)) { if (!IsFlagSet(dwFlags, CREATESUBS_NOSAVE)) { //hack to let AddToFavorites dialog display the screen-saver-activate dialog -- it //needs to pass NOUI, which disables all UI, but if you also pass FROMFAVORITES and not //ADDTOFAVORITES then we still let this dialog slip through. (Shouldn't affect anyone //else because FROMFAVORITES is always used with ADDTOFAVORITES.) if (!IsFlagSet(dwFlags, CREATESUBS_NOUI) || (IsFlagSet(dwFlags, CREATESUBS_FROMFAVORITES) && !IsFlagSet (dwFlags, CREATESUBS_ADDTOFAVORITES))) { // See if the user wants to be asked about enabling the screen saver. DWORD dwValue = BST_UNCHECKED; ReadRegValue( HKEY_CURRENT_USER, WEBCHECK_REGKEY, g_szDontAskScreenSaver, &dwValue, SIZEOF(dwValue)); if ( (subProps.fChannelFlags & CHANNEL_AGENT_PRECACHE_SCRNSAVER) && (dwValue == BST_UNCHECKED) && !IsADScreenSaverActive() ) { DialogBoxParam( MLGetHinst(), MAKEINTRESOURCE(IDD_SUBSCRIPTION_ENABLECHANNELSAVER), hwnd, EnableScreenSaverDlgProc, 0L); } } // // Create a new pidl with the user specified properties. // if (_pidl) { COfflineFolderEnum::FreePidl(_pidl); _pidl = NULL; SAFERELEASE(m_pUIHelper); } hr = CreateSubscriptionFromOOEBuf(&subProps, &_pidl); if (SUCCEEDED(hr)) { ASSERT(_pidl); // // Send a notification that a subscription has changed. // _GenerateEvent(SHCNE_CREATE, (LPITEMIDLIST)_pidl, NULL); } } //!NOSAVE else if (S_OK == hr) { //in NOSAVE mode, so don't actually create subscription -- save it back //to passed-in buffer ASSERT (pInfo); pInfo->fUpdateFlags = SUBSINFO_ALLFLAGS; //fill in all possible fields UpdateSubsInfoFromOOE (pInfo, &subProps); } } return hr; } STDMETHODIMP CSubscriptionMgr::CreateDesktopSubscription (HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszFriendlyName, DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO *pInfo) { HRESULT hr; SUBSCRIPTIONINFO siTemp = { sizeof(SUBSCRIPTIONINFO), 0 }; if (!pInfo) pInfo = &siTemp; //make sure we have a valid buffer if caller doesn't give us one //make sure adminrestrictions allow this if (SHRestricted2W(REST_NoAddingChannels, pwszURL, 0)) return E_FAIL; SUBSCRIBE_ADI_INFO parms = { this, pwszFriendlyName, pwszURL, pInfo, subsType, dwFlags }; //make sure this url is subscribable; if not, show error dialog { TCHAR sz[MAX_URL]; MyOleStrToStrN (sz, ARRAYSIZE(sz), pwszURL); if (!IsHTTPPrefixed (sz)) { SGMessageBox(hwnd, IDS_HTTPONLY, MB_ICONINFORMATION | MB_OK); return E_INVALIDARG; } } INT_PTR iDlgResult = DialogBoxParam (MLGetHinst(), MAKEINTRESOURCE(IDD_DESKTOP_SUBSCRIPTION_SUMMARY), hwnd, SummarizeDesktopSubscriptionDlgProc, (LPARAM)&parms); switch (iDlgResult) { case -1: hr = E_FAIL; break; case IDCANCEL: hr = S_FALSE; break; default: hr = CreateSubscriptionNoSummary (hwnd, pwszURL, pwszFriendlyName, CREATESUBS_NOUI | dwFlags, subsType, pInfo); break; } return hr; } STDMETHODIMP CSubscriptionMgr::GetDefaultInfo( SUBSCRIPTIONTYPE subType, SUBSCRIPTIONINFO *pInfo ) { // // Validate the parameters. // if (!IS_VALID_SUBSCRIPTIONTYPE(subType) || !pInfo || (pInfo->cbSize != sizeof(SUBSCRIPTIONINFO))) { ASSERT(FALSE); return E_INVALIDARG; } memset((void *)pInfo, 0, sizeof(SUBSCRIPTIONINFO)); pInfo->cbSize = sizeof(SUBSCRIPTIONINFO); // Fill in default structure. Note that lines are commented out // below to indicate the field is initialized to 0 without wasting // code (memset above already cleared structure out.) pInfo->fUpdateFlags = SUBSINFO_RECURSE | SUBSINFO_MAILNOT | SUBSINFO_WEBCRAWL /*| SUBSINFO_SCHEDULE */ | SUBSINFO_CHANGESONLY | SUBSINFO_CHANNELFLAGS; pInfo->dwRecurseLevels = DEFAULTLEVEL; pInfo->schedule = SUBSSCHED_AUTO; switch (subType) { case SUBSTYPE_URL: // pInfo->bChangesOnly = FALSE; // pInfo->bMailNotification = FALSE; // pInfo->bPasswordNeeded = FALSE; pInfo->fWebcrawlerFlags = DEFAULTFLAGS; break; case SUBSTYPE_CHANNEL: // pInfo->bChangesOnly = FALSE; // pInfo->bMailNotification = FALSE; pInfo->fChannelFlags = CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_DYNAMIC_SCHEDULE; break; case SUBSTYPE_DESKTOPCHANNEL: // pInfo->bChangesOnly = FALSE; // pInfo->bMailNotification = FALSE; pInfo->fChannelFlags = CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_DYNAMIC_SCHEDULE; break; case SUBSTYPE_DESKTOPURL: // pInfo->bChangesOnly = FALSE; // pInfo->bMailNotification = FALSE; pInfo->fWebcrawlerFlags = DEFAULTFLAGS; break; default: return E_NOTIMPL; } return S_OK; } STDMETHODIMP CSubscriptionMgr::GetSubscriptionInfo( LPCWSTR pwszURL, SUBSCRIPTIONINFO *pInfo ) { HRESULT hr; // // Validate the parameters. // if (!pInfo || !pwszURL || (pInfo->cbSize != sizeof(SUBSCRIPTIONINFO))) { ASSERT(FALSE); return E_INVALIDARG; } BOOL bSubscribe; hr = IsSubscribed(pwszURL, &bSubscribe); RETURN_ON_FAILURE(hr); if (!bSubscribe) { return E_FAIL; } // We cannot rely on the caller passing us a clean SUBSCRIPTIONINFO // structure. We need to clean it ourselves. DWORD dwFlags = pInfo->fUpdateFlags; ZeroMemory(pInfo, sizeof(SUBSCRIPTIONINFO)); pInfo->cbSize = sizeof(SUBSCRIPTIONINFO); pInfo->fUpdateFlags = dwFlags; OOEBuf ooeb; // Alas, we need the code in UpdateSubsInfoFromOOE CopyToOOEBuf (&(_pidl->ooe), &ooeb); //to work once for a buf and once for an entry, and it's UpdateSubsInfoFromOOE (pInfo, &ooeb); //easier to convert entry->buf so we do that here. return S_OK; } void UpdateSubsInfoFromOOE (SUBSCRIPTIONINFO* pInfo, POOEBuf pooe) { DWORD dwFlags = pInfo->fUpdateFlags & SUBSINFO_ALLFLAGS; SUBSCRIPTIONTYPE subType = GetItemCategory(pooe); if (dwFlags & SUBSINFO_USER) { SAFEFREEBSTR (pInfo->bstrUserName); CreateBSTRFromTSTR(&(pInfo->bstrUserName), pooe->username); } if (dwFlags & SUBSINFO_PASSWORD) { SAFEFREEBSTR (pInfo->bstrPassword); CreateBSTRFromTSTR(&(pInfo->bstrPassword), pooe->password); } if (dwFlags & SUBSINFO_FRIENDLYNAME) { SAFEFREEBSTR (pInfo->bstrFriendlyName); CreateBSTRFromTSTR(&(pInfo->bstrFriendlyName), pooe->m_Name); } pInfo->fUpdateFlags = dwFlags; if (dwFlags & SUBSINFO_SCHEDULE) { pInfo->schedule = GetGroup(pooe); if (pInfo->schedule == SUBSSCHED_CUSTOM) { if (pooe->groupCookie != GUID_NULL) { pInfo->customGroupCookie = pooe->groupCookie; } else { GetItemSchedule(&pooe->m_Cookie, &pInfo->customGroupCookie); if (pInfo->customGroupCookie == GUID_NULL) { pInfo->schedule = SUBSSCHED_MANUAL; } } } } if (PTASK_TRIGGER pInfoTrigger = (PTASK_TRIGGER)pInfo->pTrigger) { if (pInfoTrigger->cbTriggerSize == pooe->m_Trigger.cbTriggerSize) *(pInfoTrigger) = pooe->m_Trigger; else pInfoTrigger->cbTriggerSize = 0; } //otherwise, it's already null and we can't do anything about it... luckily, we'll never //have a trigger that we need to write back to a SUBSCRIPTIONINFO that didn't already have //one. if (dwFlags & SUBSINFO_RECURSE) pInfo->dwRecurseLevels = pooe->m_RecurseLevels; if (dwFlags & SUBSINFO_WEBCRAWL) pInfo->fWebcrawlerFlags = pooe->m_RecurseFlags; if (dwFlags & SUBSINFO_MAILNOT) pInfo->bMailNotification = pooe->bMail; if (dwFlags & SUBSINFO_GLEAM) pInfo->bGleam = pooe->bGleam; if (dwFlags & SUBSINFO_CHANGESONLY) pInfo->bChangesOnly = pooe->bChangesOnly; if (dwFlags & SUBSINFO_NEEDPASSWORD) pInfo->bNeedPassword = pooe->bNeedPassword; if (dwFlags & SUBSINFO_CHANNELFLAGS) { if ((subType==SUBSTYPE_CHANNEL)||(subType==SUBSTYPE_DESKTOPCHANNEL)) { pInfo->fChannelFlags = pooe->fChannelFlags; } else { pInfo->fChannelFlags = 0; pInfo->fUpdateFlags &= (~SUBSINFO_CHANNELFLAGS); } } if (dwFlags & SUBSINFO_MAXSIZEKB) pInfo->dwMaxSizeKB = pooe->m_SizeLimit; if (dwFlags & SUBSINFO_TYPE) { pInfo->subType = GetItemCategory(pooe); ASSERT(IS_VALID_SUBSCRIPTIONTYPE(pInfo->subType)); } if (dwFlags & SUBSINFO_TASKFLAGS) { pInfo->fTaskFlags = pooe->grfTaskTrigger; } } STDMETHODIMP CSubscriptionMgr::UpdateSubscription(LPCWSTR pwszURL) { ASSERT(pwszURL); BOOL bSubscribe = FALSE; HRESULT hr = IsSubscribed(pwszURL, &bSubscribe); CLSID clsId; RETURN_ON_FAILURE(hr); if (!bSubscribe) { return E_INVALIDARG; } // // Fail if restrictions are in place. // FEATURE: Should we have a flag parameter to override this? // if (SHRestricted2W(REST_NoManualUpdates, NULL, 0)) { SGMessageBox(NULL, IDS_RESTRICTED, MB_OK); return E_ACCESSDENIED; } clsId = _pidl->ooe.m_Cookie; hr = SendUpdateRequests(NULL, &clsId, 1); _pidl->ooe.m_Cookie = clsId; return hr; } STDMETHODIMP CSubscriptionMgr::UpdateAll() { // // Fail if restrictions are in place. // FEATURE: Should we have a flag parameter to override this? // if (SHRestricted2W(REST_NoManualUpdates, NULL, 0)) { SGMessageBox(NULL, IDS_RESTRICTED, MB_OK); return E_ACCESSDENIED; } return SendUpdateRequests(NULL, NULL, 0); } HRESULT MergeOOEBuf(POOEBuf p1, POOEBuf p2, DWORD fMask) { ASSERT(p1 && p2); DWORD dwMask = p2->dwFlags & fMask; if (dwMask == 0) return S_OK; if (p1->clsidDest != p2->clsidDest) return E_INVALIDARG; if (dwMask & PROP_WEBCRAWL_COOKIE) { // We shouldn't merge cookies. } if (dwMask & PROP_WEBCRAWL_SIZE) { p1->m_SizeLimit = p2->m_SizeLimit; } if (dwMask & PROP_WEBCRAWL_FLAGS) { p1->m_RecurseFlags = p2->m_RecurseFlags; } if (dwMask & PROP_WEBCRAWL_LEVEL) { p1->m_RecurseLevels = p2->m_RecurseLevels; } if (dwMask & PROP_WEBCRAWL_URL) { StrCpyN(p1->m_URL, p2->m_URL, MAX_URL); } if (dwMask & PROP_WEBCRAWL_NAME) { StrCpyN(p1->m_Name, p2->m_Name, MAX_NAME); } if (dwMask & PROP_WEBCRAWL_PSWD) { StrCpyN(p1->password, p2->password, MAX_PASSWORD); } if (dwMask & PROP_WEBCRAWL_UNAME) { StrCpyN(p1->username, p2->username, MAX_USERNAME); } if (dwMask & PROP_WEBCRAWL_DESKTOP) { p1->bDesktop = p2->bDesktop; } if (dwMask & PROP_WEBCRAWL_CHANNEL) { p1->bChannel = p2->bChannel; } if (dwMask & PROP_WEBCRAWL_EMAILNOTF) { p1->bMail = p2->bMail; } if (dwMask & PROP_WEBCRAWL_RESCH) { p1->grfTaskTrigger = p2->grfTaskTrigger; p1->groupCookie = p2->groupCookie; p1->fChannelFlags |= (p2->fChannelFlags & CHANNEL_AGENT_DYNAMIC_SCHEDULE); } if (dwMask & PROP_WEBCRAWL_LAST) { p1->m_LastUpdated = p2->m_LastUpdated; } if (dwMask & PROP_WEBCRAWL_STATUS) { p1->status = p2->status; } if (dwMask & PROP_WEBCRAWL_PRIORITY) { p1->m_Priority = p2->m_Priority; } if (dwMask & PROP_WEBCRAWL_GLEAM) { p1->bGleam = p2->bGleam; } if (dwMask & PROP_WEBCRAWL_CHANGESONLY) { p1->bChangesOnly = p2->bChangesOnly; } if (dwMask & PROP_WEBCRAWL_CHANNELFLAGS) { p1->fChannelFlags = p2->fChannelFlags; } p1->dwFlags |= (p2->dwFlags & fMask & (~PROP_WEBCRAWL_COOKIE)); return S_OK; } INT_PTR CALLBACK SummarizeDesktopSubscriptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { SUBSCRIBE_ADI_INFO* pInfo = (SUBSCRIBE_ADI_INFO*)GetProp(hDlg,SUBSCRIBEADIPROP); switch (uMsg) { case WM_INITDIALOG: pInfo = (SUBSCRIBE_ADI_INFO*)lParam; ASSERT (pInfo); SetProp (hDlg, SUBSCRIBEADIPROP, (HANDLE)pInfo); { //block to declare vars to update captions TCHAR sz[MAX_URL]; if (pInfo->subsType == SUBSTYPE_DESKTOPCHANNEL) { if(MLLoadString( (pInfo->pSubsInfo->bNeedPassword ? IDS_DESKTOPCHANNEL_SUMMARY_TEXT : IDS_DESKTOPCHANNEL_SUMMARY_NOPW), sz, ARRAYSIZE(sz))) { SetDlgItemText(hDlg, IDC_DESKTOP_SUMMARY_TEXT, sz); } } MyOleStrToStrN (sz, ARRAYSIZE(sz), pInfo->pwszName); SetListViewToString(GetDlgItem(hDlg, IDC_SUBSCRIBE_ADI_NAME), sz); MyOleStrToStrN (sz, ARRAYSIZE(sz), pInfo->pwszUrl); SetListViewToString (GetDlgItem (hDlg, IDC_SUBSCRIBE_ADI_URL), sz); } break; case WM_COMMAND: ASSERT (pInfo); switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hDlg, IDCANCEL); break; case IDOK: //subscription happens in calling function when we return IDOK EndDialog(hDlg, IDOK); break; case IDC_SUBSCRIBE_CUSTOMIZE: //run through wizard in NOSAVE mode if (pInfo->pMgr && S_OK == pInfo->pMgr->CreateSubscriptionNoSummary (hDlg, pInfo->pwszUrl, pInfo->pwszName, pInfo->dwFlags | CREATESUBS_NOSAVE, pInfo->subsType, pInfo->pSubsInfo)) { SendMessage (hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDOK), TRUE); } break; } break; case WM_NOTIFY: if (LOWORD(wParam) == IDC_SUBSCRIBE_ADI_URL) { NM_LISTVIEW * pnmlv = (NM_LISTVIEW *)lParam; if (pnmlv->hdr.code == LVN_GETINFOTIP) { TCHAR szURL[MAX_URL]; LV_ITEM lvi = {0}; lvi.mask = LVIF_TEXT; lvi.pszText = szURL; lvi.cchTextMax = ARRAYSIZE(szURL); if (!ListView_GetItem (GetDlgItem (hDlg, IDC_SUBSCRIBE_ADI_URL), &lvi)) return FALSE; NMLVGETINFOTIP * pTip = (NMLVGETINFOTIP *)pnmlv; ASSERT(pTip); StrCpyN(pTip->pszText, szURL, pTip->cchTextMax); return TRUE; } } break; case WM_DESTROY: RemoveProp (hDlg, SUBSCRIBEADIPROP); break; } return FALSE; }