//============================================================================= // Copyright (c) 2000 Microsoft Corporation // // controls.cpp // // User interface control classes. // // Created 02/29/2000 johnstep (John Stephens) //============================================================================= #include "precomp.hpp" #include "controls.hpp" #include "resource.h" #include "utils.hpp" #include #include //----------------------------------------------------------------------------- // Values //----------------------------------------------------------------------------- // These are the positions of the children controls of the credential control, // in DLUs: // make more space to localize the edit control tags. #define SIZEFIX 20 #define CREDUI_CONTROL_USERNAME_STATIC_X 0 #define CREDUI_CONTROL_USERNAME_STATIC_Y 2 #define CREDUI_CONTROL_USERNAME_STATIC_WIDTH (48 + SIZEFIX) #define CREDUI_CONTROL_USERNAME_STATIC_HEIGHT 8 #define CREDUI_CONTROL_USERNAME_X (50 + SIZEFIX) #define CREDUI_CONTROL_USERNAME_Y 0 #define CREDUI_CONTROL_USERNAME_WIDTH (121 - SIZEFIX) #define CREDUI_CONTROL_USERNAME_HEIGHT 96 #define CREDUI_CONTROL_VIEW_X 175 #define CREDUI_CONTROL_VIEW_Y 0 #define CREDUI_CONTROL_VIEW_WIDTH 13 #define CREDUI_CONTROL_VIEW_HEIGHT 13 #define CREDUI_CONTROL_PASSWORD_STATIC_X 0 #define CREDUI_CONTROL_PASSWORD_STATIC_Y 19 #define CREDUI_CONTROL_PASSWORD_STATIC_WIDTH (48 + SIZEFIX) #define CREDUI_CONTROL_PASSWORD_STATIC_HEIGHT 8 #define CREDUI_CONTROL_PASSWORD_X (50 + SIZEFIX) #define CREDUI_CONTROL_PASSWORD_Y 17 #define CREDUI_CONTROL_PASSWORD_WIDTH (121 - SIZEFIX) #define CREDUI_CONTROL_PASSWORD_HEIGHT 12 #define CREDUI_CONTROL_SAVE_X (50 + SIZEFIX) #define CREDUI_CONTROL_SAVE_Y 36 #define CREDUI_CONTROL_SAVE_WIDTH 138 #define CREDUI_CONTROL_SAVE_HEIGHT 10 // Use a common maximum string length for certificate display names: #define CREDUI_MAX_CERT_NAME_LENGTH 256 #define CREDUI_MAX_CMDLINE_MSG_LENGTH 256 //----------------------------------------------------------------------------- // Global Variables //----------------------------------------------------------------------------- CLSID CreduiStringArrayClassId = // 82BD0E67-9FEA-4748-8672-D5EFE5B779B0 { 0x82BD0E67, 0x9FEA, 0x4748, {0x86, 0x72, 0xD5, 0xEF, 0xE5, 0xB7, 0x79, 0xB0} }; // Balloon tip infos for PasswordBox control: CONST CREDUI_BALLOON_TIP_INFO CreduiCapsLockTipInfo = { CreduiStrings.CapsLockTipTitle, CreduiStrings.CapsLockTipText, TTI_WARNING, 90, 76 }; // Balloon tip infos for Credential control: CONST CREDUI_BALLOON_TIP_INFO CreduiBackwardsTipInfo = { CreduiStrings.BackwardsTipTitle, CreduiStrings.BackwardsTipText, TTI_ERROR, 90, 76 }; WCHAR CreduiCustomTipTitle[CREDUI_MAX_BALLOON_TITLE_LENGTH + 1]; WCHAR CreduiCustomTipMessage[CREDUI_MAX_BALLOON_MESSAGE_LENGTH + 1]; CREDUI_BALLOON_TIP_INFO CreduiCustomTipInfo = { CreduiCustomTipTitle, CreduiCustomTipMessage, TTI_INFO, 90, 76 }; //----------------------------------------------------------------------------- // CreduiBalloonTip Class Implementation //----------------------------------------------------------------------------- //============================================================================= // CreduiBalloonTip::CreduiBalloonTip // // Created 02/24/2000 johnstep (John Stephens) //============================================================================= CreduiBalloonTip::CreduiBalloonTip() { Window = NULL; ParentWindow = NULL; ControlWindow = NULL; TipInfo = NULL; Visible = FALSE; } //============================================================================= // CreduiBalloonTip::~CreduiBalloonTip // // Created 02/24/2000 johnstep (John Stephens) //============================================================================= CreduiBalloonTip::~CreduiBalloonTip() { if (Window != NULL) { DestroyWindow(Window); Window = NULL; } } //============================================================================= // CreduiBalloonTip::Init // // Creates and initializes the balloon window. // // Arguments: // instance (in) - this module // parentWindow (in) - the parent of the tool tip window // // Returns TRUE on success or FALSE otherwise. // // Created 02/24/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiBalloonTip::Init( HINSTANCE instance, HWND parentWindow ) { if (Window != NULL) { DestroyWindow(Window); Window = NULL; ParentWindow = NULL; ControlWindow = NULL; TipInfo = NULL; Visible = FALSE; } Window = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, parentWindow, NULL, instance, NULL); // Only assign class member values once we have successfully created the // window: if (Window != NULL) { ParentWindow = parentWindow; TipInfo = NULL; return TRUE; } return FALSE; } //============================================================================= // CreduiBalloonTip::SetInfo // // Sets the tool tip information and adds or updates the tool. // // Returns TRUE on success or FALSE otherwise. // // Created 03/02/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiBalloonTip::SetInfo( HWND controlWindow, CONST CREDUI_BALLOON_TIP_INFO *tipInfo ) { //if ((controlWindow != ControlWindow) || (tipInfo != TipInfo)) { TOOLINFO info; ZeroMemory(&info, sizeof info); info.cbSize = sizeof info; info.hwnd = ParentWindow; info.uId = reinterpret_cast(ParentWindow); // If the tool already exists, hide it, then update the information. // Otherwise, add the tool now: if (SendMessage(Window, TTM_GETTOOLINFO, 0, reinterpret_cast(&info))) { if (Visible) { Hide(); } ZeroMemory(&info, sizeof info); info.cbSize = sizeof info; info.hwnd = ParentWindow; info.uId = reinterpret_cast(ParentWindow); info.uFlags = TTF_IDISHWND | TTF_TRACK; info.hinst = NULL; info.lpszText = const_cast(tipInfo->Text); info.lParam = 0; SendMessage(Window, TTM_SETTOOLINFO, 0, reinterpret_cast(&info)); } else { info.uFlags = TTF_IDISHWND | TTF_TRACK; info.hinst = NULL; info.lpszText = const_cast(tipInfo->Text); info.lParam = 0; if (!SendMessage(Window, TTM_ADDTOOL, 0, reinterpret_cast(&info))) { return FALSE; } } SendMessage(Window, TTM_SETTITLE, tipInfo->Icon, reinterpret_cast(tipInfo->Title)); TipInfo = const_cast(tipInfo); ControlWindow = controlWindow; } return TRUE; } //============================================================================= // CreduiBalloonTip::Show // // Updates the position of the balloon window, and then displays it. // // Returns TRUE on success or FALSE otherwise. // // Created 02/24/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiBalloonTip::Show() { if (!Visible && IsWindowEnabled(ControlWindow)) { SetFocus(ControlWindow); RECT rect; GetWindowRect(ControlWindow, &rect); SendMessage(Window, TTM_TRACKPOSITION, 0, MAKELONG( rect.left + TipInfo->XPercent * (rect.right - rect.left) / 100, rect.top + TipInfo->YPercent * (rect.bottom - rect.top) / 100)); TOOLINFO info; ZeroMemory(&info, sizeof info); info.cbSize = sizeof info; info.hwnd = ParentWindow; info.uId = reinterpret_cast(ParentWindow); SendMessage(Window, TTM_TRACKACTIVATE, TRUE, reinterpret_cast(&info)); Visible = TRUE; } return TRUE; } //============================================================================= // CreduiBalloonTip::Hide // // Hides the balloon window. // // Returns TRUE on success or FALSE otherwise. // // Created 02/24/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiBalloonTip::Hide() { if (Visible) { SendMessage(Window, TTM_TRACKACTIVATE, (WPARAM) FALSE, 0); Visible = FALSE; if (ParentWindow) { HWND hD = GetParent(ParentWindow); if (hD) { InvalidateRgn(hD,NULL,FALSE); UpdateWindow(hD); } } } return TRUE; } //----------------------------------------------------------------------------- // CreduiPasswordBox Class Implementation //----------------------------------------------------------------------------- //============================================================================= // CreduiPasswordBox::CreduiPasswordBox // // Created 06/06/2000 johnstep (John Stephens) //============================================================================= CreduiPasswordBox::CreduiPasswordBox() { OriginalMessageHandler = NULL; Window = NULL; PasswordFont = NULL; BalloonTip = NULL; CapsLockTipInfo = NULL; } //============================================================================= // CreduiPasswordBox::~CreduiPasswordBox // // Created 06/06/2000 johnstep (John Stephens) //============================================================================= CreduiPasswordBox::~CreduiPasswordBox() { if (PasswordFont != NULL) { DeleteObject(static_cast(PasswordFont)); PasswordFont = NULL; } } //============================================================================= // CreduiPasswordBox::Init // // Created 06/06/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiPasswordBox::Init( HWND window, CreduiBalloonTip *balloonTip, CONST CREDUI_BALLOON_TIP_INFO *capsLockTipInfo, HFONT passwordFont, WCHAR passwordChar) { // If passwordFont was passed, use it here, but leave the class // PasswordFont NULL so it will not be cleaned up by the destructor. If // it was not passed, create a font here, which will be freed by the // destructor: if (passwordFont == NULL) { passwordFont = PasswordFont; } Window = window; // If we still failed to create the font, and are not planning to display // balloon tips, then there's nothing do to, just return. if ((passwordFont == NULL) && (balloonTip == NULL)) { return FALSE; } if (balloonTip != NULL) { if (capsLockTipInfo == NULL) { return FALSE; } BalloonTip = balloonTip; CapsLockTipInfo = capsLockTipInfo; OriginalMessageHandler = reinterpret_cast( GetWindowLongPtr(Window, GWLP_WNDPROC)); if (OriginalMessageHandler != NULL) { SetLastError(ERROR_SUCCESS); if ((SetWindowLongPtr( Window, GWLP_USERDATA, reinterpret_cast(this)) == 0) && (GetLastError() != ERROR_SUCCESS)) { return FALSE; } SetLastError(ERROR_SUCCESS); if (SetWindowLongPtr( Window, GWLP_WNDPROC, reinterpret_cast(MessageHandlerCallback)) && (GetLastError() != ERROR_SUCCESS)) { return FALSE; } } else { return FALSE; } } if (passwordFont != NULL) { SendMessage(Window, WM_SETFONT, reinterpret_cast(passwordFont), 0); SendMessage(Window, EM_SETPASSWORDCHAR, passwordChar, 0); } return TRUE; } //============================================================================= // CreduiPasswordBox::MessageHandler // // This callback function just calls through to the original, except in a // special case where Caps Lock is pressed. We then check to see if the tip is // currently being displayed, and if the new state of Caps Lock is off, hide // the tip. // // Arguments: // message (in) // wParam (in) // lParam (in) // // Returns the result of calling the original message handler in every case. // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= LRESULT CreduiPasswordBox::MessageHandler( UINT message, WPARAM wParam, LPARAM lParam ) { switch (message) { case WM_KEYDOWN: if (wParam == VK_CAPITAL) { } else { if (BalloonTip->IsVisible()) { BalloonTip->Hide(); } } break; case WM_SETFOCUS: // Make sure no one can steal the focus while a user is // entering their password: LockSetForegroundWindow(LSFW_LOCK); // If the Caps Lock key is down, notify the user, unless the // password tip is already visible: if (!BalloonTip->IsVisible() && CreduiIsCapsLockOn()) { // BalloonTip->SetInfo(Window, CapsLockTipInfo); // BalloonTip->Show(); } break; case WM_PASTE: if (BalloonTip->IsVisible()) { BalloonTip->Hide(); } break; case WM_KILLFOCUS: if (BalloonTip->IsVisible()) { BalloonTip->Hide(); } // Make sure other processes can set foreground window // once again: LockSetForegroundWindow(LSFW_UNLOCK); break; } return CallWindowProc(OriginalMessageHandler, Window, message, wParam, lParam); } //============================================================================= // CreduiPasswordBox::MessageHandlerCallback // // This calls through to CreduiPasswordBox::MessageHandler, from the this // pointer. // // Arguments: // passwordWindow (in) // message (in) // wParam (in) // lParam (in) // // Returns the result of calling the original message handler in every case. // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= LRESULT CALLBACK CreduiPasswordBox::MessageHandlerCallback( HWND passwordWindow, UINT message, WPARAM wParam, LPARAM lParam ) { CreduiPasswordBox *that = reinterpret_cast( GetWindowLongPtr(passwordWindow, GWLP_USERDATA)); ASSERT(that != NULL); ASSERT(that->BalloonTip != NULL); ASSERT(that->CapsLockTipInfo != NULL); ASSERT(that->Window == passwordWindow); return that->MessageHandler(message, wParam, lParam); } //----------------------------------------------------------------------------- // CreduiStringArrayFactory Class Implementation //----------------------------------------------------------------------------- //============================================================================= // CreduiStringArrayFactory::CreduiStringArrayFactory // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= CreduiStringArrayFactory::CreduiStringArrayFactory() { ReferenceCount = 1; } //============================================================================= // CreduiStringArrayFactory::~CreduiStringArrayFactory // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= CreduiStringArrayFactory::~CreduiStringArrayFactory() { } //============================================================================= // CreduiStringArrayFactory::QueryInterface (IUnknown) // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArrayFactory::QueryInterface( CONST IID &interfaceId, VOID **outInterface ) { if ((interfaceId == IID_IUnknown) || (interfaceId == IID_IClassFactory)) { *outInterface = static_cast(static_cast(this)); } else { *outInterface = NULL; return E_NOINTERFACE; } static_cast(*outInterface)->AddRef(); return S_OK; } //============================================================================= // CreduiStringArrayFactory::Addref (IUnknown) // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= ULONG CreduiStringArrayFactory::AddRef() { return InterlockedIncrement(reinterpret_cast(&ReferenceCount)); } //============================================================================= // CreduiStringArrayFactory::Release (IUnknown) // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= ULONG CreduiStringArrayFactory::Release() { if (InterlockedDecrement(reinterpret_cast(&ReferenceCount)) > 0) { return ReferenceCount; } delete this; return 0; } //============================================================================= // CreduiClassFactory::CreateInstance (IClassFactory) // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArrayFactory::CreateInstance( IUnknown *unknownOuter, CONST IID &interfaceId, VOID **outInterface ) { if (unknownOuter != NULL) { return CLASS_E_NOAGGREGATION; } CreduiStringArray *stringArray = new CreduiStringArray; if (stringArray == NULL) { return E_OUTOFMEMORY; } HRESULT result = stringArray->QueryInterface(interfaceId, outInterface); // Release the string array object in any case, because of the // QueryInterface succeeded, it already took another reference count on // the object: stringArray->Release(); return result; } //============================================================================= // CreduiClassFactory::LockServer (IClassFactory) // // Created 04/03/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArrayFactory::LockServer( BOOL lock ) { if (lock) { InterlockedIncrement(reinterpret_cast( &CreduiComReferenceCount)); } else { InterlockedDecrement(reinterpret_cast( &CreduiComReferenceCount)); } return S_OK; } //----------------------------------------------------------------------------- // CreduiStringArray Class Implementation //----------------------------------------------------------------------------- //============================================================================= // CreduiStringArray::CreduiStringArray // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= CreduiStringArray::CreduiStringArray() { ReferenceCount = 1; Index = 0; Count = 0; MaxCount = 0; Array = NULL; InterlockedIncrement(reinterpret_cast(&CreduiComReferenceCount)); } //============================================================================= // CreduiStringArray::~CreduiStringArray // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= CreduiStringArray::~CreduiStringArray() { if (Array != NULL) { while (Count > 0) { delete [] Array[--Count]; } delete [] Array; MaxCount = 0; Count = 0; } InterlockedDecrement(reinterpret_cast(&CreduiComReferenceCount)); } //============================================================================= // CreduiStringArray::Init // // Initializes the string array. // // Arguments: // count (in) - number of strings in the array // // Returns TRUE on success or FALSE otherwise. // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiStringArray::Init( UINT count ) { Count = 0; MaxCount = count; Array = new WCHAR *[count]; if (Array != NULL) { return TRUE; } // Clean up: MaxCount = 0; return FALSE; } //============================================================================= // CreduiStringArray::Find // // Searches for a string in the array. // // Arguments: // string (in) - string to search for // // Returns TRUE if the string was found or FALSE otherwise. // // Created 02/27/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiStringArray::Find( CONST WCHAR *string ) { // Search for the string: for (UINT i = 0; i < Count; ++i) { ASSERT(Array[i] != NULL); if (lstrcmpi(Array[i], string) == 0) { return TRUE; } } return FALSE; } //============================================================================= // CreduiStringArray::Add // // Adds a string to the array. // // Arguments: // string (in) - string to add // // Returns TRUE if the string was added or FALSE otherwise. // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiStringArray::Add( CONST WCHAR *string ) { // The array does not grow, so once we reach the limit, no more: if (Count < MaxCount) { Array[Count] = new WCHAR[lstrlen(string) + 1]; if (Array[Count] != NULL) { lstrcpy(Array[Count++], string); return TRUE; } } return FALSE; } //============================================================================= // CreduiStringArray::QueryInterface (IUnknown) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArray::QueryInterface( CONST IID &interfaceId, VOID **outInterface ) { if ((interfaceId == IID_IUnknown) || (interfaceId == IID_IEnumString)) { *outInterface = static_cast(static_cast(this)); } else { *outInterface = NULL; return E_NOINTERFACE; } static_cast(*outInterface)->AddRef(); return S_OK; } //============================================================================= // CreduiStringArray::Addref (IUnknown) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= ULONG CreduiStringArray::AddRef() { return InterlockedIncrement(reinterpret_cast(&ReferenceCount)); } //============================================================================= // CreduiStringArray::Release (IUnknown) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= ULONG CreduiStringArray::Release() { if (InterlockedDecrement(reinterpret_cast(&ReferenceCount)) > 0) { return ReferenceCount; } delete this; return 0; } //============================================================================= // CreduiStringArray::Next (IEnumString) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArray::Next( ULONG count, LPOLESTR *array, ULONG *countFetched ) { if ((count > 1) && (countFetched == NULL)) { return E_INVALIDARG; } count = min(count, Count - Index); for (UINT i = 0; i < count; ++i) { array[i] = static_cast(CoTaskMemAlloc( (sizeof (WCHAR)) * (lstrlen(Array[Index]) + 1))); if (array[i] != NULL) { lstrcpy(array[i], Array[Index]); } else { while (i > 0) { CoTaskMemFree(array[--i]); array[i] = NULL; } if (countFetched != NULL) { *countFetched = 0; } return E_OUTOFMEMORY; } Index++; } if (countFetched != NULL) { *countFetched = count; } return (count > 0) ? S_OK : S_FALSE; } //============================================================================= // CreduiStringArray::Skip (IEnumString) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArray::Skip( ULONG ) { return E_NOTIMPL; } //============================================================================= // CreduiStringArray::Reset (IEnumString) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArray::Reset() { Index = 0; return S_OK; } //============================================================================= // CreduiStringArray::Clone (IEnumString) // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= HRESULT CreduiStringArray::Clone( IEnumString ** ) { return E_NOTIMPL; } //----------------------------------------------------------------------------- // CreduiAutoCompleteComboBox Class Implementation //----------------------------------------------------------------------------- //============================================================================= // CreduiAutoCompleteComboBox::CreduiAutoCompleteComboBox // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= CreduiAutoCompleteComboBox::CreduiAutoCompleteComboBox() { Window = NULL; ImageList = NULL; StringArray = NULL; } //============================================================================= // CreduiAutoCompleteComboBox::~CreduiAutoCompleteComboBox // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= CreduiAutoCompleteComboBox::~CreduiAutoCompleteComboBox() { if (StringArray != NULL) { StringArray->Release(); StringArray = NULL; } if (ImageList != NULL) { ImageList_Destroy(ImageList); ImageList = NULL; } } //============================================================================= // CreduiAutoCompleteComboBox::Init // // Initializes the shell auto complete list control for the given combo box, // and sets the auto complete string list. // // Arguments: // instance (in) // comboBoxWindow (in) // stringCount (in) // imageListResourceId (in) - optional image list for the combo box // // Returns TRUE on success or FALSE otherwise. // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiAutoCompleteComboBox::Init( HMODULE instance, HWND comboBoxWindow, UINT stringCount, INT imageListResourceId, INT initialImage ) { Window = comboBoxWindow; if (imageListResourceId != 0) { ImageList = ImageList_LoadImage( instance, MAKEINTRESOURCE(imageListResourceId), 0, 16, RGB(0, 128, 128), IMAGE_BITMAP, LR_DEFAULTSIZE | LR_SHARED); if (ImageList != NULL) { SendMessage(Window, CBEM_SETIMAGELIST, 0, reinterpret_cast(ImageList)); } else { return FALSE; } } BOOL success = FALSE; if (stringCount > 0) { HRESULT result = CoCreateInstance(CreduiStringArrayClassId, NULL, CLSCTX_INPROC_SERVER, IID_IEnumString, reinterpret_cast(&StringArray)); if (SUCCEEDED(result)) { if (StringArray->Init(stringCount)) { success = TRUE; } else { StringArray->Release(); StringArray = NULL; } } } else { success = TRUE; } if (success == TRUE) { COMBOBOXEXITEMW item; item.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; item.iItem = -1; item.iImage = initialImage; item.iSelectedImage = initialImage; SendMessage(Window, CBEM_SETITEM, 0, reinterpret_cast(&item)); return TRUE; } if (ImageList != NULL) { ImageList_Destroy(ImageList); ImageList = NULL; } return FALSE; } //============================================================================= // CreduiAutoCompleteComboBox::Add // // Returns the index of the new item or -1 on failure. // // Created 02/25/2000 johnstep (John Stephens) //============================================================================= INT CreduiAutoCompleteComboBox::Add( WCHAR *string, INT image, BOOL autoComplete, BOOL addUnique, INT indexBefore, INT indent ) { INT index = -1; if (addUnique) { index = (INT) SendMessage(Window, CB_FINDSTRINGEXACT, 0, reinterpret_cast(string)); } if (index == -1) { if (!autoComplete || StringArray->Add(string)) { COMBOBOXEXITEMW item; item.mask = CBEIF_TEXT | CBEIF_INDENT; item.iItem = indexBefore; item.pszText = string; item.iIndent = indent; if (ImageList != NULL) { item.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; item.iImage = image; item.iSelectedImage = image; } index = (INT) SendMessage(Window, CBEM_INSERTITEM, 0, reinterpret_cast(&item)); } } return index; } //============================================================================= // CreduiAutoCompleteComboBox::Update // // Updates an existing item. This does not update the associated string for // auto complete items. // // Created 04/15/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiAutoCompleteComboBox::Update( INT index, WCHAR *string, INT image ) { COMBOBOXEXITEMW item; item.iItem = index; // Use CBEM_SETITEM in these cases: // // 1. We're updating the default (-1) item. // 2. The dropdown is closed. // // For other cases, we delete and recreate the item for the desired // result. BOOL isDropped = (BOOL) SendMessage(Window, CB_GETDROPPEDSTATE, 0, 0); if ((index == -1) || !isDropped) { item.mask = CBEIF_TEXT; item.pszText = string; if (ImageList != NULL) { item.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; item.iImage = image; item.iSelectedImage = image; } if (SendMessage(Window, CBEM_SETITEM, 0, reinterpret_cast(&item)) != 0) { RECT rect; GetClientRect(Window, &rect); InvalidateRect(Window, &rect, FALSE); return TRUE; } } else { item.mask = CBEIF_IMAGE | CBEIF_INDENT | CBEIF_SELECTEDIMAGE; if (SendMessage(Window, CBEM_GETITEM, 0, reinterpret_cast(&item))) { item.mask |= CBEIF_TEXT; item.pszText = string; LPARAM data = SendMessage(Window, CB_GETITEMDATA, index, 0); if (ImageList != NULL) { item.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; item.iImage = image; item.iSelectedImage = image; } SendMessage(Window, CBEM_DELETEITEM, index, 0); index = (INT) SendMessage(Window, CBEM_INSERTITEM, 0, reinterpret_cast(&item)); if (index != -1) { SendMessage(Window, CB_SETITEMDATA, index, data); INT current = (INT) SendMessage(Window, CB_GETCURSEL, 0, 0); if (current == index) { SendMessage(Window, CB_SETCURSEL, current, 0); } return TRUE; } } } return FALSE; } //============================================================================= // CreduiAutoCompleteComboBox::Enable // // Created 02/27/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiAutoCompleteComboBox::Enable() { BOOL success = TRUE; if (StringArray != NULL) { success = FALSE; IAutoComplete2 *autoCompleteInterface; HRESULT result = CoCreateInstance( CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete2, reinterpret_cast(&autoCompleteInterface)); if (SUCCEEDED(result)) { result = autoCompleteInterface->Init((HWND) SendMessage(Window, CBEM_GETEDITCONTROL, 0, 0), StringArray, NULL, NULL); if (SUCCEEDED(result)) { result = autoCompleteInterface->SetOptions(ACO_AUTOSUGGEST); if (SUCCEEDED(result)) { success = TRUE; } else { CreduiDebugLog("CreduiAutoCompleteComboBox::Enable: " "SetOptions failed: 0x%08X\n", result); } } autoCompleteInterface->Release(); autoCompleteInterface = NULL; } else { CreduiDebugLog( "CreduiAutoCompleteComboBox::Enable: " "CoCreateInstance CLSID_AutoComplete failed: 0x%08X\n", result); } StringArray->Release(); StringArray = NULL; } return success; } //----------------------------------------------------------------------------- // CreduiIconParentWindow Class Implementation //----------------------------------------------------------------------------- CONST WCHAR *CreduiIconParentWindow::ClassName = L"CreduiIconParentWindow"; HINSTANCE CreduiIconParentWindow::Instance = NULL; LONG CreduiIconParentWindow::Registered = FALSE; //============================================================================= // CreduiIconParentWindow::CreduiIconParentWindow // // Created 02/29/2000 johnstep (John Stephens) //============================================================================= CreduiIconParentWindow::CreduiIconParentWindow() { Window = NULL; } //============================================================================= // CreduiIconParentWindow::~CreduiIconParentWindow // // Created 02/29/2000 johnstep (John Stephens) //============================================================================= CreduiIconParentWindow::~CreduiIconParentWindow() { if (Window != NULL) { DestroyWindow(Window); Window = NULL; } } //============================================================================= // CreduiIconParentWindow::Register // // Set the instance to allow registration, which will be deferred until a // window needs to be created. // // Arguments: // instance (in) // // Returns TRUE on success or FALSE otherwise. // // Created 04/16/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiIconParentWindow::Register( HINSTANCE instance ) { Instance = instance; return TRUE; } //============================================================================= // CreduiIconParentWindow::Unegister // // Unregisters the window class, if registered. // // Returns TRUE on success or FALSE otherwise. // // Created 04/16/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiIconParentWindow::Unregister() { if (InterlockedCompareExchange(&Registered, FALSE, TRUE)) { return UnregisterClass(ClassName, Instance); } return TRUE; } //============================================================================= // CreduiIconParentWindow::Init // // Registers the window class, if not already registered, and creates the // window. // // Arguments: // instance (in) - module to load the icon from // iconResourceId (in) // // Returns TRUE on success or FALSE otherwise. // // Created 02/29/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiIconParentWindow::Init( HINSTANCE instance, UINT iconResourceId ) { WNDCLASS windowClass; ZeroMemory(&windowClass, sizeof windowClass); if (!InterlockedCompareExchange(&Registered, TRUE, FALSE)) { windowClass.lpfnWndProc = DefWindowProc; windowClass.hInstance = Instance; windowClass.hIcon = LoadIcon(instance, MAKEINTRESOURCE(iconResourceId)); windowClass.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); windowClass.lpszClassName = ClassName; InterlockedExchange(&Registered, RegisterClass(&windowClass) != 0); if (!InterlockedCompareExchange(&Registered, FALSE, FALSE)) { return FALSE; } } Window = CreateWindow( L"CreduiIconParentWindow", NULL, WS_CAPTION | WS_SYSMENU, 0, 0, 0, 0, NULL, NULL, instance, NULL); return (Window != NULL); } //----------------------------------------------------------------------------- // CreduiCredentialControl Class Implementation //----------------------------------------------------------------------------- CONST WCHAR *CreduiCredentialControl::ClassName = WC_CREDENTIAL; HINSTANCE CreduiCredentialControl::Instance = NULL; LONG CreduiCredentialControl::Registered = FALSE; //============================================================================= // CreduiCredentialControl::CreduiCredentialControl // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= CreduiCredentialControl::CreduiCredentialControl() { IsInitialized = FALSE; DisabledControlMask = 0; Window = NULL; Style = 0; UserNameStaticWindow = NULL; UserNameControlWindow = NULL; ViewCertControlWindow = NULL; PasswordStaticWindow = NULL; PasswordControlWindow = NULL; FirstPaint = FALSE; ShowBalloonTip = FALSE; IsAutoComplete = FALSE; NoEditUserName = FALSE; KeepUserName = FALSE; IsPassport = FALSE; CertHashes = NULL; CertCount = 0; CertBaseInComboBox = 0; UserNameCertHash = NULL; SmartCardBaseInComboBox = 0; SmartCardReadCount = 0; IsChangingUserName = FALSE; IsChangingPassword = FALSE; UserNameSelection = 0; ScardUiHandle = NULL; DoingCommandLine = FALSE; TargetName = NULL; InitialUserName = NULL; } //============================================================================= // CreduiCredentialControl::~CreduiCredentialControl // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= CreduiCredentialControl::~CreduiCredentialControl() { } //============================================================================= // CreduiCredentialControl::Register // // Arguments: // instance (in) // // Returns TRUE on success or FALSE otherwise. // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::Register( HINSTANCE instance ) { Instance = instance; WNDCLASS windowClass; ZeroMemory(&windowClass, sizeof windowClass); if (!InterlockedCompareExchange(&Registered, TRUE, FALSE)) { windowClass.style = CS_GLOBALCLASS; windowClass.lpfnWndProc = MessageHandlerCallback; windowClass.cbWndExtra = sizeof (CreduiCredentialControl *); windowClass.hInstance = Instance; windowClass.hIcon = NULL; windowClass.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); windowClass.lpszClassName = ClassName; InterlockedExchange(&Registered, RegisterClass(&windowClass) != 0); if (!InterlockedCompareExchange(&Registered, FALSE, FALSE)) { return FALSE; } } return TRUE; } //============================================================================= // CreduiCredentialControl::Unegister // // Unregisters the window class, if registered. // // Returns TRUE on success or FALSE otherwise. // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::Unregister() { if (InterlockedCompareExchange(&Registered, FALSE, TRUE)) { return UnregisterClass(ClassName, Instance); } return TRUE; } //============================================================================= // CreduiCredentialControl::ViewCertificate // // Views the certificate at index in our combo box. // // Arguments: // index (in) - index in the user name combo box // // Returns TRUE if the certificate was viewed, otherwise FALSE. // // Created 03/27/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::ViewCertificate( INT index ) { BOOL success = FALSE; if (index < CertBaseInComboBox) { return FALSE; } CONST CERT_CONTEXT *certContext = NULL; HCERTSTORE certStore = NULL; // If this is not a smart card, open the MY store and find the certificate // from the hash. Otherwise, just grab the certificate context from the // CERT_ENUM structure: if ((SmartCardBaseInComboBox > 0) && (index >= SmartCardBaseInComboBox)) { CERT_ENUM *certEnum = reinterpret_cast( SendMessage(UserNameControlWindow, CB_GETITEMDATA, index, 0)); if (certEnum != NULL) { certContext = certEnum->pCertContext; } } else { certStore = CertOpenSystemStore(NULL, L"MY"); if (certStore != NULL) { CRYPT_HASH_BLOB hashBlob; hashBlob.cbData = CERT_HASH_LENGTH; hashBlob.pbData = reinterpret_cast( CertHashes[index - CertBaseInComboBox]); certContext = CertFindCertificateInStore( certStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &hashBlob, NULL); } } // If we found a certificate context, view the certificate: if (certContext != NULL) { // Now, show the certificate with the common UI: CRYPTUI_VIEWCERTIFICATE_STRUCT certViewInfo; ZeroMemory(&certViewInfo, sizeof certViewInfo); certViewInfo.dwSize = sizeof certViewInfo; certViewInfo.hwndParent = Window; certViewInfo.pCertContext = certContext; BOOL changed; changed = FALSE; CryptUIDlgViewCertificate(&certViewInfo, &changed); // Get the name again, in case it changed. However, skip this if this // is a card reader, and is now invalid: COMBOBOXEXITEMW item; BOOL updateName = TRUE; if (index >= SmartCardBaseInComboBox) { item.mask = CBEIF_IMAGE; item.iItem = index; if (!SendMessage(UserNameControlWindow, CBEM_GETITEM, 0, reinterpret_cast(&item)) || (item.iImage == IMAGE_SMART_CARD_MISSING)) { updateName = FALSE; } } if (updateName) { WCHAR displayName[CREDUI_MAX_CERT_NAME_LENGTH]; if (!CreduiGetCertificateDisplayName( certContext, displayName, CREDUI_MAX_CERT_NAME_LENGTH, CreduiStrings.Certificate, CERT_NAME_FRIENDLY_DISPLAY_TYPE)) { lstrcpyn(displayName, CreduiStrings.Certificate, CREDUI_MAX_CERT_NAME_LENGTH); } COMBOBOXEXITEMW item; item.mask = CBEIF_TEXT; item.iItem = index; item.pszText = displayName; SendMessage(UserNameControlWindow, CBEM_SETITEM, 0, reinterpret_cast(&item)); } success = TRUE; } // If we opened a store, free the certificate and close the store: if (certStore != NULL) { if (certContext != NULL) { CertFreeCertificateContext(certContext); } if (!CertCloseStore(certStore, 0)) { CreduiDebugLog("CreduiCredentialControl::ViewCertificate: " "CertCloseStore failed: %u\n", GetLastError()); } } return success; } //============================================================================= // CreduiCredentialControl::AddCertificates // // Adds interesting certificates to the combo box, and allocates an array of // hashes to match. The hash is all we need to store the credential, and can // be used to get a CERT_CONTEXT later to view the certificate. // // Assume CertCount is 0 upon entry. // // Stack space is used for temporary storage of hashes, since each hash is // only 160 bits. We use a linked list structure, so including the next // pointer and worst case alignment (8-byte) on 64-bit, the maximum structure // size is 32 bytes. We don't want to consume too much stack space, so limit // the number of entries to 256, which will consume up to 8 KB of stack space. // // Returns TRUE if at least one interesting certificate exists, and all were // added to the combo box without error. Otherwise, returns FALSE. // // Created 03/25/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::AddCertificates() { BOOL success = FALSE; ASSERT(CertCount == 0); HCERTSTORE certStore = CertOpenSystemStore(NULL, L"MY"); if (certStore != NULL) { struct HASH_ENTRY { UCHAR Hash[CERT_HASH_LENGTH]; HASH_ENTRY *Next; }; HASH_ENTRY *hashList = NULL; HASH_ENTRY *current = NULL; HASH_ENTRY *next = NULL; CONST CERT_CONTEXT *certContext = NULL; // NOTE: Currently, add all client authentication certificates. This // should be revisited. CHAR *ekUsageIdentifiers[] = { szOID_PKIX_KP_CLIENT_AUTH }; CERT_ENHKEY_USAGE ekUsage = { 1, ekUsageIdentifiers }; // We allow a maximum of 256 certificates to be added. This is a // reasonable limit, given the current user interface. If this is an // unreasonable limit for the personal certificate store, then this // can always be changed. while (CertCount < 256) { certContext = CertFindCertificateInStore( certStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, CERT_FIND_ENHKEY_USAGE, static_cast(&ekUsage), certContext); if (certContext != NULL) { DWORD length = CERT_HASH_LENGTH; // Only allocate a new entry if necessary. Something may have // failed from the previous loop iteration, so we can just // reuse the entry allocated then: if (next == NULL) { // Wrap the alloca in an exception handler because it will // throw a stack overflow exception on failure. Of course, // of we're out of stack space, we may not even be able to // clean up properly without throwing an exception. __try { next = static_cast( alloca(sizeof HASH_ENTRY)); } __except(EXCEPTION_EXECUTE_HANDLER) { next = NULL; } // If this fails, for whatever reason, break out of the // loop: if (next == NULL) { CertFreeCertificateContext(certContext); break; } next->Next = NULL; } if (!CertGetCertificateContextProperty( certContext, CERT_SHA1_HASH_PROP_ID, static_cast(&next->Hash), &length)) { // If we failed to get the hash for this certificate, just // ignore it and continue with the next. The memory we // allocation for this entry will be used on the next // iteration if we do not set it to NULL. continue; } if (CreduiIsRemovableCertificate(certContext)) { // If this certificate requires a removable component, // such as a smart card, then skip it. We will enumerate // these later. continue; } WCHAR displayName[CREDUI_MAX_CERT_NAME_LENGTH]; if (!CreduiGetCertificateDisplayName( certContext, displayName, CREDUI_MAX_CERT_NAME_LENGTH, CreduiStrings.Certificate, CERT_NAME_FRIENDLY_DISPLAY_TYPE)) { lstrcpyn(displayName, CreduiStrings.Certificate, CREDUI_MAX_CERT_NAME_LENGTH); } // Add the certificate to the combo box. Certificate names may // not be unique, so allow duplicates: if (UserNameComboBox.Add( displayName, CreduiIsExpiredCertificate(certContext) ? IMAGE_CERT_EXPIRED : IMAGE_CERT, FALSE, FALSE) == -1) { CertFreeCertificateContext(certContext); break; } // Everything succeeded, so add the certificate to our list: if (current == NULL) { current = next; hashList = current; } else { ASSERT(current->Next == NULL); current->Next = next; current = current->Next; } if (current == NULL) { CertFreeCertificateContext(certContext); break; } // Set next to NULL so we will allocate new memory on the // next iteration: next = NULL; CertCount++; } else { break; } } if (CertCount > 0) { current = hashList; // Now, allocate the final array of certificates. We allocate // this in a single block to help avoid thrashing the heap: CertHashes = new UCHAR [CertCount][CERT_HASH_LENGTH]; if (CertHashes != NULL) { for (UINT i = 0; i < CertCount; ++i) { CopyMemory(CertHashes[i], current->Hash, CERT_HASH_LENGTH); current = current->Next; } success = TRUE; } } CertCloseStore(certStore, 0); } return success; } //============================================================================= // CreduiCredentialControl::FindSmartCardInComboBox // // Finds a smart card entry in the user name combo box based on a CERT_ENUM. // // Arguments: // certEnum (in) // // Returns the index of the smart card or -1 if not found. // // Created 04/15/2000 johnstep (John Stephens) //============================================================================= INT CreduiCredentialControl::FindSmartCardInComboBox( CERT_ENUM *certEnum ) { UINT count = (UINT) SendMessage(UserNameControlWindow, CB_GETCOUNT, 0, 0); if (count == CB_ERR) { return -1; } CERT_ENUM *findCertEnum; for (UINT i = SmartCardBaseInComboBox; i < count; ++i) { findCertEnum = reinterpret_cast( SendMessage(UserNameControlWindow, CB_GETITEMDATA, i, 0)); ASSERT(findCertEnum != NULL); if (lstrcmpi(findCertEnum->pszReaderName, certEnum->pszReaderName) == 0) { return i; } } return -1; } //============================================================================= // CreduiCredentialControl::RemoveSmartCardFromComboBox // // Removes all entries for this smart card from the user name combo box. // // Arguments: // certEnum (in) // removeParent (in) // // Created 07/12/2000 johnstep (John Stephens) //============================================================================= VOID CreduiCredentialControl::RemoveSmartCardFromComboBox( CERT_ENUM *certEnum, BOOL removeParent ) { INT count = (INT) SendMessage(UserNameControlWindow, CB_GETCOUNT, 0, 0); INT current = (INT) SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0); if (count != CB_ERR) { CERT_ENUM *findCertEnum; BOOL parentEntry = TRUE; BOOL currentRemoved = FALSE; for (INT i = SmartCardBaseInComboBox; i < count; ++i) { findCertEnum = reinterpret_cast( SendMessage(UserNameControlWindow, CB_GETITEMDATA, i, 0)); ASSERT(findCertEnum != NULL); if (lstrcmpi(findCertEnum->pszReaderName, certEnum->pszReaderName) == 0) { if (parentEntry) { parentEntry = FALSE; if (!removeParent) { continue; } } if (current == i) { currentRemoved = TRUE; } SendMessage( UserNameControlWindow, CBEM_DELETEITEM, i, 0); i--, count--; } else if (!parentEntry) { break; } } if (currentRemoved) { if (removeParent) { IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, -1, 0); UserNameComboBox.Update(-1, L"", IMAGE_USERNAME); IsChangingUserName = FALSE; IsChangingPassword = TRUE; SetWindowText(PasswordControlWindow, NULL); IsChangingPassword = FALSE; OnUserNameSelectionChange(); } else { IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, --i, 0); IsChangingUserName = FALSE; } OnUserNameSelectionChange(); } } } //============================================================================= // CreduiCredentialControl::HandleSmartCardMessages // // Handle smart card messages. // // Arguments: // message (in) // certEnum (in) // // Returns TRUE if the message was handled or FALSE otherwise. // // Created 04/14/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::HandleSmartCardMessages( UINT message, CERT_ENUM *certEnum ) { ASSERT(ScardUiHandle != NULL); // This is sort of ugly since we cannot use a switch. First check for any // possible smart card message because we must do some things in common // for any of the messages: if ((message == CreduiScarduiWmReaderArrival) || (message == CreduiScarduiWmReaderRemoval) || (message == CreduiScarduiWmCardInsertion) || (message == CreduiScarduiWmCardRemoval) || (message == CreduiScarduiWmCardCertAvail) || (message == CreduiScarduiWmCardStatus)) { if (certEnum == NULL) { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "NULL was passed for the CERT_ENUM!"); // We handled the message, even though it was invalid: return TRUE; } ASSERT(certEnum->pszReaderName != NULL); } else { return FALSE; } WCHAR *displayString; WCHAR string[256]; // Must be >= CREDUI_MAX_CERT_NAME_LENGTH ASSERT((sizeof string / (sizeof string[0])) >= CREDUI_MAX_CERT_NAME_LENGTH); INT index = FindSmartCardInComboBox(certEnum); if (message == CreduiScarduiWmReaderArrival) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: Reader arrival event for %0x\n",this->Window); #endif // Add the reader, if it is not already there; it should not be: if (index == -1) { // // Reset command line Hearbeat timer. // Heartbeats = 0; INT index = UserNameComboBox.Add( DoingCommandLine ? CreduiStrings.NoCard : CreduiStrings.EmptyReader, IMAGE_SMART_CARD_MISSING, FALSE, FALSE); if (index != -1) { SendMessage(UserNameControlWindow, CB_SETITEMDATA, index, reinterpret_cast(certEnum)); if (UserNameCertHash != NULL) { EnableWindow(ViewCertControlWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_VIEW; SetWindowText( PasswordStaticWindow, CreduiStrings.PinStatic); IsChangingPassword = TRUE; SetWindowText(PasswordControlWindow, NULL); IsChangingPassword = FALSE; EnableWindow(PasswordControlWindow, TRUE); EnableWindow(PasswordStaticWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_PASSWORD; SetWindowText( UserNameStaticWindow, CreduiStrings.SmartCardStatic); if (SaveControlWindow != NULL) { EnableWindow(SaveControlWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_SAVE; } IsChangingUserName = TRUE; UserNameComboBox.Update( -1, DoingCommandLine ? CreduiStrings.NoCard : CreduiStrings.EmptyReader, IMAGE_SMART_CARD_MISSING); IsChangingUserName = FALSE; } } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Failed to add smart card\n"); } } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Reader arrived more than once"); } } else if (message == CreduiScarduiWmReaderRemoval) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: Reader removal event for %0x\n",this->Window); #endif if (index != -1) { RemoveSmartCardFromComboBox(certEnum, TRUE); } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Reader removed more than once"); } } else if (message == CreduiScarduiWmCardInsertion) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card insertion event for %0x\n",this->Window); #endif if (index != -1) { // // Reset command line Hearbeat timer. // Heartbeats = 0; SmartCardReadCount++; if (UserNameCertHash != NULL) { IsChangingUserName = TRUE; UserNameComboBox.Update( -1, CreduiStrings.ReadingCard, IMAGE_SMART_CARD_MISSING); IsChangingUserName = FALSE; } IsChangingUserName = TRUE; UserNameComboBox.Update(index, CreduiStrings.ReadingCard, IMAGE_SMART_CARD_MISSING); IsChangingUserName = FALSE; } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Card insertion to absent reader\n"); } } else if (message == CreduiScarduiWmCardRemoval) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card removal event for %0x\n",this->Window); #endif if (index != -1) { if (BalloonTip.GetInfo() == &CreduiBackwardsTipInfo) { BalloonTip.Hide(); } IsChangingUserName = TRUE; UserNameComboBox.Update(index, DoingCommandLine ? CreduiStrings.NoCard : CreduiStrings.EmptyReader, IMAGE_SMART_CARD_MISSING); IsChangingUserName = FALSE; RemoveSmartCardFromComboBox(certEnum, FALSE); } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Card removal from absent reader\n"); } } else if (message == CreduiScarduiWmCardCertAvail) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: cert available event for %0x\n",this->Window); #endif if (index != -1) { // Filter certificates which are not for client authentication: if (!CreduiIsClientAuthCertificate(certEnum->pCertContext)) { return TRUE; } UINT image = IMAGE_SMART_CARD_MISSING; COMBOBOXEXITEM item; item.mask = CBEIF_IMAGE; item.iItem = index; SendMessage(UserNameControlWindow, CBEM_GETITEM, 0, reinterpret_cast(&item)); // // For command line, // get the UPN display name since the user is expected to type it. // For GUI, // get the friendly display name since it is "friendly". // if (!CreduiGetCertificateDisplayName( certEnum->pCertContext, string, CREDUI_MAX_CERT_NAME_LENGTH, CreduiStrings.Certificate, DoingCommandLine ? CERT_NAME_UPN_TYPE : CERT_NAME_FRIENDLY_DISPLAY_TYPE)) { lstrcpyn(string, CreduiStrings.Certificate, CREDUI_MAX_CERT_NAME_LENGTH); } displayString = string; // // Trim trailing spaces and -'s so it doesn't look cheesy // if ( DoingCommandLine ) { DWORD StringLength = wcslen(string); while ( StringLength > 0 ) { if ( string[StringLength-1] == ' ' || string[StringLength-1] == '-' ) { string[StringLength-1] = '\0'; StringLength--; } else { break; } } } #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: cert name '%ws' %0x\n", string, this->Window); #endif if (SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0) == index) { EnableWindow(ViewCertControlWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_VIEW; } image = CreduiIsExpiredCertificate(certEnum->pCertContext) ? IMAGE_SMART_CARD_EXPIRED : IMAGE_SMART_CARD; INT newIndex = index; if (item.iImage != IMAGE_SMART_CARD_MISSING) { newIndex = UserNameComboBox.Add(displayString, image, FALSE, FALSE, index + 1, 1); if (newIndex != -1) { SendMessage(UserNameControlWindow, CB_SETITEMDATA, newIndex, reinterpret_cast(certEnum)); } else { newIndex = index; } } if (newIndex == index) { IsChangingUserName = TRUE; UserNameComboBox.Update(index, displayString, image); IsChangingUserName = FALSE; } if (UserNameCertHash != NULL) { UCHAR hash[CERT_HASH_LENGTH]; DWORD length = CERT_HASH_LENGTH; if (CertGetCertificateContextProperty( certEnum->pCertContext, CERT_SHA1_HASH_PROP_ID, static_cast(&hash), &length)) { if (RtlCompareMemory(UserNameCertHash, hash, CERT_HASH_LENGTH) == CERT_HASH_LENGTH) { delete [] UserNameCertHash; UserNameCertHash = NULL; IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, newIndex, 0); IsChangingUserName = FALSE; OnUserNameSelectionChange(); } } } } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Card certificate to absent reader\n"); } } else if (message == CreduiScarduiWmCardStatus) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status event for %0x\n",this->Window); #endif if (index != -1) { if (--SmartCardReadCount == 0) { if (UserNameCertHash != NULL) { IsChangingUserName = TRUE; SetWindowText(UserNameControlWindow, DoingCommandLine ? CreduiStrings.NoCard : CreduiStrings.EmptyReader); IsChangingUserName = FALSE; } } UINT image = IMAGE_SMART_CARD_MISSING; BOOL showBalloon = FALSE; switch (certEnum->dwStatus) { case SCARD_S_SUCCESS: UINT image; COMBOBOXEXITEM item; #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status SUCCESS: %ws\n", certEnum->pszCardName ); #endif item.mask = CBEIF_IMAGE; item.iItem = index; if (SendMessage(UserNameControlWindow, CBEM_GETITEM, 0, reinterpret_cast(&item)) && (item.iImage != IMAGE_SMART_CARD_MISSING)) { return TRUE; } displayString = CreduiStrings.EmptyCard; break; case SCARD_E_UNKNOWN_CARD: #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status UNKNOWN CARD\n"); #endif displayString = CreduiStrings.UnknownCard; break; case SCARD_W_UNRESPONSIVE_CARD: #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status UNRESPONSIVE CARD\n"); #endif displayString = CreduiStrings.BackwardsCard; if (!DoingCommandLine) showBalloon = TRUE; break; case NTE_KEYSET_NOT_DEF: #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status NTE_KEYSET_NOT_DEF\n"); #endif // TODO: This case should be removed eventually. displayString = CreduiStrings.EmptyCard; break; case SCARD_W_REMOVED_CARD: #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status REMOVED CARD\n"); #endif displayString = DoingCommandLine ? CreduiStrings.NoCard : CreduiStrings.EmptyReader; CreduiStrings.EmptyReader; break; default: #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: card status ERROR\n"); #endif displayString = CreduiStrings.CardError; break; } IsChangingUserName = TRUE; UserNameComboBox.Update(index, displayString, image); IsChangingUserName = FALSE; if (showBalloon && !BalloonTip.IsVisible()) { BalloonTip.SetInfo(UserNameControlWindow, &CreduiBackwardsTipInfo); BalloonTip.Show(); } } else { CreduiDebugLog( "CreduiCredentialControl::HandleSmartCardMessages: " "Card status to absent reader\n"); } } // We handled the message: return TRUE; } //============================================================================= // CreduiCredentialControl::CreateControls // // Created 06/23/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::CreateControls() { // First we need the parent window: HWND dialogWindow = GetParent(Window); if (dialogWindow == NULL) { return FALSE; } // Create the various windows: RECT clientRect; RECT rect; UINT add; BOOL noViewCert = FALSE; if ( Style & CRS_KEEPUSERNAME ) { KeepUserName = TRUE; } if (!(Style & CRS_USERNAMES) ) { NoEditUserName = TRUE; } else if ((Style & (CRS_CERTIFICATES | CRS_SMARTCARDS)) == 0) { noViewCert = TRUE; } if ( Style & CRS_SINGLESIGNON ) IsPassport = TRUE; else IsPassport = FALSE; // Determine how much wider the control is than the minimum to resize and // reposition controls as necessary: GetClientRect(Window, &clientRect); rect.left = 0; rect.top = 0; rect.right = CREDUI_CONTROL_MIN_WIDTH; rect.bottom = CREDUI_CONTROL_MIN_HEIGHT; if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } if ((clientRect.right - clientRect.left) > (rect.right - rect.left)) { add = (clientRect.right - clientRect.left) - (rect.right - rect.left); } else { add = 0; } // Create user name static text control: rect.left = CREDUI_CONTROL_USERNAME_STATIC_X; rect.top = CREDUI_CONTROL_USERNAME_STATIC_Y; rect.right = rect.left + CREDUI_CONTROL_USERNAME_STATIC_WIDTH; rect.bottom = rect.top + CREDUI_CONTROL_USERNAME_STATIC_HEIGHT; if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } WCHAR* pUserNameLabel; if ( IsPassport ) pUserNameLabel = CreduiStrings.EmailName; else pUserNameLabel = CreduiStrings.UserNameStatic; UserNameStaticWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"STATIC", pUserNameLabel, WS_VISIBLE | WS_CHILD | WS_GROUP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, Window, reinterpret_cast(IDC_USERNAME_STATIC), CreduiCredentialControl::Instance, NULL); if (UserNameStaticWindow == NULL) { goto ErrorExit; } // Create user name combo box: rect.left = CREDUI_CONTROL_USERNAME_X; rect.top = CREDUI_CONTROL_USERNAME_Y; if (!noViewCert) { rect.right = rect.left + CREDUI_CONTROL_USERNAME_WIDTH; } else { rect.right = CREDUI_CONTROL_VIEW_X + CREDUI_CONTROL_VIEW_WIDTH; } if ( KeepUserName ) { rect.top += 2; // fudge it to make them line up better rect.bottom = rect.top + CREDUI_CONTROL_PASSWORD_STATIC_HEIGHT; // make it the same height as the password edit } else { rect.bottom = rect.top + CREDUI_CONTROL_USERNAME_HEIGHT; // set the height } if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } // This block of statements and the usage of lExStyles : see bug 439840 LONG_PTR lExStyles = GetWindowLongPtr(Window,GWL_EXSTYLE); SetWindowLongPtr(Window,GWL_EXSTYLE,(lExStyles | WS_EX_NOINHERITLAYOUT)); if ( KeepUserName ) { // create an edit box instead of a combo box UserNameControlWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"Edit", L"", WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_READONLY, rect.left, rect.top, rect.right - rect.left + add, rect.bottom - rect.top, Window, reinterpret_cast(IDC_USERNAME), CreduiCredentialControl::Instance, NULL); } else { UserNameControlWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"ComboBoxEx32", L"", WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_VSCROLL | (NoEditUserName ? CBS_DROPDOWNLIST : CBS_DROPDOWN) | CBS_AUTOHSCROLL, rect.left, rect.top, rect.right - rect.left + add, rect.bottom - rect.top, Window, reinterpret_cast(IDC_USERNAME), CreduiCredentialControl::Instance, NULL); } SetWindowLongPtr(Window,GWL_EXSTYLE,lExStyles); if (UserNameControlWindow == NULL) { goto ErrorExit; } // Create view button: if (!noViewCert) { rect.left = CREDUI_CONTROL_VIEW_X; rect.top = CREDUI_CONTROL_VIEW_Y; rect.right = rect.left + CREDUI_CONTROL_VIEW_WIDTH; rect.bottom = rect.top + CREDUI_CONTROL_VIEW_HEIGHT; if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } ViewCertControlWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"BUTTON", L"&...", WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | BS_PUSHBUTTON | BS_CENTER, rect.left + add, rect.top, rect.right - rect.left, rect.bottom - rect.top, Window, reinterpret_cast(IDC_VIEW_CERT), CreduiCredentialControl::Instance, NULL); if (ViewCertControlWindow == NULL) { goto ErrorExit; } EnableWindow(ViewCertControlWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_VIEW; } // Create password static text control: rect.left = CREDUI_CONTROL_PASSWORD_STATIC_X; rect.top = CREDUI_CONTROL_PASSWORD_STATIC_Y; rect.right = rect.left + CREDUI_CONTROL_PASSWORD_STATIC_WIDTH; rect.bottom = rect.top + CREDUI_CONTROL_PASSWORD_STATIC_HEIGHT; if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } PasswordStaticWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"STATIC", CreduiStrings.PasswordStatic, WS_VISIBLE | WS_CHILD | WS_GROUP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, Window, reinterpret_cast(IDC_PASSWORD_STATIC), CreduiCredentialControl::Instance, NULL); if (PasswordStaticWindow == NULL) { goto ErrorExit; } // Create password edit control: rect.left = CREDUI_CONTROL_PASSWORD_X; rect.top = CREDUI_CONTROL_PASSWORD_Y; if (!noViewCert) { rect.right = rect.left + CREDUI_CONTROL_PASSWORD_WIDTH; } else { rect.right = CREDUI_CONTROL_VIEW_X + CREDUI_CONTROL_VIEW_WIDTH; } rect.bottom = rect.top + CREDUI_CONTROL_PASSWORD_HEIGHT; if (!DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } // This block of statements and the usage of lExStyles : see bug 439840 lExStyles = GetWindowLongPtr(Window,GWL_EXSTYLE); SetWindowLongPtr(Window,GWL_EXSTYLE,(lExStyles | WS_EX_NOINHERITLAYOUT)); PasswordControlWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE, L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_PASSWORD | ES_AUTOHSCROLL, rect.left, rect.top, rect.right - rect.left + add, rect.bottom - rect.top + 1, // NOTE: Add 1 for now, investigate Window, reinterpret_cast(IDC_PASSWORD), CreduiCredentialControl::Instance, NULL); SetWindowLongPtr(Window,GWL_EXSTYLE,lExStyles); if (PasswordControlWindow == NULL) { goto ErrorExit; } // Create save check box: if (Style & CRS_SAVECHECK ) { rect.left = CREDUI_CONTROL_SAVE_X; rect.top = CREDUI_CONTROL_SAVE_Y; rect.right = rect.left + CREDUI_CONTROL_SAVE_WIDTH; rect.bottom = rect.top + CREDUI_CONTROL_SAVE_HEIGHT; if (!DoingCommandLine && !MapDialogRect(dialogWindow, &rect)) { goto ErrorExit; } WCHAR* pSavePromptString; if ( IsPassport ) pSavePromptString = CreduiStrings.PassportSave; else pSavePromptString = CreduiStrings.Save; SaveControlWindow = CreateWindowEx( WS_EX_NOPARENTNOTIFY, L"BUTTON", pSavePromptString, WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | BS_AUTOCHECKBOX, rect.left, rect.top, rect.right - rect.left + add, rect.bottom - rect.top, Window, reinterpret_cast(IDC_SAVE), CreduiCredentialControl::Instance, NULL); if (SaveControlWindow == NULL) { goto ErrorExit; } SendMessage(SaveControlWindow, BM_SETCHECK, BST_UNCHECKED, 0); } SendMessage( Window, WM_SETFONT, SendMessage(dialogWindow, WM_GETFONT, 0, 0), FALSE); return TRUE; ErrorExit: if (SaveControlWindow != NULL) { DestroyWindow(SaveControlWindow); SaveControlWindow = NULL; } if (PasswordControlWindow != NULL) { DestroyWindow(PasswordControlWindow); PasswordControlWindow = NULL; } if (PasswordStaticWindow != NULL) { DestroyWindow(PasswordStaticWindow); PasswordStaticWindow = NULL; } if (ViewCertControlWindow != NULL) { DestroyWindow(ViewCertControlWindow); ViewCertControlWindow = NULL; } if (UserNameControlWindow != NULL) { DestroyWindow(UserNameControlWindow); UserNameControlWindow = NULL; } if (UserNameStaticWindow != NULL) { DestroyWindow(UserNameStaticWindow); UserNameStaticWindow = NULL; } return FALSE; } LPWSTR TrimUsername( IN LPWSTR AccountDomainName OPTIONAL, IN LPWSTR UserName ) /*++ Routine Description: Returns a pointer to the substring of UserName past any AccountDomainName prefix. Arguments: AccountDomainName - The DomainName to check to see if it prefixes the UserName. UserName - The UserName to check Return Values: Return a pointer to the non-prefixed username --*/ { DWORD AccountDomainNameLength; DWORD UserNameLength; WCHAR Temp[CNLEN+1]; // // If we couldn't determine the AccountDomainName, // return the complete user name. // if ( AccountDomainName == NULL ) { return UserName; } // // If the user name isn't prefixed by the account domain name, // return the complete user name. // AccountDomainNameLength = lstrlen( AccountDomainName ); UserNameLength = lstrlen( UserName ); if ( AccountDomainNameLength > CNLEN || AccountDomainNameLength < 1 ) { return UserName; } if ( AccountDomainNameLength+2 > UserNameLength ) { return UserName; } if ( UserName[AccountDomainNameLength] != '\\' ) { return UserName; } RtlCopyMemory( Temp, UserName, AccountDomainNameLength*sizeof(WCHAR) ); Temp[AccountDomainNameLength] = '\0'; if ( lstrcmpi( Temp, AccountDomainName ) != 0 ) { return UserName; } return &UserName[AccountDomainNameLength+1]; } //============================================================================= // CreduiCredentialControl::InitComboBoxUserNames // // Created 06/23/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::InitComboBoxUserNames() { CREDENTIAL **credentialSet = NULL; LOCALGROUP_MEMBERS_INFO_2 *groupInfo = NULL; DWORD nameCount = 0; LPWSTR AccountDomainName = NULL; if (Style & CRS_ADMINISTRATORS) { // // Enumerate the members of LocalAdministrators // if ( !CreduiGetAdministratorsGroupInfo(&groupInfo, &nameCount)) { return FALSE; } } else { if (!LocalCredEnumerateW(NULL, 0, &nameCount, &credentialSet)) { return FALSE; } } // Initialize COM for STA, unless there are zero names: if ((Style & CRS_AUTOCOMPLETE) && nameCount > 0) { HRESULT comResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (SUCCEEDED(comResult)) { IsAutoComplete = TRUE; } else { // The auto complete object and our string object require a STA. // Our object could easily support a MTA, but we do not support // marshaling between apartments. if (comResult == RPC_E_CHANGED_MODE) { CreduiDebugLog("CreduiCredentialControl: " "Auto complete disabled for MTA\n"); } IsAutoComplete = FALSE; } } else { IsAutoComplete = FALSE; } // Initialize the auto complete combo box: if (!UserNameComboBox.Init(CreduiInstance, UserNameControlWindow, IsAutoComplete ? nameCount : 0, IDB_TYPES, IMAGE_USERNAME)) { // If initialization failed, and we had attempted for auto complete // support, try again without auto complete: if (IsAutoComplete) { IsAutoComplete = FALSE; CoUninitialize(); if (!UserNameComboBox.Init(CreduiInstance, UserNameControlWindow, 0, IDB_TYPES, IMAGE_USERNAME)) { return FALSE; } } else { return FALSE; } } // // If we'll complete the user name, // truncate any username displayed here. // (We'll complete it later.) // if ( Style & CRS_COMPLETEUSERNAME ) { AccountDomainName = GetAccountDomainName(); } // Add user names from credentials, if not requesting an // Administrator: if (!(Style & CRS_KEEPUSERNAME)) { // only add usernames if we're not keeping the one set UINT i = 0; if (!(Style & CRS_ADMINISTRATORS)) { for (i = 0; i < nameCount; ++i) { // Skip domain certificates: if (credentialSet[i]->Type == CRED_TYPE_DOMAIN_CERTIFICATE) { continue; } // If this is a generic credential, look for a marshaled // credential, and skip, if found: if ((credentialSet[i]->Type == CRED_TYPE_GENERIC) && LocalCredIsMarshaledCredentialW(credentialSet[i]->UserName)) { continue; } // Skip this credential if the name is empty: if (credentialSet[i]->UserName == NULL) { continue; } // Add the user name to the combo box with auto complete. If // this fails, do not continue: if (UserNameComboBox.Add( TrimUsername( AccountDomainName, credentialSet[i]->UserName), 0, IsAutoComplete, TRUE) == -1) { break; } } LocalCredFree(static_cast(credentialSet)); } else if (groupInfo != NULL) { PSID adminSid = NULL; if ( !CreduiLookupLocalSidFromRid(DOMAIN_USER_RID_ADMIN, &adminSid)) { adminSid = NULL; } // Add local administrators to the combo box: for (i = 0; i < nameCount; ++i) { if ( groupInfo[i].lgrmi2_sidusage == SidTypeUser ) { DWORD ComboBoxIndex; BOOLEAN IsAdminAccount; BOOLEAN RememberComboBoxIndex; // // If this is Personal and not safe mode, // Ignore the well-known Administrator account. // IsAdminAccount = (adminSid != NULL) && EqualSid(adminSid, groupInfo[i].lgrmi2_sid); if ( CreduiIsPersonal && !CreduiIsSafeMode && IsAdminAccount ) { continue; } // // If the caller wants to prepopulate the edit box, // flag that we need to remember this account // // Detect the well known admin account // RememberComboBoxIndex = FALSE; if ( (Style & CRS_PREFILLADMIN) != 0 && IsAdminAccount ) { RememberComboBoxIndex = TRUE; } // // Add the name to the combo box // ComboBoxIndex = UserNameComboBox.Add( TrimUsername( AccountDomainName, groupInfo[i].lgrmi2_domainandname), 0, IsAutoComplete, TRUE); if ( ComboBoxIndex == -1 ) { break; } // // If we're to remember the index, // do so. // if ( RememberComboBoxIndex ) { UserNameSelection = ComboBoxIndex; IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, ComboBoxIndex, 0); IsChangingUserName = FALSE; } } } delete [] adminSid; NetApiBufferFree(groupInfo); } } if ( AccountDomainName != NULL ) { NetApiBufferFree( AccountDomainName ); } return TRUE; } //============================================================================= // CreduiCredentialControl::InitWindow // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::InitWindow() { // Set that we're intialized here, even though the controls have not yet // been created, etc.: IsInitialized = TRUE; // Make sure WS_EX_CONTROLPARENT is set: SetWindowLong(Window, GWL_EXSTYLE, GetWindowLong(Window, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); // Initialize the balloon tip for this window: if (!CreateControls() || !BalloonTip.Init(CreduiInstance, Window)) { return FALSE; } // Limit the number of characters entered into the user name and password // edit controls: SendMessage(UserNameControlWindow, CB_LIMITTEXT, CREDUI_MAX_USERNAME_LENGTH, 0); SendMessage(PasswordControlWindow, EM_LIMITTEXT, CREDUI_MAX_PASSWORD_LENGTH, 0); // Set the password character to something cooler: PasswordBox.Init(PasswordControlWindow, &BalloonTip, &CreduiCapsLockTipInfo); // Initialize the user name auto complete combo box: if ( !KeepUserName ) { if (((Style & CRS_USERNAMES) && InitComboBoxUserNames()) || UserNameComboBox.Init(CreduiInstance, UserNameControlWindow, 0, IDB_TYPES, IMAGE_USERNAME)) { // Since we're finished adding auto complete names, enable it now. // On failure, the UI can still be presented: UserNameComboBox.Enable(); BOOL haveCertificates = FALSE; CertBaseInComboBox = (ULONG) SendMessage(UserNameControlWindow, CB_GETCOUNT, 0, 0); if (Style & CRS_CERTIFICATES) { haveCertificates = AddCertificates(); } SmartCardBaseInComboBox = CertBaseInComboBox + CertCount; if ((Style & CRS_SMARTCARDS) && CreduiHasSmartCardSupport) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: Call to SCardUIInit for %0x\n",Window); #endif ScardUiHandle = SCardUIInit(Window); if (ScardUiHandle == NULL) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: Call to SCardUIInit failed\n"); #endif CreduiDebugLog("CreduiCredentialControl::InitWindow: " "SCardUIInit failed\n"); } } // If NoEditUserName is allowed, make sure we eithet have at least one certificate // or a prefilled username for the control, otherwise fail if (NoEditUserName ) { if (!haveCertificates && (ScardUiHandle == NULL)) { return FALSE; } IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, 0, 0); IsChangingUserName = FALSE; // If we have at least one certificate, enable the view control // now. If a smart card, it will be enabled later: if (CertCount > 0) { EnableWindow(ViewCertControlWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_VIEW; } } // Wait until everything has been initialized before // we have the update. This will now properly determine if the default // user name is a smart card or not. OnUserNameSelectionChange(); } else { return FALSE; } } if ( !DoingCommandLine ) { SetFocus(UserNameControlWindow); } return TRUE; } //============================================================================= // CredioCredentialControl::Enable // // Enables or disables all the user controls in the control. // // Arguments: // enable (in) - TRUE to enable the controls, FALSE to disable. // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= VOID CreduiCredentialControl::Enable( BOOL enable ) { if (enable && (DisabledControlMask & DISABLED_CONTROL)) { DisabledControlMask &= ~DISABLED_CONTROL; //EnableWindow(UserNameStaticWindow, TRUE); //EnableWindow(UserNameControlWindow, TRUE); if (!(DisabledControlMask & DISABLED_CONTROL_USERNAME)) { EnableWindow(UserNameControlWindow, TRUE); EnableWindow(UserNameStaticWindow, TRUE); } if (!(DisabledControlMask & DISABLED_CONTROL_PASSWORD)) { EnableWindow(PasswordControlWindow, TRUE); EnableWindow(PasswordStaticWindow, TRUE); } if (!(DisabledControlMask & DISABLED_CONTROL_VIEW)) { EnableWindow(ViewCertControlWindow, TRUE); } if (SaveControlWindow != NULL) { if (!(DisabledControlMask & DISABLED_CONTROL_SAVE)) { EnableWindow(SaveControlWindow, TRUE); } } IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, UserNameSelection, 0); IsChangingUserName = FALSE; OnUserNameSelectionChange(); } else if (!(DisabledControlMask & DISABLED_CONTROL)) { // Hide the balloon tip before disabling the window: if (BalloonTip.IsVisible()) { BalloonTip.Hide(); } DisabledControlMask |= DISABLED_CONTROL; UserNameSelection = (LONG) SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0); EnableWindow(UserNameStaticWindow, FALSE); EnableWindow(UserNameControlWindow, FALSE); EnableWindow(ViewCertControlWindow, FALSE); EnableWindow(PasswordControlWindow, FALSE); SetFocus(UserNameControlWindow); EnableWindow(PasswordStaticWindow, FALSE); if (SaveControlWindow != NULL) { EnableWindow(SaveControlWindow, FALSE); } } } //============================================================================= // CreduiCredentialControl::MessageHandlerCallback // // This is the actual callback function for the control window. // // Arguments: // window (in) // message (in) // wParam (in) // lParam (in) // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= LRESULT CALLBACK CreduiCredentialControl::MessageHandlerCallback( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) { // CreduiDebugLog( "Control Callback: %8.8lx %8.8lx %8.8lx\n", message, wParam, lParam ); CreduiCredentialControl *that = reinterpret_cast( GetWindowLongPtr(window, 0)); if (that != NULL) { LRESULT result2; ASSERT(window == that->Window); // CreduiDebugLog( "Certhashes: %8.8lx %8.8lx\n", that, that->CertHashes ); result2 = that->MessageHandler(message, wParam, lParam); // CreduiDebugLog( "Certhashes2: %8.8lx %8.8lx\n", that, that->CertHashes ); return result2; } if (message == WM_CREATE) { CreduiCredentialControl *control = new CreduiCredentialControl; if (control != NULL) { // Initialize some state: control->FirstPaint = TRUE; control->ShowBalloonTip = FALSE; control->Window = window; control->Style = GetWindowLong(window, GWL_STYLE); // Store this object's pointer in the user data window long: SetLastError(0); LONG_PTR retPtr = SetWindowLongPtr(window, 0, reinterpret_cast(control)); if ( retPtr != 0 || GetLastError() == 0 ) { // we sucessfully set the window pointer // If any of the required styles are set, initialize the window // now. Otherwise, defer until CRM_INITSTYLE: if (control->Style & (CRS_USERNAMES | CRS_CERTIFICATES | CRS_SMARTCARDS)) { if (control->InitWindow()) { return TRUE; } } else { return TRUE; } } SetWindowLongPtr(window, 0, 0); delete control; control = NULL; } DestroyWindow(window); return 0; } return DefWindowProc(window, message, wParam, lParam); } //============================================================================= // CreduiCredentialControl::OnSetUserNameA // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnSetUserNameA( CHAR *userNameA ) { BOOL success = FALSE; if (userNameA != NULL) { ULONG bufferSize = lstrlenA(userNameA) + 1; WCHAR *userName = new WCHAR[bufferSize]; if (userName != NULL) { if (MultiByteToWideChar(CP_ACP, 0, userNameA, -1, userName, bufferSize) > 0) { success = OnSetUserName(userName); } delete [] userName; } } else { success = OnSetUserName(NULL); } return success; }; //============================================================================= // CreduiCredentialControl::OnSetUserName // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnSetUserName( WCHAR *userName ) { if ((userName == NULL) || !LocalCredIsMarshaledCredentialW(userName)) { // // Save the initial user name for command line // if ( DoingCommandLine ) { InitialUserName = new WCHAR[lstrlen(userName) + 1]; if ( InitialUserName == NULL ) { return FALSE; } lstrcpy( InitialUserName, userName ); } return SetWindowText(UserNameControlWindow, userName); } else { CRED_MARSHAL_TYPE credMarshalType; CERT_CREDENTIAL_INFO *certCredInfo = NULL; BOOL foundCert = FALSE; if (LocalCredUnmarshalCredentialW( userName, &credMarshalType, reinterpret_cast(&certCredInfo))) { // Search for the certificate. What can we do if it is a // smart card? Well, at least we can still search for it, // but it is a bit more work because we must retrieve the // hash from the context. if (credMarshalType == CertCredential) { for (UINT i = 0; i < CertCount; ++i) { if (RtlCompareMemory(CertHashes[i], certCredInfo->rgbHashOfCert, CERT_HASH_LENGTH) == CERT_HASH_LENGTH) { IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, CertBaseInComboBox + i, 0); IsChangingUserName = FALSE; OnUserNameSelectionChange(); EnableWindow(ViewCertControlWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_VIEW; foundCert = TRUE; break; } } // If we couldn't find the certificate in our list, determine // if this is a smart card certificate, based on its entry in // the MY certificate store. If it is, store the hash and // check for it on certificate arrival messages: if (!foundCert) { CONST CERT_CONTEXT *certContext = NULL; HCERTSTORE certStore = NULL; certStore = CertOpenSystemStore(NULL, L"MY"); if (certStore != NULL) { CRYPT_HASH_BLOB hashBlob; hashBlob.cbData = CERT_HASH_LENGTH; hashBlob.pbData = reinterpret_cast( certCredInfo->rgbHashOfCert); certContext = CertFindCertificateInStore( certStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &hashBlob, NULL); } // If we found a certificate context, check to see if it // is from a smart card: if ((certContext != NULL) && CreduiIsRemovableCertificate(certContext)) { UserNameCertHash = new UCHAR [1][CERT_HASH_LENGTH]; if (UserNameCertHash != NULL) { CopyMemory(UserNameCertHash, certCredInfo->rgbHashOfCert, CERT_HASH_LENGTH); foundCert = TRUE; } } // If we opened a store, free the certificate and close // the store: if (certStore != NULL) { if (certContext != NULL) { CertFreeCertificateContext(certContext); } if (!CertCloseStore(certStore, 0)) { CreduiDebugLog( "CreduiCredentialControl::OnSetUserName: " "CertCloseStore failed: %u\n", GetLastError()); } } } } LocalCredFree(static_cast(certCredInfo)); } else { // Could not unmarshal, so just forget it: CreduiDebugLog( "CreduiCredentialControl::OnSetUserName: " "CredUnmarshalCredential failed: %u\n", GetLastError()); } return foundCert; } }; //============================================================================= // CreduiCredentialControl::OnGetUserNameA // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnGetUserNameA( CHAR *userNameA, ULONG maxChars ) { BOOL success = FALSE; if ((userNameA != NULL) && (maxChars != 0)) { WCHAR *userName = new WCHAR[maxChars + 1]; if (userName != NULL) { if (OnGetUserName(userName, maxChars) && WideCharToMultiByte( CP_ACP, 0, userName, -1, userNameA, maxChars + 1, NULL, NULL)) { success = TRUE; } delete [] userName; } } return success; }; //============================================================================= // CreduiCredentialControl::OnGetUserName // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnGetUserName( WCHAR *userName, ULONG maxChars ) { if ( KeepUserName ) { SetLastError(0); return (GetWindowText(UserNameControlWindow, userName, maxChars + 1) > 0) || (GetLastError() == ERROR_SUCCESS); } else { COMBOBOXEXITEM item; item.iItem = SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0); // If we are trying to match a smart card certificate, fail this: if (UserNameCertHash != NULL) { return FALSE; } // If this is not a certificate, it's easy: if ((item.iItem == CB_ERR) || (item.iItem < CertBaseInComboBox)) { BOOL RetVal; SetLastError(0); RetVal = GetWindowText(UserNameControlWindow, userName, maxChars + 1) > 0; if ( !RetVal ) { return ( GetLastError() == ERROR_SUCCESS ); } // // Complete the typed in username if ( Style & CRS_COMPLETEUSERNAME) { RetVal = CompleteUserName( userName, maxChars, NULL, // No target info NULL, 0); // No target name } else { RetVal = TRUE; } return RetVal; } // This is a certificate, maybe from a smart card: item.mask = CBEIF_IMAGE | CBEIF_TEXT; item.pszText = userName; item.cchTextMax = maxChars + 1; if (!SendMessage(UserNameControlWindow, CBEM_GETITEM, 0, reinterpret_cast(&item))) { return FALSE; } CERT_CREDENTIAL_INFO certCredInfo; certCredInfo.cbSize = sizeof certCredInfo; if (item.iItem >= SmartCardBaseInComboBox) { if (item.iImage == IMAGE_SMART_CARD_MISSING) { return FALSE; } CERT_ENUM *certEnum = reinterpret_cast( SendMessage(UserNameControlWindow, CB_GETITEMDATA, item.iItem, 0)); // NOTE: Consider more complete error handling here. if (certEnum != NULL) { DWORD length = CERT_HASH_LENGTH; if (!CertGetCertificateContextProperty( certEnum->pCertContext, CERT_SHA1_HASH_PROP_ID, static_cast( certCredInfo.rgbHashOfCert), &length)) { return FALSE; } } else { return FALSE; } } else { CopyMemory(certCredInfo.rgbHashOfCert, &CertHashes[item.iItem - CertBaseInComboBox], CERT_HASH_LENGTH); } WCHAR *marshaledCred; if (LocalCredMarshalCredentialW(CertCredential, &certCredInfo, &marshaledCred)) { lstrcpyn(userName, marshaledCred, maxChars + 1); LocalCredFree(static_cast(marshaledCred)); return TRUE; } else { CreduiDebugLog("CreduiCredentialControl::OnGetUserName: " "CredMarshalCredential failed: %u\n", GetLastError()); return FALSE; } } } //============================================================================= // CreduiCredentialControl::OnSetPasswordA // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnSetPasswordA( CHAR *passwordA ) { return SetWindowTextA(PasswordControlWindow, passwordA); }; //============================================================================= // CreduiCredentialControl::OnSetPassword // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnSetPassword( WCHAR *password ) { return SetWindowText(PasswordControlWindow, password); }; //============================================================================= // CreduiCredentialControl::OnGetPasswordA // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnGetPasswordA( CHAR *passwordA, ULONG maxChars ) { if (DisabledControlMask & DISABLED_CONTROL_PASSWORD) { return FALSE; } SetLastError(0); return (GetWindowTextA(PasswordControlWindow, passwordA, maxChars + 1) > 0) || (GetLastError() == ERROR_SUCCESS); }; //============================================================================= // CreduiCredentialControl::OnGetPassword // // Created 06/22/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnGetPassword( WCHAR *password, ULONG maxChars ) { if (DisabledControlMask & DISABLED_CONTROL_PASSWORD) { return FALSE; } SetLastError(0); return (GetWindowText(PasswordControlWindow, password, maxChars + 1) > 0) || (GetLastError() == ERROR_SUCCESS); }; //============================================================================= // CreduiCredentialControl::OnGetUserNameLength // // Created 07/19/2000 johnstep (John Stephens) //============================================================================= LONG CreduiCredentialControl::OnGetUserNameLength() { COMBOBOXEXITEM item; if (UserNameCertHash != NULL) { return -1; } item.iItem = SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0); // If this is not a certificate, it's easy: if ((item.iItem == CB_ERR) || (item.iItem < CertBaseInComboBox)) { return GetWindowTextLength(UserNameControlWindow); } else { WCHAR userName[CREDUI_MAX_USERNAME_LENGTH + 1]; if (OnGetUserName(userName, CREDUI_MAX_USERNAME_LENGTH)) { return lstrlen(userName); } else { return -1; } } } //============================================================================= // CreduiCredentialControl::OnShowBalloonA // // Created 06/23/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnShowBalloonA( CREDUI_BALLOONA *balloonA ) { // If NULL was passed, this means to hide the balloon: if (balloonA == NULL) { if (BalloonTip.IsVisible()) { BalloonTip.Hide(); } return TRUE; } // Argument validation, should match OnShowBalloon: if ((balloonA->dwVersion != 1) || (balloonA->pszTitleText == NULL) || (balloonA->pszMessageText == NULL)) { return FALSE; } if ((balloonA->pszTitleText[0] == '\0') || (balloonA->pszMessageText[0] == '\0')) { return FALSE; } BOOL success = FALSE; CREDUI_BALLOON balloon; balloon.dwVersion = balloonA->dwVersion; balloon.iControl = balloonA->iControl; balloon.iIcon = balloonA->iIcon; ULONG titleTextLength = lstrlenA(balloonA->pszTitleText); ULONG messageTextLength = lstrlenA(balloonA->pszMessageText); balloon.pszTitleText = new WCHAR[titleTextLength + 1]; if (balloon.pszTitleText != NULL) { if (MultiByteToWideChar(CP_ACP, 0, balloonA->pszTitleText, -1, balloon.pszTitleText, titleTextLength + 1) > 0) { balloon.pszMessageText = new WCHAR[messageTextLength + 1]; if (balloon.pszMessageText != NULL) { if (MultiByteToWideChar(CP_ACP, 0, balloonA->pszMessageText, -1, balloon.pszMessageText, messageTextLength + 1) > 0) { success = OnShowBalloon(&balloon); } delete [] balloon.pszMessageText; } } delete [] balloon.pszTitleText; } return success; }; //============================================================================= // CreduiCredentialControl::OnShowBalloon // // Created 06/23/2000 johnstep (John Stephens) //============================================================================= BOOL CreduiCredentialControl::OnShowBalloon( CREDUI_BALLOON *balloon ) { // If NULL was passed, this means to hide the balloon: if (balloon == NULL) { if (BalloonTip.IsVisible()) { BalloonTip.Hide(); } return TRUE; } // Argument validation: if ((balloon->dwVersion != 1) || (balloon->pszTitleText == NULL) || (balloon->pszMessageText == NULL)) { return FALSE; } if ((balloon->pszTitleText[0] == L'\0') || (balloon->pszMessageText[0] == L'\0')) { return FALSE; } lstrcpyn(CreduiCustomTipInfo.Title, balloon->pszTitleText, CREDUI_MAX_BALLOON_TITLE_LENGTH + 1); lstrcpyn(CreduiCustomTipInfo.Text, balloon->pszMessageText, CREDUI_MAX_BALLOON_MESSAGE_LENGTH + 1); CreduiCustomTipInfo.Icon = balloon->iIcon; // BalloonTip.SetInfo( // (balloon->iControl == CREDUI_CONTROL_PASSWORD) ? // PasswordControlWindow : UserNameControlWindow, // &CreduiCustomTipInfo); if ( balloon->iControl != CREDUI_CONTROL_PASSWORD ) BalloonTip.SetInfo( UserNameControlWindow, &CreduiCustomTipInfo); BalloonTip.Show(); return TRUE; }; //============================================================================= // CreduiCredentialControl::OnUserNameSelectionChange // // Created 06/21/2000 johnstep (John Stephens) //============================================================================= VOID CreduiCredentialControl::OnUserNameSelectionChange() { COMBOBOXEXITEM item; LRESULT current; // Delete the user name certificate hash if the user has changed the // selection: if (UserNameCertHash != NULL) { delete [] UserNameCertHash; UserNameCertHash = NULL; } current = SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0); item.mask = CBEIF_IMAGE; item.iItem = current; SendMessage(UserNameControlWindow, CBEM_GETITEM, 0, reinterpret_cast(&item)); if (current < CertBaseInComboBox) { EnableWindow(ViewCertControlWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_VIEW; SetWindowText( PasswordStaticWindow, CreduiStrings.PasswordStatic); EnableWindow(PasswordControlWindow, TRUE); EnableWindow(PasswordStaticWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_PASSWORD; WCHAR* pUserNameLabel; if ( IsPassport ) pUserNameLabel = CreduiStrings.EmailName; else pUserNameLabel = CreduiStrings.UserNameStatic; SetWindowText( UserNameStaticWindow, pUserNameLabel); if (SaveControlWindow != NULL) { EnableWindow(SaveControlWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_SAVE; } } else { SetWindowText( PasswordStaticWindow, CreduiStrings.PinStatic); if (item.iImage != IMAGE_SMART_CARD_MISSING) { EnableWindow(ViewCertControlWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_VIEW; } else { EnableWindow(ViewCertControlWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_VIEW; } IsChangingPassword = TRUE; SetWindowText(PasswordControlWindow, NULL); IsChangingPassword = FALSE; if (current >= SmartCardBaseInComboBox) { EnableWindow(PasswordControlWindow, TRUE); EnableWindow(PasswordStaticWindow, TRUE); DisabledControlMask &= ~DISABLED_CONTROL_PASSWORD; } else { EnableWindow(PasswordControlWindow, FALSE); EnableWindow(PasswordStaticWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_PASSWORD; } SetWindowText( UserNameStaticWindow, item.iImage >= IMAGE_SMART_CARD ? CreduiStrings.SmartCardStatic : CreduiStrings.CertificateStatic); if (SaveControlWindow != NULL) { EnableWindow(SaveControlWindow, FALSE); DisabledControlMask |= DISABLED_CONTROL_SAVE; } } } //============================================================================= // CreduiCredentialControl::MessageHandler // // Called from the control window callback to handle the window messages. // // Arguments: // message (in) // wParam (in) // lParam (in) // // Created 06/20/2000 johnstep (John Stephens) //============================================================================= LRESULT CreduiCredentialControl::MessageHandler( UINT message, WPARAM wParam, LPARAM lParam ) { // If not initialized, only handle CRM_INITSTYLE: if (!IsInitialized) { if (message == CRM_INITSTYLE) { wParam &= CRS_USERNAMES | CRS_CERTIFICATES | CRS_SMARTCARDS | CRS_ADMINISTRATORS | CRS_PREFILLADMIN | CRS_COMPLETEUSERNAME | CRS_SAVECHECK | CRS_KEEPUSERNAME; if (wParam != 0) { Style |= wParam; SetWindowLong(Window, GWL_STYLE, GetWindowLong(Window, GWL_STYLE) | Style); DoingCommandLine = (BOOL) lParam; return InitWindow(); } return FALSE; } else { return DefWindowProc(Window, message, wParam, lParam); } } else if (message == WM_ENABLE) { Enable((BOOL) wParam); } // Always handle smart card messages, if support is available: if (ScardUiHandle != NULL) { // This function call will return TRUE if the message was handled: if (HandleSmartCardMessages( message, reinterpret_cast(wParam))) { return 0; } } switch (message) { case CRM_SETUSERNAMEMAX: SendMessage(UserNameControlWindow, CB_LIMITTEXT, wParam, 0); return TRUE; case CRM_SETPASSWORDMAX: SendMessage(PasswordControlWindow, EM_LIMITTEXT, wParam, 0); return TRUE; case CRM_DISABLEUSERNAME: { DisabledControlMask |= DISABLED_CONTROL_USERNAME; EnableWindow(UserNameControlWindow,FALSE); EnableWindow(UserNameStaticWindow,FALSE); return TRUE; } case CRM_ENABLEUSERNAME: { DisabledControlMask &= ~DISABLED_CONTROL_USERNAME; EnableWindow(UserNameControlWindow,TRUE); EnableWindow(UserNameStaticWindow,TRUE); return TRUE; } case CRM_GETUSERNAMEMAX: return SendMessage( reinterpret_cast( SendMessage(Window, CBEM_GETEDITCONTROL, 0, 0)), EM_GETLIMITTEXT, 0, 0); case CRM_GETPASSWORDMAX: return SendMessage(UserNameControlWindow, EM_GETLIMITTEXT, 0, 0); case CRM_SETUSERNAMEA: return OnSetUserNameA(reinterpret_cast(lParam)); case CRM_SETUSERNAMEW: return OnSetUserName(reinterpret_cast(lParam)); case CRM_GETUSERNAMEA: return OnGetUserNameA(reinterpret_cast(lParam), (ULONG) wParam); case CRM_GETUSERNAMEW: return OnGetUserName(reinterpret_cast(lParam), (ULONG) wParam); case CRM_SETPASSWORDA: return OnSetPasswordA(reinterpret_cast(lParam)); case CRM_SETPASSWORDW: return OnSetPassword(reinterpret_cast(lParam)); case CRM_GETPASSWORDA: return OnGetPasswordA(reinterpret_cast(lParam), (ULONG) wParam); case CRM_GETPASSWORDW: return OnGetPassword(reinterpret_cast(lParam), (ULONG) wParam); case CRM_GETUSERNAMELENGTH: return OnGetUserNameLength(); case CRM_GETPASSWORDLENGTH: if (IsWindowEnabled(PasswordControlWindow)) { return GetWindowTextLength(PasswordControlWindow); } return -1; case CRM_SETFOCUS: if ( DoingCommandLine ) { return 0; } switch (wParam) { case CREDUI_CONTROL_USERNAME: SetFocus(UserNameControlWindow); return TRUE; case CREDUI_CONTROL_PASSWORD: if (IsWindowEnabled(PasswordControlWindow)) { SetFocus(PasswordControlWindow); // NOTE: Is it OK to always select the entire password text // on this explicit set focus message? SendMessage(PasswordControlWindow, EM_SETSEL, 0, -1); return TRUE; } break; } return 0; case CRM_SHOWBALLOONA: return OnShowBalloonA(reinterpret_cast(lParam)); case CRM_SHOWBALLOONW: return OnShowBalloon(reinterpret_cast(lParam)); case CRM_GETMINSIZE: SIZE *minSize; minSize = reinterpret_cast(lParam); if (minSize != NULL) { minSize->cx = CREDUI_CONTROL_MIN_WIDTH; minSize->cy = CREDUI_CONTROL_MIN_HEIGHT; if (Style & CRS_SAVECHECK ) { minSize->cy += CREDUI_CONTROL_ADD_SAVE; } return TRUE; } return FALSE; case CRM_SETCHECK: switch (wParam) { case CREDUI_CONTROL_SAVE: if ((Style & CRS_SAVECHECK ) && IsWindowEnabled(SaveControlWindow)) { CheckDlgButton(Window, IDC_SAVE, lParam ? BST_CHECKED : BST_UNCHECKED); return TRUE; } break; } return FALSE; case CRM_GETCHECK: switch (wParam) { case CREDUI_CONTROL_SAVE: return (Style & CRS_SAVECHECK ) && IsWindowEnabled(SaveControlWindow) && IsDlgButtonChecked(Window, IDC_SAVE); default: return FALSE; } case CRM_DOCMDLINE: ASSERT( DoingCommandLine ); // // For smartcards, // just start the timer and we'll prompt when the timer has gone off. // TargetName = (LPWSTR)lParam; if ( Style & CRS_SMARTCARDS) { DWORD WinStatus; Heartbeats = 0; { WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1]; szMsg[0] = 0; FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, CreduiInstance, IDS_READING_SMARTCARDS, 0, szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, NULL); CredPutStdout(szMsg); } if ( SetTimer ( Window, CREDUI_HEARTBEAT_TIMER, CREDUI_HEARTBEAT_TIMER_VALUE, NULL ) == 0 ) { // bail out of our wait loop if we couldn't set a timer return GetLastError(); } // // For passwords, // just do the prompt to save. // } else { CmdlineSavePrompt(); PostQuitMessage( NO_ERROR ); } return NO_ERROR; case WM_HELP: return OnHelpInfo(lParam); case WM_SETFONT: // Forward font setting from dialog to each control, except the // password control since we use a special font there: if (UserNameStaticWindow != NULL) { SendMessage(UserNameStaticWindow, message, wParam, lParam); } if (UserNameControlWindow != NULL) { SendMessage(UserNameControlWindow, message, wParam, lParam); } if (ViewCertControlWindow != NULL) { SendMessage(ViewCertControlWindow, message, wParam, lParam); } if (PasswordStaticWindow != NULL) { SendMessage(PasswordStaticWindow, message, wParam, lParam); } if (PasswordControlWindow != NULL) { SendMessage(PasswordControlWindow, message, wParam, lParam); } if (SaveControlWindow != NULL) { SendMessage(SaveControlWindow, message, wParam, lParam); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_VIEW_CERT: ViewCertificate((INT) SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0)); return 0; case IDC_PASSWORD: if (HIWORD(wParam) == EN_CHANGE) { // Always send the change message? SendMessage( GetParent(Window), WM_COMMAND, MAKELONG(GetWindowLongPtr(Window, GWLP_ID), CRN_PASSWORDCHANGE), reinterpret_cast(Window)); } return 0; case IDC_USERNAME: switch (HIWORD(wParam)) { case CBN_EDITCHANGE: case CBN_DROPDOWN: case CBN_KILLFOCUS: if ((HIWORD(wParam) != CBN_EDITCHANGE) || !IsChangingUserName) { if (BalloonTip.IsVisible()) { BalloonTip.Hide(); } } if (HIWORD(wParam) == CBN_EDITCHANGE) { // Always send the change message? SendMessage( GetParent(Window), WM_COMMAND, MAKELONG(GetWindowLongPtr(Window, GWLP_ID), CRN_USERNAMECHANGE), reinterpret_cast(Window)); // If the name has changed as a result of user editing, // reset to user name settings: BOOL isDropped = (BOOL) SendMessage(UserNameControlWindow, CB_GETDROPPEDSTATE, 0, 0); if (isDropped) { OnUserNameSelectionChange(); RECT rect; GetClientRect(UserNameControlWindow, &rect); InvalidateRect(UserNameControlWindow, &rect, FALSE); SendMessage(Window, CB_SETCURSEL, SendMessage(Window, CB_GETCURSEL, 0, 0), 0); return 0; } if (IsChangingUserName) { return 0; } if (((UserNameCertHash != NULL) || (SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0) >= CertBaseInComboBox)) && !isDropped) { if (UserNameCertHash != NULL) { delete [] UserNameCertHash; UserNameCertHash = NULL; } if (!SendMessage(UserNameControlWindow, CB_GETDROPPEDSTATE, 0, 0)) { SetFocus(UserNameControlWindow); if (SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0) == CB_ERR) { IsChangingUserName = TRUE; UserNameComboBox.Update( -1, L"", IMAGE_USERNAME); IsChangingUserName = FALSE; IsChangingPassword = TRUE; SetWindowText(PasswordControlWindow, NULL); IsChangingPassword = FALSE; OnUserNameSelectionChange(); } } } } if (HIWORD(wParam) == CBN_DROPDOWN) { if (UserNameCertHash != NULL) { delete [] UserNameCertHash; UserNameCertHash = NULL; IsChangingUserName = TRUE; UserNameComboBox.Update( -1, L"", IMAGE_USERNAME); IsChangingUserName = FALSE; IsChangingPassword = TRUE; SetWindowText(PasswordControlWindow, NULL); IsChangingPassword = FALSE; OnUserNameSelectionChange(); } } return 0; case CBN_SELCHANGE: OnUserNameSelectionChange(); return 0; } break; case IDC_SAVE: if (HIWORD(wParam) == BN_CLICKED) { return TRUE; } break; } break; case WM_PAINT: if (FirstPaint && GetUpdateRect(Window, NULL, FALSE)) { FirstPaint = FALSE; if (ShowBalloonTip) { ShowBalloonTip = FALSE; BalloonTip.Show(); } } break; case WM_TIMER: if ( wParam == CREDUI_HEARTBEAT_TIMER ) { Heartbeats++; #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: thump thump\n",this->Window); #endif // // If we've waited long enough, // or all cards have been read, // process the cards. // if ( Heartbeats > CREDUI_MAX_HEARTBEATS || ( Heartbeats > 2 && SmartCardReadCount == 0 )) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: Heartbeat timeout\n",this->Window); #endif fputs( "\n", stdout ); KillTimer ( Window, CREDUI_HEARTBEAT_TIMER ); CmdlineSmartCardPrompt(); // // If we're going to wait longer, // let the user know we're making progress. // } else { fputs( ".", stdout ); } } break; case WM_DESTROY: if (PasswordControlWindow != NULL) { SetWindowText(PasswordControlWindow, NULL); DestroyWindow(PasswordControlWindow); PasswordControlWindow = NULL; } if (PasswordStaticWindow != NULL) { DestroyWindow(PasswordStaticWindow); PasswordStaticWindow = NULL; } if (ViewCertControlWindow != NULL) { DestroyWindow(ViewCertControlWindow); ViewCertControlWindow = NULL; } if (UserNameControlWindow != NULL) { DestroyWindow(UserNameControlWindow); UserNameControlWindow = NULL; } if (UserNameStaticWindow != NULL) { DestroyWindow(UserNameStaticWindow); UserNameStaticWindow = NULL; } if (ScardUiHandle != NULL) { #ifdef SCARDREPORTS CreduiDebugLog("CREDUI: Call to SCardUIExit\n"); #endif SCardUIExit(ScardUiHandle); ScardUiHandle = NULL; } if (UserNameCertHash != NULL) { delete [] UserNameCertHash; UserNameCertHash = NULL; } if (CertCount > 0) { ASSERT(CertHashes != NULL); delete [] CertHashes; CertHashes = NULL; CertCount = 0; } if ( InitialUserName != NULL ) { delete InitialUserName; InitialUserName = NULL; } // Only call CoUninitialize if we successfully initialized for STA: if (IsAutoComplete) { CoUninitialize(); } return 0; case WM_NCDESTROY: delete this; return 0; } return DefWindowProc(Window, message, wParam, lParam); } BOOL CreduiCredentialControl::GetSmartCardInfo( IN DWORD SmartCardIndex, IN DWORD BufferLength, OUT LPWSTR Buffer, OUT BOOL *IsValid, OUT CERT_ENUM **CertEnum OPTIONAL ) /*++ Routine Description: Routine to get the smart card info for a smart card in the combo box Arguments: SmartCardIndex - Index of the smart card relative to SmartCardBaseInComboBox BufferLength - Specifies the length of Buffer (in characters) Buffer - Specifies the buffer to return the text for the smart card IsValid - Return TRUE if the smartcard is valid Returns FALSE otherwise CertEnum - If specified, returns the description of the cert on the smartcard This field is should be ignore if IsValid is returns false Return Values: Returns TRUE if Buffer and IsValid are filled in. --*/ { COMBOBOXEXITEM item; // // Get the item from the control // item.iItem = SmartCardBaseInComboBox + SmartCardIndex; item.mask = CBEIF_IMAGE | CBEIF_TEXT; item.pszText = Buffer; item.cchTextMax = BufferLength; if (!SendMessage(UserNameControlWindow, CBEM_GETITEM, 0, reinterpret_cast(&item))) { return FALSE; } *IsValid = (item.iImage == IMAGE_SMART_CARD); if ( CertEnum != NULL) { if ( *IsValid ) { *CertEnum = (CERT_ENUM *) SendMessage( UserNameControlWindow, CB_GETITEMDATA, item.iItem, 0); // NOTE: Consider more complete error handling here. if ( *CertEnum == NULL) { return FALSE; } } } return TRUE; } LPWSTR CreduiCredentialControl::MatchSmartCard( IN DWORD SmartCardCount, IN LPWSTR UserName, OUT LPDWORD RetCertIndex, OUT CERT_ENUM **RetCertEnum ) /*++ Routine Description: Returns the smart card that matches UserName. Arguments: SmartCardCount - specifies the number of smart cards to search UserName - specifies the user name to match RetCertIndex - returns an index to the found smart card. RetCertEnum - returns the description of the cert on the smartcard Return Values: Returns NULL if UserName matches one of the smart cards On failure, returns a printf-style format string describing the error --*/ { WCHAR SmartCardText[CREDUI_MAX_USERNAME_LENGTH + 1]; DWORD i; BOOL SmartCardValid; CERT_ENUM *CertEnum; CERT_ENUM *SavedCertEnum = NULL; DWORD SavedCertIndex = 0; // // Loop through the list of smart cards seeing if we see a match // for ( i=0; i 0) iWhich -= 1; if ( !GetSmartCardInfo( iWhich, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, &SavedCertEnum ) ) { // Invalid if that indexed card did not read correctly szMsg[0] = 0; FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, CreduiInstance, IDS_READERINVALID, 0, szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, NULL); CredPutStdout(szMsg); WinStatus = ERROR_CANCELLED; goto Cleanup; } // At this point, a valid number was entered, and an attempt to read the card made // GetSmartCardInfo() returned OK, but SmartCardValid may still be false if (!SmartCardValid) { szMsg[0] = 0; FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, CreduiInstance, IDS_READERINVALID, 0, szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, NULL); CredPutStdout(szMsg); WinStatus = ERROR_CANCELLED; goto Cleanup; } else { SavedCertIndex = iWhich; WinStatus = NO_ERROR; goto Cleanup; } } WinStatus = ERROR_CANCELLED; // // Complete the operation. // // WinStatus is the status of the operation so far // if NO_ERROR, SavedCertEnum is the description of the cert to use, and // SavedCertIndex is the index to the selected cert. // Cleanup: if ( WinStatus == NO_ERROR) { if ( CredUIMarshallNode ( SavedCertEnum, UserName ) ) { // // Save the username // UserNameSelection = SmartCardBaseInComboBox + SavedCertIndex; IsChangingUserName = TRUE; SendMessage(UserNameControlWindow, CB_SETCURSEL, UserNameSelection, 0); IsChangingUserName = FALSE; // // Prompt for the pin // //CredPutStdout( CreduiStrings.PinPrompt ); //swprintf(szMsg,CreduiStrings.CmdLineThisCard,SavedCertIndex + 1); szMsg[0] = 0; i = SavedCertIndex + 1; FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, Instance, IDS_CMDLINE_THISCARD, 0, szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, (va_list *) &i); CredPutStdout(szMsg); CredGetStdin( Password, CREDUI_MAX_PASSWORD_LENGTH, FALSE ); // // Save the pin // if (!OnSetPassword( Password ) ) { WinStatus = GetLastError(); CreduiDebugLog("CreduiCredentialControl::CmdlineSmartCardPrompt: " "OnSetPassword failed: %u\n", WinStatus ); } // // Prompt whether the save the cred or not // CmdlineSavePrompt(); } else { WinStatus = GetLastError(); CreduiDebugLog("CreduiCredentialControl::CmdlineSmartCardPrompt: " "CredMarshalCredential failed: %u\n", WinStatus ); } } // // Tell our parent window that we're done prompting // PostQuitMessage( WinStatus ); return; } void CreduiCredentialControl::CmdlineSavePrompt() /*++ Routine Description: Command line code to prompt for saving the credential Arguments: None Return Values: None --*/ { WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1]; WCHAR szY[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1]; WCHAR szN[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1]; // // Only prompt if we've been asked to display the checkbox // while ( Style & CRS_SAVECHECK ) { WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH+1]; // Fetch the strings one by one from the messages, and cobble them together WCHAR *rgsz[2]; szY[0] = 0; szN[0] = 0; rgsz[0] = szY; rgsz[1] = szN; szMsg[0] = 0; // Fetch yes and no strings FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, CreduiInstance, IDS_YES_TEXT, 0, szY, CREDUI_MAX_CMDLINE_MSG_LENGTH, NULL); FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, CreduiInstance, IDS_NO_TEXT, 0, szN, CREDUI_MAX_CMDLINE_MSG_LENGTH, NULL); // Arg substitute them into the prompt FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, CreduiInstance, IDS_SAVE_PROMPT, 0, szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, (va_list *) rgsz); szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH] = L'\0'; CredPutStdout( szMsg ); CredGetStdin( szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, TRUE ); // if ( toupper(szMsg[0]) == toupper(CreduiStrings.YesText[0]) ) { if ( toupper(szMsg[0]) == toupper(szY[0]) ) { Credential_CheckSave( Window, TRUE ); break; // } else if ( toupper(szMsg[0]) == toupper(CreduiStrings.NoText[0]) ) { } else if ( toupper(szMsg[0]) == toupper(szN[0]) ) { Credential_CheckSave( Window, FALSE ); break; } } } UINT CreduiCredentialControl::MapID(UINT uiID) { switch(uiID) { case IDC_USERNAME: return IDH_USERNAMEEDIT; case IDC_PASSWORD: return IDH_PASSWORDEDIT; case IDC_SAVE: return IDH_SAVECHECKBOX; default: return IDS_NOHELP; } } BOOL CreduiCredentialControl::OnHelpInfo(LPARAM lp) { HELPINFO* pH; INT iMapped; pH = (HELPINFO *) lp; HH_POPUP stPopUp; RECT rcW; UINT gID; 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 = Instance; 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; 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; }