cryptui: Show selected certificate when requested in the certificate manager dialog.
[wine] / dlls / cryptui / main.c
1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winuser.h"
30 #include "softpub.h"
31 #include "wingdi.h"
32 #include "richedit.h"
33 #include "ole2.h"
34 #include "richole.h"
35 #include "commdlg.h"
36 #include "commctrl.h"
37 #include "cryptuiapi.h"
38 #include "cryptuires.h"
39 #include "urlmon.h"
40 #include "hlink.h"
41 #include "winreg.h"
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(cryptui);
46
47 static HINSTANCE hInstance;
48
49 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
50 {
51     TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
52
53     switch (fdwReason)
54     {
55         case DLL_WINE_PREATTACH:
56             return FALSE;    /* prefer native version */
57         case DLL_PROCESS_ATTACH:
58             hInstance = hinstDLL;
59             DisableThreadLibraryCalls(hinstDLL);
60             break;
61         case DLL_PROCESS_DETACH:
62             break;
63         default:
64             break;
65     }
66     return TRUE;
67 }
68
69 #define MAX_STRING_LEN 512
70
71 static void add_cert_columns(HWND hwnd)
72 {
73     HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
74     RECT rc;
75     WCHAR buf[MAX_STRING_LEN];
76     LVCOLUMNW column;
77
78     SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
79     GetWindowRect(lv, &rc);
80     LoadStringW(hInstance, IDS_SUBJECT_COLUMN, buf,
81      sizeof(buf) / sizeof(buf[0]));
82     column.mask = LVCF_WIDTH | LVCF_TEXT;
83     column.cx = (rc.right - rc.left) * 29 / 100 - 2;
84     column.pszText = buf;
85     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
86     LoadStringW(hInstance, IDS_ISSUER_COLUMN, buf,
87      sizeof(buf) / sizeof(buf[0]));
88     SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
89     column.cx = (rc.right - rc.left) * 16 / 100 - 2;
90     LoadStringW(hInstance, IDS_EXPIRATION_COLUMN, buf,
91      sizeof(buf) / sizeof(buf[0]));
92     SendMessageW(lv, LVM_INSERTCOLUMNW, 2, (LPARAM)&column);
93     column.cx = (rc.right - rc.left) * 23 / 100 - 1;
94     LoadStringW(hInstance, IDS_FRIENDLY_NAME_COLUMN, buf,
95      sizeof(buf) / sizeof(buf[0]));
96     SendMessageW(lv, LVM_INSERTCOLUMNW, 3, (LPARAM)&column);
97 }
98
99 static void add_cert_to_view(HWND lv, PCCERT_CONTEXT cert, DWORD *allocatedLen,
100  LPWSTR *str)
101 {
102     DWORD len;
103     LVITEMW item;
104     WCHAR dateFmt[80]; /* sufficient for LOCALE_SSHORTDATE */
105     WCHAR date[80];
106     SYSTEMTIME sysTime;
107
108     item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
109     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
110     item.iSubItem = 0;
111     item.iImage = 0;
112     item.lParam = (LPARAM)CertDuplicateCertificateContext(cert);
113     len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL,
114      NULL, 0);
115     if (len > *allocatedLen)
116     {
117         HeapFree(GetProcessHeap(), 0, *str);
118         *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
119         if (*str)
120             *allocatedLen = len;
121     }
122     if (*str)
123     {
124         CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL,
125          *str, len);
126         item.pszText = *str;
127         SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
128     }
129
130     item.mask = LVIF_TEXT;
131     len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
132      CERT_NAME_ISSUER_FLAG, NULL, NULL, 0);
133     if (len > *allocatedLen)
134     {
135         HeapFree(GetProcessHeap(), 0, *str);
136         *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
137         if (*str)
138             *allocatedLen = len;
139     }
140     if (*str)
141     {
142         CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
143          CERT_NAME_ISSUER_FLAG, NULL, *str, len);
144         item.pszText = *str;
145         item.iSubItem = 1;
146         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
147     }
148
149     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt,
150      sizeof(dateFmt) / sizeof(dateFmt[0]));
151     FileTimeToSystemTime(&cert->pCertInfo->NotAfter, &sysTime);
152     GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date,
153      sizeof(date) / sizeof(date[0]));
154     item.pszText = date;
155     item.iSubItem = 2;
156     SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
157
158     len = CertGetNameStringW(cert, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL,
159      NULL, 0);
160     if (len > *allocatedLen)
161     {
162         HeapFree(GetProcessHeap(), 0, *str);
163         *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
164         if (*str)
165             *allocatedLen = len;
166     }
167     if (*str)
168     {
169         CertGetNameStringW(cert, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL,
170          *str, len);
171         item.pszText = *str;
172         item.iSubItem = 3;
173         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
174     }
175 }
176
177 static LPSTR get_cert_mgr_usages(void)
178 {
179     static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M',
180      'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a',
181      'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u',
182      'r','p','o','s','e',0 };
183     LPSTR str = NULL;
184     HKEY key;
185
186     if (!RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_READ,
187      NULL, &key, NULL))
188     {
189         LONG rc;
190         DWORD type, size;
191
192         rc = RegQueryValueExA(key, "Purpose", NULL, &type, NULL, &size);
193         if ((!rc || rc == ERROR_MORE_DATA) && type == REG_SZ)
194         {
195             str = HeapAlloc(GetProcessHeap(), 0, size);
196             if (str)
197             {
198                 rc = RegQueryValueExA(key, "Purpose", NULL, NULL, (LPBYTE)str,
199                  &size);
200                 if (rc)
201                 {
202                     HeapFree(GetProcessHeap(), 0, str);
203                     str = NULL;
204                 }
205             }
206         }
207         RegCloseKey(key);
208     }
209     return str;
210 }
211
212 typedef enum {
213     PurposeFilterShowAll = 0,
214     PurposeFilterShowAdvanced = 1,
215     PurposeFilterShowOID = 2
216 } PurposeFilter;
217
218 static void initialize_purpose_selection(HWND hwnd)
219 {
220     HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION);
221     WCHAR buf[MAX_STRING_LEN];
222     LPSTR usages;
223     int index;
224
225     LoadStringW(hInstance, IDS_PURPOSE_ALL, buf,
226      sizeof(buf) / sizeof(buf[0]));
227     index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
228     SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)PurposeFilterShowAll);
229     LoadStringW(hInstance, IDS_PURPOSE_ADVANCED, buf,
230      sizeof(buf) / sizeof(buf[0]));
231     index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
232     SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)PurposeFilterShowAdvanced);
233     SendMessageW(cb, CB_SETCURSEL, 0, 0);
234     if ((usages = get_cert_mgr_usages()))
235     {
236         LPSTR ptr, comma;
237
238         for (ptr = usages, comma = strchr(ptr, ','); ptr && *ptr;
239          ptr = comma ? comma + 1 : NULL,
240          comma = ptr ? strchr(ptr, ',') : NULL)
241         {
242             PCCRYPT_OID_INFO info;
243
244             if (comma)
245                 *comma = 0;
246             if ((info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, ptr, 0)))
247             {
248                 index = SendMessageW(cb, CB_INSERTSTRING, 0,
249                  (LPARAM)info->pwszName);
250                 SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)info);
251             }
252         }
253         HeapFree(GetProcessHeap(), 0, usages);
254     }
255 }
256
257 extern BOOL WINAPI WTHelperGetKnownUsages(DWORD action,
258  PCCRYPT_OID_INFO **usages);
259
260 static CERT_ENHKEY_USAGE *add_oid_to_usage(CERT_ENHKEY_USAGE *usage, LPSTR oid)
261 {
262     if (!usage->cUsageIdentifier)
263         usage->rgpszUsageIdentifier = HeapAlloc(GetProcessHeap(), 0,
264          sizeof(LPSTR));
265     else
266         usage->rgpszUsageIdentifier = HeapReAlloc(GetProcessHeap(), 0,
267          usage->rgpszUsageIdentifier,
268          (usage->cUsageIdentifier + 1) * sizeof(LPSTR));
269     if (usage->rgpszUsageIdentifier)
270         usage->rgpszUsageIdentifier[usage->cUsageIdentifier++] = oid;
271     else
272     {
273         HeapFree(GetProcessHeap(), 0, usage);
274         usage = NULL;
275     }
276     return usage;
277 }
278
279 static CERT_ENHKEY_USAGE *convert_usages_str_to_usage(LPSTR usageStr)
280 {
281     CERT_ENHKEY_USAGE *usage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
282      sizeof(CERT_ENHKEY_USAGE));
283
284     if (usage)
285     {
286         LPSTR ptr, comma;
287
288         for (ptr = usageStr, comma = strchr(ptr, ','); usage && ptr && *ptr;
289          ptr = comma ? comma + 1 : NULL,
290          comma = ptr ? strchr(ptr, ',') : NULL)
291         {
292             if (comma)
293                 *comma = 0;
294             add_oid_to_usage(usage, ptr);
295         }
296     }
297     return usage;
298 }
299
300 static CERT_ENHKEY_USAGE *create_advanced_filter(void)
301 {
302     CERT_ENHKEY_USAGE *advancedUsage = HeapAlloc(GetProcessHeap(),
303      HEAP_ZERO_MEMORY, sizeof(CERT_ENHKEY_USAGE));
304
305     if (advancedUsage)
306     {
307         PCCRYPT_OID_INFO *usages;
308
309         if (WTHelperGetKnownUsages(1, &usages))
310         {
311             LPSTR disabledUsagesStr;
312
313             if ((disabledUsagesStr = get_cert_mgr_usages()))
314             {
315                 CERT_ENHKEY_USAGE *disabledUsages =
316                  convert_usages_str_to_usage(disabledUsagesStr);
317
318                 if (disabledUsages)
319                 {
320                     PCCRYPT_OID_INFO *ptr;
321
322                     for (ptr = usages; *ptr; ptr++)
323                     {
324                         DWORD i;
325                         BOOL disabled = FALSE;
326
327                         for (i = 0; !disabled &&
328                          i < disabledUsages->cUsageIdentifier; i++)
329                             if (!strcmp(disabledUsages->rgpszUsageIdentifier[i],
330                              (*ptr)->pszOID))
331                                 disabled = TRUE;
332                         if (!disabled)
333                             add_oid_to_usage(advancedUsage,
334                              (LPSTR)(*ptr)->pszOID);
335                     }
336                     /* The individual strings are pointers to disabledUsagesStr,
337                      * so they're freed when it is.
338                      */
339                     HeapFree(GetProcessHeap(), 0,
340                      disabledUsages->rgpszUsageIdentifier);
341                     HeapFree(GetProcessHeap(), 0, disabledUsages);
342                 }
343                 HeapFree(GetProcessHeap(), 0, disabledUsagesStr);
344             }
345             WTHelperGetKnownUsages(2, &usages);
346         }
347     }
348     return advancedUsage;
349 }
350
351 static void show_store_certs(HWND hwnd, HCERTSTORE store)
352 {
353     HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
354     HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION);
355     PCCERT_CONTEXT cert = NULL;
356     DWORD allocatedLen = 0;
357     LPWSTR str = NULL;
358     int index;
359     PurposeFilter filter = PurposeFilterShowAll;
360     LPCSTR oid = NULL;
361     CERT_ENHKEY_USAGE *advanced = NULL;
362
363     index = SendMessageW(cb, CB_GETCURSEL, 0, 0);
364     if (index >= 0)
365     {
366         INT_PTR data = SendMessageW(cb, CB_GETITEMDATA, index, 0);
367
368         if (!HIWORD(data))
369             filter = data;
370         else
371         {
372             PCCRYPT_OID_INFO info = (PCCRYPT_OID_INFO)data;
373
374             filter = PurposeFilterShowOID;
375             oid = info->pszOID;
376         }
377     }
378     if (filter == PurposeFilterShowAdvanced)
379         advanced = create_advanced_filter();
380     do {
381         cert = CertEnumCertificatesInStore(store, cert);
382         if (cert)
383         {
384             BOOL show = FALSE;
385
386             if (filter == PurposeFilterShowAll)
387                 show = TRUE;
388             else
389             {
390                 int numOIDs;
391                 DWORD cbOIDs = 0;
392
393                 if (CertGetValidUsages(1, &cert, &numOIDs, NULL, &cbOIDs))
394                 {
395                     if (numOIDs == -1)
396                     {
397                         /* -1 implies all usages are valid */
398                         show = TRUE;
399                     }
400                     else
401                     {
402                         LPSTR *oids = HeapAlloc(GetProcessHeap(), 0, cbOIDs);
403
404                         if (oids)
405                         {
406                             if (CertGetValidUsages(1, &cert, &numOIDs, oids,
407                              &cbOIDs))
408                             {
409                                 int i;
410
411                                 if (filter == PurposeFilterShowOID)
412                                 {
413                                     for (i = 0; !show && i < numOIDs; i++)
414                                         if (!strcmp(oids[i], oid))
415                                             show = TRUE;
416                                 }
417                                 else
418                                 {
419                                     for (i = 0; !show && i < numOIDs; i++)
420                                     {
421                                         DWORD j;
422
423                                         for (j = 0; !show &&
424                                          j < advanced->cUsageIdentifier; j++)
425                                             if (!strcmp(oids[i],
426                                              advanced->rgpszUsageIdentifier[j]))
427                                                 show = TRUE;
428                                     }
429                                 }
430                             }
431                             HeapFree(GetProcessHeap(), 0, oids);
432                         }
433                     }
434                 }
435             }
436             if (show)
437                 add_cert_to_view(lv, cert, &allocatedLen, &str);
438         }
439     } while (cert);
440     HeapFree(GetProcessHeap(), 0, str);
441     if (advanced)
442     {
443         HeapFree(GetProcessHeap(), 0, advanced->rgpszUsageIdentifier);
444         HeapFree(GetProcessHeap(), 0, advanced);
445     }
446 }
447
448 static const WCHAR my[] = { 'M','y',0 };
449 static const WCHAR addressBook[] = {
450  'A','d','d','r','e','s','s','B','o','o','k',0 };
451 static const WCHAR ca[] = { 'C','A',0 };
452 static const WCHAR root[] = { 'R','o','o','t',0 };
453 static const WCHAR trustedPublisher[] = {
454  'T','r','u','s','t','e','d','P','u','b','l','i','s','h','e','r',0 };
455 static const WCHAR disallowed[] = { 'D','i','s','a','l','l','o','w','e','d',0 };
456
457 static LPCWSTR defaultStoreList[] = {
458  my, addressBook, ca, root, trustedPublisher, disallowed };
459 static LPCWSTR publisherStoreList[] = { root, trustedPublisher, disallowed };
460
461 static void show_cert_stores(HWND hwnd, DWORD dwFlags)
462 {
463     LPCWSTR *storeList;
464     int cStores, i;
465     HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES);
466
467     if (dwFlags & CRYPTUI_CERT_MGR_PUBLISHER_TAB)
468     {
469         storeList = publisherStoreList;
470         cStores = sizeof(publisherStoreList) / sizeof(publisherStoreList[0]);
471     }
472     else
473     {
474         storeList = defaultStoreList;
475         cStores = sizeof(defaultStoreList) / sizeof(defaultStoreList[0]);
476     }
477     if (dwFlags & CRYPTUI_CERT_MGR_SINGLE_TAB_FLAG)
478         cStores = 1;
479     for (i = 0; i < cStores; i++)
480     {
481         LPCWSTR name;
482         TCITEMW item;
483         HCERTSTORE store;
484
485         if (!(name = CryptFindLocalizedName(storeList[i])))
486             name = storeList[i];
487         store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
488          CERT_SYSTEM_STORE_CURRENT_USER, storeList[i]);
489         item.mask = TCIF_TEXT | TCIF_PARAM;
490         item.pszText = (LPWSTR)name;
491         item.lParam = (LPARAM)store;
492         SendMessageW(tab, TCM_INSERTITEMW, i, (LPARAM)&item);
493     }
494 }
495
496 static void free_certs(HWND lv)
497 {
498     LVITEMW item;
499     int items = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
500
501     for (i = 0; i < items; i++)
502     {
503         item.mask = LVIF_PARAM;
504         item.iItem = i;
505         item.iSubItem = 0;
506         SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
507         CertFreeCertificateContext((PCCERT_CONTEXT)item.lParam);
508     }
509 }
510
511 static HCERTSTORE cert_mgr_index_to_store(HWND tab, int index)
512 {
513     TCITEMW item;
514
515     item.mask = TCIF_PARAM;
516     SendMessageW(tab, TCM_GETITEMW, index, (LPARAM)&item);
517     return (HCERTSTORE)item.lParam;
518 }
519
520 static HCERTSTORE cert_mgr_current_store(HWND hwnd)
521 {
522     HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES);
523
524     return cert_mgr_index_to_store(tab, SendMessageW(tab, TCM_GETCURSEL, 0, 0));
525 }
526
527 static void close_stores(HWND tab)
528 {
529     int i, tabs = SendMessageW(tab, TCM_GETITEMCOUNT, 0, 0);
530
531     for (i = 0; i < tabs; i++)
532         CertCloseStore(cert_mgr_index_to_store(tab, i), 0);
533 }
534
535 static void refresh_store_certs(HWND hwnd)
536 {
537     HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
538
539     free_certs(lv);
540     SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0);
541     show_store_certs(hwnd, cert_mgr_current_store(hwnd));
542 }
543
544 typedef enum {
545     CheckBitmapIndexUnchecked = 1,
546     CheckBitmapIndexChecked = 2,
547     CheckBitmapIndexDisabledUnchecked = 3,
548     CheckBitmapIndexDisabledChecked = 4
549 } CheckBitmapIndex;
550
551 static void add_known_usage(HWND lv, PCCRYPT_OID_INFO info,
552  CheckBitmapIndex state)
553 {
554     LVITEMW item;
555
556     item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
557     item.state = INDEXTOSTATEIMAGEMASK(state);
558     item.stateMask = LVIS_STATEIMAGEMASK;
559     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
560     item.iSubItem = 0;
561     item.lParam = (LPARAM)info;
562     item.pszText = (LPWSTR)info->pwszName;
563     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
564 }
565
566 static void add_known_usages_to_list(HWND lv, CheckBitmapIndex state)
567 {
568     PCCRYPT_OID_INFO *usages;
569
570     if (WTHelperGetKnownUsages(1, &usages))
571     {
572         PCCRYPT_OID_INFO *ptr;
573
574         for (ptr = usages; *ptr; ptr++)
575             add_known_usage(lv, *ptr, state);
576         WTHelperGetKnownUsages(2, &usages);
577     }
578 }
579
580 static void toggle_usage(HWND hwnd, int iItem)
581 {
582     LVITEMW item;
583     int res;
584     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
585
586     item.mask = LVIF_STATE;
587     item.iItem = iItem;
588     item.iSubItem = 0;
589     item.stateMask = LVIS_STATEIMAGEMASK;
590     res = SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
591     if (res)
592     {
593         int state = item.state >> 12;
594
595         item.state = INDEXTOSTATEIMAGEMASK(
596          state == CheckBitmapIndexChecked ? CheckBitmapIndexUnchecked :
597          CheckBitmapIndexChecked);
598         SendMessageW(lv, LVM_SETITEMSTATE, iItem, (LPARAM)&item);
599     }
600 }
601
602 static LONG_PTR find_oid_in_list(HWND lv, LPCSTR oid)
603 {
604     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
605      (void *)oid, CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
606     LONG_PTR ret;
607
608     if (oidInfo)
609     {
610         LVFINDINFOW findInfo;
611
612         findInfo.flags = LVFI_PARAM;
613         findInfo.lParam = (LPARAM)oidInfo;
614         ret = SendMessageW(lv, LVM_FINDITEMW, -1, (LPARAM)&findInfo);
615     }
616     else
617     {
618         LVFINDINFOA findInfo;
619
620         findInfo.flags = LVFI_STRING;
621         findInfo.psz = oid;
622         ret = SendMessageW(lv, LVM_FINDITEMA, -1, (LPARAM)&findInfo);
623     }
624     return ret;
625 }
626
627 static void save_cert_mgr_usages(HWND hwnd)
628 {
629     static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M',
630      'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a',
631      'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u',
632      'r','p','o','s','e',0 };
633     HKEY key;
634     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
635     int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
636     LVITEMW item;
637     LPSTR str = NULL;
638
639     item.mask = LVIF_STATE | LVIF_PARAM;
640     item.iSubItem = 0;
641     item.stateMask = LVIS_STATEIMAGEMASK;
642     for (i = 0; i < purposes; i++)
643     {
644         item.iItem = i;
645         if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
646         {
647             int state = item.state >> 12;
648
649             if (state == CheckBitmapIndexUnchecked)
650             {
651                 CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
652                 BOOL firstString = TRUE;
653
654                 if (!str)
655                     str = HeapAlloc(GetProcessHeap(), 0,
656                      strlen(info->pszOID) + 1);
657                 else
658                 {
659                     str = HeapReAlloc(GetProcessHeap(), 0, str,
660                      strlen(str) + 1 + strlen(info->pszOID) + 1);
661                     firstString = FALSE;
662                 }
663                 if (str)
664                 {
665                     LPSTR ptr = firstString ? str : str + strlen(str);
666
667                     if (!firstString)
668                         *ptr++ = ',';
669                     strcpy(ptr, info->pszOID);
670                 }
671             }
672         }
673     }
674     if (!RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_ALL_ACCESS,
675      NULL, &key, NULL))
676     {
677         if (str)
678             RegSetValueExA(key, "Purpose", 0, REG_SZ, (const BYTE *)str,
679              strlen(str) + 1);
680         else
681             RegDeleteValueA(key, "Purpose");
682         RegCloseKey(key);
683     }
684     HeapFree(GetProcessHeap(), 0, str);
685 }
686
687 static LRESULT CALLBACK cert_mgr_advanced_dlg_proc(HWND hwnd, UINT msg,
688  WPARAM wp, LPARAM lp)
689 {
690     switch (msg)
691     {
692     case WM_INITDIALOG:
693     {
694         RECT rc;
695         LVCOLUMNW column;
696         HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
697         HIMAGELIST imageList;
698         LPSTR disabledUsages;
699
700         GetWindowRect(lv, &rc);
701         column.mask = LVCF_WIDTH;
702         column.cx = rc.right - rc.left;
703         SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
704         imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 4, 0);
705         if (imageList)
706         {
707             HBITMAP bmp;
708             COLORREF backColor = RGB(255, 0, 255);
709
710             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS));
711             ImageList_AddMasked(imageList, bmp, backColor);
712             DeleteObject(bmp);
713             ImageList_SetBkColor(imageList, CLR_NONE);
714             SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)imageList);
715             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)imageList);
716         }
717         add_known_usages_to_list(lv, CheckBitmapIndexChecked);
718         if ((disabledUsages = get_cert_mgr_usages()))
719         {
720             LPSTR ptr, comma;
721
722             for (ptr = disabledUsages, comma = strchr(ptr, ','); ptr && *ptr;
723              ptr = comma ? comma + 1 : NULL,
724              comma = ptr ? strchr(ptr, ',') : NULL)
725             {
726                 LONG_PTR index;
727
728                 if (comma)
729                     *comma = 0;
730                 if ((index = find_oid_in_list(lv, ptr)) != -1)
731                     toggle_usage(hwnd, index);
732             }
733             HeapFree(GetProcessHeap(), 0, disabledUsages);
734         }
735         break;
736     }
737     case WM_NOTIFY:
738     {
739         NMHDR *hdr = (NMHDR *)lp;
740         NMITEMACTIVATE *nm;
741
742         switch (hdr->code)
743         {
744         case NM_CLICK:
745             nm = (NMITEMACTIVATE *)lp;
746             toggle_usage(hwnd, nm->iItem);
747             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
748             break;
749         }
750         break;
751     }
752     case WM_COMMAND:
753         switch (wp)
754         {
755         case IDOK:
756             save_cert_mgr_usages(hwnd);
757             ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER));
758             EndDialog(hwnd, IDOK);
759             break;
760         case IDCANCEL:
761             ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER));
762             EndDialog(hwnd, IDCANCEL);
763             break;
764         }
765         break;
766     }
767     return 0;
768 }
769
770 static void cert_mgr_clear_cert_selection(HWND hwnd)
771 {
772     WCHAR empty[] = { 0 };
773
774     EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), FALSE);
775     EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), FALSE);
776     EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), FALSE);
777     SendMessageW(GetDlgItem(hwnd, IDC_MGR_PURPOSES), WM_SETTEXT, 0,
778      (LPARAM)empty);
779     refresh_store_certs(hwnd);
780 }
781
782 static void show_selected_cert(HWND hwnd, int index)
783 {
784     LVITEMW item;
785     HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
786
787     item.mask = LVIF_PARAM;
788     item.iItem = index;
789     item.iSubItem = 0;
790     if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
791     {
792         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
793
794         memset(&viewInfo, 0, sizeof(viewInfo));
795         viewInfo.dwSize = sizeof(viewInfo);
796         viewInfo.hwndParent = hwnd;
797         viewInfo.pCertContext = (PCCERT_CONTEXT)item.lParam;
798         /* FIXME: this should be modal */
799         CryptUIDlgViewCertificateW(&viewInfo, NULL);
800     }
801 }
802
803 static LRESULT CALLBACK cert_mgr_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
804  LPARAM lp)
805 {
806     switch (msg)
807     {
808     case WM_INITDIALOG:
809     {
810         PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr =
811          (PCCRYPTUI_CERT_MGR_STRUCT)lp;
812         HIMAGELIST imageList;
813         HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES);
814
815         imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0);
816         if (imageList)
817         {
818             HBITMAP bmp;
819             COLORREF backColor = RGB(255, 0, 255);
820
821             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS));
822             ImageList_AddMasked(imageList, bmp, backColor);
823             DeleteObject(bmp);
824             ImageList_SetBkColor(imageList, CLR_NONE);
825             SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_SETIMAGELIST,
826              LVSIL_SMALL, (LPARAM)imageList);
827             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)imageList);
828         }
829         initialize_purpose_selection(hwnd);
830         add_cert_columns(hwnd);
831         if (pCryptUICertMgr->pwszTitle)
832             SendMessageW(hwnd, WM_SETTEXT, 0,
833              (LPARAM)pCryptUICertMgr->pwszTitle);
834         show_cert_stores(hwnd, pCryptUICertMgr->dwFlags);
835         show_store_certs(hwnd, cert_mgr_index_to_store(tab, 0));
836         break;
837     }
838     case WM_NOTIFY:
839     {
840         NMHDR *hdr = (NMHDR *)lp;
841
842         switch (hdr->code)
843         {
844         case TCN_SELCHANGE:
845             cert_mgr_clear_cert_selection(hwnd);
846             break;
847         case LVN_ITEMCHANGED:
848         {
849             NMITEMACTIVATE *nm;
850             HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
851
852             nm = (NMITEMACTIVATE*)lp;
853             if (nm->uNewState & LVN_ITEMACTIVATE)
854             {
855                 int numSelected = SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0);
856
857                 EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), numSelected > 0);
858                 EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), numSelected > 0);
859                 EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), numSelected == 1);
860             }
861             break;
862         }
863         case NM_DBLCLK:
864             show_selected_cert(hwnd, ((NMITEMACTIVATE *)lp)->iItem);
865             break;
866         }
867         break;
868     }
869     case WM_COMMAND:
870         switch (wp)
871         {
872         case ((CBN_SELCHANGE << 16) | IDC_MGR_PURPOSE_SELECTION):
873             cert_mgr_clear_cert_selection(hwnd);
874             break;
875         case IDC_MGR_ADVANCED:
876             if (DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_CERT_MGR_ADVANCED),
877              hwnd, cert_mgr_advanced_dlg_proc) == IDOK)
878             {
879                 HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION);
880                 int index, len;
881                 LPWSTR curString = NULL;
882
883                 index = SendMessageW(cb, CB_GETCURSEL, 0, 0);
884                 if (index >= 0)
885                 {
886                     len = SendMessageW(cb, CB_GETLBTEXTLEN, index, 0);
887                     curString = HeapAlloc(GetProcessHeap(), 0,
888                      (len + 1) * sizeof(WCHAR));
889                     SendMessageW(cb, CB_GETLBTEXT, index, (LPARAM)curString);
890                 }
891                 SendMessageW(cb, CB_RESETCONTENT, 0, 0);
892                 initialize_purpose_selection(hwnd);
893                 if (curString)
894                 {
895                     index = SendMessageW(cb, CB_FINDSTRINGEXACT, -1,
896                      (LPARAM)curString);
897                     if (index >= 0)
898                         SendMessageW(cb, CB_SETCURSEL, index, 0);
899                     HeapFree(GetProcessHeap(), 0, curString);
900                 }
901                 refresh_store_certs(hwnd);
902             }
903             break;
904         case IDC_MGR_VIEW:
905         {
906             HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
907             int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1,
908              LVNI_SELECTED);
909
910             if (selection >= 0)
911                 show_selected_cert(hwnd, selection);
912             break;
913         }
914         case IDCANCEL:
915             free_certs(GetDlgItem(hwnd, IDC_MGR_CERTS));
916             close_stores(GetDlgItem(hwnd, IDC_MGR_STORES));
917             ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER));
918             EndDialog(hwnd, IDCANCEL);
919             break;
920         }
921         break;
922     }
923     return 0;
924 }
925
926 /***********************************************************************
927  *              CryptUIDlgCertMgr (CRYPTUI.@)
928  */
929 BOOL WINAPI CryptUIDlgCertMgr(PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr)
930 {
931     TRACE("(%p)\n", pCryptUICertMgr);
932
933     if (pCryptUICertMgr->dwSize != sizeof(CRYPTUI_CERT_MGR_STRUCT))
934     {
935         WARN("unexpected size %d\n", pCryptUICertMgr->dwSize);
936         SetLastError(E_INVALIDARG);
937         return FALSE;
938     }
939     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_CERT_MGR),
940      pCryptUICertMgr->hwndParent, cert_mgr_dlg_proc, (LPARAM)pCryptUICertMgr);
941     return TRUE;
942 }
943
944 /* FIXME: real names are unknown, functions are undocumented */
945 typedef struct _CRYPTUI_ENUM_SYSTEM_STORE_ARGS
946 {
947     DWORD dwFlags;
948     void *pvSystemStoreLocationPara;
949 } CRYPTUI_ENUM_SYSTEM_STORE_ARGS, *PCRYPTUI_ENUM_SYSTEM_STORE_ARGS;
950
951 typedef struct _CRYPTUI_ENUM_DATA
952 {
953     DWORD                           cStores;
954     HCERTSTORE                     *rghStore;
955     DWORD                           cEnumArgs;
956     PCRYPTUI_ENUM_SYSTEM_STORE_ARGS rgEnumArgs;
957 } CRYPTUI_ENUM_DATA, *PCRYPTUI_ENUM_DATA;
958
959 typedef BOOL (WINAPI *PFN_SELECTED_STORE_CB)(HCERTSTORE store, HWND hwnd,
960  void *pvArg);
961
962 /* Values for dwFlags */
963 #define CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE 0x00000001
964
965 typedef struct _CRYPTUI_SELECTSTORE_INFO_A
966 {
967     DWORD                 dwSize;
968     HWND                  parent;
969     DWORD                 dwFlags;
970     LPSTR                 pszTitle;
971     LPSTR                 pszText;
972     CRYPTUI_ENUM_DATA    *pEnumData;
973     PFN_SELECTED_STORE_CB pfnSelectedStoreCallback;
974     void                 *pvArg;
975 } CRYPTUI_SELECTSTORE_INFO_A, *PCRYPTUI_SELECTSTORE_INFO_A;
976
977 typedef struct _CRYPTUI_SELECTSTORE_INFO_W
978 {
979     DWORD                 dwSize;
980     HWND                  parent;
981     DWORD                 dwFlags;
982     LPWSTR                pwszTitle;
983     LPWSTR                pwszText;
984     CRYPTUI_ENUM_DATA    *pEnumData;
985     PFN_SELECTED_STORE_CB pfnSelectedStoreCallback;
986     void                 *pvArg;
987 } CRYPTUI_SELECTSTORE_INFO_W, *PCRYPTUI_SELECTSTORE_INFO_W;
988
989 struct StoreInfo
990 {
991     enum {
992         StoreHandle,
993         SystemStore
994     } type;
995     union {
996         HCERTSTORE store;
997         LPWSTR name;
998     } DUMMYUNIONNAME;
999 };
1000
1001 static BOOL WINAPI enum_store_callback(const void *pvSystemStore,
1002  DWORD dwFlags, PCERT_SYSTEM_STORE_INFO pStoreInfo, void *pvReserved,
1003  void *pvArg)
1004 {
1005     HWND tree = GetDlgItem(pvArg, IDC_STORE_LIST);
1006     TVINSERTSTRUCTW tvis;
1007     LPCWSTR localizedName;
1008     BOOL ret = TRUE;
1009
1010     tvis.hParent = NULL;
1011     tvis.hInsertAfter = TVI_LAST;
1012     tvis.u.item.mask = TVIF_TEXT;
1013     if ((localizedName = CryptFindLocalizedName(pvSystemStore)))
1014     {
1015         struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(), 0,
1016          sizeof(struct StoreInfo));
1017
1018         if (storeInfo)
1019         {
1020             storeInfo->type = SystemStore;
1021             storeInfo->u.name = HeapAlloc(GetProcessHeap(), 0,
1022              (strlenW(pvSystemStore) + 1) * sizeof(WCHAR));
1023             if (storeInfo->u.name)
1024             {
1025                 tvis.u.item.mask |= TVIF_PARAM;
1026                 tvis.u.item.lParam = (LPARAM)storeInfo;
1027                 strcpyW(storeInfo->u.name, pvSystemStore);
1028             }
1029             else
1030             {
1031                 HeapFree(GetProcessHeap(), 0, storeInfo);
1032                 ret = FALSE;
1033             }
1034         }
1035         else
1036             ret = FALSE;
1037         tvis.u.item.pszText = (LPWSTR)localizedName;
1038     }
1039     else
1040         tvis.u.item.pszText = (LPWSTR)pvSystemStore;
1041     /* FIXME: need a folder icon for the store too */
1042     if (ret)
1043         SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis);
1044     return ret;
1045 }
1046
1047 static void enumerate_stores(HWND hwnd, CRYPTUI_ENUM_DATA *pEnumData)
1048 {
1049     DWORD i;
1050     HWND tree = GetDlgItem(hwnd, IDC_STORE_LIST);
1051
1052     for (i = 0; i < pEnumData->cEnumArgs; i++)
1053         CertEnumSystemStore(pEnumData->rgEnumArgs[i].dwFlags,
1054          pEnumData->rgEnumArgs[i].pvSystemStoreLocationPara,
1055          hwnd, enum_store_callback);
1056     for (i = 0; i < pEnumData->cStores; i++)
1057     {
1058         DWORD size;
1059
1060         if (CertGetStoreProperty(pEnumData->rghStore[i],
1061          CERT_STORE_LOCALIZED_NAME_PROP_ID, NULL, &size))
1062         {
1063             LPWSTR name = HeapAlloc(GetProcessHeap(), 0, size);
1064
1065             if (name)
1066             {
1067                 if (CertGetStoreProperty(pEnumData->rghStore[i],
1068                  CERT_STORE_LOCALIZED_NAME_PROP_ID, name, &size))
1069                 {
1070                     struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(),
1071                      0, sizeof(struct StoreInfo));
1072
1073                     if (storeInfo)
1074                     {
1075                         TVINSERTSTRUCTW tvis;
1076
1077                         storeInfo->type = StoreHandle;
1078                         storeInfo->u.store = pEnumData->rghStore[i];
1079                         tvis.hParent = NULL;
1080                         tvis.hInsertAfter = TVI_LAST;
1081                         tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
1082                         tvis.u.item.pszText = name;
1083                         tvis.u.item.lParam = (LPARAM)storeInfo;
1084                         SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis);
1085                     }
1086                 }
1087                 HeapFree(GetProcessHeap(), 0, name);
1088             }
1089         }
1090     }
1091 }
1092
1093 static void free_store_info(HWND tree)
1094 {
1095     HTREEITEM next = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CHILD,
1096      (LPARAM)NULL);
1097
1098     while (next)
1099     {
1100         TVITEMW item;
1101
1102         memset(&item, 0, sizeof(item));
1103         item.mask = TVIF_HANDLE | TVIF_PARAM;
1104         item.hItem = next;
1105         SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
1106         if (item.lParam)
1107         {
1108             struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam;
1109
1110             if (storeInfo->type == SystemStore)
1111                 HeapFree(GetProcessHeap(), 0, storeInfo->u.name);
1112             HeapFree(GetProcessHeap(), 0, storeInfo);
1113         }
1114         next = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_NEXT,
1115          (LPARAM)next);
1116     }
1117 }
1118
1119 static HCERTSTORE selected_item_to_store(HWND tree, HTREEITEM hItem)
1120 {
1121     WCHAR buf[MAX_STRING_LEN];
1122     TVITEMW item;
1123     HCERTSTORE store;
1124
1125     memset(&item, 0, sizeof(item));
1126     item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
1127     item.hItem = hItem;
1128     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
1129     item.pszText = buf;
1130     SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
1131     if (item.lParam)
1132     {
1133         struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam;
1134
1135         if (storeInfo->type == StoreHandle)
1136             store = storeInfo->u.store;
1137         else
1138             store = CertOpenSystemStoreW(0, storeInfo->u.name);
1139     }
1140     else
1141     {
1142         /* It's implicitly a system store */
1143         store = CertOpenSystemStoreW(0, buf);
1144     }
1145     return store;
1146 }
1147
1148 struct SelectStoreInfo
1149 {
1150     PCRYPTUI_SELECTSTORE_INFO_W info;
1151     HCERTSTORE                  store;
1152 };
1153
1154 static LRESULT CALLBACK select_store_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1155  LPARAM lp)
1156 {
1157     struct SelectStoreInfo *selectInfo;
1158     LRESULT ret = 0;
1159
1160     switch (msg)
1161     {
1162     case WM_INITDIALOG:
1163     {
1164         selectInfo = (struct SelectStoreInfo *)lp;
1165         SetWindowLongPtrW(hwnd, DWLP_USER, lp);
1166         if (selectInfo->info->pwszTitle)
1167             SendMessageW(hwnd, WM_SETTEXT, 0,
1168              (LPARAM)selectInfo->info->pwszTitle);
1169         if (selectInfo->info->pwszText)
1170             SendMessageW(GetDlgItem(hwnd, IDC_STORE_TEXT), WM_SETTEXT, 0,
1171              (LPARAM)selectInfo->info->pwszText);
1172         if (!(selectInfo->info->dwFlags & CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE))
1173             ShowWindow(GetDlgItem(hwnd, IDC_SHOW_PHYSICAL_STORES), FALSE);
1174         enumerate_stores(hwnd, selectInfo->info->pEnumData);
1175         break;
1176     }
1177     case WM_COMMAND:
1178         switch (wp)
1179         {
1180         case IDOK:
1181         {
1182             HWND tree = GetDlgItem(hwnd, IDC_STORE_LIST);
1183             HTREEITEM selection = (HTREEITEM)SendMessageW(tree,
1184              TVM_GETNEXTITEM, TVGN_CARET, (LPARAM)NULL);
1185
1186             selectInfo = (struct SelectStoreInfo *)GetWindowLongPtrW(hwnd,
1187              DWLP_USER);
1188             if (!selection)
1189             {
1190                 WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN], *pTitle;
1191
1192                 if (selectInfo->info->pwszTitle)
1193                     pTitle = selectInfo->info->pwszTitle;
1194                 else
1195                 {
1196                     LoadStringW(hInstance, IDS_SELECT_STORE_TITLE, title,
1197                      sizeof(title) / sizeof(title[0]));
1198                     pTitle = title;
1199                 }
1200                 LoadStringW(hInstance, IDS_SELECT_STORE, error,
1201                  sizeof(error) / sizeof(error[0]));
1202                 MessageBoxW(hwnd, error, pTitle, MB_ICONEXCLAMATION | MB_OK);
1203             }
1204             else
1205             {
1206                 HCERTSTORE store = selected_item_to_store(tree, selection);
1207
1208                 if (!selectInfo->info->pfnSelectedStoreCallback ||
1209                  selectInfo->info->pfnSelectedStoreCallback(store, hwnd,
1210                  selectInfo->info->pvArg))
1211                 {
1212                     selectInfo->store = store;
1213                     free_store_info(tree);
1214                     EndDialog(hwnd, IDOK);
1215                 }
1216                 else
1217                     CertCloseStore(store, 0);
1218             }
1219             ret = TRUE;
1220             break;
1221         }
1222         case IDCANCEL:
1223             free_store_info(GetDlgItem(hwnd, IDC_STORE_LIST));
1224             EndDialog(hwnd, IDCANCEL);
1225             ret = TRUE;
1226             break;
1227         }
1228         break;
1229     }
1230     return ret;
1231 }
1232
1233 /***********************************************************************
1234  *              CryptUIDlgSelectStoreW (CRYPTUI.@)
1235  */
1236 HCERTSTORE WINAPI CryptUIDlgSelectStoreW(PCRYPTUI_SELECTSTORE_INFO_W info)
1237 {
1238     struct SelectStoreInfo selectInfo = { info, NULL };
1239
1240     TRACE("(%p)\n", info);
1241
1242     if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_W))
1243     {
1244         WARN("unexpected size %d\n", info->dwSize);
1245         SetLastError(E_INVALIDARG);
1246         return NULL;
1247     }
1248     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_SELECT_STORE), info->parent,
1249      select_store_dlg_proc, (LPARAM)&selectInfo);
1250     return selectInfo.store;
1251 }
1252
1253 /***********************************************************************
1254  *              CryptUIDlgSelectStoreA (CRYPTUI.@)
1255  */
1256 HCERTSTORE WINAPI CryptUIDlgSelectStoreA(PCRYPTUI_SELECTSTORE_INFO_A info)
1257 {
1258     CRYPTUI_SELECTSTORE_INFO_W infoW;
1259     HCERTSTORE ret;
1260     int len;
1261
1262     TRACE("(%p)\n", info);
1263
1264     if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_A))
1265     {
1266         WARN("unexpected size %d\n", info->dwSize);
1267         SetLastError(E_INVALIDARG);
1268         return NULL;
1269     }
1270     memcpy(&infoW, &info, sizeof(info));
1271     if (info->pszTitle)
1272     {
1273         len = MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, NULL, 0);
1274         infoW.pwszTitle = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1275         MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, infoW.pwszTitle,
1276          len);
1277     }
1278     if (info->pszText)
1279     {
1280         len = MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, NULL, 0);
1281         infoW.pwszText = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1282         MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, infoW.pwszText, len);
1283     }
1284     ret = CryptUIDlgSelectStoreW(&infoW);
1285     HeapFree(GetProcessHeap(), 0, infoW.pwszText);
1286     HeapFree(GetProcessHeap(), 0, infoW.pwszTitle);
1287     return ret;
1288 }
1289
1290 /***********************************************************************
1291  *              CryptUIDlgViewCertificateA (CRYPTUI.@)
1292  */
1293 BOOL WINAPI CryptUIDlgViewCertificateA(
1294  PCCRYPTUI_VIEWCERTIFICATE_STRUCTA pCertViewInfo, BOOL *pfPropertiesChanged)
1295 {
1296     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
1297     LPWSTR title = NULL;
1298     BOOL ret;
1299
1300     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
1301
1302     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
1303     if (pCertViewInfo->szTitle)
1304     {
1305         int len = MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1,
1306          NULL, 0);
1307
1308         title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1309         if (title)
1310         {
1311             MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, title,
1312              len);
1313             viewInfo.szTitle = title;
1314         }
1315         else
1316         {
1317             ret = FALSE;
1318             goto error;
1319         }
1320     }
1321     if (pCertViewInfo->cPropSheetPages)
1322     {
1323         FIXME("ignoring additional prop sheet pages\n");
1324         viewInfo.cPropSheetPages = 0;
1325     }
1326     ret = CryptUIDlgViewCertificateW(&viewInfo, pfPropertiesChanged);
1327     HeapFree(GetProcessHeap(), 0, title);
1328 error:
1329     return ret;
1330 }
1331
1332 struct ReadStringStruct
1333 {
1334     LPCWSTR buf;
1335     LONG pos;
1336     LONG len;
1337 };
1338
1339 static DWORD CALLBACK read_text_callback(DWORD_PTR dwCookie, LPBYTE buf,
1340  LONG cb, LONG *pcb)
1341 {
1342     struct ReadStringStruct *string = (struct ReadStringStruct *)dwCookie;
1343     LONG cch = min(cb / sizeof(WCHAR), string->len - string->pos);
1344
1345     TRACE("(%p, %p, %d, %p)\n", string, buf, cb, pcb);
1346
1347     memmove(buf, string->buf + string->pos, cch * sizeof(WCHAR));
1348     string->pos += cch;
1349     *pcb = cch * sizeof(WCHAR);
1350     return 0;
1351 }
1352
1353 static void add_unformatted_text_to_control(HWND hwnd, LPCWSTR text, LONG len)
1354 {
1355     struct ReadStringStruct string;
1356     EDITSTREAM editstream;
1357
1358     TRACE("(%p, %s)\n", hwnd, debugstr_wn(text, len));
1359
1360     string.buf = text;
1361     string.pos = 0;
1362     string.len = len;
1363     editstream.dwCookie = (DWORD_PTR)&string;
1364     editstream.dwError = 0;
1365     editstream.pfnCallback = read_text_callback;
1366     SendMessageW(hwnd, EM_STREAMIN, SF_TEXT | SFF_SELECTION | SF_UNICODE,
1367      (LPARAM)&editstream);
1368 }
1369
1370 static void add_string_resource_to_control(HWND hwnd, int id)
1371 {
1372     LPWSTR str;
1373     LONG len;
1374
1375     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
1376     add_unformatted_text_to_control(hwnd, str, len);
1377 }
1378
1379 static void add_text_with_paraformat_to_control(HWND hwnd, LPCWSTR text,
1380  LONG len, const PARAFORMAT2 *fmt)
1381 {
1382     add_unformatted_text_to_control(hwnd, text, len);
1383     SendMessageW(hwnd, EM_SETPARAFORMAT, 0, (LPARAM)fmt);
1384 }
1385
1386 static void add_string_resource_with_paraformat_to_control(HWND hwnd, int id,
1387  const PARAFORMAT2 *fmt)
1388 {
1389     LPWSTR str;
1390     LONG len;
1391
1392     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
1393     add_text_with_paraformat_to_control(hwnd, str, len, fmt);
1394 }
1395
1396 static LPWSTR get_cert_name_string(PCCERT_CONTEXT pCertContext, DWORD dwType,
1397  DWORD dwFlags)
1398 {
1399     LPWSTR buf = NULL;
1400     DWORD len;
1401
1402     len = CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, NULL, 0);
1403     if (len)
1404     {
1405         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1406         if (buf)
1407             CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, buf, len);
1408     }
1409     return buf;
1410 }
1411
1412 static void add_cert_string_to_control(HWND hwnd, PCCERT_CONTEXT pCertContext,
1413  DWORD dwType, DWORD dwFlags)
1414 {
1415     LPWSTR name = get_cert_name_string(pCertContext, dwType, dwFlags);
1416
1417     if (name)
1418     {
1419         /* Don't include NULL-terminator in output */
1420         DWORD len = lstrlenW(name);
1421
1422         add_unformatted_text_to_control(hwnd, name, len);
1423         HeapFree(GetProcessHeap(), 0, name);
1424     }
1425 }
1426
1427 static void add_icon_to_control(HWND hwnd, int id)
1428 {
1429     HRESULT hr;
1430     LPRICHEDITOLE richEditOle = NULL;
1431     LPOLEOBJECT object = NULL;
1432     CLSID clsid;
1433     LPOLECACHE oleCache = NULL;
1434     FORMATETC formatEtc;
1435     DWORD conn;
1436     LPDATAOBJECT dataObject = NULL;
1437     HBITMAP bitmap = NULL;
1438     RECT rect;
1439     STGMEDIUM stgm;
1440     REOBJECT reObject;
1441
1442     TRACE("(%p, %d)\n", hwnd, id);
1443
1444     SendMessageW(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richEditOle);
1445     if (!richEditOle)
1446         goto end;
1447     hr = OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject,
1448      (void**)&object);
1449     if (FAILED(hr))
1450         goto end;
1451     hr = IOleObject_GetUserClassID(object, &clsid);
1452     if (FAILED(hr))
1453         goto end;
1454     hr = IOleObject_QueryInterface(object, &IID_IOleCache, (void**)&oleCache);
1455     if (FAILED(hr))
1456         goto end;
1457     formatEtc.cfFormat = CF_BITMAP;
1458     formatEtc.ptd = NULL;
1459     formatEtc.dwAspect = DVASPECT_CONTENT;
1460     formatEtc.lindex = -1;
1461     formatEtc.tymed = TYMED_GDI;
1462     hr = IOleCache_Cache(oleCache, &formatEtc, 0, &conn);
1463     if (FAILED(hr))
1464         goto end;
1465     hr = IOleObject_QueryInterface(object, &IID_IDataObject,
1466      (void**)&dataObject);
1467     if (FAILED(hr))
1468         goto end;
1469     bitmap = LoadImageW(hInstance, MAKEINTRESOURCEW(id), IMAGE_BITMAP, 0, 0,
1470      LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
1471     if (!bitmap)
1472         goto end;
1473     rect.left = rect.top = 0;
1474     rect.right = GetSystemMetrics(SM_CXICON);
1475     rect.bottom = GetSystemMetrics(SM_CYICON);
1476     stgm.tymed = TYMED_GDI;
1477     stgm.u.hBitmap = bitmap;
1478     stgm.pUnkForRelease = NULL;
1479     hr = IDataObject_SetData(dataObject, &formatEtc, &stgm, TRUE);
1480     if (FAILED(hr))
1481         goto end;
1482
1483     reObject.cbStruct = sizeof(reObject);
1484     reObject.cp = REO_CP_SELECTION;
1485     reObject.clsid = clsid;
1486     reObject.poleobj = object;
1487     reObject.pstg = NULL;
1488     reObject.polesite = NULL;
1489     reObject.sizel.cx = reObject.sizel.cy = 0;
1490     reObject.dvaspect = DVASPECT_CONTENT;
1491     reObject.dwFlags = 0;
1492     reObject.dwUser = 0;
1493
1494     IRichEditOle_InsertObject(richEditOle, &reObject);
1495
1496 end:
1497     if (dataObject)
1498         IDataObject_Release(dataObject);
1499     if (oleCache)
1500         IOleCache_Release(oleCache);
1501     if (object)
1502         IOleObject_Release(object);
1503     if (richEditOle)
1504         IRichEditOle_Release(richEditOle);
1505 }
1506
1507 #define MY_INDENT 200
1508
1509 static void add_oid_text_to_control(HWND hwnd, char *oid)
1510 {
1511     WCHAR nl = '\n';
1512     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid, 0);
1513     PARAFORMAT2 parFmt;
1514
1515     parFmt.cbSize = sizeof(parFmt);
1516     parFmt.dwMask = PFM_STARTINDENT;
1517     parFmt.dxStartIndent = MY_INDENT * 3;
1518     if (oidInfo)
1519     {
1520         add_text_with_paraformat_to_control(hwnd, oidInfo->pwszName,
1521          lstrlenW(oidInfo->pwszName), &parFmt);
1522         add_unformatted_text_to_control(hwnd, &nl, 1);
1523     }
1524 }
1525
1526 struct OIDToString
1527 {
1528     LPCSTR oid;
1529     int    id;
1530 };
1531
1532 /* The following list MUST be lexicographically sorted by OID */
1533 static struct OIDToString oidMap[] = {
1534  /* 1.3.6.1.4.1.311.10.3.1 */
1535  { szOID_KP_CTL_USAGE_SIGNING, IDS_PURPOSE_CTL_USAGE_SIGNING },
1536  /* 1.3.6.1.4.1.311.10.3.4 */
1537  { szOID_KP_EFS, IDS_PURPOSE_EFS },
1538  /* 1.3.6.1.4.1.311.10.3.4.1 */
1539  { szOID_EFS_RECOVERY, IDS_PURPOSE_EFS_RECOVERY },
1540  /* 1.3.6.1.4.1.311.10.3.5 */
1541  { szOID_WHQL_CRYPTO, IDS_PURPOSE_WHQL },
1542  /* 1.3.6.1.4.1.311.10.3.6 */
1543  { szOID_NT5_CRYPTO, IDS_PURPOSE_NT5 },
1544  /* 1.3.6.1.4.1.311.10.3.7 */
1545  { szOID_OEM_WHQL_CRYPTO, IDS_PURPOSE_OEM_WHQL },
1546  /* 1.3.6.1.4.1.311.10.3.8 */
1547  { szOID_EMBEDDED_NT_CRYPTO, IDS_PURPOSE_EMBEDDED_NT },
1548  /* 1.3.6.1.4.1.311.10.3.9 */
1549  { szOID_ROOT_LIST_SIGNER, IDS_PURPOSE_ROOT_LIST_SIGNER },
1550  /* 1.3.6.1.4.1.311.10.3.10 */
1551  { szOID_KP_QUALIFIED_SUBORDINATION, IDS_PURPOSE_QUALIFIED_SUBORDINATION },
1552  /* 1.3.6.1.4.1.311.10.3.11 */
1553  { szOID_KP_KEY_RECOVERY, IDS_PURPOSE_KEY_RECOVERY },
1554  /* 1.3.6.1.4.1.311.10.3.12 */
1555  { szOID_KP_DOCUMENT_SIGNING, IDS_PURPOSE_DOCUMENT_SIGNING },
1556  /* 1.3.6.1.4.1.311.10.3.13 */
1557  { szOID_KP_LIFETIME_SIGNING, IDS_PURPOSE_LIFETIME_SIGNING },
1558  /* 1.3.6.1.4.1.311.10.5.1 */
1559  { szOID_DRM, IDS_PURPOSE_DRM },
1560  /* 1.3.6.1.4.1.311.10.6.1 */
1561  { szOID_LICENSES, IDS_PURPOSE_LICENSES },
1562  /* 1.3.6.1.4.1.311.10.6.2 */
1563  { szOID_LICENSE_SERVER, IDS_PURPOSE_LICENSE_SERVER },
1564  /* 1.3.6.1.4.1.311.20.2.1 */
1565  { szOID_ENROLLMENT_AGENT, IDS_PURPOSE_ENROLLMENT_AGENT },
1566  /* 1.3.6.1.4.1.311.20.2.2 */
1567  { szOID_KP_SMARTCARD_LOGON, IDS_PURPOSE_SMARTCARD_LOGON },
1568  /* 1.3.6.1.4.1.311.21.5 */
1569  { szOID_KP_CA_EXCHANGE, IDS_PURPOSE_CA_EXCHANGE },
1570  /* 1.3.6.1.4.1.311.21.6 */
1571  { szOID_KP_KEY_RECOVERY_AGENT, IDS_PURPOSE_KEY_RECOVERY_AGENT },
1572  /* 1.3.6.1.4.1.311.21.19 */
1573  { szOID_DS_EMAIL_REPLICATION, IDS_PURPOSE_DS_EMAIL_REPLICATION },
1574  /* 1.3.6.1.5.5.7.3.1 */
1575  { szOID_PKIX_KP_SERVER_AUTH, IDS_PURPOSE_SERVER_AUTH },
1576  /* 1.3.6.1.5.5.7.3.2 */
1577  { szOID_PKIX_KP_CLIENT_AUTH, IDS_PURPOSE_CLIENT_AUTH },
1578  /* 1.3.6.1.5.5.7.3.3 */
1579  { szOID_PKIX_KP_CODE_SIGNING, IDS_PURPOSE_CODE_SIGNING },
1580  /* 1.3.6.1.5.5.7.3.4 */
1581  { szOID_PKIX_KP_EMAIL_PROTECTION, IDS_PURPOSE_EMAIL_PROTECTION },
1582  /* 1.3.6.1.5.5.7.3.5 */
1583  { szOID_PKIX_KP_IPSEC_END_SYSTEM, IDS_PURPOSE_IPSEC },
1584  /* 1.3.6.1.5.5.7.3.6 */
1585  { szOID_PKIX_KP_IPSEC_TUNNEL, IDS_PURPOSE_IPSEC },
1586  /* 1.3.6.1.5.5.7.3.7 */
1587  { szOID_PKIX_KP_IPSEC_USER, IDS_PURPOSE_IPSEC },
1588  /* 1.3.6.1.5.5.7.3.8 */
1589  { szOID_PKIX_KP_TIMESTAMP_SIGNING, IDS_PURPOSE_TIMESTAMP_SIGNING },
1590 };
1591
1592 static struct OIDToString *findSupportedOID(LPCSTR oid)
1593 {
1594     int indexHigh = sizeof(oidMap) / sizeof(oidMap[0]) - 1, indexLow = 0, i;
1595     struct OIDToString *ret = NULL;
1596
1597     for (i = (indexLow + indexHigh) / 2; !ret && indexLow <= indexHigh;
1598      i = (indexLow + indexHigh) / 2)
1599     {
1600         int cmp;
1601
1602         cmp = strcmp(oid, oidMap[i].oid);
1603         if (!cmp)
1604             ret = &oidMap[i];
1605         else if (cmp > 0)
1606             indexLow = i + 1;
1607         else
1608             indexHigh = i - 1;
1609     }
1610     return ret;
1611 }
1612
1613 static void add_local_oid_text_to_control(HWND text, LPCSTR oid)
1614 {
1615     struct OIDToString *entry;
1616     WCHAR nl = '\n';
1617     PARAFORMAT2 parFmt;
1618
1619     parFmt.cbSize = sizeof(parFmt);
1620     parFmt.dwMask = PFM_STARTINDENT;
1621     parFmt.dxStartIndent = MY_INDENT * 3;
1622     if ((entry = findSupportedOID(oid)))
1623     {
1624         WCHAR *str, *linebreak, *ptr;
1625         BOOL multiline = FALSE;
1626         int len;
1627
1628         len = LoadStringW(hInstance, entry->id, (LPWSTR)&str, 0);
1629         ptr = str;
1630         do {
1631             if ((linebreak = memchrW(ptr, '\n', len)))
1632             {
1633                 WCHAR copy[MAX_STRING_LEN];
1634
1635                 multiline = TRUE;
1636                 /* The source string contains a newline, which the richedit
1637                  * control won't find since it's interpreted as a paragraph
1638                  * break.  Therefore copy up to the newline.  lstrcpynW always
1639                  * NULL-terminates, so pass one more than the length of the
1640                  * source line so the copy includes the entire line and the
1641                  * NULL-terminator.
1642                  */
1643                 lstrcpynW(copy, ptr, linebreak - ptr + 1);
1644                 add_text_with_paraformat_to_control(text, copy,
1645                  linebreak - ptr, &parFmt);
1646                 ptr = linebreak + 1;
1647                 add_unformatted_text_to_control(text, &nl, 1);
1648             }
1649             else if (multiline && *ptr)
1650             {
1651                 /* Add the last line */
1652                 add_text_with_paraformat_to_control(text, ptr,
1653                  len - (ptr - str), &parFmt);
1654                 add_unformatted_text_to_control(text, &nl, 1);
1655             }
1656         } while (linebreak);
1657         if (!multiline)
1658         {
1659             add_text_with_paraformat_to_control(text, str, len, &parFmt);
1660             add_unformatted_text_to_control(text, &nl, 1);
1661         }
1662     }
1663     else
1664     {
1665         WCHAR *oidW = HeapAlloc(GetProcessHeap(), 0,
1666          (strlen(oid) + 1) * sizeof(WCHAR));
1667
1668         if (oidW)
1669         {
1670             LPCSTR src;
1671             WCHAR *dst;
1672
1673             for (src = oid, dst = oidW; *src; src++, dst++)
1674                 *dst = *src;
1675             *dst = 0;
1676             add_text_with_paraformat_to_control(text, oidW, lstrlenW(oidW),
1677              &parFmt);
1678             add_unformatted_text_to_control(text, &nl, 1);
1679             HeapFree(GetProcessHeap(), 0, oidW);
1680         }
1681     }
1682 }
1683
1684 static void display_app_usages(HWND text, PCCERT_CONTEXT cert,
1685  BOOL *anyUsageAdded)
1686 {
1687     static char any_app_policy[] = szOID_ANY_APPLICATION_POLICY;
1688     WCHAR nl = '\n';
1689     CHARFORMATW charFmt;
1690     PCERT_EXTENSION policyExt;
1691     if (!*anyUsageAdded)
1692     {
1693         PARAFORMAT2 parFmt;
1694
1695         parFmt.cbSize = sizeof(parFmt);
1696         parFmt.dwMask = PFM_STARTINDENT;
1697         parFmt.dxStartIndent = MY_INDENT;
1698         add_string_resource_with_paraformat_to_control(text,
1699          IDS_CERT_INFO_PURPOSES, &parFmt);
1700         add_unformatted_text_to_control(text, &nl, 1);
1701         *anyUsageAdded = TRUE;
1702     }
1703     memset(&charFmt, 0, sizeof(charFmt));
1704     charFmt.cbSize = sizeof(charFmt);
1705     charFmt.dwMask = CFM_BOLD;
1706     charFmt.dwEffects = 0;
1707     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
1708     if ((policyExt = CertFindExtension(szOID_APPLICATION_CERT_POLICIES,
1709      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1710     {
1711         CERT_POLICIES_INFO *policies;
1712         DWORD size;
1713
1714         if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES,
1715          policyExt->Value.pbData, policyExt->Value.cbData,
1716          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
1717         {
1718             DWORD i;
1719
1720             for (i = 0; i < policies->cPolicyInfo; i++)
1721             {
1722                 DWORD j;
1723
1724                 for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
1725                     add_local_oid_text_to_control(text,
1726                      policies->rgPolicyInfo[i].rgPolicyQualifier[j].
1727                      pszPolicyQualifierId);
1728             }
1729             LocalFree(policies);
1730         }
1731     }
1732     else
1733         add_oid_text_to_control(text, any_app_policy);
1734 }
1735
1736 static BOOL display_cert_usages(HWND text, PCCERT_CONTEXT cert,
1737  BOOL *anyUsageAdded)
1738 {
1739     WCHAR nl = '\n';
1740     DWORD size;
1741     BOOL badUsages = FALSE;
1742
1743     if (CertGetEnhancedKeyUsage(cert, 0, NULL, &size))
1744     {
1745         CHARFORMATW charFmt;
1746         static char any_cert_policy[] = szOID_ANY_CERT_POLICY;
1747         PCERT_ENHKEY_USAGE usage = HeapAlloc(GetProcessHeap(), 0, size);
1748
1749         if (usage)
1750         {
1751             if (CertGetEnhancedKeyUsage(cert, 0, usage, &size))
1752             {
1753                 DWORD i;
1754
1755                 if (!*anyUsageAdded)
1756                 {
1757                     PARAFORMAT2 parFmt;
1758
1759                     parFmt.cbSize = sizeof(parFmt);
1760                     parFmt.dwMask = PFM_STARTINDENT;
1761                     parFmt.dxStartIndent = MY_INDENT;
1762                     add_string_resource_with_paraformat_to_control(text,
1763                      IDS_CERT_INFO_PURPOSES, &parFmt);
1764                     add_unformatted_text_to_control(text, &nl, 1);
1765                     *anyUsageAdded = TRUE;
1766                 }
1767                 memset(&charFmt, 0, sizeof(charFmt));
1768                 charFmt.cbSize = sizeof(charFmt);
1769                 charFmt.dwMask = CFM_BOLD;
1770                 charFmt.dwEffects = 0;
1771                 SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION,
1772                  (LPARAM)&charFmt);
1773                 if (!usage->cUsageIdentifier)
1774                     add_oid_text_to_control(text, any_cert_policy);
1775                 else
1776                     for (i = 0; i < usage->cUsageIdentifier; i++)
1777                         add_local_oid_text_to_control(text,
1778                          usage->rgpszUsageIdentifier[i]);
1779             }
1780             else
1781                 badUsages = TRUE;
1782             HeapFree(GetProcessHeap(), 0, usage);
1783         }
1784         else
1785             badUsages = TRUE;
1786     }
1787     else
1788         badUsages = TRUE;
1789     return badUsages;
1790 }
1791
1792 static void set_policy_text(HWND text,
1793  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
1794 {
1795     BOOL includeCertUsages = FALSE, includeAppUsages = FALSE;
1796     BOOL badUsages = FALSE, anyUsageAdded = FALSE;
1797
1798     if (pCertViewInfo->cPurposes)
1799     {
1800         DWORD i;
1801
1802         for (i = 0; i < pCertViewInfo->cPurposes; i++)
1803         {
1804             if (!strcmp(pCertViewInfo->rgszPurposes[i], szOID_ANY_CERT_POLICY))
1805                 includeCertUsages = TRUE;
1806             else if (!strcmp(pCertViewInfo->rgszPurposes[i],
1807              szOID_ANY_APPLICATION_POLICY))
1808                 includeAppUsages = TRUE;
1809             else
1810                 badUsages = TRUE;
1811         }
1812     }
1813     else
1814         includeAppUsages = includeCertUsages = TRUE;
1815     if (includeAppUsages)
1816         display_app_usages(text, pCertViewInfo->pCertContext, &anyUsageAdded);
1817     if (includeCertUsages)
1818         badUsages = display_cert_usages(text, pCertViewInfo->pCertContext,
1819          &anyUsageAdded);
1820     if (badUsages)
1821     {
1822         PARAFORMAT2 parFmt;
1823
1824         parFmt.cbSize = sizeof(parFmt);
1825         parFmt.dwMask = PFM_STARTINDENT;
1826         parFmt.dxStartIndent = MY_INDENT;
1827         add_string_resource_with_paraformat_to_control(text,
1828          IDS_CERT_INFO_BAD_PURPOSES, &parFmt);
1829     }
1830 }
1831
1832 static CRYPT_OBJID_BLOB *find_policy_qualifier(CERT_POLICIES_INFO *policies,
1833  LPCSTR policyOid)
1834 {
1835     CRYPT_OBJID_BLOB *ret = NULL;
1836     DWORD i;
1837
1838     for (i = 0; !ret && i < policies->cPolicyInfo; i++)
1839     {
1840         DWORD j;
1841
1842         for (j = 0; !ret && j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
1843             if (!strcmp(policies->rgPolicyInfo[i].rgPolicyQualifier[j].
1844              pszPolicyQualifierId, policyOid))
1845                 ret = &policies->rgPolicyInfo[i].rgPolicyQualifier[j].
1846                  Qualifier;
1847     }
1848     return ret;
1849 }
1850
1851 static WCHAR *get_cps_str_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
1852 {
1853     LPWSTR qualifierStr = NULL;
1854     CERT_NAME_VALUE *qualifierValue;
1855     DWORD size;
1856
1857     if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_VALUE,
1858      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
1859      &qualifierValue, &size))
1860     {
1861         size = CertRDNValueToStrW(qualifierValue->dwValueType,
1862          &qualifierValue->Value, NULL, 0);
1863         qualifierStr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1864         if (qualifierStr)
1865             CertRDNValueToStrW(qualifierValue->dwValueType,
1866              &qualifierValue->Value, qualifierStr, size);
1867         LocalFree(qualifierValue);
1868     }
1869     return qualifierStr;
1870 }
1871
1872 static WCHAR *get_user_notice_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
1873 {
1874     LPWSTR str = NULL;
1875     CERT_POLICY_QUALIFIER_USER_NOTICE *qualifierValue;
1876     DWORD size;
1877
1878     if (CryptDecodeObjectEx(X509_ASN_ENCODING,
1879      X509_PKIX_POLICY_QUALIFIER_USERNOTICE,
1880      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
1881      &qualifierValue, &size))
1882     {
1883         str = HeapAlloc(GetProcessHeap(), 0,
1884          (strlenW(qualifierValue->pszDisplayText) + 1) * sizeof(WCHAR));
1885         if (str)
1886             strcpyW(str, qualifierValue->pszDisplayText);
1887         LocalFree(qualifierValue);
1888     }
1889     return str;
1890 }
1891
1892 struct IssuerStatement
1893 {
1894     LPWSTR cps;
1895     LPWSTR userNotice;
1896 };
1897
1898 static void set_issuer_statement(HWND hwnd,
1899  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
1900 {
1901     PCERT_EXTENSION policyExt;
1902
1903     if (!(pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ISSUERSTATEMENT) &&
1904      (policyExt = CertFindExtension(szOID_CERT_POLICIES,
1905      pCertViewInfo->pCertContext->pCertInfo->cExtension,
1906      pCertViewInfo->pCertContext->pCertInfo->rgExtension)))
1907     {
1908         CERT_POLICIES_INFO *policies;
1909         DWORD size;
1910
1911         if (CryptDecodeObjectEx(X509_ASN_ENCODING, policyExt->pszObjId,
1912          policyExt->Value.pbData, policyExt->Value.cbData,
1913          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
1914         {
1915             CRYPT_OBJID_BLOB *qualifier;
1916             LPWSTR cps = NULL, userNotice = NULL;
1917
1918             if ((qualifier = find_policy_qualifier(policies,
1919              szOID_PKIX_POLICY_QUALIFIER_CPS)))
1920                 cps = get_cps_str_from_qualifier(qualifier);
1921             if ((qualifier = find_policy_qualifier(policies,
1922              szOID_PKIX_POLICY_QUALIFIER_USERNOTICE)))
1923                 userNotice = get_user_notice_from_qualifier(qualifier);
1924             if (cps || userNotice)
1925             {
1926                 struct IssuerStatement *issuerStatement =
1927                  HeapAlloc(GetProcessHeap(), 0, sizeof(struct IssuerStatement));
1928
1929                 if (issuerStatement)
1930                 {
1931                     issuerStatement->cps = cps;
1932                     issuerStatement->userNotice = userNotice;
1933                     EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), TRUE);
1934                     SetWindowLongPtrW(hwnd, DWLP_USER,
1935                      (ULONG_PTR)issuerStatement);
1936                 }
1937             }
1938             LocalFree(policies);
1939         }
1940     }
1941 }
1942
1943 static void set_cert_info(HWND hwnd,
1944  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
1945 {
1946     CHARFORMATW charFmt;
1947     PARAFORMAT2 parFmt;
1948     HWND icon = GetDlgItem(hwnd, IDC_CERTIFICATE_ICON);
1949     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_INFO);
1950     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
1951      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
1952      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
1953      pCertViewInfo->idxCounterSigner);
1954     CRYPT_PROVIDER_CERT *root =
1955      &provSigner->pasCertChain[provSigner->csCertChain - 1];
1956
1957     if (!provSigner->pChainContext ||
1958      (provSigner->pChainContext->TrustStatus.dwErrorStatus &
1959      CERT_TRUST_IS_PARTIAL_CHAIN))
1960         add_icon_to_control(icon, IDB_CERT_WARNING);
1961     else if (!root->fTrustedRoot)
1962         add_icon_to_control(icon, IDB_CERT_ERROR);
1963     else
1964         add_icon_to_control(icon, IDB_CERT);
1965
1966     memset(&charFmt, 0, sizeof(charFmt));
1967     charFmt.cbSize = sizeof(charFmt);
1968     charFmt.dwMask = CFM_BOLD;
1969     charFmt.dwEffects = CFE_BOLD;
1970     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
1971     /* FIXME: vertically center text */
1972     parFmt.cbSize = sizeof(parFmt);
1973     parFmt.dwMask = PFM_STARTINDENT;
1974     parFmt.dxStartIndent = MY_INDENT;
1975     add_string_resource_with_paraformat_to_control(text,
1976      IDS_CERTIFICATEINFORMATION, &parFmt);
1977
1978     text = GetDlgItem(hwnd, IDC_CERTIFICATE_STATUS);
1979     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
1980     if (provSigner->dwError == TRUST_E_CERT_SIGNATURE)
1981         add_string_resource_with_paraformat_to_control(text,
1982          IDS_CERT_INFO_BAD_SIG, &parFmt);
1983     else if (!provSigner->pChainContext ||
1984      (provSigner->pChainContext->TrustStatus.dwErrorStatus &
1985      CERT_TRUST_IS_PARTIAL_CHAIN))
1986         add_string_resource_with_paraformat_to_control(text,
1987          IDS_CERT_INFO_PARTIAL_CHAIN, &parFmt);
1988     else if (!root->fTrustedRoot)
1989     {
1990         if (provSigner->csCertChain == 1 && root->fSelfSigned)
1991             add_string_resource_with_paraformat_to_control(text,
1992              IDS_CERT_INFO_UNTRUSTED_CA, &parFmt);
1993         else
1994             add_string_resource_with_paraformat_to_control(text,
1995              IDS_CERT_INFO_UNTRUSTED_ROOT, &parFmt);
1996     }
1997     else
1998     {
1999         set_policy_text(text, pCertViewInfo);
2000         set_issuer_statement(hwnd, pCertViewInfo);
2001     }
2002 }
2003
2004 static void set_cert_name_string(HWND hwnd, PCCERT_CONTEXT cert,
2005  DWORD nameFlags, int heading)
2006 {
2007     WCHAR nl = '\n';
2008     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
2009     CHARFORMATW charFmt;
2010     PARAFORMAT2 parFmt;
2011
2012     memset(&charFmt, 0, sizeof(charFmt));
2013     charFmt.cbSize = sizeof(charFmt);
2014     charFmt.dwMask = CFM_BOLD;
2015     charFmt.dwEffects = CFE_BOLD;
2016     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2017     parFmt.cbSize = sizeof(parFmt);
2018     parFmt.dwMask = PFM_STARTINDENT;
2019     parFmt.dxStartIndent = MY_INDENT * 3;
2020     add_string_resource_with_paraformat_to_control(text, heading, &parFmt);
2021     charFmt.dwEffects = 0;
2022     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2023     add_cert_string_to_control(text, cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
2024      nameFlags);
2025     add_unformatted_text_to_control(text, &nl, 1);
2026     add_unformatted_text_to_control(text, &nl, 1);
2027     add_unformatted_text_to_control(text, &nl, 1);
2028
2029 }
2030
2031 static void add_date_string_to_control(HWND hwnd, const FILETIME *fileTime)
2032 {
2033     WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */
2034     WCHAR date[80];
2035     SYSTEMTIME sysTime;
2036
2037     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt,
2038      sizeof(dateFmt) / sizeof(dateFmt[0]));
2039     FileTimeToSystemTime(fileTime, &sysTime);
2040     GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date,
2041      sizeof(date) / sizeof(date[0]));
2042     add_unformatted_text_to_control(hwnd, date, lstrlenW(date));
2043 }
2044
2045 static void set_cert_validity_period(HWND hwnd, PCCERT_CONTEXT cert)
2046 {
2047     WCHAR nl = '\n';
2048     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
2049     CHARFORMATW charFmt;
2050     PARAFORMAT2 parFmt;
2051
2052     memset(&charFmt, 0, sizeof(charFmt));
2053     charFmt.cbSize = sizeof(charFmt);
2054     charFmt.dwMask = CFM_BOLD;
2055     charFmt.dwEffects = CFE_BOLD;
2056     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2057     parFmt.cbSize = sizeof(parFmt);
2058     parFmt.dwMask = PFM_STARTINDENT;
2059     parFmt.dxStartIndent = MY_INDENT * 3;
2060     add_string_resource_with_paraformat_to_control(text, IDS_VALID_FROM,
2061      &parFmt);
2062     charFmt.dwEffects = 0;
2063     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2064     add_date_string_to_control(text, &cert->pCertInfo->NotBefore);
2065     charFmt.dwEffects = CFE_BOLD;
2066     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2067     add_string_resource_to_control(text, IDS_VALID_TO);
2068     charFmt.dwEffects = 0;
2069     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2070     add_date_string_to_control(text, &cert->pCertInfo->NotAfter);
2071     add_unformatted_text_to_control(text, &nl, 1);
2072 }
2073
2074 static void set_general_info(HWND hwnd,
2075  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2076 {
2077     set_cert_info(hwnd, pCertViewInfo);
2078     set_cert_name_string(hwnd, pCertViewInfo->pCertContext, 0,
2079      IDS_SUBJECT_HEADING);
2080     set_cert_name_string(hwnd, pCertViewInfo->pCertContext,
2081      CERT_NAME_ISSUER_FLAG, IDS_ISSUER_HEADING);
2082     set_cert_validity_period(hwnd, pCertViewInfo->pCertContext);
2083 }
2084
2085 static LRESULT CALLBACK user_notice_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
2086  LPARAM lp)
2087 {
2088     LRESULT ret = 0;
2089     HWND text;
2090     struct IssuerStatement *issuerStatement;
2091
2092     switch (msg)
2093     {
2094     case WM_INITDIALOG:
2095         text = GetDlgItem(hwnd, IDC_USERNOTICE);
2096         issuerStatement = (struct IssuerStatement *)lp;
2097         add_unformatted_text_to_control(text, issuerStatement->userNotice,
2098          strlenW(issuerStatement->userNotice));
2099         if (issuerStatement->cps)
2100             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)issuerStatement->cps);
2101         else
2102             EnableWindow(GetDlgItem(hwnd, IDC_CPS), FALSE);
2103         break;
2104     case WM_COMMAND:
2105         switch (wp)
2106         {
2107         case IDOK:
2108             EndDialog(hwnd, IDOK);
2109             ret = TRUE;
2110             break;
2111         case IDC_CPS:
2112         {
2113             IBindCtx *bctx = NULL;
2114             LPWSTR cps;
2115
2116             CreateBindCtx(0, &bctx);
2117             cps = (LPWSTR)GetWindowLongPtrW(hwnd, DWLP_USER);
2118             HlinkSimpleNavigateToString(cps, NULL, NULL, NULL, bctx, NULL,
2119              HLNF_OPENINNEWWINDOW, 0);
2120             IBindCtx_Release(bctx);
2121             break;
2122         }
2123         }
2124     }
2125     return ret;
2126 }
2127
2128 static void show_user_notice(HWND hwnd, struct IssuerStatement *issuerStatement)
2129 {
2130     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_USERNOTICE), hwnd,
2131      user_notice_dlg_proc, (LPARAM)issuerStatement);
2132 }
2133
2134 static LRESULT CALLBACK general_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
2135  LPARAM lp)
2136 {
2137     PROPSHEETPAGEW *page;
2138     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
2139
2140     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2141
2142     switch (msg)
2143     {
2144     case WM_INITDIALOG:
2145         page = (PROPSHEETPAGEW *)lp;
2146         pCertViewInfo = (PCCRYPTUI_VIEWCERTIFICATE_STRUCTW)page->lParam;
2147         if (pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ADDTOSTORE)
2148             ShowWindow(GetDlgItem(hwnd, IDC_ADDTOSTORE), FALSE);
2149         EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), FALSE);
2150         set_general_info(hwnd, pCertViewInfo);
2151         break;
2152     case WM_COMMAND:
2153         switch (wp)
2154         {
2155         case IDC_ADDTOSTORE:
2156             CryptUIWizImport(0, hwnd, NULL, NULL, NULL);
2157             break;
2158         case IDC_ISSUERSTATEMENT:
2159         {
2160             struct IssuerStatement *issuerStatement =
2161              (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
2162
2163             if (issuerStatement)
2164             {
2165                 if (issuerStatement->userNotice)
2166                     show_user_notice(hwnd, issuerStatement);
2167                 else if (issuerStatement->cps)
2168                 {
2169                     IBindCtx *bctx = NULL;
2170
2171                     CreateBindCtx(0, &bctx);
2172                     HlinkSimpleNavigateToString(issuerStatement->cps, NULL,
2173                      NULL, NULL, bctx, NULL, HLNF_OPENINNEWWINDOW, 0);
2174                     IBindCtx_Release(bctx);
2175                 }
2176             }
2177             break;
2178         }
2179         }
2180         break;
2181     }
2182     return 0;
2183 }
2184
2185 static UINT CALLBACK general_callback_proc(HWND hwnd, UINT msg,
2186  PROPSHEETPAGEW *page)
2187 {
2188     struct IssuerStatement *issuerStatement;
2189
2190     switch (msg)
2191     {
2192     case PSPCB_RELEASE:
2193         issuerStatement =
2194          (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
2195         if (issuerStatement)
2196         {
2197             HeapFree(GetProcessHeap(), 0, issuerStatement->cps);
2198             HeapFree(GetProcessHeap(), 0, issuerStatement->userNotice);
2199             HeapFree(GetProcessHeap(), 0, issuerStatement);
2200         }
2201         break;
2202     }
2203     return 1;
2204 }
2205
2206 static void init_general_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2207  PROPSHEETPAGEW *page)
2208 {
2209     memset(page, 0, sizeof(PROPSHEETPAGEW));
2210     page->dwSize = sizeof(PROPSHEETPAGEW);
2211     page->dwFlags = PSP_USECALLBACK;
2212     page->pfnCallback = general_callback_proc;
2213     page->hInstance = hInstance;
2214     page->u.pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL);
2215     page->pfnDlgProc = general_dlg_proc;
2216     page->lParam = (LPARAM)pCertViewInfo;
2217 }
2218
2219 typedef WCHAR * (*field_format_func)(PCCERT_CONTEXT cert);
2220
2221 static WCHAR *field_format_version(PCCERT_CONTEXT cert)
2222 {
2223     static const WCHAR fmt[] = { 'V','%','d',0 };
2224     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, 12 * sizeof(WCHAR));
2225
2226     if (buf)
2227         sprintfW(buf, fmt, cert->pCertInfo->dwVersion);
2228     return buf;
2229 }
2230
2231 static WCHAR *format_hex_string(void *pb, DWORD cb)
2232 {
2233     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, (cb * 3 + 1) * sizeof(WCHAR));
2234
2235     if (buf)
2236     {
2237         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
2238         DWORD i;
2239         WCHAR *ptr;
2240
2241         for (i = 0, ptr = buf; i < cb; i++, ptr += 3)
2242             sprintfW(ptr, fmt, ((BYTE *)pb)[i]);
2243     }
2244     return buf;
2245 }
2246
2247 static WCHAR *field_format_serial_number(PCCERT_CONTEXT cert)
2248 {
2249     return format_hex_string(cert->pCertInfo->SerialNumber.pbData,
2250      cert->pCertInfo->SerialNumber.cbData);
2251 }
2252
2253 static WCHAR *field_format_issuer(PCCERT_CONTEXT cert)
2254 {
2255     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
2256      CERT_NAME_ISSUER_FLAG);
2257 }
2258
2259 static WCHAR *field_format_detailed_cert_name(PCERT_NAME_BLOB name)
2260 {
2261     WCHAR *str = NULL;
2262     DWORD len = CertNameToStrW(X509_ASN_ENCODING, name,
2263      CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2264
2265     if (len)
2266     {
2267         str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2268         if (str)
2269             CertNameToStrW(X509_ASN_ENCODING, name,
2270              CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, str, len);
2271     }
2272     return str;
2273 }
2274
2275 static WCHAR *field_format_detailed_issuer(PCCERT_CONTEXT cert, void *param)
2276 {
2277     return field_format_detailed_cert_name(&cert->pCertInfo->Issuer);
2278 }
2279
2280 static WCHAR *field_format_subject(PCCERT_CONTEXT cert)
2281 {
2282     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
2283 }
2284
2285 static WCHAR *field_format_detailed_subject(PCCERT_CONTEXT cert, void *param)
2286 {
2287     return field_format_detailed_cert_name(&cert->pCertInfo->Subject);
2288 }
2289
2290 static WCHAR *format_long_date(const FILETIME *fileTime)
2291 {
2292     WCHAR dateFmt[80]; /* long enough for LOCALE_SLONGDATE */
2293     DWORD len;
2294     WCHAR *buf = NULL;
2295     SYSTEMTIME sysTime;
2296
2297     /* FIXME: format isn't quite right, want time too */
2298     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SLONGDATE, dateFmt,
2299      sizeof(dateFmt) / sizeof(dateFmt[0]));
2300     FileTimeToSystemTime(fileTime, &sysTime);
2301     len = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, NULL, 0);
2302     if (len)
2303     {
2304         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2305         if (buf)
2306             GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, buf,
2307              len);
2308     }
2309     return buf;
2310 }
2311
2312 static WCHAR *field_format_from_date(PCCERT_CONTEXT cert)
2313 {
2314     return format_long_date(&cert->pCertInfo->NotBefore);
2315 }
2316
2317 static WCHAR *field_format_to_date(PCCERT_CONTEXT cert)
2318 {
2319     return format_long_date(&cert->pCertInfo->NotAfter);
2320 }
2321
2322 static WCHAR *field_format_public_key(PCCERT_CONTEXT cert)
2323 {
2324     PCCRYPT_OID_INFO oidInfo;
2325     WCHAR *buf = NULL;
2326
2327     oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2328      cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 0);
2329     if (oidInfo)
2330     {
2331         WCHAR fmt[MAX_STRING_LEN];
2332
2333         if (LoadStringW(hInstance, IDS_FIELD_PUBLIC_KEY_FORMAT, fmt,
2334          sizeof(fmt) / sizeof(fmt[0])))
2335         {
2336             /* Allocate the output buffer.  Use the number of bytes in the
2337              * public key as a conservative (high) estimate for the number of
2338              * digits in its output.
2339              * The output is of the form (in English)
2340              * "<public key algorithm> (<public key bit length> bits)".
2341              * Ordinarily having two positional parameters in a string is not a
2342              * good idea, but as this isn't a sentence fragment, it shouldn't
2343              * be word-order dependent.
2344              */
2345             buf = HeapAlloc(GetProcessHeap(), 0,
2346              (strlenW(fmt) + strlenW(oidInfo->pwszName) +
2347              cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData * 8)
2348              * sizeof(WCHAR));
2349             if (buf)
2350                 sprintfW(buf, fmt, oidInfo->pwszName,
2351                  CertGetPublicKeyLength(X509_ASN_ENCODING,
2352                   &cert->pCertInfo->SubjectPublicKeyInfo));
2353         }
2354     }
2355     return buf;
2356 }
2357
2358 static WCHAR *field_format_detailed_public_key(PCCERT_CONTEXT cert, void *param)
2359 {
2360     return format_hex_string(
2361      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
2362      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData);
2363 }
2364
2365 struct field_value_data;
2366 struct detail_data
2367 {
2368     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
2369     BOOL *pfPropertiesChanged;
2370     int cFields;
2371     struct field_value_data *fields;
2372 };
2373
2374 typedef void (*add_fields_func)(HWND hwnd, struct detail_data *data);
2375
2376 typedef WCHAR *(*create_detailed_value_func)(PCCERT_CONTEXT cert, void *param);
2377
2378 struct field_value_data
2379 {
2380     create_detailed_value_func create;
2381     LPWSTR detailed_value;
2382     void *param;
2383 };
2384
2385 static void add_field_value_data(struct detail_data *data,
2386  create_detailed_value_func create, void *param)
2387 {
2388     if (data->cFields)
2389         data->fields = HeapReAlloc(GetProcessHeap(), 0, data->fields,
2390          (data->cFields + 1) * sizeof(struct field_value_data));
2391     else
2392         data->fields = HeapAlloc(GetProcessHeap(), 0,
2393          sizeof(struct field_value_data));
2394     if (data->fields)
2395     {
2396         data->fields[data->cFields].create = create;
2397         data->fields[data->cFields].detailed_value = NULL;
2398         data->fields[data->cFields].param = param;
2399         data->cFields++;
2400     }
2401 }
2402
2403 static void add_field_and_value_to_list(HWND hwnd, struct detail_data *data,
2404  LPWSTR field, LPWSTR value, create_detailed_value_func create, void *param)
2405 {
2406     LVITEMW item;
2407     int iItem = SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0);
2408
2409     item.mask = LVIF_TEXT | LVIF_PARAM;
2410     item.iItem = iItem;
2411     item.iSubItem = 0;
2412     item.pszText = field;
2413     item.lParam = (LPARAM)data;
2414     SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item);
2415     if (value)
2416     {
2417         item.pszText = value;
2418         item.iSubItem = 1;
2419         SendMessageW(hwnd, LVM_SETITEMTEXTW, iItem, (LPARAM)&item);
2420     }
2421     add_field_value_data(data, create, param);
2422 }
2423
2424 static void add_string_id_and_value_to_list(HWND hwnd, struct detail_data *data,
2425  int id, LPWSTR value, create_detailed_value_func create, void *param)
2426 {
2427     WCHAR buf[MAX_STRING_LEN];
2428
2429     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
2430     add_field_and_value_to_list(hwnd, data, buf, value, create, param);
2431 }
2432
2433 struct v1_field
2434 {
2435     int id;
2436     field_format_func format;
2437     create_detailed_value_func create_detailed_value;
2438 };
2439
2440 static void add_v1_field(HWND hwnd, struct detail_data *data,
2441  const struct v1_field *field)
2442 {
2443     WCHAR *val = field->format(data->pCertViewInfo->pCertContext);
2444
2445     if (val)
2446     {
2447         add_string_id_and_value_to_list(hwnd, data, field->id, val,
2448          field->create_detailed_value, NULL);
2449         HeapFree(GetProcessHeap(), 0, val);
2450     }
2451 }
2452
2453 static const struct v1_field v1_fields[] = {
2454  { IDS_FIELD_VERSION, field_format_version, NULL },
2455  { IDS_FIELD_SERIAL_NUMBER, field_format_serial_number, NULL },
2456  { IDS_FIELD_ISSUER, field_format_issuer, field_format_detailed_issuer },
2457  { IDS_FIELD_VALID_FROM, field_format_from_date, NULL },
2458  { IDS_FIELD_VALID_TO, field_format_to_date, NULL },
2459  { IDS_FIELD_SUBJECT, field_format_subject, field_format_detailed_subject },
2460  { IDS_FIELD_PUBLIC_KEY, field_format_public_key,
2461    field_format_detailed_public_key }
2462 };
2463
2464 static void add_v1_fields(HWND hwnd, struct detail_data *data)
2465 {
2466     int i;
2467     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2468
2469     /* The last item in v1_fields is the public key, which is not in the loop
2470      * because it's a special case.
2471      */
2472     for (i = 0; i < sizeof(v1_fields) / sizeof(v1_fields[0]) - 1; i++)
2473         add_v1_field(hwnd, data, &v1_fields[i]);
2474     if (cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData)
2475         add_v1_field(hwnd, data, &v1_fields[i]);
2476 }
2477
2478 static WCHAR *crypt_format_extension(PCERT_EXTENSION ext, DWORD formatStrType)
2479 {
2480     WCHAR *str = NULL;
2481     DWORD size;
2482
2483     if (CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
2484      ext->pszObjId, ext->Value.pbData, ext->Value.cbData, NULL, &size))
2485     {
2486         str = HeapAlloc(GetProcessHeap(), 0, size);
2487         CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
2488          ext->pszObjId, ext->Value.pbData, ext->Value.cbData, str, &size);
2489     }
2490     return str;
2491 }
2492
2493 static WCHAR *field_format_extension_hex_with_ascii(PCERT_EXTENSION ext)
2494 {
2495     WCHAR *str = NULL;
2496
2497     if (ext->Value.cbData)
2498     {
2499         /* The output is formatted as:
2500          * <hex bytes>  <ascii bytes>\n
2501          * where <hex bytes> is a string of up to 8 bytes, output as %02x,
2502          * and <ascii bytes> is the ASCII equivalent of each byte, or '.' if
2503          * the byte is not printable.
2504          * So, for example, the extension value consisting of the following
2505          * bytes:
2506          *   0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
2507          *   0x13,0x09,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67
2508          * is output as:
2509          *   30 14 31 12 30 10 06 03  0.1.0...
2510          *   55 04 03 13 09 4a 75 61  U....Jua
2511          *   6e 20 4c 61 6e 67        n Lang
2512          * The allocation size therefore requires:
2513          * - 4 characters per character in an 8-byte line
2514          *   (2 for the hex format, one for the space, one for the ASCII value)
2515          * - 3 more characters per 8-byte line (two spaces and a newline)
2516          * - 1 character for the terminating nul
2517          * FIXME: should use a fixed-width font for this
2518          */
2519         DWORD lines = (ext->Value.cbData + 7) / 8;
2520
2521         str = HeapAlloc(GetProcessHeap(), 0,
2522          (lines * 8 * 4 + lines * 3 + 1) * sizeof(WCHAR));
2523         if (str)
2524         {
2525             static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
2526             DWORD i, j;
2527             WCHAR *ptr;
2528
2529             for (i = 0, ptr = str; i < ext->Value.cbData; i += 8)
2530             {
2531                 /* Output as hex bytes first */
2532                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr += 3)
2533                     sprintfW(ptr, fmt, ext->Value.pbData[j]);
2534                 /* Pad the hex output with spaces for alignment */
2535                 if (j == ext->Value.cbData && j % 8)
2536                 {
2537                     static const WCHAR pad[] = { ' ',' ',' ' };
2538
2539                     for (; j % 8; j++, ptr += sizeof(pad) / sizeof(pad[0]))
2540                         memcpy(ptr, pad, sizeof(pad));
2541                 }
2542                 /* The last sprintfW included a space, so just insert one
2543                  * more space between the hex bytes and the ASCII output
2544                  */
2545                 *ptr++ = ' ';
2546                 /* Output as ASCII bytes */
2547                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr++)
2548                 {
2549                     if (isprintW(ext->Value.pbData[j]) &&
2550                      !isspaceW(ext->Value.pbData[j]))
2551                         *ptr = ext->Value.pbData[j];
2552                     else
2553                         *ptr = '.';
2554                 }
2555                 *ptr++ = '\n';
2556             }
2557             *ptr++ = '\0';
2558         }
2559     }
2560     return str;
2561 }
2562
2563 static WCHAR *field_format_detailed_extension(PCCERT_CONTEXT cert, void *param)
2564 {
2565     PCERT_EXTENSION ext = param;
2566     LPWSTR str = crypt_format_extension(ext,
2567      CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX);
2568
2569     if (!str)
2570         str = field_format_extension_hex_with_ascii(ext);
2571     return str;
2572 }
2573
2574 static void add_cert_extension_detail(HWND hwnd, struct detail_data *data,
2575  PCERT_EXTENSION ext)
2576 {
2577     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2578      ext->pszObjId, 0);
2579     LPWSTR val = crypt_format_extension(ext, 0);
2580
2581     if (oidInfo)
2582         add_field_and_value_to_list(hwnd, data, (LPWSTR)oidInfo->pwszName,
2583          val, field_format_detailed_extension, ext);
2584     else
2585     {
2586         DWORD len = strlen(ext->pszObjId);
2587         LPWSTR oidW = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2588
2589         if (oidW)
2590         {
2591             DWORD i;
2592
2593             for (i = 0; i <= len; i++)
2594                 oidW[i] = ext->pszObjId[i];
2595             add_field_and_value_to_list(hwnd, data, oidW, val,
2596              field_format_detailed_extension, ext);
2597             HeapFree(GetProcessHeap(), 0, oidW);
2598         }
2599     }
2600     HeapFree(GetProcessHeap(), 0, val);
2601 }
2602
2603 static void add_all_extensions(HWND hwnd, struct detail_data *data)
2604 {
2605     DWORD i;
2606     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2607
2608     for (i = 0; i < cert->pCertInfo->cExtension; i++)
2609         add_cert_extension_detail(hwnd, data, &cert->pCertInfo->rgExtension[i]);
2610 }
2611
2612 static void add_critical_extensions(HWND hwnd, struct detail_data *data)
2613 {
2614     DWORD i;
2615     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2616
2617     for (i = 0; i < cert->pCertInfo->cExtension; i++)
2618         if (cert->pCertInfo->rgExtension[i].fCritical)
2619             add_cert_extension_detail(hwnd, data,
2620              &cert->pCertInfo->rgExtension[i]);
2621 }
2622
2623 typedef WCHAR * (*prop_to_value_func)(void *pb, DWORD cb);
2624
2625 struct prop_id_to_string_id
2626 {
2627     DWORD prop;
2628     int id;
2629     BOOL prop_is_string;
2630     prop_to_value_func prop_to_value;
2631 };
2632
2633 static WCHAR *format_enhanced_key_usage_value(void *pb, DWORD cb)
2634 {
2635     CERT_EXTENSION ext;
2636
2637     ext.pszObjId = (LPSTR)X509_ENHANCED_KEY_USAGE;
2638     ext.fCritical = FALSE;
2639     ext.Value.pbData = pb;
2640     ext.Value.cbData = cb;
2641     return crypt_format_extension(&ext, 0);
2642 }
2643
2644 /* Logically the access state should also be checked, and IDC_EDITPROPERTIES
2645  * disabled for read-only certificates, but native doesn't appear to do that.
2646  */
2647 static const struct prop_id_to_string_id prop_id_map[] = {
2648  { CERT_HASH_PROP_ID, IDS_PROP_HASH, FALSE, format_hex_string },
2649  { CERT_FRIENDLY_NAME_PROP_ID, IDS_PROP_FRIENDLY_NAME, TRUE, NULL },
2650  { CERT_DESCRIPTION_PROP_ID, IDS_PROP_DESCRIPTION, TRUE, NULL },
2651  { CERT_ENHKEY_USAGE_PROP_ID, IDS_PROP_ENHKEY_USAGE, FALSE,
2652    format_enhanced_key_usage_value },
2653 };
2654
2655 static void add_properties(HWND hwnd, struct detail_data *data)
2656 {
2657     DWORD i;
2658     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2659
2660     for (i = 0; i < sizeof(prop_id_map) / sizeof(prop_id_map[0]); i++)
2661     {
2662         DWORD cb;
2663
2664         if (CertGetCertificateContextProperty(cert, prop_id_map[i].prop, NULL,
2665          &cb))
2666         {
2667             BYTE *pb;
2668             WCHAR *val = NULL;
2669
2670             /* FIXME: MS adds a separate value for the signature hash
2671              * algorithm.
2672              */
2673             pb = HeapAlloc(GetProcessHeap(), 0, cb);
2674             if (pb)
2675             {
2676                 if (CertGetCertificateContextProperty(cert,
2677                  prop_id_map[i].prop, pb, &cb))
2678                 {
2679                     if (prop_id_map[i].prop_is_string)
2680                     {
2681                         val = (LPWSTR)pb;
2682                         /* Don't double-free pb */
2683                         pb = NULL;
2684                     }
2685                     else
2686                         val = prop_id_map[i].prop_to_value(pb, cb);
2687                 }
2688                 HeapFree(GetProcessHeap(), 0, pb);
2689             }
2690             add_string_id_and_value_to_list(hwnd, data, prop_id_map[i].id, val,
2691              NULL, NULL);
2692         }
2693     }
2694 }
2695
2696 static void add_all_fields(HWND hwnd, struct detail_data *data)
2697 {
2698     add_v1_fields(hwnd, data);
2699     add_all_extensions(hwnd, data);
2700     add_properties(hwnd, data);
2701 }
2702
2703 struct selection_list_item
2704 {
2705     int id;
2706     add_fields_func add;
2707 };
2708
2709 const struct selection_list_item listItems[] = {
2710  { IDS_FIELDS_ALL, add_all_fields },
2711  { IDS_FIELDS_V1, add_v1_fields },
2712  { IDS_FIELDS_EXTENSIONS, add_all_extensions },
2713  { IDS_FIELDS_CRITICAL_EXTENSIONS, add_critical_extensions },
2714  { IDS_FIELDS_PROPERTIES, add_properties },
2715 };
2716
2717 static void create_show_list(HWND hwnd, struct detail_data *data)
2718 {
2719     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
2720     WCHAR buf[MAX_STRING_LEN];
2721     int i;
2722
2723     for (i = 0; i < sizeof(listItems) / sizeof(listItems[0]); i++)
2724     {
2725         int index;
2726
2727         LoadStringW(hInstance, listItems[i].id, buf,
2728          sizeof(buf) / sizeof(buf[0]));
2729         index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
2730         SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)data);
2731     }
2732     SendMessageW(cb, CB_SETCURSEL, 0, 0);
2733 }
2734
2735 static void create_listview_columns(HWND hwnd)
2736 {
2737     HWND lv = GetDlgItem(hwnd, IDC_DETAIL_LIST);
2738     RECT rc;
2739     WCHAR buf[MAX_STRING_LEN];
2740     LVCOLUMNW column;
2741
2742     SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
2743     GetWindowRect(lv, &rc);
2744     LoadStringW(hInstance, IDS_FIELD, buf, sizeof(buf) / sizeof(buf[0]));
2745     column.mask = LVCF_WIDTH | LVCF_TEXT;
2746     column.cx = (rc.right - rc.left) / 2 - 2;
2747     column.pszText = buf;
2748     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
2749     LoadStringW(hInstance, IDS_VALUE, buf, sizeof(buf) / sizeof(buf[0]));
2750     SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
2751 }
2752
2753 static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel)
2754 {
2755     HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
2756
2757     if (sel >= 0 && sel < sizeof(listItems) / sizeof(listItems[0]))
2758     {
2759         SendMessageW(list, LVM_DELETEALLITEMS, 0, 0);
2760         listItems[sel].add(list, data);
2761     }
2762 }
2763
2764 static void create_cert_details_list(HWND hwnd, struct detail_data *data)
2765 {
2766     create_show_list(hwnd, data);
2767     create_listview_columns(hwnd);
2768     set_fields_selection(hwnd, data, 0);
2769 }
2770
2771 static void add_purpose(HWND hwnd, LPCSTR oid)
2772 {
2773     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
2774     PCRYPT_OID_INFO info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2775      sizeof(CRYPT_OID_INFO));
2776
2777     if (info)
2778     {
2779         char *oidCopy = HeapAlloc(GetProcessHeap(), 0, strlen(oid) + 1);
2780
2781         if (oidCopy)
2782         {
2783             LVITEMA item;
2784
2785             strcpy(oidCopy, oid);
2786             info->cbSize = sizeof(CRYPT_OID_INFO);
2787             info->pszOID = oidCopy;
2788             item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
2789             item.state = INDEXTOSTATEIMAGEMASK(CheckBitmapIndexChecked);
2790             item.stateMask = LVIS_STATEIMAGEMASK;
2791             item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
2792             item.iSubItem = 0;
2793             item.lParam = (LPARAM)info;
2794             item.pszText = oidCopy;
2795             SendMessageA(lv, LVM_INSERTITEMA, 0, (LPARAM)&item);
2796         }
2797         else
2798             HeapFree(GetProcessHeap(), 0, info);
2799     }
2800 }
2801
2802 static BOOL is_valid_oid(LPCSTR oid)
2803 {
2804     BOOL ret;
2805
2806     if (oid[0] != '0' && oid[0] != '1' && oid[0] != '2')
2807         ret = FALSE;
2808     else if (oid[1] != '.')
2809         ret = FALSE;
2810     else if (!oid[2])
2811         ret = FALSE;
2812     else
2813     {
2814         const char *ptr;
2815         BOOL expectNum = TRUE;
2816
2817         for (ptr = oid + 2, ret = TRUE; ret && *ptr; ptr++)
2818         {
2819             if (expectNum)
2820             {
2821                 if (!isdigit(*ptr))
2822                     ret = FALSE;
2823                 else if (*(ptr + 1) == '.')
2824                     expectNum = FALSE;
2825             }
2826             else
2827             {
2828                 if (*ptr != '.')
2829                     ret = FALSE;
2830                 else if (!(*(ptr + 1)))
2831                     ret = FALSE;
2832                 else
2833                     expectNum = TRUE;
2834             }
2835         }
2836     }
2837     return ret;
2838 }
2839
2840 static BOOL is_oid_in_list(HWND hwnd, LPCSTR oid)
2841 {
2842     return find_oid_in_list(GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES), oid)
2843      != -1;
2844 }
2845
2846 #define MAX_PURPOSE 255
2847
2848 static LRESULT CALLBACK add_purpose_dlg_proc(HWND hwnd, UINT msg,
2849  WPARAM wp, LPARAM lp)
2850 {
2851     LRESULT ret = 0;
2852     char buf[MAX_PURPOSE + 1];
2853
2854     switch (msg)
2855     {
2856     case WM_INITDIALOG:
2857         SendMessageW(GetDlgItem(hwnd, IDC_NEW_PURPOSE), EM_SETLIMITTEXT,
2858          MAX_PURPOSE, 0);
2859         ShowScrollBar(GetDlgItem(hwnd, IDC_NEW_PURPOSE), SB_VERT, FALSE);
2860         SetWindowLongPtrW(hwnd, DWLP_USER, lp);
2861         break;
2862     case WM_COMMAND:
2863         switch (HIWORD(wp))
2864         {
2865         case EN_CHANGE:
2866             if (LOWORD(wp) == IDC_NEW_PURPOSE)
2867             {
2868                 /* Show/hide scroll bar on description depending on how much
2869                  * text it has.
2870                  */
2871                 HWND description = GetDlgItem(hwnd, IDC_NEW_PURPOSE);
2872                 int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0);
2873
2874                 ShowScrollBar(description, SB_VERT, lines > 1);
2875             }
2876             break;
2877         case BN_CLICKED:
2878             switch (LOWORD(wp))
2879             {
2880             case IDOK:
2881                 SendMessageA(GetDlgItem(hwnd, IDC_NEW_PURPOSE), WM_GETTEXT,
2882                  sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
2883                 if (!buf[0])
2884                 {
2885                     /* An empty purpose is the same as cancelling */
2886                     EndDialog(hwnd, IDCANCEL);
2887                     ret = TRUE;
2888                 }
2889                 else if (!is_valid_oid(buf))
2890                 {
2891                     WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
2892
2893                     LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_ERROR, error,
2894                      sizeof(error) / sizeof(error[0]));
2895                     LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title,
2896                      sizeof(title) / sizeof(title[0]));
2897                     MessageBoxW(hwnd, error, title, MB_ICONERROR | MB_OK);
2898                 }
2899                 else if (is_oid_in_list(
2900                  (HWND)GetWindowLongPtrW(hwnd, DWLP_USER), buf))
2901                 {
2902                     WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
2903
2904                     LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_EXISTS,
2905                      error, sizeof(error) / sizeof(error[0]));
2906                     LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title,
2907                      sizeof(title) / sizeof(title[0]));
2908                     MessageBoxW(hwnd, error, title, MB_ICONEXCLAMATION | MB_OK);
2909                 }
2910                 else
2911                 {
2912                     HWND parent = (HWND)GetWindowLongPtrW(hwnd, DWLP_USER);
2913
2914                     add_purpose(parent, buf);
2915                     EndDialog(hwnd, wp);
2916                     ret = TRUE;
2917                 }
2918                 break;
2919             case IDCANCEL:
2920                 EndDialog(hwnd, wp);
2921                 ret = TRUE;
2922                 break;
2923             }
2924             break;
2925         }
2926         break;
2927     }
2928     return ret;
2929 }
2930
2931 static WCHAR *get_cert_property_as_string(PCCERT_CONTEXT cert, DWORD prop)
2932 {
2933     WCHAR *name = NULL;
2934     DWORD cb;
2935
2936     if (CertGetCertificateContextProperty(cert, prop, NULL, &cb))
2937     {
2938         name = HeapAlloc(GetProcessHeap(), 0, cb);
2939         if (name)
2940         {
2941             if (!CertGetCertificateContextProperty(cert, prop, (LPBYTE)name,
2942              &cb))
2943             {
2944                 HeapFree(GetProcessHeap(), 0, name);
2945                 name = NULL;
2946             }
2947         }
2948     }
2949     return name;
2950 }
2951
2952 static void redraw_states(HWND list, BOOL enabled)
2953 {
2954     int items = SendMessageW(list, LVM_GETITEMCOUNT, 0, 0), i;
2955
2956     for (i = 0; i < items; i++)
2957     {
2958         BOOL change = FALSE;
2959         int state;
2960
2961         state = SendMessageW(list, LVM_GETITEMSTATE, i, LVIS_STATEIMAGEMASK);
2962         /* This reverses the INDEXTOSTATEIMAGEMASK shift.  There doesn't appear
2963          * to be a handy macro for it.
2964          */
2965         state >>= 12;
2966         if (enabled)
2967         {
2968             if (state == CheckBitmapIndexDisabledChecked)
2969             {
2970                 state = CheckBitmapIndexChecked;
2971                 change = TRUE;
2972             }
2973             if (state == CheckBitmapIndexDisabledUnchecked)
2974             {
2975                 state = CheckBitmapIndexUnchecked;
2976                 change = TRUE;
2977             }
2978         }
2979         else
2980         {
2981             if (state == CheckBitmapIndexChecked)
2982             {
2983                 state = CheckBitmapIndexDisabledChecked;
2984                 change = TRUE;
2985             }
2986             if (state == CheckBitmapIndexUnchecked)
2987             {
2988                 state = CheckBitmapIndexDisabledUnchecked;
2989                 change = TRUE;
2990             }
2991         }
2992         if (change)
2993         {
2994             LVITEMW item;
2995
2996             item.state = INDEXTOSTATEIMAGEMASK(state);
2997             item.stateMask = LVIS_STATEIMAGEMASK;
2998             SendMessageW(list, LVM_SETITEMSTATE, i, (LPARAM)&item);
2999         }
3000     }
3001 }
3002
3003 typedef enum {
3004     PurposeEnableAll = 0,
3005     PurposeDisableAll,
3006     PurposeEnableSelected
3007 } PurposeSelection;
3008
3009 static void select_purposes(HWND hwnd, PurposeSelection selection)
3010 {
3011     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3012
3013     switch (selection)
3014     {
3015     case PurposeEnableAll:
3016     case PurposeDisableAll:
3017         EnableWindow(lv, FALSE);
3018         redraw_states(lv, FALSE);
3019         EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), FALSE);
3020         break;
3021     case PurposeEnableSelected:
3022         EnableWindow(lv, TRUE);
3023         redraw_states(lv, TRUE);
3024         EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), TRUE);
3025     }
3026 }
3027
3028 struct edit_cert_data
3029 {
3030     PCCERT_CONTEXT cert;
3031     BOOL *pfPropertiesChanged;
3032     HIMAGELIST imageList;
3033 };
3034
3035 static void show_cert_usages(HWND hwnd, struct edit_cert_data *data)
3036 {
3037     PCCERT_CONTEXT cert = data->cert;
3038     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3039     PCERT_ENHKEY_USAGE usage;
3040     DWORD size;
3041     RECT rc;
3042     LVCOLUMNW column;
3043     PurposeSelection purposeSelection;
3044
3045     GetWindowRect(lv, &rc);
3046     column.mask = LVCF_WIDTH;
3047     column.cx = rc.right - rc.left;
3048     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
3049     SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)data->imageList);
3050
3051     /* Get enhanced key usage.  Have to check for a property and an extension
3052      * separately, because CertGetEnhancedKeyUsage will succeed and return an
3053      * empty usage if neither is set.  Unfortunately an empty usage implies
3054      * no usage is allowed, so we have to distinguish between the two cases.
3055      */
3056     if (CertGetEnhancedKeyUsage(cert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
3057      NULL, &size))
3058     {
3059         usage = HeapAlloc(GetProcessHeap(), 0, size);
3060         if (!CertGetEnhancedKeyUsage(cert,
3061          CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
3062         {
3063             HeapFree(GetProcessHeap(), 0, usage);
3064             usage = NULL;
3065         }
3066         else if (usage->cUsageIdentifier)
3067             purposeSelection = PurposeEnableSelected;
3068         else
3069             purposeSelection = PurposeDisableAll;
3070     }
3071     else if (CertGetEnhancedKeyUsage(cert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
3072      NULL, &size))
3073     {
3074         usage = HeapAlloc(GetProcessHeap(), 0, size);
3075         if (!CertGetEnhancedKeyUsage(cert,
3076          CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
3077         {
3078             HeapFree(GetProcessHeap(), 0, usage);
3079             usage = NULL;
3080         }
3081         else if (usage->cUsageIdentifier)
3082             purposeSelection = PurposeEnableAll;
3083         else
3084             purposeSelection = PurposeDisableAll;
3085     }
3086     else
3087     {
3088         purposeSelection = PurposeEnableAll;
3089         usage = NULL;
3090     }
3091     if (usage)
3092     {
3093         DWORD i;
3094
3095         for (i = 0; i < usage->cUsageIdentifier; i++)
3096         {
3097             PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
3098              usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
3099
3100             if (info)
3101                 add_known_usage(lv, info, CheckBitmapIndexDisabledChecked);
3102             else
3103                 add_purpose(hwnd, usage->rgpszUsageIdentifier[i]);
3104         }
3105         HeapFree(GetProcessHeap(), 0, usage);
3106     }
3107     else
3108         add_known_usages_to_list(lv, CheckBitmapIndexDisabledChecked);
3109     select_purposes(hwnd, purposeSelection);
3110     SendMessageW(GetDlgItem(hwnd, IDC_ENABLE_ALL_PURPOSES + purposeSelection),
3111      BM_CLICK, 0, 0);
3112 }
3113
3114 static void set_general_cert_properties(HWND hwnd, struct edit_cert_data *data)
3115 {
3116     PCCERT_CONTEXT cert = data->cert;
3117     WCHAR *str;
3118
3119     if ((str = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID)))
3120     {
3121         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_SETTEXT, 0,
3122          (LPARAM)str);
3123         HeapFree(GetProcessHeap(), 0, str);
3124     }
3125     if ((str = get_cert_property_as_string(cert, CERT_DESCRIPTION_PROP_ID)))
3126     {
3127         SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_SETTEXT, 0,
3128          (LPARAM)str);
3129         HeapFree(GetProcessHeap(), 0, str);
3130     }
3131     show_cert_usages(hwnd, data);
3132 }
3133
3134 static void set_cert_string_property(PCCERT_CONTEXT cert, DWORD prop,
3135  LPWSTR str)
3136 {
3137     if (str && strlenW(str))
3138     {
3139         CRYPT_DATA_BLOB blob;
3140
3141         blob.pbData = (BYTE *)str;
3142         blob.cbData = (strlenW(str) + 1) * sizeof(WCHAR);
3143         CertSetCertificateContextProperty(cert, prop, 0, &blob);
3144     }
3145     else
3146         CertSetCertificateContextProperty(cert, prop, 0, NULL);
3147 }
3148
3149 #define WM_REFRESH_VIEW WM_USER + 0
3150
3151 static BOOL CALLBACK refresh_propsheet_pages(HWND hwnd, LPARAM lParam)
3152 {
3153     if ((GetClassLongW(hwnd, GCW_ATOM) == WC_DIALOG))
3154         SendMessageW(hwnd, WM_REFRESH_VIEW, 0, 0);
3155     return TRUE;
3156 }
3157
3158 #define MAX_FRIENDLY_NAME 40
3159 #define MAX_DESCRIPTION 255
3160
3161 static void apply_general_changes(HWND hwnd)
3162 {
3163     WCHAR buf[MAX_DESCRIPTION + 1];
3164     struct edit_cert_data *data =
3165      (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER);
3166
3167     SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_GETTEXT,
3168      sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
3169     set_cert_string_property(data->cert, CERT_FRIENDLY_NAME_PROP_ID, buf);
3170     SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_GETTEXT,
3171      sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
3172     set_cert_string_property(data->cert, CERT_DESCRIPTION_PROP_ID, buf);
3173     if (IsDlgButtonChecked(hwnd, IDC_ENABLE_ALL_PURPOSES))
3174     {
3175         /* Setting a NULL usage removes the enhanced key usage property. */
3176         CertSetEnhancedKeyUsage(data->cert, NULL);
3177     }
3178     else if (IsDlgButtonChecked(hwnd, IDC_DISABLE_ALL_PURPOSES))
3179     {
3180         CERT_ENHKEY_USAGE usage = { 0, NULL };
3181
3182         CertSetEnhancedKeyUsage(data->cert, &usage);
3183     }
3184     else if (IsDlgButtonChecked(hwnd, IDC_ENABLE_SELECTED_PURPOSES))
3185     {
3186         HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3187         CERT_ENHKEY_USAGE usage = { 0, NULL };
3188         int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
3189         LVITEMW item;
3190
3191         item.mask = LVIF_STATE | LVIF_PARAM;
3192         item.iSubItem = 0;
3193         item.stateMask = LVIS_STATEIMAGEMASK;
3194         for (i = 0; i < purposes; i++)
3195         {
3196             item.iItem = i;
3197             if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
3198             {
3199                 int state = item.state >> 12;
3200
3201                 if (state == CheckBitmapIndexChecked)
3202                 {
3203                     CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
3204
3205                     if (usage.cUsageIdentifier)
3206                         usage.rgpszUsageIdentifier =
3207                          HeapReAlloc(GetProcessHeap(), 0,
3208                          usage.rgpszUsageIdentifier,
3209                          (usage.cUsageIdentifier + 1) * sizeof(LPSTR));
3210                     else
3211                         usage.rgpszUsageIdentifier =
3212                          HeapAlloc(GetProcessHeap(), 0, sizeof(LPSTR));
3213                     if (usage.rgpszUsageIdentifier)
3214                         usage.rgpszUsageIdentifier[usage.cUsageIdentifier++] =
3215                          (LPSTR)info->pszOID;
3216                 }
3217             }
3218         }
3219         CertSetEnhancedKeyUsage(data->cert, &usage);
3220         HeapFree(GetProcessHeap(), 0, usage.rgpszUsageIdentifier);
3221     }
3222     EnumChildWindows(GetParent(GetParent(hwnd)), refresh_propsheet_pages, 0);
3223     if (data->pfPropertiesChanged)
3224         *data->pfPropertiesChanged = TRUE;
3225 }
3226
3227 static LRESULT CALLBACK cert_properties_general_dlg_proc(HWND hwnd, UINT msg,
3228  WPARAM wp, LPARAM lp)
3229 {
3230     PROPSHEETPAGEW *page;
3231
3232     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
3233
3234     switch (msg)
3235     {
3236     case WM_INITDIALOG:
3237     {
3238         HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
3239         struct detail_data *detailData;
3240         struct edit_cert_data *editData;
3241
3242         page = (PROPSHEETPAGEW *)lp;
3243         detailData = (struct detail_data *)page->lParam;
3244         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), EM_SETLIMITTEXT,
3245          MAX_FRIENDLY_NAME, 0);
3246         SendMessageW(description, EM_SETLIMITTEXT, MAX_DESCRIPTION, 0);
3247         ShowScrollBar(description, SB_VERT, FALSE);
3248         editData = HeapAlloc(GetProcessHeap(), 0,
3249          sizeof(struct edit_cert_data));
3250         if (editData)
3251         {
3252             editData->imageList = ImageList_Create(16, 16,
3253              ILC_COLOR4 | ILC_MASK, 4, 0);
3254             if (editData->imageList)
3255             {
3256                 HBITMAP bmp;
3257                 COLORREF backColor = RGB(255, 0, 255);
3258
3259                 bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS));
3260                 ImageList_AddMasked(editData->imageList, bmp, backColor);
3261                 DeleteObject(bmp);
3262                 ImageList_SetBkColor(editData->imageList, CLR_NONE);
3263             }
3264             editData->cert = detailData->pCertViewInfo->pCertContext;
3265             editData->pfPropertiesChanged = detailData->pfPropertiesChanged;
3266             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)editData);
3267             set_general_cert_properties(hwnd, editData);
3268         }
3269         break;
3270     }
3271     case WM_NOTIFY:
3272     {
3273         NMHDR *hdr = (NMHDR *)lp;
3274         NMITEMACTIVATE *nm;
3275
3276         switch (hdr->code)
3277         {
3278         case NM_CLICK:
3279             nm = (NMITEMACTIVATE *)lp;
3280             toggle_usage(hwnd, nm->iItem);
3281             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3282             break;
3283         case PSN_APPLY:
3284             apply_general_changes(hwnd);
3285             break;
3286         }
3287         break;
3288     }
3289     case WM_COMMAND:
3290         switch (HIWORD(wp))
3291         {
3292         case EN_CHANGE:
3293             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3294             if (LOWORD(wp) == IDC_DESCRIPTION)
3295             {
3296                 /* Show/hide scroll bar on description depending on how much
3297                  * text it has.
3298                  */
3299                 HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
3300                 int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0);
3301
3302                 ShowScrollBar(description, SB_VERT, lines > 1);
3303             }
3304             break;
3305         case BN_CLICKED:
3306             switch (LOWORD(wp))
3307             {
3308             case IDC_ADD_PURPOSE:
3309                 if (DialogBoxParamW(hInstance,
3310                  MAKEINTRESOURCEW(IDD_ADD_CERT_PURPOSE), hwnd,
3311                  add_purpose_dlg_proc, (LPARAM)hwnd) == IDOK)
3312                     SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3313                 break;
3314             case IDC_ENABLE_ALL_PURPOSES:
3315             case IDC_DISABLE_ALL_PURPOSES:
3316             case IDC_ENABLE_SELECTED_PURPOSES:
3317                 SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3318                 select_purposes(hwnd, LOWORD(wp) - IDC_ENABLE_ALL_PURPOSES);
3319                 break;
3320             }
3321             break;
3322         }
3323         break;
3324     }
3325     return 0;
3326 }
3327
3328 static UINT CALLBACK cert_properties_general_callback(HWND hwnd, UINT msg,
3329  PROPSHEETPAGEW *page)
3330 {
3331     HWND lv;
3332     int cItem, i;
3333     struct edit_cert_data *data;
3334
3335     switch (msg)
3336     {
3337     case PSPCB_RELEASE:
3338         lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3339         cItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
3340         for (i = 0; i < cItem; i++)
3341         {
3342             LVITEMW item;
3343
3344             item.mask = LVIF_PARAM;
3345             item.iItem = i;
3346             item.iSubItem = 0;
3347             if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item) && item.lParam)
3348             {
3349                 PCRYPT_OID_INFO info = (PCRYPT_OID_INFO)item.lParam;
3350
3351                 if (info->cbSize == sizeof(CRYPT_OID_INFO) && !info->dwGroupId)
3352                 {
3353                     HeapFree(GetProcessHeap(), 0, (LPSTR)info->pszOID);
3354                     HeapFree(GetProcessHeap(), 0, info);
3355                 }
3356             }
3357         }
3358         data = (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER);
3359         if (data)
3360         {
3361             ImageList_Destroy(data->imageList);
3362             HeapFree(GetProcessHeap(), 0, data);
3363         }
3364         break;
3365     }
3366     return 1;
3367 }
3368
3369 static void show_edit_cert_properties_dialog(HWND parent,
3370  struct detail_data *data)
3371 {
3372     PROPSHEETHEADERW hdr;
3373     PROPSHEETPAGEW page; /* FIXME: need to add a cross-certificate page */
3374
3375     TRACE("(%p)\n", data);
3376
3377     memset(&page, 0, sizeof(PROPSHEETPAGEW));
3378     page.dwSize = sizeof(page);
3379     page.dwFlags = PSP_USECALLBACK;
3380     page.pfnCallback = cert_properties_general_callback;
3381     page.hInstance = hInstance;
3382     page.u.pszTemplate = MAKEINTRESOURCEW(IDD_CERT_PROPERTIES_GENERAL);
3383     page.pfnDlgProc = cert_properties_general_dlg_proc;
3384     page.lParam = (LPARAM)data;
3385
3386     memset(&hdr, 0, sizeof(hdr));
3387     hdr.dwSize = sizeof(hdr);
3388     hdr.hwndParent = parent;
3389     hdr.dwFlags = PSH_PROPSHEETPAGE;
3390     hdr.hInstance = hInstance;
3391     hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE_PROPERTIES);
3392     hdr.u3.ppsp = &page;
3393     hdr.nPages = 1;
3394     PropertySheetW(&hdr);
3395 }
3396
3397 static void free_detail_fields(struct detail_data *data)
3398 {
3399     DWORD i;
3400
3401     for (i = 0; i < data->cFields; i++)
3402         HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value);
3403     HeapFree(GetProcessHeap(), 0, data->fields);
3404     data->fields = NULL;
3405     data->cFields = 0;
3406 }
3407
3408 static void refresh_details_view(HWND hwnd)
3409 {
3410     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
3411     int curSel;
3412     struct detail_data *data;
3413
3414     curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
3415     /* Actually, any index will do, since they all store the same data value */
3416     data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0);
3417     free_detail_fields(data);
3418     set_fields_selection(hwnd, data, curSel);
3419 }
3420
3421 static LRESULT CALLBACK detail_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
3422  LPARAM lp)
3423 {
3424     PROPSHEETPAGEW *page;
3425     struct detail_data *data;
3426
3427     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
3428
3429     switch (msg)
3430     {
3431     case WM_INITDIALOG:
3432         page = (PROPSHEETPAGEW *)lp;
3433         data = (struct detail_data *)page->lParam;
3434         create_cert_details_list(hwnd, data);
3435         if (!(data->pCertViewInfo->dwFlags & CRYPTUI_ENABLE_EDITPROPERTIES))
3436             EnableWindow(GetDlgItem(hwnd, IDC_EDITPROPERTIES), FALSE);
3437         if (data->pCertViewInfo->dwFlags & CRYPTUI_DISABLE_EXPORT)
3438             EnableWindow(GetDlgItem(hwnd, IDC_EXPORT), FALSE);
3439         break;
3440     case WM_NOTIFY:
3441     {
3442         NMITEMACTIVATE *nm;
3443         HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
3444
3445         nm = (NMITEMACTIVATE*)lp;
3446         if (nm->hdr.hwndFrom == list && nm->uNewState & LVN_ITEMACTIVATE
3447          && nm->hdr.code == LVN_ITEMCHANGED)
3448         {
3449             data = (struct detail_data *)nm->lParam;
3450             if (nm->iItem >= 0 && data && nm->iItem < data->cFields)
3451             {
3452                 WCHAR buf[MAX_STRING_LEN], *val = NULL;
3453                 HWND valueCtl = GetDlgItem(hwnd, IDC_DETAIL_VALUE);
3454
3455                 if (data->fields[nm->iItem].create)
3456                     val = data->fields[nm->iItem].create(
3457                      data->pCertViewInfo->pCertContext,
3458                      data->fields[nm->iItem].param);
3459                 else
3460                 {
3461                     LVITEMW item;
3462                     int res;
3463
3464                     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
3465                     item.mask = LVIF_TEXT;
3466                     item.pszText = buf;
3467                     item.iItem = nm->iItem;
3468                     item.iSubItem = 1;
3469                     res = SendMessageW(list, LVM_GETITEMW, 0, (LPARAM)&item);
3470                     if (res)
3471                         val = buf;
3472                 }
3473                 /* Select all the text in the control, the next update will
3474                  * replace it
3475                  */
3476                 SendMessageW(valueCtl, EM_SETSEL, 0, -1);
3477                 add_unformatted_text_to_control(valueCtl, val,
3478                  val ? strlenW(val) : 0);
3479                 if (val != buf)
3480                     HeapFree(GetProcessHeap(), 0, val);
3481             }
3482         }
3483         break;
3484     }
3485     case WM_COMMAND:
3486         switch (wp)
3487         {
3488         case IDC_EXPORT:
3489             FIXME("call CryptUIWizExport\n");
3490             break;
3491         case IDC_EDITPROPERTIES:
3492         {
3493             HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
3494             int curSel;
3495
3496             curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
3497             /* Actually, any index will do, since they all store the same
3498              * data value
3499              */
3500             data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA,
3501              curSel, 0);
3502             show_edit_cert_properties_dialog(GetParent(hwnd), data);
3503             break;
3504         }
3505         case ((CBN_SELCHANGE << 16) | IDC_DETAIL_SELECT):
3506             refresh_details_view(hwnd);
3507             break;
3508         }
3509         break;
3510     case WM_REFRESH_VIEW:
3511         refresh_details_view(hwnd);
3512         break;
3513     }
3514     return 0;
3515 }
3516
3517 static UINT CALLBACK detail_callback(HWND hwnd, UINT msg,
3518  PROPSHEETPAGEW *page)
3519 {
3520     struct detail_data *data;
3521
3522     switch (msg)
3523     {
3524     case PSPCB_RELEASE:
3525         data = (struct detail_data *)page->lParam;
3526         free_detail_fields(data);
3527         HeapFree(GetProcessHeap(), 0, data);
3528         break;
3529     }
3530     return 0;
3531 }
3532
3533 static BOOL init_detail_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
3534  BOOL *pfPropertiesChanged, PROPSHEETPAGEW *page)
3535 {
3536     BOOL ret;
3537     struct detail_data *data = HeapAlloc(GetProcessHeap(), 0,
3538      sizeof(struct detail_data));
3539
3540     if (data)
3541     {
3542         data->pCertViewInfo = pCertViewInfo;
3543         data->pfPropertiesChanged = pfPropertiesChanged;
3544         data->cFields = 0;
3545         data->fields = NULL;
3546         memset(page, 0, sizeof(PROPSHEETPAGEW));
3547         page->dwSize = sizeof(PROPSHEETPAGEW);
3548         page->dwFlags = PSP_USECALLBACK;
3549         page->pfnCallback = detail_callback;
3550         page->hInstance = hInstance;
3551         page->u.pszTemplate = MAKEINTRESOURCEW(IDD_DETAIL);
3552         page->pfnDlgProc = detail_dlg_proc;
3553         page->lParam = (LPARAM)data;
3554         ret = TRUE;
3555     }
3556     else
3557         ret = FALSE;
3558     return ret;
3559 }
3560
3561 struct hierarchy_data
3562 {
3563     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
3564     HIMAGELIST imageList;
3565     DWORD selectedCert;
3566 };
3567
3568 static LPARAM index_to_lparam(struct hierarchy_data *data, DWORD index)
3569 {
3570     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3571      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3572      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
3573      data->pCertViewInfo->idxCounterSigner);
3574
3575     /* Takes advantage of the fact that a pointer is 32-bit aligned, and
3576      * therefore always even.
3577      */
3578     if (index == provSigner->csCertChain - 1)
3579         return (LPARAM)data;
3580     return index << 1 | 1;
3581 }
3582
3583 static inline DWORD lparam_to_index(struct hierarchy_data *data, LPARAM lp)
3584 {
3585     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3586      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3587      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
3588      data->pCertViewInfo->idxCounterSigner);
3589
3590     if (!(lp & 1))
3591         return provSigner->csCertChain - 1;
3592     return lp >> 1;
3593 }
3594
3595 static struct hierarchy_data *get_hierarchy_data_from_tree_item(HWND tree,
3596  HTREEITEM hItem)
3597 {
3598     struct hierarchy_data *data = NULL;
3599     HTREEITEM root = NULL;
3600
3601     do {
3602         HTREEITEM parent = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM,
3603          TVGN_PARENT, (LPARAM)hItem);
3604
3605         if (!parent)
3606             root = hItem;
3607         hItem = parent;
3608     } while (hItem);
3609     if (root)
3610     {
3611         TVITEMW item;
3612
3613         item.mask = TVIF_PARAM;
3614         item.hItem = root;
3615         SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
3616         data = (struct hierarchy_data *)item.lParam;
3617     }
3618     return data;
3619 }
3620
3621 static WCHAR *get_cert_display_name(PCCERT_CONTEXT cert)
3622 {
3623     WCHAR *name = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID);
3624
3625     if (!name)
3626         name = get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
3627     return name;
3628 }
3629
3630 static void show_cert_chain(HWND hwnd, struct hierarchy_data *data)
3631 {
3632     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
3633     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3634      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3635      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
3636      data->pCertViewInfo->idxCounterSigner);
3637     DWORD i;
3638     HTREEITEM parent = NULL;
3639
3640     SendMessageW(tree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)data->imageList);
3641     for (i = provSigner->csCertChain; i; i--)
3642     {
3643         LPWSTR name;
3644
3645         name = get_cert_display_name(provSigner->pasCertChain[i - 1].pCert);
3646         if (name)
3647         {
3648             TVINSERTSTRUCTW tvis;
3649
3650             tvis.hParent = parent;
3651             tvis.hInsertAfter = TVI_LAST;
3652             tvis.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE |
3653              TVIF_SELECTEDIMAGE | TVIF_PARAM;
3654             tvis.u.item.pszText = name;
3655             tvis.u.item.state = TVIS_EXPANDED;
3656             tvis.u.item.stateMask = TVIS_EXPANDED;
3657             if (i == 1 &&
3658              (provSigner->pChainContext->TrustStatus.dwErrorStatus &
3659              CERT_TRUST_IS_PARTIAL_CHAIN))
3660             {
3661                 /* The root of the chain has a special case:  if the chain is
3662                  * a partial chain, the icon is a warning icon rather than an
3663                  * error icon.
3664                  */
3665                 tvis.u.item.iImage = 2;
3666             }
3667             else if (provSigner->pasCertChain[i - 1].pChainElement->TrustStatus.
3668              dwErrorStatus == 0)
3669                 tvis.u.item.iImage = 0;
3670             else
3671                 tvis.u.item.iImage = 1;
3672             tvis.u.item.iSelectedImage = tvis.u.item.iImage;
3673             tvis.u.item.lParam = index_to_lparam(data, i - 1);
3674             parent = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0,
3675              (LPARAM)&tvis);
3676             HeapFree(GetProcessHeap(), 0, name);
3677         }
3678     }
3679 }
3680
3681 static void set_certificate_status(HWND hwnd, CRYPT_PROVIDER_CERT *cert)
3682 {
3683     /* Select all the text in the control, the next update will replace it */
3684     SendMessageW(hwnd, EM_SETSEL, 0, -1);
3685     /* Set the highest priority error messages first. */
3686     if (!(cert->dwConfidence & CERT_CONFIDENCE_SIG))
3687         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_SIGNATURE);
3688     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIME))
3689         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIME);
3690     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIMENEST))
3691         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIMENEST);
3692     else if (cert->dwRevokedReason)
3693         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_REVOKED);
3694     else
3695         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_VALID);
3696 }
3697
3698 static void set_certificate_status_for_end_cert(HWND hwnd,
3699  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
3700 {
3701     HWND status = GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT);
3702     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3703      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
3704      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
3705      pCertViewInfo->idxCounterSigner);
3706     CRYPT_PROVIDER_CERT *provCert = WTHelperGetProvCertFromChain(provSigner,
3707      pCertViewInfo->idxCert);
3708
3709     set_certificate_status(status, provCert);
3710 }
3711
3712 static void show_cert_hierarchy(HWND hwnd, struct hierarchy_data *data)
3713 {
3714     /* Disable view certificate button until a certificate is selected */
3715     EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), FALSE);
3716     show_cert_chain(hwnd, data);
3717     set_certificate_status_for_end_cert(hwnd, data->pCertViewInfo);
3718 }
3719
3720 static void show_dialog_for_selected_cert(HWND hwnd)
3721 {
3722     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
3723     TVITEMW item;
3724     struct hierarchy_data *data;
3725     DWORD selection;
3726
3727     memset(&item, 0, sizeof(item));
3728     item.mask = TVIF_HANDLE | TVIF_PARAM;
3729     item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CARET,
3730      (LPARAM)NULL);
3731     SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
3732     data = get_hierarchy_data_from_tree_item(tree, item.hItem);
3733     selection = lparam_to_index(data, item.lParam);
3734     if (selection != 0)
3735     {
3736         CRYPT_PROVIDER_SGNR *provSigner;
3737         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
3738         BOOL changed = FALSE;
3739
3740         provSigner = WTHelperGetProvSignerFromChain(
3741          (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3742          data->pCertViewInfo->idxSigner,
3743          data->pCertViewInfo->fCounterSigner,
3744          data->pCertViewInfo->idxCounterSigner);
3745         memset(&viewInfo, 0, sizeof(viewInfo));
3746         viewInfo.dwSize = sizeof(viewInfo);
3747         viewInfo.dwFlags = data->pCertViewInfo->dwFlags;
3748         viewInfo.szTitle = data->pCertViewInfo->szTitle;
3749         viewInfo.pCertContext = provSigner->pasCertChain[selection].pCert;
3750         viewInfo.cStores = data->pCertViewInfo->cStores;
3751         viewInfo.rghStores = data->pCertViewInfo->rghStores;
3752         viewInfo.cPropSheetPages = data->pCertViewInfo->cPropSheetPages;
3753         viewInfo.rgPropSheetPages = data->pCertViewInfo->rgPropSheetPages;
3754         viewInfo.nStartPage = data->pCertViewInfo->nStartPage;
3755         CryptUIDlgViewCertificateW(&viewInfo, &changed);
3756         if (changed)
3757         {
3758             /* Delete the contents of the tree */
3759             SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
3760             /* Reinitialize the tree */
3761             show_cert_hierarchy(hwnd, data);
3762         }
3763     }
3764 }
3765
3766 static LRESULT CALLBACK hierarchy_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
3767  LPARAM lp)
3768 {
3769     PROPSHEETPAGEW *page;
3770     struct hierarchy_data *data;
3771     LRESULT ret = 0;
3772     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
3773
3774     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
3775
3776     switch (msg)
3777     {
3778     case WM_INITDIALOG:
3779         page = (PROPSHEETPAGEW *)lp;
3780         data = (struct hierarchy_data *)page->lParam;
3781         show_cert_hierarchy(hwnd, data);
3782         break;
3783     case WM_NOTIFY:
3784     {
3785         NMHDR *hdr;
3786
3787         hdr = (NMHDR *)lp;
3788         switch (hdr->code)
3789         {
3790         case TVN_SELCHANGEDW:
3791         {
3792             NMTREEVIEWW *nm = (NMTREEVIEWW*)lp;
3793             DWORD selection;
3794             CRYPT_PROVIDER_SGNR *provSigner;
3795
3796             data = get_hierarchy_data_from_tree_item(tree, nm->itemNew.hItem);
3797             selection = lparam_to_index(data, nm->itemNew.lParam);
3798             provSigner = WTHelperGetProvSignerFromChain(
3799              (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3800              data->pCertViewInfo->idxSigner,
3801              data->pCertViewInfo->fCounterSigner,
3802              data->pCertViewInfo->idxCounterSigner);
3803             EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), selection != 0);
3804             set_certificate_status(GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT),
3805              &provSigner->pasCertChain[selection]);
3806             break;
3807         }
3808         case NM_DBLCLK:
3809             show_dialog_for_selected_cert(hwnd);
3810             SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
3811             ret = 1;
3812             break;
3813         }
3814         break;
3815     }
3816     case WM_COMMAND:
3817         switch (wp)
3818         {
3819         case IDC_VIEWCERTIFICATE:
3820             show_dialog_for_selected_cert(hwnd);
3821             break;
3822         }
3823         break;
3824     case WM_REFRESH_VIEW:
3825     {
3826         TVITEMW item;
3827
3828         /* Get hierarchy data */
3829         memset(&item, 0, sizeof(item));
3830         item.mask = TVIF_HANDLE | TVIF_PARAM;
3831         item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_ROOT,
3832          (LPARAM)NULL);
3833         data = get_hierarchy_data_from_tree_item(tree, item.hItem);
3834         /* Delete the contents of the tree */
3835         SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
3836         /* Reinitialize the tree */
3837         show_cert_hierarchy(hwnd, data);
3838         break;
3839     }
3840     }
3841     return ret;
3842 }
3843
3844 static UINT CALLBACK hierarchy_callback(HWND hwnd, UINT msg,
3845  PROPSHEETPAGEW *page)
3846 {
3847     struct hierarchy_data *data;
3848
3849     switch (msg)
3850     {
3851     case PSPCB_RELEASE:
3852         data = (struct hierarchy_data *)page->lParam;
3853         ImageList_Destroy(data->imageList);
3854         HeapFree(GetProcessHeap(), 0, data);
3855         break;
3856     }
3857     return 0;
3858 }
3859
3860 static BOOL init_hierarchy_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
3861  PROPSHEETPAGEW *page)
3862 {
3863     struct hierarchy_data *data = HeapAlloc(GetProcessHeap(), 0,
3864      sizeof(struct hierarchy_data));
3865     BOOL ret = FALSE;
3866
3867     if (data)
3868     {
3869         data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0);
3870         if (data->imageList)
3871         {
3872             HBITMAP bmp;
3873             COLORREF backColor = RGB(255, 0, 255);
3874
3875             data->pCertViewInfo = pCertViewInfo;
3876             data->selectedCert = 0xffffffff;
3877
3878             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS));
3879             ImageList_AddMasked(data->imageList, bmp, backColor);
3880             DeleteObject(bmp);
3881             ImageList_SetBkColor(data->imageList, CLR_NONE);
3882
3883             memset(page, 0, sizeof(PROPSHEETPAGEW));
3884             page->dwSize = sizeof(PROPSHEETPAGEW);
3885             page->dwFlags = PSP_USECALLBACK;
3886             page->hInstance = hInstance;
3887             page->u.pszTemplate = MAKEINTRESOURCEW(IDD_HIERARCHY);
3888             page->pfnDlgProc = hierarchy_dlg_proc;
3889             page->lParam = (LPARAM)data;
3890             page->pfnCallback = hierarchy_callback;
3891             ret = TRUE;
3892         }
3893         else
3894             HeapFree(GetProcessHeap(), 0, data);
3895     }
3896     return ret;
3897 }
3898
3899 static int CALLBACK cert_prop_sheet_proc(HWND hwnd, UINT msg, LPARAM lp)
3900 {
3901     RECT rc;
3902     POINT topLeft;
3903
3904     TRACE("(%p, %08x, %08lx)\n", hwnd, msg, lp);
3905
3906     switch (msg)
3907     {
3908     case PSCB_INITIALIZED:
3909         /* Get cancel button's position.. */
3910         GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rc);
3911         topLeft.x = rc.left;
3912         topLeft.y = rc.top;
3913         ScreenToClient(hwnd, &topLeft);
3914         /* hide the cancel button.. */
3915         ShowWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
3916         /* get the OK button's size.. */
3917         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
3918         /* and move the OK button to the cancel button's original position. */
3919         MoveWindow(GetDlgItem(hwnd, IDOK), topLeft.x, topLeft.y,
3920          rc.right - rc.left, rc.bottom - rc.top, FALSE);
3921         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
3922         break;
3923     }
3924     return 0;
3925 }
3926
3927 static BOOL show_cert_dialog(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
3928  CRYPT_PROVIDER_CERT *provCert, BOOL *pfPropertiesChanged)
3929 {
3930     static const WCHAR riched[] = { 'r','i','c','h','e','d','2','0',0 };
3931     DWORD nPages;
3932     PROPSHEETPAGEW *pages;
3933     BOOL ret = FALSE;
3934     HMODULE lib = LoadLibraryW(riched);
3935
3936     nPages = pCertViewInfo->cPropSheetPages + 1; /* one for the General tab */
3937     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
3938         nPages++;
3939     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
3940         nPages++;
3941     pages = HeapAlloc(GetProcessHeap(), 0, nPages * sizeof(PROPSHEETPAGEW));
3942     if (pages)
3943     {
3944         PROPSHEETHEADERW hdr;
3945         CRYPTUI_INITDIALOG_STRUCT *init = NULL;
3946         DWORD i;
3947
3948         memset(&hdr, 0, sizeof(hdr));
3949         hdr.dwSize = sizeof(hdr);
3950         hdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK;
3951         hdr.hInstance = hInstance;
3952         if (pCertViewInfo->szTitle)
3953             hdr.pszCaption = pCertViewInfo->szTitle;
3954         else
3955             hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE);
3956         init_general_page(pCertViewInfo, &pages[hdr.nPages++]);
3957         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
3958         {
3959             if (init_detail_page(pCertViewInfo, pfPropertiesChanged,
3960              &pages[hdr.nPages]))
3961                 hdr.nPages++;
3962         }
3963         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
3964         {
3965             if (init_hierarchy_page(pCertViewInfo, &pages[hdr.nPages]))
3966                 hdr.nPages++;
3967         }
3968         /* Copy each additional page, and create the init dialog struct for it
3969          */
3970         if (pCertViewInfo->cPropSheetPages)
3971         {
3972             init = HeapAlloc(GetProcessHeap(), 0,
3973              pCertViewInfo->cPropSheetPages *
3974              sizeof(CRYPTUI_INITDIALOG_STRUCT));
3975             if (init)
3976             {
3977                 for (i = 0; i < pCertViewInfo->cPropSheetPages; i++)
3978                 {
3979                     memcpy(&pages[hdr.nPages + i],
3980                      &pCertViewInfo->rgPropSheetPages[i],
3981                      sizeof(PROPSHEETPAGEW));
3982                     init[i].lParam = pCertViewInfo->rgPropSheetPages[i].lParam;
3983                     init[i].pCertContext = pCertViewInfo->pCertContext;
3984                     pages[hdr.nPages + i].lParam = (LPARAM)&init[i];
3985                 }
3986                 if (pCertViewInfo->nStartPage & 0x8000)
3987                 {
3988                     /* Start page index is relative to the number of default
3989                      * pages
3990                      */
3991                     hdr.u2.nStartPage = pCertViewInfo->nStartPage + hdr.nPages;
3992                 }
3993                 else
3994                     hdr.u2.nStartPage = pCertViewInfo->nStartPage;
3995                 hdr.nPages = nPages;
3996                 ret = TRUE;
3997             }
3998             else
3999                 SetLastError(ERROR_OUTOFMEMORY);
4000         }
4001         else
4002         {
4003             /* Ignore the relative flag if there aren't any additional pages */
4004             hdr.u2.nStartPage = pCertViewInfo->nStartPage & 0x7fff;
4005             ret = TRUE;
4006         }
4007         if (ret)
4008         {
4009             INT_PTR l;
4010
4011             hdr.u3.ppsp = pages;
4012             hdr.pfnCallback = cert_prop_sheet_proc;
4013             l = PropertySheetW(&hdr);
4014             if (l == 0)
4015             {
4016                 SetLastError(ERROR_CANCELLED);
4017                 ret = FALSE;
4018             }
4019         }
4020         HeapFree(GetProcessHeap(), 0, init);
4021         HeapFree(GetProcessHeap(), 0, pages);
4022     }
4023     else
4024         SetLastError(ERROR_OUTOFMEMORY);
4025     FreeLibrary(lib);
4026     return ret;
4027 }
4028
4029 /***********************************************************************
4030  *              CryptUIDlgViewCertificateW (CRYPTUI.@)
4031  */
4032 BOOL WINAPI CryptUIDlgViewCertificateW(
4033  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, BOOL *pfPropertiesChanged)
4034 {
4035     static GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY;
4036     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
4037     WINTRUST_DATA wvt;
4038     WINTRUST_CERT_INFO cert;
4039     BOOL ret = FALSE;
4040     CRYPT_PROVIDER_SGNR *signer;
4041     CRYPT_PROVIDER_CERT *provCert = NULL;
4042
4043     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
4044
4045     if (pCertViewInfo->dwSize != sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))
4046     {
4047         SetLastError(ERROR_INVALID_PARAMETER);
4048         return FALSE;
4049     }
4050     /* Make a local copy in case we have to call WinVerifyTrust ourselves */
4051     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
4052     if (!viewInfo.u.hWVTStateData)
4053     {
4054         memset(&wvt, 0, sizeof(wvt));
4055         wvt.cbStruct = sizeof(wvt);
4056         wvt.dwUIChoice = WTD_UI_NONE;
4057         if (viewInfo.dwFlags &
4058          CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
4059             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
4060         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_END_CERT)
4061             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_END_CERT;
4062         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN)
4063             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN;
4064         wvt.dwUnionChoice = WTD_CHOICE_CERT;
4065         memset(&cert, 0, sizeof(cert));
4066         cert.cbStruct = sizeof(cert);
4067         cert.psCertContext = (CERT_CONTEXT *)viewInfo.pCertContext;
4068         cert.chStores = viewInfo.cStores;
4069         cert.pahStores = viewInfo.rghStores;
4070         wvt.u.pCert = &cert;
4071         wvt.dwStateAction = WTD_STATEACTION_VERIFY;
4072         WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
4073         viewInfo.u.pCryptProviderData =
4074          WTHelperProvDataFromStateData(wvt.hWVTStateData);
4075         signer = WTHelperGetProvSignerFromChain(
4076          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, 0, FALSE, 0);
4077         provCert = WTHelperGetProvCertFromChain(signer, 0);
4078         ret = TRUE;
4079     }
4080     else
4081     {
4082         viewInfo.u.pCryptProviderData =
4083          WTHelperProvDataFromStateData(viewInfo.u.hWVTStateData);
4084         signer = WTHelperGetProvSignerFromChain(
4085          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData,
4086          viewInfo.idxSigner, viewInfo.fCounterSigner,
4087          viewInfo.idxCounterSigner);
4088         provCert = WTHelperGetProvCertFromChain(signer, viewInfo.idxCert);
4089         ret = TRUE;
4090     }
4091     if (ret)
4092     {
4093         ret = show_cert_dialog(&viewInfo, provCert, pfPropertiesChanged);
4094         if (!viewInfo.u.hWVTStateData)
4095         {
4096             wvt.dwStateAction = WTD_STATEACTION_CLOSE;
4097             WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
4098         }
4099     }
4100     return ret;
4101 }
4102
4103 /***********************************************************************
4104  *              CryptUIDlgViewContext (CRYPTUI.@)
4105  */
4106 BOOL WINAPI CryptUIDlgViewContext(DWORD dwContextType, LPVOID pvContext,
4107  HWND hwnd, LPCWSTR pwszTitle, DWORD dwFlags, LPVOID pvReserved)
4108 {
4109     BOOL ret;
4110
4111     TRACE("(%d, %p, %p, %s, %08x, %p)\n", dwContextType, pvContext, hwnd,
4112      debugstr_w(pwszTitle), dwFlags, pvReserved);
4113
4114     switch (dwContextType)
4115     {
4116     case CERT_STORE_CERTIFICATE_CONTEXT:
4117     {
4118         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
4119
4120         memset(&viewInfo, 0, sizeof(viewInfo));
4121         viewInfo.dwSize = sizeof(viewInfo);
4122         viewInfo.hwndParent = hwnd;
4123         viewInfo.szTitle = pwszTitle;
4124         viewInfo.pCertContext = pvContext;
4125         ret = CryptUIDlgViewCertificateW(&viewInfo, NULL);
4126         break;
4127     }
4128     default:
4129         FIXME("unimplemented for context type %d\n", dwContextType);
4130         SetLastError(E_INVALIDARG);
4131         ret = FALSE;
4132     }
4133     return ret;
4134 }
4135
4136 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
4137  * or szOID_BASIC_CONSTRAINTS2, whichever is present) to determine if it
4138  * should be a CA.  If neither extension is present, returns
4139  * defaultIfNotSpecified.
4140  */
4141 static BOOL is_ca_cert(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified)
4142 {
4143     BOOL isCA = defaultIfNotSpecified;
4144     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
4145      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
4146
4147     if (ext)
4148     {
4149         CERT_BASIC_CONSTRAINTS_INFO *info;
4150         DWORD size = 0;
4151
4152         if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
4153          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
4154          NULL, (LPBYTE)&info, &size))
4155         {
4156             if (info->SubjectType.cbData == 1)
4157                 isCA = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
4158             LocalFree(info);
4159         }
4160     }
4161     else
4162     {
4163         ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
4164          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
4165         if (ext)
4166         {
4167             CERT_BASIC_CONSTRAINTS2_INFO info;
4168             DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
4169
4170             if (CryptDecodeObjectEx(X509_ASN_ENCODING,
4171              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
4172              0, NULL, &info, &size))
4173                 isCA = info.fCA;
4174         }
4175     }
4176     return isCA;
4177 }
4178
4179 static HCERTSTORE choose_store_for_cert(PCCERT_CONTEXT cert)
4180 {
4181     LPCWSTR storeName;
4182
4183     if (is_ca_cert(cert, TRUE))
4184         storeName = ca;
4185     else
4186         storeName = addressBook;
4187     return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
4188      CERT_SYSTEM_STORE_CURRENT_USER, storeName);
4189 }
4190
4191 static BOOL import_cert(PCCERT_CONTEXT cert, HCERTSTORE hDestCertStore)
4192 {
4193     HCERTSTORE store;
4194     BOOL ret;
4195
4196     if (!cert)
4197     {
4198         SetLastError(E_INVALIDARG);
4199         return FALSE;
4200     }
4201     if (hDestCertStore) store = hDestCertStore;
4202     else
4203     {
4204         if (!(store = choose_store_for_cert(cert)))
4205         {
4206             WARN("unable to open certificate store\n");
4207             return FALSE;
4208         }
4209     }
4210     ret = CertAddCertificateContextToStore(store, cert,
4211      CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL);
4212     if (!hDestCertStore) CertCloseStore(store, 0);
4213     return ret;
4214 }
4215
4216 static BOOL import_crl(PCCRL_CONTEXT crl, HCERTSTORE hDestCertStore)
4217 {
4218     HCERTSTORE store;
4219     BOOL ret;
4220
4221     if (!crl)
4222     {
4223         SetLastError(E_INVALIDARG);
4224         return FALSE;
4225     }
4226     if (hDestCertStore) store = hDestCertStore;
4227     else
4228     {
4229         if (!(store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
4230          CERT_SYSTEM_STORE_CURRENT_USER, ca)))
4231         {
4232             WARN("unable to open certificate store\n");
4233             return FALSE;
4234         }
4235     }
4236     ret = CertAddCRLContextToStore(store, crl,
4237      CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL);
4238     if (!hDestCertStore) CertCloseStore(store, 0);
4239     return ret;
4240 }
4241
4242 static BOOL import_ctl(PCCTL_CONTEXT ctl, HCERTSTORE hDestCertStore)
4243 {
4244     HCERTSTORE store;
4245     BOOL ret;
4246
4247     if (!ctl)
4248     {
4249         SetLastError(E_INVALIDARG);
4250         return FALSE;
4251     }
4252     if (hDestCertStore) store = hDestCertStore;
4253     else
4254     {
4255         static const WCHAR trust[] = { 'T','r','u','s','t',0 };
4256
4257         if (!(store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
4258          CERT_SYSTEM_STORE_CURRENT_USER, trust)))
4259         {
4260             WARN("unable to open certificate store\n");
4261             return FALSE;
4262         }
4263     }
4264     ret = CertAddCTLContextToStore(store, ctl,
4265      CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL);
4266     if (!hDestCertStore) CertCloseStore(store, 0);
4267     return ret;
4268 }
4269
4270 /* Checks type, a type such as CERT_QUERY_CONTENT_CERT returned by
4271  * CryptQueryObject, against the allowed types.  Returns TRUE if the
4272  * type is allowed, FALSE otherwise.
4273  */
4274 static BOOL check_context_type(DWORD dwFlags, DWORD type)
4275 {
4276     BOOL ret;
4277
4278     if (dwFlags &
4279      (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL |
4280      CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4281     {
4282         switch (type)
4283         {
4284         case CERT_QUERY_CONTENT_CERT:
4285         case CERT_QUERY_CONTENT_SERIALIZED_CERT:
4286             ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT;
4287             break;
4288         case CERT_QUERY_CONTENT_CRL:
4289         case CERT_QUERY_CONTENT_SERIALIZED_CRL:
4290             ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL;
4291             break;
4292         case CERT_QUERY_CONTENT_CTL:
4293         case CERT_QUERY_CONTENT_SERIALIZED_CTL:
4294             ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL;
4295             break;
4296         default:
4297             /* The remaining types contain more than one type, so allow
4298              * any combination.
4299              */
4300             ret = TRUE;
4301         }
4302     }
4303     else
4304     {
4305         /* No allowed types specified, so any type is allowed */
4306         ret = TRUE;
4307     }
4308     if (!ret)
4309         SetLastError(E_INVALIDARG);
4310     return ret;
4311 }
4312
4313
4314 static void import_warning(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle,
4315  int warningID)
4316 {
4317     if (!(dwFlags & CRYPTUI_WIZ_NO_UI))
4318     {
4319         WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
4320         LPCWSTR pTitle;
4321
4322         if (szTitle)
4323             pTitle = szTitle;
4324         else
4325         {
4326             LoadStringW(hInstance, IDS_IMPORT_WIZARD, title,
4327              sizeof(title) / sizeof(title[0]));
4328             pTitle = title;
4329         }
4330         LoadStringW(hInstance, warningID, error,
4331          sizeof(error) / sizeof(error[0]));
4332         MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK);
4333     }
4334 }
4335
4336 static void import_warn_type_mismatch(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle)
4337 {
4338     import_warning(dwFlags, hwnd, szTitle, IDS_IMPORT_TYPE_MISMATCH);
4339 }
4340
4341 static BOOL check_store_context_type(DWORD dwFlags, HCERTSTORE store)
4342 {
4343     BOOL ret;
4344
4345     if (dwFlags &
4346      (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL |
4347      CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4348     {
4349         PCCERT_CONTEXT cert;
4350         PCCRL_CONTEXT crl;
4351         PCCTL_CONTEXT ctl;
4352
4353         ret = TRUE;
4354         if ((cert = CertEnumCertificatesInStore(store, NULL)))
4355         {
4356             CertFreeCertificateContext(cert);
4357             if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT))
4358                 ret = FALSE;
4359         }
4360         if (ret && (crl = CertEnumCRLsInStore(store, NULL)))
4361         {
4362             CertFreeCRLContext(crl);
4363             if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL))
4364                 ret = FALSE;
4365         }
4366         if (ret && (ctl = CertEnumCTLsInStore(store, NULL)))
4367         {
4368             CertFreeCTLContext(ctl);
4369             if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4370                 ret = FALSE;
4371         }
4372     }
4373     else
4374         ret = TRUE;
4375     if (!ret)
4376         SetLastError(E_INVALIDARG);
4377     return ret;
4378 }
4379
4380 static BOOL import_store(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle,
4381  HCERTSTORE source, HCERTSTORE dest)
4382 {
4383     BOOL ret;
4384
4385     if ((ret = check_store_context_type(dwFlags, source)))
4386     {
4387         PCCERT_CONTEXT cert = NULL;
4388         PCCRL_CONTEXT crl = NULL;
4389         PCCTL_CONTEXT ctl = NULL;
4390
4391         do {
4392             cert = CertEnumCertificatesInStore(source, cert);
4393             if (cert)
4394                 ret = import_cert(cert, dest);
4395         } while (ret && cert);
4396         do {
4397             crl = CertEnumCRLsInStore(source, crl);
4398             if (crl)
4399                 ret = import_crl(crl, dest);
4400         } while (ret && crl);
4401         do {
4402             ctl = CertEnumCTLsInStore(source, ctl);
4403             if (ctl)
4404                 ret = import_ctl(ctl, dest);
4405         } while (ret && ctl);
4406     }
4407     else
4408         import_warn_type_mismatch(dwFlags, hwnd, szTitle);
4409     return ret;
4410 }
4411
4412 static HCERTSTORE open_store_from_file(DWORD dwFlags, LPCWSTR fileName,
4413  DWORD *pContentType)
4414 {
4415     HCERTSTORE store = NULL;
4416     DWORD contentType = 0, expectedContentTypeFlags;
4417
4418     if (dwFlags &
4419      (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL |
4420      CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4421     {
4422         expectedContentTypeFlags =
4423          CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
4424          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
4425          CERT_QUERY_CONTENT_FLAG_PFX;
4426         if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT)
4427             expectedContentTypeFlags |=
4428              CERT_QUERY_CONTENT_FLAG_CERT |
4429              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT;
4430         if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL)
4431             expectedContentTypeFlags |=
4432              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
4433              CERT_QUERY_CONTENT_FLAG_CRL;
4434         if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL)
4435             expectedContentTypeFlags |=
4436              CERT_QUERY_CONTENT_FLAG_CTL |
4437              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL;
4438     }
4439     else
4440         expectedContentTypeFlags =
4441          CERT_QUERY_CONTENT_FLAG_CERT |
4442          CERT_QUERY_CONTENT_FLAG_CTL |
4443          CERT_QUERY_CONTENT_FLAG_CRL |
4444          CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
4445          CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
4446          CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
4447          CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
4448          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
4449          CERT_QUERY_CONTENT_FLAG_PFX;
4450
4451     CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName,
4452      expectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL,
4453      &contentType, NULL, &store, NULL, NULL);
4454     if (pContentType)
4455         *pContentType = contentType;
4456     return store;
4457 }
4458
4459 static BOOL import_file(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle,
4460  LPCWSTR fileName, HCERTSTORE dest)
4461 {
4462     HCERTSTORE source;
4463     BOOL ret;
4464
4465     if ((source = open_store_from_file(dwFlags, fileName, NULL)))
4466     {
4467         ret = import_store(dwFlags, hwnd, szTitle, source, dest);
4468         CertCloseStore(source, 0);
4469     }
4470     else
4471         ret = FALSE;
4472     return ret;
4473 }
4474
4475 struct ImportWizData
4476 {
4477     HFONT titleFont;
4478     DWORD dwFlags;
4479     LPCWSTR pwszWizardTitle;
4480     CRYPTUI_WIZ_IMPORT_SRC_INFO importSrc;
4481     LPWSTR fileName;
4482     DWORD contentType;
4483     BOOL freeSource;
4484     HCERTSTORE hDestCertStore;
4485     BOOL freeDest;
4486     BOOL autoDest;
4487     BOOL success;
4488 };
4489
4490 static LRESULT CALLBACK import_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4491  LPARAM lp)
4492 {
4493     LRESULT ret = 0;
4494
4495     switch (msg)
4496     {
4497     case WM_INITDIALOG:
4498     {
4499         struct ImportWizData *data;
4500         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
4501         WCHAR fontFace[MAX_STRING_LEN];
4502         HDC hDC = GetDC(hwnd);
4503         int height;
4504
4505         data = (struct ImportWizData *)page->lParam;
4506         LoadStringW(hInstance, IDS_WIZARD_TITLE_FONT, fontFace,
4507          sizeof(fontFace) / sizeof(fontFace[0]));
4508         height = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
4509         data->titleFont = CreateFontW(height, 0, 0, 0, FW_BOLD, 0, 0, 0,
4510          DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
4511          DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontFace);
4512         SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_TITLE), WM_SETFONT,
4513          (WPARAM)data->titleFont, TRUE);
4514         ReleaseDC(hwnd, hDC);
4515         break;
4516     }
4517     case WM_NOTIFY:
4518     {
4519         NMHDR *hdr = (NMHDR *)lp;
4520
4521         switch (hdr->code)
4522         {
4523         case PSN_SETACTIVE:
4524             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, PSWIZB_NEXT);
4525             ret = TRUE;
4526             break;
4527         }
4528         break;
4529     }
4530     }
4531     return ret;
4532 }
4533
4534 static const WCHAR filter_cert[] = { '*','.','c','e','r',';','*','.',
4535  'c','r','t',0 };
4536 static const WCHAR filter_pfx[] = { '*','.','p','f','x',';','*','.',
4537  'p','1','2',0 };
4538 static const WCHAR filter_crl[] = { '*','.','c','r','l',0 };
4539 static const WCHAR filter_ctl[] = { '*','.','s','t','l',0 };
4540 static const WCHAR filter_serialized_store[] = { '*','.','s','s','t',0 };
4541 static const WCHAR filter_cms[] = { '*','.','s','p','c',';','*','.',
4542  'p','7','b',0 };
4543 static const WCHAR filter_all[] = { '*','.','*',0 };
4544
4545 struct StringToFilter
4546 {
4547     int     id;
4548     DWORD   allowFlags;
4549     LPCWSTR filter;
4550 } import_filters[] = {
4551  { IDS_IMPORT_FILTER_CERT, CRYPTUI_WIZ_IMPORT_ALLOW_CERT, filter_cert },
4552  { IDS_IMPORT_FILTER_PFX, 0, filter_pfx },
4553  { IDS_IMPORT_FILTER_CRL, CRYPTUI_WIZ_IMPORT_ALLOW_CRL, filter_crl },
4554  { IDS_IMPORT_FILTER_CTL, CRYPTUI_WIZ_IMPORT_ALLOW_CTL, filter_ctl },
4555  { IDS_IMPORT_FILTER_SERIALIZED_STORE, 0, filter_serialized_store },
4556  { IDS_IMPORT_FILTER_CMS, 0, filter_cms },
4557  { IDS_IMPORT_FILTER_ALL, 0, filter_all },
4558 };
4559
4560 static WCHAR *make_import_file_filter(DWORD dwFlags)
4561 {
4562     DWORD i;
4563     int len, totalLen = 2;
4564     LPWSTR filter = NULL, str;
4565
4566     for (i = 0; i < sizeof(import_filters) / sizeof(import_filters[0]); i++)
4567     {
4568         if (!import_filters[i].allowFlags || !dwFlags ||
4569          (dwFlags & import_filters[i].allowFlags))
4570         {
4571             len = LoadStringW(hInstance, import_filters[i].id, (LPWSTR)&str, 0);
4572             totalLen += len + strlenW(import_filters[i].filter) + 2;
4573         }
4574     }
4575     filter = HeapAlloc(GetProcessHeap(), 0, totalLen * sizeof(WCHAR));
4576     if (filter)
4577     {
4578         LPWSTR ptr;
4579
4580         ptr = filter;
4581         for (i = 0; i < sizeof(import_filters) / sizeof(import_filters[0]); i++)
4582         {
4583             if (!import_filters[i].allowFlags || !dwFlags ||
4584              (dwFlags & import_filters[i].allowFlags))
4585             {
4586                 len = LoadStringW(hInstance, import_filters[i].id,
4587                  (LPWSTR)&str, 0);
4588                 memcpy(ptr, str, len * sizeof(WCHAR));
4589                 ptr += len;
4590                 *ptr++ = 0;
4591                 strcpyW(ptr, import_filters[i].filter);
4592                 ptr += strlenW(import_filters[i].filter) + 1;
4593             }
4594         }
4595         *ptr++ = 0;
4596     }
4597     return filter;
4598 }
4599
4600 static BOOL import_validate_filename(HWND hwnd, struct ImportWizData *data,
4601  LPCWSTR fileName)
4602 {
4603     HANDLE file;
4604     BOOL ret = FALSE;
4605
4606     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
4607      OPEN_EXISTING, 0, NULL);
4608     if (file != INVALID_HANDLE_VALUE)
4609     {
4610         HCERTSTORE source = open_store_from_file(data->dwFlags, fileName,
4611          &data->contentType);
4612         int warningID = 0;
4613
4614         if (!source)
4615             warningID = IDS_IMPORT_BAD_FORMAT;
4616         else if (!check_store_context_type(data->dwFlags, source))
4617             warningID = IDS_IMPORT_TYPE_MISMATCH;
4618         else
4619         {
4620             data->importSrc.dwSubjectChoice =
4621              CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE;
4622             data->importSrc.u.hCertStore = source;
4623             data->freeSource = TRUE;
4624             ret = TRUE;
4625         }
4626         if (warningID)
4627         {
4628             import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
4629              warningID);
4630         }
4631         CloseHandle(file);
4632     }
4633     else
4634     {
4635         WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
4636         LPCWSTR pTitle;
4637         LPWSTR msgBuf, fullError;
4638
4639         if (data->pwszWizardTitle)
4640             pTitle = data->pwszWizardTitle;
4641         else
4642         {
4643             LoadStringW(hInstance, IDS_IMPORT_WIZARD, title,
4644              sizeof(title) / sizeof(title[0]));
4645             pTitle = title;
4646         }
4647         LoadStringW(hInstance, IDS_IMPORT_OPEN_FAILED, error,
4648          sizeof(error) / sizeof(error[0]));
4649         FormatMessageW(
4650          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
4651          GetLastError(), 0, (LPWSTR) &msgBuf, 0, NULL);
4652         fullError = HeapAlloc(GetProcessHeap(), 0,
4653          (strlenW(error) + strlenW(fileName) + strlenW(msgBuf) + 3)
4654          * sizeof(WCHAR));
4655         if (fullError)
4656         {
4657             LPWSTR ptr = fullError;
4658
4659             strcpyW(ptr, error);
4660             ptr += strlenW(error);
4661             strcpyW(ptr, fileName);
4662             ptr += strlenW(fileName);
4663             *ptr++ = ':';
4664             *ptr++ = '\n';
4665             strcpyW(ptr, msgBuf);
4666             MessageBoxW(hwnd, fullError, pTitle, MB_ICONERROR | MB_OK);
4667             HeapFree(GetProcessHeap(), 0, fullError);
4668         }
4669         LocalFree(msgBuf);
4670     }
4671     return ret;
4672 }
4673
4674 static LRESULT CALLBACK import_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4675  LPARAM lp)
4676 {
4677     LRESULT ret = 0;
4678     struct ImportWizData *data;
4679
4680     switch (msg)
4681     {
4682     case WM_INITDIALOG:
4683     {
4684         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
4685
4686         data = (struct ImportWizData *)page->lParam;
4687         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
4688         break;
4689     }
4690     case WM_NOTIFY:
4691     {
4692         NMHDR *hdr = (NMHDR *)lp;
4693
4694         switch (hdr->code)
4695         {
4696         case PSN_SETACTIVE:
4697             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
4698              PSWIZB_BACK | PSWIZB_NEXT);
4699             ret = TRUE;
4700             break;
4701         case PSN_WIZNEXT:
4702         {
4703             HWND fileNameEdit = GetDlgItem(hwnd, IDC_IMPORT_FILENAME);
4704             DWORD len = SendMessageW(fileNameEdit, WM_GETTEXTLENGTH, 0, 0);
4705
4706             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4707             if (!len)
4708             {
4709                 import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
4710                  IDS_IMPORT_EMPTY_FILE);
4711                 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
4712                 ret = 1;
4713             }
4714             else
4715             {
4716                 LPWSTR fileName = HeapAlloc(GetProcessHeap(), 0,
4717                  (len + 1) * sizeof(WCHAR));
4718
4719                 if (fileName)
4720                 {
4721                     SendMessageW(fileNameEdit, WM_GETTEXT, len + 1,
4722                      (LPARAM)fileName);
4723                     if (!import_validate_filename(hwnd, data, fileName))
4724                     {
4725                         HeapFree(GetProcessHeap(), 0, fileName);
4726                         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
4727                         ret = 1;
4728                     }
4729                     else
4730                         data->fileName = fileName;
4731                 }
4732             }
4733             break;
4734         }
4735         }
4736         break;
4737     }
4738     case WM_COMMAND:
4739         switch (wp)
4740         {
4741         case IDC_IMPORT_BROWSE_FILE:
4742         {
4743             OPENFILENAMEW ofn;
4744             WCHAR fileBuf[MAX_PATH];
4745
4746             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4747             memset(&ofn, 0, sizeof(ofn));
4748             ofn.lStructSize = sizeof(ofn);
4749             ofn.hwndOwner = hwnd;
4750             ofn.lpstrFilter = make_import_file_filter(data->dwFlags);
4751             ofn.lpstrFile = fileBuf;
4752             ofn.nMaxFile = sizeof(fileBuf) / sizeof(fileBuf[0]);
4753             fileBuf[0] = 0;
4754             if (GetOpenFileNameW(&ofn))
4755                 SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_FILENAME), WM_SETTEXT,
4756                  0, (LPARAM)ofn.lpstrFile);
4757             HeapFree(GetProcessHeap(), 0, (LPWSTR)ofn.lpstrFilter);
4758             break;
4759         }
4760         }
4761         break;
4762     }
4763     return ret;
4764 }
4765
4766 static LRESULT CALLBACK import_store_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4767  LPARAM lp)
4768 {
4769     LRESULT ret = 0;
4770     struct ImportWizData *data;
4771
4772     switch (msg)
4773     {
4774     case WM_INITDIALOG:
4775     {
4776         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
4777
4778         data = (struct ImportWizData *)page->lParam;
4779         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
4780         SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_AUTO_STORE), BM_CLICK, 0, 0);
4781         if (data->dwFlags & CRYPTUI_WIZ_IMPORT_NO_CHANGE_DEST_STORE)
4782             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE), FALSE);
4783         EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), FALSE);
4784         EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), FALSE);
4785         break;
4786     }
4787     case WM_NOTIFY:
4788     {
4789         NMHDR *hdr = (NMHDR *)lp;
4790
4791         switch (hdr->code)
4792         {
4793         case PSN_SETACTIVE:
4794             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
4795              PSWIZB_BACK | PSWIZB_NEXT);
4796             ret = TRUE;
4797             break;
4798         case PSN_WIZNEXT:
4799         {
4800             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4801             if (IsDlgButtonChecked(hwnd, IDC_IMPORT_SPECIFY_STORE) &&
4802              !data->hDestCertStore)
4803             {
4804                 import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
4805                  IDS_IMPORT_SELECT_STORE);
4806                 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
4807                 ret = 1;
4808             }
4809             break;
4810         }
4811         }
4812         break;
4813     }
4814     case WM_COMMAND:
4815         switch (wp)
4816         {
4817         case IDC_IMPORT_AUTO_STORE:
4818             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4819             data->autoDest = TRUE;
4820             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), FALSE);
4821             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), FALSE);
4822             break;
4823         case IDC_IMPORT_SPECIFY_STORE:
4824             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4825             data->autoDest = FALSE;
4826             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), TRUE);
4827             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), TRUE);
4828             break;
4829         case IDC_IMPORT_BROWSE_STORE:
4830         {
4831             CRYPTUI_ENUM_SYSTEM_STORE_ARGS enumArgs = {
4832              CERT_SYSTEM_STORE_CURRENT_USER, NULL };
4833             CRYPTUI_ENUM_DATA enumData = { 0, NULL, 1, &enumArgs };
4834             CRYPTUI_SELECTSTORE_INFO_W selectInfo;
4835             HCERTSTORE store;
4836
4837             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4838             selectInfo.dwSize = sizeof(selectInfo);
4839             selectInfo.parent = hwnd;
4840             selectInfo.dwFlags = CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE;
4841             selectInfo.pwszTitle = selectInfo.pwszTitle = NULL;
4842             selectInfo.pEnumData = &enumData;
4843             selectInfo.pfnSelectedStoreCallback = NULL;
4844             if ((store = CryptUIDlgSelectStoreW(&selectInfo)))
4845             {
4846                 WCHAR storeTitle[MAX_STRING_LEN];
4847
4848                 LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED,
4849                  storeTitle, sizeof(storeTitle) / sizeof(storeTitle[0]));
4850                 SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_STORE), WM_SETTEXT,
4851                  0, (LPARAM)storeTitle);
4852                 data->hDestCertStore = store;
4853                 data->freeDest = TRUE;
4854             }
4855             break;
4856         }
4857         }
4858         break;
4859     }
4860     return ret;
4861 }
4862
4863 static void show_import_details(HWND lv, struct ImportWizData *data)
4864 {
4865     WCHAR text[MAX_STRING_LEN];
4866     LVITEMW item;
4867     int contentID;
4868
4869     item.mask = LVIF_TEXT;
4870     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
4871     item.iSubItem = 0;
4872     LoadStringW(hInstance, IDS_IMPORT_STORE_SELECTION, text,
4873      sizeof(text)/ sizeof(text[0]));
4874     item.pszText = text;
4875     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
4876     item.iSubItem = 1;
4877     if (data->autoDest)
4878         LoadStringW(hInstance, IDS_IMPORT_DEST_AUTOMATIC, text,
4879          sizeof(text)/ sizeof(text[0]));
4880     else
4881         LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED, text,
4882          sizeof(text)/ sizeof(text[0]));
4883     SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
4884     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
4885     item.iSubItem = 0;
4886     LoadStringW(hInstance, IDS_IMPORT_CONTENT, text,
4887      sizeof(text)/ sizeof(text[0]));
4888     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
4889     switch (data->contentType)
4890     {
4891     case CERT_QUERY_CONTENT_CERT:
4892     case CERT_QUERY_CONTENT_SERIALIZED_CERT:
4893         contentID = IDS_IMPORT_CONTENT_CERT;
4894         break;
4895     case CERT_QUERY_CONTENT_CRL:
4896     case CERT_QUERY_CONTENT_SERIALIZED_CRL:
4897         contentID = IDS_IMPORT_CONTENT_CRL;
4898         break;
4899     case CERT_QUERY_CONTENT_CTL:
4900     case CERT_QUERY_CONTENT_SERIALIZED_CTL:
4901         contentID = IDS_IMPORT_CONTENT_CTL;
4902         break;
4903     case CERT_QUERY_CONTENT_PKCS7_SIGNED:
4904         contentID = IDS_IMPORT_CONTENT_CMS;
4905         break;
4906     case CERT_QUERY_CONTENT_FLAG_PFX:
4907         contentID = IDS_IMPORT_CONTENT_PFX;
4908         break;
4909     default:
4910         contentID = IDS_IMPORT_CONTENT_STORE;
4911         break;
4912     }
4913     LoadStringW(hInstance, contentID, text, sizeof(text)/ sizeof(text[0]));
4914     item.iSubItem = 1;
4915     SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
4916     if (data->fileName)
4917     {
4918         item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
4919         item.iSubItem = 0;
4920         LoadStringW(hInstance, IDS_IMPORT_FILE, text,
4921          sizeof(text)/ sizeof(text[0]));
4922         SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
4923         item.iSubItem = 1;
4924         item.pszText = data->fileName;
4925         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
4926     }
4927 }
4928
4929 static BOOL do_import(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
4930  PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
4931 {
4932     BOOL ret;
4933
4934     switch (pImportSrc->dwSubjectChoice)
4935     {
4936     case CRYPTUI_WIZ_IMPORT_SUBJECT_FILE:
4937         ret = import_file(dwFlags, hwndParent, pwszWizardTitle,
4938          pImportSrc->u.pwszFileName, hDestCertStore);
4939         break;
4940     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
4941         if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CERT)))
4942             ret = import_cert(pImportSrc->u.pCertContext, hDestCertStore);
4943         else
4944             import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle);
4945         break;
4946     case CRYPTUI_WIZ_IMPORT_SUBJECT_CRL_CONTEXT:
4947         if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CRL)))
4948             ret = import_crl(pImportSrc->u.pCRLContext, hDestCertStore);
4949         else
4950             import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle);
4951         break;
4952     case CRYPTUI_WIZ_IMPORT_SUBJECT_CTL_CONTEXT:
4953         if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CTL)))
4954             ret = import_ctl(pImportSrc->u.pCTLContext, hDestCertStore);
4955         else
4956             import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle);
4957         break;
4958     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE:
4959         ret = import_store(dwFlags, hwndParent, pwszWizardTitle,
4960          pImportSrc->u.hCertStore, hDestCertStore);
4961         break;
4962     default:
4963         WARN("unknown source type: %u\n", pImportSrc->dwSubjectChoice);
4964         SetLastError(E_INVALIDARG);
4965         ret = FALSE;
4966     }
4967     return ret;
4968 }
4969
4970 static LRESULT CALLBACK import_finish_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4971  LPARAM lp)
4972 {
4973     LRESULT ret = 0;
4974     struct ImportWizData *data;
4975
4976     switch (msg)
4977     {
4978     case WM_INITDIALOG:
4979     {
4980         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
4981         HWND lv = GetDlgItem(hwnd, IDC_IMPORT_SETTINGS);
4982         RECT rc;
4983         LVCOLUMNW column;
4984
4985         data = (struct ImportWizData *)page->lParam;
4986         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
4987         SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_TITLE), WM_SETFONT,
4988          (WPARAM)data->titleFont, TRUE);
4989         GetWindowRect(lv, &rc);
4990         column.mask = LVCF_WIDTH;
4991         column.cx = (rc.right - rc.left) / 2 - 2;
4992         SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
4993         SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
4994         show_import_details(lv, data);
4995         break;
4996     }
4997     case WM_NOTIFY:
4998     {
4999         NMHDR *hdr = (NMHDR *)lp;
5000
5001         switch (hdr->code)
5002         {
5003         case PSN_SETACTIVE:
5004         {
5005             HWND lv = GetDlgItem(hwnd, IDC_IMPORT_SETTINGS);
5006
5007             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5008             SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0);
5009             show_import_details(lv, data);
5010             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
5011              PSWIZB_BACK | PSWIZB_FINISH);
5012             ret = TRUE;
5013             break;
5014         }
5015         case PSN_WIZFINISH:
5016         {
5017             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5018             if ((data->success = do_import(data->dwFlags, hwnd,
5019              data->pwszWizardTitle, &data->importSrc, data->hDestCertStore)))
5020             {
5021                 WCHAR title[MAX_STRING_LEN], message[MAX_STRING_LEN];
5022                 LPCWSTR pTitle;
5023
5024                 if (data->pwszWizardTitle)
5025                     pTitle = data->pwszWizardTitle;
5026                 else
5027                 {
5028                     LoadStringW(hInstance, IDS_IMPORT_WIZARD, title,
5029                      sizeof(title) / sizeof(title[0]));
5030                     pTitle = title;
5031                 }
5032                 LoadStringW(hInstance, IDS_IMPORT_SUCCEEDED, message,
5033                  sizeof(message) / sizeof(message[0]));
5034                 MessageBoxW(hwnd, message, pTitle, MB_OK);
5035             }
5036             else
5037                 import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
5038                  IDS_IMPORT_SUCCEEDED);
5039             break;
5040         }
5041         }
5042         break;
5043     }
5044     }
5045     return ret;
5046 }
5047
5048 static BOOL show_import_ui(DWORD dwFlags, HWND hwndParent,
5049  LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc,
5050  HCERTSTORE hDestCertStore)
5051 {
5052     PROPSHEETHEADERW hdr;
5053     PROPSHEETPAGEW pages[4];
5054     struct ImportWizData data;
5055     int nPages = 0;
5056
5057     data.dwFlags = dwFlags;
5058     data.pwszWizardTitle = pwszWizardTitle;
5059     if (pImportSrc)
5060         memcpy(&data.importSrc, pImportSrc, sizeof(data.importSrc));
5061     else
5062         memset(&data.importSrc, 0, sizeof(data.importSrc));
5063     data.fileName = NULL;
5064     data.freeSource = FALSE;
5065     data.hDestCertStore = hDestCertStore;
5066     data.freeDest = FALSE;
5067     data.autoDest = TRUE;
5068     data.success = TRUE;
5069
5070     memset(&pages, 0, sizeof(pages));
5071
5072     pages[nPages].dwSize = sizeof(pages[0]);
5073     pages[nPages].hInstance = hInstance;
5074     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_WELCOME);
5075     pages[nPages].pfnDlgProc = import_welcome_dlg_proc;
5076     pages[nPages].dwFlags = PSP_HIDEHEADER;
5077     pages[nPages].lParam = (LPARAM)&data;
5078     nPages++;
5079
5080     if (!pImportSrc ||
5081      pImportSrc->dwSubjectChoice == CRYPTUI_WIZ_IMPORT_SUBJECT_FILE)
5082     {
5083         pages[nPages].dwSize = sizeof(pages[0]);
5084         pages[nPages].hInstance = hInstance;
5085         pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_FILE);
5086         pages[nPages].pfnDlgProc = import_file_dlg_proc;
5087         pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
5088         pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_IMPORT_FILE_TITLE);
5089         pages[nPages].pszHeaderSubTitle =
5090          MAKEINTRESOURCEW(IDS_IMPORT_FILE_SUBTITLE);
5091         pages[nPages].lParam = (LPARAM)&data;
5092         nPages++;
5093     }
5094     else
5095     {
5096         switch (pImportSrc->dwSubjectChoice)
5097         {
5098         case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
5099             data.contentType = CERT_QUERY_CONTENT_CERT;
5100             break;
5101         case CRYPTUI_WIZ_IMPORT_SUBJECT_CRL_CONTEXT:
5102             data.contentType = CERT_QUERY_CONTENT_CRL;
5103             break;
5104         case CRYPTUI_WIZ_IMPORT_SUBJECT_CTL_CONTEXT:
5105             data.contentType = CERT_QUERY_CONTENT_CTL;
5106             break;
5107         case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE:
5108             data.contentType = CERT_QUERY_CONTENT_SERIALIZED_STORE;
5109             break;
5110         }
5111     }
5112
5113     pages[nPages].dwSize = sizeof(pages[0]);
5114     pages[nPages].hInstance = hInstance;
5115     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_STORE);
5116     pages[nPages].pfnDlgProc = import_store_dlg_proc;
5117     pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
5118     pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_IMPORT_STORE_TITLE);
5119     pages[nPages].pszHeaderSubTitle =
5120      MAKEINTRESOURCEW(IDS_IMPORT_STORE_SUBTITLE);
5121     pages[nPages].lParam = (LPARAM)&data;
5122     nPages++;
5123
5124     pages[nPages].dwSize = sizeof(pages[0]);
5125     pages[nPages].hInstance = hInstance;
5126     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_FINISH);
5127     pages[nPages].pfnDlgProc = import_finish_dlg_proc;
5128     pages[nPages].dwFlags = PSP_HIDEHEADER;
5129     pages[nPages].lParam = (LPARAM)&data;
5130     nPages++;
5131
5132     memset(&hdr, 0, sizeof(hdr));
5133     hdr.dwSize = sizeof(hdr);
5134     hdr.hwndParent = hwndParent;
5135     hdr.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD97_NEW | PSH_HEADER |
5136      PSH_WATERMARK;
5137     hdr.hInstance = hInstance;
5138     if (pwszWizardTitle)
5139         hdr.pszCaption = pwszWizardTitle;
5140     else
5141         hdr.pszCaption = MAKEINTRESOURCEW(IDS_IMPORT_WIZARD);
5142     hdr.u3.ppsp = pages;
5143     hdr.nPages = nPages;
5144     hdr.u4.pszbmWatermark = MAKEINTRESOURCEW(IDB_CERT_WATERMARK);
5145     hdr.u5.pszbmHeader = MAKEINTRESOURCEW(IDB_CERT_HEADER);
5146     PropertySheetW(&hdr);
5147     HeapFree(GetProcessHeap(), 0, data.fileName);
5148     if (data.freeSource &&
5149      data.importSrc.dwSubjectChoice == CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE)
5150         CertCloseStore(data.importSrc.u.hCertStore, 0);
5151     DeleteObject(data.titleFont);
5152     return data.success;
5153 }
5154
5155 BOOL WINAPI CryptUIWizImport(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
5156                              PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
5157 {
5158     BOOL ret;
5159
5160     TRACE("(0x%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, debugstr_w(pwszWizardTitle),
5161           pImportSrc, hDestCertStore);
5162
5163     if (pImportSrc &&
5164      pImportSrc->dwSize != sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO))
5165     {
5166         SetLastError(E_INVALIDARG);
5167         return FALSE;
5168     }
5169
5170     if (!(dwFlags & CRYPTUI_WIZ_NO_UI))
5171         ret = show_import_ui(dwFlags, hwndParent, pwszWizardTitle, pImportSrc,
5172          hDestCertStore);
5173     else if (pImportSrc)
5174         ret = do_import(dwFlags, hwndParent, pwszWizardTitle, pImportSrc,
5175          hDestCertStore);
5176     else
5177     {
5178         /* Can't have no UI without specifying source */
5179         SetLastError(E_INVALIDARG);
5180         ret = FALSE;
5181     }
5182
5183     return ret;
5184 }