/*++ Copyright (c) 2000,2001 Microsoft Corporation Module Name: MAINDLG.CPP Abstract: Implementation of the mail keymgr dialog which displays existing credentials and offers the ability to create, edit, or delete them. Author: Environment: WinXP --*/ // test/dev switch variables ////////////////////////////////////////////////////////////////////////////// // // Include files // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "switches.h" #include "Dlg.h" #include "Res.h" #include "KRDlg.h" #include "keymgr.h" #include "testaudit.h" //=================================== // special type value used to obviate string comparison to detect special nature // of the *Session credential. Value arbitrary. #define SESSION_FLAG_VALUE (0x2222) // tooltips support #define TIPSTRINGLEN 500 TCHAR szTipString[TIPSTRINGLEN]; WNDPROC lpfnOldWindowProc = NULL; // used to subclass the list box LRESULT CALLBACK ListBoxSubClassFunction(HWND,WORD,WPARAM,LPARAM); // global state vars - comm between prop dlg and list dlg LONG_PTR g_CurrentKey = 0; // currently selected item in the main dlg BOOL g_HaveShownRASCred = FALSE; // TRUE the first time one is shown BOOL g_fReloadList = TRUE; // reload list if something changed DWORD_PTR g_dwHCookie = 0; // HTML HELP system cookie HWND g_hMainDlg = NULL; // used to give add/new access to target list C_AddKeyDlg *g_AKdlg = NULL; // used for notifications /********************************************************************** gTestReadCredential() Arguments: None Returns: BOOL, TRUE if the selected credential could be successfully read. Comments: Read the credential currently selected in the list box from the keyring. sets g_szTargetName sets g_pExisitingCred **********************************************************************/ BOOL gTestReadCredential(void) { TCHAR *pC; BOOL f; LRESULT lR; LRESULT lRet = 0; DWORD dwType; g_pExistingCred = NULL; // Fetch current credential from list into g_szTargetName lR = SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETCURSEL,0,0L); if (lR == LB_ERR) { return FALSE; } else { g_CurrentKey = lR; lRet = SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETTEXT,lR,(LPARAM) g_szTargetName); } // Possible error - zero chars returned from list box if (lRet == 0) { ASSERT(0); return FALSE; // zero characters returned } // Get the target type from the combo box item data dwType = (DWORD) SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETITEMDATA,lR,0); if (LB_ERR == dwType) { return FALSE; } // null term the targetname shown in the UI, // trimming the suffix if there is one pC = _tcschr(g_szTargetName,g_rgcCert[0]); if (pC) { pC--; *pC = 0x0; // null terminate namestring } // Attempt to read the credential from the store // The returned credential will have to be freed if leaving this block f = (CredRead(g_szTargetName, (ULONG) dwType, 0, &g_pExistingCred)); if (!f) { return FALSE; // g_pExistingCred is empty } return TRUE; // g_pExistingCred has been filled } /********************************************************************** MapID() Arguments: UINT dialog control ID Returns: UINT string resource number Comments: Convert a dialog control identifier to a string identifier. **********************************************************************/ UINT C_KeyringDlg::MapID(UINT uiID) { switch(uiID) { case IDC_KEYLIST: return IDH_KEYLIST; case IDC_NEWKEY: return IDH_NEW; case IDC_DELETEKEY: return IDH_DELETE; case IDC_CHANGE_PASSWORD: return IDH_CHANGEPASSWORD; case IDC_EDITKEY: return IDH_EDIT; case IDOK: case IDCANCEL: return IDH_CLOSE; default: return IDS_NOHELP; } } ////////////////////////////////////////////////////////////////////////////// // // C_KeyringDlg // // Constructor. // // parameters: // hwndParent parent window for the dialog (may be NULL) // hInstance instance handle of the parent window (may be NULL) // lIDD dialog template id // pfnDlgProc pointer to the function that will process messages for // the dialog. if it is NULL, the default dialog proc // will be used. // // returns: // Nothing. // ////////////////////////////////////////////////////////////////////////////// C_KeyringDlg::C_KeyringDlg( HWND hwndParent, HINSTANCE hInstance, LONG lIDD, DLGPROC pfnDlgProc // = NULL ) : C_Dlg(hwndParent, hInstance, lIDD, pfnDlgProc) { m_hInst = hInstance; // our instance handle m_cCredCount = 0; g_AKdlg = NULL; // addkey dialog not up fInit = FALSE; // initial screen draw undone } // C_KeyringDlg::C_KeyringDlg /********************************************************************** Initialize the keyring UI credential list. Read the credentials currently on the user's keyring and show the targetnames in the list. Called on initial show of the dialog, and again after handling add or delete. Sets the numeric tag for each list entry to equal the credential type. *Session creds are detected here and special handled. Certificate and Passport creds are detected here an a suffix applied to their name. **********************************************************************/ void C_KeyringDlg::BuildList() { DWORD dwCredCount = 0; CREDENTIAL **pCredentialPtrArray = NULL; BOOL bResult = 0; DWORD i,dwCredType; PCREDENTIAL pThisCred = NULL; TCHAR *pTargetName = NULL; LRESULT idx = 0; TCHAR szMsg[64]; #if TESTAUDIT BOOL f34 = FALSE; #endif g_HaveShownRASCred = FALSE; // clear the listbox ::SendDlgItemMessage(m_hDlg,IDC_KEYLIST,LB_RESETCONTENT,NULL,0); bResult = CredEnumerate(NULL,0,&dwCredCount,&pCredentialPtrArray); if ((m_cCredCount != dwCredCount) || (g_fReloadList)) { if (bResult) { for (i=0 ; i < dwCredCount ; i++) { #ifdef LOUDLY if (!bResult) OutputDebugString(L"Keymgr: Adding a cred to the window\n"); #endif pThisCred = pCredentialPtrArray[i]; pTargetName = pThisCred->TargetName; // handle CRED_SESSION_WILDCARD_NAME_W by replacing the string if (0 == _tcsicmp(pTargetName,CRED_SESSION_WILDCARD_NAME)) { if (g_HaveShownRASCred) { CHECKPOINT(41,L"Multiple *Session creds"); continue; } CHECKPOINT(32,L"Keymgr: *Session cred in cred list"); LoadString ( m_hInst, IDS_SESSIONCRED, szMsg, 64 ); pTargetName = szMsg; dwCredType = SESSION_FLAG_VALUE; g_HaveShownRASCred = TRUE; } else { dwCredType = pThisCred->Type; } // name suffixes are localizable // we use g_szTargetName for this in order to avoid another // memory allocation, as this buffer is not yet in use. switch (dwCredType) { case CRED_TYPE_GENERIC: continue; break; // this particular type is not visible in keymgr case CRED_TYPE_DOMAIN_VISIBLE_PASSWORD: { #ifndef SHOWPASSPORT continue; #endif #ifdef SHOWPASSPORT CHECKPOINT(33,L"Keymgr: Passport cred in cred list"); // SHOWPASSPORT is currently turned on _tcsncpy(g_szTargetName,pTargetName,CRED_MAX_DOMAIN_TARGET_NAME_LENGTH); g_szTargetName[CRED_MAX_DOMAIN_TARGET_NAME_LENGTH] = 0; _tcsncat(g_szTargetName,_T(" "),2); _tcsncat(g_szTargetName,g_rgcPassport,MAXSUFFIXSIZE); g_szTargetName[TARGETNAMEMAXLENGTH] = 0; break; #endif } case CRED_TYPE_DOMAIN_PASSWORD: case SESSION_FLAG_VALUE: // find RAS credential #if TESTAUDIT // This checkpoint would be very noisy a lot of the time. // Use f34 to get it to show just once. if (!f34) { CHECKPOINT(34,"Keymgr: Password cred in cred list"); f34 = TRUE; } #endif _tcsncpy(g_szTargetName,pTargetName,CRED_MAX_DOMAIN_TARGET_NAME_LENGTH); g_szTargetName[CRED_MAX_DOMAIN_TARGET_NAME_LENGTH] = 0; break; case CRED_TYPE_DOMAIN_CERTIFICATE: CHECKPOINT(35,"Keymgr: Certificate cred in cred list"); _tcsncpy(g_szTargetName,pTargetName,CRED_MAX_DOMAIN_TARGET_NAME_LENGTH); g_szTargetName[CRED_MAX_DOMAIN_TARGET_NAME_LENGTH] = 0; _tcsncat(g_szTargetName,_T(" "),2); _tcsncat(g_szTargetName,g_rgcCert,MAXSUFFIXSIZE); g_szTargetName[TARGETNAMEMAXLENGTH] = 0; break; default: break; } idx = ::SendDlgItemMessage(m_hDlg,IDC_KEYLIST,LB_ADDSTRING,NULL,(LPARAM) g_szTargetName); if (idx != LB_ERR) { idx = ::SendDlgItemMessage(m_hDlg,IDC_KEYLIST,LB_SETITEMDATA,(WPARAM)idx,dwCredType); } } if (pCredentialPtrArray) CredFree(pCredentialPtrArray); } else { g_CurrentKey = 0; } // Show one as active. SetCurrentKey(g_CurrentKey); g_fReloadList = FALSE; } } /********************************************************************** Set an appropriate state for the buttons on the UI, given the SKU of the platform, and the population of the keyring. If creds exist on the keyring, set the cursor on the key list to the first item. Thereafter, this function permist the last cursor to be reloaded after doing something to the list. The cursor is reset after an add operation, because the behavior of the cursor is difficult to do properly under that circumstance, as you don't know where the item will be inserted in the list. On personal, do not show the ADD button, and change the page text. If no creds, disable the DELETE and PROPERTIES buttons **********************************************************************/ void C_KeyringDlg::SetCurrentKey(LONG_PTR iKey) { LONG_PTR iKeys; HWND hH; LRESULT idx; BOOL fDisabled = FALSE; // If there are items in the list, select the first one and set focus to the list iKeys = ::SendDlgItemMessage ( m_hDlg, IDC_KEYLIST, LB_GETCOUNT, (WPARAM) 0, 0L ); fDisabled = (GetPersistenceOptions(CRED_TYPE_DOMAIN_PASSWORD) == CRED_PERSIST_NONE); #if TESTAUDIT if (iKeys > 100) CHECKPOINT(30,L"Keymgr: Large number of credentials > 100"); if (iKeys == 0) CHECKPOINT(31,L"Keymgr: No saved credentials - list empty"); #endif // If there are no creds and credman is disabled, the dialog should not be displayed // If there are creds, and credman is disabled, show the dialog without the ADD button if (fDisabled && !fInit) { // (Disable with HKLM\System\CurrentControlSet\Control\Lsa\DisableDomainCreds = 1) CHECKPOINT(36,L"Keymgr: Personal SKU or credman disabled"); // Make the intro text better descriptive of this condition WCHAR szMsg[MAX_STRING_SIZE+1]; LoadString ( m_hInst, IDS_INTROTEXT, szMsg, MAX_STRING_SIZE ); hH = GetDlgItem(m_hDlg,IDC_INTROTEXT); if (hH) SetWindowText(hH,szMsg); // remove the add button hH = GetDlgItem(m_hDlg,IDC_NEWKEY); if (hH) { EnableWindow(hH,FALSE); ShowWindow(hH,SW_HIDE); } // move remaining buttons upfield 22 units hH = GetDlgItem(m_hDlg,IDC_DELETEKEY); if (hH) { HWND hw1; HWND hw2; RECT rw1; RECT rw2; INT xsize; INT ysize; INT delta; BOOL bOK = FALSE; hw1 = hH; hw2 = GetDlgItem(m_hDlg,IDC_EDITKEY); if (hw1 && hw2) { if (GetWindowRect(hw1,&rw1) && GetWindowRect(hw2,&rw2)) { MapWindowPoints(NULL,m_hDlg,(LPPOINT)(&rw1),2); MapWindowPoints(NULL,m_hDlg,(LPPOINT)(&rw2),2); delta = rw2.top - rw1.top; xsize = rw2.right - rw2.left; ysize = rw2.bottom - rw2.top; bOK = MoveWindow(hw1,rw1.left,rw1.top - delta,xsize,ysize,TRUE); if (bOK) { bOK = MoveWindow(hw2,rw2.left,rw2.top - delta,xsize,ysize,TRUE); } } } } // prevent moving the buttons twice fInit = TRUE; } // Set the default button to either properties or add if ( iKeys > 0 ) { hH = GetDlgItem(m_hDlg,IDC_KEYLIST); SetFocus(hH); // if asking for key beyond end of list, mark the last one if (iKey >= iKeys) iKey = iKeys - 1; idx = SendDlgItemMessage ( m_hDlg, IDC_KEYLIST, LB_SETCURSEL, iKey, 0L ); hH = GetDlgItem(m_hDlg,IDC_EDITKEY); if (hH) EnableWindow(hH,TRUE); hH = GetDlgItem(m_hDlg,IDC_DELETEKEY); if (hH) EnableWindow(hH,TRUE); } else { if (!fDisabled) { // no items in the list, set focus to the New button hH = GetDlgItem(m_hDlg,IDC_NEWKEY); SetFocus(hH); } hH = GetDlgItem(m_hDlg,IDC_EDITKEY); if (hH) EnableWindow(hH,FALSE); hH = GetDlgItem(m_hDlg,IDC_DELETEKEY); if (hH) EnableWindow(hH,FALSE); } } // Remove the currently highlighted key from the listbox /********************************************************************** DeleteKey() Arguments: None Returns: None Comments: Deletes the credential currently selected in the list box. **********************************************************************/ void C_KeyringDlg::DeleteKey() { TCHAR szMsg[MAX_STRING_SIZE + MAXSUFFIXSIZE] = {0}; TCHAR szTitle[MAX_STRING_SIZE] = {0};; TCHAR *pC; // point this to the raw name LONG_PTR lR = LB_ERR; LONG_PTR lSel = LB_ERR; BOOL bResult = FALSE; DWORD dwCredType = 0; CHECKPOINT(37,L"Keymgr: Delete a credential"); LoadString ( m_hInst, IDS_DELETEWARNING, szMsg, MAX_STRING_SIZE ); LoadString ( m_hInst, IDS_APP_NAME, szTitle, MAX_STRING_SIZE ); // ask for confirm to delete lR = MessageBox ( m_hDlg, szMsg, szTitle, MB_OKCANCEL ); if (IDOK != lR) { return; } // Get the credential type information from the item data lSel = SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETCURSEL,0,0L); if (lSel == LB_ERR) { ASSERT(0); goto faildelete; } g_CurrentKey = lSel; lR = SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETTEXT,lSel,(LPARAM) g_szTargetName); if (lR == LB_ERR) { ASSERT(0); goto faildelete; } dwCredType = (DWORD) SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETITEMDATA,lSel,0); if (LB_ERR == dwCredType) { ASSERT(0); goto faildelete; } // Special case RAS creds, because there can be more than one. We only show a single entry, // and deleting it automatically seeks and deletes the other if it is present. For this reason, the // type information associated with this cred is a special value, and not use in the delete. if (dwCredType == SESSION_FLAG_VALUE) { CHECKPOINT(42,L"Delete session cred"); // convert the name from user friendly to the internal representation _tcsncpy(g_szTargetName,CRED_SESSION_WILDCARD_NAME,TARGETNAMEMAXLENGTH); g_szTargetName[TARGETNAMEMAXLENGTH - 1] = 0; // bResult will be success if either deleted successfully bResult = CredDelete(g_szTargetName,CRED_TYPE_DOMAIN_PASSWORD,0); if (!bResult) { bResult = CredDelete(g_szTargetName,CRED_TYPE_DOMAIN_CERTIFICATE,0); } else { CredDelete(g_szTargetName,CRED_TYPE_DOMAIN_CERTIFICATE,0); } } else { // null term the targetname shown in the UI, // trimming the suffix if there is one pC = _tcschr(g_szTargetName,g_rgcCert[0]); if (pC) { pC--; *pC = 0x0; // null terminate namestring } // Delete the single credential on the cursor bResult = CredDelete(g_szTargetName,dwCredType,0); } faildelete: if (bResult != TRUE) { LoadString ( m_hInst, IDS_DELETEFAILED, szMsg, MAX_STRING_SIZE ); LoadString ( m_hInst, IDS_APP_NAME, szTitle, MAX_STRING_SIZE ); MessageBox ( m_hDlg, szMsg, szTitle, MB_OK); } else { // successful delete - resort and re-present the list g_fReloadList = TRUE; } } /********************************************************************** OnAppMessage() Arguments: None Returns: BOOL always TRUE Comments: Empty handler for method of class. **********************************************************************/ BOOL C_KeyringDlg::OnAppMessage( UINT uMessage, WPARAM wparam, LPARAM lparam ) { return TRUE; } // OnAppMessage ////////////////////////////////////////////////////////////////////////////// // // OnInitDialog // // Dialog control and data initialization. // // parameters: // hwndDlg window handle of the dialog box // hwndFocus window handle of the control that will receive focus // // returns: // TRUE if the system should set the default keyboard focus // FALSE if the keyboard focus is set by this app // ////////////////////////////////////////////////////////////////////////////// BOOL C_KeyringDlg::OnInitDialog( HWND hwndDlg, HWND hwndFocus ) { // these really should all be in the keyringdlg class DWORD i; LRESULT lr; HtmlHelp(NULL,NULL,HH_INITIALIZE,(DWORD_PTR) &g_dwHCookie); // Allow other dialog to query the contents of the listbox g_hMainDlg = hwndDlg; m_hDlg = hwndDlg; g_CurrentKey = 0; g_fReloadList = TRUE; g_szTargetName = (TCHAR *) malloc((TARGETNAMEMAXLENGTH + 1) * sizeof(TCHAR)); ASSERT(g_szTargetName); if (NULL == g_szTargetName) { return FALSE; } // Fetch Icons from the image and assoc them with this dialog HICON hI = LoadIcon(m_hInst,MAKEINTRESOURCE(IDI_SMALL)); lr = SendMessage(hwndDlg,WM_SETICON,(WPARAM) ICON_SMALL,(LPARAM)hI); C_Dlg::OnInitDialog(hwndDlg, hwndFocus); // Even if mirrored language is default, set list box style to LTR // (NOT CURRENTLY TURNED ON) #ifdef FORCELISTLTR { LONG_PTR lExStyles; HWND hwList; hwList = GetDlgItem(hwndDlg,IDC_KEYLIST); if (hwList) { lExStyles = GetWindowLongPtr(hwList,GWL_EXSTYLE); lExStyles &= ~WS_EX_RTLREADING; SetWindowLongPtr(hwList,GWL_EXSTYLE,lExStyles); InvalidateRect(hwList,NULL,TRUE); } } #endif // read in the suffix strings for certificate types // locate first differing character // // This code assumes that the strings all have a common preamble, // and that all are different in the first character position // past the preamble. Localized strings should be selected which // have this property, like (Generic) and (Certificate). i = LoadString(g_hInstance,IDS_CERTSUFFIX,g_rgcCert,MAXSUFFIXSIZE); ASSERT(i !=0); i = LoadString(g_hInstance,IDS_PASSPORTSUFFIX,g_rgcPassport,MAXSUFFIXSIZE); // Read currently saved creds and display names in list box BuildList(); SetCurrentKey(g_CurrentKey); InitTooltips(); return TRUE; } // C_KeyringDlg::OnInitDialog /********************************************************************** OnDestroyDialog Arguments: None Returns: BOOL, always TRUE Comments: Performs cleanup needed as dialog is destroyed. In this case, its only action is to release the HTML Help resources. **********************************************************************/ BOOL C_KeyringDlg::OnDestroyDialog( void ) { free(g_szTargetName); HtmlHelp(NULL,NULL,HH_UNINITIALIZE,(DWORD_PTR)g_dwHCookie); return TRUE; } /********************************************************************** DoEdit() Arguments: None Returns: BOOL always TRUE Comments: Filters some special creds on the basis of their special nature. For the editable ones, kicks off the edit dialog to edit the credential held at g_pExistingCred. **********************************************************************/ BOOL C_KeyringDlg::DoEdit(void) { LRESULT lR; lR = SendDlgItemMessage(m_hDlg,IDC_KEYLIST,LB_GETCURSEL,0,0L); if (LB_ERR == lR) { // On error, no dialog shown, edit command handled return TRUE; } else { // something selected g_CurrentKey = lR; // If a RAS cred, show it specially, indicate no edit allowed lR = SendDlgItemMessage(m_hDlg,IDC_KEYLIST,LB_GETITEMDATA,lR,0); if (lR == SESSION_FLAG_VALUE) { CHECKPOINT(38,L"Keymgr: Attempt edit a RAS cred"); // load string and display message box TCHAR szMsg[MAX_STRING_SIZE]; TCHAR szTitle[MAX_STRING_SIZE]; LoadString ( m_hInst, IDS_APP_NAME, szTitle, MAX_STRING_SIZE ); LoadString ( m_hInst, IDS_CANNOTEDIT, szMsg, MAX_STRING_SIZE ); MessageBox ( m_hDlg, szMsg, szTitle, MB_OK ); return TRUE; } #ifdef SHOWPASSPORT #ifdef NEWPASSPORT // if a passport cred, show it specially, indicate no edit allowed if (lR == CRED_TYPE_DOMAIN_VISIBLE_PASSWORD) { CHECKPOINT(39,L"Keymgr: Attempt edit a passport cred"); // load string and display message box TCHAR szMsg[MAX_STRING_SIZE]; TCHAR szTitle[MAX_STRING_SIZE]; LoadString ( m_hInst, IDS_APP_NAME, szTitle, MAX_STRING_SIZE ); LoadString ( m_hInst, IDS_PASSPORT2, szMsg, MAX_STRING_SIZE ); INT iResponse = MessageBox ( m_hDlg, szMsg, szTitle, MB_YESNO ); if (IDYES == iResponse) { CHECKPOINT(40,L"Keymgr: Launch passport website for Passport cred edit"); HKEY hKey = NULL; DWORD dwType; //BYTE rgb[500]; BYTE *rgb=(BYTE *) malloc(INTERNET_MAX_URL_LENGTH * sizeof(WCHAR)); if (rgb) { DWORD cbData = INTERNET_MAX_URL_LENGTH * sizeof(WCHAR); BOOL Flag = TRUE; // launch the passport web site #ifndef PASSPORTURLINREGISTRY ShellExecute(m_hDlg,L"open",L"http://www.passport.com",NULL,NULL,SW_SHOWNORMAL); #else // read registry key to get target string for ShellExec if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Passport", 0, KEY_QUERY_VALUE, &hKey)) { if (ERROR_SUCCESS == RegQueryValueEx(hKey, L"Properties", NULL, &dwType, rgb, &cbData)) { // test the URL for reasonableness before launching WCHAR *szUrl = (WCHAR *)malloc(INTERNET_MAX_URL_LENGTH); if (szUrl) { DWORD ccUrlBuffer = INTERNET_MAX_URL_LENGTH; if (S_OK == UrlCanonicalize((LPCTSTR)rgb, szUrl,&ccUrlBuffer, URL_ESCAPE_UNSAFE | URL_ESCAPE_PERCENT)) { if (UrlIs(szUrl,URLIS_URL)) { WCHAR szScheme[20]; DWORD ccScheme = 20; if (SUCCEEDED(UrlGetPart(szUrl,szScheme,&ccScheme,URL_PART_SCHEME,0))) { // at the least, verify that the target is https schemed if (0 == _wcsicmp(szScheme,L"https")) { ShellExecute(m_hDlg,L"open",(LPCTSTR)rgb,NULL,NULL,SW_SHOWNORMAL); Flag = FALSE; } } } } free(szUrl); } } } #ifdef LOUDLY else { OutputDebugString(L"DoEdit: reg key HKCU... open failed\n"); } #endif if (Flag) { if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Passport", 0, KEY_QUERY_VALUE, &hKey)) { if (ERROR_SUCCESS == RegQueryValueEx(hKey, L"Properties", NULL, &dwType, rgb, &cbData)) { // test the URL for reasonableness before launching WCHAR *szUrl = (WCHAR *) malloc(INTERNET_MAX_URL_LENGTH); if (szUrl) { DWORD ccUrlBuffer = INTERNET_MAX_URL_LENGTH; if (S_OK == UrlCanonicalize((LPCTSTR)rgb, szUrl,&ccUrlBuffer,0)) { if (UrlIs(szUrl,URLIS_URL)) { WCHAR szScheme[20]; DWORD ccScheme = 20; if (SUCCEEDED(UrlGetPart(szUrl,szScheme,&ccScheme,URL_PART_SCHEME,0))) { if (0 == _wcsicmp(szScheme,L"https")) { // at the least, verify that the target is https scheme ShellExecute(m_hDlg,L"open",(LPCTSTR)rgb,NULL,NULL,SW_SHOWNORMAL); Flag = FALSE; } } } } free(szUrl); } } } #ifdef LOUDLY else { OutputDebugString(L"DoEdit: reg key HKLM... open failed\n"); } #endif } if (Flag) { LoadString ( m_hInst, IDS_APP_NAME, szTitle, MAX_STRING_SIZE ); LoadString ( m_hInst, IDS_PASSPORTNOURL, szMsg, MAX_STRING_SIZE ); MessageBox ( m_hDlg, szMsg, szTitle, MB_ICONHAND ); #ifdef LOUDLY OutputDebugString(L"DoEdit: Passport URL missing\n"); #endif } #endif free(rgb); } else { // out of memory - nothing we can do return TRUE; } } return TRUE; } #else // if a passport cred, show it specially, indicate no edit allowed if (lR == CRED_TYPE_DOMAIN_VISIBLE_PASSWORD) { // load string and display message box TCHAR szMsg[MAX_STRING_SIZE]; TCHAR szTitle[MAX_STRING_SIZE]; LoadString ( m_hInst, IDS_APP_NAME, szTitle, MAX_STRING_SIZE ); LoadString ( m_hInst, IDS_PASSPORT, szMsg, MAX_STRING_SIZE ); MessageBox ( m_hDlg, szMsg, szTitle, MB_OK ); return TRUE; } #endif #endif } // cred is selected, not special type. Attempt to read it if (FALSE == gTestReadCredential()) { return TRUE; } g_AKdlg = new C_AddKeyDlg(g_hMainDlg,g_hInstance,IDD_ADDCRED,NULL); if (NULL == g_AKdlg) { // failed to instantiate add/new dialog if (g_pExistingCred) CredFree(g_pExistingCred); g_pExistingCred = NULL; return TRUE; } else { // read OK, dialog OK, proceed with edit dlg g_AKdlg->m_bEdit = TRUE; g_AKdlg->DoModal((LPARAM)g_AKdlg); // a credential name may have changed, so reload the list delete g_AKdlg; g_AKdlg = NULL; if (g_pExistingCred) { CredFree(g_pExistingCred); } g_pExistingCred = NULL; } return TRUE; } ////////////////////////////////////////////////////////////////////////////// // // OnCommand // // Route WM_COMMAND message to appropriate handlers. // // parameters: // wNotifyCode code describing action that has occured // wSenderId id of the control sending the message, if the message // is from a dialog // hwndSender window handle of the window sending the message if the // message is not from a dialog // // returns: // TRUE if the message was processed completely // FALSE if Windows is to process the message // ////////////////////////////////////////////////////////////////////////////// BOOL C_KeyringDlg::OnHelpInfo(LPARAM lp) { HELPINFO* pH; INT iMapped; pH = (HELPINFO *) lp; HH_POPUP stPopUp; RECT rcW; UINT gID; CHECKPOINT(15,"Keymgr: Main dialog OnHelpInfo"); gID = pH->iCtrlId; iMapped = MapID(gID); if (iMapped == 0) { return TRUE; } if (IDS_NOHELP != iMapped) { memset(&stPopUp,0,sizeof(stPopUp)); stPopUp.cbStruct = sizeof(HH_POPUP); stPopUp.hinst = g_hInstance; stPopUp.idString = iMapped; stPopUp.pszText = NULL; stPopUp.clrForeground = -1; stPopUp.clrBackground = -1; stPopUp.rcMargins.top = -1; stPopUp.rcMargins.bottom = -1; stPopUp.rcMargins.left = -1; stPopUp.rcMargins.right = -1; // bug 393244 - leave NULL to allow HHCTRL.OCX to get font information of its own, // which it needs to perform the UNICODE to multibyte conversion. Otherwise, // HHCTRL must convert using this font without charset information. stPopUp.pszFont = NULL; if (GetWindowRect((HWND)pH->hItemHandle,&rcW)) { stPopUp.pt.x = (rcW.left + rcW.right) / 2; stPopUp.pt.y = (rcW.top + rcW.bottom) / 2; } else stPopUp.pt = pH->MousePos; HtmlHelp((HWND) pH->hItemHandle,NULL,HH_DISPLAY_TEXT_POPUP,(DWORD_PTR) &stPopUp); } return TRUE; } // code for handling linkage to a .chm file is disabled #if 1 BOOL C_KeyringDlg::OnHelpButton(void) { return FALSE; } #else BOOL C_KeyringDlg::OnHelpButton(void) { TCHAR rgc[MAX_PATH + 1]; TCHAR rgcHelpFile[]=TEXT("\\keyhelp.chm"); INT ccHelp = _tcslen(rgcHelpFile); GetSystemDirectory(rgc,MAX_PATH); if (_tcslen(rgc) + ccHelp > MAX_PATH) { return FALSE; } _tcsncat(rgc, rgcHelpFile, ccHelp + 1); rgc[MAX_PATH - 1] = 0; HWND hwnd = (m_hwnd,rgc,HH_DISPLAY_TOC,NULL); if (NULL != hwnd) return TRUE; return FALSE; } #endif /********************************************************************** OnCommand() Arguments: None Returns: BOOL always TRUE Comments: Dispatcher for button presses and help requests. **********************************************************************/ BOOL C_KeyringDlg::OnCommand( WORD wNotifyCode, WORD wSenderId, HWND hwndSender ) { // Was the message handled? // BOOL fHandled = FALSE; switch (wSenderId) { case IDC_HELPKEY: OnHelpButton(); break; case IDC_KEYLIST: if (LBN_SELCHANGE == wNotifyCode) break; if (LBN_DBLCLK == wNotifyCode) { fHandled = DoEdit(); BuildList(); // targetname could have changed SetCurrentKey(g_CurrentKey); break; } case IDCANCEL: case IDOK: if (BN_CLICKED == wNotifyCode) { OnOK( ); fHandled = TRUE; } break; case IDC_EDITKEY: { fHandled = DoEdit(); BuildList(); // targetname could have changed SetCurrentKey(g_CurrentKey); break; } // NEW and DELETE can alter the count of creds, and the button population case IDC_NEWKEY: { g_pExistingCred = NULL; g_AKdlg = new C_AddKeyDlg(g_hMainDlg,g_hInstance,IDD_ADDCRED,NULL); if (NULL == g_AKdlg) { fHandled = TRUE; break; } else { g_AKdlg->m_bEdit = FALSE; g_AKdlg->DoModal((LPARAM)g_AKdlg); // a credential name may have changed delete g_AKdlg; g_AKdlg = NULL; BuildList(); SetCurrentKey(g_CurrentKey); } break; } break; case IDC_DELETEKEY: DeleteKey(); // frees g_pExistingCred as a side effect // refresh list display BuildList(); SetCurrentKey(g_CurrentKey); break; } // switch return fHandled; } // C_KeyringDlg::OnCommand ////////////////////////////////////////////////////////////////////////////// // // OnOK // // Validate user name, synthesize computer name, and destroy dialog. // // parameters: // None. // // returns: // Nothing. // ////////////////////////////////////////////////////////////////////////////// void C_KeyringDlg::OnOK( ) { ASSERT(::IsWindow(m_hwnd)); EndDialog(IDOK); } // C_KeyringDlg::OnOK ////////////////////////////////////////////////////////////////////////////// // // ToolTip Support // // ////////////////////////////////////////////////////////////////////////////// /********************************************************************** InitToolTips() Derive a bounding rectangle for the nth element of a list box, 0 based. Refuse to generate rectangles for nonexistent elements. Return TRUE if a rectangle was generated, otherwise FALSE. **********************************************************************/ BOOL C_KeyringDlg::InitTooltips(void) { TOOLINFO ti; memset(&ti,0,sizeof(TOOLINFO)); ti.cbSize = sizeof(TOOLINFO); INT n = 0; RECT rLB; // list box bounding rect for client portion HWND hLB = GetDlgItem(m_hDlg,IDC_KEYLIST); if (NULL == hLB) { return FALSE; } // Create the tooltip window that will be activated and shown when // a tooltip is displayed HWND hwndTip = CreateWindowEx(NULL,TOOLTIPS_CLASS,NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, m_hDlg,NULL,m_hInstance, NULL); if (NULL == hwndTip) { return FALSE; } SetWindowPos(hwndTip,HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); // Subclass the list box here in order to get the TTN_GETDISPINFO notification lpfnOldWindowProc = (WNDPROC) SetWindowLongPtr(hLB,GWLP_WNDPROC,(LONG_PTR) ListBoxSubClassFunction); INT_PTR iHeight = SendMessage(hLB,LB_GETITEMHEIGHT,0,0); if ((LB_ERR == iHeight) || (iHeight == 0)) { return FALSE; } if (!GetClientRect(hLB,&rLB)) { return FALSE; } INT_PTR m = rLB.bottom - rLB.top; // unit count client area height m = m/iHeight; // find out how many items INT_PTR i; // loop control LONG itop = 0; // top of tip item rect for (i=0 ; i < m ; i++) { ti.uFlags = TTF_SUBCLASS; ti.hwnd = hLB; // window that gets the TTN_GETDISPINFO ti.uId = IDC_KEYLIST; ti.hinst = m_hInstance; ti.lpszText = LPSTR_TEXTCALLBACK; ti.rect.top = itop; ti.rect.bottom = itop + (LONG) iHeight - 1; ti.rect.left = rLB.left; ti.rect.right = rLB.right; itop += (LONG) iHeight; ti.lParam = (LPARAM) n++; #ifdef LOUDLY2 OutputDebugString(L"Adding a tip control region\n"); _stprintf(szTemp,L"top = %d bottom = %d left = %d right = %d\n",ti.rect.top,ti.rect.bottom,ti.rect.left,ti.rect.right); OutputDebugString(szTemp); #endif // Add the keylist to the tool list as a single unit SendMessage(hwndTip,TTM_ADDTOOL,(WPARAM) 0,(LPARAM)(LPTOOLINFO)&ti); } return TRUE; } /********************************************************************** // Get item number from pD->lParam // Fetch that text string from listbox at pD->hwnd // trim suffix // Call translation API // Write back the string **********************************************************************/ BOOL SetToolText(NMTTDISPINFO *pD) { CREDENTIAL *pCred = NULL; // used to read cred under mouse ptr INT_PTR iWhich; // which index into list HWND hLB; // list box hwnd //NMHDR *pHdr; // notification msg hdr TCHAR rgt[TIPSTRINGLEN]; // temp string for tooltip TCHAR szCredName[TARGETNAMEMAXLENGTH]; // credname TCHAR *pszTargetName; // ptr to target name in pCred DWORD dwType; // type of target cred TCHAR *pC; // used for suffix trimming BOOL f; // used for suffix trimming LRESULT lRet; // api ret value ULONG ulOutSize; // ret from CredpValidateTargetName() WILDCARD_TYPE OutType; // enum type to receive ret from api UNICODE_STRING OutUString; // UNICODESTRING to package ret from api WCHAR *pwc; UINT iString; // resource # of string TCHAR rgcFormat[TIPSTRINGLEN]; // Hold tooltip template string NTSTATUS ns; //pHdr = &(pD->hdr); hLB = GetDlgItem(g_hMainDlg,IDC_KEYLIST); iWhich = SendMessage(hLB,LB_GETTOPINDEX,0,0); iWhich += pD->lParam; #ifdef LOUDLY TCHAR rga[100]; _stprintf(rga,L"Text reqst for %d\n",iWhich); OutputDebugString(rga); #endif // Read the indicated cred from the store, by first fetching the name string and type // from the listbox lRet = SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETTEXT,iWhich,(LPARAM) szCredName); if ((LB_ERR == lRet) || (0 == lRet)) { return FALSE; } dwType = (DWORD) SendDlgItemMessage(g_hMainDlg,IDC_KEYLIST,LB_GETITEMDATA,iWhich,0); #ifdef LOUDLY OutputDebugString(L"Target: "); OutputDebugString(szCredName); OutputDebugString(L"\n"); #endif // null term the targetname, trimming the suffix if there is one pC = _tcschr(szCredName,g_rgcCert[0]); if (pC) { pC--; *pC = 0x0; // null terminate namestring } #ifdef LOUDLY OutputDebugString(L"Trimmed target: "); OutputDebugString(szCredName); OutputDebugString(L"\n"); #endif // For special cred, replace the credname with a special string if (dwType == SESSION_FLAG_VALUE) { _tcsncpy(szCredName,CRED_SESSION_WILDCARD_NAME,TARGETNAMEMAXLENGTH - 1); dwType = CRED_TYPE_DOMAIN_PASSWORD; } // Attempt to read the credential from the store // The returned credential will have to be freed if leaving this block f = (CredRead(szCredName, (ULONG) dwType , 0, &pCred)); if (!f) { return FALSE; } #ifdef LOUDLY if (f) OutputDebugString(L"Successful Cred Read\n"); #endif // clear tip strings szTipString[0] = 0; rgt[0] = 0; #ifndef SIMPLETOOLTIPS pszTargetName = pCred->TargetName; if (NULL == pszTargetName) return FALSE; ns = CredpValidateTargetName( pCred->TargetName, pCred->Type, MightBeUsernameTarget, NULL, NULL, &ulOutSize, &OutType, &OutUString); if (!SUCCEEDED(ns)) { return FALSE; } pwc = OutUString.Buffer; switch (OutType) { case WcDfsShareName: iString = IDS_TIPDFS; break; case WcServerName: iString = IDS_TIPSERVER; break; case WcServerWildcard: iString = IDS_TIPTAIL; pwc++; // trim off the leading '.' break; case WcDomainWildcard: iString = IDS_TIPDOMAIN; break; case WcUniversalSessionWildcard: iString = IDS_TIPDIALUP; break; case WcUniversalWildcard: iString = IDS_TIPOTHER; break; case WcUserName: iString = IDS_TIPUSER; break; default: ASSERT(0); iString = 0; break; } // Show tip text unless we fail to get the string // On fail, show the username if (0 != LoadString(g_hInstance,iString,rgcFormat,TIPSTRINGLEN)) { _stprintf(rgt,rgcFormat,pwc); } else { if (0 != LoadString(g_hInstance,IDS_LOGASUSER,rgcFormat,500)) { _stprintf(rgt,rgcFormat,iWhich,pCred->UserName); } else { rgt[0] = 0; } } #endif #ifdef LOUDLY OutputDebugString(L"Tip text:"); //OutputDebugString(pCred->UserName); OutputDebugString(rgt); OutputDebugString(L"\n"); #endif if (rgt[0] == 0) { if (pCred) CredFree(pCred); return FALSE; } //_tcscpy(szTipString,pCred->UserName); // copy to a more persistent buffer _tcsncpy(szTipString,rgt,TIPSTRINGLEN - 1); // copy to a more persistent buffer pD->lpszText = szTipString; // point the response to it pD->hinst = NULL; if (pCred) { CredFree(pCred); } return TRUE; } /********************************************************************** ListBoxSubClassFunction() Arguments: None Returns: BOOL always TRUE Comments: Message handler subclassing function for the list box, which intercepts requests for tooltip display information and processes them. **********************************************************************/ LRESULT CALLBACK ListBoxSubClassFunction(HWND hW,WORD Message,WPARAM wparam,LPARAM lparam) { if (Message == WM_NOTIFY) { if ((int) wparam == IDC_KEYLIST) { NMHDR *pnm = (NMHDR *) lparam; if (pnm->code == TTN_GETDISPINFO) { NMTTDISPINFO *pDi; pDi = (NMTTDISPINFO *) pnm; SetToolText(pDi); } } } return CallWindowProc(lpfnOldWindowProc,hW,Message,wparam,lparam); }