credui: Add a stub for CredUIInitControls.
[wine] / dlls / credui / credui_main.c
1 /*
2  * Credentials User Interface
3  *
4  * Copyright 2006 Robert Shearman (for CodeWeavers)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnt.h"
26 #include "winuser.h"
27 #include "wincred.h"
28 #include "commctrl.h"
29
30 #include "credui_resources.h"
31
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 #include "wine/list.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(credui);
37
38 #define TOOLID_INCORRECTPASSWORD    1
39 #define TOOLID_CAPSLOCKON           2
40
41 #define ID_CAPSLOCKPOP              1
42
43 struct pending_credentials
44 {
45     struct list entry;
46     PWSTR pszTargetName;
47     PWSTR pszUsername;
48     PWSTR pszPassword;
49     BOOL generic;
50 };
51
52 static HINSTANCE hinstCredUI;
53
54 static struct list pending_credentials_list = LIST_INIT(pending_credentials_list);
55
56 static CRITICAL_SECTION csPendingCredentials;
57 static CRITICAL_SECTION_DEBUG critsect_debug =
58 {
59     0, 0, &csPendingCredentials,
60     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
61     0, 0, { (DWORD_PTR)(__FILE__ ": csPendingCredentials") }
62 };
63 static CRITICAL_SECTION csPendingCredentials = { &critsect_debug, -1, 0, 0, 0, 0 };
64
65
66 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
67 {
68     struct pending_credentials *entry, *cursor2;
69     TRACE("(0x%p, %d, %p)\n",hinstDLL,fdwReason,lpvReserved);
70
71     switch (fdwReason)
72     {
73     case DLL_WINE_PREATTACH:
74         return FALSE;   /* prefer native version */
75
76     case DLL_PROCESS_ATTACH:
77         DisableThreadLibraryCalls(hinstDLL);
78         hinstCredUI = hinstDLL;
79         InitCommonControls();
80         break;
81
82     case DLL_PROCESS_DETACH:
83         LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &pending_credentials_list, struct pending_credentials, entry)
84         {
85             list_remove(&entry->entry);
86
87             HeapFree(GetProcessHeap(), 0, entry->pszTargetName);
88             HeapFree(GetProcessHeap(), 0, entry->pszUsername);
89             ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
90             HeapFree(GetProcessHeap(), 0, entry->pszPassword);
91             HeapFree(GetProcessHeap(), 0, entry);
92         }
93         DeleteCriticalSection(&csPendingCredentials);
94         break;
95     }
96
97     return TRUE;
98 }
99
100 static DWORD save_credentials(PCWSTR pszTargetName, PCWSTR pszUsername,
101                               PCWSTR pszPassword, BOOL generic)
102 {
103     CREDENTIALW cred;
104
105     TRACE("saving servername %s with username %s\n", debugstr_w(pszTargetName), debugstr_w(pszUsername));
106
107     cred.Flags = 0;
108     cred.Type = generic ? CRED_TYPE_GENERIC : CRED_TYPE_DOMAIN_PASSWORD;
109     cred.TargetName = (LPWSTR)pszTargetName;
110     cred.Comment = NULL;
111     cred.CredentialBlobSize = strlenW(pszPassword) * sizeof(WCHAR);
112     cred.CredentialBlob = (LPBYTE)pszPassword;
113     cred.Persist = CRED_PERSIST_ENTERPRISE;
114     cred.AttributeCount = 0;
115     cred.Attributes = NULL;
116     cred.TargetAlias = NULL;
117     cred.UserName = (LPWSTR)pszUsername;
118
119     if (CredWriteW(&cred, 0))
120         return ERROR_SUCCESS;
121     else
122     {
123         DWORD ret = GetLastError();
124         ERR("CredWriteW failed with error %d\n", ret);
125         return ret;
126     }
127 }
128
129 struct cred_dialog_params
130 {
131     PCWSTR pszTargetName;
132     PCWSTR pszMessageText;
133     PCWSTR pszCaptionText;
134     HBITMAP hbmBanner;
135     PWSTR pszUsername;
136     ULONG ulUsernameMaxChars;
137     PWSTR pszPassword;
138     ULONG ulPasswordMaxChars;
139     BOOL fSave;
140     DWORD dwFlags;
141     HWND hwndBalloonTip;
142     BOOL fBalloonTipActive;
143 };
144
145 static void CredDialogFillUsernameCombo(HWND hwndUsername, const struct cred_dialog_params *params)
146 {
147     DWORD count;
148     DWORD i;
149     PCREDENTIALW *credentials;
150
151     if (!CredEnumerateW(NULL, 0, &count, &credentials))
152         return;
153
154     for (i = 0; i < count; i++)
155     {
156         COMBOBOXEXITEMW comboitem;
157         DWORD j;
158         BOOL duplicate = FALSE;
159
160         if (params->dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS)
161         {
162             if ((credentials[i]->Type != CRED_TYPE_GENERIC) || !credentials[i]->UserName)
163                 continue;
164         }
165         else
166         {
167             if (credentials[i]->Type == CRED_TYPE_GENERIC)
168                 continue;
169         }
170
171         /* don't add another item with the same name if we've already added it */
172         for (j = 0; j < i; j++)
173             if (!strcmpW(credentials[i]->UserName, credentials[j]->UserName))
174             {
175                 duplicate = TRUE;
176                 break;
177             }
178
179         if (duplicate)
180             continue;
181
182         comboitem.mask = CBEIF_TEXT;
183         comboitem.iItem = -1;
184         comboitem.pszText = credentials[i]->UserName;
185         SendMessageW(hwndUsername, CBEM_INSERTITEMW, 0, (LPARAM)&comboitem);
186     }
187
188     CredFree(credentials);
189 }
190
191 static void CredDialogCreateBalloonTip(HWND hwndDlg, struct cred_dialog_params *params)
192 {
193     TTTOOLINFOW toolinfo;
194     WCHAR wszText[256];
195
196     if (params->hwndBalloonTip)
197         return;
198
199     params->hwndBalloonTip = CreateWindowExW(WS_EX_TOOLWINDOW, TOOLTIPS_CLASSW,
200         NULL, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT,
201         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL,
202         hinstCredUI, NULL);
203     SetWindowPos(params->hwndBalloonTip, HWND_TOPMOST, 0, 0, 0, 0,
204                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
205
206     if (!LoadStringW(hinstCredUI, IDS_INCORRECTPASSWORD, wszText, sizeof(wszText)/sizeof(wszText[0])))
207     {
208         ERR("failed to load IDS_INCORRECTPASSWORD\n");
209         return;
210     }
211
212     toolinfo.cbSize = sizeof(toolinfo);
213     toolinfo.uFlags = TTF_TRACK;
214     toolinfo.hwnd = hwndDlg;
215     toolinfo.uId = TOOLID_INCORRECTPASSWORD;
216     memset(&toolinfo.rect, 0, sizeof(toolinfo.rect));
217     toolinfo.hinst = NULL;
218     toolinfo.lpszText = wszText;
219     toolinfo.lParam = 0;
220     toolinfo.lpReserved = NULL;
221     SendMessageW(params->hwndBalloonTip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo);
222
223     if (!LoadStringW(hinstCredUI, IDS_CAPSLOCKON, wszText, sizeof(wszText)/sizeof(wszText[0])))
224     {
225         ERR("failed to load IDS_CAPSLOCKON\n");
226         return;
227     }
228
229     toolinfo.uId = TOOLID_CAPSLOCKON;
230     SendMessageW(params->hwndBalloonTip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo);
231 }
232
233 static void CredDialogShowIncorrectPasswordBalloon(HWND hwndDlg, struct cred_dialog_params *params)
234 {
235     TTTOOLINFOW toolinfo;
236     RECT rcPassword;
237     INT x;
238     INT y;
239     WCHAR wszTitle[256];
240
241     /* user name likely wrong so balloon would be confusing. focus is also
242      * not set to the password edit box, so more notification would need to be
243      * handled */
244     if (!params->pszUsername[0])
245         return;
246
247     /* don't show two balloon tips at once */
248     if (params->fBalloonTipActive)
249         return;
250
251     if (!LoadStringW(hinstCredUI, IDS_INCORRECTPASSWORDTITLE, wszTitle, sizeof(wszTitle)/sizeof(wszTitle[0])))
252     {
253         ERR("failed to load IDS_INCORRECTPASSWORDTITLE\n");
254         return;
255     }
256
257     CredDialogCreateBalloonTip(hwndDlg, params);
258
259     memset(&toolinfo, 0, sizeof(toolinfo));
260     toolinfo.cbSize = sizeof(toolinfo);
261     toolinfo.hwnd = hwndDlg;
262     toolinfo.uId = TOOLID_INCORRECTPASSWORD;
263
264     SendMessageW(params->hwndBalloonTip, TTM_SETTITLEW, TTI_ERROR, (LPARAM)wszTitle);
265
266     GetWindowRect(GetDlgItem(hwndDlg, IDC_PASSWORD), &rcPassword);
267     /* centered vertically and in the right side of the password edit control */
268     x = rcPassword.right - 12;
269     y = (rcPassword.top + rcPassword.bottom) / 2;
270     SendMessageW(params->hwndBalloonTip, TTM_TRACKPOSITION, 0, MAKELONG(x, y));
271
272     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolinfo);
273
274     params->fBalloonTipActive = TRUE;
275 }
276
277 static void CredDialogShowCapsLockBalloon(HWND hwndDlg, struct cred_dialog_params *params)
278 {
279     TTTOOLINFOW toolinfo;
280     RECT rcPassword;
281     INT x;
282     INT y;
283     WCHAR wszTitle[256];
284
285     /* don't show two balloon tips at once */
286     if (params->fBalloonTipActive)
287         return;
288
289     if (!LoadStringW(hinstCredUI, IDS_CAPSLOCKONTITLE, wszTitle, sizeof(wszTitle)/sizeof(wszTitle[0])))
290     {
291         ERR("failed to load IDS_IDSCAPSLOCKONTITLE\n");
292         return;
293     }
294
295     CredDialogCreateBalloonTip(hwndDlg, params);
296
297     memset(&toolinfo, 0, sizeof(toolinfo));
298     toolinfo.cbSize = sizeof(toolinfo);
299     toolinfo.hwnd = hwndDlg;
300     toolinfo.uId = TOOLID_CAPSLOCKON;
301
302     SendMessageW(params->hwndBalloonTip, TTM_SETTITLEW, TTI_WARNING, (LPARAM)wszTitle);
303
304     GetWindowRect(GetDlgItem(hwndDlg, IDC_PASSWORD), &rcPassword);
305     /* just inside the left side of the password edit control */
306     x = rcPassword.left + 12;
307     y = rcPassword.bottom - 3;
308     SendMessageW(params->hwndBalloonTip, TTM_TRACKPOSITION, 0, MAKELONG(x, y));
309
310     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolinfo);
311
312     SetTimer(hwndDlg, ID_CAPSLOCKPOP,
313              SendMessageW(params->hwndBalloonTip, TTM_GETDELAYTIME, TTDT_AUTOPOP, 0),
314              NULL);
315
316     params->fBalloonTipActive = TRUE;
317 }
318
319 static void CredDialogHideBalloonTip(HWND hwndDlg, struct cred_dialog_params *params)
320 {
321     TTTOOLINFOW toolinfo;
322
323     if (!params->hwndBalloonTip)
324         return;
325
326     memset(&toolinfo, 0, sizeof(toolinfo));
327
328     toolinfo.cbSize = sizeof(toolinfo);
329     toolinfo.hwnd = hwndDlg;
330     toolinfo.uId = 0;
331     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&toolinfo);
332     toolinfo.uId = 1;
333     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&toolinfo);
334
335     params->fBalloonTipActive = FALSE;
336 }
337
338 static inline BOOL CredDialogCapsLockOn(void)
339 {
340     return GetKeyState(VK_CAPITAL) & 0x1 ? TRUE : FALSE;
341 }
342
343 static LRESULT CALLBACK CredDialogPasswordSubclassProc(HWND hwnd, UINT uMsg,
344     WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
345 {
346     struct cred_dialog_params *params = (struct cred_dialog_params *)dwRefData;
347     switch (uMsg)
348     {
349     case WM_KEYDOWN:
350         if (wParam == VK_CAPITAL)
351         {
352             HWND hwndDlg = GetParent(hwnd);
353             if (CredDialogCapsLockOn())
354                 CredDialogShowCapsLockBalloon(hwndDlg, params);
355             else
356                 CredDialogHideBalloonTip(hwndDlg, params);
357         }
358         break;
359     case WM_DESTROY:
360         RemoveWindowSubclass(hwnd, CredDialogPasswordSubclassProc, uIdSubclass);
361         break;
362     }
363     return DefSubclassProc(hwnd, uMsg, wParam, lParam);
364 }
365
366 static BOOL CredDialogInit(HWND hwndDlg, struct cred_dialog_params *params)
367 {
368     HWND hwndUsername = GetDlgItem(hwndDlg, IDC_USERNAME);
369     HWND hwndPassword = GetDlgItem(hwndDlg, IDC_PASSWORD);
370
371     SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)params);
372
373     if (params->hbmBanner)
374         SendMessageW(GetDlgItem(hwndDlg, IDB_BANNER), STM_SETIMAGE,
375                      IMAGE_BITMAP, (LPARAM)params->hbmBanner);
376
377     if (params->pszMessageText)
378         SetDlgItemTextW(hwndDlg, IDC_MESSAGE, params->pszMessageText);
379     else
380     {
381         WCHAR format[256];
382         WCHAR message[256];
383         LoadStringW(hinstCredUI, IDS_MESSAGEFORMAT, format, sizeof(format)/sizeof(format[0]));
384         snprintfW(message, sizeof(message)/sizeof(message[0]), format, params->pszTargetName);
385         SetDlgItemTextW(hwndDlg, IDC_MESSAGE, message);
386     }
387     SetWindowTextW(hwndUsername, params->pszUsername);
388     SetWindowTextW(hwndPassword, params->pszPassword);
389
390     CredDialogFillUsernameCombo(hwndUsername, params);
391
392     if (params->pszUsername[0])
393     {
394         /* prevent showing a balloon tip here */
395         params->fBalloonTipActive = TRUE;
396         SetFocus(hwndPassword);
397         params->fBalloonTipActive = FALSE;
398     }
399     else
400         SetFocus(hwndUsername);
401
402     if (params->pszCaptionText)
403         SetWindowTextW(hwndDlg, params->pszCaptionText);
404     else
405     {
406         WCHAR format[256];
407         WCHAR title[256];
408         LoadStringW(hinstCredUI, IDS_TITLEFORMAT, format, sizeof(format)/sizeof(format[0]));
409         snprintfW(title, sizeof(title)/sizeof(title[0]), format, params->pszTargetName);
410         SetWindowTextW(hwndDlg, title);
411     }
412
413     if (params->dwFlags & (CREDUI_FLAGS_DO_NOT_PERSIST|CREDUI_FLAGS_PERSIST))
414         ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_HIDE);
415     else if (params->fSave)
416         CheckDlgButton(hwndDlg, IDC_SAVE, BST_CHECKED);
417
418     /* setup subclassing for Caps Lock detection */
419     SetWindowSubclass(hwndPassword, CredDialogPasswordSubclassProc, 1, (DWORD_PTR)params);
420
421     if (params->dwFlags & CREDUI_FLAGS_INCORRECT_PASSWORD)
422         CredDialogShowIncorrectPasswordBalloon(hwndDlg, params);
423     else if ((GetFocus() == hwndPassword) && CredDialogCapsLockOn())
424         CredDialogShowCapsLockBalloon(hwndDlg, params);
425
426     return FALSE;
427 }
428
429 static void CredDialogCommandOk(HWND hwndDlg, struct cred_dialog_params *params)
430 {
431     HWND hwndUsername = GetDlgItem(hwndDlg, IDC_USERNAME);
432     LPWSTR user;
433     INT len;
434     INT len2;
435
436     len = GetWindowTextLengthW(hwndUsername);
437     user = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
438     GetWindowTextW(hwndUsername, user, len + 1);
439
440     if (!user[0])
441     {
442         HeapFree(GetProcessHeap(), 0, user);
443         return;
444     }
445
446     if (!strchrW(user, '\\') && !strchrW(user, '@'))
447     {
448         ULONG len_target = strlenW(params->pszTargetName);
449         memcpy(params->pszUsername, params->pszTargetName,
450                min(len_target, params->ulUsernameMaxChars) * sizeof(WCHAR));
451         if (len_target + 1 < params->ulUsernameMaxChars)
452             params->pszUsername[len_target] = '\\';
453         if (len_target + 2 < params->ulUsernameMaxChars)
454             params->pszUsername[len_target + 1] = '\0';
455     }
456     else if (params->ulUsernameMaxChars > 0)
457         params->pszUsername[0] = '\0';
458
459     len2 = strlenW(params->pszUsername);
460     memcpy(params->pszUsername + len2, user, min(len, params->ulUsernameMaxChars - len2) * sizeof(WCHAR));
461     if (params->ulUsernameMaxChars)
462         params->pszUsername[len2 + min(len, params->ulUsernameMaxChars - len2 - 1)] = '\0';
463
464     HeapFree(GetProcessHeap(), 0, user);
465
466     GetDlgItemTextW(hwndDlg, IDC_PASSWORD, params->pszPassword,
467                     params->ulPasswordMaxChars);
468
469     params->fSave = IsDlgButtonChecked(hwndDlg, IDC_SAVE) == BST_CHECKED;
470
471     EndDialog(hwndDlg, IDOK);
472 }
473
474 static INT_PTR CALLBACK CredDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,
475                                        LPARAM lParam)
476 {
477     switch (uMsg)
478     {
479         case WM_INITDIALOG:
480         {
481             struct cred_dialog_params *params = (struct cred_dialog_params *)lParam;
482
483             return CredDialogInit(hwndDlg, params);
484         }
485         case WM_COMMAND:
486             switch (wParam)
487             {
488                 case MAKELONG(IDOK, BN_CLICKED):
489                 {
490                     struct cred_dialog_params *params =
491                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
492                     CredDialogCommandOk(hwndDlg, params);
493                     return TRUE;
494                 }
495                 case MAKELONG(IDCANCEL, BN_CLICKED):
496                     EndDialog(hwndDlg, IDCANCEL);
497                     return TRUE;
498                 case MAKELONG(IDC_PASSWORD, EN_SETFOCUS):
499                     if (CredDialogCapsLockOn())
500                     {
501                         struct cred_dialog_params *params =
502                             (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
503                         CredDialogShowCapsLockBalloon(hwndDlg, params);
504                     }
505                     /* don't allow another window to steal focus while the
506                      * user is typing their password */
507                     LockSetForegroundWindow(LSFW_LOCK);
508                     return TRUE;
509                 case MAKELONG(IDC_PASSWORD, EN_KILLFOCUS):
510                 {
511                     struct cred_dialog_params *params =
512                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
513                     /* the user is no longer typing their password, so allow
514                      * other windows to become foreground ones */
515                     LockSetForegroundWindow(LSFW_UNLOCK);
516                     CredDialogHideBalloonTip(hwndDlg, params);
517                     return TRUE;
518                 }
519                 case MAKELONG(IDC_PASSWORD, EN_CHANGE):
520                 {
521                     struct cred_dialog_params *params =
522                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
523                     CredDialogHideBalloonTip(hwndDlg, params);
524                     return TRUE;
525                 }
526             }
527             return FALSE;
528         case WM_TIMER:
529             if (wParam == ID_CAPSLOCKPOP)
530             {
531                 struct cred_dialog_params *params =
532                     (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
533                 CredDialogHideBalloonTip(hwndDlg, params);
534                 return TRUE;
535             }
536             return FALSE;
537         case WM_DESTROY:
538         {
539             struct cred_dialog_params *params =
540                 (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
541             if (params->hwndBalloonTip) DestroyWindow(params->hwndBalloonTip);
542             return TRUE;
543         }
544         default:
545             return FALSE;
546     }
547 }
548
549 /******************************************************************************
550  *           CredUIPromptForCredentialsW [CREDUI.@]
551  */
552 DWORD WINAPI CredUIPromptForCredentialsW(PCREDUI_INFOW pUIInfo,
553                                          PCWSTR pszTargetName,
554                                          PCtxtHandle Reserved,
555                                          DWORD dwAuthError,
556                                          PWSTR pszUsername,
557                                          ULONG ulUsernameMaxChars,
558                                          PWSTR pszPassword,
559                                          ULONG ulPasswordMaxChars, PBOOL pfSave,
560                                          DWORD dwFlags)
561 {
562     INT_PTR ret;
563     struct cred_dialog_params params;
564     DWORD result = ERROR_SUCCESS;
565
566     TRACE("(%p, %s, %p, %d, %s, %d, %p, %d, %p, 0x%08x)\n", pUIInfo,
567           debugstr_w(pszTargetName), Reserved, dwAuthError, debugstr_w(pszUsername),
568           ulUsernameMaxChars, pszPassword, ulPasswordMaxChars, pfSave, dwFlags);
569
570     if ((dwFlags & (CREDUI_FLAGS_ALWAYS_SHOW_UI|CREDUI_FLAGS_GENERIC_CREDENTIALS)) == CREDUI_FLAGS_ALWAYS_SHOW_UI)
571         return ERROR_INVALID_FLAGS;
572
573     if (!pszTargetName)
574         return ERROR_INVALID_PARAMETER;
575
576     if ((dwFlags & CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX) && !pfSave)
577         return ERROR_INVALID_PARAMETER;
578
579     params.pszTargetName = pszTargetName;
580     if (pUIInfo)
581     {
582         params.pszMessageText = pUIInfo->pszMessageText;
583         params.pszCaptionText = pUIInfo->pszCaptionText;
584         params.hbmBanner  = pUIInfo->hbmBanner;
585     }
586     else
587     {
588         params.pszMessageText = NULL;
589         params.pszCaptionText = NULL;
590         params.hbmBanner = NULL;
591     }
592     params.pszUsername = pszUsername;
593     params.ulUsernameMaxChars = ulUsernameMaxChars;
594     params.pszPassword = pszPassword;
595     params.ulPasswordMaxChars = ulPasswordMaxChars;
596     params.fSave = pfSave ? *pfSave : FALSE;
597     params.dwFlags = dwFlags;
598     params.hwndBalloonTip = NULL;
599     params.fBalloonTipActive = FALSE;
600
601     ret = DialogBoxParamW(hinstCredUI, MAKEINTRESOURCEW(IDD_CREDDIALOG),
602                           pUIInfo ? pUIInfo->hwndParent : NULL,
603                           CredDialogProc, (LPARAM)&params);
604     if (ret <= 0)
605         return GetLastError();
606
607     if (ret == IDCANCEL)
608     {
609         TRACE("dialog cancelled\n");
610         return ERROR_CANCELLED;
611     }
612
613     if (pfSave)
614         *pfSave = params.fSave;
615
616     if (params.fSave)
617     {
618         if (dwFlags & CREDUI_FLAGS_EXPECT_CONFIRMATION)
619         {
620             BOOL found = FALSE;
621             struct pending_credentials *entry;
622             int len;
623
624             EnterCriticalSection(&csPendingCredentials);
625
626             /* find existing pending credentials for the same target and overwrite */
627             /* FIXME: is this correct? */
628             LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
629                 if (!strcmpW(pszTargetName, entry->pszTargetName))
630                 {
631                     found = TRUE;
632                     HeapFree(GetProcessHeap(), 0, entry->pszUsername);
633                     ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
634                     HeapFree(GetProcessHeap(), 0, entry->pszPassword);
635                 }
636
637             if (!found)
638             {
639                 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
640                 len = strlenW(pszTargetName);
641                 entry->pszTargetName = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
642                 memcpy(entry->pszTargetName, pszTargetName, (len + 1)*sizeof(WCHAR));
643                 list_add_tail(&pending_credentials_list, &entry->entry);
644             }
645
646             len = strlenW(params.pszUsername);
647             entry->pszUsername = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
648             memcpy(entry->pszUsername, params.pszUsername, (len + 1)*sizeof(WCHAR));
649             len = strlenW(params.pszPassword);
650             entry->pszPassword = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
651             memcpy(entry->pszPassword, params.pszPassword, (len + 1)*sizeof(WCHAR));
652             entry->generic = dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS ? TRUE : FALSE;
653
654             LeaveCriticalSection(&csPendingCredentials);
655         }
656         else
657             result = save_credentials(pszTargetName, pszUsername, pszPassword,
658                                       dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS ? TRUE : FALSE);
659     }
660
661     return result;
662 }
663
664 /******************************************************************************
665  *           CredUIConfirmCredentialsW [CREDUI.@]
666  */
667 DWORD WINAPI CredUIConfirmCredentialsW(PCWSTR pszTargetName, BOOL bConfirm)
668 {
669     struct pending_credentials *entry;
670     DWORD result = ERROR_NOT_FOUND;
671
672     TRACE("(%s, %s)\n", debugstr_w(pszTargetName), bConfirm ? "TRUE" : "FALSE");
673
674     if (!pszTargetName)
675         return ERROR_INVALID_PARAMETER;
676
677     EnterCriticalSection(&csPendingCredentials);
678
679     LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
680     {
681         if (!strcmpW(pszTargetName, entry->pszTargetName))
682         {
683             if (bConfirm)
684                 result = save_credentials(entry->pszTargetName, entry->pszUsername,
685                                           entry->pszPassword, entry->generic);
686             else
687                 result = ERROR_SUCCESS;
688
689             list_remove(&entry->entry);
690
691             HeapFree(GetProcessHeap(), 0, entry->pszTargetName);
692             HeapFree(GetProcessHeap(), 0, entry->pszUsername);
693             ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
694             HeapFree(GetProcessHeap(), 0, entry->pszPassword);
695             HeapFree(GetProcessHeap(), 0, entry);
696
697             break;
698         }
699     }
700
701     LeaveCriticalSection(&csPendingCredentials);
702
703     return result;
704 }
705
706 /******************************************************************************
707  *           CredUIParseUserNameW [CREDUI.@]
708  */
709 DWORD WINAPI CredUIParseUserNameW(PCWSTR pszUserName, PWSTR pszUser,
710                                   ULONG ulMaxUserChars, PWSTR pszDomain,
711                                   ULONG ulMaxDomainChars)
712 {
713     PWSTR p;
714
715     TRACE("(%s, %p, %d, %p, %d)\n", debugstr_w(pszUserName), pszUser,
716           ulMaxUserChars, pszDomain, ulMaxDomainChars);
717
718     if (!pszUserName || !pszUser || !ulMaxUserChars || !pszDomain ||
719         !ulMaxDomainChars)
720         return ERROR_INVALID_PARAMETER;
721
722     /* FIXME: handle marshaled credentials */
723
724     p = strchrW(pszUserName, '\\');
725     if (p)
726     {
727         if (p - pszUserName > ulMaxDomainChars - 1)
728             return ERROR_INSUFFICIENT_BUFFER;
729         if (strlenW(p + 1) > ulMaxUserChars - 1)
730             return ERROR_INSUFFICIENT_BUFFER;
731         strcpyW(pszUser, p + 1);
732         memcpy(pszDomain, pszUserName, (p - pszUserName)*sizeof(WCHAR));
733         pszDomain[p - pszUserName] = '\0';
734
735         return ERROR_SUCCESS;
736     }
737
738     p = strrchrW(pszUserName, '@');
739     if (p)
740     {
741         if (p + 1 - pszUserName > ulMaxUserChars - 1)
742             return ERROR_INSUFFICIENT_BUFFER;
743         if (strlenW(p + 1) > ulMaxDomainChars - 1)
744             return ERROR_INSUFFICIENT_BUFFER;
745         strcpyW(pszDomain, p + 1);
746         memcpy(pszUser, pszUserName, (p - pszUserName)*sizeof(WCHAR));
747         pszUser[p - pszUserName] = '\0';
748
749         return ERROR_SUCCESS;
750     }
751
752     if (strlenW(pszUserName) > ulMaxUserChars - 1)
753         return ERROR_INSUFFICIENT_BUFFER;
754     strcpyW(pszUser, pszUserName);
755     pszDomain[0] = '\0';
756
757     return ERROR_SUCCESS;
758 }
759
760 /******************************************************************************
761  *           CredUIStoreSSOCredA [CREDUI.@]
762  */
763 DWORD WINAPI CredUIStoreSSOCredA(PCSTR pszRealm, PCSTR pszUsername,
764                                  PCSTR pszPassword, BOOL bPersist)
765 {
766     FIXME("(%s, %s, %p, %d)\n", debugstr_a(pszRealm), debugstr_a(pszUsername),
767           pszPassword, bPersist);
768     return ERROR_SUCCESS;
769 }
770
771 /******************************************************************************
772  *           CredUIStoreSSOCredW [CREDUI.@]
773  */
774 DWORD WINAPI CredUIStoreSSOCredW(PCWSTR pszRealm, PCWSTR pszUsername,
775                                  PCWSTR pszPassword, BOOL bPersist)
776 {
777     FIXME("(%s, %s, %p, %d)\n", debugstr_w(pszRealm), debugstr_w(pszUsername),
778           pszPassword, bPersist);
779     return ERROR_SUCCESS;
780 }
781
782 /******************************************************************************
783  *           CredUIReadSSOCredA [CREDUI.@]
784  */
785 DWORD WINAPI CredUIReadSSOCredA(PCSTR pszRealm, PSTR *ppszUsername)
786 {
787     FIXME("(%s, %p)\n", debugstr_a(pszRealm), ppszUsername);
788     if (ppszUsername)
789         *ppszUsername = NULL;
790     return ERROR_NOT_FOUND;
791 }
792
793 /******************************************************************************
794  *           CredUIReadSSOCredW [CREDUI.@]
795  */
796 DWORD WINAPI CredUIReadSSOCredW(PCWSTR pszRealm, PWSTR *ppszUsername)
797 {
798     FIXME("(%s, %p)\n", debugstr_w(pszRealm), ppszUsername);
799     if (ppszUsername)
800         *ppszUsername = NULL;
801     return ERROR_NOT_FOUND;
802 }
803
804 /******************************************************************************
805  * CredUIInitControls [CREDUI.@]
806  */
807 BOOL WINAPI CredUIInitControls(void)
808 {
809     FIXME("() stub\n");
810     return TRUE;
811 }