ReactOS  0.4.13-dev-257-gfabbd7c
main.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "config.h"
20 
21 #include <stdarg.h>
22 
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winuser.h"
30 #include "softpub.h"
31 #include "wingdi.h"
32 #include "richedit.h"
33 #include "ole2.h"
34 #include "richole.h"
35 #include "commdlg.h"
36 #include "commctrl.h"
37 #include "cryptuiapi.h"
38 #include "cryptuires.h"
39 #include "urlmon.h"
40 #include "hlink.h"
41 #include "winreg.h"
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44 
46 
48 
49 static const WCHAR empty[] = {0};
50 
52 {
53  TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
54 
55  switch (fdwReason)
56  {
57  case DLL_WINE_PREATTACH:
58  return FALSE; /* prefer native version */
59  case DLL_PROCESS_ATTACH:
60  hInstance = hinstDLL;
61  DisableThreadLibraryCalls(hinstDLL);
62  break;
63  }
64  return TRUE;
65 }
66 
67 static WCHAR *strdupAtoW( const char *str )
68 {
69  DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
70  WCHAR *ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
71  if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
72  return ret;
73 }
74 
75 #define MAX_STRING_LEN 512
76 
78 {
80  RECT rc;
83 
85  GetWindowRect(lv, &rc);
87  column.mask = LVCF_WIDTH | LVCF_TEXT;
88  column.cx = (rc.right - rc.left) * 29 / 100 - 2;
89  column.pszText = buf;
93  column.cx = (rc.right - rc.left) * 16 / 100 - 2;
96  column.cx = (rc.right - rc.left) * 23 / 100 - 1;
99 }
100 
101 static void add_cert_to_view(HWND lv, PCCERT_CONTEXT cert, DWORD *allocatedLen,
102  LPWSTR *str)
103 {
104  DWORD len;
105  LVITEMW item;
106  WCHAR dateFmt[80]; /* sufficient for LOCALE_SSHORTDATE */
107  WCHAR date[80];
108  SYSTEMTIME sysTime;
109  LPWSTR none;
110 
111  item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
112  item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
113  item.iSubItem = 0;
114  item.iImage = 0;
117  NULL, 0);
118  if (len > *allocatedLen)
119  {
120  HeapFree(GetProcessHeap(), 0, *str);
121  *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
122  if (*str)
123  *allocatedLen = len;
124  }
125  if (*str)
126  {
128  *str, len);
129  item.pszText = *str;
131  }
132 
133  item.mask = LVIF_TEXT;
136  if (len > *allocatedLen)
137  {
138  HeapFree(GetProcessHeap(), 0, *str);
139  *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
140  if (*str)
141  *allocatedLen = len;
142  }
143  if (*str)
144  {
147  item.pszText = *str;
148  item.iSubItem = 1;
150  }
151 
153  FileTimeToSystemTime(&cert->pCertInfo->NotAfter, &sysTime);
154  GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date, ARRAY_SIZE(date));
155  item.pszText = date;
156  item.iSubItem = 2;
158 
160  NULL, &len))
162  if (len > *allocatedLen)
163  {
164  HeapFree(GetProcessHeap(), 0, *str);
165  *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
166  if (*str)
167  *allocatedLen = len;
168  }
169  if (*str)
170  {
172  *str, &len))
173  item.pszText = none;
174  else
175  item.pszText = *str;
176  item.iSubItem = 3;
178  }
179 }
180 
182 {
183  static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M',
184  'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a',
185  'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u',
186  'r','p','o','s','e',0 };
187  LPSTR str = NULL;
188  HKEY key;
189 
190  if (!RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_READ,
191  NULL, &key, NULL))
192  {
193  LONG rc;
194  DWORD type, size;
195 
196  rc = RegQueryValueExA(key, "Purpose", NULL, &type, NULL, &size);
197  if ((!rc || rc == ERROR_MORE_DATA) && type == REG_SZ)
198  {
199  str = HeapAlloc(GetProcessHeap(), 0, size);
200  if (str)
201  {
202  rc = RegQueryValueExA(key, "Purpose", NULL, NULL, (LPBYTE)str,
203  &size);
204  if (rc)
205  {
206  HeapFree(GetProcessHeap(), 0, str);
207  str = NULL;
208  }
209  }
210  }
211  RegCloseKey(key);
212  }
213  return str;
214 }
215 
216 typedef enum {
220 } PurposeFilter;
221 
223 {
226  LPSTR usages;
227  int index;
228 
235  SendMessageW(cb, CB_SETCURSEL, 0, 0);
236  if ((usages = get_cert_mgr_usages()))
237  {
238  LPSTR ptr, comma;
239 
240  for (ptr = usages, comma = strchr(ptr, ','); ptr && *ptr;
241  ptr = comma ? comma + 1 : NULL,
242  comma = ptr ? strchr(ptr, ',') : NULL)
243  {
245 
246  if (comma)
247  *comma = 0;
249  {
251  (LPARAM)info->pwszName);
253  }
254  }
256  }
257 }
258 
261 
263 {
264  if (!usage->cUsageIdentifier)
266  sizeof(LPSTR));
267  else
268  usage->rgpszUsageIdentifier = HeapReAlloc(GetProcessHeap(), 0,
269  usage->rgpszUsageIdentifier,
270  (usage->cUsageIdentifier + 1) * sizeof(LPSTR));
271  if (usage->rgpszUsageIdentifier)
272  usage->rgpszUsageIdentifier[usage->cUsageIdentifier++] = oid;
273  else
274  {
276  usage = NULL;
277  }
278  return usage;
279 }
280 
282 {
284  sizeof(CERT_ENHKEY_USAGE));
285 
286  if (usage)
287  {
288  LPSTR ptr, comma;
289 
290  for (ptr = usageStr, comma = strchr(ptr, ','); usage && ptr && *ptr;
291  ptr = comma ? comma + 1 : NULL,
292  comma = ptr ? strchr(ptr, ',') : NULL)
293  {
294  if (comma)
295  *comma = 0;
297  }
298  }
299  return usage;
300 }
301 
303 {
304  CERT_ENHKEY_USAGE *advancedUsage = HeapAlloc(GetProcessHeap(),
306 
307  if (advancedUsage)
308  {
310 
312  {
313  LPSTR disabledUsagesStr;
314 
315  if ((disabledUsagesStr = get_cert_mgr_usages()))
316  {
317  CERT_ENHKEY_USAGE *disabledUsages =
318  convert_usages_str_to_usage(disabledUsagesStr);
319 
320  if (disabledUsages)
321  {
323 
324  for (ptr = usages; advancedUsage && *ptr; ptr++)
325  {
326  DWORD i;
327  BOOL disabled = FALSE;
328 
329  for (i = 0; !disabled &&
330  i < disabledUsages->cUsageIdentifier; i++)
331  if (!strcmp(disabledUsages->rgpszUsageIdentifier[i],
332  (*ptr)->pszOID))
333  disabled = TRUE;
334  if (!disabled)
335  advancedUsage = add_oid_to_usage(advancedUsage,
336  (LPSTR)(*ptr)->pszOID);
337  }
338  /* The individual strings are pointers to disabledUsagesStr,
339  * so they're freed when it is.
340  */
342  disabledUsages->rgpszUsageIdentifier);
343  HeapFree(GetProcessHeap(), 0, disabledUsages);
344  }
345  HeapFree(GetProcessHeap(), 0, disabledUsagesStr);
346  }
348  }
349  }
350  return advancedUsage;
351 }
352 
353 static int CALLBACK cert_mgr_sort_by_subject(LPARAM lp1, LPARAM lp2, LPARAM lp);
354 
356 {
360  DWORD allocatedLen = 0;
361  LPWSTR str = NULL;
362  int index;
364  LPCSTR oid = NULL;
365  CERT_ENHKEY_USAGE *advanced = NULL;
366 
367  index = SendMessageW(cb, CB_GETCURSEL, 0, 0);
368  if (index >= 0)
369  {
371 
372  if (!HIWORD(data))
373  filter = data;
374  else
375  {
377 
379  oid = info->pszOID;
380  }
381  }
383  advanced = create_advanced_filter();
384  do {
386  if (cert)
387  {
388  BOOL show = FALSE;
389 
391  show = TRUE;
392  else
393  {
394  int numOIDs;
395  DWORD cbOIDs = 0;
396 
397  if (CertGetValidUsages(1, &cert, &numOIDs, NULL, &cbOIDs))
398  {
399  if (numOIDs == -1)
400  {
401  /* -1 implies all usages are valid */
402  show = TRUE;
403  }
404  else
405  {
406  LPSTR *oids = HeapAlloc(GetProcessHeap(), 0, cbOIDs);
407 
408  if (oids)
409  {
410  if (CertGetValidUsages(1, &cert, &numOIDs, oids,
411  &cbOIDs))
412  {
413  int i;
414 
416  {
417  for (i = 0; !show && i < numOIDs; i++)
418  if (!strcmp(oids[i], oid))
419  show = TRUE;
420  }
421  else
422  {
423  for (i = 0; !show && i < numOIDs; i++)
424  {
425  DWORD j;
426 
427  for (j = 0; !show &&
428  j < advanced->cUsageIdentifier; j++)
429  if (!strcmp(oids[i],
430  advanced->rgpszUsageIdentifier[j]))
431  show = TRUE;
432  }
433  }
434  }
435  HeapFree(GetProcessHeap(), 0, oids);
436  }
437  }
438  }
439  }
440  if (show)
441  add_cert_to_view(lv, cert, &allocatedLen, &str);
442  }
443  } while (cert);
444  HeapFree(GetProcessHeap(), 0, str);
445  if (advanced)
446  {
448  HeapFree(GetProcessHeap(), 0, advanced);
449  }
452 }
453 
454 static const WCHAR my[] = { 'M','y',0 };
455 static const WCHAR addressBook[] = {
456  'A','d','d','r','e','s','s','B','o','o','k',0 };
457 static const WCHAR ca[] = { 'C','A',0 };
458 static const WCHAR root[] = { 'R','o','o','t',0 };
459 static const WCHAR trustedPublisher[] = {
460  'T','r','u','s','t','e','d','P','u','b','l','i','s','h','e','r',0 };
461 static const WCHAR disallowed[] = { 'D','i','s','a','l','l','o','w','e','d',0 };
462 
464 {
468 };
469 
470 static const struct CertMgrStoreInfo defaultStoreList[] = {
479 };
480 
481 static const struct CertMgrStoreInfo publisherStoreList[] = {
486 };
487 
489 {
493  const struct CertMgrStoreInfo *stores;
494 };
495 
497 {
498  const struct CertMgrStoreInfo *storeList;
499  int cStores, i;
501 
503  {
504  storeList = publisherStoreList;
505  cStores = ARRAY_SIZE(publisherStoreList);
506  }
507  else
508  {
509  storeList = defaultStoreList;
510  cStores = ARRAY_SIZE(defaultStoreList);
511  }
513  cStores = 1;
514  data->nStores = cStores;
515  data->stores = storeList;
516  for (i = 0; i < cStores; i++)
517  {
518  LPCWSTR name;
519  TCITEMW item;
520  HCERTSTORE store;
521 
522  if (!(name = CryptFindLocalizedName(storeList[i].name)))
523  name = storeList[i].name;
526  item.mask = TCIF_TEXT | TCIF_PARAM;
527  item.pszText = (LPWSTR)name;
528  item.lParam = (LPARAM)store;
530  }
531 }
532 
533 static void free_certs(HWND lv)
534 {
535  LVITEMW item;
536  int items = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
537 
538  for (i = 0; i < items; i++)
539  {
540  item.mask = LVIF_PARAM;
541  item.iItem = i;
542  item.iSubItem = 0;
545  }
546 }
547 
549 {
550  TCITEMW item;
551 
552  item.mask = TCIF_PARAM;
554  return (HCERTSTORE)item.lParam;
555 }
556 
558 {
560 
561  return cert_mgr_index_to_store(tab, SendMessageW(tab, TCM_GETCURSEL, 0, 0));
562 }
563 
564 static void close_stores(HWND tab)
565 {
566  int i, tabs = SendMessageW(tab, TCM_GETITEMCOUNT, 0, 0);
567 
568  for (i = 0; i < tabs; i++)
570 }
571 
573 {
575 
576  free_certs(lv);
577  SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0);
579 }
580 
581 typedef enum {
587 
590 {
591  LVITEMW item;
592 
593  item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
595  item.stateMask = LVIS_STATEIMAGEMASK;
596  item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
597  item.iSubItem = 0;
598  item.lParam = (LPARAM)info;
599  item.pszText = (LPWSTR)info->pwszName;
601 }
602 
604 {
606 
608  {
610 
611  for (ptr = usages; *ptr; ptr++)
612  add_known_usage(lv, *ptr, state);
614  }
615 }
616 
617 static void toggle_usage(HWND hwnd, int iItem)
618 {
619  LVITEMW item;
620  int res;
622 
623  item.mask = LVIF_STATE;
624  item.iItem = iItem;
625  item.iSubItem = 0;
626  item.stateMask = LVIS_STATEIMAGEMASK;
627  res = SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
628  if (res)
629  {
630  int state = item.state >> 12;
631 
632  item.state = INDEXTOSTATEIMAGEMASK(
635  SendMessageW(lv, LVM_SETITEMSTATE, iItem, (LPARAM)&item);
636  }
637 }
638 
640 {
642  (void *)oid, CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
643  LONG_PTR ret;
644 
645  if (oidInfo)
646  {
647  LVFINDINFOW findInfo;
648 
649  findInfo.flags = LVFI_PARAM;
650  findInfo.lParam = (LPARAM)oidInfo;
651  ret = SendMessageW(lv, LVM_FINDITEMW, -1, (LPARAM)&findInfo);
652  }
653  else
654  {
655  LVFINDINFOA findInfo;
656 
657  findInfo.flags = LVFI_STRING;
658  findInfo.psz = oid;
659  ret = SendMessageW(lv, LVM_FINDITEMA, -1, (LPARAM)&findInfo);
660  }
661  return ret;
662 }
663 
665 {
666  static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M',
667  'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a',
668  'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u',
669  'r','p','o','s','e',0 };
670  HKEY key;
672  int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
673  LVITEMW item;
674  LPSTR str = NULL;
675 
676  item.mask = LVIF_STATE | LVIF_PARAM;
677  item.iSubItem = 0;
678  item.stateMask = LVIS_STATEIMAGEMASK;
679  for (i = 0; i < purposes; i++)
680  {
681  item.iItem = i;
682  if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
683  {
684  int state = item.state >> 12;
685 
687  {
688  CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
689  BOOL firstString = TRUE;
690 
691  if (!str)
693  strlen(info->pszOID) + 1);
694  else
695  {
697  strlen(str) + 1 + strlen(info->pszOID) + 1);
698  firstString = FALSE;
699  }
700  if (str)
701  {
702  LPSTR ptr = firstString ? str : str + strlen(str);
703 
704  if (!firstString)
705  *ptr++ = ',';
706  strcpy(ptr, info->pszOID);
707  }
708  }
709  }
710  }
712  NULL, &key, NULL))
713  {
714  if (str)
715  RegSetValueExA(key, "Purpose", 0, REG_SZ, (const BYTE *)str,
716  strlen(str) + 1);
717  else
718  RegDeleteValueA(key, "Purpose");
719  RegCloseKey(key);
720  }
721  HeapFree(GetProcessHeap(), 0, str);
722 }
723 
725  WPARAM wp, LPARAM lp)
726 {
727  switch (msg)
728  {
729  case WM_INITDIALOG:
730  {
731  RECT rc;
734  HIMAGELIST imageList;
735  LPSTR disabledUsages;
736 
737  GetWindowRect(lv, &rc);
738  column.mask = LVCF_WIDTH;
739  column.cx = rc.right - rc.left;
741  imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 4, 0);
742  if (imageList)
743  {
744  HBITMAP bmp;
745  COLORREF backColor = RGB(255, 0, 255);
746 
748  ImageList_AddMasked(imageList, bmp, backColor);
749  DeleteObject(bmp);
750  ImageList_SetBkColor(imageList, CLR_NONE);
752  SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)imageList);
753  }
755  if ((disabledUsages = get_cert_mgr_usages()))
756  {
757  LPSTR ptr, comma;
758 
759  for (ptr = disabledUsages, comma = strchr(ptr, ','); ptr && *ptr;
760  ptr = comma ? comma + 1 : NULL,
761  comma = ptr ? strchr(ptr, ',') : NULL)
762  {
763  LONG_PTR index;
764 
765  if (comma)
766  *comma = 0;
767  if ((index = find_oid_in_list(lv, ptr)) != -1)
769  }
770  HeapFree(GetProcessHeap(), 0, disabledUsages);
771  }
772  break;
773  }
774  case WM_NOTIFY:
775  {
776  NMHDR *hdr = (NMHDR *)lp;
777  NMITEMACTIVATE *nm;
778 
779  switch (hdr->code)
780  {
781  case NM_CLICK:
782  nm = (NMITEMACTIVATE *)lp;
783  toggle_usage(hwnd, nm->iItem);
785  break;
786  }
787  break;
788  }
789  case WM_COMMAND:
790  switch (wp)
791  {
792  case IDOK:
795  EndDialog(hwnd, IDOK);
796  break;
797  case IDCANCEL:
800  break;
801  }
802  break;
803  }
804  return 0;
805 }
806 
808 {
813  (LPARAM)empty);
815 }
816 
818 {
820  LVITEMW item;
821 
822  item.mask = LVIF_PARAM;
823  item.iItem = index;
824  item.iSubItem = 0;
826  (LPARAM)&item))
827  cert = (PCCERT_CONTEXT)item.lParam;
828  return cert;
829 }
830 
832 {
834 
835  if (cert)
836  {
838 
839  memset(&viewInfo, 0, sizeof(viewInfo));
840  viewInfo.dwSize = sizeof(viewInfo);
841  viewInfo.hwndParent = hwnd;
842  viewInfo.pCertContext = cert;
843  /* FIXME: this should be modal */
844  CryptUIDlgViewCertificateW(&viewInfo, NULL);
845  }
846 }
847 
849 {
851  DWORD size;
852 
853  /* Get enhanced key usage. Have to check for a property and an extension
854  * separately, because CertGetEnhancedKeyUsage will succeed and return an
855  * empty usage if neither is set. Unfortunately an empty usage implies
856  * no usage is allowed, so we have to distinguish between the two cases.
857  */
859  NULL, &size))
860  {
864  {
866  usage = NULL;
867  }
868  }
870  NULL, &size))
871  {
875  {
877  usage = NULL;
878  }
879  }
880  else
881  usage = NULL;
882  if (usage)
883  {
884  if (usage->cUsageIdentifier)
885  {
886  static const WCHAR commaSpace[] = { ',',' ',0 };
887  DWORD i, len = 1;
888  LPWSTR ptr;
889 
890  for (i = 0; i < usage->cUsageIdentifier; i++)
891  {
894  usage->rgpszUsageIdentifier[i],
896 
897  if (info)
898  len += strlenW(info->pwszName);
899  else
900  len += strlen(usage->rgpszUsageIdentifier[i]);
901  if (i < usage->cUsageIdentifier - 1)
902  len += strlenW(commaSpace);
903  }
904  *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
905  if (*str)
906  {
907  for (i = 0, ptr = *str; i < usage->cUsageIdentifier; i++)
908  {
911  usage->rgpszUsageIdentifier[i],
913 
914  if (info)
915  {
916  strcpyW(ptr, info->pwszName);
917  ptr += strlenW(info->pwszName);
918  }
919  else
920  {
921  LPCSTR src = usage->rgpszUsageIdentifier[i];
922 
923  for (; *src; ptr++, src++)
924  *ptr = *src;
925  *ptr = 0;
926  }
927  if (i < usage->cUsageIdentifier - 1)
928  {
930  ptr += strlenW(commaSpace);
931  }
932  }
933  *ptr = 0;
934  }
936  }
937  else
938  {
939  size = MAX_STRING_LEN * sizeof(WCHAR);
940  *str = HeapAlloc(GetProcessHeap(), 0, size);
941  if (*str)
943  }
944  }
945  else
946  {
947  size = MAX_STRING_LEN * sizeof(WCHAR);
948  *str = HeapAlloc(GetProcessHeap(), 0, size);
949  if (*str)
951  }
952 }
953 
955 {
958  LPWSTR str = NULL;
959 
961  if (str)
962  {
964  HeapFree(GetProcessHeap(), 0, str);
965  }
966 }
967 
969 {
970  int tabIndex = SendMessageW(GetDlgItem(hwnd, IDC_MGR_STORES),
971  TCM_GETCURSEL, 0, 0);
972  struct CertMgrData *data =
974 
975  if (tabIndex < data->nStores)
976  {
979  LPCWSTR pTitle;
980  int warningID;
981 
982  if (SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0) > 1)
983  warningID = data->stores[tabIndex].removePluralWarning;
984  else
985  warningID = data->stores[tabIndex].removeWarning;
986  if (data->title)
987  pTitle = data->title;
988  else
989  {
991  pTitle = title;
992  }
994  if (MessageBoxW(hwnd, warning, pTitle, MB_YESNO) == IDYES)
995  {
996  int selection = -1;
997 
998  do {
1000  LVNI_SELECTED);
1001  if (selection >= 0)
1002  {
1004  selection);
1005 
1007  }
1008  } while (selection >= 0);
1010  }
1011  }
1012 }
1013 
1015 {
1017  int selectionCount = SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0);
1018 
1019  if (selectionCount == 1)
1020  {
1021  int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1,
1022  LVNI_SELECTED);
1023 
1024  if (selection >= 0)
1025  {
1027 
1028  if (cert)
1029  {
1031 
1032  info.dwSize = sizeof(info);
1033  info.pwszExportFileName = NULL;
1034  info.dwSubjectChoice = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT;
1035  info.u.pCertContext = cert;
1036  info.cStores = 0;
1038  }
1039  }
1040  }
1041  else if (selectionCount > 1)
1042  {
1045 
1046  if (store)
1047  {
1049  int selection = -1;
1050 
1051  info.dwSize = sizeof(info);
1052  info.pwszExportFileName = NULL;
1053  info.dwSubjectChoice =
1055  info.u.hCertStore = store;
1056  info.cStores = 0;
1057  do {
1059  LVNI_SELECTED);
1060  if (selection >= 0)
1061  {
1063  selection);
1064 
1067  }
1068  } while (selection >= 0);
1070  CertCloseStore(store, 0);
1071  }
1072  }
1073 }
1074 
1075 static int cert_mgr_sort_by_text(HWND lv, int col, int index1, int index2)
1076 {
1077  LVITEMW item;
1078  WCHAR buf1[MAX_STRING_LEN];
1079  WCHAR buf2[MAX_STRING_LEN];
1080 
1081  item.cchTextMax = ARRAY_SIZE(buf1);
1082  item.mask = LVIF_TEXT;
1083  item.pszText = buf1;
1084  item.iItem = index1;
1085  item.iSubItem = col;
1086  SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
1087  item.pszText = buf2;
1088  item.iItem = index2;
1089  SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item);
1090  return strcmpW(buf1, buf2);
1091 }
1092 
1094 {
1095  return cert_mgr_sort_by_text((HWND)lp, 0, lp1, lp2);
1096 }
1097 
1099 {
1100  return cert_mgr_sort_by_text((HWND)lp, 1, lp1, lp2);
1101 }
1102 
1104 {
1107  return CompareFileTime(&cert1->pCertInfo->NotAfter,
1108  &cert2->pCertInfo->NotAfter);
1109 }
1110 
1112  LPARAM lp)
1113 {
1114  return cert_mgr_sort_by_text((HWND)lp, 3, lp1, lp2);
1115 }
1116 
1118  LPARAM lp)
1119 {
1120  struct CertMgrData *data;
1121 
1122  switch (msg)
1123  {
1124  case WM_INITDIALOG:
1125  {
1126  PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr =
1129 
1130  data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CertMgrData));
1131  if (!data)
1132  return 0;
1133  data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0);
1134  if (data->imageList)
1135  {
1136  HBITMAP bmp;
1137  COLORREF backColor = RGB(255, 0, 255);
1138 
1140  ImageList_AddMasked(data->imageList, bmp, backColor);
1141  DeleteObject(bmp);
1142  ImageList_SetBkColor(data->imageList, CLR_NONE);
1144  LVSIL_SMALL, (LPARAM)data->imageList);
1145  }
1147  data->title = pCryptUICertMgr->pwszTitle;
1148 
1151  if (pCryptUICertMgr->pwszTitle)
1153  (LPARAM)pCryptUICertMgr->pwszTitle);
1154  show_cert_stores(hwnd, pCryptUICertMgr->dwFlags, data);
1156  break;
1157  }
1158  case WM_NOTIFY:
1159  {
1160  NMHDR *hdr = (NMHDR *)lp;
1161 
1162  switch (hdr->code)
1163  {
1164  case TCN_SELCHANGE:
1166  break;
1167  case LVN_ITEMCHANGED:
1168  {
1169  NMITEMACTIVATE *nm = (NMITEMACTIVATE*)lp;
1171  int numSelected = SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0);
1172 
1173  EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), numSelected > 0);
1174  EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), numSelected > 0);
1175  EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), numSelected == 1);
1176  if (numSelected == 1)
1178  else
1180  (LPARAM)empty);
1181  break;
1182  }
1183  case NM_DBLCLK:
1184  show_selected_cert(hwnd, ((NMITEMACTIVATE *)lp)->iItem);
1185  break;
1186  case LVN_KEYDOWN:
1187  {
1188  NMLVKEYDOWN *lvk = (NMLVKEYDOWN *)lp;
1189 
1190  if (lvk->wVKey == VK_DELETE)
1192  break;
1193  }
1194  case LVN_COLUMNCLICK:
1195  {
1196  NMLISTVIEW *nmlv = (NMLISTVIEW *)lp;
1198 
1199  /* FIXME: doesn't support swapping sort order between ascending
1200  * and descending.
1201  */
1202  switch (nmlv->iSubItem)
1203  {
1204  case 0:
1207  break;
1208  case 1:
1211  break;
1212  case 2:
1213  SendMessageW(lv, LVM_SORTITEMS, 0,
1215  break;
1216  case 3:
1219  break;
1220  }
1221  break;
1222  }
1223  }
1224  break;
1225  }
1226  case WM_COMMAND:
1227  switch (wp)
1228  {
1229  case ((CBN_SELCHANGE << 16) | IDC_MGR_PURPOSE_SELECTION):
1231  break;
1232  case IDC_MGR_IMPORT:
1233  if (CryptUIWizImport(0, hwnd, NULL, NULL,
1236  break;
1237  case IDC_MGR_ADVANCED:
1240  {
1242  int index, len;
1243  LPWSTR curString = NULL;
1244 
1245  index = SendMessageW(cb, CB_GETCURSEL, 0, 0);
1246  if (index >= 0)
1247  {
1249  curString = HeapAlloc(GetProcessHeap(), 0,
1250  (len + 1) * sizeof(WCHAR));
1251  SendMessageW(cb, CB_GETLBTEXT, index, (LPARAM)curString);
1252  }
1255  if (curString)
1256  {
1258  (LPARAM)curString);
1259  if (index >= 0)
1261  HeapFree(GetProcessHeap(), 0, curString);
1262  }
1264  }
1265  break;
1266  case IDC_MGR_VIEW:
1267  {
1269  int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1,
1270  LVNI_SELECTED);
1271 
1272  if (selection >= 0)
1274  break;
1275  }
1276  case IDC_MGR_EXPORT:
1278  break;
1279  case IDC_MGR_REMOVE:
1281  break;
1282  case IDCANCEL:
1286  ImageList_Destroy(data->imageList);
1287  HeapFree(GetProcessHeap(), 0, data);
1289  break;
1290  }
1291  break;
1292  }
1293  return 0;
1294 }
1295 
1296 /***********************************************************************
1297  * CryptUIDlgCertMgr (CRYPTUI.@)
1298  */
1300 {
1301  TRACE("(%p)\n", pCryptUICertMgr);
1302 
1303  if (pCryptUICertMgr->dwSize != sizeof(CRYPTUI_CERT_MGR_STRUCT))
1304  {
1305  WARN("unexpected size %d\n", pCryptUICertMgr->dwSize);
1307  return FALSE;
1308  }
1310  pCryptUICertMgr->hwndParent, cert_mgr_dlg_proc, (LPARAM)pCryptUICertMgr);
1311  return TRUE;
1312 }
1313 
1314 /* FIXME: real names are unknown, functions are undocumented */
1316 {
1320 
1321 typedef struct _CRYPTUI_ENUM_DATA
1322 {
1328 
1330  void *pvArg);
1331 
1332 /* Values for dwFlags */
1333 #define CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE 0x00000001
1334 
1336 {
1344  void *pvArg;
1346 
1348 {
1356  void *pvArg;
1358 
1360 {
1361  enum {
1364  } type;
1365  union {
1368  } DUMMYUNIONNAME;
1369 };
1370 
1371 static BOOL WINAPI enum_store_callback(const void *pvSystemStore,
1372  DWORD dwFlags, PCERT_SYSTEM_STORE_INFO pStoreInfo, void *pvReserved,
1373  void *pvArg)
1374 {
1375  HWND tree = GetDlgItem(pvArg, IDC_STORE_LIST);
1376  TVINSERTSTRUCTW tvis;
1377  LPCWSTR localizedName;
1378  BOOL ret = TRUE;
1379 
1380  tvis.hParent = NULL;
1381  tvis.hInsertAfter = TVI_LAST;
1382  tvis.u.item.mask = TVIF_TEXT;
1383  if ((localizedName = CryptFindLocalizedName(pvSystemStore)))
1384  {
1385  struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(), 0,
1386  sizeof(struct StoreInfo));
1387 
1388  if (storeInfo)
1389  {
1390  storeInfo->type = SystemStore;
1391  storeInfo->u.name = HeapAlloc(GetProcessHeap(), 0,
1392  (strlenW(pvSystemStore) + 1) * sizeof(WCHAR));
1393  if (storeInfo->u.name)
1394  {
1395  tvis.u.item.mask |= TVIF_PARAM;
1396  tvis.u.item.lParam = (LPARAM)storeInfo;
1397  strcpyW(storeInfo->u.name, pvSystemStore);
1398  }
1399  else
1400  {
1401  HeapFree(GetProcessHeap(), 0, storeInfo);
1402  ret = FALSE;
1403  }
1404  }
1405  else
1406  ret = FALSE;
1407  tvis.u.item.pszText = (LPWSTR)localizedName;
1408  }
1409  else
1410  tvis.u.item.pszText = (LPWSTR)pvSystemStore;
1411  /* FIXME: need a folder icon for the store too */
1412  if (ret)
1413  SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis);
1414  return ret;
1415 }
1416 
1418 {
1419  DWORD i;
1421 
1422  for (i = 0; i < pEnumData->cEnumArgs; i++)
1426  for (i = 0; i < pEnumData->cStores; i++)
1427  {
1428  DWORD size;
1429 
1430  if (CertGetStoreProperty(pEnumData->rghStore[i],
1432  {
1434 
1435  if (name)
1436  {
1437  if (CertGetStoreProperty(pEnumData->rghStore[i],
1439  {
1440  struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(),
1441  0, sizeof(struct StoreInfo));
1442 
1443  if (storeInfo)
1444  {
1445  TVINSERTSTRUCTW tvis;
1446 
1447  storeInfo->type = StoreHandle;
1448  storeInfo->u.store = pEnumData->rghStore[i];
1449  tvis.hParent = NULL;
1450  tvis.hInsertAfter = TVI_LAST;
1451  tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
1452  tvis.u.item.pszText = name;
1453  tvis.u.item.lParam = (LPARAM)storeInfo;
1454  SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis);
1455  }
1456  }
1457  HeapFree(GetProcessHeap(), 0, name);
1458  }
1459  }
1460  }
1461 }
1462 
1464 {
1466  0);
1467 
1468  while (next)
1469  {
1470  TVITEMW item;
1471 
1472  memset(&item, 0, sizeof(item));
1473  item.mask = TVIF_HANDLE | TVIF_PARAM;
1474  item.hItem = next;
1476  if (item.lParam)
1477  {
1478  struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam;
1479 
1480  if (storeInfo->type == SystemStore)
1481  HeapFree(GetProcessHeap(), 0, storeInfo->u.name);
1482  HeapFree(GetProcessHeap(), 0, storeInfo);
1483  }
1485  (LPARAM)next);
1486  }
1487 }
1488 
1490 {
1492  TVITEMW item;
1493  HCERTSTORE store;
1494 
1495  memset(&item, 0, sizeof(item));
1496  item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
1497  item.hItem = hItem;
1498  item.cchTextMax = ARRAY_SIZE(buf);
1499  item.pszText = buf;
1501  if (item.lParam)
1502  {
1503  struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam;
1504 
1505  if (storeInfo->type == StoreHandle)
1506  store = storeInfo->u.store;
1507  else
1508  store = CertOpenSystemStoreW(0, storeInfo->u.name);
1509  }
1510  else
1511  {
1512  /* It's implicitly a system store */
1514  }
1515  return store;
1516 }
1517 
1519 {
1522 };
1523 
1525  LPARAM lp)
1526 {
1527  struct SelectStoreInfo *selectInfo;
1528  LRESULT ret = 0;
1529 
1530  switch (msg)
1531  {
1532  case WM_INITDIALOG:
1533  {
1534  selectInfo = (struct SelectStoreInfo *)lp;
1536  if (selectInfo->info->pwszTitle)
1538  (LPARAM)selectInfo->info->pwszTitle);
1539  if (selectInfo->info->pwszText)
1541  (LPARAM)selectInfo->info->pwszText);
1542  if (!(selectInfo->info->dwFlags & CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE))
1544  enumerate_stores(hwnd, selectInfo->info->pEnumData);
1545  break;
1546  }
1547  case WM_COMMAND:
1548  switch (wp)
1549  {
1550  case IDOK:
1551  {
1555 
1556  selectInfo = (struct SelectStoreInfo *)GetWindowLongPtrW(hwnd,
1557  DWLP_USER);
1558  if (!selection)
1559  {
1561 
1562  if (selectInfo->info->pwszTitle)
1563  pTitle = selectInfo->info->pwszTitle;
1564  else
1565  {
1567  pTitle = title;
1568  }
1571  }
1572  else
1573  {
1575 
1576  if (!selectInfo->info->pfnSelectedStoreCallback ||
1577  selectInfo->info->pfnSelectedStoreCallback(store, hwnd,
1578  selectInfo->info->pvArg))
1579  {
1580  selectInfo->store = store;
1582  EndDialog(hwnd, IDOK);
1583  }
1584  else
1585  CertCloseStore(store, 0);
1586  }
1587  ret = TRUE;
1588  break;
1589  }
1590  case IDCANCEL:
1593  ret = TRUE;
1594  break;
1595  }
1596  break;
1597  }
1598  return ret;
1599 }
1600 
1601 /***********************************************************************
1602  * CryptUIDlgSelectStoreW (CRYPTUI.@)
1603  */
1605 {
1606  struct SelectStoreInfo selectInfo = { info, NULL };
1607 
1608  TRACE("(%p)\n", info);
1609 
1610  if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_W))
1611  {
1612  WARN("unexpected size %d\n", info->dwSize);
1614  return NULL;
1615  }
1617  select_store_dlg_proc, (LPARAM)&selectInfo);
1618  return selectInfo.store;
1619 }
1620 
1621 /***********************************************************************
1622  * CryptUIDlgSelectStoreA (CRYPTUI.@)
1623  */
1625 {
1627  HCERTSTORE ret;
1628  int len;
1629 
1630  TRACE("(%p)\n", info);
1631 
1632  if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_A))
1633  {
1634  WARN("unexpected size %d\n", info->dwSize);
1636  return NULL;
1637  }
1638  memcpy(&infoW, info, sizeof(*info));
1639  if (info->pszTitle)
1640  {
1641  len = MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, NULL, 0);
1642  infoW.pwszTitle = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1643  MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, infoW.pwszTitle,
1644  len);
1645  }
1646  if (info->pszText)
1647  {
1648  len = MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, NULL, 0);
1649  infoW.pwszText = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1650  MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, infoW.pwszText, len);
1651  }
1653  HeapFree(GetProcessHeap(), 0, infoW.pwszText);
1654  HeapFree(GetProcessHeap(), 0, infoW.pwszTitle);
1655  return ret;
1656 }
1657 
1658 /***********************************************************************
1659  * CryptUIDlgViewCertificateA (CRYPTUI.@)
1660  */
1662  PCCRYPTUI_VIEWCERTIFICATE_STRUCTA pCertViewInfo, BOOL *pfPropertiesChanged)
1663 {
1665  LPWSTR title = NULL;
1666  BOOL ret;
1667 
1668  TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged);
1669 
1670  memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo));
1671  if (pCertViewInfo->szTitle)
1672  {
1673  int len = MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1,
1674  NULL, 0);
1675 
1676  title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1677  if (title)
1678  {
1679  MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, title,
1680  len);
1681  viewInfo.szTitle = title;
1682  }
1683  else
1684  {
1685  ret = FALSE;
1686  goto error;
1687  }
1688  }
1689  if (pCertViewInfo->cPropSheetPages)
1690  {
1691  FIXME("ignoring additional prop sheet pages\n");
1692  viewInfo.cPropSheetPages = 0;
1693  }
1694  ret = CryptUIDlgViewCertificateW(&viewInfo, pfPropertiesChanged);
1695  HeapFree(GetProcessHeap(), 0, title);
1696 error:
1697  return ret;
1698 }
1699 
1701 {
1705 };
1706 
1708  LONG cb, LONG *pcb)
1709 {
1710  struct ReadStringStruct *string = (struct ReadStringStruct *)dwCookie;
1711  LONG cch = min(cb / sizeof(WCHAR), string->len - string->pos);
1712 
1713  TRACE("(%p, %p, %d, %p)\n", string, buf, cb, pcb);
1714 
1715  memmove(buf, string->buf + string->pos, cch * sizeof(WCHAR));
1716  string->pos += cch;
1717  *pcb = cch * sizeof(WCHAR);
1718  return 0;
1719 }
1720 
1722 {
1723  struct ReadStringStruct string;
1724  EDITSTREAM editstream;
1725 
1726  TRACE("(%p, %s)\n", hwnd, debugstr_wn(text, len));
1727 
1728  string.buf = text;
1729  string.pos = 0;
1730  string.len = len;
1731  editstream.dwCookie = (DWORD_PTR)&string;
1732  editstream.dwError = 0;
1733  editstream.pfnCallback = read_text_callback;
1735  (LPARAM)&editstream);
1736 }
1737 
1739 {
1740  LPWSTR str;
1741  LONG len;
1742 
1743  len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
1745 }
1746 
1748  LONG len, const PARAFORMAT2 *fmt)
1749 {
1752 }
1753 
1755  const PARAFORMAT2 *fmt)
1756 {
1757  LPWSTR str;
1758  LONG len;
1759 
1760  len = LoadStringW(hInstance, id, (LPWSTR)&str, 0);
1762 }
1763 
1765  DWORD dwFlags)
1766 {
1767  LPWSTR buf = NULL;
1768  DWORD len;
1769 
1771  if (len)
1772  {
1773  buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1774  if (buf)
1776  }
1777  return buf;
1778 }
1779 
1781  DWORD dwType, DWORD dwFlags)
1782 {
1784 
1785  if (name)
1786  {
1787  /* Don't include NULL-terminator in output */
1788  DWORD len = lstrlenW(name);
1789 
1791  HeapFree(GetProcessHeap(), 0, name);
1792  }
1793 }
1794 
1795 static void add_icon_to_control(HWND hwnd, int id)
1796 {
1797  HRESULT hr;
1798  IRichEditOle *richEditOle = NULL;
1799  IOleObject *object = NULL;
1800  CLSID clsid;
1801  LPOLECACHE oleCache = NULL;
1802  FORMATETC formatEtc;
1803  DWORD conn;
1804  IDataObject *dataObject = NULL;
1805  HBITMAP bitmap = NULL;
1806  STGMEDIUM stgm;
1807  IOleClientSite *clientSite = NULL;
1808  REOBJECT reObject;
1809 
1810  TRACE("(%p, %d)\n", hwnd, id);
1811 
1812  SendMessageW(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richEditOle);
1813  if (!richEditOle)
1814  goto end;
1816  (void**)&object);
1817  if (FAILED(hr))
1818  goto end;
1819  hr = IOleObject_GetUserClassID(object, &clsid);
1820  if (FAILED(hr))
1821  goto end;
1822  hr = IOleObject_QueryInterface(object, &IID_IOleCache, (void**)&oleCache);
1823  if (FAILED(hr))
1824  goto end;
1825  formatEtc.cfFormat = CF_BITMAP;
1826  formatEtc.ptd = NULL;
1827  formatEtc.dwAspect = DVASPECT_CONTENT;
1828  formatEtc.lindex = -1;
1829  formatEtc.tymed = TYMED_GDI;
1830  hr = IOleCache_Cache(oleCache, &formatEtc, 0, &conn);
1831  if (FAILED(hr))
1832  goto end;
1833  hr = IOleObject_QueryInterface(object, &IID_IDataObject,
1834  (void**)&dataObject);
1835  if (FAILED(hr))
1836  goto end;
1837  hr = IRichEditOle_GetClientSite(richEditOle, &clientSite);
1838  if (FAILED(hr))
1839  goto end;
1842  if (!bitmap)
1843  goto end;
1844  stgm.tymed = TYMED_GDI;
1845  stgm.u.hBitmap = bitmap;
1846  stgm.pUnkForRelease = NULL;
1847  hr = IDataObject_SetData(dataObject, &formatEtc, &stgm, TRUE);
1848  if (FAILED(hr))
1849  goto end;
1850 
1851  reObject.cbStruct = sizeof(reObject);
1852  reObject.cp = REO_CP_SELECTION;
1853  reObject.clsid = clsid;
1854  reObject.poleobj = object;
1855  reObject.pstg = NULL;
1856  reObject.polesite = clientSite;
1857  reObject.sizel.cx = reObject.sizel.cy = 0;
1858  reObject.dvaspect = DVASPECT_CONTENT;
1859  reObject.dwFlags = 0;
1860  reObject.dwUser = 0;
1861 
1862  IRichEditOle_InsertObject(richEditOle, &reObject);
1863 
1864 end:
1865  if (clientSite)
1866  IOleClientSite_Release(clientSite);
1867  if (dataObject)
1868  IDataObject_Release(dataObject);
1869  if (oleCache)
1870  IOleCache_Release(oleCache);
1871  if (object)
1872  IOleObject_Release(object);
1873  if (richEditOle)
1874  IRichEditOle_Release(richEditOle);
1875 }
1876 
1877 #define MY_INDENT 200
1878 
1879 static void add_oid_text_to_control(HWND hwnd, char *oid)
1880 {
1881  WCHAR nl = '\n';
1883  PARAFORMAT2 parFmt;
1884 
1885  parFmt.cbSize = sizeof(parFmt);
1886  parFmt.dwMask = PFM_STARTINDENT;
1887  parFmt.dxStartIndent = MY_INDENT * 3;
1888  if (oidInfo)
1889  {
1891  lstrlenW(oidInfo->pwszName), &parFmt);
1893  }
1894 }
1895 
1897 {
1899  int id;
1900 };
1901 
1902 /* The following list MUST be lexicographically sorted by OID */
1903 static struct OIDToString oidMap[] = {
1904  /* 1.3.6.1.4.1.311.10.3.1 */
1906  /* 1.3.6.1.4.1.311.10.3.4 */
1908  /* 1.3.6.1.4.1.311.10.3.4.1 */
1910  /* 1.3.6.1.4.1.311.10.3.5 */
1912  /* 1.3.6.1.4.1.311.10.3.6 */
1914  /* 1.3.6.1.4.1.311.10.3.7 */
1916  /* 1.3.6.1.4.1.311.10.3.8 */
1918  /* 1.3.6.1.4.1.311.10.3.9 */
1920  /* 1.3.6.1.4.1.311.10.3.10 */
1922  /* 1.3.6.1.4.1.311.10.3.11 */
1924  /* 1.3.6.1.4.1.311.10.3.12 */
1926  /* 1.3.6.1.4.1.311.10.3.13 */
1928  /* 1.3.6.1.4.1.311.10.5.1 */
1930  /* 1.3.6.1.4.1.311.10.6.1 */
1932  /* 1.3.6.1.4.1.311.10.6.2 */
1934  /* 1.3.6.1.4.1.311.20.2.1 */
1936  /* 1.3.6.1.4.1.311.20.2.2 */
1938  /* 1.3.6.1.4.1.311.21.5 */
1940  /* 1.3.6.1.4.1.311.21.6 */
1942  /* 1.3.6.1.4.1.311.21.19 */
1944  /* 1.3.6.1.5.5.7.3.1 */
1946  /* 1.3.6.1.5.5.7.3.2 */
1948  /* 1.3.6.1.5.5.7.3.3 */
1950  /* 1.3.6.1.5.5.7.3.4 */
1952  /* 1.3.6.1.5.5.7.3.5 */
1954  /* 1.3.6.1.5.5.7.3.6 */
1956  /* 1.3.6.1.5.5.7.3.7 */
1958  /* 1.3.6.1.5.5.7.3.8 */
1960 };
1961 
1963 {
1964  int indexHigh = ARRAY_SIZE(oidMap) - 1, indexLow = 0;
1965 
1966  while (indexLow <= indexHigh)
1967  {
1968  int cmp, i = (indexLow + indexHigh) / 2;
1969  if (!(cmp = strcmp(oid, oidMap[i].oid)))
1970  return &oidMap[i];
1971  if (cmp > 0)
1972  indexLow = i + 1;
1973  else
1974  indexHigh = i - 1;
1975  }
1976  return NULL;
1977 }
1978 
1980 {
1981  struct OIDToString *entry;
1982  WCHAR nl = '\n';
1983  PARAFORMAT2 parFmt;
1984 
1985  parFmt.cbSize = sizeof(parFmt);
1986  parFmt.dwMask = PFM_STARTINDENT;
1987  parFmt.dxStartIndent = MY_INDENT * 3;
1988  if ((entry = findSupportedOID(oid)))
1989  {
1990  WCHAR *str, *linebreak, *ptr;
1991  BOOL multiline = FALSE;
1992  int len;
1993 
1994  len = LoadStringW(hInstance, entry->id, (LPWSTR)&str, 0);
1995  ptr = str;
1996  do {
1997  if ((linebreak = memchrW(ptr, '\n', len)))
1998  {
2000 
2001  multiline = TRUE;
2002  /* The source string contains a newline, which the richedit
2003  * control won't find since it's interpreted as a paragraph
2004  * break. Therefore copy up to the newline. lstrcpynW always
2005  * NULL-terminates, so pass one more than the length of the
2006  * source line so the copy includes the entire line and the
2007  * NULL-terminator.
2008  */
2009  lstrcpynW(copy, ptr, linebreak - ptr + 1);
2011  linebreak - ptr, &parFmt);
2012  ptr = linebreak + 1;
2014  }
2015  else if (multiline && *ptr)
2016  {
2017  /* Add the last line */
2019  len - (ptr - str), &parFmt);
2021  }
2022  } while (linebreak);
2023  if (!multiline)
2024  {
2027  }
2028  }
2029  else
2030  {
2031  WCHAR *oidW = HeapAlloc(GetProcessHeap(), 0,
2032  (strlen(oid) + 1) * sizeof(WCHAR));
2033 
2034  if (oidW)
2035  {
2036  LPCSTR src;
2037  WCHAR *dst;
2038 
2039  for (src = oid, dst = oidW; *src; src++, dst++)
2040  *dst = *src;
2041  *dst = 0;
2043  &parFmt);
2045  HeapFree(GetProcessHeap(), 0, oidW);
2046  }
2047  }
2048 }
2049 
2051  BOOL *anyUsageAdded)
2052 {
2053  static char any_app_policy[] = szOID_ANY_APPLICATION_POLICY;
2054  WCHAR nl = '\n';
2055  CHARFORMATW charFmt;
2056  PCERT_EXTENSION policyExt;
2057  if (!*anyUsageAdded)
2058  {
2059  PARAFORMAT2 parFmt;
2060 
2061  parFmt.cbSize = sizeof(parFmt);
2062  parFmt.dwMask = PFM_STARTINDENT;
2063  parFmt.dxStartIndent = MY_INDENT;
2065  IDS_CERT_INFO_PURPOSES, &parFmt);
2067  *anyUsageAdded = TRUE;
2068  }
2069  memset(&charFmt, 0, sizeof(charFmt));
2070  charFmt.cbSize = sizeof(charFmt);
2071  charFmt.dwMask = CFM_BOLD;
2072  charFmt.dwEffects = 0;
2075  cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
2076  {
2077  CERT_POLICIES_INFO *policies;
2078  DWORD size;
2079 
2081  policyExt->Value.pbData, policyExt->Value.cbData,
2082  CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
2083  {
2084  DWORD i;
2085 
2086  for (i = 0; i < policies->cPolicyInfo; i++)
2087  {
2088  DWORD j;
2089 
2090  for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
2092  policies->rgPolicyInfo[i].rgPolicyQualifier[j].
2093  pszPolicyQualifierId);
2094  }
2095  LocalFree(policies);
2096  }
2097  }
2098  else
2099  add_oid_text_to_control(text, any_app_policy);
2100 }
2101 
2103  BOOL *anyUsageAdded)
2104 {
2105  WCHAR nl = '\n';
2106  DWORD size;
2107  BOOL badUsages = FALSE;
2108 
2110  {
2111  CHARFORMATW charFmt;
2112  static char any_cert_policy[] = szOID_ANY_CERT_POLICY;
2114 
2115  if (usage)
2116  {
2118  {
2119  DWORD i;
2120 
2121  if (!*anyUsageAdded)
2122  {
2123  PARAFORMAT2 parFmt;
2124 
2125  parFmt.cbSize = sizeof(parFmt);
2126  parFmt.dwMask = PFM_STARTINDENT;
2127  parFmt.dxStartIndent = MY_INDENT;
2129  IDS_CERT_INFO_PURPOSES, &parFmt);
2131  *anyUsageAdded = TRUE;
2132  }
2133  memset(&charFmt, 0, sizeof(charFmt));
2134  charFmt.cbSize = sizeof(charFmt);
2135  charFmt.dwMask = CFM_BOLD;
2136  charFmt.dwEffects = 0;
2138  (LPARAM)&charFmt);
2139  if (!usage->cUsageIdentifier)
2140  add_oid_text_to_control(text, any_cert_policy);
2141  else
2142  for (i = 0; i < usage->cUsageIdentifier; i++)
2144  usage->rgpszUsageIdentifier[i]);
2145  }
2146  else
2147  badUsages = TRUE;
2148  HeapFree(GetProcessHeap(), 0, usage);
2149  }
2150  else
2151  badUsages = TRUE;
2152  }
2153  else
2154  badUsages = TRUE;
2155  return badUsages;
2156 }
2157 
2159  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2160 {
2161  BOOL includeCertUsages = FALSE, includeAppUsages = FALSE;
2162  BOOL badUsages = FALSE, anyUsageAdded = FALSE;
2163 
2164  if (pCertViewInfo->cPurposes)
2165  {
2166  DWORD i;
2167 
2168  for (i = 0; i < pCertViewInfo->cPurposes; i++)
2169  {
2170  if (!strcmp(pCertViewInfo->rgszPurposes[i], szOID_ANY_CERT_POLICY))
2171  includeCertUsages = TRUE;
2172  else if (!strcmp(pCertViewInfo->rgszPurposes[i],
2174  includeAppUsages = TRUE;
2175  else
2176  badUsages = TRUE;
2177  }
2178  }
2179  else
2180  includeAppUsages = includeCertUsages = TRUE;
2181  if (includeAppUsages)
2182  display_app_usages(text, pCertViewInfo->pCertContext, &anyUsageAdded);
2183  if (includeCertUsages)
2184  badUsages = display_cert_usages(text, pCertViewInfo->pCertContext,
2185  &anyUsageAdded);
2186  if (badUsages)
2187  {
2188  PARAFORMAT2 parFmt;
2189 
2190  parFmt.cbSize = sizeof(parFmt);
2191  parFmt.dwMask = PFM_STARTINDENT;
2192  parFmt.dxStartIndent = MY_INDENT;
2194  IDS_CERT_INFO_BAD_PURPOSES, &parFmt);
2195  }
2196 }
2197 
2199  LPCSTR policyOid)
2200 {
2202  DWORD i;
2203 
2204  for (i = 0; !ret && i < policies->cPolicyInfo; i++)
2205  {
2206  DWORD j;
2207 
2208  for (j = 0; !ret && j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
2209  if (!strcmp(policies->rgPolicyInfo[i].rgPolicyQualifier[j].
2210  pszPolicyQualifierId, policyOid))
2211  ret = &policies->rgPolicyInfo[i].rgPolicyQualifier[j].
2212  Qualifier;
2213  }
2214  return ret;
2215 }
2216 
2218 {
2219  LPWSTR qualifierStr = NULL;
2220  CERT_NAME_VALUE *qualifierValue;
2221  DWORD size;
2222 
2224  qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
2225  &qualifierValue, &size))
2226  {
2227  size = CertRDNValueToStrW(qualifierValue->dwValueType,
2228  &qualifierValue->Value, NULL, 0);
2229  qualifierStr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
2230  if (qualifierStr)
2231  CertRDNValueToStrW(qualifierValue->dwValueType,
2232  &qualifierValue->Value, qualifierStr, size);
2233  LocalFree(qualifierValue);
2234  }
2235  return qualifierStr;
2236 }
2237 
2239 {
2240  LPWSTR str = NULL;
2241  CERT_POLICY_QUALIFIER_USER_NOTICE *qualifierValue;
2242  DWORD size;
2243 
2246  qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
2247  &qualifierValue, &size))
2248  {
2249  str = HeapAlloc(GetProcessHeap(), 0,
2250  (strlenW(qualifierValue->pszDisplayText) + 1) * sizeof(WCHAR));
2251  if (str)
2252  strcpyW(str, qualifierValue->pszDisplayText);
2253  LocalFree(qualifierValue);
2254  }
2255  return str;
2256 }
2257 
2259 {
2262 };
2263 
2265  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2266 {
2267  PCERT_EXTENSION policyExt;
2268 
2269  if (!(pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ISSUERSTATEMENT) &&
2271  pCertViewInfo->pCertContext->pCertInfo->cExtension,
2272  pCertViewInfo->pCertContext->pCertInfo->rgExtension)))
2273  {
2274  CERT_POLICIES_INFO *policies;
2275  DWORD size;
2276 
2278  policyExt->Value.pbData, policyExt->Value.cbData,
2279  CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size))
2280  {
2282  LPWSTR cps = NULL, userNotice = NULL;
2283 
2284  if ((qualifier = find_policy_qualifier(policies,
2287  if ((qualifier = find_policy_qualifier(policies,
2290  if (cps || userNotice)
2291  {
2292  struct IssuerStatement *issuerStatement =
2293  HeapAlloc(GetProcessHeap(), 0, sizeof(struct IssuerStatement));
2294 
2295  if (issuerStatement)
2296  {
2297  issuerStatement->cps = cps;
2298  issuerStatement->userNotice = userNotice;
2301  (ULONG_PTR)issuerStatement);
2302  }
2303  }
2304  LocalFree(policies);
2305  }
2306  }
2307 }
2308 
2310  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2311 {
2312  CHARFORMATW charFmt;
2313  PARAFORMAT2 parFmt;
2317  (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData,
2318  pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner,
2319  pCertViewInfo->idxCounterSigner);
2321  &provSigner->pasCertChain[provSigner->csCertChain - 1];
2322 
2323  if (!provSigner->pChainContext ||
2324  (provSigner->pChainContext->TrustStatus.dwErrorStatus &
2327  else if (!root->fTrustedRoot)
2329  else
2331 
2332  memset(&charFmt, 0, sizeof(charFmt));
2333  charFmt.cbSize = sizeof(charFmt);
2334  charFmt.dwMask = CFM_BOLD;
2335  charFmt.dwEffects = CFE_BOLD;
2337  /* FIXME: vertically center text */
2338  parFmt.cbSize = sizeof(parFmt);
2339  parFmt.dwMask = PFM_STARTINDENT;
2340  parFmt.dxStartIndent = MY_INDENT;
2342  IDS_CERTIFICATEINFORMATION, &parFmt);
2343 
2346  if (provSigner->dwError == TRUST_E_CERT_SIGNATURE)
2348  IDS_CERT_INFO_BAD_SIG, &parFmt);
2349  else if (!provSigner->pChainContext ||
2350  (provSigner->pChainContext->TrustStatus.dwErrorStatus &
2353  IDS_CERT_INFO_PARTIAL_CHAIN, &parFmt);
2354  else if (!root->fTrustedRoot)
2355  {
2356  if (provSigner->csCertChain == 1 && root->fSelfSigned)
2358  IDS_CERT_INFO_UNTRUSTED_CA, &parFmt);
2359  else
2361  IDS_CERT_INFO_UNTRUSTED_ROOT, &parFmt);
2362  }
2363  else
2364  {
2365  set_policy_text(text, pCertViewInfo);
2366  set_issuer_statement(hwnd, pCertViewInfo);
2367  }
2368 }
2369 
2371  DWORD nameFlags, int heading)
2372 {
2373  WCHAR nl = '\n';
2375  CHARFORMATW charFmt;
2376  PARAFORMAT2 parFmt;
2377 
2378  memset(&charFmt, 0, sizeof(charFmt));
2379  charFmt.cbSize = sizeof(charFmt);
2380  charFmt.dwMask = CFM_BOLD;
2381  charFmt.dwEffects = CFE_BOLD;
2383  parFmt.cbSize = sizeof(parFmt);
2384  parFmt.dwMask = PFM_STARTINDENT;
2385  parFmt.dxStartIndent = MY_INDENT * 3;
2387  charFmt.dwEffects = 0;
2390  nameFlags);
2394 
2395 }
2396 
2398 {
2399  WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */
2400  WCHAR date[80];
2401  SYSTEMTIME sysTime;
2402 
2404  FileTimeToSystemTime(fileTime, &sysTime);
2405  GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date, ARRAY_SIZE(date));
2407 }
2408 
2410 {
2411  WCHAR nl = '\n';
2413  CHARFORMATW charFmt;
2414  PARAFORMAT2 parFmt;
2415 
2416  memset(&charFmt, 0, sizeof(charFmt));
2417  charFmt.cbSize = sizeof(charFmt);
2418  charFmt.dwMask = CFM_BOLD;
2419  charFmt.dwEffects = CFE_BOLD;
2421  parFmt.cbSize = sizeof(parFmt);
2422  parFmt.dwMask = PFM_STARTINDENT;
2423  parFmt.dxStartIndent = MY_INDENT * 3;
2425  &parFmt);
2426  charFmt.dwEffects = 0;
2428  add_date_string_to_control(text, &cert->pCertInfo->NotBefore);
2429  charFmt.dwEffects = CFE_BOLD;
2432  charFmt.dwEffects = 0;
2434  add_date_string_to_control(text, &cert->pCertInfo->NotAfter);
2436 }
2437 
2439  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo)
2440 {
2441  set_cert_info(hwnd, pCertViewInfo);
2442  set_cert_name_string(hwnd, pCertViewInfo->pCertContext, 0,
2444  set_cert_name_string(hwnd, pCertViewInfo->pCertContext,
2446  set_cert_validity_period(hwnd, pCertViewInfo->pCertContext);
2447 }
2448 
2450  LPARAM lp)
2451 {
2452  LRESULT ret = 0;
2453  HWND text;
2454  struct IssuerStatement *issuerStatement;
2455 
2456  switch (msg)
2457  {
2458  case WM_INITDIALOG:
2460  issuerStatement = (struct IssuerStatement *)lp;
2462  strlenW(issuerStatement->userNotice));
2463  if (issuerStatement->cps)
2464  SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)issuerStatement->cps);
2465  else
2467  break;
2468  case WM_COMMAND:
2469  switch (wp)
2470  {
2471  case IDOK:
2472  EndDialog(hwnd, IDOK);
2473  ret = TRUE;
2474  break;
2475  case IDC_CPS:
2476  {
2477  IBindCtx *bctx = NULL;
2478  LPWSTR cps;
2479 
2480  CreateBindCtx(0, &bctx);
2483  HLNF_OPENINNEWWINDOW, 0);
2484  IBindCtx_Release(bctx);
2485  break;
2486  }
2487  }
2488  }
2489  return ret;
2490 }
2491 
2492 static void show_user_notice(HWND hwnd, struct IssuerStatement *issuerStatement)
2493 {
2495  user_notice_dlg_proc, (LPARAM)issuerStatement);
2496 }
2497 
2499  LPARAM lp)
2500 {
2502  PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo;
2503 
2504  TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
2505 
2506  switch (msg)
2507  {
2508  case WM_INITDIALOG:
2509  page = (PROPSHEETPAGEW *)lp;
2510  pCertViewInfo = (PCCRYPTUI_VIEWCERTIFICATE_STRUCTW)page->lParam;
2511  if (pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ADDTOSTORE)
2514  set_general_info(hwnd, pCertViewInfo);
2515  break;
2516  case WM_COMMAND:
2517  switch (wp)
2518  {
2519  case IDC_ADDTOSTORE:
2521  break;
2522  case IDC_ISSUERSTATEMENT:
2523  {
2524  struct IssuerStatement *issuerStatement =
2526 
2527  if (issuerStatement)
2528  {
2529  if (issuerStatement->userNotice)
2530  show_user_notice(hwnd, issuerStatement);
2531  else if (issuerStatement->cps)
2532  {
2533  IBindCtx *bctx = NULL;
2534 
2535  CreateBindCtx(0, &bctx);
2536  HlinkSimpleNavigateToString(issuerStatement->cps, NULL,
2537  NULL, NULL, bctx, NULL, HLNF_OPENINNEWWINDOW, 0);
2538  IBindCtx_Release(bctx);
2539  }
2540  }
2541  break;
2542  }
2543  }
2544  break;
2545  }
2546  return 0;
2547 }
2548 
2551 {
2552  struct IssuerStatement *issuerStatement;
2553 
2554  switch (msg)
2555  {
2556  case PSPCB_RELEASE:
2557  issuerStatement =
2559  if (issuerStatement)
2560  {
2561  HeapFree(GetProcessHeap(), 0, issuerStatement->cps);
2562  HeapFree(GetProcessHeap(), 0, issuerStatement->userNotice);
2563  HeapFree(GetProcessHeap(), 0, issuerStatement);
2564  }
2565  break;
2566  }
2567  return 1;
2568 }
2569 
2572 {
2573  memset(page, 0, sizeof(PROPSHEETPAGEW));
2574  page->dwSize = sizeof(PROPSHEETPAGEW);
2575  page->dwFlags = PSP_USECALLBACK;
2576  page->pfnCallback = general_callback_proc;
2577  page->hInstance = hInstance;
2578  page->u.pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL);
2579  page->pfnDlgProc = general_dlg_proc;
2580  page->lParam = (LPARAM)pCertViewInfo;
2581 }
2582 
2583 typedef WCHAR * (*field_format_func)(PCCERT_CONTEXT cert);
2584 
2586 {
2587  static const WCHAR fmt[] = { 'V','%','d',0 };
2588  WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, 12 * sizeof(WCHAR));
2589 
2590  if (buf)
2591  sprintfW(buf, fmt, cert->pCertInfo->dwVersion);
2592  return buf;
2593 }
2594 
2595 static WCHAR *format_hex_string(void *pb, DWORD cb)
2596 {
2597  WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, (cb * 3 + 1) * sizeof(WCHAR));
2598 
2599  if (buf)
2600  {
2601  static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
2602  DWORD i;
2603  WCHAR *ptr;
2604 
2605  for (i = 0, ptr = buf; i < cb; i++, ptr += 3)
2606  sprintfW(ptr, fmt, ((BYTE *)pb)[i]);
2607  }
2608  return buf;
2609 }
2610 
2612 {
2613  return format_hex_string(cert->pCertInfo->SerialNumber.pbData,
2614  cert->pCertInfo->SerialNumber.cbData);
2615 }
2616 
2618 {
2621 }
2622 
2624 {
2625  WCHAR *str = NULL;
2628 
2629  if (len)
2630  {
2631  str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2632  if (str)
2635  }
2636  return str;
2637 }
2638 
2640 {
2641  return field_format_detailed_cert_name(&cert->pCertInfo->Issuer);
2642 }
2643 
2645 {
2647 }
2648 
2650 {
2651  return field_format_detailed_cert_name(&cert->pCertInfo->Subject);
2652 }
2653 
2655 {
2656  WCHAR dateFmt[80]; /* long enough for LOCALE_SLONGDATE */
2657  DWORD len;
2658  WCHAR *buf = NULL;
2659  SYSTEMTIME sysTime;
2660 
2661  /* FIXME: format isn't quite right, want time too */
2663  FileTimeToSystemTime(fileTime, &sysTime);
2664  len = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, NULL, 0);
2665  if (len)
2666  {
2667  buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2668  if (buf)
2669  GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, buf,
2670  len);
2671  }
2672  return buf;
2673 }
2674 
2676 {
2677  return format_long_date(&cert->pCertInfo->NotBefore);
2678 }
2679 
2681 {
2682  return format_long_date(&cert->pCertInfo->NotAfter);
2683 }
2684 
2686 {
2688  WCHAR *buf = NULL;
2689 
2691  cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 0);
2692  if (oidInfo)
2693  {
2695 
2697  {
2698  DWORD len;
2699 
2700  /* Allocate the output buffer. Use the number of bytes in the
2701  * public key as a conservative (high) estimate for the number of
2702  * digits in its output.
2703  * The output is of the form (in English)
2704  * "<public key algorithm> (<public key bit length> bits)".
2705  * Ordinarily having two positional parameters in a string is not a
2706  * good idea, but as this isn't a sentence fragment, it shouldn't
2707  * be word-order dependent.
2708  */
2709  len = strlenW(fmt) + strlenW(oidInfo->pwszName) +
2710  cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData * 8;
2711  buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*buf));
2712  if (buf)
2713  {
2714  DWORD_PTR args[2];
2715  args[0] = (DWORD_PTR)oidInfo->pwszName;
2717  &cert->pCertInfo->SubjectPublicKeyInfo);
2719  fmt, 0, 0, buf, len, (__ms_va_list*)args);
2720  }
2721  }
2722  }
2723  return buf;
2724 }
2725 
2727 {
2728  return format_hex_string(
2729  cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
2730  cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData);
2731 }
2732 
2733 struct field_value_data;
2735 {
2738  int cFields;
2740 };
2741 
2743 
2744 typedef WCHAR *(*create_detailed_value_func)(PCCERT_CONTEXT cert, void *param);
2745 
2747 {
2750  void *param;
2751 };
2752 
2755 {
2756  if (data->cFields)
2757  data->fields = HeapReAlloc(GetProcessHeap(), 0, data->fields,
2758  (data->cFields + 1) * sizeof(struct field_value_data));
2759  else
2760  data->fields = HeapAlloc(GetProcessHeap(), 0,
2761  sizeof(struct field_value_data));
2762  if (data->fields)
2763  {
2764  data->fields[data->cFields].create = create;
2765  data->fields[data->cFields].detailed_value = NULL;
2766  data->fields[data->cFields].param = param;
2767  data->cFields++;
2768  }
2769 }
2770 
2773 {
2774  LVITEMW item;
2775  int iItem = SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0);
2776 
2777  item.mask = LVIF_TEXT | LVIF_PARAM;
2778  item.iItem = iItem;
2779  item.iSubItem = 0;
2780  item.pszText = field;
2781  item.lParam = (LPARAM)data;
2783  if (value)
2784  {
2785  item.pszText = value;
2786  item.iSubItem = 1;
2788  }
2790 }
2791 
2794 {
2796 
2799 }
2800 
2801 struct v1_field
2802 {
2803  int id;
2806 };
2807 
2808 static void add_v1_field(HWND hwnd, struct detail_data *data,
2809  const struct v1_field *field)
2810 {
2811  WCHAR *val = field->format(data->pCertViewInfo->pCertContext);
2812 
2813  if (val)
2814  {
2816  field->create_detailed_value, NULL);
2817  HeapFree(GetProcessHeap(), 0, val);
2818  }
2819 }
2820 
2821 static const struct v1_field v1_fields[] = {
2830 };
2831 
2832 static void add_v1_fields(HWND hwnd, struct detail_data *data)
2833 {
2834  unsigned int i;
2835  PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2836 
2837  /* The last item in v1_fields is the public key, which is not in the loop
2838  * because it's a special case.
2839  */
2840  for (i = 0; i < ARRAY_SIZE(v1_fields) - 1; i++)
2842  if (cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData)
2844 }
2845 
2846 static WCHAR *crypt_format_extension(const CERT_EXTENSION *ext, DWORD formatStrType)
2847 {
2848  WCHAR *str = NULL;
2849  DWORD size;
2850 
2851  if (CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
2852  ext->pszObjId, ext->Value.pbData, ext->Value.cbData, NULL, &size))
2853  {
2854  str = HeapAlloc(GetProcessHeap(), 0, size);
2855  CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL,
2856  ext->pszObjId, ext->Value.pbData, ext->Value.cbData, str, &size);
2857  }
2858  return str;
2859 }
2860 
2862 {
2863  WCHAR *str = NULL;
2864 
2865  if (ext->Value.cbData)
2866  {
2867  /* The output is formatted as:
2868  * <hex bytes> <ascii bytes>\n
2869  * where <hex bytes> is a string of up to 8 bytes, output as %02x,
2870  * and <ascii bytes> is the ASCII equivalent of each byte, or '.' if
2871  * the byte is not printable.
2872  * So, for example, the extension value consisting of the following
2873  * bytes:
2874  * 0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
2875  * 0x13,0x09,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67
2876  * is output as:
2877  * 30 14 31 12 30 10 06 03 0.1.0...
2878  * 55 04 03 13 09 4a 75 61 U....Jua
2879  * 6e 20 4c 61 6e 67 n Lang
2880  * The allocation size therefore requires:
2881  * - 4 characters per character in an 8-byte line
2882  * (2 for the hex format, one for the space, one for the ASCII value)
2883  * - 3 more characters per 8-byte line (two spaces and a newline)
2884  * - 1 character for the terminating nul
2885  * FIXME: should use a fixed-width font for this
2886  */
2887  DWORD lines = (ext->Value.cbData + 7) / 8;
2888 
2889  str = HeapAlloc(GetProcessHeap(), 0,
2890  (lines * 8 * 4 + lines * 3 + 1) * sizeof(WCHAR));
2891  if (str)
2892  {
2893  static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
2894  DWORD i, j;
2895  WCHAR *ptr;
2896 
2897  for (i = 0, ptr = str; i < ext->Value.cbData; i += 8)
2898  {
2899  /* Output as hex bytes first */
2900  for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr += 3)
2901  sprintfW(ptr, fmt, ext->Value.pbData[j]);
2902  /* Pad the hex output with spaces for alignment */
2903  if (j == ext->Value.cbData && j % 8)
2904  {
2905  static const WCHAR pad[] = { ' ',' ',' ' };
2906 
2907  for (; j % 8; j++, ptr += ARRAY_SIZE(pad))
2908  memcpy(ptr, pad, sizeof(pad));
2909  }
2910  /* The last sprintfW included a space, so just insert one
2911  * more space between the hex bytes and the ASCII output
2912  */
2913  *ptr++ = ' ';
2914  /* Output as ASCII bytes */
2915  for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr++)
2916  {
2917  if (isprintW(ext->Value.pbData[j]) &&
2918  !isspaceW(ext->Value.pbData[j]))
2919  *ptr = ext->Value.pbData[j];
2920  else
2921  *ptr = '.';
2922  }
2923  *ptr++ = '\n';
2924  }
2925  *ptr++ = '\0';
2926  }
2927  }
2928  return str;
2929 }
2930 
2932 {
2936 
2937  if (!str)
2939  return str;
2940 }
2941 
2944 {
2946  ext->pszObjId, 0);
2948 
2949  if (oidInfo)
2952  else
2953  {
2954  DWORD len = strlen(ext->pszObjId);
2955  LPWSTR oidW = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2956 
2957  if (oidW)
2958  {
2959  DWORD i;
2960 
2961  for (i = 0; i <= len; i++)
2962  oidW[i] = ext->pszObjId[i];
2965  HeapFree(GetProcessHeap(), 0, oidW);
2966  }
2967  }
2968  HeapFree(GetProcessHeap(), 0, val);
2969 }
2970 
2972 {
2973  DWORD i;
2974  PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2975 
2976  for (i = 0; i < cert->pCertInfo->cExtension; i++)
2977  add_cert_extension_detail(hwnd, data, &cert->pCertInfo->rgExtension[i]);
2978 }
2979 
2981 {
2982  DWORD i;
2983  PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
2984 
2985  for (i = 0; i < cert->pCertInfo->cExtension; i++)
2986  if (cert->pCertInfo->rgExtension[i].fCritical)
2988  &cert->pCertInfo->rgExtension[i]);
2989 }
2990 
2991 typedef WCHAR * (*prop_to_value_func)(void *pb, DWORD cb);
2992 
2994 {
2996  int id;
2999 };
3000 
3002 {
3004 
3005  ext.pszObjId = (LPSTR)X509_ENHANCED_KEY_USAGE;
3006  ext.fCritical = FALSE;
3007  ext.Value.pbData = pb;
3008  ext.Value.cbData = cb;
3009  return crypt_format_extension(&ext, 0);
3010 }
3011 
3012 /* Logically the access state should also be checked, and IDC_EDITPROPERTIES
3013  * disabled for read-only certificates, but native doesn't appear to do that.
3014  */
3015 static const struct prop_id_to_string_id prop_id_map[] = {
3021 };
3022 
3023 static void add_properties(HWND hwnd, struct detail_data *data)
3024 {
3025  DWORD i;
3026  PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext;
3027 
3028  for (i = 0; i < ARRAY_SIZE(prop_id_map); i++)
3029  {
3030  DWORD cb;
3031 
3033  &cb))
3034  {
3035  BYTE *pb;
3036  WCHAR *val = NULL;
3037 
3038  /* FIXME: MS adds a separate value for the signature hash
3039  * algorithm.
3040  */
3041  pb = HeapAlloc(GetProcessHeap(), 0, cb);
3042  if (pb)
3043  {
3045  prop_id_map[i].prop, pb, &cb))
3046  {
3048  {
3049  val = (LPWSTR)pb;
3050  /* Don't double-free pb */
3051  pb = NULL;
3052  }
3053  else
3054  val = prop_id_map[i].prop_to_value(pb, cb);
3055  }
3056  HeapFree(GetProcessHeap(), 0, pb);
3057  }
3059  NULL, NULL);
3060  }
3061  }
3062 }
3063 
3064 static void add_all_fields(HWND hwnd, struct detail_data *data)
3065 {
3069 }
3070 
3072 {
3073  int id;
3075 };
3076 
3077 static const struct selection_list_item listItems[] = {
3083 };
3084 
3086 {
3089  int i;
3090 
3091  for (i = 0; i < ARRAY_SIZE(listItems); i++)
3092  {
3093  int index;
3094 
3098  }
3099  SendMessageW(cb, CB_SETCURSEL, 0, 0);
3100 }
3101 
3103 {
3105  RECT rc;
3107  LVCOLUMNW column;
3108 
3110  GetWindowRect(lv, &rc);
3112  column.mask = LVCF_WIDTH | LVCF_TEXT;
3113  column.cx = (rc.right - rc.left) / 2 - 2;
3114  column.pszText = buf;
3118 }
3119 
3120 static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel)
3121 {
3123 
3124  if (sel >= 0 && sel < ARRAY_SIZE(listItems))
3125  {
3127  listItems[sel].add(list, data);
3128  }
3129 }
3130 
3132 {
3136 }
3137 
3138 static void add_purpose(HWND hwnd, LPCSTR oid)
3139 {
3142  sizeof(CRYPT_OID_INFO));
3143 
3144  if (info)
3145  {
3146  char *oidCopy = HeapAlloc(GetProcessHeap(), 0, strlen(oid) + 1);
3147 
3148  if (oidCopy)
3149  {
3150  LVITEMA item;
3151 
3152  strcpy(oidCopy, oid);
3153  info->cbSize = sizeof(CRYPT_OID_INFO);
3154  info->pszOID = oidCopy;
3155  item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
3157  item.stateMask = LVIS_STATEIMAGEMASK;
3158  item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
3159  item.iSubItem = 0;
3160  item.lParam = (LPARAM)info;
3161  item.pszText = oidCopy;
3163  }
3164  else
3165  HeapFree(GetProcessHeap(), 0, info);
3166  }
3167 }
3168 
3170 {
3171  BOOL ret;
3172 
3173  if (oid[0] != '0' && oid[0] != '1' && oid[0] != '2')
3174  ret = FALSE;
3175  else if (oid[1] != '.')
3176  ret = FALSE;
3177  else if (!oid[2])
3178  ret = FALSE;
3179  else
3180  {
3181  const char *ptr;
3182  BOOL expectNum = TRUE;
3183 
3184  for (ptr = oid + 2, ret = TRUE; ret && *ptr; ptr++)
3185  {
3186  if (expectNum)
3187  {
3188  if (!isdigit(*ptr))
3189  ret = FALSE;
3190  else if (*(ptr + 1) == '.')
3191  expectNum = FALSE;
3192  }
3193  else
3194  {
3195  if (*ptr != '.')
3196  ret = FALSE;
3197  else if (!(*(ptr + 1)))
3198  ret = FALSE;
3199  else
3200  expectNum = TRUE;
3201  }
3202  }
3203  }
3204  return ret;
3205 }
3206 
3208 {
3210  != -1;
3211 }
3212 
3213 #define MAX_PURPOSE 255
3214 
3216  WPARAM wp, LPARAM lp)
3217 {
3218  LRESULT ret = 0;
3219  char buf[MAX_PURPOSE + 1];
3220 
3221  switch (msg)
3222  {
3223  case WM_INITDIALOG:
3225  MAX_PURPOSE, 0);
3228  break;
3229  case WM_COMMAND:
3230  switch (HIWORD(wp))
3231  {
3232  case EN_CHANGE:
3233  if (LOWORD(wp) == IDC_NEW_PURPOSE)
3234  {
3235  /* Show/hide scroll bar on description depending on how much
3236  * text it has.
3237  */
3240 
3242  }
3243  break;
3244  case BN_CLICKED:
3245  switch (LOWORD(wp))
3246  {
3247  case IDOK:
3249  (LPARAM)buf);
3250  if (!buf[0])
3251  {
3252  /* An empty purpose is the same as cancelling */
3254  ret = TRUE;
3255  }
3256  else if (!is_valid_oid(buf))
3257  {
3259 
3263  }
3264  else if (is_oid_in_list(
3266  {
3268 
3270  ARRAY_SIZE(error));
3273  }
3274  else
3275  {
3277 
3279  EndDialog(hwnd, wp);
3280  ret = TRUE;
3281  }
3282  break;
3283  case IDCANCEL:
3284  EndDialog(hwnd, wp);
3285  ret = TRUE;
3286  break;
3287  }
3288  break;
3289  }
3290  break;
3291  }
3292  return ret;
3293 }
3294 
3296 {
3297  WCHAR *name = NULL;
3298  DWORD cb;
3299 
3301  {
3302  name = HeapAlloc(GetProcessHeap(), 0, cb);
3303  if (name)
3304  {
3306  {
3307  HeapFree(GetProcessHeap(), 0, name);
3308  name = NULL;
3309  }
3310  }
3311  }
3312  return name;
3313 }
3314 
3316 {
3317  int items = SendMessageW(list, LVM_GETITEMCOUNT, 0, 0), i;
3318 
3319  for (i = 0; i < items; i++)
3320  {
3321  BOOL change = FALSE;
3322  int state;
3323 
3325  /* This reverses the INDEXTOSTATEIMAGEMASK shift. There doesn't appear
3326  * to be a handy macro for it.
3327  */
3328  state >>= 12;
3329  if (enabled)
3330  {
3332  {
3334  change = TRUE;
3335  }
3337  {
3339  change = TRUE;
3340  }
3341  }
3342  else
3343  {
3345  {
3347  change = TRUE;
3348  }
3350  {
3352  change = TRUE;
3353  }
3354  }
3355  if (change)
3356  {
3357  LVITEMW item;
3358 
3359  item.state = INDEXTOSTATEIMAGEMASK(state);
3360  item.stateMask = LVIS_STATEIMAGEMASK;
3362  }
3363  }
3364 }
3365 
3366 typedef enum {
3371 
3373 {
3375 
3376  switch (selection)
3377  {
3378  case PurposeEnableAll:
3379  case PurposeDisableAll:
3380  EnableWindow(lv, FALSE);
3381  redraw_states(lv, FALSE);
3383  break;
3384  case PurposeEnableSelected:
3385  EnableWindow(lv, TRUE);
3386  redraw_states(lv, TRUE);
3388  }
3389 }
3390 
3392 {
3396 };
3397 
3399 {
3400  PCCERT_CONTEXT cert = data->cert;
3403  DWORD size;
3404  RECT rc;
3405  LVCOLUMNW column;
3406  PurposeSelection purposeSelection = PurposeEnableAll;
3407 
3408  GetWindowRect(lv, &rc);
3409  column.mask = LVCF_WIDTH;
3410  column.cx = rc.right - rc.left;
3413 
3414  /* Get enhanced key usage. Have to check for a property and an extension
3415  * separately, because CertGetEnhancedKeyUsage will succeed and return an
3416  * empty usage if neither is set. Unfortunately an empty usage implies
3417  * no usage is allowed, so we have to distinguish between the two cases.
3418  */
3420  NULL, &size))
3421  {
3425  {
3426  HeapFree(GetProcessHeap(), 0, usage);
3427  usage = NULL;
3428  }
3429  else if (usage->cUsageIdentifier)
3430  purposeSelection = PurposeEnableSelected;
3431  else
3432  purposeSelection = PurposeDisableAll;
3433  }
3435  NULL, &size))
3436  {
3440  {
3441  HeapFree(GetProcessHeap(), 0, usage);
3442  usage = NULL;
3443  }
3444  else if (usage->cUsageIdentifier)
3445  purposeSelection = PurposeEnableAll;
3446  else
3447  purposeSelection = PurposeDisableAll;
3448  }
3449  else
3450  {
3451  purposeSelection = PurposeEnableAll;
3452  usage = NULL;
3453  }
3454  if (usage)
3455  {
3456  DWORD i;
3457 
3458  for (i = 0; i < usage->cUsageIdentifier; i++)
3459  {
3461  usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
3462 
3463  if (info)
3465  else
3466  add_purpose(hwnd, usage->rgpszUsageIdentifier[i]);
3467  }
3468  HeapFree(GetProcessHeap(), 0, usage);
3469  }
3470  else
3472  select_purposes(hwnd, purposeSelection);
3473  SendMessageW(GetDlgItem(hwnd, IDC_ENABLE_ALL_PURPOSES + purposeSelection),
3474  BM_CLICK, 0, 0);
3475 }
3476 
3478 {
3479  PCCERT_CONTEXT cert = data->cert;
3480  WCHAR *str;
3481 
3483  {
3485  (LPARAM)str);
3486  HeapFree(GetProcessHeap(), 0, str);
3487  }
3489  {
3491  (LPARAM)str);
3492  HeapFree(GetProcessHeap(), 0, str);
3493  }
3495 }
3496 
3498  LPWSTR str)
3499 {
3500  if (str && *str)
3501  {
3503 
3504  blob.pbData = (BYTE *)str;
3505  blob.cbData = (strlenW(str) + 1) * sizeof(WCHAR);
3507  }
3508  else
3510 }
3511 
3512 #define WM_REFRESH_VIEW WM_USER + 0
3513 
3515 {
3516  if ((GetClassLongW(hwnd, GCW_ATOM) == WC_DIALOG))
3518  return TRUE;
3519 }
3520 
3521 #define MAX_FRIENDLY_NAME 40
3522 #define MAX_DESCRIPTION 255
3523 
3525 {
3526  WCHAR buf[MAX_DESCRIPTION + 1];
3527  struct edit_cert_data *data =
3529 
3535  {
3536  /* Setting a NULL usage removes the enhanced key usage property. */
3538  }
3540  {
3541  CERT_ENHKEY_USAGE usage = { 0, NULL };
3542 
3544  }
3546  {
3548  CERT_ENHKEY_USAGE usage = { 0, NULL };
3549  int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i;
3550  LVITEMW item;
3551 
3552  item.mask = LVIF_STATE | LVIF_PARAM;
3553  item.iSubItem = 0;
3554  item.stateMask = LVIS_STATEIMAGEMASK;
3555  for (i = 0; i < purposes; i++)
3556  {
3557  item.iItem = i;
3558  if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item))
3559  {
3560  int state = item.state >> 12;
3561 
3563  {
3564  CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam;
3565 
3566  if (usage.cUsageIdentifier)
3567  usage.rgpszUsageIdentifier =
3569  usage.rgpszUsageIdentifier,
3570  (usage.cUsageIdentifier + 1) * sizeof(LPSTR));
3571  else
3572  usage.rgpszUsageIdentifier =
3573  HeapAlloc(GetProcessHeap(), 0, sizeof(LPSTR));
3574  if (usage.rgpszUsageIdentifier)
3575  usage.rgpszUsageIdentifier[usage.cUsageIdentifier++] =
3576  (LPSTR)info->pszOID;
3577  }
3578  }
3579  }
3581  HeapFree(GetProcessHeap(), 0, usage.rgpszUsageIdentifier);
3582  }
3584  if (data->pfPropertiesChanged)
3585  *data->pfPropertiesChanged = TRUE;
3586 }
3587 
3589  WPARAM wp, LPARAM lp)
3590 {
3592 
3593  TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp);
3594 
3595  switch (msg)
3596  {
3597  case WM_INITDIALOG:
3598  {
3600  struct detail_data *detailData;
3601  struct edit_cert_data *editData;
3602 
3603  page = (PROPSHEETPAGEW *)lp;
3604  detailData = (struct detail_data *)page->lParam;
3606  MAX_FRIENDLY_NAME, 0);
3609  editData = HeapAlloc(GetProcessHeap(), 0,
3610  sizeof(struct edit_cert_data));
3611  if (editData)
3612  {
3613  editData->imageList = ImageList_Create(16, 16,
3614  ILC_COLOR4 | ILC_MASK, 4, 0);
3615  if (editData->imageList)
3616  {
3617  HBITMAP bmp;
3618  COLORREF backColor = RGB(255, 0, 255);
3619 
3621  ImageList_AddMasked(editData->imageList, bmp, backColor);
3622  DeleteObject(bmp);
3624  }
3625  editData->cert = detailData->pCertViewInfo->pCertContext;
3626  editData->pfPropertiesChanged = detailData->pfPropertiesChanged;
3627  SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)editData);
3628  set_general_cert_properties(hwnd, editData);
3629  }
3630  break;
3631  }
3632  case WM_NOTIFY:
3633  {
3634  NMHDR *hdr = (NMHDR *)lp;
3635  NMITEMACTIVATE *nm;
3636 
3637  switch (hdr->code)
3638  {
3639  case NM_CLICK:
3640  nm = (NMITEMACTIVATE *)lp;
3641  toggle_usage(hwnd, nm->iItem);
3643  break;
3644  case PSN_APPLY:
3646  break;
3647  }
3648  break;
3649  }
3650  case WM_COMMAND:
3651  switch (HIWORD(wp))
3652  {
3653  case EN_CHANGE:
3655  if (LOWORD(wp) == IDC_DESCRIPTION)
3656  {
3657  /* Show/hide scroll bar on description depending on how much
3658  * text it has.
3659  */
3662 
3664  }
3665  break;
3666  case BN_CLICKED:
3667  switch (LOWORD(wp))
3668  {
3669  case IDC_ADD_PURPOSE:
3674  break;
3680  break;
3681  }
3682  break;
3683  }
3684  break;
3685  }
3686  return 0;
3687 }
3688 
3691 {
3692  HWND lv;
3693  int cItem, i;
3694  struct edit_cert_data *data;
3695 
3696  switch (msg)
3697  {
3698  case PSPCB_RELEASE:
3700  cItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0);
3701  for (i = 0; i < cItem; i++)
3702  {
3703  LVITEMW item;
3704 
3705  item.mask = LVIF_PARAM;
3706  item.iItem = i;
3707  item.iSubItem = 0;
3708  if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item) && item.lParam)
3709  {
3711 
3712  if (info->cbSize == sizeof(CRYPT_OID_INFO) && !info->dwGroupId)
3713  {
3714  HeapFree(GetProcessHeap(), 0, (LPSTR)info->pszOID);
3715  HeapFree(GetProcessHeap(), 0, info);
3716  }
3717  }
3718  }
3720  if (data)
3721  {
3722  ImageList_Destroy(data->imageList);
3723  HeapFree(GetProcessHeap(), 0, data);
3724  }
3725  break;
3726  }
3727  return 1;
3728 }
3729 
3731  struct detail_data *data)
3732 {
3734  PROPSHEETPAGEW page; /* FIXME: need to add a cross-certificate page */
3735 
3736  TRACE("(%p)\n", data);
3737 
3738  memset(&page, 0, sizeof(PROPSHEETPAGEW));
3739  page.dwSize = sizeof(page);
3740  page.dwFlags = PSP_USECALLBACK;
3741  page.pfnCallback = cert_properties_general_callback;
3742  page.hInstance = hInstance;
3745  page.lParam = (LPARAM)data;
3746 
3747  memset(&hdr, 0, sizeof(hdr));
3748  hdr.dwSize = sizeof(hdr);
3749  hdr.hwndParent = parent;
3750  hdr.dwFlags = PSH_PROPSHEETPAGE;
3751  hdr.hInstance = hInstance;
3753  hdr.u3.ppsp = &page;
3754  hdr.nPages = 1;
3755  PropertySheetW(&hdr);
3756 }
3757 
3759 {
3760  int i;
3761 
3762  for (i = 0; i < data->cFields; i++)
3763  HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value);
3764  HeapFree(GetProcessHeap(), 0, data->fields);
3765  data->fields = NULL;
3766  data->cFields = 0;
3767 }
3768 
3770 {
3772  int curSel;
3773  struct detail_data *data;
3774 
3775  curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0);
3776  /* Actually, any index will do, since they all store the same data value */
3777  data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0);
3779  set_fields_selection(hwnd, data, curSel);
3780 }
3781