credui: Don't save credentials if CREDUI_FLAGS_DO_NOT_PERSIST is specified.
[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 /******************************************************************************
552  *           CredUIPromptForCredentialsW [CREDUI.@]
553  */
554 DWORD WINAPI CredUIPromptForCredentialsW(PCREDUI_INFOW pUIInfo,
555                                          PCWSTR pszTargetName,
556                                          PCtxtHandle Reserved,
557                                          DWORD dwAuthError,
558                                          PWSTR pszUsername,
559                                          ULONG ulUsernameMaxChars,
560                                          PWSTR pszPassword,
561                                          ULONG ulPasswordMaxChars, PBOOL pfSave,
562                                          DWORD dwFlags)
563 {
564     INT_PTR ret;
565     struct cred_dialog_params params;
566     DWORD result = ERROR_SUCCESS;
567
568     TRACE("(%p, %s, %p, %d, %s, %d, %p, %d, %p, 0x%08x)\n", pUIInfo,
569           debugstr_w(pszTargetName), Reserved, dwAuthError, debugstr_w(pszUsername),
570           ulUsernameMaxChars, pszPassword, ulPasswordMaxChars, pfSave, dwFlags);
571
572     if ((dwFlags & (CREDUI_FLAGS_ALWAYS_SHOW_UI|CREDUI_FLAGS_GENERIC_CREDENTIALS)) == CREDUI_FLAGS_ALWAYS_SHOW_UI)
573         return ERROR_INVALID_FLAGS;
574
575     if (!pszTargetName)
576         return ERROR_INVALID_PARAMETER;
577
578     if ((dwFlags & CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX) && !pfSave)
579         return ERROR_INVALID_PARAMETER;
580
581     params.pszTargetName = pszTargetName;
582     if (pUIInfo)
583     {
584         params.pszMessageText = pUIInfo->pszMessageText;
585         params.pszCaptionText = pUIInfo->pszCaptionText;
586         params.hbmBanner  = pUIInfo->hbmBanner;
587     }
588     else
589     {
590         params.pszMessageText = NULL;
591         params.pszCaptionText = NULL;
592         params.hbmBanner = NULL;
593     }
594     params.pszUsername = pszUsername;
595     params.ulUsernameMaxChars = ulUsernameMaxChars;
596     params.pszPassword = pszPassword;
597     params.ulPasswordMaxChars = ulPasswordMaxChars;
598     params.fSave = pfSave ? *pfSave : FALSE;
599     params.dwFlags = dwFlags;
600     params.hwndBalloonTip = NULL;
601     params.fBalloonTipActive = FALSE;
602
603     ret = DialogBoxParamW(hinstCredUI, MAKEINTRESOURCEW(IDD_CREDDIALOG),
604                           pUIInfo ? pUIInfo->hwndParent : NULL,
605                           CredDialogProc, (LPARAM)&params);
606     if (ret <= 0)
607         return GetLastError();
608
609     if (ret == IDCANCEL)
610     {
611         TRACE("dialog cancelled\n");
612         return ERROR_CANCELLED;
613     }
614
615     if (pfSave)
616         *pfSave = params.fSave;
617
618     if (params.fSave)
619     {
620         if (dwFlags & CREDUI_FLAGS_EXPECT_CONFIRMATION)
621         {
622             BOOL found = FALSE;
623             struct pending_credentials *entry;
624             int len;
625
626             EnterCriticalSection(&csPendingCredentials);
627
628             /* find existing pending credentials for the same target and overwrite */
629             /* FIXME: is this correct? */
630             LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
631                 if (!strcmpW(pszTargetName, entry->pszTargetName))
632                 {
633                     found = TRUE;
634                     HeapFree(GetProcessHeap(), 0, entry->pszUsername);
635                     ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
636                     HeapFree(GetProcessHeap(), 0, entry->pszPassword);
637                 }
638
639             if (!found)
640             {
641                 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
642                 len = strlenW(pszTargetName);
643                 entry->pszTargetName = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
644                 memcpy(entry->pszTargetName, pszTargetName, (len + 1)*sizeof(WCHAR));
645                 list_add_tail(&pending_credentials_list, &entry->entry);
646             }
647
648             len = strlenW(params.pszUsername);
649             entry->pszUsername = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
650             memcpy(entry->pszUsername, params.pszUsername, (len + 1)*sizeof(WCHAR));
651             len = strlenW(params.pszPassword);
652             entry->pszPassword = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
653             memcpy(entry->pszPassword, params.pszPassword, (len + 1)*sizeof(WCHAR));
654             entry->generic = (dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) != 0;
655
656             LeaveCriticalSection(&csPendingCredentials);
657         }
658         else if (!(dwFlags & CREDUI_FLAGS_DO_NOT_PERSIST))
659             result = save_credentials(pszTargetName, pszUsername, pszPassword,
660                                       (dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) != 0);
661     }
662
663     return result;
664 }
665
666 /******************************************************************************
667  *           CredUIConfirmCredentialsW [CREDUI.@]
668  */
669 DWORD WINAPI CredUIConfirmCredentialsW(PCWSTR pszTargetName, BOOL bConfirm)
670 {
671     struct pending_credentials *entry;
672     DWORD result = ERROR_NOT_FOUND;
673
674     TRACE("(%s, %s)\n", debugstr_w(pszTargetName), bConfirm ? "TRUE" : "FALSE");
675
676     if (!pszTargetName)
677         return ERROR_INVALID_PARAMETER;
678
679     EnterCriticalSection(&csPendingCredentials);
680
681     LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
682     {
683         if (!strcmpW(pszTargetName, entry->pszTargetName))
684         {
685             if (bConfirm)
686                 result = save_credentials(entry->pszTargetName, entry->pszUsername,
687                                           entry->pszPassword, entry->generic);
688             else
689                 result = ERROR_SUCCESS;
690
691             list_remove(&entry->entry);
692
693             HeapFree(GetProcessHeap(), 0, entry->pszTargetName);
694             HeapFree(GetProcessHeap(), 0, entry->pszUsername);
695             ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
696             HeapFree(GetProcessHeap(), 0, entry->pszPassword);
697             HeapFree(GetProcessHeap(), 0, entry);
698
699             break;
700         }
701     }
702
703     LeaveCriticalSection(&csPendingCredentials);
704
705     return result;
706 }
707
708 /******************************************************************************
709  *           CredUIParseUserNameW [CREDUI.@]
710  */
711 DWORD WINAPI CredUIParseUserNameW(PCWSTR pszUserName, PWSTR pszUser,
712                                   ULONG ulMaxUserChars, PWSTR pszDomain,
713                                   ULONG ulMaxDomainChars)
714 {
715     PWSTR p;
716
717     TRACE("(%s, %p, %d, %p, %d)\n", debugstr_w(pszUserName), pszUser,
718           ulMaxUserChars, pszDomain, ulMaxDomainChars);
719
720     if (!pszUserName || !pszUser || !ulMaxUserChars || !pszDomain ||
721         !ulMaxDomainChars)
722         return ERROR_INVALID_PARAMETER;
723
724     /* FIXME: handle marshaled credentials */
725
726     p = strchrW(pszUserName, '\\');
727     if (p)
728     {
729         if (p - pszUserName > ulMaxDomainChars - 1)
730             return ERROR_INSUFFICIENT_BUFFER;
731         if (strlenW(p + 1) > ulMaxUserChars - 1)
732             return ERROR_INSUFFICIENT_BUFFER;
733         strcpyW(pszUser, p + 1);
734         memcpy(pszDomain, pszUserName, (p - pszUserName)*sizeof(WCHAR));
735         pszDomain[p - pszUserName] = '\0';
736
737         return ERROR_SUCCESS;
738     }
739
740     p = strrchrW(pszUserName, '@');
741     if (p)
742     {
743         if (p + 1 - pszUserName > ulMaxUserChars - 1)
744             return ERROR_INSUFFICIENT_BUFFER;
745         if (strlenW(p + 1) > ulMaxDomainChars - 1)
746             return ERROR_INSUFFICIENT_BUFFER;
747         strcpyW(pszDomain, p + 1);
748         memcpy(pszUser, pszUserName, (p - pszUserName)*sizeof(WCHAR));
749         pszUser[p - pszUserName] = '\0';
750
751         return ERROR_SUCCESS;
752     }
753
754     if (strlenW(pszUserName) > ulMaxUserChars - 1)
755         return ERROR_INSUFFICIENT_BUFFER;
756     strcpyW(pszUser, pszUserName);
757     pszDomain[0] = '\0';
758
759     return ERROR_SUCCESS;
760 }
761
762 /******************************************************************************
763  *           CredUIStoreSSOCredA [CREDUI.@]
764  */
765 DWORD WINAPI CredUIStoreSSOCredA(PCSTR pszRealm, PCSTR pszUsername,
766                                  PCSTR pszPassword, BOOL bPersist)
767 {
768     FIXME("(%s, %s, %p, %d)\n", debugstr_a(pszRealm), debugstr_a(pszUsername),
769           pszPassword, bPersist);
770     return ERROR_SUCCESS;
771 }
772
773 /******************************************************************************
774  *           CredUIStoreSSOCredW [CREDUI.@]
775  */
776 DWORD WINAPI CredUIStoreSSOCredW(PCWSTR pszRealm, PCWSTR pszUsername,
777                                  PCWSTR pszPassword, BOOL bPersist)
778 {
779     FIXME("(%s, %s, %p, %d)\n", debugstr_w(pszRealm), debugstr_w(pszUsername),
780           pszPassword, bPersist);
781     return ERROR_SUCCESS;
782 }
783
784 /******************************************************************************
785  *           CredUIReadSSOCredA [CREDUI.@]
786  */
787 DWORD WINAPI CredUIReadSSOCredA(PCSTR pszRealm, PSTR *ppszUsername)
788 {
789     FIXME("(%s, %p)\n", debugstr_a(pszRealm), ppszUsername);
790     if (ppszUsername)
791         *ppszUsername = NULL;
792     return ERROR_NOT_FOUND;
793 }
794
795 /******************************************************************************
796  *           CredUIReadSSOCredW [CREDUI.@]
797  */
798 DWORD WINAPI CredUIReadSSOCredW(PCWSTR pszRealm, PWSTR *ppszUsername)
799 {
800     FIXME("(%s, %p)\n", debugstr_w(pszRealm), ppszUsername);
801     if (ppszUsername)
802         *ppszUsername = NULL;
803     return ERROR_NOT_FOUND;
804 }
805
806 /******************************************************************************
807  * CredUIInitControls [CREDUI.@]
808  */
809 BOOL WINAPI CredUIInitControls(void)
810 {
811     FIXME("() stub\n");
812     return TRUE;
813 }