user32: Use a finally handler to make sure capture is always released when exiting...
[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 struct CertMgrStoreInfo
458 {
459     LPCWSTR name;
460     int removeWarning;
461     int removePluralWarning;
462 };
463
464 static const struct CertMgrStoreInfo defaultStoreList[] = {
465  { my, IDS_WARN_REMOVE_MY, IDS_WARN_REMOVE_PLURAL_MY },
466  { addressBook, IDS_WARN_REMOVE_ADDRESSBOOK,
467    IDS_WARN_REMOVE_PLURAL_ADDRESSBOOK },
468  { ca, IDS_WARN_REMOVE_CA, IDS_WARN_REMOVE_PLURAL_CA },
469  { root, IDS_WARN_REMOVE_ROOT, IDS_WARN_REMOVE_PLURAL_ROOT },
470  { trustedPublisher, IDS_WARN_REMOVE_TRUSTEDPUBLISHER,
471    IDS_WARN_REMOVE_PLURAL_TRUSTEDPUBLISHER },
472  { disallowed, IDS_WARN_REMOVE_DEFAULT },
473 };
474
475 static const struct CertMgrStoreInfo publisherStoreList[] = {
476  { root, IDS_WARN_REMOVE_ROOT, IDS_WARN_REMOVE_PLURAL_ROOT },
477  { trustedPublisher, IDS_WARN_REMOVE_TRUSTEDPUBLISHER,
478    IDS_WARN_REMOVE_PLURAL_TRUSTEDPUBLISHER },
479  { disallowed, IDS_WARN_REMOVE_PLURAL_DEFAULT },
480 };
481
482 struct CertMgrData
483 {
484     HIMAGELIST imageList;
485     LPCWSTR title;
486     DWORD nStores;
487     const struct CertMgrStoreInfo *stores;
488 };
489
490 static void show_cert_stores(HWND hwnd, DWORD dwFlags, struct CertMgrData *data)
491 {
492     const struct CertMgrStoreInfo *storeList;
493     int cStores, i;
494     HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES);
495
496     if (dwFlags & CRYPTUI_CERT_MGR_PUBLISHER_TAB)
497     {
498         storeList = publisherStoreList;
499         cStores = sizeof(publisherStoreList) / sizeof(publisherStoreList[0]);
500     }
501     else
502     {
503         storeList = defaultStoreList;
504         cStores = sizeof(defaultStoreList) / sizeof(defaultStoreList[0]);
505     }
506     if (dwFlags & CRYPTUI_CERT_MGR_SINGLE_TAB_FLAG)
507         cStores = 1;
508     data->nStores = cStores;
509     data->stores = storeList;
510     for (i = 0; i < cStores; i++)
511     {
512         LPCWSTR name;
513         TCITEMW item;
514         HCERTSTORE store;
515
516         if (!(name = CryptFindLocalizedName(storeList[i].name)))
517             name = storeList[i].name;
518         store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
519          CERT_SYSTEM_STORE_CURRENT_USER, storeList[i].name);
520         item.mask = TCIF_TEXT | TCIF_PARAM;
521         item.pszText = (LPWSTR)name;
522         item.lParam = (LPARAM)store;
523         SendMessageW(tab, TCM_INSERTITEMW, i, (LPARAM)&item);
524     }
525 }
526
527 static void free_certs(HWND lv)
528 {
529     LVITEMW item;
530     int items = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
531
532     for (i = 0; i < items; i++)
533     {
534         item.mask = LVIF_PARAM;
535         item.iItem = i;
536         item.iSubItem = 0;
537         SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
538         CertFreeCertificateContext((PCCERT_CONTEXT)item.lParam);
539     }
540 }
541
542 static HCERTSTORE cert_mgr_index_to_store(HWND tab, int index)
543 {
544     TCITEMW item;
545
546     item.mask = TCIF_PARAM;
547     SendMessageW(tab, TCM_GETITEMW, index, (LPARAM)&item);
548     return (HCERTSTORE)item.lParam;
549 }
550
551 static HCERTSTORE cert_mgr_current_store(HWND hwnd)
552 {
553     HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES);
554
555     return cert_mgr_index_to_store(tab, SendMessageW(tab, TCM_GETCURSEL, 0, 0));
556 }
557
558 static void close_stores(HWND tab)
559 {
560     int i, tabs = SendMessageW(tab, TCM_GETITEMCOUNT, 0, 0);
561
562     for (i = 0; i < tabs; i++)
563         CertCloseStore(cert_mgr_index_to_store(tab, i), 0);
564 }
565
566 static void refresh_store_certs(HWND hwnd)
567 {
568     HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
569
570     free_certs(lv);
571     SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0);
572     show_store_certs(hwnd, cert_mgr_current_store(hwnd));
573 }
574
575 typedef enum {
576     CheckBitmapIndexUnchecked = 1,
577     CheckBitmapIndexChecked = 2,
578     CheckBitmapIndexDisabledUnchecked = 3,
579     CheckBitmapIndexDisabledChecked = 4
580 } CheckBitmapIndex;
581
582 static void add_known_usage(HWND lv, PCCRYPT_OID_INFO info,
583  CheckBitmapIndex state)
584 {
585     LVITEMW item;
586
587     item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
588     item.state = INDEXTOSTATEIMAGEMASK(state);
589     item.stateMask = LVIS_STATEIMAGEMASK;
590     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
591     item.iSubItem = 0;
592     item.lParam = (LPARAM)info;
593     item.pszText = (LPWSTR)info->pwszName;
594     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
595 }
596
597 static void add_known_usages_to_list(HWND lv, CheckBitmapIndex state)
598 {
599     PCCRYPT_OID_INFO *usages;
600
601     if (WTHelperGetKnownUsages(1, &usages))
602     {
603         PCCRYPT_OID_INFO *ptr;
604
605         for (ptr = usages; *ptr; ptr++)
606             add_known_usage(lv, *ptr, state);
607         WTHelperGetKnownUsages(2, &usages);
608     }
609 }
610
611 static void toggle_usage(HWND hwnd, int iItem)
612 {
613     LVITEMW item;
614     int res;
615     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
616
617     item.mask = LVIF_STATE;
618     item.iItem = iItem;
619     item.iSubItem = 0;
620     item.stateMask = LVIS_STATEIMAGEMASK;
621     res = SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
622     if (res)
623     {
624         int state = item.state >> 12;
625
626         item.state = INDEXTOSTATEIMAGEMASK(
627          state == CheckBitmapIndexChecked ? CheckBitmapIndexUnchecked :
628          CheckBitmapIndexChecked);
629         SendMessageW(lv, LVM_SETITEMSTATE, iItem, (LPARAM)&item);
630     }
631 }
632
633 static LONG_PTR find_oid_in_list(HWND lv, LPCSTR oid)
634 {
635     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
636      (void *)oid, CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
637     LONG_PTR ret;
638
639     if (oidInfo)
640     {
641         LVFINDINFOW findInfo;
642
643         findInfo.flags = LVFI_PARAM;
644         findInfo.lParam = (LPARAM)oidInfo;
645         ret = SendMessageW(lv, LVM_FINDITEMW, -1, (LPARAM)&findInfo);
646     }
647     else
648     {
649         LVFINDINFOA findInfo;
650
651         findInfo.flags = LVFI_STRING;
652         findInfo.psz = oid;
653         ret = SendMessageW(lv, LVM_FINDITEMA, -1, (LPARAM)&findInfo);
654     }
655     return ret;
656 }
657
658 static void save_cert_mgr_usages(HWND hwnd)
659 {
660     static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M',
661      'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a',
662      'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u',
663      'r','p','o','s','e',0 };
664     HKEY key;
665     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
666     int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
667     LVITEMW item;
668     LPSTR str = NULL;
669
670     item.mask = LVIF_STATE | LVIF_PARAM;
671     item.iSubItem = 0;
672     item.stateMask = LVIS_STATEIMAGEMASK;
673     for (i = 0; i < purposes; i++)
674     {
675         item.iItem = i;
676         if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
677         {
678             int state = item.state >> 12;
679
680             if (state == CheckBitmapIndexUnchecked)
681             {
682                 CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
683                 BOOL firstString = TRUE;
684
685                 if (!str)
686                     str = HeapAlloc(GetProcessHeap(), 0,
687                      strlen(info->pszOID) + 1);
688                 else
689                 {
690                     str = HeapReAlloc(GetProcessHeap(), 0, str,
691                      strlen(str) + 1 + strlen(info->pszOID) + 1);
692                     firstString = FALSE;
693                 }
694                 if (str)
695                 {
696                     LPSTR ptr = firstString ? str : str + strlen(str);
697
698                     if (!firstString)
699                         *ptr++ = ',';
700                     strcpy(ptr, info->pszOID);
701                 }
702             }
703         }
704     }
705     if (!RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_ALL_ACCESS,
706      NULL, &key, NULL))
707     {
708         if (str)
709             RegSetValueExA(key, "Purpose", 0, REG_SZ, (const BYTE *)str,
710              strlen(str) + 1);
711         else
712             RegDeleteValueA(key, "Purpose");
713         RegCloseKey(key);
714     }
715     HeapFree(GetProcessHeap(), 0, str);
716 }
717
718 static LRESULT CALLBACK cert_mgr_advanced_dlg_proc(HWND hwnd, UINT msg,
719  WPARAM wp, LPARAM lp)
720 {
721     switch (msg)
722     {
723     case WM_INITDIALOG:
724     {
725         RECT rc;
726         LVCOLUMNW column;
727         HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
728         HIMAGELIST imageList;
729         LPSTR disabledUsages;
730
731         GetWindowRect(lv, &rc);
732         column.mask = LVCF_WIDTH;
733         column.cx = rc.right - rc.left;
734         SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
735         imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 4, 0);
736         if (imageList)
737         {
738             HBITMAP bmp;
739             COLORREF backColor = RGB(255, 0, 255);
740
741             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS));
742             ImageList_AddMasked(imageList, bmp, backColor);
743             DeleteObject(bmp);
744             ImageList_SetBkColor(imageList, CLR_NONE);
745             SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)imageList);
746             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)imageList);
747         }
748         add_known_usages_to_list(lv, CheckBitmapIndexChecked);
749         if ((disabledUsages = get_cert_mgr_usages()))
750         {
751             LPSTR ptr, comma;
752
753             for (ptr = disabledUsages, comma = strchr(ptr, ','); ptr && *ptr;
754              ptr = comma ? comma + 1 : NULL,
755              comma = ptr ? strchr(ptr, ',') : NULL)
756             {
757                 LONG_PTR index;
758
759                 if (comma)
760                     *comma = 0;
761                 if ((index = find_oid_in_list(lv, ptr)) != -1)
762                     toggle_usage(hwnd, index);
763             }
764             HeapFree(GetProcessHeap(), 0, disabledUsages);
765         }
766         break;
767     }
768     case WM_NOTIFY:
769     {
770         NMHDR *hdr = (NMHDR *)lp;
771         NMITEMACTIVATE *nm;
772
773         switch (hdr->code)
774         {
775         case NM_CLICK:
776             nm = (NMITEMACTIVATE *)lp;
777             toggle_usage(hwnd, nm->iItem);
778             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
779             break;
780         }
781         break;
782     }
783     case WM_COMMAND:
784         switch (wp)
785         {
786         case IDOK:
787             save_cert_mgr_usages(hwnd);
788             ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER));
789             EndDialog(hwnd, IDOK);
790             break;
791         case IDCANCEL:
792             ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER));
793             EndDialog(hwnd, IDCANCEL);
794             break;
795         }
796         break;
797     }
798     return 0;
799 }
800
801 static void cert_mgr_clear_cert_selection(HWND hwnd)
802 {
803     WCHAR empty[] = { 0 };
804
805     EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), FALSE);
806     EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), FALSE);
807     EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), FALSE);
808     SendMessageW(GetDlgItem(hwnd, IDC_MGR_PURPOSES), WM_SETTEXT, 0,
809      (LPARAM)empty);
810     refresh_store_certs(hwnd);
811 }
812
813 static PCCERT_CONTEXT cert_mgr_index_to_cert(HWND hwnd, int index)
814 {
815     PCCERT_CONTEXT cert = NULL;
816     LVITEMW item;
817
818     item.mask = LVIF_PARAM;
819     item.iItem = index;
820     item.iSubItem = 0;
821     if (SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_GETITEMW, 0,
822      (LPARAM)&item))
823         cert = (PCCERT_CONTEXT)item.lParam;
824     return cert;
825 }
826
827 static void show_selected_cert(HWND hwnd, int index)
828 {
829     PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, index);
830
831     if (cert)
832     {
833         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
834
835         memset(&viewInfo, 0, sizeof(viewInfo));
836         viewInfo.dwSize = sizeof(viewInfo);
837         viewInfo.hwndParent = hwnd;
838         viewInfo.pCertContext = cert;
839         /* FIXME: this should be modal */
840         CryptUIDlgViewCertificateW(&viewInfo, NULL);
841     }
842 }
843
844 static void cert_mgr_show_cert_usages(HWND hwnd, int index)
845 {
846     HWND text = GetDlgItem(hwnd, IDC_MGR_PURPOSES);
847     PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, index);
848     PCERT_ENHKEY_USAGE usage;
849     DWORD size;
850
851     /* Get enhanced key usage.  Have to check for a property and an extension
852      * separately, because CertGetEnhancedKeyUsage will succeed and return an
853      * empty usage if neither is set.  Unfortunately an empty usage implies
854      * no usage is allowed, so we have to distinguish between the two cases.
855      */
856     if (CertGetEnhancedKeyUsage(cert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
857      NULL, &size))
858     {
859         usage = HeapAlloc(GetProcessHeap(), 0, size);
860         if (!CertGetEnhancedKeyUsage(cert,
861          CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
862         {
863             HeapFree(GetProcessHeap(), 0, usage);
864             usage = NULL;
865         }
866     }
867     else if (CertGetEnhancedKeyUsage(cert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
868      NULL, &size))
869     {
870         usage = HeapAlloc(GetProcessHeap(), 0, size);
871         if (!CertGetEnhancedKeyUsage(cert,
872          CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
873         {
874             HeapFree(GetProcessHeap(), 0, usage);
875             usage = NULL;
876         }
877     }
878     else
879         usage = NULL;
880     if (usage)
881     {
882         if (usage->cUsageIdentifier)
883         {
884             static const WCHAR commaSpace[] = { ',',' ',0 };
885             DWORD i, len = 1;
886             LPWSTR str, ptr;
887
888             for (i = 0; i < usage->cUsageIdentifier; i++)
889             {
890                 PCCRYPT_OID_INFO info =
891                  CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
892                  usage->rgpszUsageIdentifier[i],
893                  CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
894
895                 if (info)
896                     len += strlenW(info->pwszName);
897                 else
898                     len += strlen(usage->rgpszUsageIdentifier[i]);
899                 if (i < usage->cUsageIdentifier - 1)
900                     len += strlenW(commaSpace);
901             }
902             str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
903             if (str)
904             {
905                 for (i = 0, ptr = str; i < usage->cUsageIdentifier; i++)
906                 {
907                     PCCRYPT_OID_INFO info =
908                      CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
909                      usage->rgpszUsageIdentifier[i],
910                      CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
911
912                     if (info)
913                     {
914                         strcpyW(ptr, info->pwszName);
915                         ptr += strlenW(info->pwszName);
916                     }
917                     else
918                     {
919                         LPCSTR src = usage->rgpszUsageIdentifier[i];
920
921                         for (; *src; ptr++, src++)
922                             *ptr = *src;
923                         *ptr = 0;
924                     }
925                     if (i < usage->cUsageIdentifier - 1)
926                     {
927                         strcpyW(ptr, commaSpace);
928                         ptr += strlenW(commaSpace);
929                     }
930                 }
931                 *ptr = 0;
932                 SendMessageW(text, WM_SETTEXT, 0, (LPARAM)str);
933                 HeapFree(GetProcessHeap(), 0, str);
934             }
935             HeapFree(GetProcessHeap(), 0, usage);
936         }
937         else
938         {
939             WCHAR buf[MAX_STRING_LEN];
940
941             LoadStringW(hInstance, IDS_ALLOWED_PURPOSE_NONE, buf,
942              sizeof(buf) / sizeof(buf[0]));
943             SendMessageW(text, WM_SETTEXT, 0, (LPARAM)buf);
944         }
945     }
946     else
947     {
948         WCHAR buf[MAX_STRING_LEN];
949
950         LoadStringW(hInstance, IDS_ALLOWED_PURPOSE_ALL, buf,
951          sizeof(buf) / sizeof(buf[0]));
952         SendMessageW(text, WM_SETTEXT, 0, (LPARAM)buf);
953     }
954 }
955
956 static void cert_mgr_do_remove(HWND hwnd)
957 {
958     int tabIndex = SendMessageW(GetDlgItem(hwnd, IDC_MGR_STORES),
959      TCM_GETCURSEL, 0, 0);
960     struct CertMgrData *data =
961      (struct CertMgrData *)GetWindowLongPtrW(hwnd, DWLP_USER);
962
963     if (tabIndex < data->nStores)
964     {
965         HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
966         WCHAR warning[MAX_STRING_LEN], title[MAX_STRING_LEN];
967         LPCWSTR pTitle;
968         int warningID;
969
970         if (SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0) > 1)
971             warningID = data->stores[tabIndex].removePluralWarning;
972         else
973             warningID = data->stores[tabIndex].removeWarning;
974         if (data->title)
975             pTitle = data->title;
976         else
977         {
978             LoadStringW(hInstance, IDS_CERT_MGR, title,
979              sizeof(title) / sizeof(title[0]));
980             pTitle = title;
981         }
982         LoadStringW(hInstance, warningID, warning,
983          sizeof(warning) / sizeof(warning[0]));
984         if (MessageBoxW(hwnd, warning, pTitle, MB_YESNO) == IDYES)
985         {
986             int selection = -1;
987
988             do {
989                 selection = SendMessageW(lv, LVM_GETNEXTITEM, selection,
990                  LVNI_SELECTED);
991                 if (selection >= 0)
992                 {
993                     PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd,
994                      selection);
995
996                     CertDeleteCertificateFromStore(cert);
997                 }
998             } while (selection >= 0);
999             cert_mgr_clear_cert_selection(hwnd);
1000         }
1001     }
1002 }
1003
1004 static LRESULT CALLBACK cert_mgr_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1005  LPARAM lp)
1006 {
1007     struct CertMgrData *data;
1008
1009     switch (msg)
1010     {
1011     case WM_INITDIALOG:
1012     {
1013         PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr =
1014          (PCCRYPTUI_CERT_MGR_STRUCT)lp;
1015         HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES);
1016
1017         data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CertMgrData));
1018         if (data)
1019         {
1020             data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK,
1021              2, 0);
1022             if (data->imageList)
1023             {
1024                 HBITMAP bmp;
1025                 COLORREF backColor = RGB(255, 0, 255);
1026
1027                 bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS));
1028                 ImageList_AddMasked(data->imageList, bmp, backColor);
1029                 DeleteObject(bmp);
1030                 ImageList_SetBkColor(data->imageList, CLR_NONE);
1031                 SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_SETIMAGELIST,
1032                  LVSIL_SMALL, (LPARAM)data->imageList);
1033             }
1034             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
1035             data->title = pCryptUICertMgr->pwszTitle;
1036         }
1037         initialize_purpose_selection(hwnd);
1038         add_cert_columns(hwnd);
1039         if (pCryptUICertMgr->pwszTitle)
1040             SendMessageW(hwnd, WM_SETTEXT, 0,
1041              (LPARAM)pCryptUICertMgr->pwszTitle);
1042         show_cert_stores(hwnd, pCryptUICertMgr->dwFlags, data);
1043         show_store_certs(hwnd, cert_mgr_index_to_store(tab, 0));
1044         break;
1045     }
1046     case WM_NOTIFY:
1047     {
1048         NMHDR *hdr = (NMHDR *)lp;
1049
1050         switch (hdr->code)
1051         {
1052         case TCN_SELCHANGE:
1053             cert_mgr_clear_cert_selection(hwnd);
1054             break;
1055         case LVN_ITEMCHANGED:
1056         {
1057             NMITEMACTIVATE *nm;
1058             HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
1059
1060             nm = (NMITEMACTIVATE*)lp;
1061             if (nm->uNewState & LVN_ITEMACTIVATE)
1062             {
1063                 int numSelected = SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0);
1064
1065                 EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), numSelected > 0);
1066                 EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), numSelected > 0);
1067                 EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), numSelected == 1);
1068                 if (numSelected == 1)
1069                     cert_mgr_show_cert_usages(hwnd, nm->iItem);
1070             }
1071             break;
1072         }
1073         case NM_DBLCLK:
1074             show_selected_cert(hwnd, ((NMITEMACTIVATE *)lp)->iItem);
1075             break;
1076         case LVN_KEYDOWN:
1077         {
1078             NMLVKEYDOWN *lvk = (NMLVKEYDOWN *)lp;
1079
1080             if (lvk->wVKey == VK_DELETE)
1081                 cert_mgr_do_remove(hwnd);
1082             break;
1083         }
1084         }
1085         break;
1086     }
1087     case WM_COMMAND:
1088         switch (wp)
1089         {
1090         case ((CBN_SELCHANGE << 16) | IDC_MGR_PURPOSE_SELECTION):
1091             cert_mgr_clear_cert_selection(hwnd);
1092             break;
1093         case IDC_MGR_IMPORT:
1094             if (CryptUIWizImport(0, hwnd, NULL, NULL,
1095              cert_mgr_current_store(hwnd)))
1096                 refresh_store_certs(hwnd);
1097             break;
1098         case IDC_MGR_ADVANCED:
1099             if (DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_CERT_MGR_ADVANCED),
1100              hwnd, cert_mgr_advanced_dlg_proc) == IDOK)
1101             {
1102                 HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION);
1103                 int index, len;
1104                 LPWSTR curString = NULL;
1105
1106                 index = SendMessageW(cb, CB_GETCURSEL, 0, 0);
1107                 if (index >= 0)
1108                 {
1109                     len = SendMessageW(cb, CB_GETLBTEXTLEN, index, 0);
1110                     curString = HeapAlloc(GetProcessHeap(), 0,
1111                      (len + 1) * sizeof(WCHAR));
1112                     SendMessageW(cb, CB_GETLBTEXT, index, (LPARAM)curString);
1113                 }
1114                 SendMessageW(cb, CB_RESETCONTENT, 0, 0);
1115                 initialize_purpose_selection(hwnd);
1116                 if (curString)
1117                 {
1118                     index = SendMessageW(cb, CB_FINDSTRINGEXACT, -1,
1119                      (LPARAM)curString);
1120                     if (index >= 0)
1121                         SendMessageW(cb, CB_SETCURSEL, index, 0);
1122                     HeapFree(GetProcessHeap(), 0, curString);
1123                 }
1124                 refresh_store_certs(hwnd);
1125             }
1126             break;
1127         case IDC_MGR_VIEW:
1128         {
1129             HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
1130             int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1,
1131              LVNI_SELECTED);
1132
1133             if (selection >= 0)
1134                 show_selected_cert(hwnd, selection);
1135             break;
1136         }
1137         case IDC_MGR_EXPORT:
1138         {
1139             HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS);
1140             int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1,
1141              LVNI_SELECTED);
1142
1143             if (selection >= 0)
1144             {
1145                 PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, selection);
1146
1147                 if (cert)
1148                 {
1149                     CRYPTUI_WIZ_EXPORT_INFO info;
1150
1151                     info.dwSize = sizeof(info);
1152                     info.pwszExportFileName = NULL;
1153                     info.dwSubjectChoice = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT;
1154                     info.u.pCertContext = cert;
1155                     info.cStores = 0;
1156                     CryptUIWizExport(0, hwnd, NULL, &info, NULL);
1157                 }
1158             }
1159             break;
1160         }
1161         case IDC_MGR_REMOVE:
1162             cert_mgr_do_remove(hwnd);
1163             break;
1164         case IDCANCEL:
1165             free_certs(GetDlgItem(hwnd, IDC_MGR_CERTS));
1166             close_stores(GetDlgItem(hwnd, IDC_MGR_STORES));
1167             close_stores(GetDlgItem(hwnd, IDC_MGR_STORES));
1168             data = (struct CertMgrData *)GetWindowLongPtrW(hwnd, DWLP_USER);
1169             ImageList_Destroy(data->imageList);
1170             HeapFree(GetProcessHeap(), 0, data);
1171             EndDialog(hwnd, IDCANCEL);
1172             break;
1173         }
1174         break;
1175     }
1176     return 0;
1177 }
1178
1179 /***********************************************************************
1180  *              CryptUIDlgCertMgr (CRYPTUI.@)
1181  */
1182 BOOL WINAPI CryptUIDlgCertMgr(PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr)
1183 {
1184     TRACE("(%p)\n", pCryptUICertMgr);
1185
1186     if (pCryptUICertMgr->dwSize != sizeof(CRYPTUI_CERT_MGR_STRUCT))
1187     {
1188         WARN("unexpected size %d\n", pCryptUICertMgr->dwSize);
1189         SetLastError(E_INVALIDARG);
1190         return FALSE;
1191     }
1192     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_CERT_MGR),
1193      pCryptUICertMgr->hwndParent, cert_mgr_dlg_proc, (LPARAM)pCryptUICertMgr);
1194     return TRUE;
1195 }
1196
1197 /* FIXME: real names are unknown, functions are undocumented */
1198 typedef struct _CRYPTUI_ENUM_SYSTEM_STORE_ARGS
1199 {
1200     DWORD dwFlags;
1201     void *pvSystemStoreLocationPara;
1202 } CRYPTUI_ENUM_SYSTEM_STORE_ARGS, *PCRYPTUI_ENUM_SYSTEM_STORE_ARGS;
1203
1204 typedef struct _CRYPTUI_ENUM_DATA
1205 {
1206     DWORD                           cStores;
1207     HCERTSTORE                     *rghStore;
1208     DWORD                           cEnumArgs;
1209     PCRYPTUI_ENUM_SYSTEM_STORE_ARGS rgEnumArgs;
1210 } CRYPTUI_ENUM_DATA, *PCRYPTUI_ENUM_DATA;
1211
1212 typedef BOOL (WINAPI *PFN_SELECTED_STORE_CB)(HCERTSTORE store, HWND hwnd,
1213  void *pvArg);
1214
1215 /* Values for dwFlags */
1216 #define CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE 0x00000001
1217
1218 typedef struct _CRYPTUI_SELECTSTORE_INFO_A
1219 {
1220     DWORD                 dwSize;
1221     HWND                  parent;
1222     DWORD                 dwFlags;
1223     LPSTR                 pszTitle;
1224     LPSTR                 pszText;
1225     CRYPTUI_ENUM_DATA    *pEnumData;
1226     PFN_SELECTED_STORE_CB pfnSelectedStoreCallback;
1227     void                 *pvArg;
1228 } CRYPTUI_SELECTSTORE_INFO_A, *PCRYPTUI_SELECTSTORE_INFO_A;
1229
1230 typedef struct _CRYPTUI_SELECTSTORE_INFO_W
1231 {
1232     DWORD                 dwSize;
1233     HWND                  parent;
1234     DWORD                 dwFlags;
1235     LPWSTR                pwszTitle;
1236     LPWSTR                pwszText;
1237     CRYPTUI_ENUM_DATA    *pEnumData;
1238     PFN_SELECTED_STORE_CB pfnSelectedStoreCallback;
1239     void                 *pvArg;
1240 } CRYPTUI_SELECTSTORE_INFO_W, *PCRYPTUI_SELECTSTORE_INFO_W;
1241
1242 struct StoreInfo
1243 {
1244     enum {
1245         StoreHandle,
1246         SystemStore
1247     } type;
1248     union {
1249         HCERTSTORE store;
1250         LPWSTR name;
1251     } DUMMYUNIONNAME;
1252 };
1253
1254 static BOOL WINAPI enum_store_callback(const void *pvSystemStore,
1255  DWORD dwFlags, PCERT_SYSTEM_STORE_INFO pStoreInfo, void *pvReserved,
1256  void *pvArg)
1257 {
1258     HWND tree = GetDlgItem(pvArg, IDC_STORE_LIST);
1259     TVINSERTSTRUCTW tvis;
1260     LPCWSTR localizedName;
1261     BOOL ret = TRUE;
1262
1263     tvis.hParent = NULL;
1264     tvis.hInsertAfter = TVI_LAST;
1265     tvis.u.item.mask = TVIF_TEXT;
1266     if ((localizedName = CryptFindLocalizedName(pvSystemStore)))
1267     {
1268         struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(), 0,
1269          sizeof(struct StoreInfo));
1270
1271         if (storeInfo)
1272         {
1273             storeInfo->type = SystemStore;
1274             storeInfo->u.name = HeapAlloc(GetProcessHeap(), 0,
1275              (strlenW(pvSystemStore) + 1) * sizeof(WCHAR));
1276             if (storeInfo->u.name)
1277             {
1278                 tvis.u.item.mask |= TVIF_PARAM;
1279                 tvis.u.item.lParam = (LPARAM)storeInfo;
1280                 strcpyW(storeInfo->u.name, pvSystemStore);
1281             }
1282             else
1283             {
1284                 HeapFree(GetProcessHeap(), 0, storeInfo);
1285                 ret = FALSE;
1286             }
1287         }
1288         else
1289             ret = FALSE;
1290         tvis.u.item.pszText = (LPWSTR)localizedName;
1291     }
1292     else
1293         tvis.u.item.pszText = (LPWSTR)pvSystemStore;
1294     /* FIXME: need a folder icon for the store too */
1295     if (ret)
1296         SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis);
1297     return ret;
1298 }
1299
1300 static void enumerate_stores(HWND hwnd, CRYPTUI_ENUM_DATA *pEnumData)
1301 {
1302     DWORD i;
1303     HWND tree = GetDlgItem(hwnd, IDC_STORE_LIST);
1304
1305     for (i = 0; i < pEnumData->cEnumArgs; i++)
1306         CertEnumSystemStore(pEnumData->rgEnumArgs[i].dwFlags,
1307          pEnumData->rgEnumArgs[i].pvSystemStoreLocationPara,
1308          hwnd, enum_store_callback);
1309     for (i = 0; i < pEnumData->cStores; i++)
1310     {
1311         DWORD size;
1312
1313         if (CertGetStoreProperty(pEnumData->rghStore[i],
1314          CERT_STORE_LOCALIZED_NAME_PROP_ID, NULL, &size))
1315         {
1316             LPWSTR name = HeapAlloc(GetProcessHeap(), 0, size);
1317
1318             if (name)
1319             {
1320                 if (CertGetStoreProperty(pEnumData->rghStore[i],
1321                  CERT_STORE_LOCALIZED_NAME_PROP_ID, name, &size))
1322                 {
1323                     struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(),
1324                      0, sizeof(struct StoreInfo));
1325
1326                     if (storeInfo)
1327                     {
1328                         TVINSERTSTRUCTW tvis;
1329
1330                         storeInfo->type = StoreHandle;
1331                         storeInfo->u.store = pEnumData->rghStore[i];
1332                         tvis.hParent = NULL;
1333                         tvis.hInsertAfter = TVI_LAST;
1334                         tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
1335                         tvis.u.item.pszText = name;
1336                         tvis.u.item.lParam = (LPARAM)storeInfo;
1337                         SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis);
1338                     }
1339                 }
1340                 HeapFree(GetProcessHeap(), 0, name);
1341             }
1342         }
1343     }
1344 }
1345
1346 static void free_store_info(HWND tree)
1347 {
1348     HTREEITEM next = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CHILD,
1349      (LPARAM)NULL);
1350
1351     while (next)
1352     {
1353         TVITEMW item;
1354
1355         memset(&item, 0, sizeof(item));
1356         item.mask = TVIF_HANDLE | TVIF_PARAM;
1357         item.hItem = next;
1358         SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
1359         if (item.lParam)
1360         {
1361             struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam;
1362
1363             if (storeInfo->type == SystemStore)
1364                 HeapFree(GetProcessHeap(), 0, storeInfo->u.name);
1365             HeapFree(GetProcessHeap(), 0, storeInfo);
1366         }
1367         next = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_NEXT,
1368          (LPARAM)next);
1369     }
1370 }
1371
1372 static HCERTSTORE selected_item_to_store(HWND tree, HTREEITEM hItem)
1373 {
1374     WCHAR buf[MAX_STRING_LEN];
1375     TVITEMW item;
1376     HCERTSTORE store;
1377
1378     memset(&item, 0, sizeof(item));
1379     item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
1380     item.hItem = hItem;
1381     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
1382     item.pszText = buf;
1383     SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
1384     if (item.lParam)
1385     {
1386         struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam;
1387
1388         if (storeInfo->type == StoreHandle)
1389             store = storeInfo->u.store;
1390         else
1391             store = CertOpenSystemStoreW(0, storeInfo->u.name);
1392     }
1393     else
1394     {
1395         /* It's implicitly a system store */
1396         store = CertOpenSystemStoreW(0, buf);
1397     }
1398     return store;
1399 }
1400
1401 struct SelectStoreInfo
1402 {
1403     PCRYPTUI_SELECTSTORE_INFO_W info;
1404     HCERTSTORE                  store;
1405 };
1406
1407 static LRESULT CALLBACK select_store_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1408  LPARAM lp)
1409 {
1410     struct SelectStoreInfo *selectInfo;
1411     LRESULT ret = 0;
1412
1413     switch (msg)
1414     {
1415     case WM_INITDIALOG:
1416     {
1417         selectInfo = (struct SelectStoreInfo *)lp;
1418         SetWindowLongPtrW(hwnd, DWLP_USER, lp);
1419         if (selectInfo->info->pwszTitle)
1420             SendMessageW(hwnd, WM_SETTEXT, 0,
1421              (LPARAM)selectInfo->info->pwszTitle);
1422         if (selectInfo->info->pwszText)
1423             SendMessageW(GetDlgItem(hwnd, IDC_STORE_TEXT), WM_SETTEXT, 0,
1424              (LPARAM)selectInfo->info->pwszText);
1425         if (!(selectInfo->info->dwFlags & CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE))
1426             ShowWindow(GetDlgItem(hwnd, IDC_SHOW_PHYSICAL_STORES), FALSE);
1427         enumerate_stores(hwnd, selectInfo->info->pEnumData);
1428         break;
1429     }
1430     case WM_COMMAND:
1431         switch (wp)
1432         {
1433         case IDOK:
1434         {
1435             HWND tree = GetDlgItem(hwnd, IDC_STORE_LIST);
1436             HTREEITEM selection = (HTREEITEM)SendMessageW(tree,
1437              TVM_GETNEXTITEM, TVGN_CARET, (LPARAM)NULL);
1438
1439             selectInfo = (struct SelectStoreInfo *)GetWindowLongPtrW(hwnd,
1440              DWLP_USER);
1441             if (!selection)
1442             {
1443                 WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN], *pTitle;
1444
1445                 if (selectInfo->info->pwszTitle)
1446                     pTitle = selectInfo->info->pwszTitle;
1447                 else
1448                 {
1449                     LoadStringW(hInstance, IDS_SELECT_STORE_TITLE, title,
1450                      sizeof(title) / sizeof(title[0]));
1451                     pTitle = title;
1452                 }
1453                 LoadStringW(hInstance, IDS_SELECT_STORE, error,
1454                  sizeof(error) / sizeof(error[0]));
1455                 MessageBoxW(hwnd, error, pTitle, MB_ICONEXCLAMATION | MB_OK);
1456             }
1457             else
1458             {
1459                 HCERTSTORE store = selected_item_to_store(tree, selection);
1460
1461                 if (!selectInfo->info->pfnSelectedStoreCallback ||
1462                  selectInfo->info->pfnSelectedStoreCallback(store, hwnd,
1463                  selectInfo->info->pvArg))
1464                 {
1465                     selectInfo->store = store;
1466                     free_store_info(tree);
1467                     EndDialog(hwnd, IDOK);
1468                 }
1469                 else
1470                     CertCloseStore(store, 0);
1471             }
1472             ret = TRUE;
1473             break;
1474         }
1475         case IDCANCEL:
1476             free_store_info(GetDlgItem(hwnd, IDC_STORE_LIST));
1477             EndDialog(hwnd, IDCANCEL);
1478             ret = TRUE;
1479             break;
1480         }
1481         break;
1482     }
1483     return ret;
1484 }
1485
1486 /***********************************************************************
1487  *              CryptUIDlgSelectStoreW (CRYPTUI.@)
1488  */
1489 HCERTSTORE WINAPI CryptUIDlgSelectStoreW(PCRYPTUI_SELECTSTORE_INFO_W info)
1490 {
1491     struct SelectStoreInfo selectInfo = { info, NULL };
1492
1493     TRACE("(%p)\n", info);
1494
1495     if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_W))
1496     {
1497         WARN("unexpected size %d\n", info->dwSize);
1498         SetLastError(E_INVALIDARG);
1499         return NULL;
1500     }
1501     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_SELECT_STORE), info->parent,
1502      select_store_dlg_proc, (LPARAM)&selectInfo);
1503     return selectInfo.store;
1504 }
1505
1506 /***********************************************************************
1507  *              CryptUIDlgSelectStoreA (CRYPTUI.@)
1508  */
1509 HCERTSTORE WINAPI CryptUIDlgSelectStoreA(PCRYPTUI_SELECTSTORE_INFO_A info)
1510 {
1511     CRYPTUI_SELECTSTORE_INFO_W infoW;
1512     HCERTSTORE ret;
1513     int len;
1514
1515     TRACE("(%p)\n", info);
1516
1517     if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_A))
1518     {
1519         WARN("unexpected size %d\n", info->dwSize);
1520         SetLastError(E_INVALIDARG);
1521         return NULL;
1522     }
1523     memcpy(&infoW, &info, sizeof(info));
1524     if (info->pszTitle)
1525     {
1526         len = MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, NULL, 0);
1527         infoW.pwszTitle = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1528         MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, infoW.pwszTitle,
1529          len);
1530     }
1531     if (info->pszText)
1532     {
1533         len = MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, NULL, 0);
1534         infoW.pwszText = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1535         MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, infoW.pwszText, len);
1536     }
1537     ret = CryptUIDlgSelectStoreW(&infoW);
1538     HeapFree(GetProcessHeap(), 0, infoW.pwszText);
1539     HeapFree(GetProcessHeap(), 0, infoW.pwszTitle);
1540     return ret;
1541 }
1542
1543 /***********************************************************************
1544  *              CryptUIDlgViewCertificateA (CRYPTUI.@)
1545  */
1546 BOOL WINAPI CryptUIDlgViewCertificateA(
1547  PCCRYPTUI_VIEWCERTIFICATE_STRUCTA pCertViewInfo, BOOL *pfPropertiesChanged)
1548 {
1549     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
1550     LPWSTR title = NULL;
1551     BOOL ret;
1552
1553     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
1554
1555     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
1556     if (pCertViewInfo->szTitle)
1557     {
1558         int len = MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1,
1559          NULL, 0);
1560
1561         title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1562         if (title)
1563         {
1564             MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, title,
1565              len);
1566             viewInfo.szTitle = title;
1567         }
1568         else
1569         {
1570             ret = FALSE;
1571             goto error;
1572         }
1573     }
1574     if (pCertViewInfo->cPropSheetPages)
1575     {
1576         FIXME("ignoring additional prop sheet pages\n");
1577         viewInfo.cPropSheetPages = 0;
1578     }
1579     ret = CryptUIDlgViewCertificateW(&viewInfo, pfPropertiesChanged);
1580     HeapFree(GetProcessHeap(), 0, title);
1581 error:
1582     return ret;
1583 }
1584
1585 struct ReadStringStruct
1586 {
1587     LPCWSTR buf;
1588     LONG pos;
1589     LONG len;
1590 };
1591
1592 static DWORD CALLBACK read_text_callback(DWORD_PTR dwCookie, LPBYTE buf,
1593  LONG cb, LONG *pcb)
1594 {
1595     struct ReadStringStruct *string = (struct ReadStringStruct *)dwCookie;
1596     LONG cch = min(cb / sizeof(WCHAR), string->len - string->pos);
1597
1598     TRACE("(%p, %p, %d, %p)\n", string, buf, cb, pcb);
1599
1600     memmove(buf, string->buf + string->pos, cch * sizeof(WCHAR));
1601     string->pos += cch;
1602     *pcb = cch * sizeof(WCHAR);
1603     return 0;
1604 }
1605
1606 static void add_unformatted_text_to_control(HWND hwnd, LPCWSTR text, LONG len)
1607 {
1608     struct ReadStringStruct string;
1609     EDITSTREAM editstream;
1610
1611     TRACE("(%p, %s)\n", hwnd, debugstr_wn(text, len));
1612
1613     string.buf = text;
1614     string.pos = 0;
1615     string.len = len;
1616     editstream.dwCookie = (DWORD_PTR)&string;
1617     editstream.dwError = 0;
1618     editstream.pfnCallback = read_text_callback;
1619     SendMessageW(hwnd, EM_STREAMIN, SF_TEXT | SFF_SELECTION | SF_UNICODE,
1620      (LPARAM)&editstream);
1621 }
1622
1623 static void add_string_resource_to_control(HWND hwnd, int id)
1624 {
1625     LPWSTR str;
1626     LONG len;
1627
1628     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
1629     add_unformatted_text_to_control(hwnd, str, len);
1630 }
1631
1632 static void add_text_with_paraformat_to_control(HWND hwnd, LPCWSTR text,
1633  LONG len, const PARAFORMAT2 *fmt)
1634 {
1635     add_unformatted_text_to_control(hwnd, text, len);
1636     SendMessageW(hwnd, EM_SETPARAFORMAT, 0, (LPARAM)fmt);
1637 }
1638
1639 static void add_string_resource_with_paraformat_to_control(HWND hwnd, int id,
1640  const PARAFORMAT2 *fmt)
1641 {
1642     LPWSTR str;
1643     LONG len;
1644
1645     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
1646     add_text_with_paraformat_to_control(hwnd, str, len, fmt);
1647 }
1648
1649 static LPWSTR get_cert_name_string(PCCERT_CONTEXT pCertContext, DWORD dwType,
1650  DWORD dwFlags)
1651 {
1652     LPWSTR buf = NULL;
1653     DWORD len;
1654
1655     len = CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, NULL, 0);
1656     if (len)
1657     {
1658         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1659         if (buf)
1660             CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, buf, len);
1661     }
1662     return buf;
1663 }
1664
1665 static void add_cert_string_to_control(HWND hwnd, PCCERT_CONTEXT pCertContext,
1666  DWORD dwType, DWORD dwFlags)
1667 {
1668     LPWSTR name = get_cert_name_string(pCertContext, dwType, dwFlags);
1669
1670     if (name)
1671     {
1672         /* Don't include NULL-terminator in output */
1673         DWORD len = lstrlenW(name);
1674
1675         add_unformatted_text_to_control(hwnd, name, len);
1676         HeapFree(GetProcessHeap(), 0, name);
1677     }
1678 }
1679
1680 static void add_icon_to_control(HWND hwnd, int id)
1681 {
1682     HRESULT hr;
1683     LPRICHEDITOLE richEditOle = NULL;
1684     LPOLEOBJECT object = NULL;
1685     CLSID clsid;
1686     LPOLECACHE oleCache = NULL;
1687     FORMATETC formatEtc;
1688     DWORD conn;
1689     LPDATAOBJECT dataObject = NULL;
1690     HBITMAP bitmap = NULL;
1691     RECT rect;
1692     STGMEDIUM stgm;
1693     LPOLECLIENTSITE clientSite = NULL;
1694     REOBJECT reObject;
1695
1696     TRACE("(%p, %d)\n", hwnd, id);
1697
1698     SendMessageW(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richEditOle);
1699     if (!richEditOle)
1700         goto end;
1701     hr = OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject,
1702      (void**)&object);
1703     if (FAILED(hr))
1704         goto end;
1705     hr = IOleObject_GetUserClassID(object, &clsid);
1706     if (FAILED(hr))
1707         goto end;
1708     hr = IOleObject_QueryInterface(object, &IID_IOleCache, (void**)&oleCache);
1709     if (FAILED(hr))
1710         goto end;
1711     formatEtc.cfFormat = CF_BITMAP;
1712     formatEtc.ptd = NULL;
1713     formatEtc.dwAspect = DVASPECT_CONTENT;
1714     formatEtc.lindex = -1;
1715     formatEtc.tymed = TYMED_GDI;
1716     hr = IOleCache_Cache(oleCache, &formatEtc, 0, &conn);
1717     if (FAILED(hr))
1718         goto end;
1719     hr = IOleObject_QueryInterface(object, &IID_IDataObject,
1720      (void**)&dataObject);
1721     if (FAILED(hr))
1722         goto end;
1723     hr = IRichEditOle_GetClientSite(richEditOle, &clientSite);
1724     if (FAILED(hr))
1725         goto end;
1726     bitmap = LoadImageW(hInstance, MAKEINTRESOURCEW(id), IMAGE_BITMAP, 0, 0,
1727      LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
1728     if (!bitmap)
1729         goto end;
1730     rect.left = rect.top = 0;
1731     rect.right = GetSystemMetrics(SM_CXICON);
1732     rect.bottom = GetSystemMetrics(SM_CYICON);
1733     stgm.tymed = TYMED_GDI;
1734     stgm.u.hBitmap = bitmap;
1735     stgm.pUnkForRelease = NULL;
1736     hr = IDataObject_SetData(dataObject, &formatEtc, &stgm, TRUE);
1737     if (FAILED(hr))
1738         goto end;
1739
1740     reObject.cbStruct = sizeof(reObject);
1741     reObject.cp = REO_CP_SELECTION;
1742     reObject.clsid = clsid;
1743     reObject.poleobj = object;
1744     reObject.pstg = NULL;
1745     reObject.polesite = clientSite;
1746     reObject.sizel.cx = reObject.sizel.cy = 0;
1747     reObject.dvaspect = DVASPECT_CONTENT;
1748     reObject.dwFlags = 0;
1749     reObject.dwUser = 0;
1750
1751     IRichEditOle_InsertObject(richEditOle, &reObject);
1752
1753 end:
1754     if (clientSite)
1755         IOleClientSite_Release(clientSite);
1756     if (dataObject)
1757         IDataObject_Release(dataObject);
1758     if (oleCache)
1759         IOleCache_Release(oleCache);
1760     if (object)
1761         IOleObject_Release(object);
1762     if (richEditOle)
1763         IRichEditOle_Release(richEditOle);
1764 }
1765
1766 #define MY_INDENT 200
1767
1768 static void add_oid_text_to_control(HWND hwnd, char *oid)
1769 {
1770     WCHAR nl = '\n';
1771     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid, 0);
1772     PARAFORMAT2 parFmt;
1773
1774     parFmt.cbSize = sizeof(parFmt);
1775     parFmt.dwMask = PFM_STARTINDENT;
1776     parFmt.dxStartIndent = MY_INDENT * 3;
1777     if (oidInfo)
1778     {
1779         add_text_with_paraformat_to_control(hwnd, oidInfo->pwszName,
1780          lstrlenW(oidInfo->pwszName), &parFmt);
1781         add_unformatted_text_to_control(hwnd, &nl, 1);
1782     }
1783 }
1784
1785 struct OIDToString
1786 {
1787     LPCSTR oid;
1788     int    id;
1789 };
1790
1791 /* The following list MUST be lexicographically sorted by OID */
1792 static struct OIDToString oidMap[] = {
1793  /* 1.3.6.1.4.1.311.10.3.1 */
1794  { szOID_KP_CTL_USAGE_SIGNING, IDS_PURPOSE_CTL_USAGE_SIGNING },
1795  /* 1.3.6.1.4.1.311.10.3.4 */
1796  { szOID_KP_EFS, IDS_PURPOSE_EFS },
1797  /* 1.3.6.1.4.1.311.10.3.4.1 */
1798  { szOID_EFS_RECOVERY, IDS_PURPOSE_EFS_RECOVERY },
1799  /* 1.3.6.1.4.1.311.10.3.5 */
1800  { szOID_WHQL_CRYPTO, IDS_PURPOSE_WHQL },
1801  /* 1.3.6.1.4.1.311.10.3.6 */
1802  { szOID_NT5_CRYPTO, IDS_PURPOSE_NT5 },
1803  /* 1.3.6.1.4.1.311.10.3.7 */
1804  { szOID_OEM_WHQL_CRYPTO, IDS_PURPOSE_OEM_WHQL },
1805  /* 1.3.6.1.4.1.311.10.3.8 */
1806  { szOID_EMBEDDED_NT_CRYPTO, IDS_PURPOSE_EMBEDDED_NT },
1807  /* 1.3.6.1.4.1.311.10.3.9 */
1808  { szOID_ROOT_LIST_SIGNER, IDS_PURPOSE_ROOT_LIST_SIGNER },
1809  /* 1.3.6.1.4.1.311.10.3.10 */
1810  { szOID_KP_QUALIFIED_SUBORDINATION, IDS_PURPOSE_QUALIFIED_SUBORDINATION },
1811  /* 1.3.6.1.4.1.311.10.3.11 */
1812  { szOID_KP_KEY_RECOVERY, IDS_PURPOSE_KEY_RECOVERY },
1813  /* 1.3.6.1.4.1.311.10.3.12 */
1814  { szOID_KP_DOCUMENT_SIGNING, IDS_PURPOSE_DOCUMENT_SIGNING },
1815  /* 1.3.6.1.4.1.311.10.3.13 */
1816  { szOID_KP_LIFETIME_SIGNING, IDS_PURPOSE_LIFETIME_SIGNING },
1817  /* 1.3.6.1.4.1.311.10.5.1 */
1818  { szOID_DRM, IDS_PURPOSE_DRM },
1819  /* 1.3.6.1.4.1.311.10.6.1 */
1820  { szOID_LICENSES, IDS_PURPOSE_LICENSES },
1821  /* 1.3.6.1.4.1.311.10.6.2 */
1822  { szOID_LICENSE_SERVER, IDS_PURPOSE_LICENSE_SERVER },
1823  /* 1.3.6.1.4.1.311.20.2.1 */
1824  { szOID_ENROLLMENT_AGENT, IDS_PURPOSE_ENROLLMENT_AGENT },
1825  /* 1.3.6.1.4.1.311.20.2.2 */
1826  { szOID_KP_SMARTCARD_LOGON, IDS_PURPOSE_SMARTCARD_LOGON },
1827  /* 1.3.6.1.4.1.311.21.5 */
1828  { szOID_KP_CA_EXCHANGE, IDS_PURPOSE_CA_EXCHANGE },
1829  /* 1.3.6.1.4.1.311.21.6 */
1830  { szOID_KP_KEY_RECOVERY_AGENT, IDS_PURPOSE_KEY_RECOVERY_AGENT },
1831  /* 1.3.6.1.4.1.311.21.19 */
1832  { szOID_DS_EMAIL_REPLICATION, IDS_PURPOSE_DS_EMAIL_REPLICATION },
1833  /* 1.3.6.1.5.5.7.3.1 */
1834  { szOID_PKIX_KP_SERVER_AUTH, IDS_PURPOSE_SERVER_AUTH },
1835  /* 1.3.6.1.5.5.7.3.2 */
1836  { szOID_PKIX_KP_CLIENT_AUTH, IDS_PURPOSE_CLIENT_AUTH },
1837  /* 1.3.6.1.5.5.7.3.3 */
1838  { szOID_PKIX_KP_CODE_SIGNING, IDS_PURPOSE_CODE_SIGNING },
1839  /* 1.3.6.1.5.5.7.3.4 */
1840  { szOID_PKIX_KP_EMAIL_PROTECTION, IDS_PURPOSE_EMAIL_PROTECTION },
1841  /* 1.3.6.1.5.5.7.3.5 */
1842  { szOID_PKIX_KP_IPSEC_END_SYSTEM, IDS_PURPOSE_IPSEC },
1843  /* 1.3.6.1.5.5.7.3.6 */
1844  { szOID_PKIX_KP_IPSEC_TUNNEL, IDS_PURPOSE_IPSEC },
1845  /* 1.3.6.1.5.5.7.3.7 */
1846  { szOID_PKIX_KP_IPSEC_USER, IDS_PURPOSE_IPSEC },
1847  /* 1.3.6.1.5.5.7.3.8 */
1848  { szOID_PKIX_KP_TIMESTAMP_SIGNING, IDS_PURPOSE_TIMESTAMP_SIGNING },
1849 };
1850
1851 static struct OIDToString *findSupportedOID(LPCSTR oid)
1852 {
1853     int indexHigh = sizeof(oidMap) / sizeof(oidMap[0]) - 1, indexLow = 0, i;
1854     struct OIDToString *ret = NULL;
1855
1856     for (i = (indexLow + indexHigh) / 2; !ret && indexLow <= indexHigh;
1857      i = (indexLow + indexHigh) / 2)
1858     {
1859         int cmp;
1860
1861         cmp = strcmp(oid, oidMap[i].oid);
1862         if (!cmp)
1863             ret = &oidMap[i];
1864         else if (cmp > 0)
1865             indexLow = i + 1;
1866         else
1867             indexHigh = i - 1;
1868     }
1869     return ret;
1870 }
1871
1872 static void add_local_oid_text_to_control(HWND text, LPCSTR oid)
1873 {
1874     struct OIDToString *entry;
1875     WCHAR nl = '\n';
1876     PARAFORMAT2 parFmt;
1877
1878     parFmt.cbSize = sizeof(parFmt);
1879     parFmt.dwMask = PFM_STARTINDENT;
1880     parFmt.dxStartIndent = MY_INDENT * 3;
1881     if ((entry = findSupportedOID(oid)))
1882     {
1883         WCHAR *str, *linebreak, *ptr;
1884         BOOL multiline = FALSE;
1885         int len;
1886
1887         len = LoadStringW(hInstance, entry->id, (LPWSTR)&str, 0);
1888         ptr = str;
1889         do {
1890             if ((linebreak = memchrW(ptr, '\n', len)))
1891             {
1892                 WCHAR copy[MAX_STRING_LEN];
1893
1894                 multiline = TRUE;
1895                 /* The source string contains a newline, which the richedit
1896                  * control won't find since it's interpreted as a paragraph
1897                  * break.  Therefore copy up to the newline.  lstrcpynW always
1898                  * NULL-terminates, so pass one more than the length of the
1899                  * source line so the copy includes the entire line and the
1900                  * NULL-terminator.
1901                  */
1902                 lstrcpynW(copy, ptr, linebreak - ptr + 1);
1903                 add_text_with_paraformat_to_control(text, copy,
1904                  linebreak - ptr, &parFmt);
1905                 ptr = linebreak + 1;
1906                 add_unformatted_text_to_control(text, &nl, 1);
1907             }
1908             else if (multiline && *ptr)
1909             {
1910                 /* Add the last line */
1911                 add_text_with_paraformat_to_control(text, ptr,
1912                  len - (ptr - str), &parFmt);
1913                 add_unformatted_text_to_control(text, &nl, 1);
1914             }
1915         } while (linebreak);
1916         if (!multiline)
1917         {
1918             add_text_with_paraformat_to_control(text, str, len, &parFmt);
1919             add_unformatted_text_to_control(text, &nl, 1);
1920         }
1921     }
1922     else
1923     {
1924         WCHAR *oidW = HeapAlloc(GetProcessHeap(), 0,
1925          (strlen(oid) + 1) * sizeof(WCHAR));
1926
1927         if (oidW)
1928         {
1929             LPCSTR src;
1930             WCHAR *dst;
1931
1932             for (src = oid, dst = oidW; *src; src++, dst++)
1933                 *dst = *src;
1934             *dst = 0;
1935             add_text_with_paraformat_to_control(text, oidW, lstrlenW(oidW),
1936              &parFmt);
1937             add_unformatted_text_to_control(text, &nl, 1);
1938             HeapFree(GetProcessHeap(), 0, oidW);
1939         }
1940     }
1941 }
1942
1943 static void display_app_usages(HWND text, PCCERT_CONTEXT cert,
1944  BOOL *anyUsageAdded)
1945 {
1946     static char any_app_policy[] = szOID_ANY_APPLICATION_POLICY;
1947     WCHAR nl = '\n';
1948     CHARFORMATW charFmt;
1949     PCERT_EXTENSION policyExt;
1950     if (!*anyUsageAdded)
1951     {
1952         PARAFORMAT2 parFmt;
1953
1954         parFmt.cbSize = sizeof(parFmt);
1955         parFmt.dwMask = PFM_STARTINDENT;
1956         parFmt.dxStartIndent = MY_INDENT;
1957         add_string_resource_with_paraformat_to_control(text,
1958          IDS_CERT_INFO_PURPOSES, &parFmt);
1959         add_unformatted_text_to_control(text, &nl, 1);
1960         *anyUsageAdded = TRUE;
1961     }
1962     memset(&charFmt, 0, sizeof(charFmt));
1963     charFmt.cbSize = sizeof(charFmt);
1964     charFmt.dwMask = CFM_BOLD;
1965     charFmt.dwEffects = 0;
1966     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
1967     if ((policyExt = CertFindExtension(szOID_APPLICATION_CERT_POLICIES,
1968      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1969     {
1970         CERT_POLICIES_INFO *policies;
1971         DWORD size;
1972
1973         if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES,
1974          policyExt->Value.pbData, policyExt->Value.cbData,
1975          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
1976         {
1977             DWORD i;
1978
1979             for (i = 0; i < policies->cPolicyInfo; i++)
1980             {
1981                 DWORD j;
1982
1983                 for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
1984                     add_local_oid_text_to_control(text,
1985                      policies->rgPolicyInfo[i].rgPolicyQualifier[j].
1986                      pszPolicyQualifierId);
1987             }
1988             LocalFree(policies);
1989         }
1990     }
1991     else
1992         add_oid_text_to_control(text, any_app_policy);
1993 }
1994
1995 static BOOL display_cert_usages(HWND text, PCCERT_CONTEXT cert,
1996  BOOL *anyUsageAdded)
1997 {
1998     WCHAR nl = '\n';
1999     DWORD size;
2000     BOOL badUsages = FALSE;
2001
2002     if (CertGetEnhancedKeyUsage(cert, 0, NULL, &size))
2003     {
2004         CHARFORMATW charFmt;
2005         static char any_cert_policy[] = szOID_ANY_CERT_POLICY;
2006         PCERT_ENHKEY_USAGE usage = HeapAlloc(GetProcessHeap(), 0, size);
2007
2008         if (usage)
2009         {
2010             if (CertGetEnhancedKeyUsage(cert, 0, usage, &size))
2011             {
2012                 DWORD i;
2013
2014                 if (!*anyUsageAdded)
2015                 {
2016                     PARAFORMAT2 parFmt;
2017
2018                     parFmt.cbSize = sizeof(parFmt);
2019                     parFmt.dwMask = PFM_STARTINDENT;
2020                     parFmt.dxStartIndent = MY_INDENT;
2021                     add_string_resource_with_paraformat_to_control(text,
2022                      IDS_CERT_INFO_PURPOSES, &parFmt);
2023                     add_unformatted_text_to_control(text, &nl, 1);
2024                     *anyUsageAdded = TRUE;
2025                 }
2026                 memset(&charFmt, 0, sizeof(charFmt));
2027                 charFmt.cbSize = sizeof(charFmt);
2028                 charFmt.dwMask = CFM_BOLD;
2029                 charFmt.dwEffects = 0;
2030                 SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION,
2031                  (LPARAM)&charFmt);
2032                 if (!usage->cUsageIdentifier)
2033                     add_oid_text_to_control(text, any_cert_policy);
2034                 else
2035                     for (i = 0; i < usage->cUsageIdentifier; i++)
2036                         add_local_oid_text_to_control(text,
2037                          usage->rgpszUsageIdentifier[i]);
2038             }
2039             else
2040                 badUsages = TRUE;
2041             HeapFree(GetProcessHeap(), 0, usage);
2042         }
2043         else
2044             badUsages = TRUE;
2045     }
2046     else
2047         badUsages = TRUE;
2048     return badUsages;
2049 }
2050
2051 static void set_policy_text(HWND text,
2052  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2053 {
2054     BOOL includeCertUsages = FALSE, includeAppUsages = FALSE;
2055     BOOL badUsages = FALSE, anyUsageAdded = FALSE;
2056
2057     if (pCertViewInfo->cPurposes)
2058     {
2059         DWORD i;
2060
2061         for (i = 0; i < pCertViewInfo->cPurposes; i++)
2062         {
2063             if (!strcmp(pCertViewInfo->rgszPurposes[i], szOID_ANY_CERT_POLICY))
2064                 includeCertUsages = TRUE;
2065             else if (!strcmp(pCertViewInfo->rgszPurposes[i],
2066              szOID_ANY_APPLICATION_POLICY))
2067                 includeAppUsages = TRUE;
2068             else
2069                 badUsages = TRUE;
2070         }
2071     }
2072     else
2073         includeAppUsages = includeCertUsages = TRUE;
2074     if (includeAppUsages)
2075         display_app_usages(text, pCertViewInfo->pCertContext, &anyUsageAdded);
2076     if (includeCertUsages)
2077         badUsages = display_cert_usages(text, pCertViewInfo->pCertContext,
2078          &anyUsageAdded);
2079     if (badUsages)
2080     {
2081         PARAFORMAT2 parFmt;
2082
2083         parFmt.cbSize = sizeof(parFmt);
2084         parFmt.dwMask = PFM_STARTINDENT;
2085         parFmt.dxStartIndent = MY_INDENT;
2086         add_string_resource_with_paraformat_to_control(text,
2087          IDS_CERT_INFO_BAD_PURPOSES, &parFmt);
2088     }
2089 }
2090
2091 static CRYPT_OBJID_BLOB *find_policy_qualifier(CERT_POLICIES_INFO *policies,
2092  LPCSTR policyOid)
2093 {
2094     CRYPT_OBJID_BLOB *ret = NULL;
2095     DWORD i;
2096
2097     for (i = 0; !ret && i < policies->cPolicyInfo; i++)
2098     {
2099         DWORD j;
2100
2101         for (j = 0; !ret && j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
2102             if (!strcmp(policies->rgPolicyInfo[i].rgPolicyQualifier[j].
2103              pszPolicyQualifierId, policyOid))
2104                 ret = &policies->rgPolicyInfo[i].rgPolicyQualifier[j].
2105                  Qualifier;
2106     }
2107     return ret;
2108 }
2109
2110 static WCHAR *get_cps_str_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
2111 {
2112     LPWSTR qualifierStr = NULL;
2113     CERT_NAME_VALUE *qualifierValue;
2114     DWORD size;
2115
2116     if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_VALUE,
2117      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
2118      &qualifierValue, &size))
2119     {
2120         size = CertRDNValueToStrW(qualifierValue->dwValueType,
2121          &qualifierValue->Value, NULL, 0);
2122         qualifierStr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
2123         if (qualifierStr)
2124             CertRDNValueToStrW(qualifierValue->dwValueType,
2125              &qualifierValue->Value, qualifierStr, size);
2126         LocalFree(qualifierValue);
2127     }
2128     return qualifierStr;
2129 }
2130
2131 static WCHAR *get_user_notice_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
2132 {
2133     LPWSTR str = NULL;
2134     CERT_POLICY_QUALIFIER_USER_NOTICE *qualifierValue;
2135     DWORD size;
2136
2137     if (CryptDecodeObjectEx(X509_ASN_ENCODING,
2138      X509_PKIX_POLICY_QUALIFIER_USERNOTICE,
2139      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
2140      &qualifierValue, &size))
2141     {
2142         str = HeapAlloc(GetProcessHeap(), 0,
2143          (strlenW(qualifierValue->pszDisplayText) + 1) * sizeof(WCHAR));
2144         if (str)
2145             strcpyW(str, qualifierValue->pszDisplayText);
2146         LocalFree(qualifierValue);
2147     }
2148     return str;
2149 }
2150
2151 struct IssuerStatement
2152 {
2153     LPWSTR cps;
2154     LPWSTR userNotice;
2155 };
2156
2157 static void set_issuer_statement(HWND hwnd,
2158  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2159 {
2160     PCERT_EXTENSION policyExt;
2161
2162     if (!(pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ISSUERSTATEMENT) &&
2163      (policyExt = CertFindExtension(szOID_CERT_POLICIES,
2164      pCertViewInfo->pCertContext->pCertInfo->cExtension,
2165      pCertViewInfo->pCertContext->pCertInfo->rgExtension)))
2166     {
2167         CERT_POLICIES_INFO *policies;
2168         DWORD size;
2169
2170         if (CryptDecodeObjectEx(X509_ASN_ENCODING, policyExt->pszObjId,
2171          policyExt->Value.pbData, policyExt->Value.cbData,
2172          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
2173         {
2174             CRYPT_OBJID_BLOB *qualifier;
2175             LPWSTR cps = NULL, userNotice = NULL;
2176
2177             if ((qualifier = find_policy_qualifier(policies,
2178              szOID_PKIX_POLICY_QUALIFIER_CPS)))
2179                 cps = get_cps_str_from_qualifier(qualifier);
2180             if ((qualifier = find_policy_qualifier(policies,
2181              szOID_PKIX_POLICY_QUALIFIER_USERNOTICE)))
2182                 userNotice = get_user_notice_from_qualifier(qualifier);
2183             if (cps || userNotice)
2184             {
2185                 struct IssuerStatement *issuerStatement =
2186                  HeapAlloc(GetProcessHeap(), 0, sizeof(struct IssuerStatement));
2187
2188                 if (issuerStatement)
2189                 {
2190                     issuerStatement->cps = cps;
2191                     issuerStatement->userNotice = userNotice;
2192                     EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), TRUE);
2193                     SetWindowLongPtrW(hwnd, DWLP_USER,
2194                      (ULONG_PTR)issuerStatement);
2195                 }
2196             }
2197             LocalFree(policies);
2198         }
2199     }
2200 }
2201
2202 static void set_cert_info(HWND hwnd,
2203  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2204 {
2205     CHARFORMATW charFmt;
2206     PARAFORMAT2 parFmt;
2207     HWND icon = GetDlgItem(hwnd, IDC_CERTIFICATE_ICON);
2208     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_INFO);
2209     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
2210      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
2211      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
2212      pCertViewInfo->idxCounterSigner);
2213     CRYPT_PROVIDER_CERT *root =
2214      &provSigner->pasCertChain[provSigner->csCertChain - 1];
2215
2216     if (!provSigner->pChainContext ||
2217      (provSigner->pChainContext->TrustStatus.dwErrorStatus &
2218      CERT_TRUST_IS_PARTIAL_CHAIN))
2219         add_icon_to_control(icon, IDB_CERT_WARNING);
2220     else if (!root->fTrustedRoot)
2221         add_icon_to_control(icon, IDB_CERT_ERROR);
2222     else
2223         add_icon_to_control(icon, IDB_CERT);
2224
2225     memset(&charFmt, 0, sizeof(charFmt));
2226     charFmt.cbSize = sizeof(charFmt);
2227     charFmt.dwMask = CFM_BOLD;
2228     charFmt.dwEffects = CFE_BOLD;
2229     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2230     /* FIXME: vertically center text */
2231     parFmt.cbSize = sizeof(parFmt);
2232     parFmt.dwMask = PFM_STARTINDENT;
2233     parFmt.dxStartIndent = MY_INDENT;
2234     add_string_resource_with_paraformat_to_control(text,
2235      IDS_CERTIFICATEINFORMATION, &parFmt);
2236
2237     text = GetDlgItem(hwnd, IDC_CERTIFICATE_STATUS);
2238     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2239     if (provSigner->dwError == TRUST_E_CERT_SIGNATURE)
2240         add_string_resource_with_paraformat_to_control(text,
2241          IDS_CERT_INFO_BAD_SIG, &parFmt);
2242     else if (!provSigner->pChainContext ||
2243      (provSigner->pChainContext->TrustStatus.dwErrorStatus &
2244      CERT_TRUST_IS_PARTIAL_CHAIN))
2245         add_string_resource_with_paraformat_to_control(text,
2246          IDS_CERT_INFO_PARTIAL_CHAIN, &parFmt);
2247     else if (!root->fTrustedRoot)
2248     {
2249         if (provSigner->csCertChain == 1 && root->fSelfSigned)
2250             add_string_resource_with_paraformat_to_control(text,
2251              IDS_CERT_INFO_UNTRUSTED_CA, &parFmt);
2252         else
2253             add_string_resource_with_paraformat_to_control(text,
2254              IDS_CERT_INFO_UNTRUSTED_ROOT, &parFmt);
2255     }
2256     else
2257     {
2258         set_policy_text(text, pCertViewInfo);
2259         set_issuer_statement(hwnd, pCertViewInfo);
2260     }
2261 }
2262
2263 static void set_cert_name_string(HWND hwnd, PCCERT_CONTEXT cert,
2264  DWORD nameFlags, int heading)
2265 {
2266     WCHAR nl = '\n';
2267     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
2268     CHARFORMATW charFmt;
2269     PARAFORMAT2 parFmt;
2270
2271     memset(&charFmt, 0, sizeof(charFmt));
2272     charFmt.cbSize = sizeof(charFmt);
2273     charFmt.dwMask = CFM_BOLD;
2274     charFmt.dwEffects = CFE_BOLD;
2275     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2276     parFmt.cbSize = sizeof(parFmt);
2277     parFmt.dwMask = PFM_STARTINDENT;
2278     parFmt.dxStartIndent = MY_INDENT * 3;
2279     add_string_resource_with_paraformat_to_control(text, heading, &parFmt);
2280     charFmt.dwEffects = 0;
2281     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2282     add_cert_string_to_control(text, cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
2283      nameFlags);
2284     add_unformatted_text_to_control(text, &nl, 1);
2285     add_unformatted_text_to_control(text, &nl, 1);
2286     add_unformatted_text_to_control(text, &nl, 1);
2287
2288 }
2289
2290 static void add_date_string_to_control(HWND hwnd, const FILETIME *fileTime)
2291 {
2292     WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */
2293     WCHAR date[80];
2294     SYSTEMTIME sysTime;
2295
2296     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt,
2297      sizeof(dateFmt) / sizeof(dateFmt[0]));
2298     FileTimeToSystemTime(fileTime, &sysTime);
2299     GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date,
2300      sizeof(date) / sizeof(date[0]));
2301     add_unformatted_text_to_control(hwnd, date, lstrlenW(date));
2302 }
2303
2304 static void set_cert_validity_period(HWND hwnd, PCCERT_CONTEXT cert)
2305 {
2306     WCHAR nl = '\n';
2307     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
2308     CHARFORMATW charFmt;
2309     PARAFORMAT2 parFmt;
2310
2311     memset(&charFmt, 0, sizeof(charFmt));
2312     charFmt.cbSize = sizeof(charFmt);
2313     charFmt.dwMask = CFM_BOLD;
2314     charFmt.dwEffects = CFE_BOLD;
2315     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2316     parFmt.cbSize = sizeof(parFmt);
2317     parFmt.dwMask = PFM_STARTINDENT;
2318     parFmt.dxStartIndent = MY_INDENT * 3;
2319     add_string_resource_with_paraformat_to_control(text, IDS_VALID_FROM,
2320      &parFmt);
2321     charFmt.dwEffects = 0;
2322     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2323     add_date_string_to_control(text, &cert->pCertInfo->NotBefore);
2324     charFmt.dwEffects = CFE_BOLD;
2325     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2326     add_string_resource_to_control(text, IDS_VALID_TO);
2327     charFmt.dwEffects = 0;
2328     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
2329     add_date_string_to_control(text, &cert->pCertInfo->NotAfter);
2330     add_unformatted_text_to_control(text, &nl, 1);
2331 }
2332
2333 static void set_general_info(HWND hwnd,
2334  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2335 {
2336     set_cert_info(hwnd, pCertViewInfo);
2337     set_cert_name_string(hwnd, pCertViewInfo->pCertContext, 0,
2338      IDS_SUBJECT_HEADING);
2339     set_cert_name_string(hwnd, pCertViewInfo->pCertContext,
2340      CERT_NAME_ISSUER_FLAG, IDS_ISSUER_HEADING);
2341     set_cert_validity_period(hwnd, pCertViewInfo->pCertContext);
2342 }
2343
2344 static LRESULT CALLBACK user_notice_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
2345  LPARAM lp)
2346 {
2347     LRESULT ret = 0;
2348     HWND text;
2349     struct IssuerStatement *issuerStatement;
2350
2351     switch (msg)
2352     {
2353     case WM_INITDIALOG:
2354         text = GetDlgItem(hwnd, IDC_USERNOTICE);
2355         issuerStatement = (struct IssuerStatement *)lp;
2356         add_unformatted_text_to_control(text, issuerStatement->userNotice,
2357          strlenW(issuerStatement->userNotice));
2358         if (issuerStatement->cps)
2359             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)issuerStatement->cps);
2360         else
2361             EnableWindow(GetDlgItem(hwnd, IDC_CPS), FALSE);
2362         break;
2363     case WM_COMMAND:
2364         switch (wp)
2365         {
2366         case IDOK:
2367             EndDialog(hwnd, IDOK);
2368             ret = TRUE;
2369             break;
2370         case IDC_CPS:
2371         {
2372             IBindCtx *bctx = NULL;
2373             LPWSTR cps;
2374
2375             CreateBindCtx(0, &bctx);
2376             cps = (LPWSTR)GetWindowLongPtrW(hwnd, DWLP_USER);
2377             HlinkSimpleNavigateToString(cps, NULL, NULL, NULL, bctx, NULL,
2378              HLNF_OPENINNEWWINDOW, 0);
2379             IBindCtx_Release(bctx);
2380             break;
2381         }
2382         }
2383     }
2384     return ret;
2385 }
2386
2387 static void show_user_notice(HWND hwnd, struct IssuerStatement *issuerStatement)
2388 {
2389     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_USERNOTICE), hwnd,
2390      user_notice_dlg_proc, (LPARAM)issuerStatement);
2391 }
2392
2393 static LRESULT CALLBACK general_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
2394  LPARAM lp)
2395 {
2396     PROPSHEETPAGEW *page;
2397     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
2398
2399     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2400
2401     switch (msg)
2402     {
2403     case WM_INITDIALOG:
2404         page = (PROPSHEETPAGEW *)lp;
2405         pCertViewInfo = (PCCRYPTUI_VIEWCERTIFICATE_STRUCTW)page->lParam;
2406         if (pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ADDTOSTORE)
2407             ShowWindow(GetDlgItem(hwnd, IDC_ADDTOSTORE), FALSE);
2408         EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), FALSE);
2409         set_general_info(hwnd, pCertViewInfo);
2410         break;
2411     case WM_COMMAND:
2412         switch (wp)
2413         {
2414         case IDC_ADDTOSTORE:
2415             CryptUIWizImport(0, hwnd, NULL, NULL, NULL);
2416             break;
2417         case IDC_ISSUERSTATEMENT:
2418         {
2419             struct IssuerStatement *issuerStatement =
2420              (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
2421
2422             if (issuerStatement)
2423             {
2424                 if (issuerStatement->userNotice)
2425                     show_user_notice(hwnd, issuerStatement);
2426                 else if (issuerStatement->cps)
2427                 {
2428                     IBindCtx *bctx = NULL;
2429
2430                     CreateBindCtx(0, &bctx);
2431                     HlinkSimpleNavigateToString(issuerStatement->cps, NULL,
2432                      NULL, NULL, bctx, NULL, HLNF_OPENINNEWWINDOW, 0);
2433                     IBindCtx_Release(bctx);
2434                 }
2435             }
2436             break;
2437         }
2438         }
2439         break;
2440     }
2441     return 0;
2442 }
2443
2444 static UINT CALLBACK general_callback_proc(HWND hwnd, UINT msg,
2445  PROPSHEETPAGEW *page)
2446 {
2447     struct IssuerStatement *issuerStatement;
2448
2449     switch (msg)
2450     {
2451     case PSPCB_RELEASE:
2452         issuerStatement =
2453          (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
2454         if (issuerStatement)
2455         {
2456             HeapFree(GetProcessHeap(), 0, issuerStatement->cps);
2457             HeapFree(GetProcessHeap(), 0, issuerStatement->userNotice);
2458             HeapFree(GetProcessHeap(), 0, issuerStatement);
2459         }
2460         break;
2461     }
2462     return 1;
2463 }
2464
2465 static void init_general_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2466  PROPSHEETPAGEW *page)
2467 {
2468     memset(page, 0, sizeof(PROPSHEETPAGEW));
2469     page->dwSize = sizeof(PROPSHEETPAGEW);
2470     page->dwFlags = PSP_USECALLBACK;
2471     page->pfnCallback = general_callback_proc;
2472     page->hInstance = hInstance;
2473     page->u.pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL);
2474     page->pfnDlgProc = general_dlg_proc;
2475     page->lParam = (LPARAM)pCertViewInfo;
2476 }
2477
2478 typedef WCHAR * (*field_format_func)(PCCERT_CONTEXT cert);
2479
2480 static WCHAR *field_format_version(PCCERT_CONTEXT cert)
2481 {
2482     static const WCHAR fmt[] = { 'V','%','d',0 };
2483     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, 12 * sizeof(WCHAR));
2484
2485     if (buf)
2486         sprintfW(buf, fmt, cert->pCertInfo->dwVersion);
2487     return buf;
2488 }
2489
2490 static WCHAR *format_hex_string(void *pb, DWORD cb)
2491 {
2492     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, (cb * 3 + 1) * sizeof(WCHAR));
2493
2494     if (buf)
2495     {
2496         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
2497         DWORD i;
2498         WCHAR *ptr;
2499
2500         for (i = 0, ptr = buf; i < cb; i++, ptr += 3)
2501             sprintfW(ptr, fmt, ((BYTE *)pb)[i]);
2502     }
2503     return buf;
2504 }
2505
2506 static WCHAR *field_format_serial_number(PCCERT_CONTEXT cert)
2507 {
2508     return format_hex_string(cert->pCertInfo->SerialNumber.pbData,
2509      cert->pCertInfo->SerialNumber.cbData);
2510 }
2511
2512 static WCHAR *field_format_issuer(PCCERT_CONTEXT cert)
2513 {
2514     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
2515      CERT_NAME_ISSUER_FLAG);
2516 }
2517
2518 static WCHAR *field_format_detailed_cert_name(PCERT_NAME_BLOB name)
2519 {
2520     WCHAR *str = NULL;
2521     DWORD len = CertNameToStrW(X509_ASN_ENCODING, name,
2522      CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2523
2524     if (len)
2525     {
2526         str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2527         if (str)
2528             CertNameToStrW(X509_ASN_ENCODING, name,
2529              CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, str, len);
2530     }
2531     return str;
2532 }
2533
2534 static WCHAR *field_format_detailed_issuer(PCCERT_CONTEXT cert, void *param)
2535 {
2536     return field_format_detailed_cert_name(&cert->pCertInfo->Issuer);
2537 }
2538
2539 static WCHAR *field_format_subject(PCCERT_CONTEXT cert)
2540 {
2541     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
2542 }
2543
2544 static WCHAR *field_format_detailed_subject(PCCERT_CONTEXT cert, void *param)
2545 {
2546     return field_format_detailed_cert_name(&cert->pCertInfo->Subject);
2547 }
2548
2549 static WCHAR *format_long_date(const FILETIME *fileTime)
2550 {
2551     WCHAR dateFmt[80]; /* long enough for LOCALE_SLONGDATE */
2552     DWORD len;
2553     WCHAR *buf = NULL;
2554     SYSTEMTIME sysTime;
2555
2556     /* FIXME: format isn't quite right, want time too */
2557     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SLONGDATE, dateFmt,
2558      sizeof(dateFmt) / sizeof(dateFmt[0]));
2559     FileTimeToSystemTime(fileTime, &sysTime);
2560     len = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, NULL, 0);
2561     if (len)
2562     {
2563         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2564         if (buf)
2565             GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, buf,
2566              len);
2567     }
2568     return buf;
2569 }
2570
2571 static WCHAR *field_format_from_date(PCCERT_CONTEXT cert)
2572 {
2573     return format_long_date(&cert->pCertInfo->NotBefore);
2574 }
2575
2576 static WCHAR *field_format_to_date(PCCERT_CONTEXT cert)
2577 {
2578     return format_long_date(&cert->pCertInfo->NotAfter);
2579 }
2580
2581 static WCHAR *field_format_public_key(PCCERT_CONTEXT cert)
2582 {
2583     PCCRYPT_OID_INFO oidInfo;
2584     WCHAR *buf = NULL;
2585
2586     oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2587      cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 0);
2588     if (oidInfo)
2589     {
2590         WCHAR fmt[MAX_STRING_LEN];
2591
2592         if (LoadStringW(hInstance, IDS_FIELD_PUBLIC_KEY_FORMAT, fmt,
2593          sizeof(fmt) / sizeof(fmt[0])))
2594         {
2595             /* Allocate the output buffer.  Use the number of bytes in the
2596              * public key as a conservative (high) estimate for the number of
2597              * digits in its output.
2598              * The output is of the form (in English)
2599              * "<public key algorithm> (<public key bit length> bits)".
2600              * Ordinarily having two positional parameters in a string is not a
2601              * good idea, but as this isn't a sentence fragment, it shouldn't
2602              * be word-order dependent.
2603              */
2604             buf = HeapAlloc(GetProcessHeap(), 0,
2605              (strlenW(fmt) + strlenW(oidInfo->pwszName) +
2606              cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData * 8)
2607              * sizeof(WCHAR));
2608             if (buf)
2609                 sprintfW(buf, fmt, oidInfo->pwszName,
2610                  CertGetPublicKeyLength(X509_ASN_ENCODING,
2611                   &cert->pCertInfo->SubjectPublicKeyInfo));
2612         }
2613     }
2614     return buf;
2615 }
2616
2617 static WCHAR *field_format_detailed_public_key(PCCERT_CONTEXT cert, void *param)
2618 {
2619     return format_hex_string(
2620      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
2621      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData);
2622 }
2623
2624 struct field_value_data;
2625 struct detail_data
2626 {
2627     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
2628     BOOL *pfPropertiesChanged;
2629     int cFields;
2630     struct field_value_data *fields;
2631 };
2632
2633 typedef void (*add_fields_func)(HWND hwnd, struct detail_data *data);
2634
2635 typedef WCHAR *(*create_detailed_value_func)(PCCERT_CONTEXT cert, void *param);
2636
2637 struct field_value_data
2638 {
2639     create_detailed_value_func create;
2640     LPWSTR detailed_value;
2641     void *param;
2642 };
2643
2644 static void add_field_value_data(struct detail_data *data,
2645  create_detailed_value_func create, void *param)
2646 {
2647     if (data->cFields)
2648         data->fields = HeapReAlloc(GetProcessHeap(), 0, data->fields,
2649          (data->cFields + 1) * sizeof(struct field_value_data));
2650     else
2651         data->fields = HeapAlloc(GetProcessHeap(), 0,
2652          sizeof(struct field_value_data));
2653     if (data->fields)
2654     {
2655         data->fields[data->cFields].create = create;
2656         data->fields[data->cFields].detailed_value = NULL;
2657         data->fields[data->cFields].param = param;
2658         data->cFields++;
2659     }
2660 }
2661
2662 static void add_field_and_value_to_list(HWND hwnd, struct detail_data *data,
2663  LPWSTR field, LPWSTR value, create_detailed_value_func create, void *param)
2664 {
2665     LVITEMW item;
2666     int iItem = SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0);
2667
2668     item.mask = LVIF_TEXT | LVIF_PARAM;
2669     item.iItem = iItem;
2670     item.iSubItem = 0;
2671     item.pszText = field;
2672     item.lParam = (LPARAM)data;
2673     SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item);
2674     if (value)
2675     {
2676         item.pszText = value;
2677         item.iSubItem = 1;
2678         SendMessageW(hwnd, LVM_SETITEMTEXTW, iItem, (LPARAM)&item);
2679     }
2680     add_field_value_data(data, create, param);
2681 }
2682
2683 static void add_string_id_and_value_to_list(HWND hwnd, struct detail_data *data,
2684  int id, LPWSTR value, create_detailed_value_func create, void *param)
2685 {
2686     WCHAR buf[MAX_STRING_LEN];
2687
2688     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
2689     add_field_and_value_to_list(hwnd, data, buf, value, create, param);
2690 }
2691
2692 struct v1_field
2693 {
2694     int id;
2695     field_format_func format;
2696     create_detailed_value_func create_detailed_value;
2697 };
2698
2699 static void add_v1_field(HWND hwnd, struct detail_data *data,
2700  const struct v1_field *field)
2701 {
2702     WCHAR *val = field->format(data->pCertViewInfo->pCertContext);
2703
2704     if (val)
2705     {
2706         add_string_id_and_value_to_list(hwnd, data, field->id, val,
2707          field->create_detailed_value, NULL);
2708         HeapFree(GetProcessHeap(), 0, val);
2709     }
2710 }
2711
2712 static const struct v1_field v1_fields[] = {
2713  { IDS_FIELD_VERSION, field_format_version, NULL },
2714  { IDS_FIELD_SERIAL_NUMBER, field_format_serial_number, NULL },
2715  { IDS_FIELD_ISSUER, field_format_issuer, field_format_detailed_issuer },
2716  { IDS_FIELD_VALID_FROM, field_format_from_date, NULL },
2717  { IDS_FIELD_VALID_TO, field_format_to_date, NULL },
2718  { IDS_FIELD_SUBJECT, field_format_subject, field_format_detailed_subject },
2719  { IDS_FIELD_PUBLIC_KEY, field_format_public_key,
2720    field_format_detailed_public_key }
2721 };
2722
2723 static void add_v1_fields(HWND hwnd, struct detail_data *data)
2724 {
2725     int i;
2726     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2727
2728     /* The last item in v1_fields is the public key, which is not in the loop
2729      * because it's a special case.
2730      */
2731     for (i = 0; i < sizeof(v1_fields) / sizeof(v1_fields[0]) - 1; i++)
2732         add_v1_field(hwnd, data, &v1_fields[i]);
2733     if (cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData)
2734         add_v1_field(hwnd, data, &v1_fields[i]);
2735 }
2736
2737 static WCHAR *crypt_format_extension(PCERT_EXTENSION ext, DWORD formatStrType)
2738 {
2739     WCHAR *str = NULL;
2740     DWORD size;
2741
2742     if (CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
2743      ext->pszObjId, ext->Value.pbData, ext->Value.cbData, NULL, &size))
2744     {
2745         str = HeapAlloc(GetProcessHeap(), 0, size);
2746         CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
2747          ext->pszObjId, ext->Value.pbData, ext->Value.cbData, str, &size);
2748     }
2749     return str;
2750 }
2751
2752 static WCHAR *field_format_extension_hex_with_ascii(PCERT_EXTENSION ext)
2753 {
2754     WCHAR *str = NULL;
2755
2756     if (ext->Value.cbData)
2757     {
2758         /* The output is formatted as:
2759          * <hex bytes>  <ascii bytes>\n
2760          * where <hex bytes> is a string of up to 8 bytes, output as %02x,
2761          * and <ascii bytes> is the ASCII equivalent of each byte, or '.' if
2762          * the byte is not printable.
2763          * So, for example, the extension value consisting of the following
2764          * bytes:
2765          *   0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
2766          *   0x13,0x09,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67
2767          * is output as:
2768          *   30 14 31 12 30 10 06 03  0.1.0...
2769          *   55 04 03 13 09 4a 75 61  U....Jua
2770          *   6e 20 4c 61 6e 67        n Lang
2771          * The allocation size therefore requires:
2772          * - 4 characters per character in an 8-byte line
2773          *   (2 for the hex format, one for the space, one for the ASCII value)
2774          * - 3 more characters per 8-byte line (two spaces and a newline)
2775          * - 1 character for the terminating nul
2776          * FIXME: should use a fixed-width font for this
2777          */
2778         DWORD lines = (ext->Value.cbData + 7) / 8;
2779
2780         str = HeapAlloc(GetProcessHeap(), 0,
2781          (lines * 8 * 4 + lines * 3 + 1) * sizeof(WCHAR));
2782         if (str)
2783         {
2784             static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
2785             DWORD i, j;
2786             WCHAR *ptr;
2787
2788             for (i = 0, ptr = str; i < ext->Value.cbData; i += 8)
2789             {
2790                 /* Output as hex bytes first */
2791                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr += 3)
2792                     sprintfW(ptr, fmt, ext->Value.pbData[j]);
2793                 /* Pad the hex output with spaces for alignment */
2794                 if (j == ext->Value.cbData && j % 8)
2795                 {
2796                     static const WCHAR pad[] = { ' ',' ',' ' };
2797
2798                     for (; j % 8; j++, ptr += sizeof(pad) / sizeof(pad[0]))
2799                         memcpy(ptr, pad, sizeof(pad));
2800                 }
2801                 /* The last sprintfW included a space, so just insert one
2802                  * more space between the hex bytes and the ASCII output
2803                  */
2804                 *ptr++ = ' ';
2805                 /* Output as ASCII bytes */
2806                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr++)
2807                 {
2808                     if (isprintW(ext->Value.pbData[j]) &&
2809                      !isspaceW(ext->Value.pbData[j]))
2810                         *ptr = ext->Value.pbData[j];
2811                     else
2812                         *ptr = '.';
2813                 }
2814                 *ptr++ = '\n';
2815             }
2816             *ptr++ = '\0';
2817         }
2818     }
2819     return str;
2820 }
2821
2822 static WCHAR *field_format_detailed_extension(PCCERT_CONTEXT cert, void *param)
2823 {
2824     PCERT_EXTENSION ext = param;
2825     LPWSTR str = crypt_format_extension(ext,
2826      CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX);
2827
2828     if (!str)
2829         str = field_format_extension_hex_with_ascii(ext);
2830     return str;
2831 }
2832
2833 static void add_cert_extension_detail(HWND hwnd, struct detail_data *data,
2834  PCERT_EXTENSION ext)
2835 {
2836     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2837      ext->pszObjId, 0);
2838     LPWSTR val = crypt_format_extension(ext, 0);
2839
2840     if (oidInfo)
2841         add_field_and_value_to_list(hwnd, data, (LPWSTR)oidInfo->pwszName,
2842          val, field_format_detailed_extension, ext);
2843     else
2844     {
2845         DWORD len = strlen(ext->pszObjId);
2846         LPWSTR oidW = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2847
2848         if (oidW)
2849         {
2850             DWORD i;
2851
2852             for (i = 0; i <= len; i++)
2853                 oidW[i] = ext->pszObjId[i];
2854             add_field_and_value_to_list(hwnd, data, oidW, val,
2855              field_format_detailed_extension, ext);
2856             HeapFree(GetProcessHeap(), 0, oidW);
2857         }
2858     }
2859     HeapFree(GetProcessHeap(), 0, val);
2860 }
2861
2862 static void add_all_extensions(HWND hwnd, struct detail_data *data)
2863 {
2864     DWORD i;
2865     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2866
2867     for (i = 0; i < cert->pCertInfo->cExtension; i++)
2868         add_cert_extension_detail(hwnd, data, &cert->pCertInfo->rgExtension[i]);
2869 }
2870
2871 static void add_critical_extensions(HWND hwnd, struct detail_data *data)
2872 {
2873     DWORD i;
2874     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2875
2876     for (i = 0; i < cert->pCertInfo->cExtension; i++)
2877         if (cert->pCertInfo->rgExtension[i].fCritical)
2878             add_cert_extension_detail(hwnd, data,
2879              &cert->pCertInfo->rgExtension[i]);
2880 }
2881
2882 typedef WCHAR * (*prop_to_value_func)(void *pb, DWORD cb);
2883
2884 struct prop_id_to_string_id
2885 {
2886     DWORD prop;
2887     int id;
2888     BOOL prop_is_string;
2889     prop_to_value_func prop_to_value;
2890 };
2891
2892 static WCHAR *format_enhanced_key_usage_value(void *pb, DWORD cb)
2893 {
2894     CERT_EXTENSION ext;
2895
2896     ext.pszObjId = (LPSTR)X509_ENHANCED_KEY_USAGE;
2897     ext.fCritical = FALSE;
2898     ext.Value.pbData = pb;
2899     ext.Value.cbData = cb;
2900     return crypt_format_extension(&ext, 0);
2901 }
2902
2903 /* Logically the access state should also be checked, and IDC_EDITPROPERTIES
2904  * disabled for read-only certificates, but native doesn't appear to do that.
2905  */
2906 static const struct prop_id_to_string_id prop_id_map[] = {
2907  { CERT_HASH_PROP_ID, IDS_PROP_HASH, FALSE, format_hex_string },
2908  { CERT_FRIENDLY_NAME_PROP_ID, IDS_PROP_FRIENDLY_NAME, TRUE, NULL },
2909  { CERT_DESCRIPTION_PROP_ID, IDS_PROP_DESCRIPTION, TRUE, NULL },
2910  { CERT_ENHKEY_USAGE_PROP_ID, IDS_PROP_ENHKEY_USAGE, FALSE,
2911    format_enhanced_key_usage_value },
2912 };
2913
2914 static void add_properties(HWND hwnd, struct detail_data *data)
2915 {
2916     DWORD i;
2917     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2918
2919     for (i = 0; i < sizeof(prop_id_map) / sizeof(prop_id_map[0]); i++)
2920     {
2921         DWORD cb;
2922
2923         if (CertGetCertificateContextProperty(cert, prop_id_map[i].prop, NULL,
2924          &cb))
2925         {
2926             BYTE *pb;
2927             WCHAR *val = NULL;
2928
2929             /* FIXME: MS adds a separate value for the signature hash
2930              * algorithm.
2931              */
2932             pb = HeapAlloc(GetProcessHeap(), 0, cb);
2933             if (pb)
2934             {
2935                 if (CertGetCertificateContextProperty(cert,
2936                  prop_id_map[i].prop, pb, &cb))
2937                 {
2938                     if (prop_id_map[i].prop_is_string)
2939                     {
2940                         val = (LPWSTR)pb;
2941                         /* Don't double-free pb */
2942                         pb = NULL;
2943                     }
2944                     else
2945                         val = prop_id_map[i].prop_to_value(pb, cb);
2946                 }
2947                 HeapFree(GetProcessHeap(), 0, pb);
2948             }
2949             add_string_id_and_value_to_list(hwnd, data, prop_id_map[i].id, val,
2950              NULL, NULL);
2951         }
2952     }
2953 }
2954
2955 static void add_all_fields(HWND hwnd, struct detail_data *data)
2956 {
2957     add_v1_fields(hwnd, data);
2958     add_all_extensions(hwnd, data);
2959     add_properties(hwnd, data);
2960 }
2961
2962 struct selection_list_item
2963 {
2964     int id;
2965     add_fields_func add;
2966 };
2967
2968 const struct selection_list_item listItems[] = {
2969  { IDS_FIELDS_ALL, add_all_fields },
2970  { IDS_FIELDS_V1, add_v1_fields },
2971  { IDS_FIELDS_EXTENSIONS, add_all_extensions },
2972  { IDS_FIELDS_CRITICAL_EXTENSIONS, add_critical_extensions },
2973  { IDS_FIELDS_PROPERTIES, add_properties },
2974 };
2975
2976 static void create_show_list(HWND hwnd, struct detail_data *data)
2977 {
2978     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
2979     WCHAR buf[MAX_STRING_LEN];
2980     int i;
2981
2982     for (i = 0; i < sizeof(listItems) / sizeof(listItems[0]); i++)
2983     {
2984         int index;
2985
2986         LoadStringW(hInstance, listItems[i].id, buf,
2987          sizeof(buf) / sizeof(buf[0]));
2988         index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
2989         SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)data);
2990     }
2991     SendMessageW(cb, CB_SETCURSEL, 0, 0);
2992 }
2993
2994 static void create_listview_columns(HWND hwnd)
2995 {
2996     HWND lv = GetDlgItem(hwnd, IDC_DETAIL_LIST);
2997     RECT rc;
2998     WCHAR buf[MAX_STRING_LEN];
2999     LVCOLUMNW column;
3000
3001     SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
3002     GetWindowRect(lv, &rc);
3003     LoadStringW(hInstance, IDS_FIELD, buf, sizeof(buf) / sizeof(buf[0]));
3004     column.mask = LVCF_WIDTH | LVCF_TEXT;
3005     column.cx = (rc.right - rc.left) / 2 - 2;
3006     column.pszText = buf;
3007     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
3008     LoadStringW(hInstance, IDS_VALUE, buf, sizeof(buf) / sizeof(buf[0]));
3009     SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
3010 }
3011
3012 static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel)
3013 {
3014     HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
3015
3016     if (sel >= 0 && sel < sizeof(listItems) / sizeof(listItems[0]))
3017     {
3018         SendMessageW(list, LVM_DELETEALLITEMS, 0, 0);
3019         listItems[sel].add(list, data);
3020     }
3021 }
3022
3023 static void create_cert_details_list(HWND hwnd, struct detail_data *data)
3024 {
3025     create_show_list(hwnd, data);
3026     create_listview_columns(hwnd);
3027     set_fields_selection(hwnd, data, 0);
3028 }
3029
3030 static void add_purpose(HWND hwnd, LPCSTR oid)
3031 {
3032     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3033     PCRYPT_OID_INFO info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3034      sizeof(CRYPT_OID_INFO));
3035
3036     if (info)
3037     {
3038         char *oidCopy = HeapAlloc(GetProcessHeap(), 0, strlen(oid) + 1);
3039
3040         if (oidCopy)
3041         {
3042             LVITEMA item;
3043
3044             strcpy(oidCopy, oid);
3045             info->cbSize = sizeof(CRYPT_OID_INFO);
3046             info->pszOID = oidCopy;
3047             item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
3048             item.state = INDEXTOSTATEIMAGEMASK(CheckBitmapIndexChecked);
3049             item.stateMask = LVIS_STATEIMAGEMASK;
3050             item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
3051             item.iSubItem = 0;
3052             item.lParam = (LPARAM)info;
3053             item.pszText = oidCopy;
3054             SendMessageA(lv, LVM_INSERTITEMA, 0, (LPARAM)&item);
3055         }
3056         else
3057             HeapFree(GetProcessHeap(), 0, info);
3058     }
3059 }
3060
3061 static BOOL is_valid_oid(LPCSTR oid)
3062 {
3063     BOOL ret;
3064
3065     if (oid[0] != '0' && oid[0] != '1' && oid[0] != '2')
3066         ret = FALSE;
3067     else if (oid[1] != '.')
3068         ret = FALSE;
3069     else if (!oid[2])
3070         ret = FALSE;
3071     else
3072     {
3073         const char *ptr;
3074         BOOL expectNum = TRUE;
3075
3076         for (ptr = oid + 2, ret = TRUE; ret && *ptr; ptr++)
3077         {
3078             if (expectNum)
3079             {
3080                 if (!isdigit(*ptr))
3081                     ret = FALSE;
3082                 else if (*(ptr + 1) == '.')
3083                     expectNum = FALSE;
3084             }
3085             else
3086             {
3087                 if (*ptr != '.')
3088                     ret = FALSE;
3089                 else if (!(*(ptr + 1)))
3090                     ret = FALSE;
3091                 else
3092                     expectNum = TRUE;
3093             }
3094         }
3095     }
3096     return ret;
3097 }
3098
3099 static BOOL is_oid_in_list(HWND hwnd, LPCSTR oid)
3100 {
3101     return find_oid_in_list(GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES), oid)
3102      != -1;
3103 }
3104
3105 #define MAX_PURPOSE 255
3106
3107 static LRESULT CALLBACK add_purpose_dlg_proc(HWND hwnd, UINT msg,
3108  WPARAM wp, LPARAM lp)
3109 {
3110     LRESULT ret = 0;
3111     char buf[MAX_PURPOSE + 1];
3112
3113     switch (msg)
3114     {
3115     case WM_INITDIALOG:
3116         SendMessageW(GetDlgItem(hwnd, IDC_NEW_PURPOSE), EM_SETLIMITTEXT,
3117          MAX_PURPOSE, 0);
3118         ShowScrollBar(GetDlgItem(hwnd, IDC_NEW_PURPOSE), SB_VERT, FALSE);
3119         SetWindowLongPtrW(hwnd, DWLP_USER, lp);
3120         break;
3121     case WM_COMMAND:
3122         switch (HIWORD(wp))
3123         {
3124         case EN_CHANGE:
3125             if (LOWORD(wp) == IDC_NEW_PURPOSE)
3126             {
3127                 /* Show/hide scroll bar on description depending on how much
3128                  * text it has.
3129                  */
3130                 HWND description = GetDlgItem(hwnd, IDC_NEW_PURPOSE);
3131                 int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0);
3132
3133                 ShowScrollBar(description, SB_VERT, lines > 1);
3134             }
3135             break;
3136         case BN_CLICKED:
3137             switch (LOWORD(wp))
3138             {
3139             case IDOK:
3140                 SendMessageA(GetDlgItem(hwnd, IDC_NEW_PURPOSE), WM_GETTEXT,
3141                  sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
3142                 if (!buf[0])
3143                 {
3144                     /* An empty purpose is the same as cancelling */
3145                     EndDialog(hwnd, IDCANCEL);
3146                     ret = TRUE;
3147                 }
3148                 else if (!is_valid_oid(buf))
3149                 {
3150                     WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
3151
3152                     LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_ERROR, error,
3153                      sizeof(error) / sizeof(error[0]));
3154                     LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title,
3155                      sizeof(title) / sizeof(title[0]));
3156                     MessageBoxW(hwnd, error, title, MB_ICONERROR | MB_OK);
3157                 }
3158                 else if (is_oid_in_list(
3159                  (HWND)GetWindowLongPtrW(hwnd, DWLP_USER), buf))
3160                 {
3161                     WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
3162
3163                     LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_EXISTS,
3164                      error, sizeof(error) / sizeof(error[0]));
3165                     LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title,
3166                      sizeof(title) / sizeof(title[0]));
3167                     MessageBoxW(hwnd, error, title, MB_ICONEXCLAMATION | MB_OK);
3168                 }
3169                 else
3170                 {
3171                     HWND parent = (HWND)GetWindowLongPtrW(hwnd, DWLP_USER);
3172
3173                     add_purpose(parent, buf);
3174                     EndDialog(hwnd, wp);
3175                     ret = TRUE;
3176                 }
3177                 break;
3178             case IDCANCEL:
3179                 EndDialog(hwnd, wp);
3180                 ret = TRUE;
3181                 break;
3182             }
3183             break;
3184         }
3185         break;
3186     }
3187     return ret;
3188 }
3189
3190 static WCHAR *get_cert_property_as_string(PCCERT_CONTEXT cert, DWORD prop)
3191 {
3192     WCHAR *name = NULL;
3193     DWORD cb;
3194
3195     if (CertGetCertificateContextProperty(cert, prop, NULL, &cb))
3196     {
3197         name = HeapAlloc(GetProcessHeap(), 0, cb);
3198         if (name)
3199         {
3200             if (!CertGetCertificateContextProperty(cert, prop, (LPBYTE)name,
3201              &cb))
3202             {
3203                 HeapFree(GetProcessHeap(), 0, name);
3204                 name = NULL;
3205             }
3206         }
3207     }
3208     return name;
3209 }
3210
3211 static void redraw_states(HWND list, BOOL enabled)
3212 {
3213     int items = SendMessageW(list, LVM_GETITEMCOUNT, 0, 0), i;
3214
3215     for (i = 0; i < items; i++)
3216     {
3217         BOOL change = FALSE;
3218         int state;
3219
3220         state = SendMessageW(list, LVM_GETITEMSTATE, i, LVIS_STATEIMAGEMASK);
3221         /* This reverses the INDEXTOSTATEIMAGEMASK shift.  There doesn't appear
3222          * to be a handy macro for it.
3223          */
3224         state >>= 12;
3225         if (enabled)
3226         {
3227             if (state == CheckBitmapIndexDisabledChecked)
3228             {
3229                 state = CheckBitmapIndexChecked;
3230                 change = TRUE;
3231             }
3232             if (state == CheckBitmapIndexDisabledUnchecked)
3233             {
3234                 state = CheckBitmapIndexUnchecked;
3235                 change = TRUE;
3236             }
3237         }
3238         else
3239         {
3240             if (state == CheckBitmapIndexChecked)
3241             {
3242                 state = CheckBitmapIndexDisabledChecked;
3243                 change = TRUE;
3244             }
3245             if (state == CheckBitmapIndexUnchecked)
3246             {
3247                 state = CheckBitmapIndexDisabledUnchecked;
3248                 change = TRUE;
3249             }
3250         }
3251         if (change)
3252         {
3253             LVITEMW item;
3254
3255             item.state = INDEXTOSTATEIMAGEMASK(state);
3256             item.stateMask = LVIS_STATEIMAGEMASK;
3257             SendMessageW(list, LVM_SETITEMSTATE, i, (LPARAM)&item);
3258         }
3259     }
3260 }
3261
3262 typedef enum {
3263     PurposeEnableAll = 0,
3264     PurposeDisableAll,
3265     PurposeEnableSelected
3266 } PurposeSelection;
3267
3268 static void select_purposes(HWND hwnd, PurposeSelection selection)
3269 {
3270     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3271
3272     switch (selection)
3273     {
3274     case PurposeEnableAll:
3275     case PurposeDisableAll:
3276         EnableWindow(lv, FALSE);
3277         redraw_states(lv, FALSE);
3278         EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), FALSE);
3279         break;
3280     case PurposeEnableSelected:
3281         EnableWindow(lv, TRUE);
3282         redraw_states(lv, TRUE);
3283         EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), TRUE);
3284     }
3285 }
3286
3287 struct edit_cert_data
3288 {
3289     PCCERT_CONTEXT cert;
3290     BOOL *pfPropertiesChanged;
3291     HIMAGELIST imageList;
3292 };
3293
3294 static void show_cert_usages(HWND hwnd, struct edit_cert_data *data)
3295 {
3296     PCCERT_CONTEXT cert = data->cert;
3297     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3298     PCERT_ENHKEY_USAGE usage;
3299     DWORD size;
3300     RECT rc;
3301     LVCOLUMNW column;
3302     PurposeSelection purposeSelection;
3303
3304     GetWindowRect(lv, &rc);
3305     column.mask = LVCF_WIDTH;
3306     column.cx = rc.right - rc.left;
3307     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
3308     SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)data->imageList);
3309
3310     /* Get enhanced key usage.  Have to check for a property and an extension
3311      * separately, because CertGetEnhancedKeyUsage will succeed and return an
3312      * empty usage if neither is set.  Unfortunately an empty usage implies
3313      * no usage is allowed, so we have to distinguish between the two cases.
3314      */
3315     if (CertGetEnhancedKeyUsage(cert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
3316      NULL, &size))
3317     {
3318         usage = HeapAlloc(GetProcessHeap(), 0, size);
3319         if (!CertGetEnhancedKeyUsage(cert,
3320          CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
3321         {
3322             HeapFree(GetProcessHeap(), 0, usage);
3323             usage = NULL;
3324         }
3325         else if (usage->cUsageIdentifier)
3326             purposeSelection = PurposeEnableSelected;
3327         else
3328             purposeSelection = PurposeDisableAll;
3329     }
3330     else if (CertGetEnhancedKeyUsage(cert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
3331      NULL, &size))
3332     {
3333         usage = HeapAlloc(GetProcessHeap(), 0, size);
3334         if (!CertGetEnhancedKeyUsage(cert,
3335          CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
3336         {
3337             HeapFree(GetProcessHeap(), 0, usage);
3338             usage = NULL;
3339         }
3340         else if (usage->cUsageIdentifier)
3341             purposeSelection = PurposeEnableAll;
3342         else
3343             purposeSelection = PurposeDisableAll;
3344     }
3345     else
3346     {
3347         purposeSelection = PurposeEnableAll;
3348         usage = NULL;
3349     }
3350     if (usage)
3351     {
3352         DWORD i;
3353
3354         for (i = 0; i < usage->cUsageIdentifier; i++)
3355         {
3356             PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
3357              usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
3358
3359             if (info)
3360                 add_known_usage(lv, info, CheckBitmapIndexDisabledChecked);
3361             else
3362                 add_purpose(hwnd, usage->rgpszUsageIdentifier[i]);
3363         }
3364         HeapFree(GetProcessHeap(), 0, usage);
3365     }
3366     else
3367         add_known_usages_to_list(lv, CheckBitmapIndexDisabledChecked);
3368     select_purposes(hwnd, purposeSelection);
3369     SendMessageW(GetDlgItem(hwnd, IDC_ENABLE_ALL_PURPOSES + purposeSelection),
3370      BM_CLICK, 0, 0);
3371 }
3372
3373 static void set_general_cert_properties(HWND hwnd, struct edit_cert_data *data)
3374 {
3375     PCCERT_CONTEXT cert = data->cert;
3376     WCHAR *str;
3377
3378     if ((str = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID)))
3379     {
3380         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_SETTEXT, 0,
3381          (LPARAM)str);
3382         HeapFree(GetProcessHeap(), 0, str);
3383     }
3384     if ((str = get_cert_property_as_string(cert, CERT_DESCRIPTION_PROP_ID)))
3385     {
3386         SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_SETTEXT, 0,
3387          (LPARAM)str);
3388         HeapFree(GetProcessHeap(), 0, str);
3389     }
3390     show_cert_usages(hwnd, data);
3391 }
3392
3393 static void set_cert_string_property(PCCERT_CONTEXT cert, DWORD prop,
3394  LPWSTR str)
3395 {
3396     if (str && strlenW(str))
3397     {
3398         CRYPT_DATA_BLOB blob;
3399
3400         blob.pbData = (BYTE *)str;
3401         blob.cbData = (strlenW(str) + 1) * sizeof(WCHAR);
3402         CertSetCertificateContextProperty(cert, prop, 0, &blob);
3403     }
3404     else
3405         CertSetCertificateContextProperty(cert, prop, 0, NULL);
3406 }
3407
3408 #define WM_REFRESH_VIEW WM_USER + 0
3409
3410 static BOOL CALLBACK refresh_propsheet_pages(HWND hwnd, LPARAM lParam)
3411 {
3412     if ((GetClassLongW(hwnd, GCW_ATOM) == WC_DIALOG))
3413         SendMessageW(hwnd, WM_REFRESH_VIEW, 0, 0);
3414     return TRUE;
3415 }
3416
3417 #define MAX_FRIENDLY_NAME 40
3418 #define MAX_DESCRIPTION 255
3419
3420 static void apply_general_changes(HWND hwnd)
3421 {
3422     WCHAR buf[MAX_DESCRIPTION + 1];
3423     struct edit_cert_data *data =
3424      (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER);
3425
3426     SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_GETTEXT,
3427      sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
3428     set_cert_string_property(data->cert, CERT_FRIENDLY_NAME_PROP_ID, buf);
3429     SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_GETTEXT,
3430      sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
3431     set_cert_string_property(data->cert, CERT_DESCRIPTION_PROP_ID, buf);
3432     if (IsDlgButtonChecked(hwnd, IDC_ENABLE_ALL_PURPOSES))
3433     {
3434         /* Setting a NULL usage removes the enhanced key usage property. */
3435         CertSetEnhancedKeyUsage(data->cert, NULL);
3436     }
3437     else if (IsDlgButtonChecked(hwnd, IDC_DISABLE_ALL_PURPOSES))
3438     {
3439         CERT_ENHKEY_USAGE usage = { 0, NULL };
3440
3441         CertSetEnhancedKeyUsage(data->cert, &usage);
3442     }
3443     else if (IsDlgButtonChecked(hwnd, IDC_ENABLE_SELECTED_PURPOSES))
3444     {
3445         HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3446         CERT_ENHKEY_USAGE usage = { 0, NULL };
3447         int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
3448         LVITEMW item;
3449
3450         item.mask = LVIF_STATE | LVIF_PARAM;
3451         item.iSubItem = 0;
3452         item.stateMask = LVIS_STATEIMAGEMASK;
3453         for (i = 0; i < purposes; i++)
3454         {
3455             item.iItem = i;
3456             if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
3457             {
3458                 int state = item.state >> 12;
3459
3460                 if (state == CheckBitmapIndexChecked)
3461                 {
3462                     CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
3463
3464                     if (usage.cUsageIdentifier)
3465                         usage.rgpszUsageIdentifier =
3466                          HeapReAlloc(GetProcessHeap(), 0,
3467                          usage.rgpszUsageIdentifier,
3468                          (usage.cUsageIdentifier + 1) * sizeof(LPSTR));
3469                     else
3470                         usage.rgpszUsageIdentifier =
3471                          HeapAlloc(GetProcessHeap(), 0, sizeof(LPSTR));
3472                     if (usage.rgpszUsageIdentifier)
3473                         usage.rgpszUsageIdentifier[usage.cUsageIdentifier++] =
3474                          (LPSTR)info->pszOID;
3475                 }
3476             }
3477         }
3478         CertSetEnhancedKeyUsage(data->cert, &usage);
3479         HeapFree(GetProcessHeap(), 0, usage.rgpszUsageIdentifier);
3480     }
3481     EnumChildWindows(GetParent(GetParent(hwnd)), refresh_propsheet_pages, 0);
3482     if (data->pfPropertiesChanged)
3483         *data->pfPropertiesChanged = TRUE;
3484 }
3485
3486 static LRESULT CALLBACK cert_properties_general_dlg_proc(HWND hwnd, UINT msg,
3487  WPARAM wp, LPARAM lp)
3488 {
3489     PROPSHEETPAGEW *page;
3490
3491     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
3492
3493     switch (msg)
3494     {
3495     case WM_INITDIALOG:
3496     {
3497         HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
3498         struct detail_data *detailData;
3499         struct edit_cert_data *editData;
3500
3501         page = (PROPSHEETPAGEW *)lp;
3502         detailData = (struct detail_data *)page->lParam;
3503         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), EM_SETLIMITTEXT,
3504          MAX_FRIENDLY_NAME, 0);
3505         SendMessageW(description, EM_SETLIMITTEXT, MAX_DESCRIPTION, 0);
3506         ShowScrollBar(description, SB_VERT, FALSE);
3507         editData = HeapAlloc(GetProcessHeap(), 0,
3508          sizeof(struct edit_cert_data));
3509         if (editData)
3510         {
3511             editData->imageList = ImageList_Create(16, 16,
3512              ILC_COLOR4 | ILC_MASK, 4, 0);
3513             if (editData->imageList)
3514             {
3515                 HBITMAP bmp;
3516                 COLORREF backColor = RGB(255, 0, 255);
3517
3518                 bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS));
3519                 ImageList_AddMasked(editData->imageList, bmp, backColor);
3520                 DeleteObject(bmp);
3521                 ImageList_SetBkColor(editData->imageList, CLR_NONE);
3522             }
3523             editData->cert = detailData->pCertViewInfo->pCertContext;
3524             editData->pfPropertiesChanged = detailData->pfPropertiesChanged;
3525             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)editData);
3526             set_general_cert_properties(hwnd, editData);
3527         }
3528         break;
3529     }
3530     case WM_NOTIFY:
3531     {
3532         NMHDR *hdr = (NMHDR *)lp;
3533         NMITEMACTIVATE *nm;
3534
3535         switch (hdr->code)
3536         {
3537         case NM_CLICK:
3538             nm = (NMITEMACTIVATE *)lp;
3539             toggle_usage(hwnd, nm->iItem);
3540             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3541             break;
3542         case PSN_APPLY:
3543             apply_general_changes(hwnd);
3544             break;
3545         }
3546         break;
3547     }
3548     case WM_COMMAND:
3549         switch (HIWORD(wp))
3550         {
3551         case EN_CHANGE:
3552             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3553             if (LOWORD(wp) == IDC_DESCRIPTION)
3554             {
3555                 /* Show/hide scroll bar on description depending on how much
3556                  * text it has.
3557                  */
3558                 HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
3559                 int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0);
3560
3561                 ShowScrollBar(description, SB_VERT, lines > 1);
3562             }
3563             break;
3564         case BN_CLICKED:
3565             switch (LOWORD(wp))
3566             {
3567             case IDC_ADD_PURPOSE:
3568                 if (DialogBoxParamW(hInstance,
3569                  MAKEINTRESOURCEW(IDD_ADD_CERT_PURPOSE), hwnd,
3570                  add_purpose_dlg_proc, (LPARAM)hwnd) == IDOK)
3571                     SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3572                 break;
3573             case IDC_ENABLE_ALL_PURPOSES:
3574             case IDC_DISABLE_ALL_PURPOSES:
3575             case IDC_ENABLE_SELECTED_PURPOSES:
3576                 SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
3577                 select_purposes(hwnd, LOWORD(wp) - IDC_ENABLE_ALL_PURPOSES);
3578                 break;
3579             }
3580             break;
3581         }
3582         break;
3583     }
3584     return 0;
3585 }
3586
3587 static UINT CALLBACK cert_properties_general_callback(HWND hwnd, UINT msg,
3588  PROPSHEETPAGEW *page)
3589 {
3590     HWND lv;
3591     int cItem, i;
3592     struct edit_cert_data *data;
3593
3594     switch (msg)
3595     {
3596     case PSPCB_RELEASE:
3597         lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
3598         cItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
3599         for (i = 0; i < cItem; i++)
3600         {
3601             LVITEMW item;
3602
3603             item.mask = LVIF_PARAM;
3604             item.iItem = i;
3605             item.iSubItem = 0;
3606             if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item) && item.lParam)
3607             {
3608                 PCRYPT_OID_INFO info = (PCRYPT_OID_INFO)item.lParam;
3609
3610                 if (info->cbSize == sizeof(CRYPT_OID_INFO) && !info->dwGroupId)
3611                 {
3612                     HeapFree(GetProcessHeap(), 0, (LPSTR)info->pszOID);
3613                     HeapFree(GetProcessHeap(), 0, info);
3614                 }
3615             }
3616         }
3617         data = (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER);
3618         if (data)
3619         {
3620             ImageList_Destroy(data->imageList);
3621             HeapFree(GetProcessHeap(), 0, data);
3622         }
3623         break;
3624     }
3625     return 1;
3626 }
3627
3628 static void show_edit_cert_properties_dialog(HWND parent,
3629  struct detail_data *data)
3630 {
3631     PROPSHEETHEADERW hdr;
3632     PROPSHEETPAGEW page; /* FIXME: need to add a cross-certificate page */
3633
3634     TRACE("(%p)\n", data);
3635
3636     memset(&page, 0, sizeof(PROPSHEETPAGEW));
3637     page.dwSize = sizeof(page);
3638     page.dwFlags = PSP_USECALLBACK;
3639     page.pfnCallback = cert_properties_general_callback;
3640     page.hInstance = hInstance;
3641     page.u.pszTemplate = MAKEINTRESOURCEW(IDD_CERT_PROPERTIES_GENERAL);
3642     page.pfnDlgProc = cert_properties_general_dlg_proc;
3643     page.lParam = (LPARAM)data;
3644
3645     memset(&hdr, 0, sizeof(hdr));
3646     hdr.dwSize = sizeof(hdr);
3647     hdr.hwndParent = parent;
3648     hdr.dwFlags = PSH_PROPSHEETPAGE;
3649     hdr.hInstance = hInstance;
3650     hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE_PROPERTIES);
3651     hdr.u3.ppsp = &page;
3652     hdr.nPages = 1;
3653     PropertySheetW(&hdr);
3654 }
3655
3656 static void free_detail_fields(struct detail_data *data)
3657 {
3658     DWORD i;
3659
3660     for (i = 0; i < data->cFields; i++)
3661         HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value);
3662     HeapFree(GetProcessHeap(), 0, data->fields);
3663     data->fields = NULL;
3664     data->cFields = 0;
3665 }
3666
3667 static void refresh_details_view(HWND hwnd)
3668 {
3669     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
3670     int curSel;
3671     struct detail_data *data;
3672
3673     curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
3674     /* Actually, any index will do, since they all store the same data value */
3675     data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0);
3676     free_detail_fields(data);
3677     set_fields_selection(hwnd, data, curSel);
3678 }
3679
3680 static LRESULT CALLBACK detail_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
3681  LPARAM lp)
3682 {
3683     PROPSHEETPAGEW *page;
3684     struct detail_data *data;
3685
3686     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
3687
3688     switch (msg)
3689     {
3690     case WM_INITDIALOG:
3691         page = (PROPSHEETPAGEW *)lp;
3692         data = (struct detail_data *)page->lParam;
3693         create_cert_details_list(hwnd, data);
3694         if (!(data->pCertViewInfo->dwFlags & CRYPTUI_ENABLE_EDITPROPERTIES))
3695             EnableWindow(GetDlgItem(hwnd, IDC_EDITPROPERTIES), FALSE);
3696         if (data->pCertViewInfo->dwFlags & CRYPTUI_DISABLE_EXPORT)
3697             EnableWindow(GetDlgItem(hwnd, IDC_EXPORT), FALSE);
3698         break;
3699     case WM_NOTIFY:
3700     {
3701         NMITEMACTIVATE *nm;
3702         HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
3703
3704         nm = (NMITEMACTIVATE*)lp;
3705         if (nm->hdr.hwndFrom == list && nm->uNewState & LVN_ITEMACTIVATE
3706          && nm->hdr.code == LVN_ITEMCHANGED)
3707         {
3708             data = (struct detail_data *)nm->lParam;
3709             if (nm->iItem >= 0 && data && nm->iItem < data->cFields)
3710             {
3711                 WCHAR buf[MAX_STRING_LEN], *val = NULL;
3712                 HWND valueCtl = GetDlgItem(hwnd, IDC_DETAIL_VALUE);
3713
3714                 if (data->fields[nm->iItem].create)
3715                     val = data->fields[nm->iItem].create(
3716                      data->pCertViewInfo->pCertContext,
3717                      data->fields[nm->iItem].param);
3718                 else
3719                 {
3720                     LVITEMW item;
3721                     int res;
3722
3723                     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
3724                     item.mask = LVIF_TEXT;
3725                     item.pszText = buf;
3726                     item.iItem = nm->iItem;
3727                     item.iSubItem = 1;
3728                     res = SendMessageW(list, LVM_GETITEMW, 0, (LPARAM)&item);
3729                     if (res)
3730                         val = buf;
3731                 }
3732                 /* Select all the text in the control, the next update will
3733                  * replace it
3734                  */
3735                 SendMessageW(valueCtl, EM_SETSEL, 0, -1);
3736                 add_unformatted_text_to_control(valueCtl, val,
3737                  val ? strlenW(val) : 0);
3738                 if (val != buf)
3739                     HeapFree(GetProcessHeap(), 0, val);
3740             }
3741         }
3742         break;
3743     }
3744     case WM_COMMAND:
3745         switch (wp)
3746         {
3747         case IDC_EXPORT:
3748         {
3749             HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
3750             CRYPTUI_WIZ_EXPORT_INFO info;
3751
3752             data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, 0, 0);
3753             info.dwSize = sizeof(info);
3754             info.pwszExportFileName = NULL;
3755             info.dwSubjectChoice = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT;
3756             info.u.pCertContext = data->pCertViewInfo->pCertContext;
3757             info.cStores = 0;
3758             CryptUIWizExport(0, hwnd, NULL, &info, NULL);
3759             break;
3760         }
3761         case IDC_EDITPROPERTIES:
3762         {
3763             HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
3764             int curSel;
3765
3766             curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
3767             /* Actually, any index will do, since they all store the same
3768              * data value
3769              */
3770             data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA,
3771              curSel, 0);
3772             show_edit_cert_properties_dialog(GetParent(hwnd), data);
3773             break;
3774         }
3775         case ((CBN_SELCHANGE << 16) | IDC_DETAIL_SELECT):
3776             refresh_details_view(hwnd);
3777             break;
3778         }
3779         break;
3780     case WM_REFRESH_VIEW:
3781         refresh_details_view(hwnd);
3782         break;
3783     }
3784     return 0;
3785 }
3786
3787 static UINT CALLBACK detail_callback(HWND hwnd, UINT msg,
3788  PROPSHEETPAGEW *page)
3789 {
3790     struct detail_data *data;
3791
3792     switch (msg)
3793     {
3794     case PSPCB_RELEASE:
3795         data = (struct detail_data *)page->lParam;
3796         free_detail_fields(data);
3797         HeapFree(GetProcessHeap(), 0, data);
3798         break;
3799     }
3800     return 0;
3801 }
3802
3803 static BOOL init_detail_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
3804  BOOL *pfPropertiesChanged, PROPSHEETPAGEW *page)
3805 {
3806     BOOL ret;
3807     struct detail_data *data = HeapAlloc(GetProcessHeap(), 0,
3808      sizeof(struct detail_data));
3809
3810     if (data)
3811     {
3812         data->pCertViewInfo = pCertViewInfo;
3813         data->pfPropertiesChanged = pfPropertiesChanged;
3814         data->cFields = 0;
3815         data->fields = NULL;
3816         memset(page, 0, sizeof(PROPSHEETPAGEW));
3817         page->dwSize = sizeof(PROPSHEETPAGEW);
3818         page->dwFlags = PSP_USECALLBACK;
3819         page->pfnCallback = detail_callback;
3820         page->hInstance = hInstance;
3821         page->u.pszTemplate = MAKEINTRESOURCEW(IDD_DETAIL);
3822         page->pfnDlgProc = detail_dlg_proc;
3823         page->lParam = (LPARAM)data;
3824         ret = TRUE;
3825     }
3826     else
3827         ret = FALSE;
3828     return ret;
3829 }
3830
3831 struct hierarchy_data
3832 {
3833     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
3834     HIMAGELIST imageList;
3835     DWORD selectedCert;
3836 };
3837
3838 static LPARAM index_to_lparam(struct hierarchy_data *data, DWORD index)
3839 {
3840     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3841      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3842      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
3843      data->pCertViewInfo->idxCounterSigner);
3844
3845     /* Takes advantage of the fact that a pointer is 32-bit aligned, and
3846      * therefore always even.
3847      */
3848     if (index == provSigner->csCertChain - 1)
3849         return (LPARAM)data;
3850     return index << 1 | 1;
3851 }
3852
3853 static inline DWORD lparam_to_index(struct hierarchy_data *data, LPARAM lp)
3854 {
3855     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3856      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3857      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
3858      data->pCertViewInfo->idxCounterSigner);
3859
3860     if (!(lp & 1))
3861         return provSigner->csCertChain - 1;
3862     return lp >> 1;
3863 }
3864
3865 static struct hierarchy_data *get_hierarchy_data_from_tree_item(HWND tree,
3866  HTREEITEM hItem)
3867 {
3868     struct hierarchy_data *data = NULL;
3869     HTREEITEM root = NULL;
3870
3871     do {
3872         HTREEITEM parent = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM,
3873          TVGN_PARENT, (LPARAM)hItem);
3874
3875         if (!parent)
3876             root = hItem;
3877         hItem = parent;
3878     } while (hItem);
3879     if (root)
3880     {
3881         TVITEMW item;
3882
3883         item.mask = TVIF_PARAM;
3884         item.hItem = root;
3885         SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
3886         data = (struct hierarchy_data *)item.lParam;
3887     }
3888     return data;
3889 }
3890
3891 static WCHAR *get_cert_display_name(PCCERT_CONTEXT cert)
3892 {
3893     WCHAR *name = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID);
3894
3895     if (!name)
3896         name = get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
3897     return name;
3898 }
3899
3900 static void show_cert_chain(HWND hwnd, struct hierarchy_data *data)
3901 {
3902     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
3903     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3904      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
3905      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
3906      data->pCertViewInfo->idxCounterSigner);
3907     DWORD i;
3908     HTREEITEM parent = NULL;
3909
3910     SendMessageW(tree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)data->imageList);
3911     for (i = provSigner->csCertChain; i; i--)
3912     {
3913         LPWSTR name;
3914
3915         name = get_cert_display_name(provSigner->pasCertChain[i - 1].pCert);
3916         if (name)
3917         {
3918             TVINSERTSTRUCTW tvis;
3919
3920             tvis.hParent = parent;
3921             tvis.hInsertAfter = TVI_LAST;
3922             tvis.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE |
3923              TVIF_SELECTEDIMAGE | TVIF_PARAM;
3924             tvis.u.item.pszText = name;
3925             tvis.u.item.state = TVIS_EXPANDED;
3926             tvis.u.item.stateMask = TVIS_EXPANDED;
3927             if (i == 1 &&
3928              (provSigner->pChainContext->TrustStatus.dwErrorStatus &
3929              CERT_TRUST_IS_PARTIAL_CHAIN))
3930             {
3931                 /* The root of the chain has a special case:  if the chain is
3932                  * a partial chain, the icon is a warning icon rather than an
3933                  * error icon.
3934                  */
3935                 tvis.u.item.iImage = 2;
3936             }
3937             else if (provSigner->pasCertChain[i - 1].pChainElement->TrustStatus.
3938              dwErrorStatus == 0)
3939                 tvis.u.item.iImage = 0;
3940             else
3941                 tvis.u.item.iImage = 1;
3942             tvis.u.item.iSelectedImage = tvis.u.item.iImage;
3943             tvis.u.item.lParam = index_to_lparam(data, i - 1);
3944             parent = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0,
3945              (LPARAM)&tvis);
3946             HeapFree(GetProcessHeap(), 0, name);
3947         }
3948     }
3949 }
3950
3951 static void set_certificate_status(HWND hwnd, CRYPT_PROVIDER_CERT *cert)
3952 {
3953     /* Select all the text in the control, the next update will replace it */
3954     SendMessageW(hwnd, EM_SETSEL, 0, -1);
3955     /* Set the highest priority error messages first. */
3956     if (!(cert->dwConfidence & CERT_CONFIDENCE_SIG))
3957         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_SIGNATURE);
3958     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIME))
3959         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIME);
3960     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIMENEST))
3961         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIMENEST);
3962     else if (cert->dwRevokedReason)
3963         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_REVOKED);
3964     else
3965         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_VALID);
3966 }
3967
3968 static void set_certificate_status_for_end_cert(HWND hwnd,
3969  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
3970 {
3971     HWND status = GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT);
3972     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
3973      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
3974      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
3975      pCertViewInfo->idxCounterSigner);
3976     CRYPT_PROVIDER_CERT *provCert = WTHelperGetProvCertFromChain(provSigner,
3977      pCertViewInfo->idxCert);
3978
3979     set_certificate_status(status, provCert);
3980 }
3981
3982 static void show_cert_hierarchy(HWND hwnd, struct hierarchy_data *data)
3983 {
3984     /* Disable view certificate button until a certificate is selected */
3985     EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), FALSE);
3986     show_cert_chain(hwnd, data);
3987     set_certificate_status_for_end_cert(hwnd, data->pCertViewInfo);
3988 }
3989
3990 static void show_dialog_for_selected_cert(HWND hwnd)
3991 {
3992     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
3993     TVITEMW item;
3994     struct hierarchy_data *data;
3995     DWORD selection;
3996
3997     memset(&item, 0, sizeof(item));
3998     item.mask = TVIF_HANDLE | TVIF_PARAM;
3999     item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CARET,
4000      (LPARAM)NULL);
4001     SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
4002     data = get_hierarchy_data_from_tree_item(tree, item.hItem);
4003     selection = lparam_to_index(data, item.lParam);
4004     if (selection != 0)
4005     {
4006         CRYPT_PROVIDER_SGNR *provSigner;
4007         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
4008         BOOL changed = FALSE;
4009
4010         provSigner = WTHelperGetProvSignerFromChain(
4011          (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
4012          data->pCertViewInfo->idxSigner,
4013          data->pCertViewInfo->fCounterSigner,
4014          data->pCertViewInfo->idxCounterSigner);
4015         memset(&viewInfo, 0, sizeof(viewInfo));
4016         viewInfo.dwSize = sizeof(viewInfo);
4017         viewInfo.dwFlags = data->pCertViewInfo->dwFlags;
4018         viewInfo.szTitle = data->pCertViewInfo->szTitle;
4019         viewInfo.pCertContext = provSigner->pasCertChain[selection].pCert;
4020         viewInfo.cStores = data->pCertViewInfo->cStores;
4021         viewInfo.rghStores = data->pCertViewInfo->rghStores;
4022         viewInfo.cPropSheetPages = data->pCertViewInfo->cPropSheetPages;
4023         viewInfo.rgPropSheetPages = data->pCertViewInfo->rgPropSheetPages;
4024         viewInfo.nStartPage = data->pCertViewInfo->nStartPage;
4025         CryptUIDlgViewCertificateW(&viewInfo, &changed);
4026         if (changed)
4027         {
4028             /* Delete the contents of the tree */
4029             SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
4030             /* Reinitialize the tree */
4031             show_cert_hierarchy(hwnd, data);
4032         }
4033     }
4034 }
4035
4036 static LRESULT CALLBACK hierarchy_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4037  LPARAM lp)
4038 {
4039     PROPSHEETPAGEW *page;
4040     struct hierarchy_data *data;
4041     LRESULT ret = 0;
4042     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
4043
4044     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
4045
4046     switch (msg)
4047     {
4048     case WM_INITDIALOG:
4049         page = (PROPSHEETPAGEW *)lp;
4050         data = (struct hierarchy_data *)page->lParam;
4051         show_cert_hierarchy(hwnd, data);
4052         break;
4053     case WM_NOTIFY:
4054     {
4055         NMHDR *hdr;
4056
4057         hdr = (NMHDR *)lp;
4058         switch (hdr->code)
4059         {
4060         case TVN_SELCHANGEDW:
4061         {
4062             NMTREEVIEWW *nm = (NMTREEVIEWW*)lp;
4063             DWORD selection;
4064             CRYPT_PROVIDER_SGNR *provSigner;
4065
4066             data = get_hierarchy_data_from_tree_item(tree, nm->itemNew.hItem);
4067             selection = lparam_to_index(data, nm->itemNew.lParam);
4068             provSigner = WTHelperGetProvSignerFromChain(
4069              (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
4070              data->pCertViewInfo->idxSigner,
4071              data->pCertViewInfo->fCounterSigner,
4072              data->pCertViewInfo->idxCounterSigner);
4073             EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), selection != 0);
4074             set_certificate_status(GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT),
4075              &provSigner->pasCertChain[selection]);
4076             break;
4077         }
4078         case NM_DBLCLK:
4079             show_dialog_for_selected_cert(hwnd);
4080             SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
4081             ret = 1;
4082             break;
4083         }
4084         break;
4085     }
4086     case WM_COMMAND:
4087         switch (wp)
4088         {
4089         case IDC_VIEWCERTIFICATE:
4090             show_dialog_for_selected_cert(hwnd);
4091             break;
4092         }
4093         break;
4094     case WM_REFRESH_VIEW:
4095     {
4096         TVITEMW item;
4097
4098         /* Get hierarchy data */
4099         memset(&item, 0, sizeof(item));
4100         item.mask = TVIF_HANDLE | TVIF_PARAM;
4101         item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_ROOT,
4102          (LPARAM)NULL);
4103         data = get_hierarchy_data_from_tree_item(tree, item.hItem);
4104         /* Delete the contents of the tree */
4105         SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
4106         /* Reinitialize the tree */
4107         show_cert_hierarchy(hwnd, data);
4108         break;
4109     }
4110     }
4111     return ret;
4112 }
4113
4114 static UINT CALLBACK hierarchy_callback(HWND hwnd, UINT msg,
4115  PROPSHEETPAGEW *page)
4116 {
4117     struct hierarchy_data *data;
4118
4119     switch (msg)
4120     {
4121     case PSPCB_RELEASE:
4122         data = (struct hierarchy_data *)page->lParam;
4123         ImageList_Destroy(data->imageList);
4124         HeapFree(GetProcessHeap(), 0, data);
4125         break;
4126     }
4127     return 0;
4128 }
4129
4130 static BOOL init_hierarchy_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
4131  PROPSHEETPAGEW *page)
4132 {
4133     struct hierarchy_data *data = HeapAlloc(GetProcessHeap(), 0,
4134      sizeof(struct hierarchy_data));
4135     BOOL ret = FALSE;
4136
4137     if (data)
4138     {
4139         data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0);
4140         if (data->imageList)
4141         {
4142             HBITMAP bmp;
4143             COLORREF backColor = RGB(255, 0, 255);
4144
4145             data->pCertViewInfo = pCertViewInfo;
4146             data->selectedCert = 0xffffffff;
4147
4148             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS));
4149             ImageList_AddMasked(data->imageList, bmp, backColor);
4150             DeleteObject(bmp);
4151             ImageList_SetBkColor(data->imageList, CLR_NONE);
4152
4153             memset(page, 0, sizeof(PROPSHEETPAGEW));
4154             page->dwSize = sizeof(PROPSHEETPAGEW);
4155             page->dwFlags = PSP_USECALLBACK;
4156             page->hInstance = hInstance;
4157             page->u.pszTemplate = MAKEINTRESOURCEW(IDD_HIERARCHY);
4158             page->pfnDlgProc = hierarchy_dlg_proc;
4159             page->lParam = (LPARAM)data;
4160             page->pfnCallback = hierarchy_callback;
4161             ret = TRUE;
4162         }
4163         else
4164             HeapFree(GetProcessHeap(), 0, data);
4165     }
4166     return ret;
4167 }
4168
4169 static int CALLBACK cert_prop_sheet_proc(HWND hwnd, UINT msg, LPARAM lp)
4170 {
4171     RECT rc;
4172     POINT topLeft;
4173
4174     TRACE("(%p, %08x, %08lx)\n", hwnd, msg, lp);
4175
4176     switch (msg)
4177     {
4178     case PSCB_INITIALIZED:
4179         /* Get cancel button's position.. */
4180         GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rc);
4181         topLeft.x = rc.left;
4182         topLeft.y = rc.top;
4183         ScreenToClient(hwnd, &topLeft);
4184         /* hide the cancel button.. */
4185         ShowWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
4186         /* get the OK button's size.. */
4187         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
4188         /* and move the OK button to the cancel button's original position. */
4189         MoveWindow(GetDlgItem(hwnd, IDOK), topLeft.x, topLeft.y,
4190          rc.right - rc.left, rc.bottom - rc.top, FALSE);
4191         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
4192         break;
4193     }
4194     return 0;
4195 }
4196
4197 static BOOL show_cert_dialog(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
4198  CRYPT_PROVIDER_CERT *provCert, BOOL *pfPropertiesChanged)
4199 {
4200     static const WCHAR riched[] = { 'r','i','c','h','e','d','2','0',0 };
4201     DWORD nPages;
4202     PROPSHEETPAGEW *pages;
4203     BOOL ret = FALSE;
4204     HMODULE lib = LoadLibraryW(riched);
4205
4206     nPages = pCertViewInfo->cPropSheetPages + 1; /* one for the General tab */
4207     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
4208         nPages++;
4209     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
4210         nPages++;
4211     pages = HeapAlloc(GetProcessHeap(), 0, nPages * sizeof(PROPSHEETPAGEW));
4212     if (pages)
4213     {
4214         PROPSHEETHEADERW hdr;
4215         CRYPTUI_INITDIALOG_STRUCT *init = NULL;
4216         DWORD i;
4217
4218         memset(&hdr, 0, sizeof(hdr));
4219         hdr.dwSize = sizeof(hdr);
4220         hdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK;
4221         hdr.hInstance = hInstance;
4222         if (pCertViewInfo->szTitle)
4223             hdr.pszCaption = pCertViewInfo->szTitle;
4224         else
4225             hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE);
4226         init_general_page(pCertViewInfo, &pages[hdr.nPages++]);
4227         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
4228         {
4229             if (init_detail_page(pCertViewInfo, pfPropertiesChanged,
4230              &pages[hdr.nPages]))
4231                 hdr.nPages++;
4232         }
4233         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
4234         {
4235             if (init_hierarchy_page(pCertViewInfo, &pages[hdr.nPages]))
4236                 hdr.nPages++;
4237         }
4238         /* Copy each additional page, and create the init dialog struct for it
4239          */
4240         if (pCertViewInfo->cPropSheetPages)
4241         {
4242             init = HeapAlloc(GetProcessHeap(), 0,
4243              pCertViewInfo->cPropSheetPages *
4244              sizeof(CRYPTUI_INITDIALOG_STRUCT));
4245             if (init)
4246             {
4247                 for (i = 0; i < pCertViewInfo->cPropSheetPages; i++)
4248                 {
4249                     memcpy(&pages[hdr.nPages + i],
4250                      &pCertViewInfo->rgPropSheetPages[i],
4251                      sizeof(PROPSHEETPAGEW));
4252                     init[i].lParam = pCertViewInfo->rgPropSheetPages[i].lParam;
4253                     init[i].pCertContext = pCertViewInfo->pCertContext;
4254                     pages[hdr.nPages + i].lParam = (LPARAM)&init[i];
4255                 }
4256                 if (pCertViewInfo->nStartPage & 0x8000)
4257                 {
4258                     /* Start page index is relative to the number of default
4259                      * pages
4260                      */
4261                     hdr.u2.nStartPage = pCertViewInfo->nStartPage + hdr.nPages;
4262                 }
4263                 else
4264                     hdr.u2.nStartPage = pCertViewInfo->nStartPage;
4265                 hdr.nPages = nPages;
4266                 ret = TRUE;
4267             }
4268             else
4269                 SetLastError(ERROR_OUTOFMEMORY);
4270         }
4271         else
4272         {
4273             /* Ignore the relative flag if there aren't any additional pages */
4274             hdr.u2.nStartPage = pCertViewInfo->nStartPage & 0x7fff;
4275             ret = TRUE;
4276         }
4277         if (ret)
4278         {
4279             INT_PTR l;
4280
4281             hdr.u3.ppsp = pages;
4282             hdr.pfnCallback = cert_prop_sheet_proc;
4283             l = PropertySheetW(&hdr);
4284             if (l == 0)
4285             {
4286                 SetLastError(ERROR_CANCELLED);
4287                 ret = FALSE;
4288             }
4289         }
4290         HeapFree(GetProcessHeap(), 0, init);
4291         HeapFree(GetProcessHeap(), 0, pages);
4292     }
4293     else
4294         SetLastError(ERROR_OUTOFMEMORY);
4295     FreeLibrary(lib);
4296     return ret;
4297 }
4298
4299 /***********************************************************************
4300  *              CryptUIDlgViewCertificateW (CRYPTUI.@)
4301  */
4302 BOOL WINAPI CryptUIDlgViewCertificateW(
4303  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, BOOL *pfPropertiesChanged)
4304 {
4305     static GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY;
4306     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
4307     WINTRUST_DATA wvt;
4308     WINTRUST_CERT_INFO cert;
4309     BOOL ret = FALSE;
4310     CRYPT_PROVIDER_SGNR *signer;
4311     CRYPT_PROVIDER_CERT *provCert = NULL;
4312
4313     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
4314
4315     if (pCertViewInfo->dwSize != sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))
4316     {
4317         SetLastError(ERROR_INVALID_PARAMETER);
4318         return FALSE;
4319     }
4320     /* Make a local copy in case we have to call WinVerifyTrust ourselves */
4321     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
4322     if (!viewInfo.u.hWVTStateData)
4323     {
4324         memset(&wvt, 0, sizeof(wvt));
4325         wvt.cbStruct = sizeof(wvt);
4326         wvt.dwUIChoice = WTD_UI_NONE;
4327         if (viewInfo.dwFlags &
4328          CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
4329             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
4330         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_END_CERT)
4331             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_END_CERT;
4332         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN)
4333             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN;
4334         wvt.dwUnionChoice = WTD_CHOICE_CERT;
4335         memset(&cert, 0, sizeof(cert));
4336         cert.cbStruct = sizeof(cert);
4337         cert.psCertContext = (CERT_CONTEXT *)viewInfo.pCertContext;
4338         cert.chStores = viewInfo.cStores;
4339         cert.pahStores = viewInfo.rghStores;
4340         wvt.u.pCert = &cert;
4341         wvt.dwStateAction = WTD_STATEACTION_VERIFY;
4342         WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
4343         viewInfo.u.pCryptProviderData =
4344          WTHelperProvDataFromStateData(wvt.hWVTStateData);
4345         signer = WTHelperGetProvSignerFromChain(
4346          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, 0, FALSE, 0);
4347         provCert = WTHelperGetProvCertFromChain(signer, 0);
4348         ret = TRUE;
4349     }
4350     else
4351     {
4352         viewInfo.u.pCryptProviderData =
4353          WTHelperProvDataFromStateData(viewInfo.u.hWVTStateData);
4354         signer = WTHelperGetProvSignerFromChain(
4355          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData,
4356          viewInfo.idxSigner, viewInfo.fCounterSigner,
4357          viewInfo.idxCounterSigner);
4358         provCert = WTHelperGetProvCertFromChain(signer, viewInfo.idxCert);
4359         ret = TRUE;
4360     }
4361     if (ret)
4362     {
4363         ret = show_cert_dialog(&viewInfo, provCert, pfPropertiesChanged);
4364         if (!viewInfo.u.hWVTStateData)
4365         {
4366             wvt.dwStateAction = WTD_STATEACTION_CLOSE;
4367             WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
4368         }
4369     }
4370     return ret;
4371 }
4372
4373 /***********************************************************************
4374  *              CryptUIDlgViewContext (CRYPTUI.@)
4375  */
4376 BOOL WINAPI CryptUIDlgViewContext(DWORD dwContextType, LPVOID pvContext,
4377  HWND hwnd, LPCWSTR pwszTitle, DWORD dwFlags, LPVOID pvReserved)
4378 {
4379     BOOL ret;
4380
4381     TRACE("(%d, %p, %p, %s, %08x, %p)\n", dwContextType, pvContext, hwnd,
4382      debugstr_w(pwszTitle), dwFlags, pvReserved);
4383
4384     switch (dwContextType)
4385     {
4386     case CERT_STORE_CERTIFICATE_CONTEXT:
4387     {
4388         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
4389
4390         memset(&viewInfo, 0, sizeof(viewInfo));
4391         viewInfo.dwSize = sizeof(viewInfo);
4392         viewInfo.hwndParent = hwnd;
4393         viewInfo.szTitle = pwszTitle;
4394         viewInfo.pCertContext = pvContext;
4395         ret = CryptUIDlgViewCertificateW(&viewInfo, NULL);
4396         break;
4397     }
4398     default:
4399         FIXME("unimplemented for context type %d\n", dwContextType);
4400         SetLastError(E_INVALIDARG);
4401         ret = FALSE;
4402     }
4403     return ret;
4404 }
4405
4406 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
4407  * or szOID_BASIC_CONSTRAINTS2, whichever is present) to determine if it
4408  * should be a CA.  If neither extension is present, returns
4409  * defaultIfNotSpecified.
4410  */
4411 static BOOL is_ca_cert(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified)
4412 {
4413     BOOL isCA = defaultIfNotSpecified;
4414     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
4415      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
4416
4417     if (ext)
4418     {
4419         CERT_BASIC_CONSTRAINTS_INFO *info;
4420         DWORD size = 0;
4421
4422         if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
4423          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
4424          NULL, (LPBYTE)&info, &size))
4425         {
4426             if (info->SubjectType.cbData == 1)
4427                 isCA = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
4428             LocalFree(info);
4429         }
4430     }
4431     else
4432     {
4433         ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
4434          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
4435         if (ext)
4436         {
4437             CERT_BASIC_CONSTRAINTS2_INFO info;
4438             DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
4439
4440             if (CryptDecodeObjectEx(X509_ASN_ENCODING,
4441              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
4442              0, NULL, &info, &size))
4443                 isCA = info.fCA;
4444         }
4445     }
4446     return isCA;
4447 }
4448
4449 static HCERTSTORE choose_store_for_cert(PCCERT_CONTEXT cert)
4450 {
4451     LPCWSTR storeName;
4452
4453     if (is_ca_cert(cert, TRUE))
4454         storeName = ca;
4455     else
4456         storeName = addressBook;
4457     return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
4458      CERT_SYSTEM_STORE_CURRENT_USER, storeName);
4459 }
4460
4461 static BOOL import_cert(PCCERT_CONTEXT cert, HCERTSTORE hDestCertStore)
4462 {
4463     HCERTSTORE store;
4464     BOOL ret;
4465
4466     if (!cert)
4467     {
4468         SetLastError(E_INVALIDARG);
4469         return FALSE;
4470     }
4471     if (hDestCertStore) store = hDestCertStore;
4472     else
4473     {
4474         if (!(store = choose_store_for_cert(cert)))
4475         {
4476             WARN("unable to open certificate store\n");
4477             return FALSE;
4478         }
4479     }
4480     ret = CertAddCertificateContextToStore(store, cert,
4481      CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL);
4482     if (!hDestCertStore) CertCloseStore(store, 0);
4483     return ret;
4484 }
4485
4486 static BOOL import_crl(PCCRL_CONTEXT crl, HCERTSTORE hDestCertStore)
4487 {
4488     HCERTSTORE store;
4489     BOOL ret;
4490
4491     if (!crl)
4492     {
4493         SetLastError(E_INVALIDARG);
4494         return FALSE;
4495     }
4496     if (hDestCertStore) store = hDestCertStore;
4497     else
4498     {
4499         if (!(store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
4500          CERT_SYSTEM_STORE_CURRENT_USER, ca)))
4501         {
4502             WARN("unable to open certificate store\n");
4503             return FALSE;
4504         }
4505     }
4506     ret = CertAddCRLContextToStore(store, crl,
4507      CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL);
4508     if (!hDestCertStore) CertCloseStore(store, 0);
4509     return ret;
4510 }
4511
4512 static BOOL import_ctl(PCCTL_CONTEXT ctl, HCERTSTORE hDestCertStore)
4513 {
4514     HCERTSTORE store;
4515     BOOL ret;
4516
4517     if (!ctl)
4518     {
4519         SetLastError(E_INVALIDARG);
4520         return FALSE;
4521     }
4522     if (hDestCertStore) store = hDestCertStore;
4523     else
4524     {
4525         static const WCHAR trust[] = { 'T','r','u','s','t',0 };
4526
4527         if (!(store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
4528          CERT_SYSTEM_STORE_CURRENT_USER, trust)))
4529         {
4530             WARN("unable to open certificate store\n");
4531             return FALSE;
4532         }
4533     }
4534     ret = CertAddCTLContextToStore(store, ctl,
4535      CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL);
4536     if (!hDestCertStore) CertCloseStore(store, 0);
4537     return ret;
4538 }
4539
4540 /* Checks type, a type such as CERT_QUERY_CONTENT_CERT returned by
4541  * CryptQueryObject, against the allowed types.  Returns TRUE if the
4542  * type is allowed, FALSE otherwise.
4543  */
4544 static BOOL check_context_type(DWORD dwFlags, DWORD type)
4545 {
4546     BOOL ret;
4547
4548     if (dwFlags &
4549      (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL |
4550      CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4551     {
4552         switch (type)
4553         {
4554         case CERT_QUERY_CONTENT_CERT:
4555         case CERT_QUERY_CONTENT_SERIALIZED_CERT:
4556             ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT;
4557             break;
4558         case CERT_QUERY_CONTENT_CRL:
4559         case CERT_QUERY_CONTENT_SERIALIZED_CRL:
4560             ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL;
4561             break;
4562         case CERT_QUERY_CONTENT_CTL:
4563         case CERT_QUERY_CONTENT_SERIALIZED_CTL:
4564             ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL;
4565             break;
4566         default:
4567             /* The remaining types contain more than one type, so allow
4568              * any combination.
4569              */
4570             ret = TRUE;
4571         }
4572     }
4573     else
4574     {
4575         /* No allowed types specified, so any type is allowed */
4576         ret = TRUE;
4577     }
4578     if (!ret)
4579         SetLastError(E_INVALIDARG);
4580     return ret;
4581 }
4582
4583
4584 static void import_warning(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle,
4585  int warningID)
4586 {
4587     if (!(dwFlags & CRYPTUI_WIZ_NO_UI))
4588     {
4589         WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
4590         LPCWSTR pTitle;
4591
4592         if (szTitle)
4593             pTitle = szTitle;
4594         else
4595         {
4596             LoadStringW(hInstance, IDS_IMPORT_WIZARD, title,
4597              sizeof(title) / sizeof(title[0]));
4598             pTitle = title;
4599         }
4600         LoadStringW(hInstance, warningID, error,
4601          sizeof(error) / sizeof(error[0]));
4602         MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK);
4603     }
4604 }
4605
4606 static void import_warn_type_mismatch(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle)
4607 {
4608     import_warning(dwFlags, hwnd, szTitle, IDS_IMPORT_TYPE_MISMATCH);
4609 }
4610
4611 static BOOL check_store_context_type(DWORD dwFlags, HCERTSTORE store)
4612 {
4613     BOOL ret;
4614
4615     if (dwFlags &
4616      (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL |
4617      CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4618     {
4619         PCCERT_CONTEXT cert;
4620         PCCRL_CONTEXT crl;
4621         PCCTL_CONTEXT ctl;
4622
4623         ret = TRUE;
4624         if ((cert = CertEnumCertificatesInStore(store, NULL)))
4625         {
4626             CertFreeCertificateContext(cert);
4627             if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT))
4628                 ret = FALSE;
4629         }
4630         if (ret && (crl = CertEnumCRLsInStore(store, NULL)))
4631         {
4632             CertFreeCRLContext(crl);
4633             if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL))
4634                 ret = FALSE;
4635         }
4636         if (ret && (ctl = CertEnumCTLsInStore(store, NULL)))
4637         {
4638             CertFreeCTLContext(ctl);
4639             if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4640                 ret = FALSE;
4641         }
4642     }
4643     else
4644         ret = TRUE;
4645     if (!ret)
4646         SetLastError(E_INVALIDARG);
4647     return ret;
4648 }
4649
4650 static BOOL import_store(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle,
4651  HCERTSTORE source, HCERTSTORE dest)
4652 {
4653     BOOL ret;
4654
4655     if ((ret = check_store_context_type(dwFlags, source)))
4656     {
4657         PCCERT_CONTEXT cert = NULL;
4658         PCCRL_CONTEXT crl = NULL;
4659         PCCTL_CONTEXT ctl = NULL;
4660
4661         do {
4662             cert = CertEnumCertificatesInStore(source, cert);
4663             if (cert)
4664                 ret = import_cert(cert, dest);
4665         } while (ret && cert);
4666         do {
4667             crl = CertEnumCRLsInStore(source, crl);
4668             if (crl)
4669                 ret = import_crl(crl, dest);
4670         } while (ret && crl);
4671         do {
4672             ctl = CertEnumCTLsInStore(source, ctl);
4673             if (ctl)
4674                 ret = import_ctl(ctl, dest);
4675         } while (ret && ctl);
4676     }
4677     else
4678         import_warn_type_mismatch(dwFlags, hwnd, szTitle);
4679     return ret;
4680 }
4681
4682 static HCERTSTORE open_store_from_file(DWORD dwFlags, LPCWSTR fileName,
4683  DWORD *pContentType)
4684 {
4685     HCERTSTORE store = NULL;
4686     DWORD contentType = 0, expectedContentTypeFlags;
4687
4688     if (dwFlags &
4689      (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL |
4690      CRYPTUI_WIZ_IMPORT_ALLOW_CTL))
4691     {
4692         expectedContentTypeFlags =
4693          CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
4694          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
4695          CERT_QUERY_CONTENT_FLAG_PFX;
4696         if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT)
4697             expectedContentTypeFlags |=
4698              CERT_QUERY_CONTENT_FLAG_CERT |
4699              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT;
4700         if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL)
4701             expectedContentTypeFlags |=
4702              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
4703              CERT_QUERY_CONTENT_FLAG_CRL;
4704         if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL)
4705             expectedContentTypeFlags |=
4706              CERT_QUERY_CONTENT_FLAG_CTL |
4707              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL;
4708     }
4709     else
4710         expectedContentTypeFlags =
4711          CERT_QUERY_CONTENT_FLAG_CERT |
4712          CERT_QUERY_CONTENT_FLAG_CTL |
4713          CERT_QUERY_CONTENT_FLAG_CRL |
4714          CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
4715          CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
4716          CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
4717          CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
4718          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
4719          CERT_QUERY_CONTENT_FLAG_PFX;
4720
4721     CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName,
4722      expectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL,
4723      &contentType, NULL, &store, NULL, NULL);
4724     if (pContentType)
4725         *pContentType = contentType;
4726     return store;
4727 }
4728
4729 static BOOL import_file(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle,
4730  LPCWSTR fileName, HCERTSTORE dest)
4731 {
4732     HCERTSTORE source;
4733     BOOL ret;
4734
4735     if ((source = open_store_from_file(dwFlags, fileName, NULL)))
4736     {
4737         ret = import_store(dwFlags, hwnd, szTitle, source, dest);
4738         CertCloseStore(source, 0);
4739     }
4740     else
4741         ret = FALSE;
4742     return ret;
4743 }
4744
4745 struct ImportWizData
4746 {
4747     HFONT titleFont;
4748     DWORD dwFlags;
4749     LPCWSTR pwszWizardTitle;
4750     CRYPTUI_WIZ_IMPORT_SRC_INFO importSrc;
4751     LPWSTR fileName;
4752     DWORD contentType;
4753     BOOL freeSource;
4754     HCERTSTORE hDestCertStore;
4755     BOOL freeDest;
4756     BOOL autoDest;
4757     BOOL success;
4758 };
4759
4760 static LRESULT CALLBACK import_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4761  LPARAM lp)
4762 {
4763     LRESULT ret = 0;
4764
4765     switch (msg)
4766     {
4767     case WM_INITDIALOG:
4768     {
4769         struct ImportWizData *data;
4770         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
4771         WCHAR fontFace[MAX_STRING_LEN];
4772         HDC hDC = GetDC(hwnd);
4773         int height;
4774
4775         data = (struct ImportWizData *)page->lParam;
4776         LoadStringW(hInstance, IDS_WIZARD_TITLE_FONT, fontFace,
4777          sizeof(fontFace) / sizeof(fontFace[0]));
4778         height = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
4779         data->titleFont = CreateFontW(height, 0, 0, 0, FW_BOLD, 0, 0, 0,
4780          DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
4781          DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontFace);
4782         SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_TITLE), WM_SETFONT,
4783          (WPARAM)data->titleFont, TRUE);
4784         ReleaseDC(hwnd, hDC);
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, PSWIZB_NEXT);
4795             ret = TRUE;
4796             break;
4797         }
4798         break;
4799     }
4800     }
4801     return ret;
4802 }
4803
4804 static const WCHAR filter_cert[] = { '*','.','c','e','r',';','*','.',
4805  'c','r','t',0 };
4806 static const WCHAR filter_pfx[] = { '*','.','p','f','x',';','*','.',
4807  'p','1','2',0 };
4808 static const WCHAR filter_crl[] = { '*','.','c','r','l',0 };
4809 static const WCHAR filter_ctl[] = { '*','.','s','t','l',0 };
4810 static const WCHAR filter_serialized_store[] = { '*','.','s','s','t',0 };
4811 static const WCHAR filter_cms[] = { '*','.','s','p','c',';','*','.',
4812  'p','7','b',0 };
4813 static const WCHAR filter_all[] = { '*','.','*',0 };
4814
4815 struct StringToFilter
4816 {
4817     int     id;
4818     DWORD   allowFlags;
4819     LPCWSTR filter;
4820 } import_filters[] = {
4821  { IDS_IMPORT_FILTER_CERT, CRYPTUI_WIZ_IMPORT_ALLOW_CERT, filter_cert },
4822  { IDS_IMPORT_FILTER_PFX, 0, filter_pfx },
4823  { IDS_IMPORT_FILTER_CRL, CRYPTUI_WIZ_IMPORT_ALLOW_CRL, filter_crl },
4824  { IDS_IMPORT_FILTER_CTL, CRYPTUI_WIZ_IMPORT_ALLOW_CTL, filter_ctl },
4825  { IDS_IMPORT_FILTER_SERIALIZED_STORE, 0, filter_serialized_store },
4826  { IDS_IMPORT_FILTER_CMS, 0, filter_cms },
4827  { IDS_IMPORT_FILTER_ALL, 0, filter_all },
4828 };
4829
4830 static WCHAR *make_import_file_filter(DWORD dwFlags)
4831 {
4832     DWORD i;
4833     int len, totalLen = 2;
4834     LPWSTR filter = NULL, str;
4835
4836     for (i = 0; i < sizeof(import_filters) / sizeof(import_filters[0]); i++)
4837     {
4838         if (!import_filters[i].allowFlags || !dwFlags ||
4839          (dwFlags & import_filters[i].allowFlags))
4840         {
4841             len = LoadStringW(hInstance, import_filters[i].id, (LPWSTR)&str, 0);
4842             totalLen += len + strlenW(import_filters[i].filter) + 2;
4843         }
4844     }
4845     filter = HeapAlloc(GetProcessHeap(), 0, totalLen * sizeof(WCHAR));
4846     if (filter)
4847     {
4848         LPWSTR ptr;
4849
4850         ptr = filter;
4851         for (i = 0; i < sizeof(import_filters) / sizeof(import_filters[0]); i++)
4852         {
4853             if (!import_filters[i].allowFlags || !dwFlags ||
4854              (dwFlags & import_filters[i].allowFlags))
4855             {
4856                 len = LoadStringW(hInstance, import_filters[i].id,
4857                  (LPWSTR)&str, 0);
4858                 memcpy(ptr, str, len * sizeof(WCHAR));
4859                 ptr += len;
4860                 *ptr++ = 0;
4861                 strcpyW(ptr, import_filters[i].filter);
4862                 ptr += strlenW(import_filters[i].filter) + 1;
4863             }
4864         }
4865         *ptr++ = 0;
4866     }
4867     return filter;
4868 }
4869
4870 static BOOL import_validate_filename(HWND hwnd, struct ImportWizData *data,
4871  LPCWSTR fileName)
4872 {
4873     HANDLE file;
4874     BOOL ret = FALSE;
4875
4876     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
4877      OPEN_EXISTING, 0, NULL);
4878     if (file != INVALID_HANDLE_VALUE)
4879     {
4880         HCERTSTORE source = open_store_from_file(data->dwFlags, fileName,
4881          &data->contentType);
4882         int warningID = 0;
4883
4884         if (!source)
4885             warningID = IDS_IMPORT_BAD_FORMAT;
4886         else if (!check_store_context_type(data->dwFlags, source))
4887             warningID = IDS_IMPORT_TYPE_MISMATCH;
4888         else
4889         {
4890             data->importSrc.dwSubjectChoice =
4891              CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE;
4892             data->importSrc.u.hCertStore = source;
4893             data->freeSource = TRUE;
4894             ret = TRUE;
4895         }
4896         if (warningID)
4897         {
4898             import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
4899              warningID);
4900         }
4901         CloseHandle(file);
4902     }
4903     else
4904     {
4905         WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
4906         LPCWSTR pTitle;
4907         LPWSTR msgBuf, fullError;
4908
4909         if (data->pwszWizardTitle)
4910             pTitle = data->pwszWizardTitle;
4911         else
4912         {
4913             LoadStringW(hInstance, IDS_IMPORT_WIZARD, title,
4914              sizeof(title) / sizeof(title[0]));
4915             pTitle = title;
4916         }
4917         LoadStringW(hInstance, IDS_IMPORT_OPEN_FAILED, error,
4918          sizeof(error) / sizeof(error[0]));
4919         FormatMessageW(
4920          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
4921          GetLastError(), 0, (LPWSTR) &msgBuf, 0, NULL);
4922         fullError = HeapAlloc(GetProcessHeap(), 0,
4923          (strlenW(error) + strlenW(fileName) + strlenW(msgBuf) + 3)
4924          * sizeof(WCHAR));
4925         if (fullError)
4926         {
4927             LPWSTR ptr = fullError;
4928
4929             strcpyW(ptr, error);
4930             ptr += strlenW(error);
4931             strcpyW(ptr, fileName);
4932             ptr += strlenW(fileName);
4933             *ptr++ = ':';
4934             *ptr++ = '\n';
4935             strcpyW(ptr, msgBuf);
4936             MessageBoxW(hwnd, fullError, pTitle, MB_ICONERROR | MB_OK);
4937             HeapFree(GetProcessHeap(), 0, fullError);
4938         }
4939         LocalFree(msgBuf);
4940     }
4941     return ret;
4942 }
4943
4944 static LRESULT CALLBACK import_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
4945  LPARAM lp)
4946 {
4947     LRESULT ret = 0;
4948     struct ImportWizData *data;
4949
4950     switch (msg)
4951     {
4952     case WM_INITDIALOG:
4953     {
4954         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
4955
4956         data = (struct ImportWizData *)page->lParam;
4957         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
4958         break;
4959     }
4960     case WM_NOTIFY:
4961     {
4962         NMHDR *hdr = (NMHDR *)lp;
4963
4964         switch (hdr->code)
4965         {
4966         case PSN_SETACTIVE:
4967             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
4968              PSWIZB_BACK | PSWIZB_NEXT);
4969             ret = TRUE;
4970             break;
4971         case PSN_WIZNEXT:
4972         {
4973             HWND fileNameEdit = GetDlgItem(hwnd, IDC_IMPORT_FILENAME);
4974             DWORD len = SendMessageW(fileNameEdit, WM_GETTEXTLENGTH, 0, 0);
4975
4976             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
4977             if (!len)
4978             {
4979                 import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
4980                  IDS_IMPORT_EMPTY_FILE);
4981                 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
4982                 ret = 1;
4983             }
4984             else
4985             {
4986                 LPWSTR fileName = HeapAlloc(GetProcessHeap(), 0,
4987                  (len + 1) * sizeof(WCHAR));
4988
4989                 if (fileName)
4990                 {
4991                     SendMessageW(fileNameEdit, WM_GETTEXT, len + 1,
4992                      (LPARAM)fileName);
4993                     if (!import_validate_filename(hwnd, data, fileName))
4994                     {
4995                         HeapFree(GetProcessHeap(), 0, fileName);
4996                         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
4997                         ret = 1;
4998                     }
4999                     else
5000                         data->fileName = fileName;
5001                 }
5002             }
5003             break;
5004         }
5005         }
5006         break;
5007     }
5008     case WM_COMMAND:
5009         switch (wp)
5010         {
5011         case IDC_IMPORT_BROWSE_FILE:
5012         {
5013             OPENFILENAMEW ofn;
5014             WCHAR fileBuf[MAX_PATH];
5015
5016             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5017             memset(&ofn, 0, sizeof(ofn));
5018             ofn.lStructSize = sizeof(ofn);
5019             ofn.hwndOwner = hwnd;
5020             ofn.lpstrFilter = make_import_file_filter(data->dwFlags);
5021             ofn.lpstrFile = fileBuf;
5022             ofn.nMaxFile = sizeof(fileBuf) / sizeof(fileBuf[0]);
5023             fileBuf[0] = 0;
5024             if (GetOpenFileNameW(&ofn))
5025                 SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_FILENAME), WM_SETTEXT,
5026                  0, (LPARAM)ofn.lpstrFile);
5027             HeapFree(GetProcessHeap(), 0, (LPWSTR)ofn.lpstrFilter);
5028             break;
5029         }
5030         }
5031         break;
5032     }
5033     return ret;
5034 }
5035
5036 static LRESULT CALLBACK import_store_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
5037  LPARAM lp)
5038 {
5039     LRESULT ret = 0;
5040     struct ImportWizData *data;
5041
5042     switch (msg)
5043     {
5044     case WM_INITDIALOG:
5045     {
5046         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
5047
5048         data = (struct ImportWizData *)page->lParam;
5049         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
5050         if (!data->hDestCertStore)
5051         {
5052             SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_AUTO_STORE), BM_CLICK,
5053              0, 0);
5054             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), FALSE);
5055             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), FALSE);
5056             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE), FALSE);
5057         }
5058         else
5059         {
5060             WCHAR storeTitle[MAX_STRING_LEN];
5061
5062             SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE), BM_CLICK,
5063              0, 0);
5064             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), TRUE);
5065             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), TRUE);
5066             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE),
5067              !(data->dwFlags & CRYPTUI_WIZ_IMPORT_NO_CHANGE_DEST_STORE));
5068             LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED,
5069              storeTitle, sizeof(storeTitle) / sizeof(storeTitle[0]));
5070             SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_STORE), WM_SETTEXT,
5071              0, (LPARAM)storeTitle);
5072         }
5073         break;
5074     }
5075     case WM_NOTIFY:
5076     {
5077         NMHDR *hdr = (NMHDR *)lp;
5078
5079         switch (hdr->code)
5080         {
5081         case PSN_SETACTIVE:
5082             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
5083              PSWIZB_BACK | PSWIZB_NEXT);
5084             ret = TRUE;
5085             break;
5086         case PSN_WIZNEXT:
5087         {
5088             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5089             if (IsDlgButtonChecked(hwnd, IDC_IMPORT_SPECIFY_STORE) &&
5090              !data->hDestCertStore)
5091             {
5092                 import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
5093                  IDS_IMPORT_SELECT_STORE);
5094                 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
5095                 ret = 1;
5096             }
5097             break;
5098         }
5099         }
5100         break;
5101     }
5102     case WM_COMMAND:
5103         switch (wp)
5104         {
5105         case IDC_IMPORT_AUTO_STORE:
5106             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5107             data->autoDest = TRUE;
5108             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), FALSE);
5109             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), FALSE);
5110             break;
5111         case IDC_IMPORT_SPECIFY_STORE:
5112             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5113             data->autoDest = FALSE;
5114             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), TRUE);
5115             EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), TRUE);
5116             break;
5117         case IDC_IMPORT_BROWSE_STORE:
5118         {
5119             CRYPTUI_ENUM_SYSTEM_STORE_ARGS enumArgs = {
5120              CERT_SYSTEM_STORE_CURRENT_USER, NULL };
5121             CRYPTUI_ENUM_DATA enumData = { 0, NULL, 1, &enumArgs };
5122             CRYPTUI_SELECTSTORE_INFO_W selectInfo;
5123             HCERTSTORE store;
5124
5125             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5126             selectInfo.dwSize = sizeof(selectInfo);
5127             selectInfo.parent = hwnd;
5128             selectInfo.dwFlags = CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE;
5129             selectInfo.pwszTitle = selectInfo.pwszTitle = NULL;
5130             selectInfo.pEnumData = &enumData;
5131             selectInfo.pfnSelectedStoreCallback = NULL;
5132             if ((store = CryptUIDlgSelectStoreW(&selectInfo)))
5133             {
5134                 WCHAR storeTitle[MAX_STRING_LEN];
5135
5136                 LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED,
5137                  storeTitle, sizeof(storeTitle) / sizeof(storeTitle[0]));
5138                 SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_STORE), WM_SETTEXT,
5139                  0, (LPARAM)storeTitle);
5140                 data->hDestCertStore = store;
5141                 data->freeDest = TRUE;
5142             }
5143             break;
5144         }
5145         }
5146         break;
5147     }
5148     return ret;
5149 }
5150
5151 static void show_import_details(HWND lv, struct ImportWizData *data)
5152 {
5153     WCHAR text[MAX_STRING_LEN];
5154     LVITEMW item;
5155     int contentID;
5156
5157     item.mask = LVIF_TEXT;
5158     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
5159     item.iSubItem = 0;
5160     LoadStringW(hInstance, IDS_IMPORT_STORE_SELECTION, text,
5161      sizeof(text)/ sizeof(text[0]));
5162     item.pszText = text;
5163     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
5164     item.iSubItem = 1;
5165     if (data->autoDest)
5166         LoadStringW(hInstance, IDS_IMPORT_DEST_AUTOMATIC, text,
5167          sizeof(text)/ sizeof(text[0]));
5168     else
5169         LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED, text,
5170          sizeof(text)/ sizeof(text[0]));
5171     SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
5172     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
5173     item.iSubItem = 0;
5174     LoadStringW(hInstance, IDS_IMPORT_CONTENT, text,
5175      sizeof(text)/ sizeof(text[0]));
5176     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
5177     switch (data->contentType)
5178     {
5179     case CERT_QUERY_CONTENT_CERT:
5180     case CERT_QUERY_CONTENT_SERIALIZED_CERT:
5181         contentID = IDS_IMPORT_CONTENT_CERT;
5182         break;
5183     case CERT_QUERY_CONTENT_CRL:
5184     case CERT_QUERY_CONTENT_SERIALIZED_CRL:
5185         contentID = IDS_IMPORT_CONTENT_CRL;
5186         break;
5187     case CERT_QUERY_CONTENT_CTL:
5188     case CERT_QUERY_CONTENT_SERIALIZED_CTL:
5189         contentID = IDS_IMPORT_CONTENT_CTL;
5190         break;
5191     case CERT_QUERY_CONTENT_PKCS7_SIGNED:
5192         contentID = IDS_IMPORT_CONTENT_CMS;
5193         break;
5194     case CERT_QUERY_CONTENT_FLAG_PFX:
5195         contentID = IDS_IMPORT_CONTENT_PFX;
5196         break;
5197     default:
5198         contentID = IDS_IMPORT_CONTENT_STORE;
5199         break;
5200     }
5201     LoadStringW(hInstance, contentID, text, sizeof(text)/ sizeof(text[0]));
5202     item.iSubItem = 1;
5203     SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
5204     if (data->fileName)
5205     {
5206         item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
5207         item.iSubItem = 0;
5208         LoadStringW(hInstance, IDS_IMPORT_FILE, text,
5209          sizeof(text)/ sizeof(text[0]));
5210         SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
5211         item.iSubItem = 1;
5212         item.pszText = data->fileName;
5213         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
5214     }
5215 }
5216
5217 static BOOL do_import(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
5218  PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
5219 {
5220     BOOL ret;
5221
5222     switch (pImportSrc->dwSubjectChoice)
5223     {
5224     case CRYPTUI_WIZ_IMPORT_SUBJECT_FILE:
5225         ret = import_file(dwFlags, hwndParent, pwszWizardTitle,
5226          pImportSrc->u.pwszFileName, hDestCertStore);
5227         break;
5228     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
5229         if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CERT)))
5230             ret = import_cert(pImportSrc->u.pCertContext, hDestCertStore);
5231         else
5232             import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle);
5233         break;
5234     case CRYPTUI_WIZ_IMPORT_SUBJECT_CRL_CONTEXT:
5235         if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CRL)))
5236             ret = import_crl(pImportSrc->u.pCRLContext, hDestCertStore);
5237         else
5238             import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle);
5239         break;
5240     case CRYPTUI_WIZ_IMPORT_SUBJECT_CTL_CONTEXT:
5241         if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CTL)))
5242             ret = import_ctl(pImportSrc->u.pCTLContext, hDestCertStore);
5243         else
5244             import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle);
5245         break;
5246     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE:
5247         ret = import_store(dwFlags, hwndParent, pwszWizardTitle,
5248          pImportSrc->u.hCertStore, hDestCertStore);
5249         break;
5250     default:
5251         WARN("unknown source type: %u\n", pImportSrc->dwSubjectChoice);
5252         SetLastError(E_INVALIDARG);
5253         ret = FALSE;
5254     }
5255     return ret;
5256 }
5257
5258 static LRESULT CALLBACK import_finish_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
5259  LPARAM lp)
5260 {
5261     LRESULT ret = 0;
5262     struct ImportWizData *data;
5263
5264     switch (msg)
5265     {
5266     case WM_INITDIALOG:
5267     {
5268         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
5269         HWND lv = GetDlgItem(hwnd, IDC_IMPORT_SETTINGS);
5270         RECT rc;
5271         LVCOLUMNW column;
5272
5273         data = (struct ImportWizData *)page->lParam;
5274         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
5275         SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_TITLE), WM_SETFONT,
5276          (WPARAM)data->titleFont, TRUE);
5277         GetWindowRect(lv, &rc);
5278         column.mask = LVCF_WIDTH;
5279         column.cx = (rc.right - rc.left) / 2 - 2;
5280         SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
5281         SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
5282         show_import_details(lv, data);
5283         break;
5284     }
5285     case WM_NOTIFY:
5286     {
5287         NMHDR *hdr = (NMHDR *)lp;
5288
5289         switch (hdr->code)
5290         {
5291         case PSN_SETACTIVE:
5292         {
5293             HWND lv = GetDlgItem(hwnd, IDC_IMPORT_SETTINGS);
5294
5295             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5296             SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0);
5297             show_import_details(lv, data);
5298             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
5299              PSWIZB_BACK | PSWIZB_FINISH);
5300             ret = TRUE;
5301             break;
5302         }
5303         case PSN_WIZFINISH:
5304         {
5305             data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5306             if ((data->success = do_import(data->dwFlags, hwnd,
5307              data->pwszWizardTitle, &data->importSrc, data->hDestCertStore)))
5308             {
5309                 WCHAR title[MAX_STRING_LEN], message[MAX_STRING_LEN];
5310                 LPCWSTR pTitle;
5311
5312                 if (data->pwszWizardTitle)
5313                     pTitle = data->pwszWizardTitle;
5314                 else
5315                 {
5316                     LoadStringW(hInstance, IDS_IMPORT_WIZARD, title,
5317                      sizeof(title) / sizeof(title[0]));
5318                     pTitle = title;
5319                 }
5320                 LoadStringW(hInstance, IDS_IMPORT_SUCCEEDED, message,
5321                  sizeof(message) / sizeof(message[0]));
5322                 MessageBoxW(hwnd, message, pTitle, MB_OK);
5323             }
5324             else
5325                 import_warning(data->dwFlags, hwnd, data->pwszWizardTitle,
5326                  IDS_IMPORT_FAILED);
5327             break;
5328         }
5329         }
5330         break;
5331     }
5332     }
5333     return ret;
5334 }
5335
5336 static BOOL show_import_ui(DWORD dwFlags, HWND hwndParent,
5337  LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc,
5338  HCERTSTORE hDestCertStore)
5339 {
5340     PROPSHEETHEADERW hdr;
5341     PROPSHEETPAGEW pages[4];
5342     struct ImportWizData data;
5343     int nPages = 0;
5344
5345     data.dwFlags = dwFlags;
5346     data.pwszWizardTitle = pwszWizardTitle;
5347     if (pImportSrc)
5348         memcpy(&data.importSrc, pImportSrc, sizeof(data.importSrc));
5349     else
5350         memset(&data.importSrc, 0, sizeof(data.importSrc));
5351     data.fileName = NULL;
5352     data.freeSource = FALSE;
5353     data.hDestCertStore = hDestCertStore;
5354     data.freeDest = FALSE;
5355     data.autoDest = TRUE;
5356     data.success = TRUE;
5357
5358     memset(&pages, 0, sizeof(pages));
5359
5360     pages[nPages].dwSize = sizeof(pages[0]);
5361     pages[nPages].hInstance = hInstance;
5362     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_WELCOME);
5363     pages[nPages].pfnDlgProc = import_welcome_dlg_proc;
5364     pages[nPages].dwFlags = PSP_HIDEHEADER;
5365     pages[nPages].lParam = (LPARAM)&data;
5366     nPages++;
5367
5368     if (!pImportSrc ||
5369      pImportSrc->dwSubjectChoice == CRYPTUI_WIZ_IMPORT_SUBJECT_FILE)
5370     {
5371         pages[nPages].dwSize = sizeof(pages[0]);
5372         pages[nPages].hInstance = hInstance;
5373         pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_FILE);
5374         pages[nPages].pfnDlgProc = import_file_dlg_proc;
5375         pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
5376         pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_IMPORT_FILE_TITLE);
5377         pages[nPages].pszHeaderSubTitle =
5378          MAKEINTRESOURCEW(IDS_IMPORT_FILE_SUBTITLE);
5379         pages[nPages].lParam = (LPARAM)&data;
5380         nPages++;
5381     }
5382     else
5383     {
5384         switch (pImportSrc->dwSubjectChoice)
5385         {
5386         case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
5387             data.contentType = CERT_QUERY_CONTENT_CERT;
5388             break;
5389         case CRYPTUI_WIZ_IMPORT_SUBJECT_CRL_CONTEXT:
5390             data.contentType = CERT_QUERY_CONTENT_CRL;
5391             break;
5392         case CRYPTUI_WIZ_IMPORT_SUBJECT_CTL_CONTEXT:
5393             data.contentType = CERT_QUERY_CONTENT_CTL;
5394             break;
5395         case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE:
5396             data.contentType = CERT_QUERY_CONTENT_SERIALIZED_STORE;
5397             break;
5398         }
5399     }
5400
5401     pages[nPages].dwSize = sizeof(pages[0]);
5402     pages[nPages].hInstance = hInstance;
5403     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_STORE);
5404     pages[nPages].pfnDlgProc = import_store_dlg_proc;
5405     pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
5406     pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_IMPORT_STORE_TITLE);
5407     pages[nPages].pszHeaderSubTitle =
5408      MAKEINTRESOURCEW(IDS_IMPORT_STORE_SUBTITLE);
5409     pages[nPages].lParam = (LPARAM)&data;
5410     nPages++;
5411
5412     pages[nPages].dwSize = sizeof(pages[0]);
5413     pages[nPages].hInstance = hInstance;
5414     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_FINISH);
5415     pages[nPages].pfnDlgProc = import_finish_dlg_proc;
5416     pages[nPages].dwFlags = PSP_HIDEHEADER;
5417     pages[nPages].lParam = (LPARAM)&data;
5418     nPages++;
5419
5420     memset(&hdr, 0, sizeof(hdr));
5421     hdr.dwSize = sizeof(hdr);
5422     hdr.hwndParent = hwndParent;
5423     hdr.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD97_NEW | PSH_HEADER |
5424      PSH_WATERMARK;
5425     hdr.hInstance = hInstance;
5426     if (pwszWizardTitle)
5427         hdr.pszCaption = pwszWizardTitle;
5428     else
5429         hdr.pszCaption = MAKEINTRESOURCEW(IDS_IMPORT_WIZARD);
5430     hdr.u3.ppsp = pages;
5431     hdr.nPages = nPages;
5432     hdr.u4.pszbmWatermark = MAKEINTRESOURCEW(IDB_CERT_WATERMARK);
5433     hdr.u5.pszbmHeader = MAKEINTRESOURCEW(IDB_CERT_HEADER);
5434     PropertySheetW(&hdr);
5435     HeapFree(GetProcessHeap(), 0, data.fileName);
5436     if (data.freeSource &&
5437      data.importSrc.dwSubjectChoice == CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE)
5438         CertCloseStore(data.importSrc.u.hCertStore, 0);
5439     DeleteObject(data.titleFont);
5440     return data.success;
5441 }
5442
5443 BOOL WINAPI CryptUIWizImport(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
5444                              PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
5445 {
5446     BOOL ret;
5447
5448     TRACE("(0x%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, debugstr_w(pwszWizardTitle),
5449           pImportSrc, hDestCertStore);
5450
5451     if (pImportSrc &&
5452      pImportSrc->dwSize != sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO))
5453     {
5454         SetLastError(E_INVALIDARG);
5455         return FALSE;
5456     }
5457
5458     if (!(dwFlags & CRYPTUI_WIZ_NO_UI))
5459         ret = show_import_ui(dwFlags, hwndParent, pwszWizardTitle, pImportSrc,
5460          hDestCertStore);
5461     else if (pImportSrc)
5462         ret = do_import(dwFlags, hwndParent, pwszWizardTitle, pImportSrc,
5463          hDestCertStore);
5464     else
5465     {
5466         /* Can't have no UI without specifying source */
5467         SetLastError(E_INVALIDARG);
5468         ret = FALSE;
5469     }
5470
5471     return ret;
5472 }
5473
5474 struct ExportWizData
5475 {
5476     HFONT titleFont;
5477     DWORD dwFlags;
5478     LPCWSTR pwszWizardTitle;
5479     PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo;
5480     CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO contextInfo;
5481     LPWSTR fileName;
5482     HANDLE file;
5483     BOOL success;
5484 };
5485
5486 static LRESULT CALLBACK export_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
5487  LPARAM lp)
5488 {
5489     LRESULT ret = 0;
5490
5491     switch (msg)
5492     {
5493     case WM_INITDIALOG:
5494     {
5495         struct ExportWizData *data;
5496         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
5497         WCHAR fontFace[MAX_STRING_LEN];
5498         HDC hDC = GetDC(hwnd);
5499         int height;
5500
5501         data = (struct ExportWizData *)page->lParam;
5502         LoadStringW(hInstance, IDS_WIZARD_TITLE_FONT, fontFace,
5503          sizeof(fontFace) / sizeof(fontFace[0]));
5504         height = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
5505         data->titleFont = CreateFontW(height, 0, 0, 0, FW_BOLD, 0, 0, 0,
5506          DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
5507          DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontFace);
5508         SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_TITLE), WM_SETFONT,
5509          (WPARAM)data->titleFont, TRUE);
5510         ReleaseDC(hwnd, hDC);
5511         break;
5512     }
5513     case WM_NOTIFY:
5514     {
5515         NMHDR *hdr = (NMHDR *)lp;
5516
5517         switch (hdr->code)
5518         {
5519         case PSN_SETACTIVE:
5520             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, PSWIZB_NEXT);
5521             ret = TRUE;
5522             break;
5523         }
5524         break;
5525     }
5526     }
5527     return ret;
5528 }
5529
5530 static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
5531  LPARAM lp)
5532 {
5533     LRESULT ret = 0;
5534     struct ExportWizData *data;
5535
5536     switch (msg)
5537     {
5538     case WM_INITDIALOG:
5539     {
5540         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
5541
5542         data = (struct ExportWizData *)page->lParam;
5543         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
5544         SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_DER), BM_CLICK, 0, 0);
5545         break;
5546     }
5547     case WM_NOTIFY:
5548     {
5549         NMHDR *hdr = (NMHDR *)lp;
5550
5551         switch (hdr->code)
5552         {
5553         case PSN_SETACTIVE:
5554             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
5555              PSWIZB_BACK | PSWIZB_NEXT);
5556             ret = TRUE;
5557             break;
5558         case PSN_WIZNEXT:
5559         {
5560             data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5561             if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_DER))
5562                 data->contextInfo.dwExportFormat =
5563                  CRYPTUI_WIZ_EXPORT_FORMAT_DER;
5564             else if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_BASE64))
5565                 data->contextInfo.dwExportFormat =
5566                  CRYPTUI_WIZ_EXPORT_FORMAT_BASE64;
5567             else if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_CMS))
5568             {
5569                 data->contextInfo.dwExportFormat =
5570                  CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7;
5571                 if (IsDlgButtonChecked(hwnd, IDC_EXPORT_CMS_INCLUDE_CHAIN))
5572                     data->contextInfo.fExportChain =
5573                      CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7;
5574             }
5575             else
5576             {
5577                 data->contextInfo.dwExportFormat =
5578                  CRYPTUI_WIZ_EXPORT_FORMAT_PFX;
5579                 if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_INCLUDE_CHAIN))
5580                     data->contextInfo.fExportChain = TRUE;
5581                 if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION))
5582                     data->contextInfo.fStrongEncryption = TRUE;
5583                 if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_DELETE_PRIVATE_KEY))
5584                     data->contextInfo.fExportPrivateKeys = TRUE;
5585             }
5586             break;
5587         }
5588         }
5589         break;
5590     }
5591     case WM_COMMAND:
5592         switch (HIWORD(wp))
5593         {
5594         case BN_CLICKED:
5595             switch (LOWORD(wp))
5596             {
5597             case IDC_EXPORT_FORMAT_DER:
5598             case IDC_EXPORT_FORMAT_BASE64:
5599                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_CMS_INCLUDE_CHAIN),
5600                  FALSE);
5601                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_INCLUDE_CHAIN),
5602                  FALSE);
5603                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION),
5604                  FALSE);
5605                 EnableWindow(GetDlgItem(hwnd,
5606                  IDC_EXPORT_PFX_DELETE_PRIVATE_KEY), FALSE);
5607                 break;
5608             case IDC_EXPORT_FORMAT_CMS:
5609                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_CMS_INCLUDE_CHAIN),
5610                  TRUE);
5611                 break;
5612             case IDC_EXPORT_FORMAT_PFX:
5613                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_INCLUDE_CHAIN),
5614                  TRUE);
5615                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION),
5616                  TRUE);
5617                 EnableWindow(GetDlgItem(hwnd,
5618                  IDC_EXPORT_PFX_DELETE_PRIVATE_KEY), TRUE);
5619                 break;
5620             }
5621             break;
5622         }
5623         break;
5624     }
5625     return ret;
5626 }
5627
5628 static LPWSTR export_append_extension(struct ExportWizData *data,
5629  LPWSTR fileName)
5630 {
5631     static const WCHAR cer[] = { '.','c','e','r',0 };
5632     static const WCHAR crl[] = { '.','c','r','l',0 };
5633     static const WCHAR ctl[] = { '.','c','t','l',0 };
5634     static const WCHAR p7b[] = { '.','p','7','b',0 };
5635     static const WCHAR pfx[] = { '.','p','f','x',0 };
5636     LPCWSTR extension;
5637     LPWSTR dot;
5638     BOOL appendExtension;
5639
5640     switch (data->contextInfo.dwExportFormat)
5641     {
5642     case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7:
5643         extension = p7b;
5644         break;
5645     case CRYPTUI_WIZ_EXPORT_FORMAT_PFX:
5646         extension = pfx;
5647         break;
5648     default:
5649         switch (data->pExportInfo->dwSubjectChoice)
5650         {
5651         case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT:
5652             extension = crl;
5653             break;
5654         case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT:
5655             extension = ctl;
5656             break;
5657         default:
5658             extension = cer;
5659         }
5660     }
5661     dot = strrchrW(fileName, '.');
5662     if (dot)
5663         appendExtension = strcmpiW(dot, extension) != 0;
5664     else
5665         appendExtension = TRUE;
5666     if (appendExtension)
5667     {
5668         fileName = HeapReAlloc(GetProcessHeap(), 0, fileName,
5669          (strlenW(fileName) + strlenW(extension) + 1) * sizeof(WCHAR));
5670         if (fileName)
5671             strcatW(fileName, extension);
5672     }
5673     return fileName;
5674 }
5675
5676 static BOOL export_validate_filename(HWND hwnd, struct ExportWizData *data,
5677  LPCWSTR fileName)
5678 {
5679     HANDLE file;
5680     BOOL tryCreate = TRUE, forceCreate = FALSE, ret = FALSE;
5681
5682     file = CreateFileW(fileName, GENERIC_WRITE,
5683      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
5684     if (file != INVALID_HANDLE_VALUE)
5685     {
5686         WCHAR warning[MAX_STRING_LEN], title[MAX_STRING_LEN];
5687         LPCWSTR pTitle;
5688
5689         if (data->pwszWizardTitle)
5690             pTitle = data->pwszWizardTitle;
5691         else
5692         {
5693             LoadStringW(hInstance, IDS_EXPORT_WIZARD, title,
5694              sizeof(title) / sizeof(title[0]));
5695             pTitle = title;
5696         }
5697         LoadStringW(hInstance, IDS_EXPORT_FILE_EXISTS, warning,
5698          sizeof(warning) / sizeof(warning[0]));
5699         if (MessageBoxW(hwnd, warning, pTitle, MB_YESNO) == IDYES)
5700             forceCreate = TRUE;
5701         else
5702             tryCreate = FALSE;
5703         CloseHandle(file);
5704     }
5705     if (tryCreate)
5706     {
5707         file = CreateFileW(fileName, GENERIC_WRITE,
5708          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
5709          forceCreate ? CREATE_ALWAYS : CREATE_NEW,
5710          0, NULL);
5711         if (file != INVALID_HANDLE_VALUE)
5712         {
5713             data->file = file;
5714             ret = TRUE;
5715         }
5716         else
5717         {
5718             WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
5719             LPCWSTR pTitle;
5720             LPWSTR msgBuf, fullError;
5721
5722             if (data->pwszWizardTitle)
5723                 pTitle = data->pwszWizardTitle;
5724             else
5725             {
5726                 LoadStringW(hInstance, IDS_EXPORT_WIZARD, title,
5727                  sizeof(title) / sizeof(title[0]));
5728                 pTitle = title;
5729             }
5730             LoadStringW(hInstance, IDS_IMPORT_OPEN_FAILED, error,
5731              sizeof(error) / sizeof(error[0]));
5732             FormatMessageW(
5733              FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
5734              GetLastError(), 0, (LPWSTR) &msgBuf, 0, NULL);
5735             fullError = HeapAlloc(GetProcessHeap(), 0,
5736              (strlenW(error) + strlenW(fileName) + strlenW(msgBuf) + 3)
5737              * sizeof(WCHAR));
5738             if (fullError)
5739             {
5740                 LPWSTR ptr = fullError;
5741
5742                 strcpyW(ptr, error);
5743                 ptr += strlenW(error);
5744                 strcpyW(ptr, fileName);
5745                 ptr += strlenW(fileName);
5746                 *ptr++ = ':';
5747                 *ptr++ = '\n';
5748                 strcpyW(ptr, msgBuf);
5749                 MessageBoxW(hwnd, fullError, pTitle, MB_ICONERROR | MB_OK);
5750                 HeapFree(GetProcessHeap(), 0, fullError);
5751             }
5752             LocalFree(msgBuf);
5753         }
5754     }
5755     return ret;
5756 }
5757
5758 static const WCHAR export_filter_cert[] = { '*','.','c','e','r',0 };
5759 static const WCHAR export_filter_crl[] = { '*','.','c','r','l',0 };
5760 static const WCHAR export_filter_ctl[] = { '*','.','s','t','l',0 };
5761 static const WCHAR export_filter_cms[] = { '*','.','p','7','b',0 };
5762 static const WCHAR export_filter_pfx[] = { '*','.','p','f','x',0 };
5763
5764 static WCHAR *make_export_file_filter(DWORD exportFormat, DWORD subjectChoice)
5765 {
5766     int baseLen, allLen, totalLen = 2, baseID;
5767     LPWSTR filter = NULL, baseFilter, all;
5768     LPCWSTR filterStr;
5769
5770     switch (exportFormat)
5771     {
5772     case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64:
5773         baseID = IDS_EXPORT_FILTER_BASE64_CERT;
5774         filterStr = export_filter_cert;
5775         break;
5776     case CRYPTUI_WIZ_EXPORT_FORMAT_PFX:
5777         baseID = IDS_EXPORT_FILTER_PFX;
5778         filterStr = export_filter_pfx;
5779         break;
5780     case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7:
5781         baseID = IDS_EXPORT_FILTER_CMS;
5782         filterStr = export_filter_cms;
5783         break;
5784     default:
5785         switch (subjectChoice)
5786         {
5787         case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT:
5788             baseID = IDS_EXPORT_FILTER_CRL;
5789             filterStr = export_filter_crl;
5790             break;
5791         case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT:
5792             baseID = IDS_EXPORT_FILTER_CTL;
5793             filterStr = export_filter_ctl;
5794             break;
5795         default:
5796             baseID = IDS_EXPORT_FILTER_CERT;
5797             filterStr = export_filter_cert;
5798             break;
5799         }
5800     }
5801     baseLen = LoadStringW(hInstance, baseID, (LPWSTR)&baseFilter, 0);
5802     totalLen += baseLen + strlenW(filterStr) + 2;
5803     allLen = LoadStringW(hInstance, IDS_IMPORT_FILTER_ALL, (LPWSTR)&all, 0);
5804     totalLen += allLen + strlenW(filter_all) + 2;
5805     filter = HeapAlloc(GetProcessHeap(), 0, totalLen * sizeof(WCHAR));
5806     if (filter)
5807     {
5808         LPWSTR ptr;
5809
5810         ptr = filter;
5811         memcpy(ptr, baseFilter, baseLen * sizeof(WCHAR));
5812         ptr += baseLen;
5813         *ptr++ = 0;
5814         strcpyW(ptr, filterStr);
5815         ptr += strlenW(filterStr) + 1;
5816         memcpy(ptr, all, allLen * sizeof(WCHAR));
5817         ptr += allLen;
5818         *ptr++ = 0;
5819         strcpyW(ptr, filter_all);
5820         ptr += strlenW(filter_all) + 1;
5821         *ptr++ = 0;
5822     }
5823     return filter;
5824 }
5825
5826 static LRESULT CALLBACK export_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
5827  LPARAM lp)
5828 {
5829     LRESULT ret = 0;
5830     struct ExportWizData *data;
5831
5832     switch (msg)
5833     {
5834     case WM_INITDIALOG:
5835     {
5836         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
5837
5838         data = (struct ExportWizData *)page->lParam;
5839         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
5840         if (data->pExportInfo->dwSubjectChoice ==
5841          CRYPTUI_WIZ_EXPORT_CERT_CONTEXT)
5842         {
5843             DWORD size;
5844
5845             /* If there's a CRYPT_KEY_PROV_INFO set for this cert, assume the
5846              * cert has a private key.
5847              */
5848             if (CertGetCertificateContextProperty(
5849              data->pExportInfo->u.pCertContext, CERT_KEY_PROV_INFO_PROP_ID,
5850              NULL, &size))
5851                 EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_PFX), TRUE);
5852         }
5853         break;
5854     }
5855     case WM_NOTIFY:
5856     {
5857         NMHDR *hdr = (NMHDR *)lp;
5858
5859         switch (hdr->code)
5860         {
5861         case PSN_WIZNEXT:
5862         {
5863             HWND fileNameEdit = GetDlgItem(hwnd, IDC_EXPORT_FILENAME);
5864             DWORD len = SendMessageW(fileNameEdit, WM_GETTEXTLENGTH, 0, 0);
5865
5866             data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5867             if (!len)
5868             {
5869                 WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
5870                 LPCWSTR pTitle;
5871
5872                 if (data->pwszWizardTitle)
5873                     pTitle = data->pwszWizardTitle;
5874                 else
5875                 {
5876                     LoadStringW(hInstance, IDS_EXPORT_WIZARD, title,
5877                      sizeof(title) / sizeof(title[0]));
5878                     pTitle = title;
5879                 }
5880                 LoadStringW(hInstance, IDS_IMPORT_EMPTY_FILE, error,
5881                  sizeof(error) / sizeof(error[0]));
5882                 MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK);
5883                 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
5884                 ret = 1;
5885             }
5886             else
5887             {
5888                 LPWSTR fileName = HeapAlloc(GetProcessHeap(), 0,
5889                  (len + 1) * sizeof(WCHAR));
5890
5891                 if (fileName)
5892                 {
5893                     SendMessageW(fileNameEdit, WM_GETTEXT, len + 1,
5894                      (LPARAM)fileName);
5895                     fileName = export_append_extension(data, fileName);
5896                     if (!export_validate_filename(hwnd, data, fileName))
5897                     {
5898                         HeapFree(GetProcessHeap(), 0, fileName);
5899                         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
5900                         ret = 1;
5901                     }
5902                     else
5903                         data->fileName = fileName;
5904                 }
5905             }
5906             break;
5907         }
5908         case PSN_SETACTIVE:
5909             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
5910              PSWIZB_BACK | PSWIZB_NEXT);
5911             ret = TRUE;
5912             break;
5913         }
5914         break;
5915     }
5916     case WM_COMMAND:
5917         switch (wp)
5918         {
5919         case IDC_EXPORT_BROWSE_FILE:
5920         {
5921             OPENFILENAMEW ofn;
5922             WCHAR fileBuf[MAX_PATH];
5923
5924             data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
5925             memset(&ofn, 0, sizeof(ofn));
5926             ofn.lStructSize = sizeof(ofn);
5927             ofn.hwndOwner = hwnd;
5928             ofn.lpstrFilter = make_export_file_filter(
5929              data->contextInfo.dwExportFormat,
5930              data->pExportInfo->dwSubjectChoice);
5931             ofn.lpstrFile = fileBuf;
5932             ofn.nMaxFile = sizeof(fileBuf) / sizeof(fileBuf[0]);
5933             fileBuf[0] = 0;
5934             if (GetSaveFileNameW(&ofn))
5935                 SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_FILENAME), WM_SETTEXT,
5936                  0, (LPARAM)ofn.lpstrFile);
5937             HeapFree(GetProcessHeap(), 0, (LPWSTR)ofn.lpstrFilter);
5938             break;
5939         }
5940         }
5941         break;
5942     }
5943     return ret;
5944 }
5945
5946 static void show_export_details(HWND lv, struct ExportWizData *data)
5947 {
5948     WCHAR text[MAX_STRING_LEN];
5949     LVITEMW item;
5950     int contentID;
5951
5952     item.mask = LVIF_TEXT;
5953     if (data->fileName)
5954     {
5955         item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
5956         item.iSubItem = 0;
5957         LoadStringW(hInstance, IDS_IMPORT_FILE, text,
5958          sizeof(text)/ sizeof(text[0]));
5959         item.pszText = text;
5960         SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
5961         item.iSubItem = 1;
5962         item.pszText = data->fileName;
5963         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
5964     }
5965
5966     item.pszText = text;
5967     switch (data->pExportInfo->dwSubjectChoice)
5968     {
5969     case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT:
5970     case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT:
5971         /* do nothing */
5972         break;
5973     default:
5974     {
5975         item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
5976         item.iSubItem = 0;
5977         LoadStringW(hInstance, IDS_EXPORT_INCLUDE_CHAIN, text,
5978          sizeof(text) / sizeof(text[0]));
5979         SendMessageW(lv, LVM_INSERTITEMW, item.iItem, (LPARAM)&item);
5980         item.iSubItem = 1;
5981         LoadStringW(hInstance,
5982          data->contextInfo.fExportChain ? IDS_YES : IDS_NO, text,
5983          sizeof(text) / sizeof(text[0]));
5984         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
5985
5986         item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
5987         item.iSubItem = 0;
5988         LoadStringW(hInstance, IDS_EXPORT_KEYS, text,
5989          sizeof(text) / sizeof(text[0]));
5990         SendMessageW(lv, LVM_INSERTITEMW, item.iItem, (LPARAM)&item);
5991         item.iSubItem = 1;
5992         LoadStringW(hInstance,
5993          data->contextInfo.fExportPrivateKeys ? IDS_YES : IDS_NO, text,
5994          sizeof(text) / sizeof(text[0]));
5995         SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
5996     }
5997     }
5998
5999     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
6000     item.iSubItem = 0;
6001     LoadStringW(hInstance, IDS_EXPORT_FORMAT, text,
6002      sizeof(text)/ sizeof(text[0]));
6003     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
6004
6005     item.iSubItem = 1;
6006     switch (data->pExportInfo->dwSubjectChoice)
6007     {
6008     case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT:
6009         contentID = IDS_EXPORT_FILTER_CRL;
6010         break;
6011     case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT:
6012         contentID = IDS_EXPORT_FILTER_CTL;
6013         break;
6014     default:
6015         switch (data->contextInfo.dwExportFormat)
6016         {
6017         case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64:
6018             contentID = IDS_EXPORT_FILTER_BASE64_CERT;
6019             break;
6020         case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7:
6021             contentID = IDS_EXPORT_FILTER_CMS;
6022             break;
6023         case CRYPTUI_WIZ_EXPORT_FORMAT_PFX:
6024             contentID = IDS_EXPORT_FILTER_PFX;
6025             break;
6026         default:
6027             contentID = IDS_EXPORT_FILTER_CERT;
6028         }
6029     }
6030     LoadStringW(hInstance, contentID, text, sizeof(text) / sizeof(text[0]));
6031     SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item);
6032 }
6033
6034 static inline BOOL save_der(HANDLE file, const BYTE *pb, DWORD cb)
6035 {
6036     DWORD bytesWritten;
6037
6038     return WriteFile(file, pb, cb, &bytesWritten, NULL);
6039 }
6040
6041 static BOOL save_base64(HANDLE file, const BYTE *pb, DWORD cb)
6042 {
6043     BOOL ret;
6044     DWORD size = 0;
6045
6046     if ((ret = CryptBinaryToStringA(pb, cb, CRYPT_STRING_BASE64, NULL, &size)))
6047     {
6048         LPSTR buf = HeapAlloc(GetProcessHeap(), 0, size);
6049
6050         if (buf)
6051         {
6052             if ((ret = CryptBinaryToStringA(pb, cb, CRYPT_STRING_BASE64, buf,
6053              &size)))
6054                 ret = WriteFile(file, buf, size, &size, NULL);
6055             HeapFree(GetProcessHeap(), 0, buf);
6056         }
6057         else
6058         {
6059             SetLastError(ERROR_OUTOFMEMORY);
6060             ret = FALSE;
6061         }
6062     }
6063     return ret;
6064 }
6065
6066 static BOOL save_cms(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo,
6067  BOOL includeChain)
6068 {
6069     BOOL ret;
6070     HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
6071      CERT_STORE_CREATE_NEW_FLAG, NULL);
6072
6073     if (store)
6074     {
6075         if (includeChain)
6076         {
6077             HCERTSTORE addlStore = CertOpenStore(CERT_STORE_PROV_COLLECTION,
6078              0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
6079
6080             if (addlStore)
6081             {
6082                 DWORD i;
6083
6084                 ret = TRUE;
6085                 for (i = 0; ret && i < pExportInfo->cStores; i++)
6086                     ret = CertAddStoreToCollection(addlStore,
6087                      pExportInfo->rghStores, 0, 0);
6088                 if (ret)
6089                 {
6090                     PCCERT_CHAIN_CONTEXT chain;
6091
6092                     ret = CertGetCertificateChain(NULL,
6093                      pExportInfo->u.pCertContext, NULL, addlStore, NULL, 0,
6094                      NULL, &chain);
6095                     if (ret)
6096                     {
6097                         DWORD j;
6098
6099                         for (i = 0; ret && i < chain->cChain; i++)
6100                             for (j = 0; ret && j < chain->rgpChain[i]->cElement;
6101                              j++)
6102                                 ret = CertAddCertificateContextToStore(store,
6103                                  chain->rgpChain[i]->rgpElement[j]->pCertContext,
6104                                  CERT_STORE_ADD_ALWAYS, NULL);
6105                         CertFreeCertificateChain(chain);
6106                     }
6107                     else
6108                     {
6109                         /* No chain could be created, just add the individual
6110                          * cert to the message.
6111                          */
6112                         ret = CertAddCertificateContextToStore(store,
6113                          pExportInfo->u.pCertContext, CERT_STORE_ADD_ALWAYS,
6114                          NULL);
6115                     }
6116                 }
6117                 CertCloseStore(addlStore, 0);
6118             }
6119             else
6120                 ret = FALSE;
6121         }
6122         else
6123             ret = CertAddCertificateContextToStore(store,
6124              pExportInfo->u.pCertContext, CERT_STORE_ADD_ALWAYS, NULL);
6125         if (ret)
6126             ret = CertSaveStore(store, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
6127              CERT_STORE_SAVE_AS_PKCS7, CERT_STORE_SAVE_TO_FILE, file, 0);
6128         CertCloseStore(store, 0);
6129     }
6130     else
6131         ret = FALSE;
6132     return ret;
6133 }
6134
6135 static BOOL do_export(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo,
6136  PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO pContextInfo)
6137 {
6138     BOOL ret;
6139
6140     switch (pExportInfo->dwSubjectChoice)
6141     {
6142     case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT:
6143         ret = save_der(file,
6144          pExportInfo->u.pCRLContext->pbCrlEncoded,
6145          pExportInfo->u.pCRLContext->cbCrlEncoded);
6146         break;
6147     case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT:
6148         ret = save_der(file,
6149          pExportInfo->u.pCTLContext->pbCtlEncoded,
6150          pExportInfo->u.pCTLContext->cbCtlEncoded);
6151         break;
6152     default:
6153         switch (pContextInfo->dwExportFormat)
6154         {
6155         case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64:
6156             ret = save_base64(file,
6157              pExportInfo->u.pCertContext->pbCertEncoded,
6158              pExportInfo->u.pCertContext->cbCertEncoded);
6159             break;
6160         case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7:
6161             ret = save_cms(file, pExportInfo, pContextInfo->fExportChain);
6162             break;
6163         case CRYPTUI_WIZ_EXPORT_FORMAT_PFX:
6164             FIXME("unimplemented for PFX\n");
6165             ret = FALSE;
6166             break;
6167         default:
6168             ret = save_der(file, pExportInfo->u.pCertContext->pbCertEncoded,
6169              pExportInfo->u.pCertContext->cbCertEncoded);
6170         }
6171     }
6172     return ret;
6173 }
6174
6175 static LRESULT CALLBACK export_finish_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
6176  LPARAM lp)
6177 {
6178     LRESULT ret = 0;
6179     struct ExportWizData *data;
6180
6181     switch (msg)
6182     {
6183     case WM_INITDIALOG:
6184     {
6185         PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp;
6186         HWND lv = GetDlgItem(hwnd, IDC_EXPORT_SETTINGS);
6187         RECT rc;
6188         LVCOLUMNW column;
6189
6190         data = (struct ExportWizData *)page->lParam;
6191         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
6192         SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_TITLE), WM_SETFONT,
6193          (WPARAM)data->titleFont, TRUE);
6194         GetWindowRect(lv, &rc);
6195         column.mask = LVCF_WIDTH;
6196         column.cx = (rc.right - rc.left) / 2 - 2;
6197         SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
6198         SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
6199         show_export_details(lv, data);
6200         break;
6201     }
6202     case WM_NOTIFY:
6203     {
6204         NMHDR *hdr = (NMHDR *)lp;
6205
6206         switch (hdr->code)
6207         {
6208         case PSN_SETACTIVE:
6209         {
6210             HWND lv = GetDlgItem(hwnd, IDC_EXPORT_SETTINGS);
6211
6212             data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
6213             SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0);
6214             show_export_details(lv, data);
6215             PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0,
6216              PSWIZB_BACK | PSWIZB_FINISH);
6217             ret = TRUE;
6218             break;
6219         }
6220         case PSN_WIZFINISH:
6221         {
6222             int messageID;
6223             WCHAR title[MAX_STRING_LEN], message[MAX_STRING_LEN];
6224             LPCWSTR pTitle;
6225             DWORD mbFlags;
6226
6227             data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER);
6228             if ((data->success = do_export(data->file, data->pExportInfo,
6229              &data->contextInfo)))
6230             {
6231                 messageID = IDS_EXPORT_SUCCEEDED;
6232                 mbFlags = MB_OK;
6233             }
6234             else
6235             {
6236                 messageID = IDS_EXPORT_FAILED;
6237                 mbFlags = MB_OK | MB_ICONERROR;
6238             }
6239             if (data->pwszWizardTitle)
6240                 pTitle = data->pwszWizardTitle;
6241             else
6242             {
6243                 LoadStringW(hInstance, IDS_EXPORT_WIZARD, title,
6244                  sizeof(title) / sizeof(title[0]));
6245                 pTitle = title;
6246             }
6247             LoadStringW(hInstance, messageID, message,
6248              sizeof(message) / sizeof(message[0]));
6249             MessageBoxW(hwnd, message, pTitle, mbFlags);
6250             break;
6251         }
6252         }
6253         break;
6254     }
6255     }
6256     return ret;
6257 }
6258
6259 static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent,
6260  LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, void *pvoid)
6261 {
6262     PROPSHEETHEADERW hdr;
6263     PROPSHEETPAGEW pages[4];
6264     struct ExportWizData data;
6265     int nPages = 0;
6266     BOOL showFormatPage = TRUE;
6267
6268     data.dwFlags = dwFlags;
6269     data.pwszWizardTitle = pwszWizardTitle;
6270     data.pExportInfo = pExportInfo;
6271     if (pExportInfo->dwSubjectChoice == CRYPTUI_WIZ_EXPORT_CERT_CONTEXT &&
6272      pvoid)
6273         memcpy(&data.contextInfo, pvoid,
6274          min(((PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO)pvoid)->dwSize,
6275          sizeof(data.contextInfo)));
6276     else
6277     {
6278         data.contextInfo.dwExportFormat = 0;
6279         data.contextInfo.fExportChain = FALSE;
6280         data.contextInfo.fStrongEncryption = FALSE;
6281         data.contextInfo.fExportPrivateKeys = FALSE;
6282     }
6283     data.fileName = NULL;
6284     data.file = INVALID_HANDLE_VALUE;
6285     data.success = FALSE;
6286
6287     memset(&pages, 0, sizeof(pages));
6288
6289     pages[nPages].dwSize = sizeof(pages[0]);
6290     pages[nPages].hInstance = hInstance;
6291     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_WELCOME);
6292     pages[nPages].pfnDlgProc = export_welcome_dlg_proc;
6293     pages[nPages].dwFlags = PSP_HIDEHEADER;
6294     pages[nPages].lParam = (LPARAM)&data;
6295     nPages++;
6296
6297     switch (pExportInfo->dwSubjectChoice)
6298     {
6299     case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT:
6300     case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT:
6301         showFormatPage = FALSE;
6302         data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_DER;
6303         break;
6304     case CRYPTUI_WIZ_EXPORT_CERT_STORE:
6305         showFormatPage = FALSE;
6306         data.contextInfo.dwExportFormat =
6307          CRYPTUI_WIZ_EXPORT_FORMAT_SERIALIZED_CERT_STORE;
6308         break;
6309     case CRYPTUI_WIZ_EXPORT_CERT_STORE_CERTIFICATES_ONLY:
6310         showFormatPage = FALSE;
6311         data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7;
6312         break;
6313     }
6314     if (showFormatPage)
6315     {
6316         pages[nPages].dwSize = sizeof(pages[0]);
6317         pages[nPages].hInstance = hInstance;
6318         pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_FORMAT);
6319         pages[nPages].pfnDlgProc = export_format_dlg_proc;
6320         pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
6321         pages[nPages].pszHeaderTitle =
6322          MAKEINTRESOURCEW(IDS_EXPORT_FORMAT_TITLE);
6323         pages[nPages].pszHeaderSubTitle =
6324          MAKEINTRESOURCEW(IDS_EXPORT_FORMAT_SUBTITLE);
6325         pages[nPages].lParam = (LPARAM)&data;
6326         nPages++;
6327     }
6328
6329     pages[nPages].dwSize = sizeof(pages[0]);
6330     pages[nPages].hInstance = hInstance;
6331     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_FILE);
6332     pages[nPages].pfnDlgProc = export_file_dlg_proc;
6333     pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
6334     pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_EXPORT_FILE_TITLE);
6335     pages[nPages].pszHeaderSubTitle =
6336      MAKEINTRESOURCEW(IDS_EXPORT_FILE_SUBTITLE);
6337     pages[nPages].lParam = (LPARAM)&data;
6338     nPages++;
6339
6340     pages[nPages].dwSize = sizeof(pages[0]);
6341     pages[nPages].hInstance = hInstance;
6342     pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_FINISH);
6343     pages[nPages].pfnDlgProc = export_finish_dlg_proc;
6344     pages[nPages].dwFlags = PSP_HIDEHEADER;
6345     pages[nPages].lParam = (LPARAM)&data;
6346     nPages++;
6347
6348     memset(&hdr, 0, sizeof(hdr));
6349     hdr.dwSize = sizeof(hdr);
6350     hdr.hwndParent = hwndParent;
6351     hdr.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD97_NEW | PSH_HEADER |
6352      PSH_WATERMARK;
6353     hdr.hInstance = hInstance;
6354     if (pwszWizardTitle)
6355         hdr.pszCaption = pwszWizardTitle;
6356     else
6357         hdr.pszCaption = MAKEINTRESOURCEW(IDS_EXPORT_WIZARD);
6358     hdr.u3.ppsp = pages;
6359     hdr.nPages = nPages;
6360     hdr.u4.pszbmWatermark = MAKEINTRESOURCEW(IDB_CERT_WATERMARK);
6361     hdr.u5.pszbmHeader = MAKEINTRESOURCEW(IDB_CERT_HEADER);
6362     PropertySheetW(&hdr);
6363     DeleteObject(data.titleFont);
6364     CloseHandle(data.file);
6365     HeapFree(GetProcessHeap(), 0, data.fileName);
6366     return data.success;
6367 }
6368
6369 BOOL WINAPI CryptUIWizExport(DWORD dwFlags, HWND hwndParent,
6370  LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, void *pvoid)
6371 {
6372     BOOL ret;
6373
6374     TRACE("(%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent,
6375      debugstr_w(pwszWizardTitle), pExportInfo, pvoid);
6376
6377     if (!(dwFlags & CRYPTUI_WIZ_NO_UI))
6378         ret = show_export_ui(dwFlags, hwndParent, pwszWizardTitle, pExportInfo,
6379          pvoid);
6380     else
6381     {
6382         HANDLE file = CreateFileW(pExportInfo->pwszExportFileName,
6383          GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
6384          CREATE_ALWAYS, 0, NULL);
6385
6386         if (file != INVALID_HANDLE_VALUE)
6387         {
6388             ret = do_export(file, pExportInfo, pvoid);
6389             CloseHandle(file);
6390         }
6391         else
6392             ret = FALSE;
6393     }
6394     return ret;
6395 }