cryptui: Add (empty) edit cert properties dialog.
[wine] / dlls / cryptui / main.c
1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winuser.h"
30 #include "softpub.h"
31 #include "wingdi.h"
32 #include "richedit.h"
33 #include "ole2.h"
34 #include "richole.h"
35 #include "commctrl.h"
36 #include "cryptuiapi.h"
37 #include "cryptuires.h"
38 #include "urlmon.h"
39 #include "hlink.h"
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(cryptui);
44
45 static HINSTANCE hInstance;
46
47 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
48 {
49     TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
50
51     switch (fdwReason)
52     {
53         case DLL_WINE_PREATTACH:
54             return FALSE;    /* prefer native version */
55         case DLL_PROCESS_ATTACH:
56             hInstance = hinstDLL;
57             DisableThreadLibraryCalls(hinstDLL);
58             break;
59         case DLL_PROCESS_DETACH:
60             break;
61         default:
62             break;
63     }
64     return TRUE;
65 }
66
67 /***********************************************************************
68  *              CryptUIDlgCertMgr (CRYPTUI.@)
69  */
70 BOOL WINAPI CryptUIDlgCertMgr(PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr)
71 {
72     FIXME("(%p): stub\n", pCryptUICertMgr);
73     return FALSE;
74 }
75
76 /***********************************************************************
77  *              CryptUIDlgViewCertificateA (CRYPTUI.@)
78  */
79 BOOL WINAPI CryptUIDlgViewCertificateA(
80  PCCRYPTUI_VIEWCERTIFICATE_STRUCTA pCertViewInfo, BOOL *pfPropertiesChanged)
81 {
82     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
83     LPWSTR title = NULL;
84     BOOL ret;
85
86     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
87
88     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
89     if (pCertViewInfo->szTitle)
90     {
91         int len = MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1,
92          NULL, 0);
93
94         title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
95         if (title)
96         {
97             MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, title,
98              len);
99             viewInfo.szTitle = title;
100         }
101         else
102         {
103             ret = FALSE;
104             goto error;
105         }
106     }
107     if (pCertViewInfo->cPropSheetPages)
108     {
109         FIXME("ignoring additional prop sheet pages\n");
110         viewInfo.cPropSheetPages = 0;
111     }
112     ret = CryptUIDlgViewCertificateW(&viewInfo, pfPropertiesChanged);
113     HeapFree(GetProcessHeap(), 0, title);
114 error:
115     return ret;
116 }
117
118 struct ReadStringStruct
119 {
120     LPCWSTR buf;
121     LONG pos;
122     LONG len;
123 };
124
125 static DWORD CALLBACK read_text_callback(DWORD_PTR dwCookie, LPBYTE buf,
126  LONG cb, LONG *pcb)
127 {
128     struct ReadStringStruct *string = (struct ReadStringStruct *)dwCookie;
129     LONG cch = min(cb / sizeof(WCHAR), string->len - string->pos);
130
131     TRACE("(%p, %p, %d, %p)\n", string, buf, cb, pcb);
132
133     memmove(buf, string->buf + string->pos, cch * sizeof(WCHAR));
134     string->pos += cch;
135     *pcb = cch * sizeof(WCHAR);
136     return 0;
137 }
138
139 static void add_unformatted_text_to_control(HWND hwnd, LPCWSTR text, LONG len)
140 {
141     struct ReadStringStruct string;
142     EDITSTREAM editstream;
143
144     TRACE("(%p, %s)\n", hwnd, debugstr_wn(text, len));
145
146     string.buf = text;
147     string.pos = 0;
148     string.len = len;
149     editstream.dwCookie = (DWORD_PTR)&string;
150     editstream.dwError = 0;
151     editstream.pfnCallback = read_text_callback;
152     SendMessageW(hwnd, EM_STREAMIN, SF_TEXT | SFF_SELECTION | SF_UNICODE,
153      (LPARAM)&editstream);
154 }
155
156 static void add_string_resource_to_control(HWND hwnd, int id)
157 {
158     LPWSTR str;
159     LONG len;
160
161     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
162     add_unformatted_text_to_control(hwnd, str, len);
163 }
164
165 static void add_text_with_paraformat_to_control(HWND hwnd, LPCWSTR text,
166  LONG len, const PARAFORMAT2 *fmt)
167 {
168     add_unformatted_text_to_control(hwnd, text, len);
169     SendMessageW(hwnd, EM_SETPARAFORMAT, 0, (LPARAM)fmt);
170 }
171
172 static void add_string_resource_with_paraformat_to_control(HWND hwnd, int id,
173  const PARAFORMAT2 *fmt)
174 {
175     LPWSTR str;
176     LONG len;
177
178     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
179     add_text_with_paraformat_to_control(hwnd, str, len, fmt);
180 }
181
182 static LPWSTR get_cert_name_string(PCCERT_CONTEXT pCertContext, DWORD dwType,
183  DWORD dwFlags)
184 {
185     LPWSTR buf = NULL;
186     DWORD len;
187
188     len = CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, NULL, 0);
189     if (len)
190     {
191         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
192         if (buf)
193             CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, buf, len);
194     }
195     return buf;
196 }
197
198 static void add_cert_string_to_control(HWND hwnd, PCCERT_CONTEXT pCertContext,
199  DWORD dwType, DWORD dwFlags)
200 {
201     LPWSTR name = get_cert_name_string(pCertContext, dwType, dwFlags);
202
203     if (name)
204     {
205         /* Don't include NULL-terminator in output */
206         DWORD len = lstrlenW(name);
207
208         add_unformatted_text_to_control(hwnd, name, len);
209         HeapFree(GetProcessHeap(), 0, name);
210     }
211 }
212
213 static void add_icon_to_control(HWND hwnd, int id)
214 {
215     HRESULT hr;
216     LPRICHEDITOLE richEditOle = NULL;
217     LPOLEOBJECT object = NULL;
218     CLSID clsid;
219     LPOLECACHE oleCache = NULL;
220     FORMATETC formatEtc;
221     DWORD conn;
222     LPDATAOBJECT dataObject = NULL;
223     HBITMAP bitmap = NULL;
224     RECT rect;
225     STGMEDIUM stgm;
226     REOBJECT reObject;
227
228     TRACE("(%p, %d)\n", hwnd, id);
229
230     SendMessageW(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richEditOle);
231     if (!richEditOle)
232         goto end;
233     hr = OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject,
234      (void**)&object);
235     if (FAILED(hr))
236         goto end;
237     hr = IOleObject_GetUserClassID(object, &clsid);
238     if (FAILED(hr))
239         goto end;
240     hr = IOleObject_QueryInterface(object, &IID_IOleCache, (void**)&oleCache);
241     if (FAILED(hr))
242         goto end;
243     formatEtc.cfFormat = CF_BITMAP;
244     formatEtc.ptd = NULL;
245     formatEtc.dwAspect = DVASPECT_CONTENT;
246     formatEtc.lindex = -1;
247     formatEtc.tymed = TYMED_GDI;
248     hr = IOleCache_Cache(oleCache, &formatEtc, 0, &conn);
249     if (FAILED(hr))
250         goto end;
251     hr = IOleObject_QueryInterface(object, &IID_IDataObject,
252      (void**)&dataObject);
253     if (FAILED(hr))
254         goto end;
255     bitmap = LoadImageW(hInstance, MAKEINTRESOURCEW(id), IMAGE_BITMAP, 0, 0,
256      LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
257     if (!bitmap)
258         goto end;
259     rect.left = rect.top = 0;
260     rect.right = GetSystemMetrics(SM_CXICON);
261     rect.bottom = GetSystemMetrics(SM_CYICON);
262     stgm.tymed = TYMED_GDI;
263     stgm.u.hBitmap = bitmap;
264     stgm.pUnkForRelease = NULL;
265     hr = IDataObject_SetData(dataObject, &formatEtc, &stgm, TRUE);
266     if (FAILED(hr))
267         goto end;
268
269     reObject.cbStruct = sizeof(reObject);
270     reObject.cp = REO_CP_SELECTION;
271     reObject.clsid = clsid;
272     reObject.poleobj = object;
273     reObject.pstg = NULL;
274     reObject.polesite = NULL;
275     reObject.sizel.cx = reObject.sizel.cy = 0;
276     reObject.dvaspect = DVASPECT_CONTENT;
277     reObject.dwFlags = 0;
278     reObject.dwUser = 0;
279
280     IRichEditOle_InsertObject(richEditOle, &reObject);
281
282 end:
283     if (dataObject)
284         IDataObject_Release(dataObject);
285     if (oleCache)
286         IOleCache_Release(oleCache);
287     if (object)
288         IOleObject_Release(object);
289     if (richEditOle)
290         IRichEditOle_Release(richEditOle);
291 }
292
293 #define MY_INDENT 200
294
295 static void add_oid_text_to_control(HWND hwnd, char *oid)
296 {
297     WCHAR nl = '\n';
298     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid, 0);
299     PARAFORMAT2 parFmt;
300
301     parFmt.cbSize = sizeof(parFmt);
302     parFmt.dwMask = PFM_STARTINDENT;
303     parFmt.dxStartIndent = MY_INDENT * 3;
304     if (oidInfo)
305     {
306         add_text_with_paraformat_to_control(hwnd, oidInfo->pwszName,
307          lstrlenW(oidInfo->pwszName), &parFmt);
308         add_unformatted_text_to_control(hwnd, &nl, 1);
309     }
310 }
311
312 #define MAX_STRING_LEN 512
313
314 struct OIDToString
315 {
316     LPCSTR oid;
317     int    id;
318 };
319
320 /* The following list MUST be lexicographically sorted by OID */
321 static struct OIDToString oidMap[] = {
322  /* 1.3.6.1.4.1.311.10.3.1 */
323  { szOID_KP_CTL_USAGE_SIGNING, IDS_PURPOSE_CTL_USAGE_SIGNING },
324  /* 1.3.6.1.4.1.311.10.3.4 */
325  { szOID_KP_EFS, IDS_PURPOSE_EFS },
326  /* 1.3.6.1.4.1.311.10.3.4.1 */
327  { szOID_EFS_RECOVERY, IDS_PURPOSE_EFS_RECOVERY },
328  /* 1.3.6.1.4.1.311.10.3.5 */
329  { szOID_WHQL_CRYPTO, IDS_PURPOSE_WHQL },
330  /* 1.3.6.1.4.1.311.10.3.6 */
331  { szOID_NT5_CRYPTO, IDS_PURPOSE_NT5 },
332  /* 1.3.6.1.4.1.311.10.3.7 */
333  { szOID_OEM_WHQL_CRYPTO, IDS_PURPOSE_OEM_WHQL },
334  /* 1.3.6.1.4.1.311.10.3.8 */
335  { szOID_EMBEDDED_NT_CRYPTO, IDS_PURPOSE_EMBEDDED_NT },
336  /* 1.3.6.1.4.1.311.10.3.9 */
337  { szOID_ROOT_LIST_SIGNER, IDS_PURPOSE_ROOT_LIST_SIGNER },
338  /* 1.3.6.1.4.1.311.10.3.10 */
339  { szOID_KP_QUALIFIED_SUBORDINATION, IDS_PURPOSE_QUALIFIED_SUBORDINATION },
340  /* 1.3.6.1.4.1.311.10.3.11 */
341  { szOID_KP_KEY_RECOVERY, IDS_PURPOSE_KEY_RECOVERY },
342  /* 1.3.6.1.4.1.311.10.3.12 */
343  { szOID_KP_DOCUMENT_SIGNING, IDS_PURPOSE_DOCUMENT_SIGNING },
344  /* 1.3.6.1.4.1.311.10.3.13 */
345  { szOID_KP_LIFETIME_SIGNING, IDS_PURPOSE_LIFETIME_SIGNING },
346  /* 1.3.6.1.4.1.311.10.5.1 */
347  { szOID_DRM, IDS_PURPOSE_DRM },
348  /* 1.3.6.1.4.1.311.10.6.1 */
349  { szOID_LICENSES, IDS_PURPOSE_LICENSES },
350  /* 1.3.6.1.4.1.311.10.6.2 */
351  { szOID_LICENSE_SERVER, IDS_PURPOSE_LICENSE_SERVER },
352  /* 1.3.6.1.4.1.311.20.2.1 */
353  { szOID_ENROLLMENT_AGENT, IDS_PURPOSE_ENROLLMENT_AGENT },
354  /* 1.3.6.1.4.1.311.20.2.2 */
355  { szOID_KP_SMARTCARD_LOGON, IDS_PURPOSE_SMARTCARD_LOGON },
356  /* 1.3.6.1.4.1.311.21.5 */
357  { szOID_KP_CA_EXCHANGE, IDS_PURPOSE_CA_EXCHANGE },
358  /* 1.3.6.1.4.1.311.21.6 */
359  { szOID_KP_KEY_RECOVERY_AGENT, IDS_PURPOSE_KEY_RECOVERY_AGENT },
360  /* 1.3.6.1.4.1.311.21.19 */
361  { szOID_DS_EMAIL_REPLICATION, IDS_PURPOSE_DS_EMAIL_REPLICATION },
362  /* 1.3.6.1.5.5.7.3.1 */
363  { szOID_PKIX_KP_SERVER_AUTH, IDS_PURPOSE_SERVER_AUTH },
364  /* 1.3.6.1.5.5.7.3.2 */
365  { szOID_PKIX_KP_CLIENT_AUTH, IDS_PURPOSE_CLIENT_AUTH },
366  /* 1.3.6.1.5.5.7.3.3 */
367  { szOID_PKIX_KP_CODE_SIGNING, IDS_PURPOSE_CODE_SIGNING },
368  /* 1.3.6.1.5.5.7.3.4 */
369  { szOID_PKIX_KP_EMAIL_PROTECTION, IDS_PURPOSE_EMAIL_PROTECTION },
370  /* 1.3.6.1.5.5.7.3.5 */
371  { szOID_PKIX_KP_IPSEC_END_SYSTEM, IDS_PURPOSE_IPSEC },
372  /* 1.3.6.1.5.5.7.3.6 */
373  { szOID_PKIX_KP_IPSEC_TUNNEL, IDS_PURPOSE_IPSEC },
374  /* 1.3.6.1.5.5.7.3.7 */
375  { szOID_PKIX_KP_IPSEC_USER, IDS_PURPOSE_IPSEC },
376  /* 1.3.6.1.5.5.7.3.8 */
377  { szOID_PKIX_KP_TIMESTAMP_SIGNING, IDS_PURPOSE_TIMESTAMP_SIGNING },
378 };
379
380 static struct OIDToString *findSupportedOID(LPCSTR oid)
381 {
382     int indexHigh = sizeof(oidMap) / sizeof(oidMap[0]) - 1, indexLow = 0, i;
383     struct OIDToString *ret = NULL;
384
385     for (i = (indexLow + indexHigh) / 2; !ret && indexLow <= indexHigh;
386      i = (indexLow + indexHigh) / 2)
387     {
388         int cmp;
389
390         cmp = strcmp(oid, oidMap[i].oid);
391         if (!cmp)
392             ret = &oidMap[i];
393         else if (cmp > 0)
394             indexLow = i + 1;
395         else
396             indexHigh = i - 1;
397     }
398     return ret;
399 }
400
401 static void add_local_oid_text_to_control(HWND text, LPCSTR oid)
402 {
403     struct OIDToString *entry;
404     WCHAR nl = '\n';
405     PARAFORMAT2 parFmt;
406
407     parFmt.cbSize = sizeof(parFmt);
408     parFmt.dwMask = PFM_STARTINDENT;
409     parFmt.dxStartIndent = MY_INDENT * 3;
410     if ((entry = findSupportedOID(oid)))
411     {
412         WCHAR *str, *linebreak, *ptr;
413         BOOL multiline = FALSE;
414         int len;
415
416         len = LoadStringW(hInstance, entry->id, (LPWSTR)&str, 0);
417         ptr = str;
418         do {
419             if ((linebreak = memchrW(ptr, '\n', len)))
420             {
421                 WCHAR copy[MAX_STRING_LEN];
422
423                 multiline = TRUE;
424                 /* The source string contains a newline, which the richedit
425                  * control won't find since it's interpreted as a paragraph
426                  * break.  Therefore copy up to the newline.  lstrcpynW always
427                  * NULL-terminates, so pass one more than the length of the
428                  * source line so the copy includes the entire line and the
429                  * NULL-terminator.
430                  */
431                 lstrcpynW(copy, ptr, linebreak - ptr + 1);
432                 add_text_with_paraformat_to_control(text, copy,
433                  linebreak - ptr, &parFmt);
434                 ptr = linebreak + 1;
435                 add_unformatted_text_to_control(text, &nl, 1);
436             }
437             else if (multiline && *ptr)
438             {
439                 /* Add the last line */
440                 add_text_with_paraformat_to_control(text, ptr,
441                  len - (ptr - str), &parFmt);
442                 add_unformatted_text_to_control(text, &nl, 1);
443             }
444         } while (linebreak);
445         if (!multiline)
446         {
447             add_text_with_paraformat_to_control(text, str, len, &parFmt);
448             add_unformatted_text_to_control(text, &nl, 1);
449         }
450     }
451     else
452     {
453         WCHAR *oidW = HeapAlloc(GetProcessHeap(), 0,
454          (strlen(oid) + 1) * sizeof(WCHAR));
455
456         if (oidW)
457         {
458             LPCSTR src;
459             WCHAR *dst;
460
461             for (src = oid, dst = oidW; *src; src++, dst++)
462                 *dst = *src;
463             *dst = 0;
464             add_text_with_paraformat_to_control(text, oidW, lstrlenW(oidW),
465              &parFmt);
466             add_unformatted_text_to_control(text, &nl, 1);
467             HeapFree(GetProcessHeap(), 0, oidW);
468         }
469     }
470 }
471
472 static void display_app_usages(HWND text, PCCERT_CONTEXT cert,
473  BOOL *anyUsageAdded)
474 {
475     static char any_app_policy[] = szOID_ANY_APPLICATION_POLICY;
476     WCHAR nl = '\n';
477     CHARFORMATW charFmt;
478     PCERT_EXTENSION policyExt;
479     if (!*anyUsageAdded)
480     {
481         PARAFORMAT2 parFmt;
482
483         parFmt.cbSize = sizeof(parFmt);
484         parFmt.dwMask = PFM_STARTINDENT;
485         parFmt.dxStartIndent = MY_INDENT;
486         add_string_resource_with_paraformat_to_control(text,
487          IDS_CERT_INFO_PURPOSES, &parFmt);
488         add_unformatted_text_to_control(text, &nl, 1);
489         *anyUsageAdded = TRUE;
490     }
491     memset(&charFmt, 0, sizeof(charFmt));
492     charFmt.cbSize = sizeof(charFmt);
493     charFmt.dwMask = CFM_BOLD;
494     charFmt.dwEffects = 0;
495     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
496     if ((policyExt = CertFindExtension(szOID_APPLICATION_CERT_POLICIES,
497      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
498     {
499         CERT_POLICIES_INFO *policies;
500         DWORD size;
501
502         if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES,
503          policyExt->Value.pbData, policyExt->Value.cbData,
504          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
505         {
506             DWORD i;
507
508             for (i = 0; i < policies->cPolicyInfo; i++)
509             {
510                 DWORD j;
511
512                 for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
513                     add_local_oid_text_to_control(text,
514                      policies->rgPolicyInfo[i].rgPolicyQualifier[j].
515                      pszPolicyQualifierId);
516             }
517             LocalFree(policies);
518         }
519     }
520     else
521         add_oid_text_to_control(text, any_app_policy);
522 }
523
524 static BOOL display_cert_usages(HWND text, PCCERT_CONTEXT cert,
525  BOOL *anyUsageAdded)
526 {
527     WCHAR nl = '\n';
528     DWORD size;
529     BOOL badUsages = FALSE;
530
531     if (CertGetEnhancedKeyUsage(cert, 0, NULL, &size))
532     {
533         CHARFORMATW charFmt;
534         static char any_cert_policy[] = szOID_ANY_CERT_POLICY;
535         PCERT_ENHKEY_USAGE usage = HeapAlloc(GetProcessHeap(), 0, size);
536
537         if (usage)
538         {
539             if (CertGetEnhancedKeyUsage(cert, 0, usage, &size))
540             {
541                 DWORD i;
542
543                 if (!*anyUsageAdded)
544                 {
545                     PARAFORMAT2 parFmt;
546
547                     parFmt.cbSize = sizeof(parFmt);
548                     parFmt.dwMask = PFM_STARTINDENT;
549                     parFmt.dxStartIndent = MY_INDENT;
550                     add_string_resource_with_paraformat_to_control(text,
551                      IDS_CERT_INFO_PURPOSES, &parFmt);
552                     add_unformatted_text_to_control(text, &nl, 1);
553                     *anyUsageAdded = TRUE;
554                 }
555                 memset(&charFmt, 0, sizeof(charFmt));
556                 charFmt.cbSize = sizeof(charFmt);
557                 charFmt.dwMask = CFM_BOLD;
558                 charFmt.dwEffects = 0;
559                 SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION,
560                  (LPARAM)&charFmt);
561                 if (!usage->cUsageIdentifier)
562                     add_oid_text_to_control(text, any_cert_policy);
563                 else
564                     for (i = 0; i < usage->cUsageIdentifier; i++)
565                         add_local_oid_text_to_control(text,
566                          usage->rgpszUsageIdentifier[i]);
567             }
568             else
569                 badUsages = TRUE;
570             HeapFree(GetProcessHeap(), 0, usage);
571         }
572         else
573             badUsages = TRUE;
574     }
575     else
576         badUsages = TRUE;
577     return badUsages;
578 }
579
580 static void set_policy_text(HWND text,
581  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
582 {
583     BOOL includeCertUsages = FALSE, includeAppUsages = FALSE;
584     BOOL badUsages = FALSE, anyUsageAdded = FALSE;
585
586     if (pCertViewInfo->cPurposes)
587     {
588         DWORD i;
589
590         for (i = 0; i < pCertViewInfo->cPurposes; i++)
591         {
592             if (!strcmp(pCertViewInfo->rgszPurposes[i], szOID_ANY_CERT_POLICY))
593                 includeCertUsages = TRUE;
594             else if (!strcmp(pCertViewInfo->rgszPurposes[i],
595              szOID_ANY_APPLICATION_POLICY))
596                 includeAppUsages = TRUE;
597             else
598                 badUsages = TRUE;
599         }
600     }
601     else
602         includeAppUsages = includeCertUsages = TRUE;
603     if (includeAppUsages)
604         display_app_usages(text, pCertViewInfo->pCertContext, &anyUsageAdded);
605     if (includeCertUsages)
606         badUsages = display_cert_usages(text, pCertViewInfo->pCertContext,
607          &anyUsageAdded);
608     if (badUsages)
609     {
610         PARAFORMAT2 parFmt;
611
612         parFmt.cbSize = sizeof(parFmt);
613         parFmt.dwMask = PFM_STARTINDENT;
614         parFmt.dxStartIndent = MY_INDENT;
615         add_string_resource_with_paraformat_to_control(text,
616          IDS_CERT_INFO_BAD_PURPOSES, &parFmt);
617     }
618 }
619
620 static CRYPT_OBJID_BLOB *find_policy_qualifier(CERT_POLICIES_INFO *policies,
621  LPCSTR policyOid)
622 {
623     CRYPT_OBJID_BLOB *ret = NULL;
624     DWORD i;
625
626     for (i = 0; !ret && i < policies->cPolicyInfo; i++)
627     {
628         DWORD j;
629
630         for (j = 0; !ret && j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
631             if (!strcmp(policies->rgPolicyInfo[i].rgPolicyQualifier[j].
632              pszPolicyQualifierId, policyOid))
633                 ret = &policies->rgPolicyInfo[i].rgPolicyQualifier[j].
634                  Qualifier;
635     }
636     return ret;
637 }
638
639 static WCHAR *get_cps_str_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
640 {
641     LPWSTR qualifierStr = NULL;
642     CERT_NAME_VALUE *qualifierValue;
643     DWORD size;
644
645     if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_VALUE,
646      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
647      &qualifierValue, &size))
648     {
649         size = CertRDNValueToStrW(qualifierValue->dwValueType,
650          &qualifierValue->Value, NULL, 0);
651         qualifierStr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
652         if (qualifierStr)
653             CertRDNValueToStrW(qualifierValue->dwValueType,
654              &qualifierValue->Value, qualifierStr, size);
655         LocalFree(qualifierValue);
656     }
657     return qualifierStr;
658 }
659
660 static WCHAR *get_user_notice_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
661 {
662     LPWSTR str = NULL;
663     CERT_POLICY_QUALIFIER_USER_NOTICE *qualifierValue;
664     DWORD size;
665
666     if (CryptDecodeObjectEx(X509_ASN_ENCODING,
667      X509_PKIX_POLICY_QUALIFIER_USERNOTICE,
668      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
669      &qualifierValue, &size))
670     {
671         str = HeapAlloc(GetProcessHeap(), 0,
672          (strlenW(qualifierValue->pszDisplayText) + 1) * sizeof(WCHAR));
673         if (str)
674             strcpyW(str, qualifierValue->pszDisplayText);
675         LocalFree(qualifierValue);
676     }
677     return str;
678 }
679
680 struct IssuerStatement
681 {
682     LPWSTR cps;
683     LPWSTR userNotice;
684 };
685
686 static void set_issuer_statement(HWND hwnd,
687  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
688 {
689     PCERT_EXTENSION policyExt;
690
691     if (!(pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ISSUERSTATEMENT) &&
692      (policyExt = CertFindExtension(szOID_CERT_POLICIES,
693      pCertViewInfo->pCertContext->pCertInfo->cExtension,
694      pCertViewInfo->pCertContext->pCertInfo->rgExtension)))
695     {
696         CERT_POLICIES_INFO *policies;
697         DWORD size;
698
699         if (CryptDecodeObjectEx(X509_ASN_ENCODING, policyExt->pszObjId,
700          policyExt->Value.pbData, policyExt->Value.cbData,
701          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
702         {
703             CRYPT_OBJID_BLOB *qualifier;
704             LPWSTR cps = NULL, userNotice = NULL;
705
706             if ((qualifier = find_policy_qualifier(policies,
707              szOID_PKIX_POLICY_QUALIFIER_CPS)))
708                 cps = get_cps_str_from_qualifier(qualifier);
709             if ((qualifier = find_policy_qualifier(policies,
710              szOID_PKIX_POLICY_QUALIFIER_USERNOTICE)))
711                 userNotice = get_user_notice_from_qualifier(qualifier);
712             if (cps || userNotice)
713             {
714                 struct IssuerStatement *issuerStatement =
715                  HeapAlloc(GetProcessHeap(), 0, sizeof(struct IssuerStatement));
716
717                 if (issuerStatement)
718                 {
719                     issuerStatement->cps = cps;
720                     issuerStatement->userNotice = userNotice;
721                     EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), TRUE);
722                     SetWindowLongPtrW(hwnd, DWLP_USER,
723                      (ULONG_PTR)issuerStatement);
724                 }
725             }
726             LocalFree(policies);
727         }
728     }
729 }
730
731 static void set_cert_info(HWND hwnd,
732  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
733 {
734     CHARFORMATW charFmt;
735     PARAFORMAT2 parFmt;
736     HWND icon = GetDlgItem(hwnd, IDC_CERTIFICATE_ICON);
737     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_INFO);
738     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
739      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
740      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
741      pCertViewInfo->idxCounterSigner);
742     CRYPT_PROVIDER_CERT *root =
743      &provSigner->pasCertChain[provSigner->csCertChain - 1];
744
745     if (provSigner->pChainContext->TrustStatus.dwErrorStatus &
746      CERT_TRUST_IS_PARTIAL_CHAIN)
747         add_icon_to_control(icon, IDB_CERT_WARNING);
748     else if (!root->fTrustedRoot)
749         add_icon_to_control(icon, IDB_CERT_ERROR);
750     else
751         add_icon_to_control(icon, IDB_CERT);
752
753     memset(&charFmt, 0, sizeof(charFmt));
754     charFmt.cbSize = sizeof(charFmt);
755     charFmt.dwMask = CFM_BOLD;
756     charFmt.dwEffects = CFE_BOLD;
757     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
758     /* FIXME: vertically center text */
759     parFmt.cbSize = sizeof(parFmt);
760     parFmt.dwMask = PFM_STARTINDENT;
761     parFmt.dxStartIndent = MY_INDENT;
762     add_string_resource_with_paraformat_to_control(text,
763      IDS_CERTIFICATEINFORMATION, &parFmt);
764
765     text = GetDlgItem(hwnd, IDC_CERTIFICATE_STATUS);
766     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
767     if (provSigner->dwError == TRUST_E_CERT_SIGNATURE)
768         add_string_resource_with_paraformat_to_control(text,
769          IDS_CERT_INFO_BAD_SIG, &parFmt);
770     else if (provSigner->pChainContext->TrustStatus.dwErrorStatus &
771      CERT_TRUST_IS_PARTIAL_CHAIN)
772         add_string_resource_with_paraformat_to_control(text,
773          IDS_CERT_INFO_PARTIAL_CHAIN, &parFmt);
774     else if (!root->fTrustedRoot)
775     {
776         if (provSigner->csCertChain == 1 && root->fSelfSigned)
777             add_string_resource_with_paraformat_to_control(text,
778              IDS_CERT_INFO_UNTRUSTED_CA, &parFmt);
779         else
780             add_string_resource_with_paraformat_to_control(text,
781              IDS_CERT_INFO_UNTRUSTED_ROOT, &parFmt);
782     }
783     else
784     {
785         set_policy_text(text, pCertViewInfo);
786         set_issuer_statement(hwnd, pCertViewInfo);
787     }
788 }
789
790 static void set_cert_name_string(HWND hwnd, PCCERT_CONTEXT cert,
791  DWORD nameFlags, int heading)
792 {
793     WCHAR nl = '\n';
794     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
795     CHARFORMATW charFmt;
796     PARAFORMAT2 parFmt;
797
798     memset(&charFmt, 0, sizeof(charFmt));
799     charFmt.cbSize = sizeof(charFmt);
800     charFmt.dwMask = CFM_BOLD;
801     charFmt.dwEffects = CFE_BOLD;
802     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
803     parFmt.cbSize = sizeof(parFmt);
804     parFmt.dwMask = PFM_STARTINDENT;
805     parFmt.dxStartIndent = MY_INDENT * 3;
806     add_string_resource_with_paraformat_to_control(text, heading, &parFmt);
807     charFmt.dwEffects = 0;
808     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
809     add_cert_string_to_control(text, cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
810      nameFlags);
811     add_unformatted_text_to_control(text, &nl, 1);
812     add_unformatted_text_to_control(text, &nl, 1);
813     add_unformatted_text_to_control(text, &nl, 1);
814
815 }
816
817 static void add_date_string_to_control(HWND hwnd, const FILETIME *fileTime)
818 {
819     WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */
820     WCHAR date[80];
821     SYSTEMTIME sysTime;
822
823     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt,
824      sizeof(dateFmt) / sizeof(dateFmt[0]));
825     FileTimeToSystemTime(fileTime, &sysTime);
826     GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date,
827      sizeof(date) / sizeof(date[0]));
828     add_unformatted_text_to_control(hwnd, date, lstrlenW(date));
829 }
830
831 static void set_cert_validity_period(HWND hwnd, PCCERT_CONTEXT cert)
832 {
833     WCHAR nl = '\n';
834     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
835     CHARFORMATW charFmt;
836     PARAFORMAT2 parFmt;
837
838     memset(&charFmt, 0, sizeof(charFmt));
839     charFmt.cbSize = sizeof(charFmt);
840     charFmt.dwMask = CFM_BOLD;
841     charFmt.dwEffects = CFE_BOLD;
842     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
843     parFmt.cbSize = sizeof(parFmt);
844     parFmt.dwMask = PFM_STARTINDENT;
845     parFmt.dxStartIndent = MY_INDENT * 3;
846     add_string_resource_with_paraformat_to_control(text, IDS_VALID_FROM,
847      &parFmt);
848     charFmt.dwEffects = 0;
849     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
850     add_date_string_to_control(text, &cert->pCertInfo->NotBefore);
851     charFmt.dwEffects = CFE_BOLD;
852     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
853     add_string_resource_to_control(text, IDS_VALID_TO);
854     charFmt.dwEffects = 0;
855     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
856     add_date_string_to_control(text, &cert->pCertInfo->NotAfter);
857     add_unformatted_text_to_control(text, &nl, 1);
858 }
859
860 static void set_general_info(HWND hwnd,
861  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
862 {
863     set_cert_info(hwnd, pCertViewInfo);
864     set_cert_name_string(hwnd, pCertViewInfo->pCertContext, 0,
865      IDS_SUBJECT_HEADING);
866     set_cert_name_string(hwnd, pCertViewInfo->pCertContext,
867      CERT_NAME_ISSUER_FLAG, IDS_ISSUER_HEADING);
868     set_cert_validity_period(hwnd, pCertViewInfo->pCertContext);
869 }
870
871 static LRESULT CALLBACK user_notice_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
872  LPARAM lp)
873 {
874     LRESULT ret = 0;
875     HWND text;
876     struct IssuerStatement *issuerStatement;
877
878     switch (msg)
879     {
880     case WM_INITDIALOG:
881         text = GetDlgItem(hwnd, IDC_USERNOTICE);
882         issuerStatement = (struct IssuerStatement *)lp;
883         add_unformatted_text_to_control(text, issuerStatement->userNotice,
884          strlenW(issuerStatement->userNotice));
885         if (issuerStatement->cps)
886             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)issuerStatement->cps);
887         else
888             EnableWindow(GetDlgItem(hwnd, IDC_CPS), FALSE);
889         break;
890     case WM_COMMAND:
891         switch (wp)
892         {
893         case IDOK:
894             EndDialog(hwnd, IDOK);
895             ret = TRUE;
896             break;
897         case IDC_CPS:
898         {
899             IBindCtx *bctx = NULL;
900             LPWSTR cps;
901
902             CreateBindCtx(0, &bctx);
903             cps = (LPWSTR)GetWindowLongPtrW(hwnd, DWLP_USER);
904             HlinkSimpleNavigateToString(cps, NULL, NULL, NULL, bctx, NULL,
905              HLNF_OPENINNEWWINDOW, 0);
906             IBindCtx_Release(bctx);
907             break;
908         }
909         }
910     }
911     return ret;
912 }
913
914 static void show_user_notice(HWND hwnd, struct IssuerStatement *issuerStatement)
915 {
916     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_USERNOTICE), hwnd,
917      user_notice_dlg_proc, (LPARAM)issuerStatement);
918 }
919
920 static LRESULT CALLBACK general_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
921  LPARAM lp)
922 {
923     PROPSHEETPAGEW *page;
924     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
925
926     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
927
928     switch (msg)
929     {
930     case WM_INITDIALOG:
931         page = (PROPSHEETPAGEW *)lp;
932         pCertViewInfo = (PCCRYPTUI_VIEWCERTIFICATE_STRUCTW)page->lParam;
933         if (pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ADDTOSTORE)
934             ShowWindow(GetDlgItem(hwnd, IDC_ADDTOSTORE), FALSE);
935         EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), FALSE);
936         set_general_info(hwnd, pCertViewInfo);
937         break;
938     case WM_COMMAND:
939         switch (wp)
940         {
941         case IDC_ADDTOSTORE:
942             FIXME("call CryptUIWizImport\n");
943             break;
944         case IDC_ISSUERSTATEMENT:
945         {
946             struct IssuerStatement *issuerStatement =
947              (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
948
949             if (issuerStatement)
950             {
951                 if (issuerStatement->userNotice)
952                     show_user_notice(hwnd, issuerStatement);
953                 else if (issuerStatement->cps)
954                 {
955                     IBindCtx *bctx = NULL;
956
957                     CreateBindCtx(0, &bctx);
958                     HlinkSimpleNavigateToString(issuerStatement->cps, NULL,
959                      NULL, NULL, bctx, NULL, HLNF_OPENINNEWWINDOW, 0);
960                     IBindCtx_Release(bctx);
961                 }
962             }
963             break;
964         }
965         }
966         break;
967     }
968     return 0;
969 }
970
971 static UINT CALLBACK general_callback_proc(HWND hwnd, UINT msg,
972  PROPSHEETPAGEW *page)
973 {
974     struct IssuerStatement *issuerStatement;
975
976     switch (msg)
977     {
978     case PSPCB_RELEASE:
979         issuerStatement =
980          (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
981         if (issuerStatement)
982         {
983             HeapFree(GetProcessHeap(), 0, issuerStatement->cps);
984             HeapFree(GetProcessHeap(), 0, issuerStatement->userNotice);
985             HeapFree(GetProcessHeap(), 0, issuerStatement);
986         }
987         break;
988     }
989     return 1;
990 }
991
992 static void init_general_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
993  PROPSHEETPAGEW *page)
994 {
995     memset(page, 0, sizeof(PROPSHEETPAGEW));
996     page->dwSize = sizeof(PROPSHEETPAGEW);
997     page->dwFlags = PSP_USECALLBACK;
998     page->pfnCallback = general_callback_proc;
999     page->hInstance = hInstance;
1000     page->u.pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL);
1001     page->pfnDlgProc = general_dlg_proc;
1002     page->lParam = (LPARAM)pCertViewInfo;
1003 }
1004
1005 typedef WCHAR * (*field_format_func)(PCCERT_CONTEXT cert);
1006
1007 static WCHAR *field_format_version(PCCERT_CONTEXT cert)
1008 {
1009     static const WCHAR fmt[] = { 'V','%','d',0 };
1010     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, 12 * sizeof(WCHAR));
1011
1012     if (buf)
1013         sprintfW(buf, fmt, cert->pCertInfo->dwVersion);
1014     return buf;
1015 }
1016
1017 static WCHAR *format_hex_string(void *pb, DWORD cb)
1018 {
1019     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, (cb * 3 + 1) * sizeof(WCHAR));
1020
1021     if (buf)
1022     {
1023         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
1024         DWORD i;
1025         WCHAR *ptr;
1026
1027         for (i = 0, ptr = buf; i < cb; i++, ptr += 3)
1028             sprintfW(ptr, fmt, ((BYTE *)pb)[i]);
1029     }
1030     return buf;
1031 }
1032
1033 static WCHAR *field_format_serial_number(PCCERT_CONTEXT cert)
1034 {
1035     return format_hex_string(cert->pCertInfo->SerialNumber.pbData,
1036      cert->pCertInfo->SerialNumber.cbData);
1037 }
1038
1039 static WCHAR *field_format_issuer(PCCERT_CONTEXT cert)
1040 {
1041     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
1042      CERT_NAME_ISSUER_FLAG);
1043 }
1044
1045 static WCHAR *field_format_detailed_cert_name(PCERT_NAME_BLOB name)
1046 {
1047     WCHAR *str = NULL;
1048     DWORD len = CertNameToStrW(X509_ASN_ENCODING, name,
1049      CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, NULL, 0);
1050
1051     if (len)
1052     {
1053         str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1054         if (str)
1055             CertNameToStrW(X509_ASN_ENCODING, name,
1056              CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, str, len);
1057     }
1058     return str;
1059 }
1060
1061 static WCHAR *field_format_detailed_issuer(PCCERT_CONTEXT cert, void *param)
1062 {
1063     return field_format_detailed_cert_name(&cert->pCertInfo->Issuer);
1064 }
1065
1066 static WCHAR *field_format_subject(PCCERT_CONTEXT cert)
1067 {
1068     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
1069 }
1070
1071 static WCHAR *field_format_detailed_subject(PCCERT_CONTEXT cert, void *param)
1072 {
1073     return field_format_detailed_cert_name(&cert->pCertInfo->Subject);
1074 }
1075
1076 static WCHAR *format_long_date(const FILETIME *fileTime)
1077 {
1078     WCHAR dateFmt[80]; /* long enough for LOCALE_SLONGDATE */
1079     DWORD len;
1080     WCHAR *buf = NULL;
1081     SYSTEMTIME sysTime;
1082
1083     /* FIXME: format isn't quite right, want time too */
1084     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SLONGDATE, dateFmt,
1085      sizeof(dateFmt) / sizeof(dateFmt[0]));
1086     FileTimeToSystemTime(fileTime, &sysTime);
1087     len = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, NULL, 0);
1088     if (len)
1089     {
1090         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1091         if (buf)
1092             GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, buf,
1093              len);
1094     }
1095     return buf;
1096 }
1097
1098 static WCHAR *field_format_from_date(PCCERT_CONTEXT cert)
1099 {
1100     return format_long_date(&cert->pCertInfo->NotBefore);
1101 }
1102
1103 static WCHAR *field_format_to_date(PCCERT_CONTEXT cert)
1104 {
1105     return format_long_date(&cert->pCertInfo->NotAfter);
1106 }
1107
1108 static WCHAR *field_format_public_key(PCCERT_CONTEXT cert)
1109 {
1110     PCCRYPT_OID_INFO oidInfo;
1111     WCHAR *buf = NULL;
1112
1113     oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1114      cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 0);
1115     if (oidInfo)
1116     {
1117         WCHAR fmt[MAX_STRING_LEN];
1118
1119         if (LoadStringW(hInstance, IDS_FIELD_PUBLIC_KEY_FORMAT, fmt,
1120          sizeof(fmt) / sizeof(fmt[0])))
1121         {
1122             /* Allocate the output buffer.  Use the number of bytes in the
1123              * public key as a conservative (high) estimate for the number of
1124              * digits in its output.
1125              * The output is of the form (in English)
1126              * "<public key algorithm> (<public key bit length> bits)".
1127              * Ordinarily having two positional parameters in a string is not a
1128              * good idea, but as this isn't a sentence fragment, it shouldn't
1129              * be word-order dependent.
1130              */
1131             buf = HeapAlloc(GetProcessHeap(), 0,
1132              (strlenW(fmt) + strlenW(oidInfo->pwszName) +
1133              cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData * 8)
1134              * sizeof(WCHAR));
1135             if (buf)
1136                 sprintfW(buf, fmt, oidInfo->pwszName,
1137                  CertGetPublicKeyLength(X509_ASN_ENCODING,
1138                   &cert->pCertInfo->SubjectPublicKeyInfo));
1139         }
1140     }
1141     return buf;
1142 }
1143
1144 static WCHAR *field_format_detailed_public_key(PCCERT_CONTEXT cert, void *param)
1145 {
1146     return format_hex_string(
1147      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
1148      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData);
1149 }
1150
1151 struct field_value_data;
1152 struct detail_data
1153 {
1154     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
1155     BOOL *pfPropertiesChanged;
1156     int cFields;
1157     struct field_value_data *fields;
1158 };
1159
1160 typedef void (*add_fields_func)(HWND hwnd, struct detail_data *data);
1161
1162 typedef WCHAR *(*create_detailed_value_func)(PCCERT_CONTEXT cert, void *param);
1163
1164 struct field_value_data
1165 {
1166     create_detailed_value_func create;
1167     LPWSTR detailed_value;
1168     void *param;
1169 };
1170
1171 static void add_field_value_data(struct detail_data *data,
1172  create_detailed_value_func create, void *param)
1173 {
1174     if (data->cFields)
1175         data->fields = HeapReAlloc(GetProcessHeap(), 0, data->fields,
1176          (data->cFields + 1) * sizeof(struct field_value_data));
1177     else
1178         data->fields = HeapAlloc(GetProcessHeap(), 0,
1179          sizeof(struct field_value_data));
1180     if (data->fields)
1181     {
1182         data->fields[data->cFields].create = create;
1183         data->fields[data->cFields].detailed_value = NULL;
1184         data->fields[data->cFields].param = param;
1185         data->cFields++;
1186     }
1187 }
1188
1189 static void add_field_and_value_to_list(HWND hwnd, struct detail_data *data,
1190  LPWSTR field, LPWSTR value, create_detailed_value_func create, void *param)
1191 {
1192     LVITEMW item;
1193     int iItem = SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0);
1194
1195     item.mask = LVIF_TEXT | LVIF_PARAM;
1196     item.iItem = iItem;
1197     item.iSubItem = 0;
1198     item.pszText = field;
1199     item.lParam = (LPARAM)data;
1200     SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item);
1201     if (value)
1202     {
1203         item.pszText = value;
1204         item.iSubItem = 1;
1205         SendMessageW(hwnd, LVM_SETITEMTEXTW, iItem, (LPARAM)&item);
1206     }
1207     add_field_value_data(data, create, param);
1208 }
1209
1210 static void add_string_id_and_value_to_list(HWND hwnd, struct detail_data *data,
1211  int id, LPWSTR value, create_detailed_value_func create, void *param)
1212 {
1213     WCHAR buf[MAX_STRING_LEN];
1214
1215     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
1216     add_field_and_value_to_list(hwnd, data, buf, value, create, param);
1217 }
1218
1219 struct v1_field
1220 {
1221     int id;
1222     field_format_func format;
1223     create_detailed_value_func create_detailed_value;
1224 };
1225
1226 static void add_v1_field(HWND hwnd, struct detail_data *data,
1227  const struct v1_field *field)
1228 {
1229     WCHAR *val = field->format(data->pCertViewInfo->pCertContext);
1230
1231     if (val)
1232     {
1233         add_string_id_and_value_to_list(hwnd, data, field->id, val,
1234          field->create_detailed_value, NULL);
1235         HeapFree(GetProcessHeap(), 0, val);
1236     }
1237 }
1238
1239 static const struct v1_field v1_fields[] = {
1240  { IDS_FIELD_VERSION, field_format_version, NULL },
1241  { IDS_FIELD_SERIAL_NUMBER, field_format_serial_number, NULL },
1242  { IDS_FIELD_ISSUER, field_format_issuer, field_format_detailed_issuer },
1243  { IDS_FIELD_VALID_FROM, field_format_from_date, NULL },
1244  { IDS_FIELD_VALID_TO, field_format_to_date, NULL },
1245  { IDS_FIELD_SUBJECT, field_format_subject, field_format_detailed_subject },
1246  { IDS_FIELD_PUBLIC_KEY, field_format_public_key,
1247    field_format_detailed_public_key }
1248 };
1249
1250 static void add_v1_fields(HWND hwnd, struct detail_data *data)
1251 {
1252     int i;
1253     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1254
1255     /* The last item in v1_fields is the public key, which is not in the loop
1256      * because it's a special case.
1257      */
1258     for (i = 0; i < sizeof(v1_fields) / sizeof(v1_fields[0]) - 1; i++)
1259         add_v1_field(hwnd, data, &v1_fields[i]);
1260     if (cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData)
1261         add_v1_field(hwnd, data, &v1_fields[i]);
1262 }
1263
1264 static WCHAR *crypt_format_extension(PCERT_EXTENSION ext, DWORD formatStrType)
1265 {
1266     WCHAR *str = NULL;
1267     DWORD size;
1268
1269     if (CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
1270      ext->pszObjId, ext->Value.pbData, ext->Value.cbData, NULL, &size))
1271     {
1272         str = HeapAlloc(GetProcessHeap(), 0, size);
1273         CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
1274          ext->pszObjId, ext->Value.pbData, ext->Value.cbData, str, &size);
1275     }
1276     return str;
1277 }
1278
1279 static WCHAR *field_format_extension_hex_with_ascii(PCERT_EXTENSION ext)
1280 {
1281     WCHAR *str = NULL;
1282
1283     if (ext->Value.cbData)
1284     {
1285         /* The output is formatted as:
1286          * <hex bytes>  <ascii bytes>\n
1287          * where <hex bytes> is a string of up to 8 bytes, output as %02x,
1288          * and <ascii bytes> is the ASCII equivalent of each byte, or '.' if
1289          * the byte is not printable.
1290          * So, for example, the extension value consisting of the following
1291          * bytes:
1292          *   0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
1293          *   0x13,0x09,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67
1294          * is output as:
1295          *   30 14 31 12 30 10 06 03  0.1.0...
1296          *   55 04 03 13 09 4a 75 61  U....Jua
1297          *   6e 20 4c 61 6e 67        n Lang
1298          * The allocation size therefore requires:
1299          * - 4 characters per character in an 8-byte line
1300          *   (2 for the hex format, one for the space, one for the ASCII value)
1301          * - 3 more characters per 8-byte line (two spaces and a newline)
1302          * - 1 character for the terminating nul
1303          * FIXME: should use a fixed-width font for this
1304          */
1305         DWORD lines = (ext->Value.cbData + 7) / 8;
1306
1307         str = HeapAlloc(GetProcessHeap(), 0,
1308          (lines * 8 * 4 + lines * 3 + 1) * sizeof(WCHAR));
1309         if (str)
1310         {
1311             static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
1312             DWORD i, j;
1313             WCHAR *ptr;
1314
1315             for (i = 0, ptr = str; i < ext->Value.cbData; i += 8)
1316             {
1317                 /* Output as hex bytes first */
1318                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr += 3)
1319                     sprintfW(ptr, fmt, ext->Value.pbData[j]);
1320                 /* Pad the hex output with spaces for alignment */
1321                 if (j == ext->Value.cbData && j % 8)
1322                 {
1323                     static const WCHAR pad[] = { ' ',' ',' ' };
1324
1325                     for (; j % 8; j++, ptr += sizeof(pad) / sizeof(pad[0]))
1326                         memcpy(ptr, pad, sizeof(pad));
1327                 }
1328                 /* The last sprintfW included a space, so just insert one
1329                  * more space between the hex bytes and the ASCII output
1330                  */
1331                 *ptr++ = ' ';
1332                 /* Output as ASCII bytes */
1333                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr++)
1334                 {
1335                     if (isprintW(ext->Value.pbData[j]) &&
1336                      !isspaceW(ext->Value.pbData[j]))
1337                         *ptr = ext->Value.pbData[j];
1338                     else
1339                         *ptr = '.';
1340                 }
1341                 *ptr++ = '\n';
1342             }
1343             *ptr++ = '\0';
1344         }
1345     }
1346     return str;
1347 }
1348
1349 static WCHAR *field_format_detailed_extension(PCCERT_CONTEXT cert, void *param)
1350 {
1351     PCERT_EXTENSION ext = param;
1352     LPWSTR str = crypt_format_extension(ext,
1353      CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX);
1354
1355     if (!str)
1356         str = field_format_extension_hex_with_ascii(ext);
1357     return str;
1358 }
1359
1360 static void add_cert_extension_detail(HWND hwnd, struct detail_data *data,
1361  PCERT_EXTENSION ext)
1362 {
1363     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1364      ext->pszObjId, 0);
1365     LPWSTR val = crypt_format_extension(ext, 0);
1366
1367     if (oidInfo)
1368         add_field_and_value_to_list(hwnd, data, (LPWSTR)oidInfo->pwszName,
1369          val, field_format_detailed_extension, ext);
1370     else
1371     {
1372         DWORD len = strlen(ext->pszObjId);
1373         LPWSTR oidW = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1374
1375         if (oidW)
1376         {
1377             DWORD i;
1378
1379             for (i = 0; i <= len; i++)
1380                 oidW[i] = ext->pszObjId[i];
1381             add_field_and_value_to_list(hwnd, data, oidW, val,
1382              field_format_detailed_extension, ext);
1383             HeapFree(GetProcessHeap(), 0, oidW);
1384         }
1385     }
1386     HeapFree(GetProcessHeap(), 0, val);
1387 }
1388
1389 static void add_all_extensions(HWND hwnd, struct detail_data *data)
1390 {
1391     DWORD i;
1392     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1393
1394     for (i = 0; i < cert->pCertInfo->cExtension; i++)
1395         add_cert_extension_detail(hwnd, data, &cert->pCertInfo->rgExtension[i]);
1396 }
1397
1398 static void add_critical_extensions(HWND hwnd, struct detail_data *data)
1399 {
1400     DWORD i;
1401     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1402
1403     for (i = 0; i < cert->pCertInfo->cExtension; i++)
1404         if (cert->pCertInfo->rgExtension[i].fCritical)
1405             add_cert_extension_detail(hwnd, data,
1406              &cert->pCertInfo->rgExtension[i]);
1407 }
1408
1409 typedef WCHAR * (*prop_to_value_func)(void *pb, DWORD cb);
1410
1411 struct prop_id_to_string_id
1412 {
1413     DWORD prop;
1414     int id;
1415     BOOL prop_is_string;
1416     prop_to_value_func prop_to_value;
1417 };
1418
1419 static WCHAR *format_enhanced_key_usage_value(void *pb, DWORD cb)
1420 {
1421     CERT_EXTENSION ext;
1422
1423     ext.pszObjId = (LPSTR)X509_ENHANCED_KEY_USAGE;
1424     ext.fCritical = FALSE;
1425     ext.Value.pbData = pb;
1426     ext.Value.cbData = cb;
1427     return crypt_format_extension(&ext, 0);
1428 }
1429
1430 /* Logically the access state should also be checked, and IDC_EDITPROPERTIES
1431  * disabled for read-only certificates, but native doesn't appear to do that.
1432  */
1433 static const struct prop_id_to_string_id prop_id_map[] = {
1434  { CERT_HASH_PROP_ID, IDS_PROP_HASH, FALSE, format_hex_string },
1435  { CERT_FRIENDLY_NAME_PROP_ID, IDS_PROP_FRIENDLY_NAME, TRUE, NULL },
1436  { CERT_DESCRIPTION_PROP_ID, IDS_PROP_DESCRIPTION, TRUE, NULL },
1437  { CERT_ENHKEY_USAGE_PROP_ID, IDS_PROP_ENHKEY_USAGE, FALSE,
1438    format_enhanced_key_usage_value },
1439 };
1440
1441 static void add_properties(HWND hwnd, struct detail_data *data)
1442 {
1443     DWORD i;
1444     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1445
1446     for (i = 0; i < sizeof(prop_id_map) / sizeof(prop_id_map[0]); i++)
1447     {
1448         DWORD cb;
1449
1450         if (CertGetCertificateContextProperty(cert, prop_id_map[i].prop, NULL,
1451          &cb))
1452         {
1453             BYTE *pb;
1454             WCHAR *val = NULL;
1455
1456             /* FIXME: MS adds a separate value for the signature hash
1457              * algorithm.
1458              */
1459             pb = HeapAlloc(GetProcessHeap(), 0, cb);
1460             if (pb)
1461             {
1462                 if (CertGetCertificateContextProperty(cert,
1463                  prop_id_map[i].prop, pb, &cb))
1464                 {
1465                     if (prop_id_map[i].prop_is_string)
1466                     {
1467                         val = (LPWSTR)pb;
1468                         /* Don't double-free pb */
1469                         pb = NULL;
1470                     }
1471                     else
1472                         val = prop_id_map[i].prop_to_value(pb, cb);
1473                 }
1474                 HeapFree(GetProcessHeap(), 0, pb);
1475             }
1476             add_string_id_and_value_to_list(hwnd, data, prop_id_map[i].id, val,
1477              NULL, NULL);
1478         }
1479     }
1480 }
1481
1482 static void add_all_fields(HWND hwnd, struct detail_data *data)
1483 {
1484     add_v1_fields(hwnd, data);
1485     add_all_extensions(hwnd, data);
1486     add_properties(hwnd, data);
1487 }
1488
1489 struct selection_list_item
1490 {
1491     int id;
1492     add_fields_func add;
1493 };
1494
1495 const struct selection_list_item listItems[] = {
1496  { IDS_FIELDS_ALL, add_all_fields },
1497  { IDS_FIELDS_V1, add_v1_fields },
1498  { IDS_FIELDS_EXTENSIONS, add_all_extensions },
1499  { IDS_FIELDS_CRITICAL_EXTENSIONS, add_critical_extensions },
1500  { IDS_FIELDS_PROPERTIES, add_properties },
1501 };
1502
1503 static void create_show_list(HWND hwnd, struct detail_data *data)
1504 {
1505     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
1506     WCHAR buf[MAX_STRING_LEN];
1507     int i;
1508
1509     for (i = 0; i < sizeof(listItems) / sizeof(listItems[0]); i++)
1510     {
1511         int index;
1512
1513         LoadStringW(hInstance, listItems[i].id, buf,
1514          sizeof(buf) / sizeof(buf[0]));
1515         index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
1516         SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)data);
1517     }
1518     SendMessageW(cb, CB_SETCURSEL, 0, 0);
1519 }
1520
1521 static void create_listview_columns(HWND hwnd)
1522 {
1523     HWND lv = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1524     RECT rc;
1525     WCHAR buf[MAX_STRING_LEN];
1526     LVCOLUMNW column;
1527
1528     SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
1529     GetWindowRect(lv, &rc);
1530     LoadStringW(hInstance, IDS_FIELD, buf, sizeof(buf) / sizeof(buf[0]));
1531     column.mask = LVCF_WIDTH | LVCF_TEXT;
1532     column.cx = (rc.right - rc.left) / 2 - 2;
1533     column.pszText = buf;
1534     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
1535     LoadStringW(hInstance, IDS_VALUE, buf, sizeof(buf) / sizeof(buf[0]));
1536     SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
1537 }
1538
1539 static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel)
1540 {
1541     HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1542
1543     if (sel >= 0 && sel < sizeof(listItems) / sizeof(listItems[0]))
1544     {
1545         SendMessageW(list, LVM_DELETEALLITEMS, 0, 0);
1546         listItems[sel].add(list, data);
1547     }
1548 }
1549
1550 static void create_cert_details_list(HWND hwnd, struct detail_data *data)
1551 {
1552     create_show_list(hwnd, data);
1553     create_listview_columns(hwnd);
1554     set_fields_selection(hwnd, data, 0);
1555 }
1556
1557 #define MAX_FRIENDLY_NAME 40
1558 #define MAX_DESCRIPTION 255
1559
1560 static LRESULT CALLBACK cert_properties_general_dlg_proc(HWND hwnd, UINT msg,
1561  WPARAM wp, LPARAM lp)
1562 {
1563     PROPSHEETPAGEW *page;
1564     struct detail_data *data;
1565
1566     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
1567
1568     switch (msg)
1569     {
1570     case WM_INITDIALOG:
1571     {
1572         HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
1573
1574         page = (PROPSHEETPAGEW *)lp;
1575         data = (struct detail_data *)page->lParam;
1576         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), EM_SETLIMITTEXT,
1577          MAX_FRIENDLY_NAME, 0);
1578         SendMessageW(description, EM_SETLIMITTEXT, MAX_DESCRIPTION, 0);
1579         ShowScrollBar(description, SB_VERT, FALSE);
1580         FIXME("set general properties\n");
1581         SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data);
1582         break;
1583     }
1584     case WM_COMMAND:
1585         switch (HIWORD(wp))
1586         {
1587         case BN_CLICKED:
1588             switch (LOWORD(wp))
1589             {
1590             case IDC_ADD_PURPOSE:
1591                 FIXME("show add purpose dialog\n");
1592                 break;
1593             }
1594             break;
1595         }
1596         break;
1597     }
1598     return 0;
1599 }
1600
1601 static void show_edit_cert_properties_dialog(HWND parent,
1602  struct detail_data *data)
1603 {
1604     PROPSHEETHEADERW hdr;
1605     PROPSHEETPAGEW page; /* FIXME: need to add a cross-certificate page */
1606
1607     TRACE("(%p)\n", data);
1608
1609     memset(&page, 0, sizeof(PROPSHEETPAGEW));
1610     page.dwSize = sizeof(page);
1611     page.hInstance = hInstance;
1612     page.u.pszTemplate = MAKEINTRESOURCEW(IDD_CERT_PROPERTIES_GENERAL);
1613     page.pfnDlgProc = cert_properties_general_dlg_proc;
1614     page.lParam = (LPARAM)data;
1615
1616     memset(&hdr, 0, sizeof(hdr));
1617     hdr.dwSize = sizeof(hdr);
1618     hdr.hwndParent = parent;
1619     hdr.dwFlags = PSH_PROPSHEETPAGE;
1620     hdr.hInstance = hInstance;
1621     hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE_PROPERTIES);
1622     hdr.u3.ppsp = &page;
1623     hdr.nPages = 1;
1624     PropertySheetW(&hdr);
1625 }
1626
1627 static void free_detail_fields(struct detail_data *data)
1628 {
1629     DWORD i;
1630
1631     for (i = 0; i < data->cFields; i++)
1632         HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value);
1633     HeapFree(GetProcessHeap(), 0, data->fields);
1634     data->fields = NULL;
1635     data->cFields = 0;
1636 }
1637
1638 static void refresh_details_view(HWND hwnd)
1639 {
1640     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
1641     int curSel;
1642     struct detail_data *data;
1643
1644     curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
1645     /* Actually, any index will do, since they all store the same data value */
1646     data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0);
1647     free_detail_fields(data);
1648     set_fields_selection(hwnd, data, curSel);
1649 }
1650
1651 static LRESULT CALLBACK detail_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1652  LPARAM lp)
1653 {
1654     PROPSHEETPAGEW *page;
1655     struct detail_data *data;
1656
1657     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
1658
1659     switch (msg)
1660     {
1661     case WM_INITDIALOG:
1662         page = (PROPSHEETPAGEW *)lp;
1663         data = (struct detail_data *)page->lParam;
1664         create_cert_details_list(hwnd, data);
1665         if (!(data->pCertViewInfo->dwFlags & CRYPTUI_ENABLE_EDITPROPERTIES))
1666             EnableWindow(GetDlgItem(hwnd, IDC_EDITPROPERTIES), FALSE);
1667         if (data->pCertViewInfo->dwFlags & CRYPTUI_DISABLE_EXPORT)
1668             EnableWindow(GetDlgItem(hwnd, IDC_EXPORT), FALSE);
1669         break;
1670     case WM_NOTIFY:
1671     {
1672         NMITEMACTIVATE *nm;
1673         HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1674
1675         nm = (NMITEMACTIVATE*)lp;
1676         if (nm->hdr.hwndFrom == list && nm->uNewState & LVN_ITEMACTIVATE
1677          && nm->hdr.code == LVN_ITEMCHANGED)
1678         {
1679             data = (struct detail_data *)nm->lParam;
1680             if (nm->iItem >= 0 && data && nm->iItem < data->cFields)
1681             {
1682                 WCHAR buf[MAX_STRING_LEN], *val = NULL;
1683                 HWND valueCtl = GetDlgItem(hwnd, IDC_DETAIL_VALUE);
1684
1685                 if (data->fields[nm->iItem].create)
1686                     val = data->fields[nm->iItem].create(
1687                      data->pCertViewInfo->pCertContext,
1688                      data->fields[nm->iItem].param);
1689                 else
1690                 {
1691                     LVITEMW item;
1692                     int res;
1693
1694                     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
1695                     item.mask = LVIF_TEXT;
1696                     item.pszText = buf;
1697                     item.iItem = nm->iItem;
1698                     item.iSubItem = 1;
1699                     res = SendMessageW(list, LVM_GETITEMW, 0, (LPARAM)&item);
1700                     if (res)
1701                         val = buf;
1702                 }
1703                 /* Select all the text in the control, the next update will
1704                  * replace it
1705                  */
1706                 SendMessageW(valueCtl, EM_SETSEL, 0, -1);
1707                 add_unformatted_text_to_control(valueCtl, val,
1708                  val ? strlenW(val) : 0);
1709                 if (val != buf)
1710                     HeapFree(GetProcessHeap(), 0, val);
1711             }
1712         }
1713         break;
1714     }
1715     case WM_COMMAND:
1716         switch (wp)
1717         {
1718         case IDC_EXPORT:
1719             FIXME("call CryptUIWizExport\n");
1720             break;
1721         case IDC_EDITPROPERTIES:
1722         {
1723             HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
1724             int curSel;
1725
1726             curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
1727             /* Actually, any index will do, since they all store the same
1728              * data value
1729              */
1730             data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA,
1731              curSel, 0);
1732             show_edit_cert_properties_dialog(GetParent(hwnd), data);
1733             break;
1734         }
1735         case ((CBN_SELCHANGE << 16) | IDC_DETAIL_SELECT):
1736             refresh_details_view(hwnd);
1737             break;
1738         }
1739         break;
1740     }
1741     return 0;
1742 }
1743
1744 static BOOL init_detail_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
1745  BOOL *pfPropertiesChanged, PROPSHEETPAGEW *page)
1746 {
1747     BOOL ret;
1748     struct detail_data *data = HeapAlloc(GetProcessHeap(), 0,
1749      sizeof(struct detail_data));
1750
1751     if (data)
1752     {
1753         data->pCertViewInfo = pCertViewInfo;
1754         data->pfPropertiesChanged = pfPropertiesChanged;
1755         data->cFields = 0;
1756         data->fields = NULL;
1757         memset(page, 0, sizeof(PROPSHEETPAGEW));
1758         page->dwSize = sizeof(PROPSHEETPAGEW);
1759         page->hInstance = hInstance;
1760         page->u.pszTemplate = MAKEINTRESOURCEW(IDD_DETAIL);
1761         page->pfnDlgProc = detail_dlg_proc;
1762         page->lParam = (LPARAM)data;
1763         ret = TRUE;
1764     }
1765     else
1766         ret = FALSE;
1767     return ret;
1768 }
1769
1770 struct hierarchy_data
1771 {
1772     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
1773     HIMAGELIST imageList;
1774     DWORD selectedCert;
1775 };
1776
1777 static LPARAM index_to_lparam(struct hierarchy_data *data, DWORD index)
1778 {
1779     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
1780      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
1781      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
1782      data->pCertViewInfo->idxCounterSigner);
1783
1784     /* Takes advantage of the fact that a pointer is 32-bit aligned, and
1785      * therefore always even.
1786      */
1787     if (index == provSigner->csCertChain - 1)
1788         return (LPARAM)data;
1789     return index << 1 | 1;
1790 }
1791
1792 static inline DWORD lparam_to_index(struct hierarchy_data *data, LPARAM lp)
1793 {
1794     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
1795      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
1796      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
1797      data->pCertViewInfo->idxCounterSigner);
1798
1799     if (!(lp & 1))
1800         return provSigner->csCertChain - 1;
1801     return lp >> 1;
1802 }
1803
1804 static struct hierarchy_data *get_hierarchy_data_from_tree_item(HWND tree,
1805  HTREEITEM hItem)
1806 {
1807     struct hierarchy_data *data = NULL;
1808     HTREEITEM root = NULL;
1809
1810     do {
1811         HTREEITEM parent = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM,
1812          TVGN_PARENT, (LPARAM)hItem);
1813
1814         if (!parent)
1815             root = hItem;
1816         hItem = parent;
1817     } while (hItem);
1818     if (root)
1819     {
1820         TVITEMW item;
1821
1822         item.mask = TVIF_PARAM;
1823         item.hItem = root;
1824         SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
1825         data = (struct hierarchy_data *)item.lParam;
1826     }
1827     return data;
1828 }
1829
1830 static WCHAR *get_cert_property_as_string(PCCERT_CONTEXT cert, DWORD prop)
1831 {
1832     WCHAR *name = NULL;
1833     DWORD cb;
1834
1835     if (CertGetCertificateContextProperty(cert, prop, NULL, &cb))
1836     {
1837         name = HeapAlloc(GetProcessHeap(), 0, cb);
1838         if (name)
1839         {
1840             if (!CertGetCertificateContextProperty(cert, prop, (LPBYTE)name,
1841              &cb))
1842             {
1843                 HeapFree(GetProcessHeap(), 0, name);
1844                 name = NULL;
1845             }
1846         }
1847     }
1848     return name;
1849 }
1850
1851 static WCHAR *get_cert_display_name(PCCERT_CONTEXT cert)
1852 {
1853     WCHAR *name = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID);
1854
1855     if (!name)
1856         name = get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
1857     return name;
1858 }
1859
1860 static void show_cert_chain(HWND hwnd, struct hierarchy_data *data)
1861 {
1862     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
1863     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
1864      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
1865      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
1866      data->pCertViewInfo->idxCounterSigner);
1867     DWORD i;
1868     HTREEITEM parent = NULL;
1869
1870     SendMessageW(tree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)data->imageList);
1871     for (i = provSigner->csCertChain; i; i--)
1872     {
1873         LPWSTR name;
1874
1875         name = get_cert_display_name(provSigner->pasCertChain[i - 1].pCert);
1876         if (name)
1877         {
1878             TVINSERTSTRUCTW tvis;
1879
1880             tvis.hParent = parent;
1881             tvis.hInsertAfter = TVI_LAST;
1882             tvis.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE |
1883              TVIF_SELECTEDIMAGE | TVIF_PARAM;
1884             tvis.u.item.pszText = name;
1885             tvis.u.item.state = TVIS_EXPANDED;
1886             tvis.u.item.stateMask = TVIS_EXPANDED;
1887             if (i == 1 &&
1888              (provSigner->pChainContext->TrustStatus.dwErrorStatus &
1889              CERT_TRUST_IS_PARTIAL_CHAIN))
1890             {
1891                 /* The root of the chain has a special case:  if the chain is
1892                  * a partial chain, the icon is a warning icon rather than an
1893                  * error icon.
1894                  */
1895                 tvis.u.item.iImage = 2;
1896             }
1897             else if (provSigner->pasCertChain[i - 1].pChainElement->TrustStatus.
1898              dwErrorStatus == 0)
1899                 tvis.u.item.iImage = 0;
1900             else
1901                 tvis.u.item.iImage = 1;
1902             tvis.u.item.iSelectedImage = tvis.u.item.iImage;
1903             tvis.u.item.lParam = index_to_lparam(data, i - 1);
1904             parent = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0,
1905              (LPARAM)&tvis);
1906             HeapFree(GetProcessHeap(), 0, name);
1907         }
1908     }
1909 }
1910
1911 static void set_certificate_status(HWND hwnd, CRYPT_PROVIDER_CERT *cert)
1912 {
1913     /* Select all the text in the control, the next update will replace it */
1914     SendMessageW(hwnd, EM_SETSEL, 0, -1);
1915     /* Set the highest priority error messages first. */
1916     if (!(cert->dwConfidence & CERT_CONFIDENCE_SIG))
1917         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_SIGNATURE);
1918     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIME))
1919         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIME);
1920     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIMENEST))
1921         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIMENEST);
1922     else if (cert->dwRevokedReason)
1923         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_REVOKED);
1924     else
1925         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_VALID);
1926 }
1927
1928 static void set_certificate_status_for_end_cert(HWND hwnd,
1929  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
1930 {
1931     HWND status = GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT);
1932     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
1933      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
1934      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
1935      pCertViewInfo->idxCounterSigner);
1936     CRYPT_PROVIDER_CERT *provCert = WTHelperGetProvCertFromChain(provSigner,
1937      pCertViewInfo->idxCert);
1938
1939     set_certificate_status(status, provCert);
1940 }
1941
1942 static void show_cert_hierarchy(HWND hwnd, struct hierarchy_data *data)
1943 {
1944     /* Disable view certificate button until a certificate is selected */
1945     EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), FALSE);
1946     show_cert_chain(hwnd, data);
1947     set_certificate_status_for_end_cert(hwnd, data->pCertViewInfo);
1948 }
1949
1950 static void show_dialog_for_selected_cert(HWND hwnd)
1951 {
1952     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
1953     TVITEMW item;
1954     struct hierarchy_data *data;
1955     DWORD selection;
1956
1957     memset(&item, 0, sizeof(item));
1958     item.mask = TVIF_HANDLE | TVIF_PARAM;
1959     item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CARET,
1960      (LPARAM)NULL);
1961     SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
1962     data = get_hierarchy_data_from_tree_item(tree, item.hItem);
1963     selection = lparam_to_index(data, item.lParam);
1964     if (selection != 0)
1965     {
1966         CRYPT_PROVIDER_SGNR *provSigner;
1967         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
1968         BOOL changed = FALSE;
1969
1970         provSigner = WTHelperGetProvSignerFromChain(
1971          (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
1972          data->pCertViewInfo->idxSigner,
1973          data->pCertViewInfo->fCounterSigner,
1974          data->pCertViewInfo->idxCounterSigner);
1975         memset(&viewInfo, 0, sizeof(viewInfo));
1976         viewInfo.dwSize = sizeof(viewInfo);
1977         viewInfo.dwFlags = data->pCertViewInfo->dwFlags;
1978         viewInfo.szTitle = data->pCertViewInfo->szTitle;
1979         viewInfo.pCertContext = provSigner->pasCertChain[selection].pCert;
1980         viewInfo.cStores = data->pCertViewInfo->cStores;
1981         viewInfo.rghStores = data->pCertViewInfo->rghStores;
1982         viewInfo.cPropSheetPages = data->pCertViewInfo->cPropSheetPages;
1983         viewInfo.rgPropSheetPages = data->pCertViewInfo->rgPropSheetPages;
1984         viewInfo.nStartPage = data->pCertViewInfo->nStartPage;
1985         CryptUIDlgViewCertificateW(&viewInfo, &changed);
1986         if (changed)
1987         {
1988             /* Delete the contents of the tree */
1989             SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
1990             /* Reinitialize the tree */
1991             show_cert_hierarchy(hwnd, data);
1992         }
1993     }
1994 }
1995
1996 static LRESULT CALLBACK hierarchy_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1997  LPARAM lp)
1998 {
1999     PROPSHEETPAGEW *page;
2000     struct hierarchy_data *data;
2001     LRESULT ret = 0;
2002
2003     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2004
2005     switch (msg)
2006     {
2007     case WM_INITDIALOG:
2008         page = (PROPSHEETPAGEW *)lp;
2009         data = (struct hierarchy_data *)page->lParam;
2010         show_cert_hierarchy(hwnd, data);
2011         break;
2012     case WM_NOTIFY:
2013     {
2014         NMHDR *hdr;
2015
2016         hdr = (NMHDR *)lp;
2017         switch (hdr->code)
2018         {
2019         case TVN_SELCHANGEDW:
2020         {
2021             NMTREEVIEWW *nm = (NMTREEVIEWW*)lp;
2022             HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
2023             DWORD selection;
2024             CRYPT_PROVIDER_SGNR *provSigner;
2025
2026             data = get_hierarchy_data_from_tree_item(tree, nm->itemNew.hItem);
2027             selection = lparam_to_index(data, nm->itemNew.lParam);
2028             provSigner = WTHelperGetProvSignerFromChain(
2029              (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
2030              data->pCertViewInfo->idxSigner,
2031              data->pCertViewInfo->fCounterSigner,
2032              data->pCertViewInfo->idxCounterSigner);
2033             EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), selection != 0);
2034             set_certificate_status(GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT),
2035              &provSigner->pasCertChain[selection]);
2036             break;
2037         }
2038         case NM_DBLCLK:
2039             show_dialog_for_selected_cert(hwnd);
2040             SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
2041             ret = 1;
2042             break;
2043         }
2044         break;
2045     }
2046     case WM_COMMAND:
2047         switch (wp)
2048         {
2049         case IDC_VIEWCERTIFICATE:
2050             show_dialog_for_selected_cert(hwnd);
2051             break;
2052         }
2053         break;
2054     }
2055     return ret;
2056 }
2057
2058 static UINT CALLBACK hierarchy_callback(HWND hwnd, UINT msg,
2059  PROPSHEETPAGEW *page)
2060 {
2061     struct hierarchy_data *data;
2062
2063     switch (msg)
2064     {
2065     case PSPCB_RELEASE:
2066         data = (struct hierarchy_data *)page->lParam;
2067         ImageList_Destroy(data->imageList);
2068         HeapFree(GetProcessHeap(), 0, data);
2069         break;
2070     }
2071     return 0;
2072 }
2073
2074 static BOOL init_hierarchy_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2075  PROPSHEETPAGEW *page)
2076 {
2077     struct hierarchy_data *data = HeapAlloc(GetProcessHeap(), 0,
2078      sizeof(struct hierarchy_data));
2079     BOOL ret = FALSE;
2080
2081     if (data)
2082     {
2083         data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0);
2084         if (data->imageList)
2085         {
2086             HBITMAP bmp;
2087             COLORREF backColor = RGB(255, 0, 255);
2088
2089             data->pCertViewInfo = pCertViewInfo;
2090             data->selectedCert = 0xffffffff;
2091
2092             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS));
2093             ImageList_AddMasked(data->imageList, bmp, backColor);
2094             DeleteObject(bmp);
2095             ImageList_SetBkColor(data->imageList, CLR_NONE);
2096
2097             memset(page, 0, sizeof(PROPSHEETPAGEW));
2098             page->dwSize = sizeof(PROPSHEETPAGEW);
2099             page->dwFlags = PSP_USECALLBACK;
2100             page->hInstance = hInstance;
2101             page->u.pszTemplate = MAKEINTRESOURCEW(IDD_HIERARCHY);
2102             page->pfnDlgProc = hierarchy_dlg_proc;
2103             page->lParam = (LPARAM)data;
2104             page->pfnCallback = hierarchy_callback;
2105             ret = TRUE;
2106         }
2107         else
2108             HeapFree(GetProcessHeap(), 0, data);
2109     }
2110     return ret;
2111 }
2112
2113 static int CALLBACK cert_prop_sheet_proc(HWND hwnd, UINT msg, LPARAM lp)
2114 {
2115     RECT rc;
2116     POINT topLeft;
2117
2118     TRACE("(%p, %08x, %08lx)\n", hwnd, msg, lp);
2119
2120     switch (msg)
2121     {
2122     case PSCB_INITIALIZED:
2123         /* Get cancel button's position.. */
2124         GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rc);
2125         topLeft.x = rc.left;
2126         topLeft.y = rc.top;
2127         ScreenToClient(hwnd, &topLeft);
2128         /* hide the cancel button.. */
2129         ShowWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
2130         /* get the OK button's size.. */
2131         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
2132         /* and move the OK button to the cancel button's original position. */
2133         MoveWindow(GetDlgItem(hwnd, IDOK), topLeft.x, topLeft.y,
2134          rc.right - rc.left, rc.bottom - rc.top, FALSE);
2135         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
2136         break;
2137     }
2138     return 0;
2139 }
2140
2141 static BOOL show_cert_dialog(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2142  CRYPT_PROVIDER_CERT *provCert, BOOL *pfPropertiesChanged)
2143 {
2144     static const WCHAR riched[] = { 'r','i','c','h','e','d','2','0',0 };
2145     DWORD nPages;
2146     PROPSHEETPAGEW *pages;
2147     BOOL ret = FALSE;
2148     HMODULE lib = LoadLibraryW(riched);
2149
2150     nPages = pCertViewInfo->cPropSheetPages + 1; /* one for the General tab */
2151     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
2152         nPages++;
2153     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
2154         nPages++;
2155     pages = HeapAlloc(GetProcessHeap(), 0, nPages * sizeof(PROPSHEETPAGEW));
2156     if (pages)
2157     {
2158         PROPSHEETHEADERW hdr;
2159         CRYPTUI_INITDIALOG_STRUCT *init = NULL;
2160         DWORD i;
2161
2162         memset(&hdr, 0, sizeof(hdr));
2163         hdr.dwSize = sizeof(hdr);
2164         hdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK;
2165         hdr.hInstance = hInstance;
2166         if (pCertViewInfo->szTitle)
2167             hdr.pszCaption = pCertViewInfo->szTitle;
2168         else
2169             hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE);
2170         init_general_page(pCertViewInfo, &pages[hdr.nPages++]);
2171         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
2172         {
2173             if (init_detail_page(pCertViewInfo, pfPropertiesChanged,
2174              &pages[hdr.nPages]))
2175                 hdr.nPages++;
2176         }
2177         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
2178         {
2179             if (init_hierarchy_page(pCertViewInfo, &pages[hdr.nPages]))
2180                 hdr.nPages++;
2181         }
2182         /* Copy each additional page, and create the init dialog struct for it
2183          */
2184         if (pCertViewInfo->cPropSheetPages)
2185         {
2186             init = HeapAlloc(GetProcessHeap(), 0,
2187              pCertViewInfo->cPropSheetPages *
2188              sizeof(CRYPTUI_INITDIALOG_STRUCT));
2189             if (init)
2190             {
2191                 for (i = 0; i < pCertViewInfo->cPropSheetPages; i++)
2192                 {
2193                     memcpy(&pages[hdr.nPages + i],
2194                      &pCertViewInfo->rgPropSheetPages[i],
2195                      sizeof(PROPSHEETPAGEW));
2196                     init[i].lParam = pCertViewInfo->rgPropSheetPages[i].lParam;
2197                     init[i].pCertContext = pCertViewInfo->pCertContext;
2198                     pages[hdr.nPages + i].lParam = (LPARAM)&init[i];
2199                 }
2200                 if (pCertViewInfo->nStartPage & 0x8000)
2201                 {
2202                     /* Start page index is relative to the number of default
2203                      * pages
2204                      */
2205                     hdr.u2.nStartPage = pCertViewInfo->nStartPage + hdr.nPages;
2206                 }
2207                 else
2208                     hdr.u2.nStartPage = pCertViewInfo->nStartPage;
2209                 hdr.nPages = nPages;
2210                 ret = TRUE;
2211             }
2212             else
2213                 SetLastError(ERROR_OUTOFMEMORY);
2214         }
2215         else
2216         {
2217             /* Ignore the relative flag if there aren't any additional pages */
2218             hdr.u2.nStartPage = pCertViewInfo->nStartPage & 0x7fff;
2219             ret = TRUE;
2220         }
2221         if (ret)
2222         {
2223             INT_PTR l;
2224
2225             hdr.u3.ppsp = pages;
2226             hdr.pfnCallback = cert_prop_sheet_proc;
2227             l = PropertySheetW(&hdr);
2228             if (l == 0)
2229             {
2230                 SetLastError(ERROR_CANCELLED);
2231                 ret = FALSE;
2232             }
2233         }
2234         HeapFree(GetProcessHeap(), 0, init);
2235         HeapFree(GetProcessHeap(), 0, pages);
2236     }
2237     else
2238         SetLastError(ERROR_OUTOFMEMORY);
2239     FreeLibrary(lib);
2240     return ret;
2241 }
2242
2243 /***********************************************************************
2244  *              CryptUIDlgViewCertificateW (CRYPTUI.@)
2245  */
2246 BOOL WINAPI CryptUIDlgViewCertificateW(
2247  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, BOOL *pfPropertiesChanged)
2248 {
2249     static GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY;
2250     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
2251     WINTRUST_DATA wvt;
2252     WINTRUST_CERT_INFO cert;
2253     BOOL ret = FALSE;
2254     CRYPT_PROVIDER_SGNR *signer;
2255     CRYPT_PROVIDER_CERT *provCert = NULL;
2256
2257     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
2258
2259     if (pCertViewInfo->dwSize != sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))
2260     {
2261         SetLastError(ERROR_INVALID_PARAMETER);
2262         return FALSE;
2263     }
2264     /* Make a local copy in case we have to call WinVerifyTrust ourselves */
2265     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
2266     if (!viewInfo.u.hWVTStateData)
2267     {
2268         memset(&wvt, 0, sizeof(wvt));
2269         wvt.cbStruct = sizeof(wvt);
2270         wvt.dwUIChoice = WTD_UI_NONE;
2271         if (viewInfo.dwFlags &
2272          CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
2273             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
2274         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_END_CERT)
2275             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_END_CERT;
2276         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN)
2277             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN;
2278         wvt.dwUnionChoice = WTD_CHOICE_CERT;
2279         memset(&cert, 0, sizeof(cert));
2280         cert.cbStruct = sizeof(cert);
2281         cert.psCertContext = (CERT_CONTEXT *)viewInfo.pCertContext;
2282         cert.chStores = viewInfo.cStores;
2283         cert.pahStores = viewInfo.rghStores;
2284         wvt.u.pCert = &cert;
2285         wvt.dwStateAction = WTD_STATEACTION_VERIFY;
2286         WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
2287         viewInfo.u.pCryptProviderData =
2288          WTHelperProvDataFromStateData(wvt.hWVTStateData);
2289         signer = WTHelperGetProvSignerFromChain(
2290          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, 0, FALSE, 0);
2291         provCert = WTHelperGetProvCertFromChain(signer, 0);
2292         ret = TRUE;
2293     }
2294     else
2295     {
2296         viewInfo.u.pCryptProviderData =
2297          WTHelperProvDataFromStateData(viewInfo.u.hWVTStateData);
2298         signer = WTHelperGetProvSignerFromChain(
2299          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData,
2300          viewInfo.idxSigner, viewInfo.fCounterSigner,
2301          viewInfo.idxCounterSigner);
2302         provCert = WTHelperGetProvCertFromChain(signer, viewInfo.idxCert);
2303         ret = TRUE;
2304     }
2305     if (ret)
2306     {
2307         ret = show_cert_dialog(&viewInfo, provCert, pfPropertiesChanged);
2308         if (!viewInfo.u.hWVTStateData)
2309         {
2310             wvt.dwStateAction = WTD_STATEACTION_CLOSE;
2311             WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
2312         }
2313     }
2314     return ret;
2315 }
2316
2317 static PCCERT_CONTEXT make_cert_from_file(LPCWSTR fileName)
2318 {
2319     HANDLE file;
2320     DWORD size, encoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
2321     BYTE *buffer;
2322     PCCERT_CONTEXT cert;
2323
2324     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
2325      OPEN_EXISTING, 0, NULL);
2326     if (file == INVALID_HANDLE_VALUE)
2327     {
2328         WARN("can't open certificate file %s\n", debugstr_w(fileName));
2329         return NULL;
2330     }
2331     if ((size = GetFileSize(file, NULL)))
2332     {
2333         if ((buffer = HeapAlloc(GetProcessHeap(), 0, size)))
2334         {
2335             DWORD read;
2336             if (!ReadFile(file, buffer, size, &read, NULL) || read != size)
2337             {
2338                 WARN("can't read certificate file %s\n", debugstr_w(fileName));
2339                 HeapFree(GetProcessHeap(), 0, buffer);
2340                 CloseHandle(file);
2341                 return NULL;
2342             }
2343         }
2344     }
2345     else
2346     {
2347         WARN("empty file %s\n", debugstr_w(fileName));
2348         CloseHandle(file);
2349         return NULL;
2350     }
2351     CloseHandle(file);
2352     cert = CertCreateCertificateContext(encoding, buffer, size);
2353     HeapFree(GetProcessHeap(), 0, buffer);
2354     return cert;
2355 }
2356
2357 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
2358  * or szOID_BASIC_CONSTRAINTS2, whichever is present) to determine if it
2359  * should be a CA.  If neither extension is present, returns
2360  * defaultIfNotSpecified.
2361  */
2362 static BOOL is_ca_cert(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified)
2363 {
2364     BOOL isCA = defaultIfNotSpecified;
2365     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
2366      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
2367
2368     if (ext)
2369     {
2370         CERT_BASIC_CONSTRAINTS_INFO *info;
2371         DWORD size = 0;
2372
2373         if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
2374          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
2375          NULL, (LPBYTE)&info, &size))
2376         {
2377             if (info->SubjectType.cbData == 1)
2378                 isCA = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
2379             LocalFree(info);
2380         }
2381     }
2382     else
2383     {
2384         ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
2385          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
2386         if (ext)
2387         {
2388             CERT_BASIC_CONSTRAINTS2_INFO info;
2389             DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
2390
2391             if (CryptDecodeObjectEx(X509_ASN_ENCODING,
2392              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
2393              0, NULL, &info, &size))
2394                 isCA = info.fCA;
2395         }
2396     }
2397     return isCA;
2398 }
2399
2400 static HCERTSTORE choose_store_for_cert(PCCERT_CONTEXT cert)
2401 {
2402     static const WCHAR AddressBook[] = { 'A','d','d','r','e','s','s',
2403      'B','o','o','k',0 };
2404     static const WCHAR CA[] = { 'C','A',0 };
2405     LPCWSTR storeName;
2406
2407     if (is_ca_cert(cert, TRUE))
2408         storeName = CA;
2409     else
2410         storeName = AddressBook;
2411     return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
2412      CERT_SYSTEM_STORE_CURRENT_USER, storeName);
2413 }
2414
2415 BOOL WINAPI CryptUIWizImport(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
2416                              PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
2417 {
2418     BOOL ret;
2419     HCERTSTORE store;
2420     const CERT_CONTEXT *cert;
2421     BOOL freeCert = FALSE;
2422
2423     TRACE("(0x%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, debugstr_w(pwszWizardTitle),
2424           pImportSrc, hDestCertStore);
2425
2426     if (!(dwFlags & CRYPTUI_WIZ_NO_UI)) FIXME("UI not implemented\n");
2427
2428     if (!pImportSrc ||
2429      pImportSrc->dwSize != sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO))
2430     {
2431         SetLastError(E_INVALIDARG);
2432         return FALSE;
2433     }
2434
2435     switch (pImportSrc->dwSubjectChoice)
2436     {
2437     case CRYPTUI_WIZ_IMPORT_SUBJECT_FILE:
2438         if (!(cert = make_cert_from_file(pImportSrc->u.pwszFileName)))
2439         {
2440             WARN("unable to create certificate context\n");
2441             return FALSE;
2442         }
2443         else
2444             freeCert = TRUE;
2445         break;
2446     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
2447         cert = pImportSrc->u.pCertContext;
2448         if (!cert)
2449         {
2450             SetLastError(E_INVALIDARG);
2451             return FALSE;
2452         }
2453         break;
2454     default:
2455         FIXME("source type not implemented: %u\n", pImportSrc->dwSubjectChoice);
2456         SetLastError(E_INVALIDARG);
2457         return FALSE;
2458     }
2459     if (hDestCertStore) store = hDestCertStore;
2460     else
2461     {
2462         if (!(store = choose_store_for_cert(cert)))
2463         {
2464             WARN("unable to open certificate store\n");
2465             CertFreeCertificateContext(cert);
2466             return FALSE;
2467         }
2468     }
2469     ret = CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
2470
2471     if (!hDestCertStore) CertCloseStore(store, 0);
2472     if (freeCert)
2473         CertFreeCertificateContext(cert);
2474     return ret;
2475 }