cryptui: Add a (empty) hierarchy page to the 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     static const WCHAR sep[] = { ',',' ',0 };
1422     const CERT_ENHKEY_USAGE *usage = (const CERT_ENHKEY_USAGE *)pb;
1423     static WCHAR *str = NULL;
1424     DWORD i, chars = 0;
1425
1426     for (i = 0; i < usage->cUsageIdentifier; i++)
1427     {
1428         PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1429          usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
1430
1431         if (info)
1432         {
1433             chars += strlenW(info->pwszName);
1434             if (i < usage->cUsageIdentifier - 1)
1435                 chars += strlenW(sep);
1436             if (!str)
1437             {
1438                 str = HeapAlloc(GetProcessHeap(), 0,
1439                  (chars + 1) * sizeof(WCHAR));
1440                 if (str)
1441                     *str = '\0';
1442             }
1443             else
1444                 str = HeapReAlloc(GetProcessHeap(), 0, str,
1445                  (chars + 1) * sizeof(WCHAR));
1446             if (str)
1447             {
1448                 if (i < usage->cUsageIdentifier - 1)
1449                     strcatW(str, sep);
1450                 strcatW(str, info->pwszName);
1451             }
1452         }
1453         else
1454         {
1455             chars += strlen(usage->rgpszUsageIdentifier[i]);
1456             if (i < usage->cUsageIdentifier - 1)
1457                 chars += strlenW(sep);
1458             if (!str)
1459             {
1460                 str = HeapAlloc(GetProcessHeap(), 0,
1461                  (chars + 1) * sizeof(WCHAR));
1462                 if (str)
1463                     *str = '\0';
1464             }
1465             else
1466                 str = HeapReAlloc(GetProcessHeap(), 0, str,
1467                  (chars + 1) * sizeof(WCHAR));
1468             if (str)
1469             {
1470                 WCHAR *dst;
1471                 const char *src;
1472
1473                 if (i < usage->cUsageIdentifier - 1)
1474                     strcatW(str, sep);
1475                 for (src = usage->rgpszUsageIdentifier[i],
1476                  dst = str + strlenW(str); *src; src++, dst++)
1477                     *dst = *src;
1478                 *dst = '\0';
1479             }
1480         }
1481     }
1482     return str;
1483 }
1484
1485 /* Logically the access state should also be checked, and IDC_EDITPROPERTIES
1486  * disabled for read-only certificates, but native doesn't appear to do that.
1487  */
1488 static const struct prop_id_to_string_id prop_id_map[] = {
1489  { CERT_HASH_PROP_ID, IDS_PROP_HASH, FALSE, format_hex_string },
1490  { CERT_FRIENDLY_NAME_PROP_ID, IDS_PROP_FRIENDLY_NAME, TRUE, NULL },
1491  { CERT_DESCRIPTION_PROP_ID, IDS_PROP_DESCRIPTION, TRUE, NULL },
1492  { CERT_ENHKEY_USAGE_PROP_ID, IDS_PROP_ENHKEY_USAGE, FALSE,
1493    format_enhanced_key_usage_value },
1494 };
1495
1496 static void add_properties(HWND hwnd, struct detail_data *data)
1497 {
1498     DWORD i;
1499     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1500
1501     for (i = 0; i < sizeof(prop_id_map) / sizeof(prop_id_map[0]); i++)
1502     {
1503         DWORD cb;
1504
1505         if (CertGetCertificateContextProperty(cert, prop_id_map[i].prop, NULL,
1506          &cb))
1507         {
1508             BYTE *pb;
1509             WCHAR *val = NULL;
1510
1511             /* FIXME: MS adds a separate value for the signature hash
1512              * algorithm.
1513              */
1514             pb = HeapAlloc(GetProcessHeap(), 0, cb);
1515             if (pb)
1516             {
1517                 if (CertGetCertificateContextProperty(cert,
1518                  prop_id_map[i].prop, pb, &cb))
1519                 {
1520                     if (prop_id_map[i].prop_is_string)
1521                     {
1522                         val = (LPWSTR)pb;
1523                         /* Don't double-free pb */
1524                         pb = NULL;
1525                     }
1526                     else
1527                         val = prop_id_map[i].prop_to_value(pb, cb);
1528                 }
1529                 HeapFree(GetProcessHeap(), 0, pb);
1530             }
1531             add_string_id_and_value_to_list(hwnd, data, prop_id_map[i].id, val,
1532              NULL, NULL);
1533         }
1534     }
1535 }
1536
1537 static void add_all_fields(HWND hwnd, struct detail_data *data)
1538 {
1539     add_v1_fields(hwnd, data);
1540     add_all_extensions(hwnd, data);
1541     add_properties(hwnd, data);
1542 }
1543
1544 struct selection_list_item
1545 {
1546     int id;
1547     add_fields_func add;
1548 };
1549
1550 const struct selection_list_item listItems[] = {
1551  { IDS_FIELDS_ALL, add_all_fields },
1552  { IDS_FIELDS_V1, add_v1_fields },
1553  { IDS_FIELDS_EXTENSIONS, add_all_extensions },
1554  { IDS_FIELDS_CRITICAL_EXTENSIONS, add_critical_extensions },
1555  { IDS_FIELDS_PROPERTIES, add_properties },
1556 };
1557
1558 static void create_show_list(HWND hwnd, struct detail_data *data)
1559 {
1560     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
1561     WCHAR buf[MAX_STRING_LEN];
1562     int i;
1563
1564     for (i = 0; i < sizeof(listItems) / sizeof(listItems[0]); i++)
1565     {
1566         int index;
1567
1568         LoadStringW(hInstance, listItems[i].id, buf,
1569          sizeof(buf) / sizeof(buf[0]));
1570         index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
1571         SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)data);
1572     }
1573     SendMessageW(cb, CB_SETCURSEL, 0, 0);
1574 }
1575
1576 static void create_listview_columns(HWND hwnd)
1577 {
1578     HWND lv = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1579     RECT rc;
1580     WCHAR buf[MAX_STRING_LEN];
1581     LVCOLUMNW column;
1582
1583     SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
1584     GetWindowRect(lv, &rc);
1585     LoadStringW(hInstance, IDS_FIELD, buf, sizeof(buf) / sizeof(buf[0]));
1586     column.mask = LVCF_WIDTH | LVCF_TEXT;
1587     column.cx = (rc.right - rc.left) / 2 - 2;
1588     column.pszText = buf;
1589     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
1590     LoadStringW(hInstance, IDS_VALUE, buf, sizeof(buf) / sizeof(buf[0]));
1591     SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
1592 }
1593
1594 static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel)
1595 {
1596     HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1597
1598     if (sel >= 0 && sel < sizeof(listItems) / sizeof(listItems[0]))
1599     {
1600         SendMessageW(list, LVM_DELETEALLITEMS, 0, 0);
1601         listItems[sel].add(list, data);
1602     }
1603 }
1604
1605 static void create_cert_details_list(HWND hwnd, struct detail_data *data)
1606 {
1607     create_show_list(hwnd, data);
1608     create_listview_columns(hwnd);
1609     set_fields_selection(hwnd, data, 0);
1610 }
1611
1612 static void free_detail_fields(struct detail_data *data)
1613 {
1614     DWORD i;
1615
1616     for (i = 0; i < data->cFields; i++)
1617         HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value);
1618     HeapFree(GetProcessHeap(), 0, data->fields);
1619     data->fields = NULL;
1620     data->cFields = 0;
1621 }
1622
1623 static void refresh_details_view(HWND hwnd)
1624 {
1625     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
1626     int curSel;
1627     struct detail_data *data;
1628
1629     curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
1630     /* Actually, any index will do, since they all store the same data value */
1631     data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0);
1632     free_detail_fields(data);
1633     set_fields_selection(hwnd, data, curSel);
1634 }
1635
1636 static LRESULT CALLBACK detail_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1637  LPARAM lp)
1638 {
1639     PROPSHEETPAGEW *page;
1640     struct detail_data *data;
1641
1642     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
1643
1644     switch (msg)
1645     {
1646     case WM_INITDIALOG:
1647         page = (PROPSHEETPAGEW *)lp;
1648         data = (struct detail_data *)page->lParam;
1649         create_cert_details_list(hwnd, data);
1650         if (!(data->pCertViewInfo->dwFlags & CRYPTUI_ENABLE_EDITPROPERTIES))
1651             EnableWindow(GetDlgItem(hwnd, IDC_EDITPROPERTIES), FALSE);
1652         if (data->pCertViewInfo->dwFlags & CRYPTUI_DISABLE_EXPORT)
1653             EnableWindow(GetDlgItem(hwnd, IDC_EXPORT), FALSE);
1654         break;
1655     case WM_NOTIFY:
1656     {
1657         NMITEMACTIVATE *nm;
1658         HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1659
1660         nm = (NMITEMACTIVATE*)lp;
1661         if (nm->hdr.hwndFrom == list && nm->uNewState & LVN_ITEMACTIVATE
1662          && nm->hdr.code == LVN_ITEMCHANGED)
1663         {
1664             data = (struct detail_data *)nm->lParam;
1665             if (nm->iItem >= 0 && data && nm->iItem < data->cFields)
1666             {
1667                 WCHAR buf[MAX_STRING_LEN], *val = NULL;
1668                 HWND valueCtl = GetDlgItem(hwnd, IDC_DETAIL_VALUE);
1669
1670                 if (data->fields[nm->iItem].create)
1671                     val = data->fields[nm->iItem].create(
1672                      data->pCertViewInfo->pCertContext,
1673                      data->fields[nm->iItem].param);
1674                 else
1675                 {
1676                     LVITEMW item;
1677                     int res;
1678
1679                     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
1680                     item.mask = LVIF_TEXT;
1681                     item.pszText = buf;
1682                     item.iItem = nm->iItem;
1683                     item.iSubItem = 1;
1684                     res = SendMessageW(list, LVM_GETITEMW, 0, (LPARAM)&item);
1685                     if (res)
1686                         val = buf;
1687                 }
1688                 /* Select all the text in the control, the next update will
1689                  * replace it
1690                  */
1691                 SendMessageW(valueCtl, EM_SETSEL, 0, -1);
1692                 add_unformatted_text_to_control(valueCtl, val,
1693                  val ? strlenW(val) : 0);
1694                 if (val != buf)
1695                     HeapFree(GetProcessHeap(), 0, val);
1696             }
1697         }
1698         break;
1699     }
1700     case WM_COMMAND:
1701         switch (wp)
1702         {
1703         case IDC_EXPORT:
1704             FIXME("call CryptUIWizExport\n");
1705             break;
1706         case IDC_EDITPROPERTIES:
1707             FIXME("show edit properties dialog\n");
1708             break;
1709         case ((CBN_SELCHANGE << 16) | IDC_DETAIL_SELECT):
1710             refresh_details_view(hwnd);
1711             break;
1712         }
1713         break;
1714     }
1715     return 0;
1716 }
1717
1718 static BOOL init_detail_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
1719  BOOL *pfPropertiesChanged, PROPSHEETPAGEW *page)
1720 {
1721     BOOL ret;
1722     struct detail_data *data = HeapAlloc(GetProcessHeap(), 0,
1723      sizeof(struct detail_data));
1724
1725     if (data)
1726     {
1727         data->pCertViewInfo = pCertViewInfo;
1728         data->pfPropertiesChanged = pfPropertiesChanged;
1729         data->cFields = 0;
1730         data->fields = NULL;
1731         memset(page, 0, sizeof(PROPSHEETPAGEW));
1732         page->dwSize = sizeof(PROPSHEETPAGEW);
1733         page->hInstance = hInstance;
1734         page->u.pszTemplate = MAKEINTRESOURCEW(IDD_DETAIL);
1735         page->pfnDlgProc = detail_dlg_proc;
1736         page->lParam = (LPARAM)data;
1737         ret = TRUE;
1738     }
1739     else
1740         ret = FALSE;
1741     return ret;
1742 }
1743
1744 struct hierarchy_data
1745 {
1746     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
1747     DWORD selectedCert;
1748 };
1749
1750 static void show_cert_hierarchy(HWND hwnd, struct hierarchy_data *data)
1751 {
1752     /* Disable view certificate button until a certificate is selected */
1753     EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), FALSE);
1754     FIXME("show cert chain\n");
1755 }
1756
1757 static LRESULT CALLBACK hierarchy_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
1758  LPARAM lp)
1759 {
1760     PROPSHEETPAGEW *page;
1761     struct hierarchy_data *data;
1762
1763     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
1764
1765     switch (msg)
1766     {
1767     case WM_INITDIALOG:
1768         page = (PROPSHEETPAGEW *)lp;
1769         data = (struct hierarchy_data *)page->lParam;
1770         show_cert_hierarchy(hwnd, data);
1771         break;
1772     }
1773     return 0;
1774 }
1775
1776 static UINT CALLBACK hierarchy_callback(HWND hwnd, UINT msg,
1777  PROPSHEETPAGEW *page)
1778 {
1779     struct hierarchy_data *data;
1780
1781     switch (msg)
1782     {
1783     case PSPCB_RELEASE:
1784         data = (struct hierarchy_data *)page->lParam;
1785         HeapFree(GetProcessHeap(), 0, data);
1786         break;
1787     }
1788     return 0;
1789 }
1790
1791 static BOOL init_hierarchy_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
1792  PROPSHEETPAGEW *page)
1793 {
1794     struct hierarchy_data *data = HeapAlloc(GetProcessHeap(), 0,
1795      sizeof(struct hierarchy_data));
1796     BOOL ret = FALSE;
1797
1798     if (data)
1799     {
1800         data->pCertViewInfo = pCertViewInfo;
1801         data->selectedCert = 0xffffffff;
1802
1803         memset(page, 0, sizeof(PROPSHEETPAGEW));
1804         page->dwSize = sizeof(PROPSHEETPAGEW);
1805         page->dwFlags = PSP_USECALLBACK;
1806         page->hInstance = hInstance;
1807         page->u.pszTemplate = MAKEINTRESOURCEW(IDD_HIERARCHY);
1808         page->pfnDlgProc = hierarchy_dlg_proc;
1809         page->lParam = (LPARAM)data;
1810         page->pfnCallback = hierarchy_callback;
1811         ret = TRUE;
1812     }
1813     return ret;
1814 }
1815
1816 static int CALLBACK cert_prop_sheet_proc(HWND hwnd, UINT msg, LPARAM lp)
1817 {
1818     RECT rc;
1819     POINT topLeft;
1820
1821     TRACE("(%p, %08x, %08lx)\n", hwnd, msg, lp);
1822
1823     switch (msg)
1824     {
1825     case PSCB_INITIALIZED:
1826         /* Get cancel button's position.. */
1827         GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rc);
1828         topLeft.x = rc.left;
1829         topLeft.y = rc.top;
1830         ScreenToClient(hwnd, &topLeft);
1831         /* hide the cancel button.. */
1832         ShowWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
1833         /* get the OK button's size.. */
1834         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
1835         /* and move the OK button to the cancel button's original position. */
1836         MoveWindow(GetDlgItem(hwnd, IDOK), topLeft.x, topLeft.y,
1837          rc.right - rc.left, rc.bottom - rc.top, FALSE);
1838         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
1839         break;
1840     }
1841     return 0;
1842 }
1843
1844 static BOOL show_cert_dialog(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
1845  CRYPT_PROVIDER_CERT *provCert, BOOL *pfPropertiesChanged)
1846 {
1847     static const WCHAR riched[] = { 'r','i','c','h','e','d','2','0',0 };
1848     DWORD nPages;
1849     PROPSHEETPAGEW *pages;
1850     BOOL ret = FALSE;
1851     HMODULE lib = LoadLibraryW(riched);
1852
1853     nPages = pCertViewInfo->cPropSheetPages + 1; /* one for the General tab */
1854     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
1855         nPages++;
1856     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
1857         nPages++;
1858     pages = HeapAlloc(GetProcessHeap(), 0, nPages * sizeof(PROPSHEETPAGEW));
1859     if (pages)
1860     {
1861         PROPSHEETHEADERW hdr;
1862         CRYPTUI_INITDIALOG_STRUCT *init = NULL;
1863         DWORD i;
1864
1865         memset(&hdr, 0, sizeof(hdr));
1866         hdr.dwSize = sizeof(hdr);
1867         hdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK;
1868         hdr.hInstance = hInstance;
1869         if (pCertViewInfo->szTitle)
1870             hdr.pszCaption = pCertViewInfo->szTitle;
1871         else
1872             hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE);
1873         init_general_page(pCertViewInfo, &pages[hdr.nPages++]);
1874         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
1875         {
1876             if (init_detail_page(pCertViewInfo, pfPropertiesChanged,
1877              &pages[hdr.nPages]))
1878                 hdr.nPages++;
1879         }
1880         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
1881         {
1882             if (init_hierarchy_page(pCertViewInfo, &pages[hdr.nPages]))
1883                 hdr.nPages++;
1884         }
1885         /* Copy each additional page, and create the init dialog struct for it
1886          */
1887         if (pCertViewInfo->cPropSheetPages)
1888         {
1889             init = HeapAlloc(GetProcessHeap(), 0,
1890              pCertViewInfo->cPropSheetPages *
1891              sizeof(CRYPTUI_INITDIALOG_STRUCT));
1892             if (init)
1893             {
1894                 for (i = 0; i < pCertViewInfo->cPropSheetPages; i++)
1895                 {
1896                     memcpy(&pages[hdr.nPages + i],
1897                      &pCertViewInfo->rgPropSheetPages[i],
1898                      sizeof(PROPSHEETPAGEW));
1899                     init[i].lParam = pCertViewInfo->rgPropSheetPages[i].lParam;
1900                     init[i].pCertContext = pCertViewInfo->pCertContext;
1901                     pages[hdr.nPages + i].lParam = (LPARAM)&init[i];
1902                 }
1903                 if (pCertViewInfo->nStartPage & 0x8000)
1904                 {
1905                     /* Start page index is relative to the number of default
1906                      * pages
1907                      */
1908                     hdr.u2.nStartPage = pCertViewInfo->nStartPage + hdr.nPages;
1909                 }
1910                 else
1911                     hdr.u2.nStartPage = pCertViewInfo->nStartPage;
1912                 hdr.nPages = nPages;
1913                 ret = TRUE;
1914             }
1915             else
1916                 SetLastError(ERROR_OUTOFMEMORY);
1917         }
1918         else
1919         {
1920             /* Ignore the relative flag if there aren't any additional pages */
1921             hdr.u2.nStartPage = pCertViewInfo->nStartPage & 0x7fff;
1922             ret = TRUE;
1923         }
1924         if (ret)
1925         {
1926             INT_PTR l;
1927
1928             hdr.u3.ppsp = pages;
1929             hdr.pfnCallback = cert_prop_sheet_proc;
1930             l = PropertySheetW(&hdr);
1931             if (l == 0)
1932             {
1933                 SetLastError(ERROR_CANCELLED);
1934                 ret = FALSE;
1935             }
1936         }
1937         HeapFree(GetProcessHeap(), 0, init);
1938         HeapFree(GetProcessHeap(), 0, pages);
1939     }
1940     else
1941         SetLastError(ERROR_OUTOFMEMORY);
1942     FreeLibrary(lib);
1943     return ret;
1944 }
1945
1946 /***********************************************************************
1947  *              CryptUIDlgViewCertificateW (CRYPTUI.@)
1948  */
1949 BOOL WINAPI CryptUIDlgViewCertificateW(
1950  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, BOOL *pfPropertiesChanged)
1951 {
1952     static GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY;
1953     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
1954     WINTRUST_DATA wvt;
1955     WINTRUST_CERT_INFO cert;
1956     BOOL ret = FALSE;
1957     CRYPT_PROVIDER_SGNR *signer;
1958     CRYPT_PROVIDER_CERT *provCert = NULL;
1959
1960     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
1961
1962     if (pCertViewInfo->dwSize != sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))
1963     {
1964         SetLastError(ERROR_INVALID_PARAMETER);
1965         return FALSE;
1966     }
1967     /* Make a local copy in case we have to call WinVerifyTrust ourselves */
1968     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
1969     if (!viewInfo.u.hWVTStateData)
1970     {
1971         memset(&wvt, 0, sizeof(wvt));
1972         wvt.cbStruct = sizeof(wvt);
1973         wvt.dwUIChoice = WTD_UI_NONE;
1974         if (viewInfo.dwFlags &
1975          CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
1976             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1977         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_END_CERT)
1978             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_END_CERT;
1979         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN)
1980             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN;
1981         wvt.dwUnionChoice = WTD_CHOICE_CERT;
1982         memset(&cert, 0, sizeof(cert));
1983         cert.cbStruct = sizeof(cert);
1984         cert.psCertContext = (CERT_CONTEXT *)viewInfo.pCertContext;
1985         cert.chStores = viewInfo.cStores;
1986         cert.pahStores = viewInfo.rghStores;
1987         wvt.u.pCert = &cert;
1988         wvt.dwStateAction = WTD_STATEACTION_VERIFY;
1989         WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
1990         viewInfo.u.pCryptProviderData =
1991          WTHelperProvDataFromStateData(wvt.hWVTStateData);
1992         signer = WTHelperGetProvSignerFromChain(
1993          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, 0, FALSE, 0);
1994         provCert = WTHelperGetProvCertFromChain(signer, 0);
1995         ret = TRUE;
1996     }
1997     else
1998     {
1999         viewInfo.u.pCryptProviderData =
2000          WTHelperProvDataFromStateData(viewInfo.u.hWVTStateData);
2001         signer = WTHelperGetProvSignerFromChain(
2002          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData,
2003          viewInfo.idxSigner, viewInfo.fCounterSigner,
2004          viewInfo.idxCounterSigner);
2005         provCert = WTHelperGetProvCertFromChain(signer, viewInfo.idxCert);
2006         ret = TRUE;
2007     }
2008     if (ret)
2009     {
2010         ret = show_cert_dialog(&viewInfo, provCert, pfPropertiesChanged);
2011         if (!viewInfo.u.hWVTStateData)
2012         {
2013             wvt.dwStateAction = WTD_STATEACTION_CLOSE;
2014             WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
2015         }
2016     }
2017     return ret;
2018 }
2019
2020 static PCCERT_CONTEXT make_cert_from_file(LPCWSTR fileName)
2021 {
2022     HANDLE file;
2023     DWORD size, encoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
2024     BYTE *buffer;
2025     PCCERT_CONTEXT cert;
2026
2027     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
2028      OPEN_EXISTING, 0, NULL);
2029     if (file == INVALID_HANDLE_VALUE)
2030     {
2031         WARN("can't open certificate file %s\n", debugstr_w(fileName));
2032         return NULL;
2033     }
2034     if ((size = GetFileSize(file, NULL)))
2035     {
2036         if ((buffer = HeapAlloc(GetProcessHeap(), 0, size)))
2037         {
2038             DWORD read;
2039             if (!ReadFile(file, buffer, size, &read, NULL) || read != size)
2040             {
2041                 WARN("can't read certificate file %s\n", debugstr_w(fileName));
2042                 HeapFree(GetProcessHeap(), 0, buffer);
2043                 CloseHandle(file);
2044                 return NULL;
2045             }
2046         }
2047     }
2048     else
2049     {
2050         WARN("empty file %s\n", debugstr_w(fileName));
2051         CloseHandle(file);
2052         return NULL;
2053     }
2054     CloseHandle(file);
2055     cert = CertCreateCertificateContext(encoding, buffer, size);
2056     HeapFree(GetProcessHeap(), 0, buffer);
2057     return cert;
2058 }
2059
2060 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
2061  * or szOID_BASIC_CONSTRAINTS2, whichever is present) to determine if it
2062  * should be a CA.  If neither extension is present, returns
2063  * defaultIfNotSpecified.
2064  */
2065 static BOOL is_ca_cert(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified)
2066 {
2067     BOOL isCA = defaultIfNotSpecified;
2068     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
2069      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
2070
2071     if (ext)
2072     {
2073         CERT_BASIC_CONSTRAINTS_INFO *info;
2074         DWORD size = 0;
2075
2076         if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
2077          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
2078          NULL, (LPBYTE)&info, &size))
2079         {
2080             if (info->SubjectType.cbData == 1)
2081                 isCA = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
2082             LocalFree(info);
2083         }
2084     }
2085     else
2086     {
2087         ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
2088          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
2089         if (ext)
2090         {
2091             CERT_BASIC_CONSTRAINTS2_INFO info;
2092             DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
2093
2094             if (CryptDecodeObjectEx(X509_ASN_ENCODING,
2095              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
2096              0, NULL, &info, &size))
2097                 isCA = info.fCA;
2098         }
2099     }
2100     return isCA;
2101 }
2102
2103 static HCERTSTORE choose_store_for_cert(PCCERT_CONTEXT cert)
2104 {
2105     static const WCHAR AddressBook[] = { 'A','d','d','r','e','s','s',
2106      'B','o','o','k',0 };
2107     static const WCHAR CA[] = { 'C','A',0 };
2108     LPCWSTR storeName;
2109
2110     if (is_ca_cert(cert, TRUE))
2111         storeName = CA;
2112     else
2113         storeName = AddressBook;
2114     return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
2115      CERT_SYSTEM_STORE_CURRENT_USER, storeName);
2116 }
2117
2118 BOOL WINAPI CryptUIWizImport(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
2119                              PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
2120 {
2121     BOOL ret;
2122     HCERTSTORE store;
2123     const CERT_CONTEXT *cert;
2124     BOOL freeCert = FALSE;
2125
2126     TRACE("(0x%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, debugstr_w(pwszWizardTitle),
2127           pImportSrc, hDestCertStore);
2128
2129     if (!(dwFlags & CRYPTUI_WIZ_NO_UI)) FIXME("UI not implemented\n");
2130
2131     if (!pImportSrc ||
2132      pImportSrc->dwSize != sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO))
2133     {
2134         SetLastError(E_INVALIDARG);
2135         return FALSE;
2136     }
2137
2138     switch (pImportSrc->dwSubjectChoice)
2139     {
2140     case CRYPTUI_WIZ_IMPORT_SUBJECT_FILE:
2141         if (!(cert = make_cert_from_file(pImportSrc->u.pwszFileName)))
2142         {
2143             WARN("unable to create certificate context\n");
2144             return FALSE;
2145         }
2146         else
2147             freeCert = TRUE;
2148         break;
2149     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
2150         cert = pImportSrc->u.pCertContext;
2151         if (!cert)
2152         {
2153             SetLastError(E_INVALIDARG);
2154             return FALSE;
2155         }
2156         break;
2157     default:
2158         FIXME("source type not implemented: %u\n", pImportSrc->dwSubjectChoice);
2159         SetLastError(E_INVALIDARG);
2160         return FALSE;
2161     }
2162     if (hDestCertStore) store = hDestCertStore;
2163     else
2164     {
2165         if (!(store = choose_store_for_cert(cert)))
2166         {
2167             WARN("unable to open certificate store\n");
2168             CertFreeCertificateContext(cert);
2169             return FALSE;
2170         }
2171     }
2172     ret = CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
2173
2174     if (!hDestCertStore) CertCloseStore(store, 0);
2175     if (freeCert)
2176         CertFreeCertificateContext(cert);
2177     return ret;
2178 }