cryptui: Add stubs for CryptUIDlgSelectStoreA/W.
[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 /* FIXME: real names are unknown, functions are undocumented */
77 struct _CRYPTUI_SELECTSTORE_INFO_A;
78 struct _CRYPTUI_SELECTSTORE_INFO_W;
79
80 /***********************************************************************
81  *              CryptUIDlgSelectStoreA (CRYPTUI.@)
82  */
83 HCERTSTORE WINAPI CryptUIDlgSelectStoreA(struct _CRYPTUI_SELECTSTORE_INFO_A *info)
84 {
85     FIXME("(%p): stub\n", info);
86     return NULL;
87 }
88
89 /***********************************************************************
90  *              CryptUIDlgSelectStoreW (CRYPTUI.@)
91  */
92 HCERTSTORE WINAPI CryptUIDlgSelectStoreW(struct _CRYPTUI_SELECTSTORE_INFO_W *info)
93 {
94     FIXME("(%p): stub\n", info);
95     return NULL;
96 }
97
98 /***********************************************************************
99  *              CryptUIDlgViewCertificateA (CRYPTUI.@)
100  */
101 BOOL WINAPI CryptUIDlgViewCertificateA(
102  PCCRYPTUI_VIEWCERTIFICATE_STRUCTA pCertViewInfo, BOOL *pfPropertiesChanged)
103 {
104     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
105     LPWSTR title = NULL;
106     BOOL ret;
107
108     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
109
110     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
111     if (pCertViewInfo->szTitle)
112     {
113         int len = MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1,
114          NULL, 0);
115
116         title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
117         if (title)
118         {
119             MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, title,
120              len);
121             viewInfo.szTitle = title;
122         }
123         else
124         {
125             ret = FALSE;
126             goto error;
127         }
128     }
129     if (pCertViewInfo->cPropSheetPages)
130     {
131         FIXME("ignoring additional prop sheet pages\n");
132         viewInfo.cPropSheetPages = 0;
133     }
134     ret = CryptUIDlgViewCertificateW(&viewInfo, pfPropertiesChanged);
135     HeapFree(GetProcessHeap(), 0, title);
136 error:
137     return ret;
138 }
139
140 struct ReadStringStruct
141 {
142     LPCWSTR buf;
143     LONG pos;
144     LONG len;
145 };
146
147 static DWORD CALLBACK read_text_callback(DWORD_PTR dwCookie, LPBYTE buf,
148  LONG cb, LONG *pcb)
149 {
150     struct ReadStringStruct *string = (struct ReadStringStruct *)dwCookie;
151     LONG cch = min(cb / sizeof(WCHAR), string->len - string->pos);
152
153     TRACE("(%p, %p, %d, %p)\n", string, buf, cb, pcb);
154
155     memmove(buf, string->buf + string->pos, cch * sizeof(WCHAR));
156     string->pos += cch;
157     *pcb = cch * sizeof(WCHAR);
158     return 0;
159 }
160
161 static void add_unformatted_text_to_control(HWND hwnd, LPCWSTR text, LONG len)
162 {
163     struct ReadStringStruct string;
164     EDITSTREAM editstream;
165
166     TRACE("(%p, %s)\n", hwnd, debugstr_wn(text, len));
167
168     string.buf = text;
169     string.pos = 0;
170     string.len = len;
171     editstream.dwCookie = (DWORD_PTR)&string;
172     editstream.dwError = 0;
173     editstream.pfnCallback = read_text_callback;
174     SendMessageW(hwnd, EM_STREAMIN, SF_TEXT | SFF_SELECTION | SF_UNICODE,
175      (LPARAM)&editstream);
176 }
177
178 static void add_string_resource_to_control(HWND hwnd, int id)
179 {
180     LPWSTR str;
181     LONG len;
182
183     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
184     add_unformatted_text_to_control(hwnd, str, len);
185 }
186
187 static void add_text_with_paraformat_to_control(HWND hwnd, LPCWSTR text,
188  LONG len, const PARAFORMAT2 *fmt)
189 {
190     add_unformatted_text_to_control(hwnd, text, len);
191     SendMessageW(hwnd, EM_SETPARAFORMAT, 0, (LPARAM)fmt);
192 }
193
194 static void add_string_resource_with_paraformat_to_control(HWND hwnd, int id,
195  const PARAFORMAT2 *fmt)
196 {
197     LPWSTR str;
198     LONG len;
199
200     len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
201     add_text_with_paraformat_to_control(hwnd, str, len, fmt);
202 }
203
204 static LPWSTR get_cert_name_string(PCCERT_CONTEXT pCertContext, DWORD dwType,
205  DWORD dwFlags)
206 {
207     LPWSTR buf = NULL;
208     DWORD len;
209
210     len = CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, NULL, 0);
211     if (len)
212     {
213         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
214         if (buf)
215             CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, buf, len);
216     }
217     return buf;
218 }
219
220 static void add_cert_string_to_control(HWND hwnd, PCCERT_CONTEXT pCertContext,
221  DWORD dwType, DWORD dwFlags)
222 {
223     LPWSTR name = get_cert_name_string(pCertContext, dwType, dwFlags);
224
225     if (name)
226     {
227         /* Don't include NULL-terminator in output */
228         DWORD len = lstrlenW(name);
229
230         add_unformatted_text_to_control(hwnd, name, len);
231         HeapFree(GetProcessHeap(), 0, name);
232     }
233 }
234
235 static void add_icon_to_control(HWND hwnd, int id)
236 {
237     HRESULT hr;
238     LPRICHEDITOLE richEditOle = NULL;
239     LPOLEOBJECT object = NULL;
240     CLSID clsid;
241     LPOLECACHE oleCache = NULL;
242     FORMATETC formatEtc;
243     DWORD conn;
244     LPDATAOBJECT dataObject = NULL;
245     HBITMAP bitmap = NULL;
246     RECT rect;
247     STGMEDIUM stgm;
248     REOBJECT reObject;
249
250     TRACE("(%p, %d)\n", hwnd, id);
251
252     SendMessageW(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richEditOle);
253     if (!richEditOle)
254         goto end;
255     hr = OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject,
256      (void**)&object);
257     if (FAILED(hr))
258         goto end;
259     hr = IOleObject_GetUserClassID(object, &clsid);
260     if (FAILED(hr))
261         goto end;
262     hr = IOleObject_QueryInterface(object, &IID_IOleCache, (void**)&oleCache);
263     if (FAILED(hr))
264         goto end;
265     formatEtc.cfFormat = CF_BITMAP;
266     formatEtc.ptd = NULL;
267     formatEtc.dwAspect = DVASPECT_CONTENT;
268     formatEtc.lindex = -1;
269     formatEtc.tymed = TYMED_GDI;
270     hr = IOleCache_Cache(oleCache, &formatEtc, 0, &conn);
271     if (FAILED(hr))
272         goto end;
273     hr = IOleObject_QueryInterface(object, &IID_IDataObject,
274      (void**)&dataObject);
275     if (FAILED(hr))
276         goto end;
277     bitmap = LoadImageW(hInstance, MAKEINTRESOURCEW(id), IMAGE_BITMAP, 0, 0,
278      LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
279     if (!bitmap)
280         goto end;
281     rect.left = rect.top = 0;
282     rect.right = GetSystemMetrics(SM_CXICON);
283     rect.bottom = GetSystemMetrics(SM_CYICON);
284     stgm.tymed = TYMED_GDI;
285     stgm.u.hBitmap = bitmap;
286     stgm.pUnkForRelease = NULL;
287     hr = IDataObject_SetData(dataObject, &formatEtc, &stgm, TRUE);
288     if (FAILED(hr))
289         goto end;
290
291     reObject.cbStruct = sizeof(reObject);
292     reObject.cp = REO_CP_SELECTION;
293     reObject.clsid = clsid;
294     reObject.poleobj = object;
295     reObject.pstg = NULL;
296     reObject.polesite = NULL;
297     reObject.sizel.cx = reObject.sizel.cy = 0;
298     reObject.dvaspect = DVASPECT_CONTENT;
299     reObject.dwFlags = 0;
300     reObject.dwUser = 0;
301
302     IRichEditOle_InsertObject(richEditOle, &reObject);
303
304 end:
305     if (dataObject)
306         IDataObject_Release(dataObject);
307     if (oleCache)
308         IOleCache_Release(oleCache);
309     if (object)
310         IOleObject_Release(object);
311     if (richEditOle)
312         IRichEditOle_Release(richEditOle);
313 }
314
315 #define MY_INDENT 200
316
317 static void add_oid_text_to_control(HWND hwnd, char *oid)
318 {
319     WCHAR nl = '\n';
320     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid, 0);
321     PARAFORMAT2 parFmt;
322
323     parFmt.cbSize = sizeof(parFmt);
324     parFmt.dwMask = PFM_STARTINDENT;
325     parFmt.dxStartIndent = MY_INDENT * 3;
326     if (oidInfo)
327     {
328         add_text_with_paraformat_to_control(hwnd, oidInfo->pwszName,
329          lstrlenW(oidInfo->pwszName), &parFmt);
330         add_unformatted_text_to_control(hwnd, &nl, 1);
331     }
332 }
333
334 #define MAX_STRING_LEN 512
335
336 struct OIDToString
337 {
338     LPCSTR oid;
339     int    id;
340 };
341
342 /* The following list MUST be lexicographically sorted by OID */
343 static struct OIDToString oidMap[] = {
344  /* 1.3.6.1.4.1.311.10.3.1 */
345  { szOID_KP_CTL_USAGE_SIGNING, IDS_PURPOSE_CTL_USAGE_SIGNING },
346  /* 1.3.6.1.4.1.311.10.3.4 */
347  { szOID_KP_EFS, IDS_PURPOSE_EFS },
348  /* 1.3.6.1.4.1.311.10.3.4.1 */
349  { szOID_EFS_RECOVERY, IDS_PURPOSE_EFS_RECOVERY },
350  /* 1.3.6.1.4.1.311.10.3.5 */
351  { szOID_WHQL_CRYPTO, IDS_PURPOSE_WHQL },
352  /* 1.3.6.1.4.1.311.10.3.6 */
353  { szOID_NT5_CRYPTO, IDS_PURPOSE_NT5 },
354  /* 1.3.6.1.4.1.311.10.3.7 */
355  { szOID_OEM_WHQL_CRYPTO, IDS_PURPOSE_OEM_WHQL },
356  /* 1.3.6.1.4.1.311.10.3.8 */
357  { szOID_EMBEDDED_NT_CRYPTO, IDS_PURPOSE_EMBEDDED_NT },
358  /* 1.3.6.1.4.1.311.10.3.9 */
359  { szOID_ROOT_LIST_SIGNER, IDS_PURPOSE_ROOT_LIST_SIGNER },
360  /* 1.3.6.1.4.1.311.10.3.10 */
361  { szOID_KP_QUALIFIED_SUBORDINATION, IDS_PURPOSE_QUALIFIED_SUBORDINATION },
362  /* 1.3.6.1.4.1.311.10.3.11 */
363  { szOID_KP_KEY_RECOVERY, IDS_PURPOSE_KEY_RECOVERY },
364  /* 1.3.6.1.4.1.311.10.3.12 */
365  { szOID_KP_DOCUMENT_SIGNING, IDS_PURPOSE_DOCUMENT_SIGNING },
366  /* 1.3.6.1.4.1.311.10.3.13 */
367  { szOID_KP_LIFETIME_SIGNING, IDS_PURPOSE_LIFETIME_SIGNING },
368  /* 1.3.6.1.4.1.311.10.5.1 */
369  { szOID_DRM, IDS_PURPOSE_DRM },
370  /* 1.3.6.1.4.1.311.10.6.1 */
371  { szOID_LICENSES, IDS_PURPOSE_LICENSES },
372  /* 1.3.6.1.4.1.311.10.6.2 */
373  { szOID_LICENSE_SERVER, IDS_PURPOSE_LICENSE_SERVER },
374  /* 1.3.6.1.4.1.311.20.2.1 */
375  { szOID_ENROLLMENT_AGENT, IDS_PURPOSE_ENROLLMENT_AGENT },
376  /* 1.3.6.1.4.1.311.20.2.2 */
377  { szOID_KP_SMARTCARD_LOGON, IDS_PURPOSE_SMARTCARD_LOGON },
378  /* 1.3.6.1.4.1.311.21.5 */
379  { szOID_KP_CA_EXCHANGE, IDS_PURPOSE_CA_EXCHANGE },
380  /* 1.3.6.1.4.1.311.21.6 */
381  { szOID_KP_KEY_RECOVERY_AGENT, IDS_PURPOSE_KEY_RECOVERY_AGENT },
382  /* 1.3.6.1.4.1.311.21.19 */
383  { szOID_DS_EMAIL_REPLICATION, IDS_PURPOSE_DS_EMAIL_REPLICATION },
384  /* 1.3.6.1.5.5.7.3.1 */
385  { szOID_PKIX_KP_SERVER_AUTH, IDS_PURPOSE_SERVER_AUTH },
386  /* 1.3.6.1.5.5.7.3.2 */
387  { szOID_PKIX_KP_CLIENT_AUTH, IDS_PURPOSE_CLIENT_AUTH },
388  /* 1.3.6.1.5.5.7.3.3 */
389  { szOID_PKIX_KP_CODE_SIGNING, IDS_PURPOSE_CODE_SIGNING },
390  /* 1.3.6.1.5.5.7.3.4 */
391  { szOID_PKIX_KP_EMAIL_PROTECTION, IDS_PURPOSE_EMAIL_PROTECTION },
392  /* 1.3.6.1.5.5.7.3.5 */
393  { szOID_PKIX_KP_IPSEC_END_SYSTEM, IDS_PURPOSE_IPSEC },
394  /* 1.3.6.1.5.5.7.3.6 */
395  { szOID_PKIX_KP_IPSEC_TUNNEL, IDS_PURPOSE_IPSEC },
396  /* 1.3.6.1.5.5.7.3.7 */
397  { szOID_PKIX_KP_IPSEC_USER, IDS_PURPOSE_IPSEC },
398  /* 1.3.6.1.5.5.7.3.8 */
399  { szOID_PKIX_KP_TIMESTAMP_SIGNING, IDS_PURPOSE_TIMESTAMP_SIGNING },
400 };
401
402 static struct OIDToString *findSupportedOID(LPCSTR oid)
403 {
404     int indexHigh = sizeof(oidMap) / sizeof(oidMap[0]) - 1, indexLow = 0, i;
405     struct OIDToString *ret = NULL;
406
407     for (i = (indexLow + indexHigh) / 2; !ret && indexLow <= indexHigh;
408      i = (indexLow + indexHigh) / 2)
409     {
410         int cmp;
411
412         cmp = strcmp(oid, oidMap[i].oid);
413         if (!cmp)
414             ret = &oidMap[i];
415         else if (cmp > 0)
416             indexLow = i + 1;
417         else
418             indexHigh = i - 1;
419     }
420     return ret;
421 }
422
423 static void add_local_oid_text_to_control(HWND text, LPCSTR oid)
424 {
425     struct OIDToString *entry;
426     WCHAR nl = '\n';
427     PARAFORMAT2 parFmt;
428
429     parFmt.cbSize = sizeof(parFmt);
430     parFmt.dwMask = PFM_STARTINDENT;
431     parFmt.dxStartIndent = MY_INDENT * 3;
432     if ((entry = findSupportedOID(oid)))
433     {
434         WCHAR *str, *linebreak, *ptr;
435         BOOL multiline = FALSE;
436         int len;
437
438         len = LoadStringW(hInstance, entry->id, (LPWSTR)&str, 0);
439         ptr = str;
440         do {
441             if ((linebreak = memchrW(ptr, '\n', len)))
442             {
443                 WCHAR copy[MAX_STRING_LEN];
444
445                 multiline = TRUE;
446                 /* The source string contains a newline, which the richedit
447                  * control won't find since it's interpreted as a paragraph
448                  * break.  Therefore copy up to the newline.  lstrcpynW always
449                  * NULL-terminates, so pass one more than the length of the
450                  * source line so the copy includes the entire line and the
451                  * NULL-terminator.
452                  */
453                 lstrcpynW(copy, ptr, linebreak - ptr + 1);
454                 add_text_with_paraformat_to_control(text, copy,
455                  linebreak - ptr, &parFmt);
456                 ptr = linebreak + 1;
457                 add_unformatted_text_to_control(text, &nl, 1);
458             }
459             else if (multiline && *ptr)
460             {
461                 /* Add the last line */
462                 add_text_with_paraformat_to_control(text, ptr,
463                  len - (ptr - str), &parFmt);
464                 add_unformatted_text_to_control(text, &nl, 1);
465             }
466         } while (linebreak);
467         if (!multiline)
468         {
469             add_text_with_paraformat_to_control(text, str, len, &parFmt);
470             add_unformatted_text_to_control(text, &nl, 1);
471         }
472     }
473     else
474     {
475         WCHAR *oidW = HeapAlloc(GetProcessHeap(), 0,
476          (strlen(oid) + 1) * sizeof(WCHAR));
477
478         if (oidW)
479         {
480             LPCSTR src;
481             WCHAR *dst;
482
483             for (src = oid, dst = oidW; *src; src++, dst++)
484                 *dst = *src;
485             *dst = 0;
486             add_text_with_paraformat_to_control(text, oidW, lstrlenW(oidW),
487              &parFmt);
488             add_unformatted_text_to_control(text, &nl, 1);
489             HeapFree(GetProcessHeap(), 0, oidW);
490         }
491     }
492 }
493
494 static void display_app_usages(HWND text, PCCERT_CONTEXT cert,
495  BOOL *anyUsageAdded)
496 {
497     static char any_app_policy[] = szOID_ANY_APPLICATION_POLICY;
498     WCHAR nl = '\n';
499     CHARFORMATW charFmt;
500     PCERT_EXTENSION policyExt;
501     if (!*anyUsageAdded)
502     {
503         PARAFORMAT2 parFmt;
504
505         parFmt.cbSize = sizeof(parFmt);
506         parFmt.dwMask = PFM_STARTINDENT;
507         parFmt.dxStartIndent = MY_INDENT;
508         add_string_resource_with_paraformat_to_control(text,
509          IDS_CERT_INFO_PURPOSES, &parFmt);
510         add_unformatted_text_to_control(text, &nl, 1);
511         *anyUsageAdded = TRUE;
512     }
513     memset(&charFmt, 0, sizeof(charFmt));
514     charFmt.cbSize = sizeof(charFmt);
515     charFmt.dwMask = CFM_BOLD;
516     charFmt.dwEffects = 0;
517     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
518     if ((policyExt = CertFindExtension(szOID_APPLICATION_CERT_POLICIES,
519      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
520     {
521         CERT_POLICIES_INFO *policies;
522         DWORD size;
523
524         if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES,
525          policyExt->Value.pbData, policyExt->Value.cbData,
526          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
527         {
528             DWORD i;
529
530             for (i = 0; i < policies->cPolicyInfo; i++)
531             {
532                 DWORD j;
533
534                 for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
535                     add_local_oid_text_to_control(text,
536                      policies->rgPolicyInfo[i].rgPolicyQualifier[j].
537                      pszPolicyQualifierId);
538             }
539             LocalFree(policies);
540         }
541     }
542     else
543         add_oid_text_to_control(text, any_app_policy);
544 }
545
546 static BOOL display_cert_usages(HWND text, PCCERT_CONTEXT cert,
547  BOOL *anyUsageAdded)
548 {
549     WCHAR nl = '\n';
550     DWORD size;
551     BOOL badUsages = FALSE;
552
553     if (CertGetEnhancedKeyUsage(cert, 0, NULL, &size))
554     {
555         CHARFORMATW charFmt;
556         static char any_cert_policy[] = szOID_ANY_CERT_POLICY;
557         PCERT_ENHKEY_USAGE usage = HeapAlloc(GetProcessHeap(), 0, size);
558
559         if (usage)
560         {
561             if (CertGetEnhancedKeyUsage(cert, 0, usage, &size))
562             {
563                 DWORD i;
564
565                 if (!*anyUsageAdded)
566                 {
567                     PARAFORMAT2 parFmt;
568
569                     parFmt.cbSize = sizeof(parFmt);
570                     parFmt.dwMask = PFM_STARTINDENT;
571                     parFmt.dxStartIndent = MY_INDENT;
572                     add_string_resource_with_paraformat_to_control(text,
573                      IDS_CERT_INFO_PURPOSES, &parFmt);
574                     add_unformatted_text_to_control(text, &nl, 1);
575                     *anyUsageAdded = TRUE;
576                 }
577                 memset(&charFmt, 0, sizeof(charFmt));
578                 charFmt.cbSize = sizeof(charFmt);
579                 charFmt.dwMask = CFM_BOLD;
580                 charFmt.dwEffects = 0;
581                 SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION,
582                  (LPARAM)&charFmt);
583                 if (!usage->cUsageIdentifier)
584                     add_oid_text_to_control(text, any_cert_policy);
585                 else
586                     for (i = 0; i < usage->cUsageIdentifier; i++)
587                         add_local_oid_text_to_control(text,
588                          usage->rgpszUsageIdentifier[i]);
589             }
590             else
591                 badUsages = TRUE;
592             HeapFree(GetProcessHeap(), 0, usage);
593         }
594         else
595             badUsages = TRUE;
596     }
597     else
598         badUsages = TRUE;
599     return badUsages;
600 }
601
602 static void set_policy_text(HWND text,
603  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
604 {
605     BOOL includeCertUsages = FALSE, includeAppUsages = FALSE;
606     BOOL badUsages = FALSE, anyUsageAdded = FALSE;
607
608     if (pCertViewInfo->cPurposes)
609     {
610         DWORD i;
611
612         for (i = 0; i < pCertViewInfo->cPurposes; i++)
613         {
614             if (!strcmp(pCertViewInfo->rgszPurposes[i], szOID_ANY_CERT_POLICY))
615                 includeCertUsages = TRUE;
616             else if (!strcmp(pCertViewInfo->rgszPurposes[i],
617              szOID_ANY_APPLICATION_POLICY))
618                 includeAppUsages = TRUE;
619             else
620                 badUsages = TRUE;
621         }
622     }
623     else
624         includeAppUsages = includeCertUsages = TRUE;
625     if (includeAppUsages)
626         display_app_usages(text, pCertViewInfo->pCertContext, &anyUsageAdded);
627     if (includeCertUsages)
628         badUsages = display_cert_usages(text, pCertViewInfo->pCertContext,
629          &anyUsageAdded);
630     if (badUsages)
631     {
632         PARAFORMAT2 parFmt;
633
634         parFmt.cbSize = sizeof(parFmt);
635         parFmt.dwMask = PFM_STARTINDENT;
636         parFmt.dxStartIndent = MY_INDENT;
637         add_string_resource_with_paraformat_to_control(text,
638          IDS_CERT_INFO_BAD_PURPOSES, &parFmt);
639     }
640 }
641
642 static CRYPT_OBJID_BLOB *find_policy_qualifier(CERT_POLICIES_INFO *policies,
643  LPCSTR policyOid)
644 {
645     CRYPT_OBJID_BLOB *ret = NULL;
646     DWORD i;
647
648     for (i = 0; !ret && i < policies->cPolicyInfo; i++)
649     {
650         DWORD j;
651
652         for (j = 0; !ret && j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
653             if (!strcmp(policies->rgPolicyInfo[i].rgPolicyQualifier[j].
654              pszPolicyQualifierId, policyOid))
655                 ret = &policies->rgPolicyInfo[i].rgPolicyQualifier[j].
656                  Qualifier;
657     }
658     return ret;
659 }
660
661 static WCHAR *get_cps_str_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
662 {
663     LPWSTR qualifierStr = NULL;
664     CERT_NAME_VALUE *qualifierValue;
665     DWORD size;
666
667     if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_VALUE,
668      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
669      &qualifierValue, &size))
670     {
671         size = CertRDNValueToStrW(qualifierValue->dwValueType,
672          &qualifierValue->Value, NULL, 0);
673         qualifierStr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
674         if (qualifierStr)
675             CertRDNValueToStrW(qualifierValue->dwValueType,
676              &qualifierValue->Value, qualifierStr, size);
677         LocalFree(qualifierValue);
678     }
679     return qualifierStr;
680 }
681
682 static WCHAR *get_user_notice_from_qualifier(CRYPT_OBJID_BLOB *qualifier)
683 {
684     LPWSTR str = NULL;
685     CERT_POLICY_QUALIFIER_USER_NOTICE *qualifierValue;
686     DWORD size;
687
688     if (CryptDecodeObjectEx(X509_ASN_ENCODING,
689      X509_PKIX_POLICY_QUALIFIER_USERNOTICE,
690      qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
691      &qualifierValue, &size))
692     {
693         str = HeapAlloc(GetProcessHeap(), 0,
694          (strlenW(qualifierValue->pszDisplayText) + 1) * sizeof(WCHAR));
695         if (str)
696             strcpyW(str, qualifierValue->pszDisplayText);
697         LocalFree(qualifierValue);
698     }
699     return str;
700 }
701
702 struct IssuerStatement
703 {
704     LPWSTR cps;
705     LPWSTR userNotice;
706 };
707
708 static void set_issuer_statement(HWND hwnd,
709  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
710 {
711     PCERT_EXTENSION policyExt;
712
713     if (!(pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ISSUERSTATEMENT) &&
714      (policyExt = CertFindExtension(szOID_CERT_POLICIES,
715      pCertViewInfo->pCertContext->pCertInfo->cExtension,
716      pCertViewInfo->pCertContext->pCertInfo->rgExtension)))
717     {
718         CERT_POLICIES_INFO *policies;
719         DWORD size;
720
721         if (CryptDecodeObjectEx(X509_ASN_ENCODING, policyExt->pszObjId,
722          policyExt->Value.pbData, policyExt->Value.cbData,
723          CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
724         {
725             CRYPT_OBJID_BLOB *qualifier;
726             LPWSTR cps = NULL, userNotice = NULL;
727
728             if ((qualifier = find_policy_qualifier(policies,
729              szOID_PKIX_POLICY_QUALIFIER_CPS)))
730                 cps = get_cps_str_from_qualifier(qualifier);
731             if ((qualifier = find_policy_qualifier(policies,
732              szOID_PKIX_POLICY_QUALIFIER_USERNOTICE)))
733                 userNotice = get_user_notice_from_qualifier(qualifier);
734             if (cps || userNotice)
735             {
736                 struct IssuerStatement *issuerStatement =
737                  HeapAlloc(GetProcessHeap(), 0, sizeof(struct IssuerStatement));
738
739                 if (issuerStatement)
740                 {
741                     issuerStatement->cps = cps;
742                     issuerStatement->userNotice = userNotice;
743                     EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), TRUE);
744                     SetWindowLongPtrW(hwnd, DWLP_USER,
745                      (ULONG_PTR)issuerStatement);
746                 }
747             }
748             LocalFree(policies);
749         }
750     }
751 }
752
753 static void set_cert_info(HWND hwnd,
754  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
755 {
756     CHARFORMATW charFmt;
757     PARAFORMAT2 parFmt;
758     HWND icon = GetDlgItem(hwnd, IDC_CERTIFICATE_ICON);
759     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_INFO);
760     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
761      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
762      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
763      pCertViewInfo->idxCounterSigner);
764     CRYPT_PROVIDER_CERT *root =
765      &provSigner->pasCertChain[provSigner->csCertChain - 1];
766
767     if (!provSigner->pChainContext ||
768      (provSigner->pChainContext->TrustStatus.dwErrorStatus &
769      CERT_TRUST_IS_PARTIAL_CHAIN))
770         add_icon_to_control(icon, IDB_CERT_WARNING);
771     else if (!root->fTrustedRoot)
772         add_icon_to_control(icon, IDB_CERT_ERROR);
773     else
774         add_icon_to_control(icon, IDB_CERT);
775
776     memset(&charFmt, 0, sizeof(charFmt));
777     charFmt.cbSize = sizeof(charFmt);
778     charFmt.dwMask = CFM_BOLD;
779     charFmt.dwEffects = CFE_BOLD;
780     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
781     /* FIXME: vertically center text */
782     parFmt.cbSize = sizeof(parFmt);
783     parFmt.dwMask = PFM_STARTINDENT;
784     parFmt.dxStartIndent = MY_INDENT;
785     add_string_resource_with_paraformat_to_control(text,
786      IDS_CERTIFICATEINFORMATION, &parFmt);
787
788     text = GetDlgItem(hwnd, IDC_CERTIFICATE_STATUS);
789     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
790     if (provSigner->dwError == TRUST_E_CERT_SIGNATURE)
791         add_string_resource_with_paraformat_to_control(text,
792          IDS_CERT_INFO_BAD_SIG, &parFmt);
793     else if (!provSigner->pChainContext ||
794      (provSigner->pChainContext->TrustStatus.dwErrorStatus &
795      CERT_TRUST_IS_PARTIAL_CHAIN))
796         add_string_resource_with_paraformat_to_control(text,
797          IDS_CERT_INFO_PARTIAL_CHAIN, &parFmt);
798     else if (!root->fTrustedRoot)
799     {
800         if (provSigner->csCertChain == 1 && root->fSelfSigned)
801             add_string_resource_with_paraformat_to_control(text,
802              IDS_CERT_INFO_UNTRUSTED_CA, &parFmt);
803         else
804             add_string_resource_with_paraformat_to_control(text,
805              IDS_CERT_INFO_UNTRUSTED_ROOT, &parFmt);
806     }
807     else
808     {
809         set_policy_text(text, pCertViewInfo);
810         set_issuer_statement(hwnd, pCertViewInfo);
811     }
812 }
813
814 static void set_cert_name_string(HWND hwnd, PCCERT_CONTEXT cert,
815  DWORD nameFlags, int heading)
816 {
817     WCHAR nl = '\n';
818     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
819     CHARFORMATW charFmt;
820     PARAFORMAT2 parFmt;
821
822     memset(&charFmt, 0, sizeof(charFmt));
823     charFmt.cbSize = sizeof(charFmt);
824     charFmt.dwMask = CFM_BOLD;
825     charFmt.dwEffects = CFE_BOLD;
826     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
827     parFmt.cbSize = sizeof(parFmt);
828     parFmt.dwMask = PFM_STARTINDENT;
829     parFmt.dxStartIndent = MY_INDENT * 3;
830     add_string_resource_with_paraformat_to_control(text, heading, &parFmt);
831     charFmt.dwEffects = 0;
832     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
833     add_cert_string_to_control(text, cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
834      nameFlags);
835     add_unformatted_text_to_control(text, &nl, 1);
836     add_unformatted_text_to_control(text, &nl, 1);
837     add_unformatted_text_to_control(text, &nl, 1);
838
839 }
840
841 static void add_date_string_to_control(HWND hwnd, const FILETIME *fileTime)
842 {
843     WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */
844     WCHAR date[80];
845     SYSTEMTIME sysTime;
846
847     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt,
848      sizeof(dateFmt) / sizeof(dateFmt[0]));
849     FileTimeToSystemTime(fileTime, &sysTime);
850     GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date,
851      sizeof(date) / sizeof(date[0]));
852     add_unformatted_text_to_control(hwnd, date, lstrlenW(date));
853 }
854
855 static void set_cert_validity_period(HWND hwnd, PCCERT_CONTEXT cert)
856 {
857     WCHAR nl = '\n';
858     HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES);
859     CHARFORMATW charFmt;
860     PARAFORMAT2 parFmt;
861
862     memset(&charFmt, 0, sizeof(charFmt));
863     charFmt.cbSize = sizeof(charFmt);
864     charFmt.dwMask = CFM_BOLD;
865     charFmt.dwEffects = CFE_BOLD;
866     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
867     parFmt.cbSize = sizeof(parFmt);
868     parFmt.dwMask = PFM_STARTINDENT;
869     parFmt.dxStartIndent = MY_INDENT * 3;
870     add_string_resource_with_paraformat_to_control(text, IDS_VALID_FROM,
871      &parFmt);
872     charFmt.dwEffects = 0;
873     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
874     add_date_string_to_control(text, &cert->pCertInfo->NotBefore);
875     charFmt.dwEffects = CFE_BOLD;
876     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
877     add_string_resource_to_control(text, IDS_VALID_TO);
878     charFmt.dwEffects = 0;
879     SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt);
880     add_date_string_to_control(text, &cert->pCertInfo->NotAfter);
881     add_unformatted_text_to_control(text, &nl, 1);
882 }
883
884 static void set_general_info(HWND hwnd,
885  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
886 {
887     set_cert_info(hwnd, pCertViewInfo);
888     set_cert_name_string(hwnd, pCertViewInfo->pCertContext, 0,
889      IDS_SUBJECT_HEADING);
890     set_cert_name_string(hwnd, pCertViewInfo->pCertContext,
891      CERT_NAME_ISSUER_FLAG, IDS_ISSUER_HEADING);
892     set_cert_validity_period(hwnd, pCertViewInfo->pCertContext);
893 }
894
895 static LRESULT CALLBACK user_notice_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
896  LPARAM lp)
897 {
898     LRESULT ret = 0;
899     HWND text;
900     struct IssuerStatement *issuerStatement;
901
902     switch (msg)
903     {
904     case WM_INITDIALOG:
905         text = GetDlgItem(hwnd, IDC_USERNOTICE);
906         issuerStatement = (struct IssuerStatement *)lp;
907         add_unformatted_text_to_control(text, issuerStatement->userNotice,
908          strlenW(issuerStatement->userNotice));
909         if (issuerStatement->cps)
910             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)issuerStatement->cps);
911         else
912             EnableWindow(GetDlgItem(hwnd, IDC_CPS), FALSE);
913         break;
914     case WM_COMMAND:
915         switch (wp)
916         {
917         case IDOK:
918             EndDialog(hwnd, IDOK);
919             ret = TRUE;
920             break;
921         case IDC_CPS:
922         {
923             IBindCtx *bctx = NULL;
924             LPWSTR cps;
925
926             CreateBindCtx(0, &bctx);
927             cps = (LPWSTR)GetWindowLongPtrW(hwnd, DWLP_USER);
928             HlinkSimpleNavigateToString(cps, NULL, NULL, NULL, bctx, NULL,
929              HLNF_OPENINNEWWINDOW, 0);
930             IBindCtx_Release(bctx);
931             break;
932         }
933         }
934     }
935     return ret;
936 }
937
938 static void show_user_notice(HWND hwnd, struct IssuerStatement *issuerStatement)
939 {
940     DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_USERNOTICE), hwnd,
941      user_notice_dlg_proc, (LPARAM)issuerStatement);
942 }
943
944 static LRESULT CALLBACK general_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
945  LPARAM lp)
946 {
947     PROPSHEETPAGEW *page;
948     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
949
950     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
951
952     switch (msg)
953     {
954     case WM_INITDIALOG:
955         page = (PROPSHEETPAGEW *)lp;
956         pCertViewInfo = (PCCRYPTUI_VIEWCERTIFICATE_STRUCTW)page->lParam;
957         if (pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ADDTOSTORE)
958             ShowWindow(GetDlgItem(hwnd, IDC_ADDTOSTORE), FALSE);
959         EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), FALSE);
960         set_general_info(hwnd, pCertViewInfo);
961         break;
962     case WM_COMMAND:
963         switch (wp)
964         {
965         case IDC_ADDTOSTORE:
966             FIXME("call CryptUIWizImport\n");
967             break;
968         case IDC_ISSUERSTATEMENT:
969         {
970             struct IssuerStatement *issuerStatement =
971              (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
972
973             if (issuerStatement)
974             {
975                 if (issuerStatement->userNotice)
976                     show_user_notice(hwnd, issuerStatement);
977                 else if (issuerStatement->cps)
978                 {
979                     IBindCtx *bctx = NULL;
980
981                     CreateBindCtx(0, &bctx);
982                     HlinkSimpleNavigateToString(issuerStatement->cps, NULL,
983                      NULL, NULL, bctx, NULL, HLNF_OPENINNEWWINDOW, 0);
984                     IBindCtx_Release(bctx);
985                 }
986             }
987             break;
988         }
989         }
990         break;
991     }
992     return 0;
993 }
994
995 static UINT CALLBACK general_callback_proc(HWND hwnd, UINT msg,
996  PROPSHEETPAGEW *page)
997 {
998     struct IssuerStatement *issuerStatement;
999
1000     switch (msg)
1001     {
1002     case PSPCB_RELEASE:
1003         issuerStatement =
1004          (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER);
1005         if (issuerStatement)
1006         {
1007             HeapFree(GetProcessHeap(), 0, issuerStatement->cps);
1008             HeapFree(GetProcessHeap(), 0, issuerStatement->userNotice);
1009             HeapFree(GetProcessHeap(), 0, issuerStatement);
1010         }
1011         break;
1012     }
1013     return 1;
1014 }
1015
1016 static void init_general_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
1017  PROPSHEETPAGEW *page)
1018 {
1019     memset(page, 0, sizeof(PROPSHEETPAGEW));
1020     page->dwSize = sizeof(PROPSHEETPAGEW);
1021     page->dwFlags = PSP_USECALLBACK;
1022     page->pfnCallback = general_callback_proc;
1023     page->hInstance = hInstance;
1024     page->u.pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL);
1025     page->pfnDlgProc = general_dlg_proc;
1026     page->lParam = (LPARAM)pCertViewInfo;
1027 }
1028
1029 typedef WCHAR * (*field_format_func)(PCCERT_CONTEXT cert);
1030
1031 static WCHAR *field_format_version(PCCERT_CONTEXT cert)
1032 {
1033     static const WCHAR fmt[] = { 'V','%','d',0 };
1034     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, 12 * sizeof(WCHAR));
1035
1036     if (buf)
1037         sprintfW(buf, fmt, cert->pCertInfo->dwVersion);
1038     return buf;
1039 }
1040
1041 static WCHAR *format_hex_string(void *pb, DWORD cb)
1042 {
1043     WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, (cb * 3 + 1) * sizeof(WCHAR));
1044
1045     if (buf)
1046     {
1047         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
1048         DWORD i;
1049         WCHAR *ptr;
1050
1051         for (i = 0, ptr = buf; i < cb; i++, ptr += 3)
1052             sprintfW(ptr, fmt, ((BYTE *)pb)[i]);
1053     }
1054     return buf;
1055 }
1056
1057 static WCHAR *field_format_serial_number(PCCERT_CONTEXT cert)
1058 {
1059     return format_hex_string(cert->pCertInfo->SerialNumber.pbData,
1060      cert->pCertInfo->SerialNumber.cbData);
1061 }
1062
1063 static WCHAR *field_format_issuer(PCCERT_CONTEXT cert)
1064 {
1065     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
1066      CERT_NAME_ISSUER_FLAG);
1067 }
1068
1069 static WCHAR *field_format_detailed_cert_name(PCERT_NAME_BLOB name)
1070 {
1071     WCHAR *str = NULL;
1072     DWORD len = CertNameToStrW(X509_ASN_ENCODING, name,
1073      CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, NULL, 0);
1074
1075     if (len)
1076     {
1077         str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1078         if (str)
1079             CertNameToStrW(X509_ASN_ENCODING, name,
1080              CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, str, len);
1081     }
1082     return str;
1083 }
1084
1085 static WCHAR *field_format_detailed_issuer(PCCERT_CONTEXT cert, void *param)
1086 {
1087     return field_format_detailed_cert_name(&cert->pCertInfo->Issuer);
1088 }
1089
1090 static WCHAR *field_format_subject(PCCERT_CONTEXT cert)
1091 {
1092     return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
1093 }
1094
1095 static WCHAR *field_format_detailed_subject(PCCERT_CONTEXT cert, void *param)
1096 {
1097     return field_format_detailed_cert_name(&cert->pCertInfo->Subject);
1098 }
1099
1100 static WCHAR *format_long_date(const FILETIME *fileTime)
1101 {
1102     WCHAR dateFmt[80]; /* long enough for LOCALE_SLONGDATE */
1103     DWORD len;
1104     WCHAR *buf = NULL;
1105     SYSTEMTIME sysTime;
1106
1107     /* FIXME: format isn't quite right, want time too */
1108     GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SLONGDATE, dateFmt,
1109      sizeof(dateFmt) / sizeof(dateFmt[0]));
1110     FileTimeToSystemTime(fileTime, &sysTime);
1111     len = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, NULL, 0);
1112     if (len)
1113     {
1114         buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1115         if (buf)
1116             GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, buf,
1117              len);
1118     }
1119     return buf;
1120 }
1121
1122 static WCHAR *field_format_from_date(PCCERT_CONTEXT cert)
1123 {
1124     return format_long_date(&cert->pCertInfo->NotBefore);
1125 }
1126
1127 static WCHAR *field_format_to_date(PCCERT_CONTEXT cert)
1128 {
1129     return format_long_date(&cert->pCertInfo->NotAfter);
1130 }
1131
1132 static WCHAR *field_format_public_key(PCCERT_CONTEXT cert)
1133 {
1134     PCCRYPT_OID_INFO oidInfo;
1135     WCHAR *buf = NULL;
1136
1137     oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1138      cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 0);
1139     if (oidInfo)
1140     {
1141         WCHAR fmt[MAX_STRING_LEN];
1142
1143         if (LoadStringW(hInstance, IDS_FIELD_PUBLIC_KEY_FORMAT, fmt,
1144          sizeof(fmt) / sizeof(fmt[0])))
1145         {
1146             /* Allocate the output buffer.  Use the number of bytes in the
1147              * public key as a conservative (high) estimate for the number of
1148              * digits in its output.
1149              * The output is of the form (in English)
1150              * "<public key algorithm> (<public key bit length> bits)".
1151              * Ordinarily having two positional parameters in a string is not a
1152              * good idea, but as this isn't a sentence fragment, it shouldn't
1153              * be word-order dependent.
1154              */
1155             buf = HeapAlloc(GetProcessHeap(), 0,
1156              (strlenW(fmt) + strlenW(oidInfo->pwszName) +
1157              cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData * 8)
1158              * sizeof(WCHAR));
1159             if (buf)
1160                 sprintfW(buf, fmt, oidInfo->pwszName,
1161                  CertGetPublicKeyLength(X509_ASN_ENCODING,
1162                   &cert->pCertInfo->SubjectPublicKeyInfo));
1163         }
1164     }
1165     return buf;
1166 }
1167
1168 static WCHAR *field_format_detailed_public_key(PCCERT_CONTEXT cert, void *param)
1169 {
1170     return format_hex_string(
1171      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
1172      cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData);
1173 }
1174
1175 struct field_value_data;
1176 struct detail_data
1177 {
1178     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
1179     BOOL *pfPropertiesChanged;
1180     int cFields;
1181     struct field_value_data *fields;
1182 };
1183
1184 typedef void (*add_fields_func)(HWND hwnd, struct detail_data *data);
1185
1186 typedef WCHAR *(*create_detailed_value_func)(PCCERT_CONTEXT cert, void *param);
1187
1188 struct field_value_data
1189 {
1190     create_detailed_value_func create;
1191     LPWSTR detailed_value;
1192     void *param;
1193 };
1194
1195 static void add_field_value_data(struct detail_data *data,
1196  create_detailed_value_func create, void *param)
1197 {
1198     if (data->cFields)
1199         data->fields = HeapReAlloc(GetProcessHeap(), 0, data->fields,
1200          (data->cFields + 1) * sizeof(struct field_value_data));
1201     else
1202         data->fields = HeapAlloc(GetProcessHeap(), 0,
1203          sizeof(struct field_value_data));
1204     if (data->fields)
1205     {
1206         data->fields[data->cFields].create = create;
1207         data->fields[data->cFields].detailed_value = NULL;
1208         data->fields[data->cFields].param = param;
1209         data->cFields++;
1210     }
1211 }
1212
1213 static void add_field_and_value_to_list(HWND hwnd, struct detail_data *data,
1214  LPWSTR field, LPWSTR value, create_detailed_value_func create, void *param)
1215 {
1216     LVITEMW item;
1217     int iItem = SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0);
1218
1219     item.mask = LVIF_TEXT | LVIF_PARAM;
1220     item.iItem = iItem;
1221     item.iSubItem = 0;
1222     item.pszText = field;
1223     item.lParam = (LPARAM)data;
1224     SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item);
1225     if (value)
1226     {
1227         item.pszText = value;
1228         item.iSubItem = 1;
1229         SendMessageW(hwnd, LVM_SETITEMTEXTW, iItem, (LPARAM)&item);
1230     }
1231     add_field_value_data(data, create, param);
1232 }
1233
1234 static void add_string_id_and_value_to_list(HWND hwnd, struct detail_data *data,
1235  int id, LPWSTR value, create_detailed_value_func create, void *param)
1236 {
1237     WCHAR buf[MAX_STRING_LEN];
1238
1239     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
1240     add_field_and_value_to_list(hwnd, data, buf, value, create, param);
1241 }
1242
1243 struct v1_field
1244 {
1245     int id;
1246     field_format_func format;
1247     create_detailed_value_func create_detailed_value;
1248 };
1249
1250 static void add_v1_field(HWND hwnd, struct detail_data *data,
1251  const struct v1_field *field)
1252 {
1253     WCHAR *val = field->format(data->pCertViewInfo->pCertContext);
1254
1255     if (val)
1256     {
1257         add_string_id_and_value_to_list(hwnd, data, field->id, val,
1258          field->create_detailed_value, NULL);
1259         HeapFree(GetProcessHeap(), 0, val);
1260     }
1261 }
1262
1263 static const struct v1_field v1_fields[] = {
1264  { IDS_FIELD_VERSION, field_format_version, NULL },
1265  { IDS_FIELD_SERIAL_NUMBER, field_format_serial_number, NULL },
1266  { IDS_FIELD_ISSUER, field_format_issuer, field_format_detailed_issuer },
1267  { IDS_FIELD_VALID_FROM, field_format_from_date, NULL },
1268  { IDS_FIELD_VALID_TO, field_format_to_date, NULL },
1269  { IDS_FIELD_SUBJECT, field_format_subject, field_format_detailed_subject },
1270  { IDS_FIELD_PUBLIC_KEY, field_format_public_key,
1271    field_format_detailed_public_key }
1272 };
1273
1274 static void add_v1_fields(HWND hwnd, struct detail_data *data)
1275 {
1276     int i;
1277     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1278
1279     /* The last item in v1_fields is the public key, which is not in the loop
1280      * because it's a special case.
1281      */
1282     for (i = 0; i < sizeof(v1_fields) / sizeof(v1_fields[0]) - 1; i++)
1283         add_v1_field(hwnd, data, &v1_fields[i]);
1284     if (cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData)
1285         add_v1_field(hwnd, data, &v1_fields[i]);
1286 }
1287
1288 static WCHAR *crypt_format_extension(PCERT_EXTENSION ext, DWORD formatStrType)
1289 {
1290     WCHAR *str = NULL;
1291     DWORD size;
1292
1293     if (CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
1294      ext->pszObjId, ext->Value.pbData, ext->Value.cbData, NULL, &size))
1295     {
1296         str = HeapAlloc(GetProcessHeap(), 0, size);
1297         CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
1298          ext->pszObjId, ext->Value.pbData, ext->Value.cbData, str, &size);
1299     }
1300     return str;
1301 }
1302
1303 static WCHAR *field_format_extension_hex_with_ascii(PCERT_EXTENSION ext)
1304 {
1305     WCHAR *str = NULL;
1306
1307     if (ext->Value.cbData)
1308     {
1309         /* The output is formatted as:
1310          * <hex bytes>  <ascii bytes>\n
1311          * where <hex bytes> is a string of up to 8 bytes, output as %02x,
1312          * and <ascii bytes> is the ASCII equivalent of each byte, or '.' if
1313          * the byte is not printable.
1314          * So, for example, the extension value consisting of the following
1315          * bytes:
1316          *   0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
1317          *   0x13,0x09,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67
1318          * is output as:
1319          *   30 14 31 12 30 10 06 03  0.1.0...
1320          *   55 04 03 13 09 4a 75 61  U....Jua
1321          *   6e 20 4c 61 6e 67        n Lang
1322          * The allocation size therefore requires:
1323          * - 4 characters per character in an 8-byte line
1324          *   (2 for the hex format, one for the space, one for the ASCII value)
1325          * - 3 more characters per 8-byte line (two spaces and a newline)
1326          * - 1 character for the terminating nul
1327          * FIXME: should use a fixed-width font for this
1328          */
1329         DWORD lines = (ext->Value.cbData + 7) / 8;
1330
1331         str = HeapAlloc(GetProcessHeap(), 0,
1332          (lines * 8 * 4 + lines * 3 + 1) * sizeof(WCHAR));
1333         if (str)
1334         {
1335             static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
1336             DWORD i, j;
1337             WCHAR *ptr;
1338
1339             for (i = 0, ptr = str; i < ext->Value.cbData; i += 8)
1340             {
1341                 /* Output as hex bytes first */
1342                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr += 3)
1343                     sprintfW(ptr, fmt, ext->Value.pbData[j]);
1344                 /* Pad the hex output with spaces for alignment */
1345                 if (j == ext->Value.cbData && j % 8)
1346                 {
1347                     static const WCHAR pad[] = { ' ',' ',' ' };
1348
1349                     for (; j % 8; j++, ptr += sizeof(pad) / sizeof(pad[0]))
1350                         memcpy(ptr, pad, sizeof(pad));
1351                 }
1352                 /* The last sprintfW included a space, so just insert one
1353                  * more space between the hex bytes and the ASCII output
1354                  */
1355                 *ptr++ = ' ';
1356                 /* Output as ASCII bytes */
1357                 for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr++)
1358                 {
1359                     if (isprintW(ext->Value.pbData[j]) &&
1360                      !isspaceW(ext->Value.pbData[j]))
1361                         *ptr = ext->Value.pbData[j];
1362                     else
1363                         *ptr = '.';
1364                 }
1365                 *ptr++ = '\n';
1366             }
1367             *ptr++ = '\0';
1368         }
1369     }
1370     return str;
1371 }
1372
1373 static WCHAR *field_format_detailed_extension(PCCERT_CONTEXT cert, void *param)
1374 {
1375     PCERT_EXTENSION ext = param;
1376     LPWSTR str = crypt_format_extension(ext,
1377      CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX);
1378
1379     if (!str)
1380         str = field_format_extension_hex_with_ascii(ext);
1381     return str;
1382 }
1383
1384 static void add_cert_extension_detail(HWND hwnd, struct detail_data *data,
1385  PCERT_EXTENSION ext)
1386 {
1387     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1388      ext->pszObjId, 0);
1389     LPWSTR val = crypt_format_extension(ext, 0);
1390
1391     if (oidInfo)
1392         add_field_and_value_to_list(hwnd, data, (LPWSTR)oidInfo->pwszName,
1393          val, field_format_detailed_extension, ext);
1394     else
1395     {
1396         DWORD len = strlen(ext->pszObjId);
1397         LPWSTR oidW = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1398
1399         if (oidW)
1400         {
1401             DWORD i;
1402
1403             for (i = 0; i <= len; i++)
1404                 oidW[i] = ext->pszObjId[i];
1405             add_field_and_value_to_list(hwnd, data, oidW, val,
1406              field_format_detailed_extension, ext);
1407             HeapFree(GetProcessHeap(), 0, oidW);
1408         }
1409     }
1410     HeapFree(GetProcessHeap(), 0, val);
1411 }
1412
1413 static void add_all_extensions(HWND hwnd, struct detail_data *data)
1414 {
1415     DWORD i;
1416     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1417
1418     for (i = 0; i < cert->pCertInfo->cExtension; i++)
1419         add_cert_extension_detail(hwnd, data, &cert->pCertInfo->rgExtension[i]);
1420 }
1421
1422 static void add_critical_extensions(HWND hwnd, struct detail_data *data)
1423 {
1424     DWORD i;
1425     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1426
1427     for (i = 0; i < cert->pCertInfo->cExtension; i++)
1428         if (cert->pCertInfo->rgExtension[i].fCritical)
1429             add_cert_extension_detail(hwnd, data,
1430              &cert->pCertInfo->rgExtension[i]);
1431 }
1432
1433 typedef WCHAR * (*prop_to_value_func)(void *pb, DWORD cb);
1434
1435 struct prop_id_to_string_id
1436 {
1437     DWORD prop;
1438     int id;
1439     BOOL prop_is_string;
1440     prop_to_value_func prop_to_value;
1441 };
1442
1443 static WCHAR *format_enhanced_key_usage_value(void *pb, DWORD cb)
1444 {
1445     CERT_EXTENSION ext;
1446
1447     ext.pszObjId = (LPSTR)X509_ENHANCED_KEY_USAGE;
1448     ext.fCritical = FALSE;
1449     ext.Value.pbData = pb;
1450     ext.Value.cbData = cb;
1451     return crypt_format_extension(&ext, 0);
1452 }
1453
1454 /* Logically the access state should also be checked, and IDC_EDITPROPERTIES
1455  * disabled for read-only certificates, but native doesn't appear to do that.
1456  */
1457 static const struct prop_id_to_string_id prop_id_map[] = {
1458  { CERT_HASH_PROP_ID, IDS_PROP_HASH, FALSE, format_hex_string },
1459  { CERT_FRIENDLY_NAME_PROP_ID, IDS_PROP_FRIENDLY_NAME, TRUE, NULL },
1460  { CERT_DESCRIPTION_PROP_ID, IDS_PROP_DESCRIPTION, TRUE, NULL },
1461  { CERT_ENHKEY_USAGE_PROP_ID, IDS_PROP_ENHKEY_USAGE, FALSE,
1462    format_enhanced_key_usage_value },
1463 };
1464
1465 static void add_properties(HWND hwnd, struct detail_data *data)
1466 {
1467     DWORD i;
1468     PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
1469
1470     for (i = 0; i < sizeof(prop_id_map) / sizeof(prop_id_map[0]); i++)
1471     {
1472         DWORD cb;
1473
1474         if (CertGetCertificateContextProperty(cert, prop_id_map[i].prop, NULL,
1475          &cb))
1476         {
1477             BYTE *pb;
1478             WCHAR *val = NULL;
1479
1480             /* FIXME: MS adds a separate value for the signature hash
1481              * algorithm.
1482              */
1483             pb = HeapAlloc(GetProcessHeap(), 0, cb);
1484             if (pb)
1485             {
1486                 if (CertGetCertificateContextProperty(cert,
1487                  prop_id_map[i].prop, pb, &cb))
1488                 {
1489                     if (prop_id_map[i].prop_is_string)
1490                     {
1491                         val = (LPWSTR)pb;
1492                         /* Don't double-free pb */
1493                         pb = NULL;
1494                     }
1495                     else
1496                         val = prop_id_map[i].prop_to_value(pb, cb);
1497                 }
1498                 HeapFree(GetProcessHeap(), 0, pb);
1499             }
1500             add_string_id_and_value_to_list(hwnd, data, prop_id_map[i].id, val,
1501              NULL, NULL);
1502         }
1503     }
1504 }
1505
1506 static void add_all_fields(HWND hwnd, struct detail_data *data)
1507 {
1508     add_v1_fields(hwnd, data);
1509     add_all_extensions(hwnd, data);
1510     add_properties(hwnd, data);
1511 }
1512
1513 struct selection_list_item
1514 {
1515     int id;
1516     add_fields_func add;
1517 };
1518
1519 const struct selection_list_item listItems[] = {
1520  { IDS_FIELDS_ALL, add_all_fields },
1521  { IDS_FIELDS_V1, add_v1_fields },
1522  { IDS_FIELDS_EXTENSIONS, add_all_extensions },
1523  { IDS_FIELDS_CRITICAL_EXTENSIONS, add_critical_extensions },
1524  { IDS_FIELDS_PROPERTIES, add_properties },
1525 };
1526
1527 static void create_show_list(HWND hwnd, struct detail_data *data)
1528 {
1529     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
1530     WCHAR buf[MAX_STRING_LEN];
1531     int i;
1532
1533     for (i = 0; i < sizeof(listItems) / sizeof(listItems[0]); i++)
1534     {
1535         int index;
1536
1537         LoadStringW(hInstance, listItems[i].id, buf,
1538          sizeof(buf) / sizeof(buf[0]));
1539         index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf);
1540         SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)data);
1541     }
1542     SendMessageW(cb, CB_SETCURSEL, 0, 0);
1543 }
1544
1545 static void create_listview_columns(HWND hwnd)
1546 {
1547     HWND lv = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1548     RECT rc;
1549     WCHAR buf[MAX_STRING_LEN];
1550     LVCOLUMNW column;
1551
1552     SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
1553     GetWindowRect(lv, &rc);
1554     LoadStringW(hInstance, IDS_FIELD, buf, sizeof(buf) / sizeof(buf[0]));
1555     column.mask = LVCF_WIDTH | LVCF_TEXT;
1556     column.cx = (rc.right - rc.left) / 2 - 2;
1557     column.pszText = buf;
1558     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
1559     LoadStringW(hInstance, IDS_VALUE, buf, sizeof(buf) / sizeof(buf[0]));
1560     SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column);
1561 }
1562
1563 static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel)
1564 {
1565     HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
1566
1567     if (sel >= 0 && sel < sizeof(listItems) / sizeof(listItems[0]))
1568     {
1569         SendMessageW(list, LVM_DELETEALLITEMS, 0, 0);
1570         listItems[sel].add(list, data);
1571     }
1572 }
1573
1574 static void create_cert_details_list(HWND hwnd, struct detail_data *data)
1575 {
1576     create_show_list(hwnd, data);
1577     create_listview_columns(hwnd);
1578     set_fields_selection(hwnd, data, 0);
1579 }
1580
1581 typedef enum {
1582     CheckBitmapIndexUnchecked = 1,
1583     CheckBitmapIndexChecked = 2,
1584     CheckBitmapIndexDisabledUnchecked = 3,
1585     CheckBitmapIndexDisabledChecked = 4
1586 } CheckBitmapIndex;
1587
1588 static void add_purpose(HWND hwnd, LPCSTR oid)
1589 {
1590     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
1591     PCRYPT_OID_INFO info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1592      sizeof(CRYPT_OID_INFO));
1593
1594     if (info)
1595     {
1596         char *oidCopy = HeapAlloc(GetProcessHeap(), 0, strlen(oid) + 1);
1597
1598         if (oidCopy)
1599         {
1600             LVITEMA item;
1601
1602             strcpy(oidCopy, oid);
1603             info->cbSize = sizeof(CRYPT_OID_INFO);
1604             info->pszOID = oidCopy;
1605             item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
1606             item.state = INDEXTOSTATEIMAGEMASK(CheckBitmapIndexChecked);
1607             item.stateMask = LVIS_STATEIMAGEMASK;
1608             item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
1609             item.iSubItem = 0;
1610             item.lParam = (LPARAM)info;
1611             item.pszText = oidCopy;
1612             SendMessageA(lv, LVM_INSERTITEMA, 0, (LPARAM)&item);
1613         }
1614         else
1615             HeapFree(GetProcessHeap(), 0, info);
1616     }
1617 }
1618
1619 static BOOL is_valid_oid(LPCSTR oid)
1620 {
1621     BOOL ret;
1622
1623     if (oid[0] != '0' && oid[0] != '1' && oid[0] != '2')
1624         ret = FALSE;
1625     else if (oid[1] != '.')
1626         ret = FALSE;
1627     else if (!oid[2])
1628         ret = FALSE;
1629     else
1630     {
1631         const char *ptr;
1632         BOOL expectNum = TRUE;
1633
1634         for (ptr = oid + 2, ret = TRUE; ret && *ptr; ptr++)
1635         {
1636             if (expectNum)
1637             {
1638                 if (!isdigit(*ptr))
1639                     ret = FALSE;
1640                 else if (*(ptr + 1) == '.')
1641                     expectNum = FALSE;
1642             }
1643             else
1644             {
1645                 if (*ptr != '.')
1646                     ret = FALSE;
1647                 else if (!(*(ptr + 1)))
1648                     ret = FALSE;
1649                 else
1650                     expectNum = TRUE;
1651             }
1652         }
1653     }
1654     return ret;
1655 }
1656
1657 static BOOL is_oid_in_list(HWND hwnd, LPCSTR oid)
1658 {
1659     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
1660     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1661      (void *)oid, CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
1662     BOOL ret = FALSE;
1663
1664     if (oidInfo)
1665     {
1666         LVFINDINFOW findInfo;
1667
1668         findInfo.flags = LVFI_PARAM;
1669         findInfo.lParam = (LPARAM)oidInfo;
1670         if (SendMessageW(lv, LVM_FINDITEMW, -1, (LPARAM)&findInfo) != -1)
1671             ret = TRUE;
1672     }
1673     else
1674     {
1675         LVFINDINFOA findInfo;
1676
1677         findInfo.flags = LVFI_STRING;
1678         findInfo.psz = oid;
1679         if (SendMessageW(lv, LVM_FINDITEMA, -1, (LPARAM)&findInfo) != -1)
1680             ret = TRUE;
1681     }
1682     return ret;
1683 }
1684
1685 #define MAX_PURPOSE 255
1686
1687 static LRESULT CALLBACK add_purpose_dlg_proc(HWND hwnd, UINT msg,
1688  WPARAM wp, LPARAM lp)
1689 {
1690     LRESULT ret = 0;
1691     char buf[MAX_PURPOSE + 1];
1692
1693     switch (msg)
1694     {
1695     case WM_INITDIALOG:
1696         SendMessageW(GetDlgItem(hwnd, IDC_NEW_PURPOSE), EM_SETLIMITTEXT,
1697          MAX_PURPOSE, 0);
1698         ShowScrollBar(GetDlgItem(hwnd, IDC_NEW_PURPOSE), SB_VERT, FALSE);
1699         SetWindowLongPtrW(hwnd, DWLP_USER, lp);
1700         break;
1701     case WM_COMMAND:
1702         switch (HIWORD(wp))
1703         {
1704         case EN_CHANGE:
1705             if (LOWORD(wp) == IDC_NEW_PURPOSE)
1706             {
1707                 /* Show/hide scroll bar on description depending on how much
1708                  * text it has.
1709                  */
1710                 HWND description = GetDlgItem(hwnd, IDC_NEW_PURPOSE);
1711                 int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0);
1712
1713                 ShowScrollBar(description, SB_VERT, lines > 1);
1714             }
1715             break;
1716         case BN_CLICKED:
1717             switch (LOWORD(wp))
1718             {
1719             case IDOK:
1720                 SendMessageA(GetDlgItem(hwnd, IDC_NEW_PURPOSE), WM_GETTEXT,
1721                  sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
1722                 if (!buf[0])
1723                 {
1724                     /* An empty purpose is the same as cancelling */
1725                     EndDialog(hwnd, IDCANCEL);
1726                     ret = TRUE;
1727                 }
1728                 else if (!is_valid_oid(buf))
1729                 {
1730                     WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
1731
1732                     LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_ERROR, error,
1733                      sizeof(error) / sizeof(error[0]));
1734                     LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title,
1735                      sizeof(title) / sizeof(title[0]));
1736                     MessageBoxW(hwnd, error, title, MB_ICONERROR | MB_OK);
1737                 }
1738                 else if (is_oid_in_list(
1739                  (HWND)GetWindowLongPtrW(hwnd, DWLP_USER), buf))
1740                 {
1741                     WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN];
1742
1743                     LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_EXISTS,
1744                      error, sizeof(error) / sizeof(error[0]));
1745                     LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title,
1746                      sizeof(title) / sizeof(title[0]));
1747                     MessageBoxW(hwnd, error, title, MB_ICONEXCLAMATION | MB_OK);
1748                 }
1749                 else
1750                 {
1751                     HWND parent = (HWND)GetWindowLongPtrW(hwnd, DWLP_USER);
1752
1753                     add_purpose(parent, buf);
1754                     EndDialog(hwnd, wp);
1755                     ret = TRUE;
1756                 }
1757                 break;
1758             case IDCANCEL:
1759                 EndDialog(hwnd, wp);
1760                 ret = TRUE;
1761                 break;
1762             }
1763             break;
1764         }
1765         break;
1766     }
1767     return ret;
1768 }
1769
1770 static WCHAR *get_cert_property_as_string(PCCERT_CONTEXT cert, DWORD prop)
1771 {
1772     WCHAR *name = NULL;
1773     DWORD cb;
1774
1775     if (CertGetCertificateContextProperty(cert, prop, NULL, &cb))
1776     {
1777         name = HeapAlloc(GetProcessHeap(), 0, cb);
1778         if (name)
1779         {
1780             if (!CertGetCertificateContextProperty(cert, prop, (LPBYTE)name,
1781              &cb))
1782             {
1783                 HeapFree(GetProcessHeap(), 0, name);
1784                 name = NULL;
1785             }
1786         }
1787     }
1788     return name;
1789 }
1790
1791 static void redraw_states(HWND list, BOOL enabled)
1792 {
1793     int items = SendMessageW(list, LVM_GETITEMCOUNT, 0, 0), i;
1794
1795     for (i = 0; i < items; i++)
1796     {
1797         BOOL change = FALSE;
1798         int state;
1799
1800         state = SendMessageW(list, LVM_GETITEMSTATE, i, LVIS_STATEIMAGEMASK);
1801         /* This reverses the INDEXTOSTATEIMAGEMASK shift.  There doesn't appear
1802          * to be a handy macro for it.
1803          */
1804         state >>= 12;
1805         if (enabled)
1806         {
1807             if (state == CheckBitmapIndexDisabledChecked)
1808             {
1809                 state = CheckBitmapIndexChecked;
1810                 change = TRUE;
1811             }
1812             if (state == CheckBitmapIndexDisabledUnchecked)
1813             {
1814                 state = CheckBitmapIndexUnchecked;
1815                 change = TRUE;
1816             }
1817         }
1818         else
1819         {
1820             if (state == CheckBitmapIndexChecked)
1821             {
1822                 state = CheckBitmapIndexDisabledChecked;
1823                 change = TRUE;
1824             }
1825             if (state == CheckBitmapIndexUnchecked)
1826             {
1827                 state = CheckBitmapIndexDisabledUnchecked;
1828                 change = TRUE;
1829             }
1830         }
1831         if (change)
1832         {
1833             LVITEMW item;
1834
1835             item.state = INDEXTOSTATEIMAGEMASK(state);
1836             item.stateMask = LVIS_STATEIMAGEMASK;
1837             SendMessageW(list, LVM_SETITEMSTATE, i, (LPARAM)&item);
1838         }
1839     }
1840 }
1841
1842 typedef enum {
1843     PurposeEnableAll = 0,
1844     PurposeDisableAll,
1845     PurposeEnableSelected
1846 } PurposeSelection;
1847
1848 static void select_purposes(HWND hwnd, PurposeSelection selection)
1849 {
1850     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
1851
1852     switch (selection)
1853     {
1854     case PurposeEnableAll:
1855     case PurposeDisableAll:
1856         EnableWindow(lv, FALSE);
1857         redraw_states(lv, FALSE);
1858         EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), FALSE);
1859         break;
1860     case PurposeEnableSelected:
1861         EnableWindow(lv, TRUE);
1862         redraw_states(lv, TRUE);
1863         EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), TRUE);
1864     }
1865 }
1866
1867 extern BOOL WINAPI WTHelperGetKnownUsages(DWORD action,
1868  PCCRYPT_OID_INFO **usages);
1869
1870 static void add_known_usage(HWND lv, PCCRYPT_OID_INFO info)
1871 {
1872     LVITEMW item;
1873
1874     item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
1875     item.state = INDEXTOSTATEIMAGEMASK(CheckBitmapIndexDisabledChecked);
1876     item.stateMask = LVIS_STATEIMAGEMASK;
1877     item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
1878     item.iSubItem = 0;
1879     item.lParam = (LPARAM)info;
1880     item.pszText = (LPWSTR)info->pwszName;
1881     SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item);
1882 }
1883
1884 struct edit_cert_data
1885 {
1886     PCCERT_CONTEXT cert;
1887     BOOL *pfPropertiesChanged;
1888     HIMAGELIST imageList;
1889 };
1890
1891 static void show_cert_usages(HWND hwnd, struct edit_cert_data *data)
1892 {
1893     PCCERT_CONTEXT cert = data->cert;
1894     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
1895     PCERT_ENHKEY_USAGE usage;
1896     DWORD size;
1897     PCCRYPT_OID_INFO *usages;
1898     RECT rc;
1899     LVCOLUMNW column;
1900     PurposeSelection purposeSelection;
1901
1902     GetWindowRect(lv, &rc);
1903     column.mask = LVCF_WIDTH;
1904     column.cx = rc.right - rc.left;
1905     SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column);
1906     SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)data->imageList);
1907
1908     /* Get enhanced key usage.  Have to check for a property and an extension
1909      * separately, because CertGetEnhancedKeyUsage will succeed and return an
1910      * empty usage if neither is set.  Unfortunately an empty usage implies
1911      * no usage is allowed, so we have to distinguish between the two cases.
1912      */
1913     if (CertGetEnhancedKeyUsage(cert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
1914      NULL, &size))
1915     {
1916         usage = HeapAlloc(GetProcessHeap(), 0, size);
1917         if (!CertGetEnhancedKeyUsage(cert,
1918          CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
1919         {
1920             HeapFree(GetProcessHeap(), 0, usage);
1921             usage = NULL;
1922         }
1923         else if (usage->cUsageIdentifier)
1924             purposeSelection = PurposeEnableSelected;
1925         else
1926             purposeSelection = PurposeDisableAll;
1927     }
1928     else if (CertGetEnhancedKeyUsage(cert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
1929      NULL, &size))
1930     {
1931         usage = HeapAlloc(GetProcessHeap(), 0, size);
1932         if (!CertGetEnhancedKeyUsage(cert,
1933          CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, usage, &size))
1934         {
1935             HeapFree(GetProcessHeap(), 0, usage);
1936             usage = NULL;
1937         }
1938         else if (usage->cUsageIdentifier)
1939             purposeSelection = PurposeEnableAll;
1940         else
1941             purposeSelection = PurposeDisableAll;
1942     }
1943     else
1944     {
1945         purposeSelection = PurposeEnableAll;
1946         usage = NULL;
1947     }
1948     if (usage)
1949     {
1950         DWORD i;
1951
1952         for (i = 0; i < usage->cUsageIdentifier; i++)
1953         {
1954             PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1955              usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
1956
1957             if (info)
1958                 add_known_usage(lv, info);
1959             else
1960                 add_purpose(hwnd, usage->rgpszUsageIdentifier[i]);
1961         }
1962         HeapFree(GetProcessHeap(), 0, usage);
1963     }
1964     else
1965     {
1966         if (WTHelperGetKnownUsages(1, &usages))
1967         {
1968             PCCRYPT_OID_INFO *ptr;
1969
1970             for (ptr = usages; *ptr; ptr++)
1971                 add_known_usage(lv, *ptr);
1972             WTHelperGetKnownUsages(2, &usages);
1973         }
1974     }
1975     select_purposes(hwnd, purposeSelection);
1976     SendMessageW(GetDlgItem(hwnd, IDC_ENABLE_ALL_PURPOSES + purposeSelection),
1977      BM_CLICK, 0, 0);
1978 }
1979
1980 static void set_general_cert_properties(HWND hwnd, struct edit_cert_data *data)
1981 {
1982     PCCERT_CONTEXT cert = data->cert;
1983     WCHAR *str;
1984
1985     if ((str = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID)))
1986     {
1987         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_SETTEXT, 0,
1988          (LPARAM)str);
1989         HeapFree(GetProcessHeap(), 0, str);
1990     }
1991     if ((str = get_cert_property_as_string(cert, CERT_DESCRIPTION_PROP_ID)))
1992     {
1993         SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_SETTEXT, 0,
1994          (LPARAM)str);
1995         HeapFree(GetProcessHeap(), 0, str);
1996     }
1997     show_cert_usages(hwnd, data);
1998 }
1999
2000 static void toggle_usage(HWND hwnd, int iItem)
2001 {
2002     LVITEMW item;
2003     int res;
2004     HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
2005
2006     item.mask = LVIF_STATE;
2007     item.iItem = iItem;
2008     item.iSubItem = 0;
2009     item.stateMask = LVIS_STATEIMAGEMASK;
2010     res = SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
2011     if (res)
2012     {
2013         int state = item.state >> 12;
2014
2015         item.state = INDEXTOSTATEIMAGEMASK(
2016          state == CheckBitmapIndexChecked ? CheckBitmapIndexUnchecked :
2017          CheckBitmapIndexChecked);
2018         SendMessageW(lv, LVM_SETITEMSTATE, iItem, (LPARAM)&item);
2019     }
2020 }
2021
2022 static void set_cert_string_property(PCCERT_CONTEXT cert, DWORD prop,
2023  LPWSTR str)
2024 {
2025     if (str && strlenW(str))
2026     {
2027         CRYPT_DATA_BLOB blob;
2028
2029         blob.pbData = (BYTE *)str;
2030         blob.cbData = (strlenW(str) + 1) * sizeof(WCHAR);
2031         CertSetCertificateContextProperty(cert, prop, 0, &blob);
2032     }
2033     else
2034         CertSetCertificateContextProperty(cert, prop, 0, NULL);
2035 }
2036
2037 #define WM_REFRESH_VIEW WM_USER + 0
2038
2039 static BOOL CALLBACK refresh_propsheet_pages(HWND hwnd, LPARAM lParam)
2040 {
2041     if ((GetClassLongW(hwnd, GCW_ATOM) == WC_DIALOG))
2042         SendMessageW(hwnd, WM_REFRESH_VIEW, 0, 0);
2043     return TRUE;
2044 }
2045
2046 #define MAX_FRIENDLY_NAME 40
2047 #define MAX_DESCRIPTION 255
2048
2049 static void apply_general_changes(HWND hwnd)
2050 {
2051     WCHAR buf[MAX_DESCRIPTION + 1];
2052     struct edit_cert_data *data =
2053      (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER);
2054
2055     SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_GETTEXT,
2056      sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
2057     set_cert_string_property(data->cert, CERT_FRIENDLY_NAME_PROP_ID, buf);
2058     SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_GETTEXT,
2059      sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
2060     set_cert_string_property(data->cert, CERT_DESCRIPTION_PROP_ID, buf);
2061     if (IsDlgButtonChecked(hwnd, IDC_ENABLE_ALL_PURPOSES))
2062     {
2063         /* Setting a NULL usage removes the enhanced key usage property. */
2064         CertSetEnhancedKeyUsage(data->cert, NULL);
2065     }
2066     else if (IsDlgButtonChecked(hwnd, IDC_DISABLE_ALL_PURPOSES))
2067     {
2068         CERT_ENHKEY_USAGE usage = { 0, NULL };
2069
2070         CertSetEnhancedKeyUsage(data->cert, &usage);
2071     }
2072     else if (IsDlgButtonChecked(hwnd, IDC_ENABLE_SELECTED_PURPOSES))
2073     {
2074         HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
2075         CERT_ENHKEY_USAGE usage = { 0, NULL };
2076         int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
2077         LVITEMW item;
2078
2079         item.mask = LVIF_STATE | LVIF_PARAM;
2080         item.iSubItem = 0;
2081         item.stateMask = LVIS_STATEIMAGEMASK;
2082         for (i = 0; i < purposes; i++)
2083         {
2084             item.iItem = i;
2085             if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
2086             {
2087                 int state = item.state >> 12;
2088
2089                 if (state == CheckBitmapIndexChecked)
2090                 {
2091                     CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
2092
2093                     if (usage.cUsageIdentifier)
2094                         usage.rgpszUsageIdentifier =
2095                          HeapReAlloc(GetProcessHeap(), 0,
2096                          usage.rgpszUsageIdentifier,
2097                          (usage.cUsageIdentifier + 1) * sizeof(LPSTR));
2098                     else
2099                         usage.rgpszUsageIdentifier =
2100                          HeapAlloc(GetProcessHeap(), 0, sizeof(LPSTR));
2101                     if (usage.rgpszUsageIdentifier)
2102                         usage.rgpszUsageIdentifier[usage.cUsageIdentifier++] =
2103                          (LPSTR)info->pszOID;
2104                 }
2105             }
2106         }
2107         CertSetEnhancedKeyUsage(data->cert, &usage);
2108         HeapFree(GetProcessHeap(), 0, usage.rgpszUsageIdentifier);
2109     }
2110     EnumChildWindows(GetParent(GetParent(hwnd)), refresh_propsheet_pages, 0);
2111     if (data->pfPropertiesChanged)
2112         *data->pfPropertiesChanged = TRUE;
2113 }
2114
2115 static LRESULT CALLBACK cert_properties_general_dlg_proc(HWND hwnd, UINT msg,
2116  WPARAM wp, LPARAM lp)
2117 {
2118     PROPSHEETPAGEW *page;
2119
2120     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2121
2122     switch (msg)
2123     {
2124     case WM_INITDIALOG:
2125     {
2126         HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
2127         struct detail_data *detailData;
2128         struct edit_cert_data *editData;
2129
2130         page = (PROPSHEETPAGEW *)lp;
2131         detailData = (struct detail_data *)page->lParam;
2132         SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), EM_SETLIMITTEXT,
2133          MAX_FRIENDLY_NAME, 0);
2134         SendMessageW(description, EM_SETLIMITTEXT, MAX_DESCRIPTION, 0);
2135         ShowScrollBar(description, SB_VERT, FALSE);
2136         editData = HeapAlloc(GetProcessHeap(), 0,
2137          sizeof(struct edit_cert_data));
2138         if (editData)
2139         {
2140             editData->imageList = ImageList_Create(16, 16,
2141              ILC_COLOR4 | ILC_MASK, 4, 0);
2142             if (editData->imageList)
2143             {
2144                 HBITMAP bmp;
2145                 COLORREF backColor = RGB(255, 0, 255);
2146
2147                 bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS));
2148                 ImageList_AddMasked(editData->imageList, bmp, backColor);
2149                 DeleteObject(bmp);
2150                 ImageList_SetBkColor(editData->imageList, CLR_NONE);
2151             }
2152             editData->cert = detailData->pCertViewInfo->pCertContext;
2153             editData->pfPropertiesChanged = detailData->pfPropertiesChanged;
2154             SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)editData);
2155             set_general_cert_properties(hwnd, editData);
2156         }
2157         break;
2158     }
2159     case WM_NOTIFY:
2160     {
2161         NMHDR *hdr = (NMHDR *)lp;
2162         NMITEMACTIVATE *nm;
2163
2164         switch (hdr->code)
2165         {
2166         case NM_CLICK:
2167             nm = (NMITEMACTIVATE *)lp;
2168             toggle_usage(hwnd, nm->iItem);
2169             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
2170             break;
2171         case PSN_APPLY:
2172             apply_general_changes(hwnd);
2173             break;
2174         }
2175         break;
2176     }
2177     case WM_COMMAND:
2178         switch (HIWORD(wp))
2179         {
2180         case EN_CHANGE:
2181             SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
2182             if (LOWORD(wp) == IDC_DESCRIPTION)
2183             {
2184                 /* Show/hide scroll bar on description depending on how much
2185                  * text it has.
2186                  */
2187                 HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION);
2188                 int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0);
2189
2190                 ShowScrollBar(description, SB_VERT, lines > 1);
2191             }
2192             break;
2193         case BN_CLICKED:
2194             switch (LOWORD(wp))
2195             {
2196             case IDC_ADD_PURPOSE:
2197                 if (DialogBoxParamW(hInstance,
2198                  MAKEINTRESOURCEW(IDD_ADD_CERT_PURPOSE), hwnd,
2199                  add_purpose_dlg_proc, (LPARAM)hwnd) == IDOK)
2200                     SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
2201                 break;
2202             case IDC_ENABLE_ALL_PURPOSES:
2203             case IDC_DISABLE_ALL_PURPOSES:
2204             case IDC_ENABLE_SELECTED_PURPOSES:
2205                 SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
2206                 select_purposes(hwnd, LOWORD(wp) - IDC_ENABLE_ALL_PURPOSES);
2207                 break;
2208             }
2209             break;
2210         }
2211         break;
2212     }
2213     return 0;
2214 }
2215
2216 static UINT CALLBACK cert_properties_general_callback(HWND hwnd, UINT msg,
2217  PROPSHEETPAGEW *page)
2218 {
2219     HWND lv;
2220     int cItem, i;
2221     struct edit_cert_data *data;
2222
2223     switch (msg)
2224     {
2225     case PSPCB_RELEASE:
2226         lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES);
2227         cItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
2228         for (i = 0; i < cItem; i++)
2229         {
2230             LVITEMW item;
2231
2232             item.mask = LVIF_PARAM;
2233             item.iItem = i;
2234             item.iSubItem = 0;
2235             if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item) && item.lParam)
2236             {
2237                 PCRYPT_OID_INFO info = (PCRYPT_OID_INFO)item.lParam;
2238
2239                 if (info->cbSize == sizeof(CRYPT_OID_INFO) && !info->dwGroupId)
2240                 {
2241                     HeapFree(GetProcessHeap(), 0, (LPSTR)info->pszOID);
2242                     HeapFree(GetProcessHeap(), 0, info);
2243                 }
2244             }
2245         }
2246         data = (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER);
2247         if (data)
2248         {
2249             ImageList_Destroy(data->imageList);
2250             HeapFree(GetProcessHeap(), 0, data);
2251         }
2252         break;
2253     }
2254     return 1;
2255 }
2256
2257 static void show_edit_cert_properties_dialog(HWND parent,
2258  struct detail_data *data)
2259 {
2260     PROPSHEETHEADERW hdr;
2261     PROPSHEETPAGEW page; /* FIXME: need to add a cross-certificate page */
2262
2263     TRACE("(%p)\n", data);
2264
2265     memset(&page, 0, sizeof(PROPSHEETPAGEW));
2266     page.dwSize = sizeof(page);
2267     page.dwFlags = PSP_USECALLBACK;
2268     page.pfnCallback = cert_properties_general_callback;
2269     page.hInstance = hInstance;
2270     page.u.pszTemplate = MAKEINTRESOURCEW(IDD_CERT_PROPERTIES_GENERAL);
2271     page.pfnDlgProc = cert_properties_general_dlg_proc;
2272     page.lParam = (LPARAM)data;
2273
2274     memset(&hdr, 0, sizeof(hdr));
2275     hdr.dwSize = sizeof(hdr);
2276     hdr.hwndParent = parent;
2277     hdr.dwFlags = PSH_PROPSHEETPAGE;
2278     hdr.hInstance = hInstance;
2279     hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE_PROPERTIES);
2280     hdr.u3.ppsp = &page;
2281     hdr.nPages = 1;
2282     PropertySheetW(&hdr);
2283 }
2284
2285 static void free_detail_fields(struct detail_data *data)
2286 {
2287     DWORD i;
2288
2289     for (i = 0; i < data->cFields; i++)
2290         HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value);
2291     HeapFree(GetProcessHeap(), 0, data->fields);
2292     data->fields = NULL;
2293     data->cFields = 0;
2294 }
2295
2296 static void refresh_details_view(HWND hwnd)
2297 {
2298     HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
2299     int curSel;
2300     struct detail_data *data;
2301
2302     curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
2303     /* Actually, any index will do, since they all store the same data value */
2304     data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0);
2305     free_detail_fields(data);
2306     set_fields_selection(hwnd, data, curSel);
2307 }
2308
2309 static LRESULT CALLBACK detail_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
2310  LPARAM lp)
2311 {
2312     PROPSHEETPAGEW *page;
2313     struct detail_data *data;
2314
2315     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2316
2317     switch (msg)
2318     {
2319     case WM_INITDIALOG:
2320         page = (PROPSHEETPAGEW *)lp;
2321         data = (struct detail_data *)page->lParam;
2322         create_cert_details_list(hwnd, data);
2323         if (!(data->pCertViewInfo->dwFlags & CRYPTUI_ENABLE_EDITPROPERTIES))
2324             EnableWindow(GetDlgItem(hwnd, IDC_EDITPROPERTIES), FALSE);
2325         if (data->pCertViewInfo->dwFlags & CRYPTUI_DISABLE_EXPORT)
2326             EnableWindow(GetDlgItem(hwnd, IDC_EXPORT), FALSE);
2327         break;
2328     case WM_NOTIFY:
2329     {
2330         NMITEMACTIVATE *nm;
2331         HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST);
2332
2333         nm = (NMITEMACTIVATE*)lp;
2334         if (nm->hdr.hwndFrom == list && nm->uNewState & LVN_ITEMACTIVATE
2335          && nm->hdr.code == LVN_ITEMCHANGED)
2336         {
2337             data = (struct detail_data *)nm->lParam;
2338             if (nm->iItem >= 0 && data && nm->iItem < data->cFields)
2339             {
2340                 WCHAR buf[MAX_STRING_LEN], *val = NULL;
2341                 HWND valueCtl = GetDlgItem(hwnd, IDC_DETAIL_VALUE);
2342
2343                 if (data->fields[nm->iItem].create)
2344                     val = data->fields[nm->iItem].create(
2345                      data->pCertViewInfo->pCertContext,
2346                      data->fields[nm->iItem].param);
2347                 else
2348                 {
2349                     LVITEMW item;
2350                     int res;
2351
2352                     item.cchTextMax = sizeof(buf) / sizeof(buf[0]);
2353                     item.mask = LVIF_TEXT;
2354                     item.pszText = buf;
2355                     item.iItem = nm->iItem;
2356                     item.iSubItem = 1;
2357                     res = SendMessageW(list, LVM_GETITEMW, 0, (LPARAM)&item);
2358                     if (res)
2359                         val = buf;
2360                 }
2361                 /* Select all the text in the control, the next update will
2362                  * replace it
2363                  */
2364                 SendMessageW(valueCtl, EM_SETSEL, 0, -1);
2365                 add_unformatted_text_to_control(valueCtl, val,
2366                  val ? strlenW(val) : 0);
2367                 if (val != buf)
2368                     HeapFree(GetProcessHeap(), 0, val);
2369             }
2370         }
2371         break;
2372     }
2373     case WM_COMMAND:
2374         switch (wp)
2375         {
2376         case IDC_EXPORT:
2377             FIXME("call CryptUIWizExport\n");
2378             break;
2379         case IDC_EDITPROPERTIES:
2380         {
2381             HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT);
2382             int curSel;
2383
2384             curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
2385             /* Actually, any index will do, since they all store the same
2386              * data value
2387              */
2388             data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA,
2389              curSel, 0);
2390             show_edit_cert_properties_dialog(GetParent(hwnd), data);
2391             break;
2392         }
2393         case ((CBN_SELCHANGE << 16) | IDC_DETAIL_SELECT):
2394             refresh_details_view(hwnd);
2395             break;
2396         }
2397         break;
2398     case WM_REFRESH_VIEW:
2399         refresh_details_view(hwnd);
2400         break;
2401     }
2402     return 0;
2403 }
2404
2405 static UINT CALLBACK detail_callback(HWND hwnd, UINT msg,
2406  PROPSHEETPAGEW *page)
2407 {
2408     struct detail_data *data;
2409
2410     switch (msg)
2411     {
2412     case PSPCB_RELEASE:
2413         data = (struct detail_data *)page->lParam;
2414         free_detail_fields(data);
2415         HeapFree(GetProcessHeap(), 0, data);
2416         break;
2417     }
2418     return 0;
2419 }
2420
2421 static BOOL init_detail_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2422  BOOL *pfPropertiesChanged, PROPSHEETPAGEW *page)
2423 {
2424     BOOL ret;
2425     struct detail_data *data = HeapAlloc(GetProcessHeap(), 0,
2426      sizeof(struct detail_data));
2427
2428     if (data)
2429     {
2430         data->pCertViewInfo = pCertViewInfo;
2431         data->pfPropertiesChanged = pfPropertiesChanged;
2432         data->cFields = 0;
2433         data->fields = NULL;
2434         memset(page, 0, sizeof(PROPSHEETPAGEW));
2435         page->dwSize = sizeof(PROPSHEETPAGEW);
2436         page->dwFlags = PSP_USECALLBACK;
2437         page->pfnCallback = detail_callback;
2438         page->hInstance = hInstance;
2439         page->u.pszTemplate = MAKEINTRESOURCEW(IDD_DETAIL);
2440         page->pfnDlgProc = detail_dlg_proc;
2441         page->lParam = (LPARAM)data;
2442         ret = TRUE;
2443     }
2444     else
2445         ret = FALSE;
2446     return ret;
2447 }
2448
2449 struct hierarchy_data
2450 {
2451     PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
2452     HIMAGELIST imageList;
2453     DWORD selectedCert;
2454 };
2455
2456 static LPARAM index_to_lparam(struct hierarchy_data *data, DWORD index)
2457 {
2458     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
2459      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
2460      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
2461      data->pCertViewInfo->idxCounterSigner);
2462
2463     /* Takes advantage of the fact that a pointer is 32-bit aligned, and
2464      * therefore always even.
2465      */
2466     if (index == provSigner->csCertChain - 1)
2467         return (LPARAM)data;
2468     return index << 1 | 1;
2469 }
2470
2471 static inline DWORD lparam_to_index(struct hierarchy_data *data, LPARAM lp)
2472 {
2473     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
2474      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
2475      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
2476      data->pCertViewInfo->idxCounterSigner);
2477
2478     if (!(lp & 1))
2479         return provSigner->csCertChain - 1;
2480     return lp >> 1;
2481 }
2482
2483 static struct hierarchy_data *get_hierarchy_data_from_tree_item(HWND tree,
2484  HTREEITEM hItem)
2485 {
2486     struct hierarchy_data *data = NULL;
2487     HTREEITEM root = NULL;
2488
2489     do {
2490         HTREEITEM parent = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM,
2491          TVGN_PARENT, (LPARAM)hItem);
2492
2493         if (!parent)
2494             root = hItem;
2495         hItem = parent;
2496     } while (hItem);
2497     if (root)
2498     {
2499         TVITEMW item;
2500
2501         item.mask = TVIF_PARAM;
2502         item.hItem = root;
2503         SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
2504         data = (struct hierarchy_data *)item.lParam;
2505     }
2506     return data;
2507 }
2508
2509 static WCHAR *get_cert_display_name(PCCERT_CONTEXT cert)
2510 {
2511     WCHAR *name = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID);
2512
2513     if (!name)
2514         name = get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0);
2515     return name;
2516 }
2517
2518 static void show_cert_chain(HWND hwnd, struct hierarchy_data *data)
2519 {
2520     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
2521     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
2522      (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
2523      data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner,
2524      data->pCertViewInfo->idxCounterSigner);
2525     DWORD i;
2526     HTREEITEM parent = NULL;
2527
2528     SendMessageW(tree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)data->imageList);
2529     for (i = provSigner->csCertChain; i; i--)
2530     {
2531         LPWSTR name;
2532
2533         name = get_cert_display_name(provSigner->pasCertChain[i - 1].pCert);
2534         if (name)
2535         {
2536             TVINSERTSTRUCTW tvis;
2537
2538             tvis.hParent = parent;
2539             tvis.hInsertAfter = TVI_LAST;
2540             tvis.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE |
2541              TVIF_SELECTEDIMAGE | TVIF_PARAM;
2542             tvis.u.item.pszText = name;
2543             tvis.u.item.state = TVIS_EXPANDED;
2544             tvis.u.item.stateMask = TVIS_EXPANDED;
2545             if (i == 1 &&
2546              (provSigner->pChainContext->TrustStatus.dwErrorStatus &
2547              CERT_TRUST_IS_PARTIAL_CHAIN))
2548             {
2549                 /* The root of the chain has a special case:  if the chain is
2550                  * a partial chain, the icon is a warning icon rather than an
2551                  * error icon.
2552                  */
2553                 tvis.u.item.iImage = 2;
2554             }
2555             else if (provSigner->pasCertChain[i - 1].pChainElement->TrustStatus.
2556              dwErrorStatus == 0)
2557                 tvis.u.item.iImage = 0;
2558             else
2559                 tvis.u.item.iImage = 1;
2560             tvis.u.item.iSelectedImage = tvis.u.item.iImage;
2561             tvis.u.item.lParam = index_to_lparam(data, i - 1);
2562             parent = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0,
2563              (LPARAM)&tvis);
2564             HeapFree(GetProcessHeap(), 0, name);
2565         }
2566     }
2567 }
2568
2569 static void set_certificate_status(HWND hwnd, CRYPT_PROVIDER_CERT *cert)
2570 {
2571     /* Select all the text in the control, the next update will replace it */
2572     SendMessageW(hwnd, EM_SETSEL, 0, -1);
2573     /* Set the highest priority error messages first. */
2574     if (!(cert->dwConfidence & CERT_CONFIDENCE_SIG))
2575         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_SIGNATURE);
2576     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIME))
2577         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIME);
2578     else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIMENEST))
2579         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIMENEST);
2580     else if (cert->dwRevokedReason)
2581         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_REVOKED);
2582     else
2583         add_string_resource_to_control(hwnd, IDS_CERTIFICATE_VALID);
2584 }
2585
2586 static void set_certificate_status_for_end_cert(HWND hwnd,
2587  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2588 {
2589     HWND status = GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT);
2590     CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain(
2591      (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
2592      pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
2593      pCertViewInfo->idxCounterSigner);
2594     CRYPT_PROVIDER_CERT *provCert = WTHelperGetProvCertFromChain(provSigner,
2595      pCertViewInfo->idxCert);
2596
2597     set_certificate_status(status, provCert);
2598 }
2599
2600 static void show_cert_hierarchy(HWND hwnd, struct hierarchy_data *data)
2601 {
2602     /* Disable view certificate button until a certificate is selected */
2603     EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), FALSE);
2604     show_cert_chain(hwnd, data);
2605     set_certificate_status_for_end_cert(hwnd, data->pCertViewInfo);
2606 }
2607
2608 static void show_dialog_for_selected_cert(HWND hwnd)
2609 {
2610     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
2611     TVITEMW item;
2612     struct hierarchy_data *data;
2613     DWORD selection;
2614
2615     memset(&item, 0, sizeof(item));
2616     item.mask = TVIF_HANDLE | TVIF_PARAM;
2617     item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CARET,
2618      (LPARAM)NULL);
2619     SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item);
2620     data = get_hierarchy_data_from_tree_item(tree, item.hItem);
2621     selection = lparam_to_index(data, item.lParam);
2622     if (selection != 0)
2623     {
2624         CRYPT_PROVIDER_SGNR *provSigner;
2625         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
2626         BOOL changed = FALSE;
2627
2628         provSigner = WTHelperGetProvSignerFromChain(
2629          (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
2630          data->pCertViewInfo->idxSigner,
2631          data->pCertViewInfo->fCounterSigner,
2632          data->pCertViewInfo->idxCounterSigner);
2633         memset(&viewInfo, 0, sizeof(viewInfo));
2634         viewInfo.dwSize = sizeof(viewInfo);
2635         viewInfo.dwFlags = data->pCertViewInfo->dwFlags;
2636         viewInfo.szTitle = data->pCertViewInfo->szTitle;
2637         viewInfo.pCertContext = provSigner->pasCertChain[selection].pCert;
2638         viewInfo.cStores = data->pCertViewInfo->cStores;
2639         viewInfo.rghStores = data->pCertViewInfo->rghStores;
2640         viewInfo.cPropSheetPages = data->pCertViewInfo->cPropSheetPages;
2641         viewInfo.rgPropSheetPages = data->pCertViewInfo->rgPropSheetPages;
2642         viewInfo.nStartPage = data->pCertViewInfo->nStartPage;
2643         CryptUIDlgViewCertificateW(&viewInfo, &changed);
2644         if (changed)
2645         {
2646             /* Delete the contents of the tree */
2647             SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
2648             /* Reinitialize the tree */
2649             show_cert_hierarchy(hwnd, data);
2650         }
2651     }
2652 }
2653
2654 static LRESULT CALLBACK hierarchy_dlg_proc(HWND hwnd, UINT msg, WPARAM wp,
2655  LPARAM lp)
2656 {
2657     PROPSHEETPAGEW *page;
2658     struct hierarchy_data *data;
2659     LRESULT ret = 0;
2660     HWND tree = GetDlgItem(hwnd, IDC_CERTPATH);
2661
2662     TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2663
2664     switch (msg)
2665     {
2666     case WM_INITDIALOG:
2667         page = (PROPSHEETPAGEW *)lp;
2668         data = (struct hierarchy_data *)page->lParam;
2669         show_cert_hierarchy(hwnd, data);
2670         break;
2671     case WM_NOTIFY:
2672     {
2673         NMHDR *hdr;
2674
2675         hdr = (NMHDR *)lp;
2676         switch (hdr->code)
2677         {
2678         case TVN_SELCHANGEDW:
2679         {
2680             NMTREEVIEWW *nm = (NMTREEVIEWW*)lp;
2681             DWORD selection;
2682             CRYPT_PROVIDER_SGNR *provSigner;
2683
2684             data = get_hierarchy_data_from_tree_item(tree, nm->itemNew.hItem);
2685             selection = lparam_to_index(data, nm->itemNew.lParam);
2686             provSigner = WTHelperGetProvSignerFromChain(
2687              (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData,
2688              data->pCertViewInfo->idxSigner,
2689              data->pCertViewInfo->fCounterSigner,
2690              data->pCertViewInfo->idxCounterSigner);
2691             EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), selection != 0);
2692             set_certificate_status(GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT),
2693              &provSigner->pasCertChain[selection]);
2694             break;
2695         }
2696         case NM_DBLCLK:
2697             show_dialog_for_selected_cert(hwnd);
2698             SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1);
2699             ret = 1;
2700             break;
2701         }
2702         break;
2703     }
2704     case WM_COMMAND:
2705         switch (wp)
2706         {
2707         case IDC_VIEWCERTIFICATE:
2708             show_dialog_for_selected_cert(hwnd);
2709             break;
2710         }
2711         break;
2712     case WM_REFRESH_VIEW:
2713     {
2714         TVITEMW item;
2715
2716         /* Get hierarchy data */
2717         memset(&item, 0, sizeof(item));
2718         item.mask = TVIF_HANDLE | TVIF_PARAM;
2719         item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_ROOT,
2720          (LPARAM)NULL);
2721         data = get_hierarchy_data_from_tree_item(tree, item.hItem);
2722         /* Delete the contents of the tree */
2723         SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
2724         /* Reinitialize the tree */
2725         show_cert_hierarchy(hwnd, data);
2726         break;
2727     }
2728     }
2729     return ret;
2730 }
2731
2732 static UINT CALLBACK hierarchy_callback(HWND hwnd, UINT msg,
2733  PROPSHEETPAGEW *page)
2734 {
2735     struct hierarchy_data *data;
2736
2737     switch (msg)
2738     {
2739     case PSPCB_RELEASE:
2740         data = (struct hierarchy_data *)page->lParam;
2741         ImageList_Destroy(data->imageList);
2742         HeapFree(GetProcessHeap(), 0, data);
2743         break;
2744     }
2745     return 0;
2746 }
2747
2748 static BOOL init_hierarchy_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2749  PROPSHEETPAGEW *page)
2750 {
2751     struct hierarchy_data *data = HeapAlloc(GetProcessHeap(), 0,
2752      sizeof(struct hierarchy_data));
2753     BOOL ret = FALSE;
2754
2755     if (data)
2756     {
2757         data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0);
2758         if (data->imageList)
2759         {
2760             HBITMAP bmp;
2761             COLORREF backColor = RGB(255, 0, 255);
2762
2763             data->pCertViewInfo = pCertViewInfo;
2764             data->selectedCert = 0xffffffff;
2765
2766             bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS));
2767             ImageList_AddMasked(data->imageList, bmp, backColor);
2768             DeleteObject(bmp);
2769             ImageList_SetBkColor(data->imageList, CLR_NONE);
2770
2771             memset(page, 0, sizeof(PROPSHEETPAGEW));
2772             page->dwSize = sizeof(PROPSHEETPAGEW);
2773             page->dwFlags = PSP_USECALLBACK;
2774             page->hInstance = hInstance;
2775             page->u.pszTemplate = MAKEINTRESOURCEW(IDD_HIERARCHY);
2776             page->pfnDlgProc = hierarchy_dlg_proc;
2777             page->lParam = (LPARAM)data;
2778             page->pfnCallback = hierarchy_callback;
2779             ret = TRUE;
2780         }
2781         else
2782             HeapFree(GetProcessHeap(), 0, data);
2783     }
2784     return ret;
2785 }
2786
2787 static int CALLBACK cert_prop_sheet_proc(HWND hwnd, UINT msg, LPARAM lp)
2788 {
2789     RECT rc;
2790     POINT topLeft;
2791
2792     TRACE("(%p, %08x, %08lx)\n", hwnd, msg, lp);
2793
2794     switch (msg)
2795     {
2796     case PSCB_INITIALIZED:
2797         /* Get cancel button's position.. */
2798         GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rc);
2799         topLeft.x = rc.left;
2800         topLeft.y = rc.top;
2801         ScreenToClient(hwnd, &topLeft);
2802         /* hide the cancel button.. */
2803         ShowWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
2804         /* get the OK button's size.. */
2805         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
2806         /* and move the OK button to the cancel button's original position. */
2807         MoveWindow(GetDlgItem(hwnd, IDOK), topLeft.x, topLeft.y,
2808          rc.right - rc.left, rc.bottom - rc.top, FALSE);
2809         GetWindowRect(GetDlgItem(hwnd, IDOK), &rc);
2810         break;
2811     }
2812     return 0;
2813 }
2814
2815 static BOOL show_cert_dialog(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo,
2816  CRYPT_PROVIDER_CERT *provCert, BOOL *pfPropertiesChanged)
2817 {
2818     static const WCHAR riched[] = { 'r','i','c','h','e','d','2','0',0 };
2819     DWORD nPages;
2820     PROPSHEETPAGEW *pages;
2821     BOOL ret = FALSE;
2822     HMODULE lib = LoadLibraryW(riched);
2823
2824     nPages = pCertViewInfo->cPropSheetPages + 1; /* one for the General tab */
2825     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
2826         nPages++;
2827     if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
2828         nPages++;
2829     pages = HeapAlloc(GetProcessHeap(), 0, nPages * sizeof(PROPSHEETPAGEW));
2830     if (pages)
2831     {
2832         PROPSHEETHEADERW hdr;
2833         CRYPTUI_INITDIALOG_STRUCT *init = NULL;
2834         DWORD i;
2835
2836         memset(&hdr, 0, sizeof(hdr));
2837         hdr.dwSize = sizeof(hdr);
2838         hdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK;
2839         hdr.hInstance = hInstance;
2840         if (pCertViewInfo->szTitle)
2841             hdr.pszCaption = pCertViewInfo->szTitle;
2842         else
2843             hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE);
2844         init_general_page(pCertViewInfo, &pages[hdr.nPages++]);
2845         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE))
2846         {
2847             if (init_detail_page(pCertViewInfo, pfPropertiesChanged,
2848              &pages[hdr.nPages]))
2849                 hdr.nPages++;
2850         }
2851         if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE))
2852         {
2853             if (init_hierarchy_page(pCertViewInfo, &pages[hdr.nPages]))
2854                 hdr.nPages++;
2855         }
2856         /* Copy each additional page, and create the init dialog struct for it
2857          */
2858         if (pCertViewInfo->cPropSheetPages)
2859         {
2860             init = HeapAlloc(GetProcessHeap(), 0,
2861              pCertViewInfo->cPropSheetPages *
2862              sizeof(CRYPTUI_INITDIALOG_STRUCT));
2863             if (init)
2864             {
2865                 for (i = 0; i < pCertViewInfo->cPropSheetPages; i++)
2866                 {
2867                     memcpy(&pages[hdr.nPages + i],
2868                      &pCertViewInfo->rgPropSheetPages[i],
2869                      sizeof(PROPSHEETPAGEW));
2870                     init[i].lParam = pCertViewInfo->rgPropSheetPages[i].lParam;
2871                     init[i].pCertContext = pCertViewInfo->pCertContext;
2872                     pages[hdr.nPages + i].lParam = (LPARAM)&init[i];
2873                 }
2874                 if (pCertViewInfo->nStartPage & 0x8000)
2875                 {
2876                     /* Start page index is relative to the number of default
2877                      * pages
2878                      */
2879                     hdr.u2.nStartPage = pCertViewInfo->nStartPage + hdr.nPages;
2880                 }
2881                 else
2882                     hdr.u2.nStartPage = pCertViewInfo->nStartPage;
2883                 hdr.nPages = nPages;
2884                 ret = TRUE;
2885             }
2886             else
2887                 SetLastError(ERROR_OUTOFMEMORY);
2888         }
2889         else
2890         {
2891             /* Ignore the relative flag if there aren't any additional pages */
2892             hdr.u2.nStartPage = pCertViewInfo->nStartPage & 0x7fff;
2893             ret = TRUE;
2894         }
2895         if (ret)
2896         {
2897             INT_PTR l;
2898
2899             hdr.u3.ppsp = pages;
2900             hdr.pfnCallback = cert_prop_sheet_proc;
2901             l = PropertySheetW(&hdr);
2902             if (l == 0)
2903             {
2904                 SetLastError(ERROR_CANCELLED);
2905                 ret = FALSE;
2906             }
2907         }
2908         HeapFree(GetProcessHeap(), 0, init);
2909         HeapFree(GetProcessHeap(), 0, pages);
2910     }
2911     else
2912         SetLastError(ERROR_OUTOFMEMORY);
2913     FreeLibrary(lib);
2914     return ret;
2915 }
2916
2917 /***********************************************************************
2918  *              CryptUIDlgViewCertificateW (CRYPTUI.@)
2919  */
2920 BOOL WINAPI CryptUIDlgViewCertificateW(
2921  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, BOOL *pfPropertiesChanged)
2922 {
2923     static GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY;
2924     CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
2925     WINTRUST_DATA wvt;
2926     WINTRUST_CERT_INFO cert;
2927     BOOL ret = FALSE;
2928     CRYPT_PROVIDER_SGNR *signer;
2929     CRYPT_PROVIDER_CERT *provCert = NULL;
2930
2931     TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
2932
2933     if (pCertViewInfo->dwSize != sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))
2934     {
2935         SetLastError(ERROR_INVALID_PARAMETER);
2936         return FALSE;
2937     }
2938     /* Make a local copy in case we have to call WinVerifyTrust ourselves */
2939     memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
2940     if (!viewInfo.u.hWVTStateData)
2941     {
2942         memset(&wvt, 0, sizeof(wvt));
2943         wvt.cbStruct = sizeof(wvt);
2944         wvt.dwUIChoice = WTD_UI_NONE;
2945         if (viewInfo.dwFlags &
2946          CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
2947             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
2948         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_END_CERT)
2949             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_END_CERT;
2950         if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN)
2951             wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN;
2952         wvt.dwUnionChoice = WTD_CHOICE_CERT;
2953         memset(&cert, 0, sizeof(cert));
2954         cert.cbStruct = sizeof(cert);
2955         cert.psCertContext = (CERT_CONTEXT *)viewInfo.pCertContext;
2956         cert.chStores = viewInfo.cStores;
2957         cert.pahStores = viewInfo.rghStores;
2958         wvt.u.pCert = &cert;
2959         wvt.dwStateAction = WTD_STATEACTION_VERIFY;
2960         WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
2961         viewInfo.u.pCryptProviderData =
2962          WTHelperProvDataFromStateData(wvt.hWVTStateData);
2963         signer = WTHelperGetProvSignerFromChain(
2964          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, 0, FALSE, 0);
2965         provCert = WTHelperGetProvCertFromChain(signer, 0);
2966         ret = TRUE;
2967     }
2968     else
2969     {
2970         viewInfo.u.pCryptProviderData =
2971          WTHelperProvDataFromStateData(viewInfo.u.hWVTStateData);
2972         signer = WTHelperGetProvSignerFromChain(
2973          (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData,
2974          viewInfo.idxSigner, viewInfo.fCounterSigner,
2975          viewInfo.idxCounterSigner);
2976         provCert = WTHelperGetProvCertFromChain(signer, viewInfo.idxCert);
2977         ret = TRUE;
2978     }
2979     if (ret)
2980     {
2981         ret = show_cert_dialog(&viewInfo, provCert, pfPropertiesChanged);
2982         if (!viewInfo.u.hWVTStateData)
2983         {
2984             wvt.dwStateAction = WTD_STATEACTION_CLOSE;
2985             WinVerifyTrust(NULL, &generic_cert_verify, &wvt);
2986         }
2987     }
2988     return ret;
2989 }
2990
2991 /***********************************************************************
2992  *              CryptUIDlgViewContext (CRYPTUI.@)
2993  */
2994 BOOL WINAPI CryptUIDlgViewContext(DWORD dwContextType, LPVOID pvContext,
2995  HWND hwnd, LPCWSTR pwszTitle, DWORD dwFlags, LPVOID pvReserved)
2996 {
2997     BOOL ret;
2998
2999     TRACE("(%d, %p, %p, %s, %08x, %p)\n", dwContextType, pvContext, hwnd,
3000      debugstr_w(pwszTitle), dwFlags, pvReserved);
3001
3002     switch (dwContextType)
3003     {
3004     case CERT_STORE_CERTIFICATE_CONTEXT:
3005     {
3006         CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo;
3007
3008         memset(&viewInfo, 0, sizeof(viewInfo));
3009         viewInfo.dwSize = sizeof(viewInfo);
3010         viewInfo.hwndParent = hwnd;
3011         viewInfo.szTitle = pwszTitle;
3012         viewInfo.pCertContext = pvContext;
3013         ret = CryptUIDlgViewCertificateW(&viewInfo, NULL);
3014         break;
3015     }
3016     default:
3017         FIXME("unimplemented for context type %d\n", dwContextType);
3018         SetLastError(E_INVALIDARG);
3019         ret = FALSE;
3020     }
3021     return ret;
3022 }
3023
3024 static PCCERT_CONTEXT make_cert_from_file(LPCWSTR fileName)
3025 {
3026     HANDLE file;
3027     DWORD size, encoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
3028     BYTE *buffer;
3029     PCCERT_CONTEXT cert;
3030
3031     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
3032      OPEN_EXISTING, 0, NULL);
3033     if (file == INVALID_HANDLE_VALUE)
3034     {
3035         WARN("can't open certificate file %s\n", debugstr_w(fileName));
3036         return NULL;
3037     }
3038     if ((size = GetFileSize(file, NULL)))
3039     {
3040         if ((buffer = HeapAlloc(GetProcessHeap(), 0, size)))
3041         {
3042             DWORD read;
3043             if (!ReadFile(file, buffer, size, &read, NULL) || read != size)
3044             {
3045                 WARN("can't read certificate file %s\n", debugstr_w(fileName));
3046                 HeapFree(GetProcessHeap(), 0, buffer);
3047                 CloseHandle(file);
3048                 return NULL;
3049             }
3050         }
3051     }
3052     else
3053     {
3054         WARN("empty file %s\n", debugstr_w(fileName));
3055         CloseHandle(file);
3056         return NULL;
3057     }
3058     CloseHandle(file);
3059     cert = CertCreateCertificateContext(encoding, buffer, size);
3060     HeapFree(GetProcessHeap(), 0, buffer);
3061     return cert;
3062 }
3063
3064 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
3065  * or szOID_BASIC_CONSTRAINTS2, whichever is present) to determine if it
3066  * should be a CA.  If neither extension is present, returns
3067  * defaultIfNotSpecified.
3068  */
3069 static BOOL is_ca_cert(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified)
3070 {
3071     BOOL isCA = defaultIfNotSpecified;
3072     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
3073      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
3074
3075     if (ext)
3076     {
3077         CERT_BASIC_CONSTRAINTS_INFO *info;
3078         DWORD size = 0;
3079
3080         if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
3081          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
3082          NULL, (LPBYTE)&info, &size))
3083         {
3084             if (info->SubjectType.cbData == 1)
3085                 isCA = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
3086             LocalFree(info);
3087         }
3088     }
3089     else
3090     {
3091         ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
3092          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
3093         if (ext)
3094         {
3095             CERT_BASIC_CONSTRAINTS2_INFO info;
3096             DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
3097
3098             if (CryptDecodeObjectEx(X509_ASN_ENCODING,
3099              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
3100              0, NULL, &info, &size))
3101                 isCA = info.fCA;
3102         }
3103     }
3104     return isCA;
3105 }
3106
3107 static HCERTSTORE choose_store_for_cert(PCCERT_CONTEXT cert)
3108 {
3109     static const WCHAR AddressBook[] = { 'A','d','d','r','e','s','s',
3110      'B','o','o','k',0 };
3111     static const WCHAR CA[] = { 'C','A',0 };
3112     LPCWSTR storeName;
3113
3114     if (is_ca_cert(cert, TRUE))
3115         storeName = CA;
3116     else
3117         storeName = AddressBook;
3118     return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
3119      CERT_SYSTEM_STORE_CURRENT_USER, storeName);
3120 }
3121
3122 BOOL WINAPI CryptUIWizImport(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle,
3123                              PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore)
3124 {
3125     BOOL ret;
3126     HCERTSTORE store;
3127     const CERT_CONTEXT *cert;
3128     BOOL freeCert = FALSE;
3129
3130     TRACE("(0x%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, debugstr_w(pwszWizardTitle),
3131           pImportSrc, hDestCertStore);
3132
3133     if (!(dwFlags & CRYPTUI_WIZ_NO_UI)) FIXME("UI not implemented\n");
3134
3135     if (!pImportSrc ||
3136      pImportSrc->dwSize != sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO))
3137     {
3138         SetLastError(E_INVALIDARG);
3139         return FALSE;
3140     }
3141
3142     switch (pImportSrc->dwSubjectChoice)
3143     {
3144     case CRYPTUI_WIZ_IMPORT_SUBJECT_FILE:
3145         if (!(cert = make_cert_from_file(pImportSrc->u.pwszFileName)))
3146         {
3147             WARN("unable to create certificate context\n");
3148             return FALSE;
3149         }
3150         else
3151             freeCert = TRUE;
3152         break;
3153     case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT:
3154         cert = pImportSrc->u.pCertContext;
3155         if (!cert)
3156         {
3157             SetLastError(E_INVALIDARG);
3158             return FALSE;
3159         }
3160         break;
3161     default:
3162         FIXME("source type not implemented: %u\n", pImportSrc->dwSubjectChoice);
3163         SetLastError(E_INVALIDARG);
3164         return FALSE;
3165     }
3166     if (hDestCertStore) store = hDestCertStore;
3167     else
3168     {
3169         if (!(store = choose_store_for_cert(cert)))
3170         {
3171             WARN("unable to open certificate store\n");
3172             CertFreeCertificateContext(cert);
3173             return FALSE;
3174         }
3175     }
3176     ret = CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
3177
3178     if (!hDestCertStore) CertCloseStore(store, 0);
3179     if (freeCert)
3180         CertFreeCertificateContext(cert);
3181     return ret;
3182 }