riched20: Pass the run to the text drawing routine.
[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) != 0;
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_PERSIST ||
414         (params->dwFlags & CREDUI_FLAGS_DO_NOT_PERSIST &&
415          !(params->dwFlags & CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX)))
416         ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_HIDE);
417     else if (params->fSave)
418         CheckDlgButton(hwndDlg, IDC_SAVE, BST_CHECKED);
419
420     /* setup subclassing for Caps Lock detection */
421     SetWindowSubclass(hwndPassword, CredDialogPasswordSubclassProc, 1, (DWORD_PTR)params);
422
423     if (params->dwFlags & CREDUI_FLAGS_INCORRECT_PASSWORD)
424         CredDialogShowIncorrectPasswordBalloon(hwndDlg, params);
425     else if ((GetFocus() == hwndPassword) && CredDialogCapsLockOn())
426         CredDialogShowCapsLockBalloon(hwndDlg, params);
427
428     return FALSE;
429 }
430
431 static void CredDialogCommandOk(HWND hwndDlg, struct cred_dialog_params *params)
432 {
433     HWND hwndUsername = GetDlgItem(hwndDlg, IDC_USERNAME);
434     LPWSTR user;
435     INT len;
436     INT len2;
437
438     len = GetWindowTextLengthW(hwndUsername);
439     user = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
440     GetWindowTextW(hwndUsername, user, len + 1);
441
442     if (!user[0])
443     {
444         HeapFree(GetProcessHeap(), 0, user);
445         return;
446     }
447
448     if (!strchrW(user, '\\') && !strchrW(user, '@'))
449     {
450         ULONG len_target = strlenW(params->pszTargetName);
451         memcpy(params->pszUsername, params->pszTargetName,
452                min(len_target, params->ulUsernameMaxChars) * sizeof(WCHAR));
453         if (len_target + 1 < params->ulUsernameMaxChars)
454             params->pszUsername[len_target] = '\\';
455         if (len_target + 2 < params->ulUsernameMaxChars)
456             params->pszUsername[len_target + 1] = '\0';
457     }
458     else if (params->ulUsernameMaxChars > 0)
459         params->pszUsername[0] = '\0';
460
461     len2 = strlenW(params->pszUsername);
462     memcpy(params->pszUsername + len2, user, min(len, params->ulUsernameMaxChars - len2) * sizeof(WCHAR));
463     if (params->ulUsernameMaxChars)
464         params->pszUsername[len2 + min(len, params->ulUsernameMaxChars - len2 - 1)] = '\0';
465
466     HeapFree(GetProcessHeap(), 0, user);
467
468     GetDlgItemTextW(hwndDlg, IDC_PASSWORD, params->pszPassword,
469                     params->ulPasswordMaxChars);
470
471     params->fSave = IsDlgButtonChecked(hwndDlg, IDC_SAVE) == BST_CHECKED;
472
473     EndDialog(hwndDlg, IDOK);
474 }
475
476 static INT_PTR CALLBACK CredDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,
477                                        LPARAM lParam)
478 {
479     switch (uMsg)
480     {
481         case WM_INITDIALOG:
482         {
483             struct cred_dialog_params *params = (struct cred_dialog_params *)lParam;
484
485             return CredDialogInit(hwndDlg, params);
486         }
487         case WM_COMMAND:
488             switch (wParam)
489             {
490                 case MAKELONG(IDOK, BN_CLICKED):
491                 {
492                     struct cred_dialog_params *params =
493                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
494                     CredDialogCommandOk(hwndDlg, params);
495                     return TRUE;
496                 }
497                 case MAKELONG(IDCANCEL, BN_CLICKED):
498                     EndDialog(hwndDlg, IDCANCEL);
499                     return TRUE;
500                 case MAKELONG(IDC_PASSWORD, EN_SETFOCUS):
501                     if (CredDialogCapsLockOn())
502                     {
503                         struct cred_dialog_params *params =
504                             (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
505                         CredDialogShowCapsLockBalloon(hwndDlg, params);
506                     }
507                     /* don't allow another window to steal focus while the
508                      * user is typing their password */
509                     LockSetForegroundWindow(LSFW_LOCK);
510                     return TRUE;
511                 case MAKELONG(IDC_PASSWORD, EN_KILLFOCUS):
512                 {
513                     struct cred_dialog_params *params =
514                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
515                     /* the user is no longer typing their password, so allow
516                      * other windows to become foreground ones */
517                     LockSetForegroundWindow(LSFW_UNLOCK);
518                     CredDialogHideBalloonTip(hwndDlg, params);
519                     return TRUE;
520                 }
521                 case MAKELONG(IDC_PASSWORD, EN_CHANGE):
522                 {
523                     struct cred_dialog_params *params =
524                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
525                     CredDialogHideBalloonTip(hwndDlg, params);
526                     return TRUE;
527                 }
528             }
529             return FALSE;
530         case WM_TIMER:
531             if (wParam == ID_CAPSLOCKPOP)
532             {
533                 struct cred_dialog_params *params =
534                     (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
535                 CredDialogHideBalloonTip(hwndDlg, params);
536                 return TRUE;
537             }
538             return FALSE;
539         case WM_DESTROY:
540         {
541             struct cred_dialog_params *params =
542                 (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
543             if (params->hwndBalloonTip) DestroyWindow(params->hwndBalloonTip);
544             return TRUE;
545         }
546         default:
547             return FALSE;
548     }
549 }
550
551 static BOOL find_existing_credential(const WCHAR *target, WCHAR *username, ULONG len_username,
552                                      WCHAR *password, ULONG len_password)
553 {
554     DWORD count, i;
555     CREDENTIALW **credentials;
556
557     if (!CredEnumerateW(target, 0, &count, &credentials)) return FALSE;
558     for (i = 0; i < count; i++)
559     {
560         if (credentials[i]->Type != CRED_TYPE_DOMAIN_PASSWORD)
561         {
562             FIXME("no support for type %u credentials\n", credentials[i]->Type);
563             continue;
564         }
565         if ((!*username || !strcmpW(username, credentials[i]->UserName)) &&
566             strlenW(credentials[i]->UserName) < len_username &&
567             credentials[i]->CredentialBlobSize / sizeof(WCHAR) < len_password)
568         {
569             TRACE("found existing credential for %s\n", debugstr_w(credentials[i]->UserName));
570
571             strcpyW(username, credentials[i]->UserName);
572             memcpy(password, credentials[i]->CredentialBlob, credentials[i]->CredentialBlobSize);
573             password[credentials[i]->CredentialBlobSize / sizeof(WCHAR)] = 0;
574
575             CredFree(credentials);
576             return TRUE;
577         }
578     }
579     CredFree(credentials);
580     return FALSE;
581 }
582
583 /******************************************************************************
584  *           CredUIPromptForCredentialsW [CREDUI.@]
585  */
586 DWORD WINAPI CredUIPromptForCredentialsW(PCREDUI_INFOW pUIInfo,
587                                          PCWSTR pszTargetName,
588                                          PCtxtHandle Reserved,
589                                          DWORD dwAuthError,
590                                          PWSTR pszUsername,
591                                          ULONG ulUsernameMaxChars,
592                                          PWSTR pszPassword,
593                                          ULONG ulPasswordMaxChars, PBOOL pfSave,
594                                          DWORD dwFlags)
595 {
596     INT_PTR ret;
597     struct cred_dialog_params params;
598     DWORD result = ERROR_SUCCESS;
599
600     TRACE("(%p, %s, %p, %d, %s, %d, %p, %d, %p, 0x%08x)\n", pUIInfo,
601           debugstr_w(pszTargetName), Reserved, dwAuthError, debugstr_w(pszUsername),
602           ulUsernameMaxChars, pszPassword, ulPasswordMaxChars, pfSave, dwFlags);
603
604     if ((dwFlags & (CREDUI_FLAGS_ALWAYS_SHOW_UI|CREDUI_FLAGS_GENERIC_CREDENTIALS)) == CREDUI_FLAGS_ALWAYS_SHOW_UI)
605         return ERROR_INVALID_FLAGS;
606
607     if (!pszTargetName)
608         return ERROR_INVALID_PARAMETER;
609
610     if ((dwFlags & CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX) && !pfSave)
611         return ERROR_INVALID_PARAMETER;
612
613     if (!(dwFlags & CREDUI_FLAGS_ALWAYS_SHOW_UI) &&
614         !(dwFlags & CREDUI_FLAGS_INCORRECT_PASSWORD) &&
615         find_existing_credential(pszTargetName, pszUsername, ulUsernameMaxChars, pszPassword, ulPasswordMaxChars))
616         return ERROR_SUCCESS;
617
618     params.pszTargetName = pszTargetName;
619     if (pUIInfo)
620     {
621         params.pszMessageText = pUIInfo->pszMessageText;
622         params.pszCaptionText = pUIInfo->pszCaptionText;
623         params.hbmBanner  = pUIInfo->hbmBanner;
624     }
625     else
626     {
627         params.pszMessageText = NULL;
628         params.pszCaptionText = NULL;
629         params.hbmBanner = NULL;
630     }
631     params.pszUsername = pszUsername;
632     params.ulUsernameMaxChars = ulUsernameMaxChars;
633     params.pszPassword = pszPassword;
634     params.ulPasswordMaxChars = ulPasswordMaxChars;
635     params.fSave = pfSave ? *pfSave : FALSE;
636     params.dwFlags = dwFlags;
637     params.hwndBalloonTip = NULL;
638     params.fBalloonTipActive = FALSE;
639
640     ret = DialogBoxParamW(hinstCredUI, MAKEINTRESOURCEW(IDD_CREDDIALOG),
641                           pUIInfo ? pUIInfo->hwndParent : NULL,
642                           CredDialogProc, (LPARAM)&params);
643     if (ret <= 0)
644         return GetLastError();
645
646     if (ret == IDCANCEL)
647     {
648         TRACE("dialog cancelled\n");
649         return ERROR_CANCELLED;
650     }
651
652     if (pfSave)
653         *pfSave = params.fSave;
654
655     if (params.fSave)
656     {
657         if (dwFlags & CREDUI_FLAGS_EXPECT_CONFIRMATION)
658         {
659             BOOL found = FALSE;
660             struct pending_credentials *entry;
661             int len;
662
663             EnterCriticalSection(&csPendingCredentials);
664
665             /* find existing pending credentials for the same target and overwrite */
666             /* FIXME: is this correct? */
667             LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
668                 if (!strcmpW(pszTargetName, entry->pszTargetName))
669                 {
670                     found = TRUE;
671                     HeapFree(GetProcessHeap(), 0, entry->pszUsername);
672                     ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
673                     HeapFree(GetProcessHeap(), 0, entry->pszPassword);
674                 }
675
676             if (!found)
677             {
678                 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
679                 len = strlenW(pszTargetName);
680                 entry->pszTargetName = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
681                 memcpy(entry->pszTargetName, pszTargetName, (len + 1)*sizeof(WCHAR));
682                 list_add_tail(&pending_credentials_list, &entry->entry);
683             }
684
685             len = strlenW(params.pszUsername);
686             entry->pszUsername = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
687             memcpy(entry->pszUsername, params.pszUsername, (len + 1)*sizeof(WCHAR));
688             len = strlenW(params.pszPassword);
689             entry->pszPassword = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
690             memcpy(entry->pszPassword, params.pszPassword, (len + 1)*sizeof(WCHAR));
691             entry->generic = (dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) != 0;
692
693             LeaveCriticalSection(&csPendingCredentials);
694         }
695         else if (!(dwFlags & CREDUI_FLAGS_DO_NOT_PERSIST))
696             result = save_credentials(pszTargetName, pszUsername, pszPassword,
697                                       (dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) != 0);
698     }
699
700     return result;
701 }
702
703 /******************************************************************************
704  *           CredUIConfirmCredentialsW [CREDUI.@]
705  */
706 DWORD WINAPI CredUIConfirmCredentialsW(PCWSTR pszTargetName, BOOL bConfirm)
707 {
708     struct pending_credentials *entry;
709     DWORD result = ERROR_NOT_FOUND;
710
711     TRACE("(%s, %s)\n", debugstr_w(pszTargetName), bConfirm ? "TRUE" : "FALSE");
712
713     if (!pszTargetName)
714         return ERROR_INVALID_PARAMETER;
715
716     EnterCriticalSection(&csPendingCredentials);
717
718     LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
719     {
720         if (!strcmpW(pszTargetName, entry->pszTargetName))
721         {
722             if (bConfirm)
723                 result = save_credentials(entry->pszTargetName, entry->pszUsername,
724                                           entry->pszPassword, entry->generic);
725             else
726                 result = ERROR_SUCCESS;
727
728             list_remove(&entry->entry);
729
730             HeapFree(GetProcessHeap(), 0, entry->pszTargetName);
731             HeapFree(GetProcessHeap(), 0, entry->pszUsername);
732             ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
733             HeapFree(GetProcessHeap(), 0, entry->pszPassword);
734             HeapFree(GetProcessHeap(), 0, entry);
735
736             break;
737         }
738     }
739
740     LeaveCriticalSection(&csPendingCredentials);
741
742     return result;
743 }
744
745 /******************************************************************************
746  *           CredUIParseUserNameW [CREDUI.@]
747  */
748 DWORD WINAPI CredUIParseUserNameW(PCWSTR pszUserName, PWSTR pszUser,
749                                   ULONG ulMaxUserChars, PWSTR pszDomain,
750                                   ULONG ulMaxDomainChars)
751 {
752     PWSTR p;
753
754     TRACE("(%s, %p, %d, %p, %d)\n", debugstr_w(pszUserName), pszUser,
755           ulMaxUserChars, pszDomain, ulMaxDomainChars);
756
757     if (!pszUserName || !pszUser || !ulMaxUserChars || !pszDomain ||
758         !ulMaxDomainChars)
759         return ERROR_INVALID_PARAMETER;
760
761     /* FIXME: handle marshaled credentials */
762
763     p = strchrW(pszUserName, '\\');
764     if (p)
765     {
766         if (p - pszUserName > ulMaxDomainChars - 1)
767             return ERROR_INSUFFICIENT_BUFFER;
768         if (strlenW(p + 1) > ulMaxUserChars - 1)
769             return ERROR_INSUFFICIENT_BUFFER;
770         strcpyW(pszUser, p + 1);
771         memcpy(pszDomain, pszUserName, (p - pszUserName)*sizeof(WCHAR));
772         pszDomain[p - pszUserName] = '\0';
773
774         return ERROR_SUCCESS;
775     }
776
777     p = strrchrW(pszUserName, '@');
778     if (p)
779     {
780         if (p + 1 - pszUserName > ulMaxUserChars - 1)
781             return ERROR_INSUFFICIENT_BUFFER;
782         if (strlenW(p + 1) > ulMaxDomainChars - 1)
783             return ERROR_INSUFFICIENT_BUFFER;
784         strcpyW(pszDomain, p + 1);
785         memcpy(pszUser, pszUserName, (p - pszUserName)*sizeof(WCHAR));
786         pszUser[p - pszUserName] = '\0';
787
788         return ERROR_SUCCESS;
789     }
790
791     if (strlenW(pszUserName) > ulMaxUserChars - 1)
792         return ERROR_INSUFFICIENT_BUFFER;
793     strcpyW(pszUser, pszUserName);
794     pszDomain[0] = '\0';
795
796     return ERROR_SUCCESS;
797 }
798
799 /******************************************************************************
800  *           CredUIStoreSSOCredA [CREDUI.@]
801  */
802 DWORD WINAPI CredUIStoreSSOCredA(PCSTR pszRealm, PCSTR pszUsername,
803                                  PCSTR pszPassword, BOOL bPersist)
804 {
805     FIXME("(%s, %s, %p, %d)\n", debugstr_a(pszRealm), debugstr_a(pszUsername),
806           pszPassword, bPersist);
807     return ERROR_SUCCESS;
808 }
809
810 /******************************************************************************
811  *           CredUIStoreSSOCredW [CREDUI.@]
812  */
813 DWORD WINAPI CredUIStoreSSOCredW(PCWSTR pszRealm, PCWSTR pszUsername,
814                                  PCWSTR pszPassword, BOOL bPersist)
815 {
816     FIXME("(%s, %s, %p, %d)\n", debugstr_w(pszRealm), debugstr_w(pszUsername),
817           pszPassword, bPersist);
818     return ERROR_SUCCESS;
819 }
820
821 /******************************************************************************
822  *           CredUIReadSSOCredA [CREDUI.@]
823  */
824 DWORD WINAPI CredUIReadSSOCredA(PCSTR pszRealm, PSTR *ppszUsername)
825 {
826     FIXME("(%s, %p)\n", debugstr_a(pszRealm), ppszUsername);
827     if (ppszUsername)
828         *ppszUsername = NULL;
829     return ERROR_NOT_FOUND;
830 }
831
832 /******************************************************************************
833  *           CredUIReadSSOCredW [CREDUI.@]
834  */
835 DWORD WINAPI CredUIReadSSOCredW(PCWSTR pszRealm, PWSTR *ppszUsername)
836 {
837     FIXME("(%s, %p)\n", debugstr_w(pszRealm), ppszUsername);
838     if (ppszUsername)
839         *ppszUsername = NULL;
840     return ERROR_NOT_FOUND;
841 }
842
843 /******************************************************************************
844  * CredUIInitControls [CREDUI.@]
845  */
846 BOOL WINAPI CredUIInitControls(void)
847 {
848     FIXME("() stub\n");
849     return TRUE;
850 }