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