/*****************************************************************************\ FILE: MailBox.cpp DESCRIPTION: This file implements the logic of the MailBox feature. BryanSt 2/26/2000 Copyright (C) Microsoft Corp 2000-2000. All rights reserved. \*****************************************************************************/ #include "priv.h" #include // USES_CONVERSION #include "util.h" #include "objctors.h" #include #include // INT_MAX #include // Str_SetPtr #include "wizard.h" #include "MailBox.h" #include "emailassoc.h" #ifdef FEATURE_MAILBOX #define WM_AUTODISCOVERY_FINISHED (WM_USER + 1) // These are the wizard control IDs for the Back, Next, and Finished buttons. #define IDD_WIZARD_BACK_BUTTON 0x3023 #define IDD_WIZARD_NEXT_BUTTON 0x3024 #define IDD_WIZARD_FINISH_BUTTON 0x3025 /************************************************************************** CLASS: CMailBoxProcess **************************************************************************/ class CMailBoxProcess : public IUnknown { public: //IUnknown methods STDMETHODIMP QueryInterface(REFIID, LPVOID*); STDMETHODIMP_(DWORD) AddRef(); STDMETHODIMP_(DWORD) Release(); HRESULT ParseCmdLine(LPTSTR lpCmdLine); HRESULT Run(void); CMailBoxProcess(); ~CMailBoxProcess(); private: // Private Member Variables DWORD m_cRef; TCHAR m_szEmailAddress[MAX_EMAIL_ADDRESSS]; TCHAR m_szNextText[MAX_PATH]; BOOL m_fAutoDiscoveryFailed; // Did the AutoDiscovery process fail? LPTSTR m_pszMailApp; // Which app was chosen? LPTSTR m_pszURL; // Which URL should be used to read mail? HWND m_hwndDialog; HRESULT m_hr; IMailAutoDiscovery * m_pMailAutoDiscovery; BOOL m_fGetDefaultAccount; // If yes, open to wizard page asking for email address. BOOL m_fShowGetEmailAddressPage; // Do we want to show the get email address wizard page? BOOL m_fCreateNewEmailAccount; // Does the user want to create a new email account (like on one of the free email servers: hotmail, yahoo, etc.) // Private Member Functions HRESULT _DisplayDialogAndAutoDiscover(void); HRESULT _OpenWebBasedEmail(HKEY hkey); HRESULT _OpenExeBasedEmailApp(IN LPCWSTR pszMailApp); HRESULT _OpenProprietaryEmailApp(IN BSTR bstrProtocol, IN IMailProtocolADEntry * pMailProtocol); HRESULT _OpenEmailApp(void); HRESULT _RestoreNextButton(void); HRESULT _FillListWithApps(HWND hwndList); HRESULT _OnGetDispInfo(LV_DISPINFO * pDispInfo, bool fUnicode); HRESULT _OnChooseAppListFocus(void); HRESULT _OnChooseAppURLFocus(void); HRESULT _OnAppListSelection(LPNMLISTVIEW pListview); INT_PTR _MailBoxProgressDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); INT_PTR _ChooseAppDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); INT_PTR _GetEmailAddressDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); INT_PTR _OnInit(HWND hDlg); INT_PTR _OnInitChooseApp(HWND hDlg); INT_PTR _OnUserCancelled(void); INT_PTR _OnFinished(HRESULT hr, BSTR bstrXML); INT_PTR _OnGetEmailAddressNext(void); INT_PTR _OnCommand(WPARAM wParam, LPARAM lParam); INT_PTR _OnFinishedManualAssociate(void); friend INT_PTR CALLBACK MailBoxProgressDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); friend INT_PTR CALLBACK ChooseAppDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); friend INT_PTR CALLBACK GetEmailAddressDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); }; typedef struct tagEMAILCLIENT { LPTSTR pszFriendlyName; LPTSTR pszPath; LPTSTR pszCmdLine; LPTSTR pszIconPath; LPTSTR pszEmailApp; } EMAILCLIENT; //=========================== // *** Class Internals & Helpers *** //=========================== BOOL IsWhitespace(TCHAR Char) { return ((Char == TEXT(' ')) || (Char == TEXT('\t'))); } LPTSTR APIENTRY SkipWhite(LPTSTR pszString) { while (pszString[0] && IsWhitespace(pszString[0])) { pszString = CharNext(pszString); } return pszString; } BOOL IsFlagSpecified(IN LPCTSTR pwzFlag, IN LPCTSTR pszArg) { BOOL fIsFlagSpecified = FALSE; if ((TEXT('/') == pszArg[0]) || (TEXT('-') == pszArg[0])) { if ((0 == StrCmpI(pwzFlag, &pszArg[1])) || ((0 == StrCmpNI(pwzFlag, &pszArg[1], lstrlen(pwzFlag))) && (IsWhitespace(pszArg[lstrlen(pwzFlag) + 1])) ) ) { fIsFlagSpecified = TRUE; } } return fIsFlagSpecified; } LPTSTR GetNextArgument(LPTSTR pszCmdLine) { pszCmdLine = StrChr(pszCmdLine, TEXT(' ')); if (pszCmdLine) { pszCmdLine = SkipWhite(pszCmdLine); } return pszCmdLine; } void FreeEmailClient(EMAILCLIENT * pEmailClient) { if (pEmailClient) { Str_SetPtr(&pEmailClient->pszFriendlyName, NULL); Str_SetPtr(&pEmailClient->pszPath, NULL); Str_SetPtr(&pEmailClient->pszCmdLine, NULL); Str_SetPtr(&pEmailClient->pszIconPath, NULL); Str_SetPtr(&pEmailClient->pszEmailApp, NULL); LocalFree(pEmailClient); } } HRESULT CMailBoxProcess::_DisplayDialogAndAutoDiscover(void) { HWND hwndParent = NULL; // Do we need a parent? ATOMICRELEASE(m_pMailAutoDiscovery); m_fShowGetEmailAddressPage = (m_szEmailAddress[0] ? FALSE : TRUE); DisplayMailBoxWizard((LPARAM)this, m_fShowGetEmailAddressPage); HRESULT hr = m_hr; if (SUCCEEDED(hr)) { // Create account. hr = E_FAIL; long nSize; // Loop thru the list looking for the first instance of a protocol // that we support. if (m_pMailAutoDiscovery) { hr = m_pMailAutoDiscovery->get_length(&nSize); if (SUCCEEDED(hr)) { VARIANT varIndex; IMailProtocolADEntry * pMailProtocol = NULL; hr = E_FAIL; varIndex.vt = VT_I4; for (long nIndex = 0; (nIndex < nSize); nIndex++) { varIndex.lVal = nIndex; if (SUCCEEDED(m_pMailAutoDiscovery->get_item(varIndex, &pMailProtocol))) { BSTR bstrProtocol; hr = pMailProtocol->get_Protocol(&bstrProtocol); if (SUCCEEDED(hr)) { // Is this protocol one of the ones we support? if (!StrCmpIW(bstrProtocol, STR_PT_WEBBASED)) { SysFreeString(bstrProtocol); hr = EmailAssoc_CreateWebAssociation(m_szEmailAddress, pMailProtocol); break; } // Is this protocol one of the ones we support? if (!StrCmpIW(bstrProtocol, STR_PT_POP) || !StrCmpIW(bstrProtocol, STR_PT_IMAP) || !StrCmpIW(bstrProtocol, STR_PT_DAVMAIL)) { hr = EmailAssoc_CreateStandardsBaseAssociation(m_szEmailAddress, bstrProtocol); if (SUCCEEDED(hr)) { SysFreeString(bstrProtocol); break; } } hr = _OpenProprietaryEmailApp(bstrProtocol, pMailProtocol); SysFreeString(bstrProtocol); if (SUCCEEDED(hr)) { break; } } ATOMICRELEASE(pMailProtocol); } } ATOMICRELEASE(pMailProtocol); } } // We may have failed up until now because AutoDiscovery failed or was skipped, // but the user may have selected an app from the list. if (FAILED(hr)) { if (m_pszMailApp) { HKEY hkey; hr = EmailAssoc_CreateEmailAccount(m_szEmailAddress, &hkey); if (SUCCEEDED(hr)) { hr = EmailAssoc_SetEmailAccountPreferredApp(hkey, m_pszMailApp); RegCloseKey(hkey); } } else if (m_pszURL && m_pszURL[0]) { HKEY hkey; hr = EmailAssoc_CreateEmailAccount(m_szEmailAddress, &hkey); if (SUCCEEDED(hr)) { hr = EmailAssoc_SetEmailAccountProtocol(hkey, SZ_REGDATA_WEB); if (SUCCEEDED(hr)) { hr = EmailAssoc_SetEmailAccountWebURL(hkey, m_pszURL); } RegCloseKey(hkey); } } } } return hr; } INT_PTR CALLBACK MailBoxProgressDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { CMailBoxProcess * pMBProgress = (CMailBoxProcess *)GetWindowLongPtr(hDlg, DWLP_USER); if (WM_INITDIALOG == wMsg) { PROPSHEETPAGE * pPropSheetPage = (PROPSHEETPAGE *) lParam; if (pPropSheetPage) { SetWindowLongPtr(hDlg, DWLP_USER, pPropSheetPage->lParam); pMBProgress = (CMailBoxProcess *)pPropSheetPage->lParam; } } if (pMBProgress) return pMBProgress->_MailBoxProgressDialogProc(hDlg, wMsg, wParam, lParam); return DefWindowProc(hDlg, wMsg, wParam, lParam); } INT_PTR CMailBoxProcess::_MailBoxProgressDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { INT_PTR fHandled = TRUE; // handled switch (wMsg) { case WM_INITDIALOG: fHandled = _OnInit(hDlg); break; case WM_AUTODISCOVERY_FINISHED: fHandled = _OnFinished((HRESULT)wParam, (BSTR)lParam); break; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case LVN_GETDISPINFO: wMsg++; break; case PSN_SETACTIVE: PropSheet_SetWizButtons(GetParent(hDlg), ((TRUE == m_fShowGetEmailAddressPage) ? PSWIZB_NEXT | PSWIZB_BACK : PSWIZB_NEXT)); fHandled = TRUE; // Return zero to accept the activation. break; case PSN_WIZBACK: fHandled = TRUE; // Return zero to allow the user to go to the next page. break; case PSN_WIZNEXT: m_hr = S_FALSE; _RestoreNextButton(); fHandled = 0; // Return zero to allow the user to go to the next page. break; case PSN_QUERYCANCEL: _OnUserCancelled(); fHandled = FALSE; break; default: //TraceMsg(TF_ALWAYS, "CMailBoxProcess::_MailBoxProgressDialogProc(wMsg = %d, pnmh->code = %d) WM_NOTIFY", wMsg, pnmh->code); break; } } default: //TraceMsg(TF_ALWAYS, "CMailBoxProcess::_MailBoxProgressDialogProc(wMsg = %d) WM_NOTIFY", wMsg); fHandled = FALSE; // Not handled break; } return fHandled; } INT_PTR CALLBACK ChooseAppDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { CMailBoxProcess * pMBProgress = (CMailBoxProcess *)GetWindowLongPtr(hDlg, DWLP_USER); if (WM_INITDIALOG == wMsg) { PROPSHEETPAGE * pPropSheetPage = (PROPSHEETPAGE *) lParam; if (pPropSheetPage) { SetWindowLongPtr(hDlg, DWLP_USER, pPropSheetPage->lParam); pMBProgress = (CMailBoxProcess *)pPropSheetPage->lParam; } } if (pMBProgress) return pMBProgress->_ChooseAppDialogProc(hDlg, wMsg, wParam, lParam); return DefWindowProc(hDlg, wMsg, wParam, lParam); } HRESULT CMailBoxProcess::_OnChooseAppURLFocus(void) { HWND hwndURLEditbox = GetDlgItem(m_hwndDialog, IDC_CHOOSEAPP_WEBURL_EDIT); // Something happened to cause to have us shift into the "Other:" case. CheckDlgButton(m_hwndDialog, IDC_CHOOSEAPP_WEB_RADIO, BST_CHECKED); // Uncheck the Web radio button CheckDlgButton(m_hwndDialog, IDC_CHOOSEAPP_OTHERAPP_RADIO, BST_UNCHECKED); // Uncheck the Web radio button SetFocus(hwndURLEditbox); return S_OK; } INT_PTR CMailBoxProcess::_OnFinishedManualAssociate(void) { // TODO: Only succeed the finished part if something is selected in the app list. // Also gray out the button. m_hr = S_OK; // Did the user manually configure via "Web" or an app? if (IsDlgButtonChecked(m_hwndDialog, IDC_CHOOSEAPP_WEB_RADIO)) { // Web. So save the URL. TCHAR szURL[MAX_URL_STRING]; GetWindowText(GetDlgItem(m_hwndDialog, IDC_CHOOSEAPP_WEBURL_EDIT), szURL, ARRAYSIZE(szURL)); Str_SetPtr(&m_pszURL, szURL); Str_SetPtr(&m_pszMailApp, NULL); } return FALSE; // FALSE mean means allow it to close. TRUE means keep it open. } HRESULT CMailBoxProcess::_OnAppListSelection(LPNMLISTVIEW pListview) { LPTSTR pszNewApp = NULL; // None selected. // The user choose from the list. So store the name of the app if (pListview && (-1 != pListview->iItem)) { HWND hwndList = GetDlgItem(m_hwndDialog, IDC_CHOOSEAPP_APPLIST); LVITEM pItem = {0}; pItem.iItem = pListview->iItem; pItem.iSubItem = pListview->iSubItem; pItem.mask = LVIF_PARAM; if (ListView_GetItem(hwndList, &pItem)) { EMAILCLIENT * pEmailClient = (EMAILCLIENT *) pItem.lParam; if (pEmailClient) { pszNewApp = pEmailClient->pszEmailApp; } } } Str_SetPtr(&m_pszMailApp, pszNewApp); return S_OK; } HRESULT CMailBoxProcess::_OnChooseAppListFocus(void) { HWND hwndAppList = GetDlgItem(m_hwndDialog, IDC_CHOOSEAPP_APPLIST); // Something happened to cause to have us shift into the "Other:" case. CheckDlgButton(m_hwndDialog, IDC_CHOOSEAPP_WEB_RADIO, BST_UNCHECKED); // Uncheck the Web radio button CheckDlgButton(m_hwndDialog, IDC_CHOOSEAPP_OTHERAPP_RADIO, BST_CHECKED); // Uncheck the Web radio button SetFocus(hwndAppList); return S_OK; } INT_PTR CMailBoxProcess::_ChooseAppDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { INT_PTR fHandled = TRUE; // handled switch (wMsg) { case WM_INITDIALOG: fHandled = _OnInitChooseApp(hDlg); PropSheet_SetWizButtons(GetParent(hDlg), ((TRUE == m_fShowGetEmailAddressPage) ? (PSWIZB_BACK | PSWIZB_FINISH) : PSWIZB_FINISH)); break; case WM_COMMAND: fHandled = _OnCommand(wParam, lParam); break; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; int idEvent = pnmh->code; switch (idEvent) { case LVN_GETDISPINFOA: _OnGetDispInfo((LV_DISPINFO *)lParam, false); break; case LVN_GETDISPINFOW: _OnGetDispInfo((LV_DISPINFO *)lParam, true); break; case LVN_DELETEITEM: if (lParam) { FreeEmailClient((EMAILCLIENT *) ((NM_LISTVIEW *)lParam)->lParam); } break; case LVN_ITEMCHANGED: _OnChooseAppListFocus(); _OnAppListSelection((LPNMLISTVIEW) lParam); // Keep track of the last selected item. break; case LVN_ITEMACTIVATE: break; case PSN_SETACTIVE: PropSheet_SetWizButtons(GetParent(hDlg), (PSWIZB_BACK | PSWIZB_FINISH)); fHandled = TRUE; // Return zero to accept the activation. break; case PSN_WIZBACK: // Set the prev. page to show SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR) 0); fHandled = -1; break; case PSN_WIZFINISH: fHandled = _OnFinishedManualAssociate(); break; case PSN_QUERYCANCEL: m_hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // TODO: fHandled = FALSE; break; default: //TraceMsg(TF_ALWAYS, "CMailBoxProcess::_ChooseAppDialogProc(wMsg = %d, pnmh->code = %d) WM_NOTIFY", wMsg, pnmh->code); break; } break; } default: fHandled = FALSE; // Not handled //TraceMsg(TF_ALWAYS, "CMailBoxProcess::_ChooseAppDialogProc(wMsg = %d)", wMsg); break; } return fHandled; } HRESULT CMailBoxProcess::_RestoreNextButton(void) { HWND hwndNextButton = GetDlgItem(GetParent(m_hwndDialog), IDD_WIZARD_NEXT_BUTTON); if (hwndNextButton && m_szNextText[0]) { SetWindowText(hwndNextButton, m_szNextText); } return S_OK; } INT_PTR CMailBoxProcess::_OnInit(HWND hDlg) { BOOL fHandled = FALSE; // Not handled HWND hwndWizard = GetParent(hDlg); TCHAR szSkipButton[MAX_PATH]; HWND hwndNextButton = GetDlgItem(GetParent(hDlg), IDD_WIZARD_NEXT_BUTTON); m_hwndDialog = hDlg; if (hwndNextButton && GetWindowText(hwndNextButton, m_szNextText, ARRAYSIZE(m_szNextText))) { // First, change the "Next" button into "Skip" // Save the text on the next button before we rename it. LoadString(HINST_THISDLL, IDS_SKIP_BUTTON, szSkipButton, ARRAYSIZE(szSkipButton)); // Set the next text. SetWindowText(hwndNextButton, szSkipButton); } // Set Animation. HWND hwndAnimation = GetDlgItem(hDlg, IDC_AUTODISCOVERY_ANIMATION); if (hwndAnimation) { Animate_OpenEx(hwndAnimation, HINST_THISDLL, IDA_DOWNLOADINGSETTINGS); } // Start the background task. m_hr = CMailAccountDiscovery_CreateInstance(NULL, IID_PPV_ARG(IMailAutoDiscovery, &m_pMailAutoDiscovery)); if (SUCCEEDED(m_hr)) { m_hr = m_pMailAutoDiscovery->WorkAsync(hDlg, WM_AUTODISCOVERY_FINISHED); if (SUCCEEDED(m_hr)) { m_hr = m_pMailAutoDiscovery->DiscoverMail(m_szEmailAddress); } } if (FAILED(m_hr)) { PropSheet_PressButton(GetParent(hDlg), PSBTN_NEXT); } return fHandled; } INT_PTR CMailBoxProcess::_OnCommand(WPARAM wParam, LPARAM lParam) { BOOL fHandled = 1; // Not handled (WM_COMMAND seems to be different) WORD wMsg = HIWORD(wParam); WORD idCtrl = LOWORD(wParam); switch (idCtrl) { case IDC_CHOOSEAPP_WEBURL_EDIT: switch (wMsg) { case EN_SETFOCUS: case STN_CLICKED: _OnChooseAppURLFocus(); break; default: //TraceMsg(TF_ALWAYS, "in CMailBoxProcess::_OnCommand() wMsg=%#08lx, idCtrl=%#08lx", wMsg, idCtrl); break; } break; case IDC_CHOOSEAPP_WEB_RADIO: switch (wMsg) { case BN_CLICKED: _OnChooseAppURLFocus(); break; default: //TraceMsg(TF_ALWAYS, "in CMailBoxProcess::_OnCommand() wMsg=%#08lx, idCtrl=%#08lx", wMsg, idCtrl); break; } break; case IDC_CHOOSEAPP_OTHERAPP_RADIO: switch (wMsg) { case BN_CLICKED: _OnChooseAppListFocus(); break; default: //TraceMsg(TF_ALWAYS, "in CMailBoxProcess::_OnCommand() wMsg=%#08lx, idCtrl=%#08lx", wMsg, idCtrl); break; } break; default: //TraceMsg(TF_ALWAYS, "in CMailBoxProcess::_OnCommand() wMsg=%#08lx, idCtrl=%#08lx", wMsg, idCtrl); break; } return fHandled; } INT_PTR CMailBoxProcess::_OnInitChooseApp(HWND hDlg) { BOOL fHandled = FALSE; // Not handled HWND hwndWizard = GetParent(hDlg); HWND hwndURLEditbox = GetDlgItem(hDlg, IDC_CHOOSEAPP_WEBURL_EDIT); m_hwndDialog = hDlg; // TODO: 2) Handle someone changing the combox box. LPCTSTR pszDomain = StrChr(m_szEmailAddress, CH_EMAIL_AT); if (pszDomain) { TCHAR szDesc[MAX_URL_STRING]; pszDomain = CharNext(pszDomain); // Skip past the "@" // Update the description on the dialog if the download failed. if (m_fAutoDiscoveryFailed) { TCHAR szTemplate[MAX_URL_STRING]; LoadString(HINST_THISDLL, IDS_CHOOSEAPP_FAILED_RESULTS, szTemplate, ARRAYSIZE(szTemplate)); wnsprintf(szDesc, ARRAYSIZE(szDesc), szTemplate, pszDomain); SetWindowText(GetDlgItem(hDlg, IDC_CHOOSEAPP_DESC), szDesc); } wnsprintf(szDesc, ARRAYSIZE(szDesc), TEXT("http://www.%s/"), pszDomain); SetWindowText(hwndURLEditbox, szDesc); } // Populate the List of Apps HWND hwndList = GetDlgItem(hDlg, IDC_CHOOSEAPP_APPLIST); if (hwndList) { HIMAGELIST himlLarge; HIMAGELIST himlSmall; if (Shell_GetImageLists(&himlLarge, &himlSmall)) { RECT rc; LV_COLUMN col = {LVCF_FMT | LVCF_WIDTH, LVCFMT_LEFT}; ListView_SetImageList(hwndList, himlLarge, LVSIL_NORMAL); ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL); GetWindowRect(hwndList, &rc); col.cx = rc.right - GetSystemMetrics(SM_CXVSCROLL) - (4 * GetSystemMetrics(SM_CXEDGE)); ListView_InsertColumn(hwndList, 0, &col); _FillListWithApps(hwndList); // TODO: 1) On select in the list, force "Other:" to be checked. 2) If editbox changes, set "Web:" } } // Choose the Web Radio button because that will be the default. // (Most email systems tend to use web based) _OnChooseAppURLFocus(); Edit_SetSel(hwndURLEditbox, 0, -1); return fHandled; } HRESULT _AddEmailClientToList(HWND hwndList, HKEY hkey, LPCTSTR pszMailApp, LPCTSTR pszFriendlyName) { TCHAR szPath[MAX_PATH]; HRESULT hr = EmailAssoc_GetAppPath(hkey, szPath, ARRAYSIZE(szPath)); if (SUCCEEDED(hr)) { TCHAR szCmdLine[MAX_PATH]; hr = EmailAssoc_GetAppCmdLine(hkey, szCmdLine, ARRAYSIZE(szCmdLine)); if (SUCCEEDED(hr)) { // Get the path we will use for the icon. TCHAR szIconPath[MAX_PATH]; hr = EmailAssoc_GetIconPath(hkey, szIconPath, ARRAYSIZE(szIconPath)); if (SUCCEEDED(hr)) { EMAILCLIENT * pEmailClient = (EMAILCLIENT *) LocalAlloc(LPTR, sizeof(*pEmailClient)); if (pEmailClient) { if (PathFileExists(szIconPath)) { // TODO: Add a separate reg value for icon in the new case. Str_SetPtr(&pEmailClient->pszIconPath, szIconPath); } // TODO: We may want to use the dll's version resource because it // has a product description which may be localized. Str_SetPtr(&pEmailClient->pszFriendlyName, pszFriendlyName); Str_SetPtr(&pEmailClient->pszEmailApp, pszMailApp); if (pEmailClient->pszFriendlyName) { Str_SetPtr(&pEmailClient->pszPath, szPath); if (pEmailClient->pszPath) { if (szCmdLine[0]) { Str_SetPtr(&pEmailClient->pszCmdLine, szCmdLine); } LV_ITEM item = {0}; item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; item.iItem = INT_MAX; item.iSubItem = 0; item.state = 0; item.iImage = I_IMAGECALLBACK; item.pszText = pEmailClient->pszFriendlyName; item.lParam = (LPARAM)pEmailClient; if (-1 == ListView_InsertItem(hwndList, &item)) { hr = E_FAIL; } } } else { hr = E_OUTOFMEMORY; } if (FAILED(hr)) { FreeEmailClient(pEmailClient); } } else { hr = E_OUTOFMEMORY; } } } } return hr; } HRESULT CMailBoxProcess::_FillListWithApps(HWND hwndList) { HRESULT hr = S_OK; HKEY hkey; DWORD dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_MAILCLIENTS, 0, KEY_READ, &hkey); hr = HRESULT_FROM_WIN32(dwError); if (SUCCEEDED(hr)) { TCHAR szFriendlyName[MAX_PATH]; TCHAR szKeyName[MAX_PATH]; TCHAR szCurrent[MAX_PATH]; // Nuke? TCHAR szFriendlyCurrent[MAX_PATH]; FILETIME ftLastWriteTime; DWORD nIndex; // Index counter DWORD cb; DWORD nSelected = 0; // find the currently selected client cb = sizeof(szCurrent); dwError = RegQueryValueEx(hkey, NULL, NULL, NULL, (LPBYTE)szCurrent, &cb); hr = HRESULT_FROM_WIN32(dwError); if (FAILED(hr)) { // if not found then blank the friendly name and keyname. szCurrent[0] = 0; szFriendlyCurrent[0] = 0; } // populate the list for(nIndex = 0; cb = ARRAYSIZE(szKeyName), dwError = RegEnumKeyEx(hkey, nIndex, szKeyName, &cb, NULL, NULL, NULL, &ftLastWriteTime), hr = HRESULT_FROM_WIN32(dwError), SUCCEEDED(hr); nIndex++) { HKEY hkeyClient; // get the friendly name of the client dwError = RegOpenKeyEx(hkey, szKeyName, 0, KEY_READ, &hkeyClient); hr = HRESULT_FROM_WIN32(dwError); if (SUCCEEDED(hr)) { cb = sizeof(szFriendlyName); dwError = RegQueryValueEx(hkeyClient, NULL, NULL, NULL, (LPBYTE)szFriendlyName, &cb); hr = HRESULT_FROM_WIN32(dwError); if (SUCCEEDED(hr)) { hr = _AddEmailClientToList(hwndList, hkeyClient, szKeyName, szFriendlyName); // check to see if it's the current default if (!StrCmp(szKeyName, szCurrent)) { // save its the friendly name which we'll use later to // select the current client and what index it is. StrCpyN(szFriendlyCurrent, szFriendlyName, ARRAYSIZE(szFriendlyCurrent)); nSelected = nIndex; } } // close key RegCloseKey(hkeyClient); } } // for // use a custom sort to delay the friendly names being used // ListView_SortItems(hwndList, _CompareApps, 0); // Lets select the appropriate entre. ListView_SetItemState(hwndList, nSelected, LVNI_FOCUSED, LVNI_SELECTED); // close the keys RegCloseKey(hkey); } return hr; } HRESULT CMailBoxProcess::_OnGetDispInfo(LV_DISPINFO * pDispInfo, bool fUnicode) { HRESULT hr = S_OK; if (pDispInfo && pDispInfo->item.mask & LVIF_IMAGE) { EMAILCLIENT * pEmailClient = (EMAILCLIENT *) pDispInfo->item.lParam; if (pEmailClient) { pDispInfo->item.iImage = -1; if (pEmailClient->pszIconPath) { pDispInfo->item.iImage = Shell_GetCachedImageIndex(pEmailClient->pszIconPath, 0, 0); } if (-1 == pDispInfo->item.iImage) { pDispInfo->item.iImage = Shell_GetCachedImageIndex(TEXT("shell32.dll"), II_APPLICATION, 0); } if (-1 != pDispInfo->item.iImage) { pDispInfo->item.mask = LVIF_IMAGE; } } } return hr; } INT_PTR CMailBoxProcess::_OnUserCancelled(void) { m_hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Means user cancelled _RestoreNextButton(); return FALSE; // Not handled } INT_PTR CMailBoxProcess::_OnFinished(HRESULT hr, BSTR bstrXML) { SysFreeString(bstrXML); _RestoreNextButton(); m_hr = hr; // whatever the success value was... if (S_OK == m_hr) { // We succeeded so we can end the dialog PropSheet_PressButton(GetParent(m_hwndDialog), PSBTN_FINISH); } else { // The results came back but we can't continue. So advance // to the Choose App page. m_fAutoDiscoveryFailed = TRUE; PropSheet_PressButton(GetParent(m_hwndDialog), PSBTN_NEXT); } return TRUE; // handled } INT_PTR CALLBACK GetEmailAddressDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { CMailBoxProcess * pMBProgress = (CMailBoxProcess *)GetWindowLongPtr(hDlg, DWLP_USER); if (WM_INITDIALOG == wMsg) { PROPSHEETPAGE * pPropSheetPage = (PROPSHEETPAGE *) lParam; if (pPropSheetPage) { SetWindowLongPtr(hDlg, DWLP_USER, pPropSheetPage->lParam); pMBProgress = (CMailBoxProcess *)pPropSheetPage->lParam; } } if (pMBProgress) return pMBProgress->_GetEmailAddressDialogProc(hDlg, wMsg, wParam, lParam); return DefWindowProc(hDlg, wMsg, wParam, lParam); } INT_PTR CMailBoxProcess::_GetEmailAddressDialogProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { INT_PTR fHandled = TRUE; // handled switch (wMsg) { case WM_INITDIALOG: m_hwndDialog = hDlg; break; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT); fHandled = TRUE; // Return zero to accept the activation. break; case PSN_WIZNEXT: fHandled = _OnGetEmailAddressNext(); break; case PSN_QUERYCANCEL: m_hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Means user cancelled fHandled = FALSE; break; default: //TraceMsg(TF_ALWAYS, "CMailBoxProcess::_MailBoxProgressDialogProc(wMsg = %d, pnmh->code = %d) WM_NOTIFY", wMsg, pnmh->code); break; } } default: //TraceMsg(TF_ALWAYS, "CMailBoxProcess::_MailBoxProgressDialogProc(wMsg = %d) WM_NOTIFY", wMsg); fHandled = FALSE; // Not handled break; } return fHandled; } INT_PTR CMailBoxProcess::_OnGetEmailAddressNext(void) { BOOL fCancel = TRUE; GetWindowText(GetDlgItem(m_hwndDialog, IDC_GETEMAILADDRESS_EDIT), m_szEmailAddress, ARRAYSIZE(m_szEmailAddress)); if (m_szEmailAddress[0] && StrChr(m_szEmailAddress, CH_EMAIL_AT)) { // Looks valid to me. fCancel = 0; // TODO: Add more verification code } return fCancel; // 0 mean means allow it to close. TRUE means keep it open. } HRESULT CMailBoxProcess::_OpenEmailApp(void) { HKEY hkey; HRESULT hr = EmailAssoc_OpenEmailAccount(m_szEmailAddress, &hkey); if (SUCCEEDED(hr)) { WCHAR wzPreferredApp[MAX_PATH]; // Did the user customize the app for this Email address? (Default is no) hr = EmailAssoc_GetEmailAccountPreferredApp(hkey, wzPreferredApp, ARRAYSIZE(wzPreferredApp)); if (SUCCEEDED(hr)) { // Yes, so let's launch that app. hr = _OpenExeBasedEmailApp(wzPreferredApp); } else { WCHAR wzProtocol[MAX_PATH]; hr = EmailAssoc_GetEmailAccountProtocol(hkey, wzProtocol, ARRAYSIZE(wzProtocol)); if (SUCCEEDED(hr)) { // No, but that's fine because it's the default. if (!StrCmpIW(wzProtocol, SZ_REGDATA_WEB)) { hr = _OpenWebBasedEmail(hkey); } else { hr = EmailAssoc_GetEmailAccountGetAppFromProtocol(wzProtocol, wzPreferredApp, ARRAYSIZE(wzPreferredApp)); if (SUCCEEDED(hr)) { hr = _OpenExeBasedEmailApp(wzPreferredApp); } } } } RegCloseKey(hkey); } return hr; } HRESULT CMailBoxProcess::_OpenWebBasedEmail(HKEY hkey) { WCHAR wzURL[MAX_URL_STRING]; HRESULT hr = EmailAssoc_GetEmailAccountWebURL(hkey, wzURL, ARRAYSIZE(wzURL)); if (0 == wzURL[0]) { hr = E_FAIL; } if (SUCCEEDED(hr)) { // TODO: In the future, we can get more information from get_PostHTML(). With // that information, we can either: // 1. Do an HTTP POST with header data that will simmulate logging into the server. // This is good except that we need the password. // 2. We can put form value/data pair information into an pidl created from the URL. // We can then have the browser pull this information out and pre-populate form // items. This will pre-populate the "User:" form item. This way the user // only needs to enter their password. hr = HrShellExecute(NULL, NULL, wzURL, NULL, NULL, SW_SHOW); if (SUCCEEDED(hr)) { AddEmailToAutoComplete(m_szEmailAddress); } } return hr; } HRESULT CMailBoxProcess::_OpenExeBasedEmailApp(IN LPCWSTR pszMailApp) { HKEY hkey; HRESULT hr = EmailAssoc_OpenMailApp(pszMailApp, &hkey); // TODO: Call _InstallLegacyAssociations() to make sure the associations are correct. if (SUCCEEDED(hr)) { TCHAR szPath[MAX_URL_STRING]; hr = EmailAssoc_GetAppPath(hkey, szPath, ARRAYSIZE(szPath)); if (SUCCEEDED(hr)) { TCHAR szCmdLine[MAX_URL_STRING]; szCmdLine[0] = 0; if (SUCCEEDED(EmailAssoc_GetAppCmdLine(hkey, szCmdLine, ARRAYSIZE(szCmdLine))) && // optional !StrStrI(SZ_TOKEN_EMAILADDRESS, szCmdLine)) { // They have a cmdline and they want us to replace a token. StrReplaceToken(SZ_TOKEN_EMAILADDRESS, m_szEmailAddress, szCmdLine, ARRAYSIZE(szCmdLine)); } hr = HrShellExecute(NULL, NULL, szPath, (szCmdLine[0] ? szCmdLine : NULL), NULL, SW_SHOW); if (SUCCEEDED(hr)) { AddEmailToAutoComplete(m_szEmailAddress); } } RegCloseKey(hkey); } return hr; } HRESULT CMailBoxProcess::_OpenProprietaryEmailApp(BSTR bstrProtocol, IMailProtocolADEntry * pMailProtocol) { HRESULT hr = E_FAIL; // TODO: MessageBox(NULL, TEXT("Open Proprietary Email App here. We look up in the registry for these types of apps. AOL, MSN, Compuserv are examples."), TEXT("Looser"), MB_OK); return hr; } //=========================== // *** Public Methods *** //=========================== HRESULT CMailBoxProcess::ParseCmdLine(LPTSTR pszCmdLine) { // We don't treat quote sections as blocks. PathUnquoteSpaces(pszCmdLine); while (pszCmdLine && pszCmdLine[0]) { if (IsFlagSpecified(TEXT("email"), pszCmdLine)) { pszCmdLine = GetNextArgument(pszCmdLine); if (pszCmdLine) { if ((TEXT('/') == pszCmdLine[0]) || (TEXT('-') == pszCmdLine[0])) { } else { LPTSTR pszEndOfEmailAddress = StrChr(pszCmdLine, TEXT(' ')); SIZE_T cchSizeToCopy = ARRAYSIZE(m_szEmailAddress); if (pszEndOfEmailAddress && (cchSizeToCopy > (SIZE_T)(pszEndOfEmailAddress - pszCmdLine))) { cchSizeToCopy = (pszEndOfEmailAddress - pszCmdLine) + 1; } StrCpyN(m_szEmailAddress, pszCmdLine, (int)cchSizeToCopy); pszCmdLine = GetNextArgument(pszCmdLine); } } continue; } if (IsFlagSpecified(TEXT("GetDefaultAccount"), pszCmdLine)) { pszCmdLine = GetNextArgument(pszCmdLine); m_fGetDefaultAccount = TRUE; continue; } if (IsFlagSpecified(TEXT("CreateNewEmailAccount"), pszCmdLine)) { pszCmdLine = GetNextArgument(pszCmdLine); m_fCreateNewEmailAccount = TRUE; continue; } pszCmdLine = GetNextArgument(pszCmdLine); } return S_OK; } HRESULT CMailBoxProcess::Run(void) { HRESULT hr = CoInitialize(0); if (SUCCEEDED(hr)) { if (TRUE == m_fCreateNewEmailAccount) { // TODO: look in the registry for the default email account and // copy it into m_szEmailAddress. MessageBox(NULL, TEXT("Create New Email Account"), TEXT("TODO: Add code here."), MB_OK); } else { if (TRUE == m_fGetDefaultAccount) { // If the caller wants to use the default email address, then look in the registry // for it and use that address. if (FAILED(EmailAssoc_GetDefaultEmailAccount(m_szEmailAddress, ARRAYSIZE(m_szEmailAddress)))) { m_szEmailAddress[0] = 0; } } // Legacy email applications didn't install email associations, so we // do that for them now. EmailAssoc_InstallLegacyMailAppAssociations(); if (m_szEmailAddress[0]) { // Since we know the email address, try to open it now. // (We will see if the associations are installed) hr = _OpenEmailApp(); } else { hr = E_FAIL; } if (FAILED(hr)) { hr = _DisplayDialogAndAutoDiscover(); if (SUCCEEDED(hr)) // If we got the protocol, then open the app { hr = _OpenEmailApp(); } ATOMICRELEASE(m_pMailAutoDiscovery); } } } else { CoUninitialize(); } return hr; } //=========================== // *** IUnknown Interface *** //=========================== STDMETHODIMP CMailBoxProcess::QueryInterface(REFIID riid, LPVOID *ppvObj) { static const QITAB qit[] = { QITABENT(CMailBoxProcess, IUnknown), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(DWORD) CMailBoxProcess::AddRef() { return ++m_cRef; } STDMETHODIMP_(DWORD) CMailBoxProcess::Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } //=========================== // *** Class Methods *** //=========================== CMailBoxProcess::CMailBoxProcess() { DllAddRef(); m_szNextText[0] = 0; m_szEmailAddress[0] = 0; m_fAutoDiscoveryFailed = FALSE; m_hwndDialog = NULL; m_pMailAutoDiscovery = NULL; m_pszMailApp = NULL; m_pszURL = NULL; m_fGetDefaultAccount = FALSE; m_fShowGetEmailAddressPage = FALSE; m_fCreateNewEmailAccount = FALSE; _InitComCtl32(); // So we can use the ICC_ANIMATE_CLASS common controls. m_cRef = 1; } CMailBoxProcess::~CMailBoxProcess() { ATOMICRELEASE(m_pMailAutoDiscovery); Str_SetPtr(&m_pszMailApp, NULL); Str_SetPtr(&m_pszURL, NULL); DllRelease(); } //=========================== // *** Non-Class Functons *** //=========================== int AutoDiscoverAndOpenEmail(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { TCHAR szCmdLine[MAX_EMAIL_ADDRESSS]; szCmdLine[0] = 0; if (lpCmdLine) { // TODO: Either support Unicode or tounel thru UTF8 SHAnsiToTChar((LPCSTR)lpCmdLine, szCmdLine, ARRAYSIZE(szCmdLine)); } CMailBoxProcess mailboxProcess; if (SUCCEEDED(mailboxProcess.ParseCmdLine(szCmdLine))) { mailboxProcess.Run(); } return 0; } #else // FEATURE_MAILBOX int AutoDiscoverAndOpenEmail(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { return 0; } #endif // FEATURE_MAILBOX