dialog.c 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. /*
  2. * dialog.c - dialogs for PuTTY(tel), including the configuration dialog.
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <limits.h>
  7. #include <assert.h>
  8. #include <ctype.h>
  9. #include <time.h>
  10. #include "putty.h"
  11. #include "ssh.h"
  12. #include "putty-rc.h"
  13. #include "win-gui-seat.h"
  14. #include "storage.h"
  15. #include "dialog.h"
  16. #include "licence.h"
  17. #include <commctrl.h>
  18. #include <commdlg.h>
  19. #include <shellapi.h>
  20. #ifdef MSVC4
  21. #define TVINSERTSTRUCT TV_INSERTSTRUCT
  22. #define TVITEM TV_ITEM
  23. #define ICON_BIG 1
  24. #endif
  25. typedef struct PortableDialogStuff {
  26. /*
  27. * These are the various bits of data required to handle a dialog
  28. * box that's been built up from the cross-platform dialog.c
  29. * system.
  30. */
  31. /* The 'controlbox' that was returned from the portable setup function */
  32. struct controlbox *ctrlbox;
  33. /* The dlgparam that's passed to all the runtime dlg_* functions.
  34. * Declared as an array of 1 so it's convenient to pass it as a pointer. */
  35. struct dlgparam dp[1];
  36. /*
  37. * Collections of instantiated controls. There can be more than
  38. * one of these, because sometimes you want to destroy and
  39. * recreate a subset of them - e.g. when switching panes in the
  40. * main PuTTY config box, you delete and recreate _most_ of the
  41. * controls, but not the OK and Cancel buttons at the bottom.
  42. */
  43. size_t nctrltrees;
  44. struct winctrls *ctrltrees;
  45. /*
  46. * Flag indicating whether the dialog box has been initialised.
  47. * Used to suppresss spurious firing of message handlers during
  48. * setup.
  49. */
  50. bool initialised;
  51. } PortableDialogStuff;
  52. /*
  53. * Initialise a PortableDialogStuff, before launching the dialog box.
  54. */
  55. static PortableDialogStuff *pds_new(size_t nctrltrees)
  56. {
  57. PortableDialogStuff *pds = snew(PortableDialogStuff);
  58. memset(pds, 0, sizeof(*pds));
  59. pds->ctrlbox = ctrl_new_box();
  60. dp_init(pds->dp);
  61. pds->nctrltrees = nctrltrees;
  62. pds->ctrltrees = snewn(pds->nctrltrees, struct winctrls);
  63. for (size_t i = 0; i < pds->nctrltrees; i++) {
  64. winctrl_init(&pds->ctrltrees[i]);
  65. dp_add_tree(pds->dp, &pds->ctrltrees[i]);
  66. }
  67. pds->dp->errtitle = dupprintf("%s Error", appname);
  68. pds->initialised = false;
  69. return pds;
  70. }
  71. static void pds_free(PortableDialogStuff *pds)
  72. {
  73. ctrl_free_box(pds->ctrlbox);
  74. dp_cleanup(pds->dp);
  75. for (size_t i = 0; i < pds->nctrltrees; i++)
  76. winctrl_cleanup(&pds->ctrltrees[i]);
  77. sfree(pds->ctrltrees);
  78. sfree(pds);
  79. }
  80. static INT_PTR pds_default_dlgproc(PortableDialogStuff *pds, HWND hwnd,
  81. UINT msg, WPARAM wParam, LPARAM lParam)
  82. {
  83. switch (msg) {
  84. case WM_LBUTTONUP:
  85. /*
  86. * Button release should trigger WM_OK if there was a
  87. * previous double click on the host CA list.
  88. */
  89. ReleaseCapture();
  90. if (pds->dp->ended)
  91. ShinyEndDialog(hwnd, pds->dp->endresult ? 1 : 0);
  92. break;
  93. case WM_COMMAND:
  94. case WM_DRAWITEM:
  95. default: { /* also handle drag list msg here */
  96. /*
  97. * Only process WM_COMMAND once the dialog is fully formed.
  98. */
  99. int ret;
  100. if (pds->initialised) {
  101. ret = winctrl_handle_command(pds->dp, msg, wParam, lParam);
  102. if (pds->dp->ended && GetCapture() != hwnd)
  103. ShinyEndDialog(hwnd, pds->dp->endresult ? 1 : 0);
  104. } else
  105. ret = 0;
  106. return ret;
  107. }
  108. case WM_HELP:
  109. if (!winctrl_context_help(pds->dp,
  110. hwnd, ((LPHELPINFO)lParam)->iCtrlId))
  111. MessageBeep(0);
  112. break;
  113. case WM_CLOSE:
  114. quit_help(hwnd);
  115. ShinyEndDialog(hwnd, 0);
  116. return 0;
  117. /* Grrr Explorer will maximize Dialogs! */
  118. case WM_SIZE:
  119. if (wParam == SIZE_MAXIMIZED)
  120. force_normal(hwnd);
  121. return 0;
  122. }
  123. return 0;
  124. }
  125. static void pds_initdialog_start(PortableDialogStuff *pds, HWND hwnd)
  126. {
  127. pds->dp->hwnd = hwnd;
  128. if (pds->dp->wintitle) /* apply override title, if provided */
  129. SetWindowText(hwnd, pds->dp->wintitle);
  130. /* The portable dialog system generally includes the ability to
  131. * handle context help for particular controls. Enable the
  132. * relevant window styles if we have a help file available. */
  133. if (has_help()) {
  134. LONG_PTR style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
  135. SetWindowLongPtr(hwnd, GWL_EXSTYLE, style | WS_EX_CONTEXTHELP);
  136. } else {
  137. /* If not, and if the dialog template provided a top-level
  138. * Help button, delete it */
  139. HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
  140. if (item)
  141. DestroyWindow(item);
  142. }
  143. }
  144. /*
  145. * Create the panelfuls of controls in the configuration box.
  146. */
  147. static void pds_create_controls(
  148. PortableDialogStuff *pds, size_t which_tree, int base_id,
  149. int left, int right, int top, char *path)
  150. {
  151. struct ctlpos cp;
  152. ctlposinit(&cp, pds->dp->hwnd, left, right, top);
  153. for (int index = -1; (index = ctrl_find_path(
  154. pds->ctrlbox, path, index)) >= 0 ;) {
  155. struct controlset *s = pds->ctrlbox->ctrlsets[index];
  156. winctrl_layout(pds->dp, &pds->ctrltrees[which_tree], &cp, s, &base_id);
  157. }
  158. }
  159. static void pds_initdialog_finish(PortableDialogStuff *pds)
  160. {
  161. /*
  162. * Set focus into the first available control in ctrltree #0,
  163. * which the caller was expected to set up to be the one
  164. * containing the dialog controls likely to be used first.
  165. */
  166. struct winctrl *c;
  167. for (int i = 0; (c = winctrl_findbyindex(&pds->ctrltrees[0], i)) != NULL;
  168. i++) {
  169. if (c->ctrl) {
  170. dlg_set_focus(c->ctrl, pds->dp);
  171. break;
  172. }
  173. }
  174. /*
  175. * Now we've finished creating our initial set of controls,
  176. * it's safe to actually show the window without risking setup
  177. * flicker.
  178. */
  179. ShowWindow(pds->dp->hwnd, SW_SHOWNORMAL);
  180. pds->initialised = true;
  181. }
  182. #define LOGEVENT_INITIAL_MAX 128
  183. #define LOGEVENT_CIRCULAR_MAX 128
  184. static char *events_initial[LOGEVENT_INITIAL_MAX];
  185. static char *events_circular[LOGEVENT_CIRCULAR_MAX];
  186. static int ninitial = 0, ncircular = 0, circular_first = 0;
  187. #define PRINTER_DISABLED_STRING "None (printing disabled)"
  188. void force_normal(HWND hwnd)
  189. {
  190. static bool recurse = false;
  191. WINDOWPLACEMENT wp;
  192. if (recurse)
  193. return;
  194. recurse = true;
  195. wp.length = sizeof(wp);
  196. if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
  197. wp.showCmd = SW_SHOWNORMAL;
  198. SetWindowPlacement(hwnd, &wp);
  199. }
  200. recurse = false;
  201. }
  202. static char *getevent(int i)
  203. {
  204. if (i < ninitial)
  205. return events_initial[i];
  206. if ((i -= ninitial) < ncircular)
  207. return events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX];
  208. return NULL;
  209. }
  210. static HWND logbox;
  211. HWND event_log_window(void) { return logbox; }
  212. static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg,
  213. WPARAM wParam, LPARAM lParam)
  214. {
  215. int i;
  216. switch (msg) {
  217. case WM_INITDIALOG: {
  218. char *str = dupprintf("%s Event Log", appname);
  219. SetWindowText(hwnd, str);
  220. sfree(str);
  221. static int tabs[4] = { 78, 108 };
  222. SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
  223. (LPARAM) tabs);
  224. for (i = 0; i < ninitial; i++)
  225. SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
  226. 0, (LPARAM) events_initial[i]);
  227. for (i = 0; i < ncircular; i++)
  228. SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
  229. 0, (LPARAM) events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX]);
  230. return 1;
  231. }
  232. case WM_COMMAND:
  233. switch (LOWORD(wParam)) {
  234. case IDOK:
  235. case IDCANCEL:
  236. logbox = NULL;
  237. SetActiveWindow(GetParent(hwnd));
  238. DestroyWindow(hwnd);
  239. return 0;
  240. case IDN_COPY:
  241. if (HIWORD(wParam) == BN_CLICKED ||
  242. HIWORD(wParam) == BN_DOUBLECLICKED) {
  243. int selcount;
  244. int *selitems;
  245. selcount = SendDlgItemMessage(hwnd, IDN_LIST,
  246. LB_GETSELCOUNT, 0, 0);
  247. if (selcount == 0) { /* don't even try to copy zero items */
  248. MessageBeep(0);
  249. break;
  250. }
  251. selitems = snewn(selcount, int);
  252. if (selitems) {
  253. int count = SendDlgItemMessage(hwnd, IDN_LIST,
  254. LB_GETSELITEMS,
  255. selcount,
  256. (LPARAM) selitems);
  257. static const unsigned char sel_nl[] = SEL_NL;
  258. if (count == 0) { /* can't copy zero stuff */
  259. MessageBeep(0);
  260. break;
  261. }
  262. strbuf *sb = strbuf_new();
  263. for (int i = 0; i < count; i++) {
  264. char *q = getevent(selitems[i]);
  265. put_datapl(sb, ptrlen_from_asciz(q));
  266. put_data(sb, sel_nl, sizeof(sel_nl));
  267. }
  268. write_aclip(hwnd, CLIP_SYSTEM, sb->s, sb->len);
  269. strbuf_free(sb);
  270. sfree(selitems);
  271. for (i = 0; i < (ninitial + ncircular); i++)
  272. SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
  273. false, i);
  274. }
  275. }
  276. return 0;
  277. }
  278. return 0;
  279. case WM_CLOSE:
  280. logbox = NULL;
  281. SetActiveWindow(GetParent(hwnd));
  282. DestroyWindow(hwnd);
  283. return 0;
  284. }
  285. return 0;
  286. }
  287. static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
  288. WPARAM wParam, LPARAM lParam)
  289. {
  290. switch (msg) {
  291. case WM_INITDIALOG: {
  292. char *str = dupprintf("%s Licence", appname);
  293. SetWindowText(hwnd, str);
  294. sfree(str);
  295. SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n"));
  296. return 1;
  297. }
  298. case WM_COMMAND:
  299. switch (LOWORD(wParam)) {
  300. case IDOK:
  301. case IDCANCEL:
  302. EndDialog(hwnd, 1);
  303. return 0;
  304. }
  305. return 0;
  306. case WM_CLOSE:
  307. EndDialog(hwnd, 1);
  308. return 0;
  309. }
  310. return 0;
  311. }
  312. static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
  313. WPARAM wParam, LPARAM lParam)
  314. {
  315. char *str;
  316. switch (msg) {
  317. case WM_INITDIALOG: {
  318. str = dupprintf("About %s", appname);
  319. SetWindowText(hwnd, str);
  320. sfree(str);
  321. char *buildinfo_text = buildinfo("\r\n");
  322. char *text = dupprintf(
  323. "%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
  324. appname, ver, buildinfo_text,
  325. "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
  326. sfree(buildinfo_text);
  327. SetDlgItemText(hwnd, IDA_TEXT, text);
  328. MakeDlgItemBorderless(hwnd, IDA_TEXT);
  329. sfree(text);
  330. return 1;
  331. }
  332. case WM_COMMAND:
  333. switch (LOWORD(wParam)) {
  334. case IDOK:
  335. case IDCANCEL:
  336. EndDialog(hwnd, true);
  337. return 0;
  338. case IDA_LICENCE:
  339. EnableWindow(hwnd, 0);
  340. DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
  341. hwnd, LicenceProc);
  342. EnableWindow(hwnd, 1);
  343. SetActiveWindow(hwnd);
  344. return 0;
  345. case IDA_WEB:
  346. /* Load web browser */
  347. ShellExecute(hwnd, "open",
  348. "https://www.chiark.greenend.org.uk/~sgtatham/putty/",
  349. 0, 0, SW_SHOWDEFAULT);
  350. return 0;
  351. }
  352. return 0;
  353. case WM_CLOSE:
  354. EndDialog(hwnd, true);
  355. return 0;
  356. }
  357. return 0;
  358. }
  359. /*
  360. * Null dialog procedure.
  361. */
  362. static INT_PTR CALLBACK NullDlgProc(HWND hwnd, UINT msg,
  363. WPARAM wParam, LPARAM lParam)
  364. {
  365. return 0;
  366. }
  367. enum {
  368. IDCX_ABOUT = IDC_ABOUT,
  369. IDCX_TVSTATIC,
  370. IDCX_TREEVIEW,
  371. IDCX_STDBASE,
  372. IDCX_PANELBASE = IDCX_STDBASE + 32
  373. };
  374. struct treeview_faff {
  375. HWND treeview;
  376. HTREEITEM lastat[4];
  377. };
  378. static HTREEITEM treeview_insert(struct treeview_faff *faff,
  379. int level, char *text, char *path)
  380. {
  381. TVINSERTSTRUCT ins;
  382. int i;
  383. HTREEITEM newitem;
  384. ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
  385. ins.hInsertAfter = faff->lastat[level];
  386. #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
  387. #define INSITEM DUMMYUNIONNAME.item
  388. #else
  389. #define INSITEM item
  390. #endif
  391. ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
  392. ins.INSITEM.pszText = text;
  393. ins.INSITEM.cchTextMax = strlen(text)+1;
  394. ins.INSITEM.lParam = (LPARAM)path;
  395. newitem = TreeView_InsertItem(faff->treeview, &ins);
  396. if (level > 0)
  397. TreeView_Expand(faff->treeview, faff->lastat[level - 1],
  398. (level > 1 ? TVE_COLLAPSE : TVE_EXPAND));
  399. faff->lastat[level] = newitem;
  400. for (i = level + 1; i < 4; i++)
  401. faff->lastat[i] = NULL;
  402. return newitem;
  403. }
  404. const char *dialog_box_demo_screenshot_filename = NULL;
  405. /* ctrltrees indices for the main dialog box */
  406. enum {
  407. TREE_PANEL, /* things we swap out every time treeview selects a new pane */
  408. TREE_BASE, /* fixed things at the bottom like OK and Cancel buttons */
  409. };
  410. /*
  411. * This function is the configuration box.
  412. * (Being a dialog procedure, in general it returns 0 if the default
  413. * dialog processing should be performed, and 1 if it should not.)
  414. */
  415. static INT_PTR GenericMainDlgProc(HWND hwnd, UINT msg, WPARAM wParam,
  416. LPARAM lParam, void *ctx)
  417. {
  418. PortableDialogStuff *pds = (PortableDialogStuff *)ctx;
  419. const int DEMO_SCREENSHOT_TIMER_ID = 1230;
  420. HWND treeview;
  421. struct treeview_faff tvfaff;
  422. switch (msg) {
  423. case WM_INITDIALOG:
  424. pds_initdialog_start(pds, hwnd);
  425. pds_create_controls(pds, TREE_BASE, IDCX_STDBASE, 3, 3, 235, "");
  426. SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
  427. (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
  428. centre_window(hwnd);
  429. /*
  430. * Create the tree view.
  431. */
  432. {
  433. RECT r;
  434. WPARAM font;
  435. HWND tvstatic;
  436. r.left = 3;
  437. r.right = r.left + 95;
  438. r.top = 3;
  439. r.bottom = r.top + 10;
  440. MapDialogRect(hwnd, &r);
  441. tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
  442. WS_CHILD | WS_VISIBLE,
  443. r.left, r.top,
  444. r.right - r.left, r.bottom - r.top,
  445. hwnd, (HMENU) IDCX_TVSTATIC, hinst,
  446. NULL);
  447. font = SendMessage(hwnd, WM_GETFONT, 0, 0);
  448. SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(true, 0));
  449. r.left = 3;
  450. r.right = r.left + 95;
  451. r.top = 13;
  452. r.bottom = r.top + 219;
  453. MapDialogRect(hwnd, &r);
  454. treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
  455. WS_CHILD | WS_VISIBLE |
  456. WS_TABSTOP | TVS_HASLINES |
  457. TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
  458. | TVS_LINESATROOT |
  459. TVS_SHOWSELALWAYS, r.left, r.top,
  460. r.right - r.left, r.bottom - r.top,
  461. hwnd, (HMENU) IDCX_TREEVIEW, hinst,
  462. NULL);
  463. font = SendMessage(hwnd, WM_GETFONT, 0, 0);
  464. SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(true, 0));
  465. tvfaff.treeview = treeview;
  466. memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
  467. }
  468. /*
  469. * Set up the tree view contents.
  470. */
  471. {
  472. HTREEITEM hfirst = NULL;
  473. int i;
  474. char *path = NULL;
  475. char *firstpath = NULL;
  476. for (i = 0; i < pds->ctrlbox->nctrlsets; i++) {
  477. struct controlset *s = pds->ctrlbox->ctrlsets[i];
  478. HTREEITEM item;
  479. int j;
  480. char *c;
  481. if (!s->pathname[0])
  482. continue;
  483. j = path ? ctrl_path_compare(s->pathname, path) : 0;
  484. if (j == INT_MAX)
  485. continue; /* same path, nothing to add to tree */
  486. /*
  487. * We expect never to find an implicit path
  488. * component. For example, we expect never to see
  489. * A/B/C followed by A/D/E, because that would
  490. * _implicitly_ create A/D. All our path prefixes
  491. * are expected to contain actual controls and be
  492. * selectable in the treeview; so we would expect
  493. * to see A/D _explicitly_ before encountering
  494. * A/D/E.
  495. */
  496. assert(j == ctrl_path_elements(s->pathname) - 1);
  497. c = strrchr(s->pathname, '/');
  498. if (!c)
  499. c = s->pathname;
  500. else
  501. c++;
  502. item = treeview_insert(&tvfaff, j, c, s->pathname);
  503. if (!hfirst) {
  504. hfirst = item;
  505. firstpath = s->pathname;
  506. }
  507. path = s->pathname;
  508. }
  509. /*
  510. * Put the treeview selection on to the first panel in the
  511. * ctrlbox.
  512. */
  513. TreeView_SelectItem(treeview, hfirst);
  514. /*
  515. * And create the actual control set for that panel, to
  516. * match the initial treeview selection.
  517. */
  518. assert(firstpath); /* config.c must have given us _something_ */
  519. pds_create_controls(pds, TREE_PANEL, IDCX_PANELBASE,
  520. 100, 3, 13, firstpath);
  521. dlg_refresh(NULL, pds->dp); /* and set up control values */
  522. }
  523. if (dialog_box_demo_screenshot_filename)
  524. SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
  525. pds_initdialog_finish(pds);
  526. return 0;
  527. case WM_TIMER:
  528. if (dialog_box_demo_screenshot_filename &&
  529. (UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
  530. KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
  531. char *err = save_screenshot(
  532. hwnd, dialog_box_demo_screenshot_filename);
  533. if (err) {
  534. MessageBox(hwnd, err, "Demo screenshot failure",
  535. MB_OK | MB_ICONERROR);
  536. sfree(err);
  537. }
  538. ShinyEndDialog(hwnd, 0);
  539. }
  540. return 0;
  541. case WM_NOTIFY:
  542. if (LOWORD(wParam) == IDCX_TREEVIEW &&
  543. ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
  544. /*
  545. * Selection-change events on the treeview cause us to do
  546. * a flurry of control deletion and creation - but only
  547. * after WM_INITDIALOG has finished. The initial
  548. * selection-change event(s) during treeview setup are
  549. * ignored.
  550. */
  551. HTREEITEM i;
  552. TVITEM item;
  553. char buffer[64];
  554. if (!pds->initialised)
  555. return 0;
  556. i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
  557. SendMessage (hwnd, WM_SETREDRAW, false, 0);
  558. item.hItem = i;
  559. item.pszText = buffer;
  560. item.cchTextMax = sizeof(buffer);
  561. item.mask = TVIF_TEXT | TVIF_PARAM;
  562. TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
  563. {
  564. /* Destroy all controls in the currently visible panel. */
  565. int k;
  566. HWND item;
  567. struct winctrl *c;
  568. while ((c = winctrl_findbyindex(
  569. &pds->ctrltrees[TREE_PANEL], 0)) != NULL) {
  570. for (k = 0; k < c->num_ids; k++) {
  571. item = GetDlgItem(hwnd, c->base_id + k);
  572. if (item)
  573. DestroyWindow(item);
  574. }
  575. winctrl_rem_shortcuts(pds->dp, c);
  576. winctrl_remove(&pds->ctrltrees[TREE_PANEL], c);
  577. sfree(c->data);
  578. sfree(c);
  579. }
  580. }
  581. pds_create_controls(pds, TREE_PANEL, IDCX_PANELBASE,
  582. 100, 3, 13, (char *)item.lParam);
  583. dlg_refresh(NULL, pds->dp); /* set up control values */
  584. SendMessage (hwnd, WM_SETREDRAW, true, 0);
  585. InvalidateRect (hwnd, NULL, true);
  586. SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
  587. }
  588. return 0;
  589. default:
  590. return pds_default_dlgproc(pds, hwnd, msg, wParam, lParam);
  591. }
  592. }
  593. void modal_about_box(HWND hwnd)
  594. {
  595. EnableWindow(hwnd, 0);
  596. DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
  597. EnableWindow(hwnd, 1);
  598. SetActiveWindow(hwnd);
  599. }
  600. void show_help(HWND hwnd)
  601. {
  602. launch_help(hwnd, NULL);
  603. }
  604. void defuse_showwindow(void)
  605. {
  606. /*
  607. * Work around the fact that the app's first call to ShowWindow
  608. * will ignore the default in favour of the shell-provided
  609. * setting.
  610. */
  611. {
  612. HWND hwnd;
  613. hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
  614. NULL, NullDlgProc);
  615. ShowWindow(hwnd, SW_HIDE);
  616. SetActiveWindow(hwnd);
  617. DestroyWindow(hwnd);
  618. }
  619. }
  620. bool do_config(Conf *conf)
  621. {
  622. bool ret;
  623. PortableDialogStuff *pds = pds_new(2);
  624. setup_config_box(pds->ctrlbox, false, 0, 0);
  625. win_setup_config_box(pds->ctrlbox, &pds->dp->hwnd, has_help(), false, 0);
  626. pds->dp->wintitle = dupprintf("%s Configuration", appname);
  627. pds->dp->data = conf;
  628. dlg_auto_set_fixed_pitch_flag(pds->dp);
  629. pds->dp->shortcuts['g'] = true; /* the treeview: `Cate&gory' */
  630. ret = ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), "PuTTYConfigBox",
  631. NULL, GenericMainDlgProc, pds);
  632. pds_free(pds);
  633. return ret;
  634. }
  635. bool do_reconfig(HWND hwnd, Conf *conf, int protcfginfo)
  636. {
  637. Conf *backup_conf;
  638. bool ret;
  639. int protocol;
  640. PortableDialogStuff *pds = pds_new(2);
  641. backup_conf = conf_copy(conf);
  642. protocol = conf_get_int(conf, CONF_protocol);
  643. setup_config_box(pds->ctrlbox, true, protocol, protcfginfo);
  644. win_setup_config_box(pds->ctrlbox, &pds->dp->hwnd, has_help(),
  645. true, protocol);
  646. pds->dp->wintitle = dupprintf("%s Reconfiguration", appname);
  647. pds->dp->data = conf;
  648. dlg_auto_set_fixed_pitch_flag(pds->dp);
  649. pds->dp->shortcuts['g'] = true; /* the treeview: `Cate&gory' */
  650. ret = ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), "PuTTYConfigBox",
  651. NULL, GenericMainDlgProc, pds);
  652. pds_free(pds);
  653. if (!ret)
  654. conf_copy_into(conf, backup_conf);
  655. conf_free(backup_conf);
  656. return ret;
  657. }
  658. static void win_gui_eventlog(LogPolicy *lp, const char *string)
  659. {
  660. char timebuf[40];
  661. char **location;
  662. struct tm tm;
  663. tm=ltime();
  664. strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
  665. if (ninitial < LOGEVENT_INITIAL_MAX)
  666. location = &events_initial[ninitial];
  667. else
  668. location = &events_circular[(circular_first + ncircular) % LOGEVENT_CIRCULAR_MAX];
  669. if (*location)
  670. sfree(*location);
  671. *location = dupcat(timebuf, string);
  672. if (logbox) {
  673. int count;
  674. SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
  675. 0, (LPARAM) *location);
  676. count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
  677. SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
  678. }
  679. if (ninitial < LOGEVENT_INITIAL_MAX) {
  680. ninitial++;
  681. } else if (ncircular < LOGEVENT_CIRCULAR_MAX) {
  682. ncircular++;
  683. } else if (ncircular == LOGEVENT_CIRCULAR_MAX) {
  684. circular_first = (circular_first + 1) % LOGEVENT_CIRCULAR_MAX;
  685. sfree(events_circular[circular_first]);
  686. events_circular[circular_first] = dupstr("..");
  687. }
  688. }
  689. static void win_gui_logging_error(LogPolicy *lp, const char *event)
  690. {
  691. WinGuiSeat *wgs = container_of(lp, WinGuiSeat, logpolicy);
  692. /* Send 'can't open log file' errors to the terminal window.
  693. * (Marked as stderr, although terminal.c won't care.) */
  694. seat_stderr_pl(&wgs->seat, ptrlen_from_asciz(event));
  695. seat_stderr_pl(&wgs->seat, PTRLEN_LITERAL("\r\n"));
  696. }
  697. void showeventlog(HWND hwnd)
  698. {
  699. if (!logbox) {
  700. logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
  701. hwnd, LogProc);
  702. ShowWindow(logbox, SW_SHOWNORMAL);
  703. }
  704. SetActiveWindow(logbox);
  705. }
  706. void showabout(HWND hwnd)
  707. {
  708. DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
  709. }
  710. struct hostkey_dialog_ctx {
  711. SeatDialogText *text;
  712. bool has_title;
  713. const char *helpctx;
  714. };
  715. static INT_PTR HostKeyMoreInfoProc(HWND hwnd, UINT msg, WPARAM wParam,
  716. LPARAM lParam, void *vctx)
  717. {
  718. struct hostkey_dialog_ctx *ctx = (struct hostkey_dialog_ctx *)vctx;
  719. switch (msg) {
  720. case WM_INITDIALOG: {
  721. int index = 100, y = 12;
  722. WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
  723. const char *key = NULL;
  724. for (SeatDialogTextItem *item = ctx->text->items,
  725. *end = item + ctx->text->nitems; item < end; item++) {
  726. switch (item->type) {
  727. case SDT_MORE_INFO_KEY:
  728. key = item->text;
  729. break;
  730. case SDT_MORE_INFO_VALUE_SHORT:
  731. case SDT_MORE_INFO_VALUE_BLOB: {
  732. RECT rk, rv;
  733. DWORD editstyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
  734. ES_AUTOHSCROLL | ES_READONLY;
  735. if (item->type == SDT_MORE_INFO_VALUE_BLOB) {
  736. rk.left = 12;
  737. rk.right = 376;
  738. rk.top = y;
  739. rk.bottom = 8;
  740. y += 10;
  741. editstyle |= ES_MULTILINE;
  742. rv.left = 12;
  743. rv.right = 376;
  744. rv.top = y;
  745. rv.bottom = 64;
  746. y += 68;
  747. } else {
  748. rk.left = 12;
  749. rk.right = 80;
  750. rk.top = y+2;
  751. rk.bottom = 8;
  752. rv.left = 100;
  753. rv.right = 288;
  754. rv.top = y;
  755. rv.bottom = 12;
  756. y += 16;
  757. }
  758. MapDialogRect(hwnd, &rk);
  759. HWND ctl = CreateWindowEx(
  760. 0, "STATIC", key, WS_CHILD | WS_VISIBLE,
  761. rk.left, rk.top, rk.right, rk.bottom,
  762. hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
  763. SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
  764. MapDialogRect(hwnd, &rv);
  765. ctl = CreateWindowEx(
  766. WS_EX_CLIENTEDGE, "EDIT", item->text, editstyle,
  767. rv.left, rv.top, rv.right, rv.bottom,
  768. hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
  769. SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
  770. break;
  771. }
  772. default:
  773. break;
  774. }
  775. }
  776. /*
  777. * Now resize the overall window, and move the Close button at
  778. * the bottom.
  779. */
  780. RECT r;
  781. r.left = 176;
  782. r.top = y + 10;
  783. r.right = r.bottom = 0;
  784. MapDialogRect(hwnd, &r);
  785. HWND ctl = GetDlgItem(hwnd, IDOK);
  786. SetWindowPos(ctl, NULL, r.left, r.top, 0, 0,
  787. SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
  788. r.left = r.top = r.right = 0;
  789. r.bottom = 300;
  790. MapDialogRect(hwnd, &r);
  791. int oldheight = r.bottom;
  792. r.left = r.top = r.right = 0;
  793. r.bottom = y + 30;
  794. MapDialogRect(hwnd, &r);
  795. int newheight = r.bottom;
  796. GetWindowRect(hwnd, &r);
  797. SetWindowPos(hwnd, NULL, 0, 0, r.right - r.left,
  798. r.bottom - r.top + newheight - oldheight,
  799. SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
  800. ShowWindow(hwnd, SW_SHOWNORMAL);
  801. return 1;
  802. }
  803. case WM_COMMAND:
  804. switch (LOWORD(wParam)) {
  805. case IDOK:
  806. ShinyEndDialog(hwnd, 0);
  807. return 0;
  808. }
  809. return 0;
  810. case WM_CLOSE:
  811. ShinyEndDialog(hwnd, 0);
  812. return 0;
  813. }
  814. return 0;
  815. }
  816. static const char *process_seatdialogtext(
  817. strbuf *dlg_text, const char **scary_heading, SeatDialogText *text)
  818. {
  819. const char *dlg_title = "";
  820. for (SeatDialogTextItem *item = text->items,
  821. *end = item + text->nitems; item < end; item++) {
  822. switch (item->type) {
  823. case SDT_PARA:
  824. put_fmt(dlg_text, "%s\r\n\r\n", item->text);
  825. break;
  826. case SDT_DISPLAY:
  827. put_fmt(dlg_text, "%s\r\n\r\n", item->text);
  828. break;
  829. case SDT_SCARY_HEADING:
  830. assert(scary_heading != NULL && "only expect a scary heading if "
  831. "the dialog has somewhere to put it");
  832. *scary_heading = item->text;
  833. break;
  834. case SDT_TITLE:
  835. dlg_title = item->text;
  836. break;
  837. default:
  838. break;
  839. }
  840. }
  841. /* Trim any trailing newlines */
  842. while (strbuf_chomp(dlg_text, '\r') || strbuf_chomp(dlg_text, '\n'));
  843. return dlg_title;
  844. }
  845. static INT_PTR HostKeyDialogProc(HWND hwnd, UINT msg,
  846. WPARAM wParam, LPARAM lParam, void *vctx)
  847. {
  848. struct hostkey_dialog_ctx *ctx = (struct hostkey_dialog_ctx *)vctx;
  849. switch (msg) {
  850. case WM_INITDIALOG: {
  851. strbuf *dlg_text = strbuf_new();
  852. const char *scary_heading = NULL;
  853. const char *dlg_title = process_seatdialogtext(
  854. dlg_text, &scary_heading, ctx->text);
  855. LPCTSTR iconid = IDI_QUESTION;
  856. if (scary_heading) {
  857. SetDlgItemText(hwnd, IDC_HK_TITLE, scary_heading);
  858. iconid = IDI_WARNING;
  859. }
  860. SetDlgItemText(hwnd, IDC_HK_TEXT, dlg_text->s);
  861. MakeDlgItemBorderless(hwnd, IDC_HK_TEXT);
  862. strbuf_free(dlg_text);
  863. SetWindowText(hwnd, dlg_title);
  864. if (!ctx->has_title) {
  865. HWND item = GetDlgItem(hwnd, IDC_HK_TITLE);
  866. if (item)
  867. DestroyWindow(item);
  868. }
  869. /*
  870. * Find out how tall the text in the edit control really ended
  871. * up (after line wrapping), and adjust the height of the
  872. * whole box to match it.
  873. */
  874. int height = SendDlgItemMessage(hwnd, IDC_HK_TEXT,
  875. EM_GETLINECOUNT, 0, 0);
  876. height *= 8; /* height of a text line, by definition of dialog units */
  877. int edittop = ctx->has_title ? 40 : 20;
  878. RECT r;
  879. r.left = 40;
  880. r.top = edittop;
  881. r.right = 290;
  882. r.bottom = height;
  883. MapDialogRect(hwnd, &r);
  884. SetWindowPos(GetDlgItem(hwnd, IDC_HK_TEXT), NULL,
  885. r.left, r.top, r.right, r.bottom,
  886. SWP_NOREDRAW | SWP_NOZORDER);
  887. static const struct {
  888. int id, x;
  889. } buttons[] = {
  890. { IDCANCEL, 288 },
  891. { IDC_HK_ACCEPT, 168 },
  892. { IDC_HK_ONCE, 216 },
  893. { IDC_HK_MOREINFO, 60 },
  894. { IDHELP, 12 },
  895. };
  896. for (size_t i = 0; i < lenof(buttons); i++) {
  897. HWND ctl = GetDlgItem(hwnd, buttons[i].id);
  898. r.left = buttons[i].x;
  899. r.top = edittop + height + 20;
  900. r.right = r.bottom = 0;
  901. MapDialogRect(hwnd, &r);
  902. SetWindowPos(ctl, NULL, r.left, r.top, 0, 0,
  903. SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
  904. }
  905. r.left = r.top = r.right = 0;
  906. r.bottom = 240;
  907. MapDialogRect(hwnd, &r);
  908. int oldheight = r.bottom;
  909. r.left = r.top = r.right = 0;
  910. r.bottom = edittop + height + 40;
  911. MapDialogRect(hwnd, &r);
  912. int newheight = r.bottom;
  913. GetWindowRect(hwnd, &r);
  914. SetWindowPos(hwnd, NULL, 0, 0, r.right - r.left,
  915. r.bottom - r.top + newheight - oldheight,
  916. SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
  917. HANDLE icon = LoadImage(
  918. NULL, iconid, IMAGE_ICON,
  919. GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
  920. LR_SHARED);
  921. SendDlgItemMessage(hwnd, IDC_HK_ICON, STM_SETICON, (WPARAM)icon, 0);
  922. if (!has_help()) {
  923. HWND item = GetDlgItem(hwnd, IDHELP);
  924. if (item)
  925. DestroyWindow(item);
  926. }
  927. ShowWindow(hwnd, SW_SHOWNORMAL);
  928. return 1;
  929. }
  930. case WM_CTLCOLORSTATIC: {
  931. HDC hdc = (HDC)wParam;
  932. HWND control = (HWND)lParam;
  933. if (GetWindowLongPtr(control, GWLP_ID) == IDC_HK_TITLE &&
  934. ctx->has_title) {
  935. SetBkMode(hdc, TRANSPARENT);
  936. HFONT prev_font = (HFONT)SelectObject(
  937. hdc, (HFONT)GetStockObject(SYSTEM_FONT));
  938. LOGFONT lf;
  939. if (GetObject(prev_font, sizeof(lf), &lf)) {
  940. lf.lfWeight = FW_BOLD;
  941. lf.lfHeight = lf.lfHeight * 3 / 2;
  942. HFONT bold_font = CreateFontIndirect(&lf);
  943. if (bold_font)
  944. SelectObject(hdc, bold_font);
  945. }
  946. return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE);
  947. }
  948. return 0;
  949. }
  950. case WM_COMMAND:
  951. switch (LOWORD(wParam)) {
  952. case IDC_HK_ACCEPT:
  953. case IDC_HK_ONCE:
  954. case IDCANCEL:
  955. ShinyEndDialog(hwnd, LOWORD(wParam));
  956. return 0;
  957. case IDHELP: {
  958. launch_help(hwnd, ctx->helpctx);
  959. return 0;
  960. }
  961. case IDC_HK_MOREINFO: {
  962. ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_HK_MOREINFO),
  963. "PuTTYHostKeyMoreInfo", hwnd,
  964. HostKeyMoreInfoProc, ctx);
  965. }
  966. }
  967. return 0;
  968. case WM_CLOSE:
  969. ShinyEndDialog(hwnd, IDCANCEL);
  970. return 0;
  971. }
  972. return 0;
  973. }
  974. const SeatDialogPromptDescriptions *win_seat_prompt_descriptions(Seat *seat)
  975. {
  976. static const SeatDialogPromptDescriptions descs = {
  977. .hk_accept_action = "press \"Accept\"",
  978. .hk_connect_once_action = "press \"Connect Once\"",
  979. .hk_cancel_action = "press \"Cancel\"",
  980. .hk_cancel_action_Participle = "Pressing \"Cancel\"",
  981. .weak_accept_action = "press \"Yes\"",
  982. .weak_cancel_action = "press \"No\"",
  983. };
  984. return &descs;
  985. }
  986. SeatPromptResult win_seat_confirm_ssh_host_key(
  987. Seat *seat, const char *host, int port, const char *keytype,
  988. char *keystr, SeatDialogText *text, HelpCtx helpctx,
  989. void (*callback)(void *ctx, SeatPromptResult result), void *cbctx)
  990. {
  991. WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat);
  992. struct hostkey_dialog_ctx ctx[1];
  993. ctx->text = text;
  994. ctx->helpctx = helpctx;
  995. int mbret = ShinyDialogBox(
  996. hinst, MAKEINTRESOURCE(IDD_HOSTKEY), "PuTTYHostKeyDialog",
  997. wgs->term_hwnd, HostKeyDialogProc, ctx);
  998. assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL);
  999. if (mbret == IDC_HK_ACCEPT) {
  1000. store_host_key(seat, host, port, keytype, keystr);
  1001. return SPR_OK;
  1002. } else if (mbret == IDC_HK_ONCE) {
  1003. return SPR_OK;
  1004. }
  1005. return SPR_USER_ABORT;
  1006. }
  1007. /*
  1008. * Ask whether the selected algorithm is acceptable (since it was
  1009. * below the configured 'warn' threshold).
  1010. */
  1011. SeatPromptResult win_seat_confirm_weak_crypto_primitive(
  1012. Seat *seat, SeatDialogText *text,
  1013. void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
  1014. {
  1015. strbuf *dlg_text = strbuf_new();
  1016. const char *dlg_title = process_seatdialogtext(dlg_text, NULL, text);
  1017. int mbret = MessageBox(NULL, dlg_text->s, dlg_title,
  1018. MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
  1019. socket_reselect_all();
  1020. strbuf_free(dlg_text);
  1021. if (mbret == IDYES)
  1022. return SPR_OK;
  1023. else
  1024. return SPR_USER_ABORT;
  1025. }
  1026. SeatPromptResult win_seat_confirm_weak_cached_hostkey(
  1027. Seat *seat, SeatDialogText *text,
  1028. void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
  1029. {
  1030. strbuf *dlg_text = strbuf_new();
  1031. const char *dlg_title = process_seatdialogtext(dlg_text, NULL, text);
  1032. int mbret = MessageBox(NULL, dlg_text->s, dlg_title,
  1033. MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
  1034. socket_reselect_all();
  1035. strbuf_free(dlg_text);
  1036. if (mbret == IDYES)
  1037. return SPR_OK;
  1038. else
  1039. return SPR_USER_ABORT;
  1040. }
  1041. /*
  1042. * Ask whether to wipe a session log file before writing to it.
  1043. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
  1044. */
  1045. static int win_gui_askappend(LogPolicy *lp, Filename *filename,
  1046. void (*callback)(void *ctx, int result),
  1047. void *ctx)
  1048. {
  1049. static const char msgtemplate[] =
  1050. "The session log file \"%.*s\" already exists.\n"
  1051. "You can overwrite it with a new session log,\n"
  1052. "append your session log to the end of it,\n"
  1053. "or disable session logging for this session.\n"
  1054. "Hit Yes to wipe the file, No to append to it,\n"
  1055. "or Cancel to disable logging.";
  1056. char *message;
  1057. char *mbtitle;
  1058. int mbret;
  1059. message = dupprintf(msgtemplate, FILENAME_MAX, filename->utf8path);
  1060. mbtitle = dupprintf("%s Log to File", appname);
  1061. mbret = message_box(NULL, message, mbtitle,
  1062. MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3,
  1063. true, 0);
  1064. socket_reselect_all();
  1065. sfree(message);
  1066. sfree(mbtitle);
  1067. if (mbret == IDYES)
  1068. return 2;
  1069. else if (mbret == IDNO)
  1070. return 1;
  1071. else
  1072. return 0;
  1073. }
  1074. const LogPolicyVtable win_gui_logpolicy_vt = {
  1075. .eventlog = win_gui_eventlog,
  1076. .askappend = win_gui_askappend,
  1077. .logging_error = win_gui_logging_error,
  1078. .verbose = null_lp_verbose_yes,
  1079. };
  1080. /*
  1081. * Warn about the obsolescent key file format.
  1082. *
  1083. * Uniquely among these functions, this one does _not_ expect a
  1084. * frontend handle. This means that if PuTTY is ported to a
  1085. * platform which requires frontend handles, this function will be
  1086. * an anomaly. Fortunately, the problem it addresses will not have
  1087. * been present on that platform, so it can plausibly be
  1088. * implemented as an empty function.
  1089. */
  1090. void old_keyfile_warning(void)
  1091. {
  1092. static const char mbtitle[] = "%s Key File Warning";
  1093. static const char message[] =
  1094. "You are loading an SSH-2 private key which has an\n"
  1095. "old version of the file format. This means your key\n"
  1096. "file is not fully tamperproof. Future versions of\n"
  1097. "%s may stop supporting this private key format,\n"
  1098. "so we recommend you convert your key to the new\n"
  1099. "format.\n"
  1100. "\n"
  1101. "You can perform this conversion by loading the key\n"
  1102. "into PuTTYgen and then saving it again.";
  1103. char *msg, *title;
  1104. msg = dupprintf(message, appname);
  1105. title = dupprintf(mbtitle, appname);
  1106. MessageBox(NULL, msg, title, MB_OK);
  1107. socket_reselect_all();
  1108. sfree(msg);
  1109. sfree(title);
  1110. }
  1111. static INT_PTR CAConfigProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
  1112. void *ctx)
  1113. {
  1114. PortableDialogStuff *pds = (PortableDialogStuff *)ctx;
  1115. switch (msg) {
  1116. case WM_INITDIALOG:
  1117. pds_initdialog_start(pds, hwnd);
  1118. SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
  1119. (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
  1120. centre_window(hwnd);
  1121. pds_create_controls(pds, 0, IDCX_PANELBASE, 3, 3, 3, "Main");
  1122. pds_create_controls(pds, 0, IDCX_STDBASE, 3, 3, 243, "");
  1123. dlg_refresh(NULL, pds->dp); /* and set up control values */
  1124. pds_initdialog_finish(pds);
  1125. return 0;
  1126. default:
  1127. return pds_default_dlgproc(pds, hwnd, msg, wParam, lParam);
  1128. }
  1129. }
  1130. void show_ca_config_box(dlgparam *dp)
  1131. {
  1132. PortableDialogStuff *pds = pds_new(1);
  1133. setup_ca_config_box(pds->ctrlbox);
  1134. ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_CA_CONFIG), "PuTTYConfigBox",
  1135. dp ? dp->hwnd : NULL, CAConfigProc, pds);
  1136. pds_free(pds);
  1137. }