windlg.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <limits.h>
  4. #include <assert.h>
  5. #include <ctype.h>
  6. #include <time.h>
  7. #include "putty.h"
  8. #include "ssh.h"
  9. #include "win_res.h"
  10. #include "storage.h"
  11. #include "dialog.h"
  12. #include <commctrl.h>
  13. #include <commdlg.h>
  14. #include <shellapi.h>
  15. #ifdef MSVC4
  16. #define TVINSERTSTRUCT TV_INSERTSTRUCT
  17. #define TVITEM TV_ITEM
  18. #define ICON_BIG 1
  19. #endif
  20. /*
  21. * These are the various bits of data required to handle the
  22. * portable-dialog stuff in the config box. Having them at file
  23. * scope in here isn't too bad a place to put them; if we were ever
  24. * to need more than one config box per process we could always
  25. * shift them to a per-config-box structure stored in GWL_USERDATA.
  26. */
  27. static struct controlbox *ctrlbox;
  28. /*
  29. * ctrls_base holds the OK and Cancel buttons: the controls which
  30. * are present in all dialog panels. ctrls_panel holds the ones
  31. * which change from panel to panel.
  32. */
  33. static struct winctrls ctrls_base, ctrls_panel;
  34. static struct dlgparam dp;
  35. static char **events = NULL;
  36. static int nevents = 0, negsize = 0;
  37. static int requested_help;
  38. extern Config cfg; /* defined in window.c */
  39. struct sesslist sesslist; /* exported to window.c */
  40. #define PRINTER_DISABLED_STRING "None (printing disabled)"
  41. void force_normal(HWND hwnd)
  42. {
  43. static int recurse = 0;
  44. WINDOWPLACEMENT wp;
  45. if (recurse)
  46. return;
  47. recurse = 1;
  48. wp.length = sizeof(wp);
  49. if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
  50. wp.showCmd = SW_SHOWNORMAL;
  51. SetWindowPlacement(hwnd, &wp);
  52. }
  53. recurse = 0;
  54. }
  55. static int CALLBACK LogProc(HWND hwnd, UINT msg,
  56. WPARAM wParam, LPARAM lParam)
  57. {
  58. int i;
  59. switch (msg) {
  60. case WM_INITDIALOG:
  61. {
  62. char *str = dupprintf("%s Event Log", appname);
  63. SetWindowText(hwnd, str);
  64. sfree(str);
  65. }
  66. {
  67. static int tabs[4] = { 78, 108 };
  68. SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
  69. (LPARAM) tabs);
  70. }
  71. for (i = 0; i < nevents; i++)
  72. SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
  73. 0, (LPARAM) events[i]);
  74. return 1;
  75. case WM_COMMAND:
  76. switch (LOWORD(wParam)) {
  77. case IDOK:
  78. case IDCANCEL:
  79. logbox = NULL;
  80. SetActiveWindow(GetParent(hwnd));
  81. DestroyWindow(hwnd);
  82. return 0;
  83. case IDN_COPY:
  84. if (HIWORD(wParam) == BN_CLICKED ||
  85. HIWORD(wParam) == BN_DOUBLECLICKED) {
  86. int selcount;
  87. int *selitems;
  88. selcount = SendDlgItemMessage(hwnd, IDN_LIST,
  89. LB_GETSELCOUNT, 0, 0);
  90. if (selcount == 0) { /* don't even try to copy zero items */
  91. MessageBeep(0);
  92. break;
  93. }
  94. selitems = snewn(selcount, int);
  95. if (selitems) {
  96. int count = SendDlgItemMessage(hwnd, IDN_LIST,
  97. LB_GETSELITEMS,
  98. selcount,
  99. (LPARAM) selitems);
  100. int i;
  101. int size;
  102. char *clipdata;
  103. static unsigned char sel_nl[] = SEL_NL;
  104. if (count == 0) { /* can't copy zero stuff */
  105. MessageBeep(0);
  106. break;
  107. }
  108. size = 0;
  109. for (i = 0; i < count; i++)
  110. size +=
  111. strlen(events[selitems[i]]) + sizeof(sel_nl);
  112. clipdata = snewn(size, char);
  113. if (clipdata) {
  114. char *p = clipdata;
  115. for (i = 0; i < count; i++) {
  116. char *q = events[selitems[i]];
  117. int qlen = strlen(q);
  118. memcpy(p, q, qlen);
  119. p += qlen;
  120. memcpy(p, sel_nl, sizeof(sel_nl));
  121. p += sizeof(sel_nl);
  122. }
  123. write_aclip(NULL, clipdata, size, TRUE);
  124. sfree(clipdata);
  125. }
  126. sfree(selitems);
  127. for (i = 0; i < nevents; i++)
  128. SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
  129. FALSE, i);
  130. }
  131. }
  132. return 0;
  133. }
  134. return 0;
  135. case WM_CLOSE:
  136. logbox = NULL;
  137. SetActiveWindow(GetParent(hwnd));
  138. DestroyWindow(hwnd);
  139. return 0;
  140. }
  141. return 0;
  142. }
  143. static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
  144. WPARAM wParam, LPARAM lParam)
  145. {
  146. switch (msg) {
  147. case WM_INITDIALOG:
  148. {
  149. char *str = dupprintf("%s Licence", appname);
  150. SetWindowText(hwnd, str);
  151. sfree(str);
  152. }
  153. return 1;
  154. case WM_COMMAND:
  155. switch (LOWORD(wParam)) {
  156. case IDOK:
  157. EndDialog(hwnd, 1);
  158. return 0;
  159. }
  160. return 0;
  161. case WM_CLOSE:
  162. EndDialog(hwnd, 1);
  163. return 0;
  164. }
  165. return 0;
  166. }
  167. static int CALLBACK AboutProc(HWND hwnd, UINT msg,
  168. WPARAM wParam, LPARAM lParam)
  169. {
  170. char *str;
  171. switch (msg) {
  172. case WM_INITDIALOG:
  173. str = dupprintf("About %s", appname);
  174. SetWindowText(hwnd, str);
  175. sfree(str);
  176. SetDlgItemText(hwnd, IDA_TEXT1, appname);
  177. SetDlgItemText(hwnd, IDA_VERSION, ver);
  178. return 1;
  179. case WM_COMMAND:
  180. switch (LOWORD(wParam)) {
  181. case IDOK:
  182. case IDCANCEL:
  183. EndDialog(hwnd, TRUE);
  184. return 0;
  185. case IDA_LICENCE:
  186. EnableWindow(hwnd, 0);
  187. DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
  188. NULL, LicenceProc);
  189. EnableWindow(hwnd, 1);
  190. SetActiveWindow(hwnd);
  191. return 0;
  192. case IDA_WEB:
  193. /* Load web browser */
  194. ShellExecute(hwnd, "open",
  195. "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
  196. 0, 0, SW_SHOWDEFAULT);
  197. return 0;
  198. }
  199. return 0;
  200. case WM_CLOSE:
  201. EndDialog(hwnd, TRUE);
  202. return 0;
  203. }
  204. return 0;
  205. }
  206. /*
  207. * Null dialog procedure.
  208. */
  209. static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
  210. WPARAM wParam, LPARAM lParam)
  211. {
  212. return 0;
  213. }
  214. enum {
  215. IDCX_ABOUT = IDC_ABOUT,
  216. IDCX_TVSTATIC,
  217. IDCX_TREEVIEW,
  218. IDCX_STDBASE,
  219. IDCX_PANELBASE = IDCX_STDBASE + 32
  220. };
  221. struct treeview_faff {
  222. HWND treeview;
  223. HTREEITEM lastat[4];
  224. };
  225. static HTREEITEM treeview_insert(struct treeview_faff *faff,
  226. int level, char *text, char *path)
  227. {
  228. TVINSERTSTRUCT ins;
  229. int i;
  230. HTREEITEM newitem;
  231. ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
  232. ins.hInsertAfter = faff->lastat[level];
  233. #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
  234. #define INSITEM DUMMYUNIONNAME.item
  235. #else
  236. #define INSITEM item
  237. #endif
  238. ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
  239. ins.INSITEM.pszText = text;
  240. ins.INSITEM.cchTextMax = strlen(text)+1;
  241. ins.INSITEM.lParam = (LPARAM)path;
  242. newitem = TreeView_InsertItem(faff->treeview, &ins);
  243. if (level > 0)
  244. TreeView_Expand(faff->treeview, faff->lastat[level - 1],
  245. TVE_EXPAND);
  246. faff->lastat[level] = newitem;
  247. for (i = level + 1; i < 4; i++)
  248. faff->lastat[i] = NULL;
  249. return newitem;
  250. }
  251. /*
  252. * Create the panelfuls of controls in the configuration box.
  253. */
  254. static void create_controls(HWND hwnd, char *path)
  255. {
  256. struct ctlpos cp;
  257. int index;
  258. int base_id;
  259. struct winctrls *wc;
  260. if (!path[0]) {
  261. /*
  262. * Here we must create the basic standard controls.
  263. */
  264. ctlposinit(&cp, hwnd, 3, 3, 235);
  265. wc = &ctrls_base;
  266. base_id = IDCX_STDBASE;
  267. } else {
  268. /*
  269. * Otherwise, we're creating the controls for a particular
  270. * panel.
  271. */
  272. ctlposinit(&cp, hwnd, 80, 3, 13);
  273. wc = &ctrls_panel;
  274. base_id = IDCX_PANELBASE;
  275. }
  276. for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
  277. struct controlset *s = ctrlbox->ctrlsets[index];
  278. winctrl_layout(&dp, wc, &cp, s, &base_id);
  279. }
  280. }
  281. /*
  282. * This function is the configuration box.
  283. */
  284. static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
  285. WPARAM wParam, LPARAM lParam)
  286. {
  287. HWND hw, treeview;
  288. struct treeview_faff tvfaff;
  289. int ret;
  290. switch (msg) {
  291. case WM_INITDIALOG:
  292. dp.hwnd = hwnd;
  293. create_controls(hwnd, ""); /* Open and Cancel buttons etc */
  294. SetWindowText(hwnd, dp.wintitle);
  295. SetWindowLong(hwnd, GWL_USERDATA, 0);
  296. if (help_path)
  297. SetWindowLong(hwnd, GWL_EXSTYLE,
  298. GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
  299. else {
  300. HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
  301. if (item)
  302. DestroyWindow(item);
  303. }
  304. requested_help = FALSE;
  305. SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
  306. (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
  307. /*
  308. * Centre the window.
  309. */
  310. { /* centre the window */
  311. RECT rs, rd;
  312. hw = GetDesktopWindow();
  313. if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
  314. MoveWindow(hwnd,
  315. (rs.right + rs.left + rd.left - rd.right) / 2,
  316. (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
  317. rd.right - rd.left, rd.bottom - rd.top, TRUE);
  318. }
  319. /*
  320. * Create the tree view.
  321. */
  322. {
  323. RECT r;
  324. WPARAM font;
  325. HWND tvstatic;
  326. r.left = 3;
  327. r.right = r.left + 75;
  328. r.top = 3;
  329. r.bottom = r.top + 10;
  330. MapDialogRect(hwnd, &r);
  331. tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
  332. WS_CHILD | WS_VISIBLE,
  333. r.left, r.top,
  334. r.right - r.left, r.bottom - r.top,
  335. hwnd, (HMENU) IDCX_TVSTATIC, hinst,
  336. NULL);
  337. font = SendMessage(hwnd, WM_GETFONT, 0, 0);
  338. SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
  339. r.left = 3;
  340. r.right = r.left + 75;
  341. r.top = 13;
  342. r.bottom = r.top + 219;
  343. MapDialogRect(hwnd, &r);
  344. treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
  345. WS_CHILD | WS_VISIBLE |
  346. WS_TABSTOP | TVS_HASLINES |
  347. TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
  348. | TVS_LINESATROOT |
  349. TVS_SHOWSELALWAYS, r.left, r.top,
  350. r.right - r.left, r.bottom - r.top,
  351. hwnd, (HMENU) IDCX_TREEVIEW, hinst,
  352. NULL);
  353. font = SendMessage(hwnd, WM_GETFONT, 0, 0);
  354. SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
  355. tvfaff.treeview = treeview;
  356. memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
  357. }
  358. /*
  359. * Set up the tree view contents.
  360. */
  361. {
  362. HTREEITEM hfirst = NULL;
  363. int i;
  364. char *path = NULL;
  365. for (i = 0; i < ctrlbox->nctrlsets; i++) {
  366. struct controlset *s = ctrlbox->ctrlsets[i];
  367. HTREEITEM item;
  368. int j;
  369. char *c;
  370. if (!s->pathname[0])
  371. continue;
  372. j = path ? ctrl_path_compare(s->pathname, path) : 0;
  373. if (j == INT_MAX)
  374. continue; /* same path, nothing to add to tree */
  375. /*
  376. * We expect never to find an implicit path
  377. * component. For example, we expect never to see
  378. * A/B/C followed by A/D/E, because that would
  379. * _implicitly_ create A/D. All our path prefixes
  380. * are expected to contain actual controls and be
  381. * selectable in the treeview; so we would expect
  382. * to see A/D _explicitly_ before encountering
  383. * A/D/E.
  384. */
  385. assert(j == ctrl_path_elements(s->pathname) - 1);
  386. c = strrchr(s->pathname, '/');
  387. if (!c)
  388. c = s->pathname;
  389. else
  390. c++;
  391. item = treeview_insert(&tvfaff, j, c, s->pathname);
  392. if (!hfirst)
  393. hfirst = item;
  394. path = s->pathname;
  395. }
  396. /*
  397. * Put the treeview selection on to the Session panel.
  398. * This should also cause creation of the relevant
  399. * controls.
  400. */
  401. TreeView_SelectItem(treeview, hfirst);
  402. }
  403. /*
  404. * Set focus into the first available control.
  405. */
  406. {
  407. int i;
  408. struct winctrl *c;
  409. for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
  410. i++) {
  411. if (c->ctrl) {
  412. dlg_set_focus(c->ctrl, &dp);
  413. break;
  414. }
  415. }
  416. }
  417. SetWindowLong(hwnd, GWL_USERDATA, 1);
  418. return 0;
  419. case WM_LBUTTONUP:
  420. /*
  421. * Button release should trigger WM_OK if there was a
  422. * previous double click on the session list.
  423. */
  424. ReleaseCapture();
  425. if (dp.ended)
  426. SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
  427. break;
  428. case WM_NOTIFY:
  429. if (LOWORD(wParam) == IDCX_TREEVIEW &&
  430. ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
  431. HTREEITEM i =
  432. TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
  433. TVITEM item;
  434. char buffer[64];
  435. SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
  436. item.hItem = i;
  437. item.pszText = buffer;
  438. item.cchTextMax = sizeof(buffer);
  439. item.mask = TVIF_TEXT | TVIF_PARAM;
  440. TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
  441. {
  442. /* Destroy all controls in the currently visible panel. */
  443. int k;
  444. HWND item;
  445. struct winctrl *c;
  446. while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
  447. for (k = 0; k < c->num_ids; k++) {
  448. item = GetDlgItem(hwnd, c->base_id + k);
  449. if (item)
  450. DestroyWindow(item);
  451. }
  452. winctrl_rem_shortcuts(&dp, c);
  453. winctrl_remove(&ctrls_panel, c);
  454. sfree(c->data);
  455. sfree(c);
  456. }
  457. }
  458. create_controls(hwnd, (char *)item.lParam);
  459. dlg_refresh(NULL, &dp); /* set up control values */
  460. SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
  461. InvalidateRect (hwnd, NULL, TRUE);
  462. SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
  463. return 0;
  464. }
  465. break;
  466. case WM_COMMAND:
  467. case WM_DRAWITEM:
  468. default: /* also handle drag list msg here */
  469. /*
  470. * Only process WM_COMMAND once the dialog is fully formed.
  471. */
  472. if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
  473. ret = winctrl_handle_command(&dp, msg, wParam, lParam);
  474. if (dp.ended && GetCapture() != hwnd)
  475. SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
  476. } else
  477. ret = 0;
  478. return ret;
  479. case WM_HELP:
  480. if (help_path) {
  481. if (winctrl_context_help(&dp, hwnd,
  482. ((LPHELPINFO)lParam)->iCtrlId))
  483. requested_help = TRUE;
  484. else
  485. MessageBeep(0);
  486. }
  487. break;
  488. case WM_CLOSE:
  489. if (requested_help) {
  490. WinHelp(hwnd, help_path, HELP_QUIT, 0);
  491. requested_help = FALSE;
  492. }
  493. SaneEndDialog(hwnd, 0);
  494. return 0;
  495. /* Grrr Explorer will maximize Dialogs! */
  496. case WM_SIZE:
  497. if (wParam == SIZE_MAXIMIZED)
  498. force_normal(hwnd);
  499. return 0;
  500. }
  501. return 0;
  502. }
  503. void modal_about_box(HWND hwnd)
  504. {
  505. EnableWindow(hwnd, 0);
  506. DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
  507. EnableWindow(hwnd, 1);
  508. SetActiveWindow(hwnd);
  509. }
  510. void show_help(HWND hwnd)
  511. {
  512. if (help_path) {
  513. WinHelp(hwnd, help_path,
  514. help_has_contents ? HELP_FINDER : HELP_CONTENTS,
  515. 0);
  516. requested_help = TRUE;
  517. }
  518. }
  519. void defuse_showwindow(void)
  520. {
  521. /*
  522. * Work around the fact that the app's first call to ShowWindow
  523. * will ignore the default in favour of the shell-provided
  524. * setting.
  525. */
  526. {
  527. HWND hwnd;
  528. hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
  529. NULL, NullDlgProc);
  530. ShowWindow(hwnd, SW_HIDE);
  531. SetActiveWindow(hwnd);
  532. DestroyWindow(hwnd);
  533. }
  534. }
  535. int do_config(void)
  536. {
  537. int ret;
  538. ctrlbox = ctrl_new_box();
  539. setup_config_box(ctrlbox, &sesslist, FALSE, 0);
  540. win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
  541. dp_init(&dp);
  542. winctrl_init(&ctrls_base);
  543. winctrl_init(&ctrls_panel);
  544. dp_add_tree(&dp, &ctrls_base);
  545. dp_add_tree(&dp, &ctrls_panel);
  546. dp.wintitle = dupprintf("%s Configuration", appname);
  547. dp.errtitle = dupprintf("%s Error", appname);
  548. dp.data = &cfg;
  549. dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
  550. get_sesslist(&sesslist, TRUE);
  551. ret =
  552. SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
  553. GenericMainDlgProc);
  554. get_sesslist(&sesslist, FALSE);
  555. ctrl_free_box(ctrlbox);
  556. winctrl_cleanup(&ctrls_panel);
  557. winctrl_cleanup(&ctrls_base);
  558. dp_cleanup(&dp);
  559. return ret;
  560. }
  561. int do_reconfig(HWND hwnd)
  562. {
  563. Config backup_cfg;
  564. int ret;
  565. backup_cfg = cfg; /* structure copy */
  566. ctrlbox = ctrl_new_box();
  567. setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
  568. win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
  569. dp_init(&dp);
  570. winctrl_init(&ctrls_base);
  571. winctrl_init(&ctrls_panel);
  572. dp_add_tree(&dp, &ctrls_base);
  573. dp_add_tree(&dp, &ctrls_panel);
  574. dp.wintitle = dupprintf("%s Reconfiguration", appname);
  575. dp.errtitle = dupprintf("%s Error", appname);
  576. dp.data = &cfg;
  577. dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
  578. ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
  579. GenericMainDlgProc);
  580. ctrl_free_box(ctrlbox);
  581. winctrl_cleanup(&ctrls_base);
  582. winctrl_cleanup(&ctrls_panel);
  583. dp_cleanup(&dp);
  584. if (!ret)
  585. cfg = backup_cfg; /* structure copy */
  586. return ret;
  587. }
  588. void logevent(void *frontend, const char *string)
  589. {
  590. char timebuf[40];
  591. time_t t;
  592. log_eventlog(logctx, string);
  593. if (nevents >= negsize) {
  594. negsize += 64;
  595. events = sresize(events, negsize, char *);
  596. }
  597. time(&t);
  598. strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
  599. localtime(&t));
  600. events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
  601. strcpy(events[nevents], timebuf);
  602. strcat(events[nevents], string);
  603. if (logbox) {
  604. int count;
  605. SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
  606. 0, (LPARAM) events[nevents]);
  607. count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
  608. SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
  609. }
  610. nevents++;
  611. }
  612. void showeventlog(HWND hwnd)
  613. {
  614. if (!logbox) {
  615. logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
  616. hwnd, LogProc);
  617. ShowWindow(logbox, SW_SHOWNORMAL);
  618. }
  619. SetActiveWindow(logbox);
  620. }
  621. void showabout(HWND hwnd)
  622. {
  623. DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
  624. }
  625. void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
  626. char *keystr, char *fingerprint)
  627. {
  628. int ret;
  629. static const char absentmsg[] =
  630. "The server's host key is not cached in the registry. You\n"
  631. "have no guarantee that the server is the computer you\n"
  632. "think it is.\n"
  633. "The server's %s key fingerprint is:\n"
  634. "%s\n"
  635. "If you trust this host, hit Yes to add the key to\n"
  636. "%s's cache and carry on connecting.\n"
  637. "If you want to carry on connecting just once, without\n"
  638. "adding the key to the cache, hit No.\n"
  639. "If you do not trust this host, hit Cancel to abandon the\n"
  640. "connection.\n";
  641. static const char wrongmsg[] =
  642. "WARNING - POTENTIAL SECURITY BREACH!\n"
  643. "\n"
  644. "The server's host key does not match the one %s has\n"
  645. "cached in the registry. This means that either the\n"
  646. "server administrator has changed the host key, or you\n"
  647. "have actually connected to another computer pretending\n"
  648. "to be the server.\n"
  649. "The new %s key fingerprint is:\n"
  650. "%s\n"
  651. "If you were expecting this change and trust the new key,\n"
  652. "hit Yes to update %s's cache and continue connecting.\n"
  653. "If you want to carry on connecting but without updating\n"
  654. "the cache, hit No.\n"
  655. "If you want to abandon the connection completely, hit\n"
  656. "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
  657. static const char mbtitle[] = "%s Security Alert";
  658. /*
  659. * Verify the key against the registry.
  660. */
  661. ret = verify_host_key(host, port, keytype, keystr);
  662. if (ret == 0) /* success - key matched OK */
  663. return;
  664. if (ret == 2) { /* key was different */
  665. int mbret;
  666. char *message, *title;
  667. message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname);
  668. title = dupprintf(mbtitle, appname);
  669. mbret = MessageBox(NULL, message, title,
  670. MB_ICONWARNING | MB_YESNOCANCEL);
  671. sfree(message);
  672. sfree(title);
  673. if (mbret == IDYES)
  674. store_host_key(host, port, keytype, keystr);
  675. if (mbret == IDCANCEL)
  676. cleanup_exit(0);
  677. }
  678. if (ret == 1) { /* key was absent */
  679. int mbret;
  680. char *message, *title;
  681. message = dupprintf(absentmsg, keytype, fingerprint, appname);
  682. title = dupprintf(mbtitle, appname);
  683. mbret = MessageBox(NULL, message, title,
  684. MB_ICONWARNING | MB_YESNOCANCEL);
  685. sfree(message);
  686. sfree(title);
  687. if (mbret == IDYES)
  688. store_host_key(host, port, keytype, keystr);
  689. if (mbret == IDCANCEL)
  690. cleanup_exit(0);
  691. }
  692. }
  693. /*
  694. * Ask whether the selected cipher is acceptable (since it was
  695. * below the configured 'warn' threshold).
  696. * cs: 0 = both ways, 1 = client->server, 2 = server->client
  697. */
  698. void askcipher(void *frontend, char *ciphername, int cs)
  699. {
  700. static const char mbtitle[] = "%s Security Alert";
  701. static const char msg[] =
  702. "The first %.35scipher supported by the server\n"
  703. "is %.64s, which is below the configured\n"
  704. "warning threshold.\n"
  705. "Do you want to continue with this connection?\n";
  706. char *message, *title;
  707. int mbret;
  708. message = dupprintf(msg, ((cs == 0) ? "" :
  709. (cs == 1) ? "client-to-server " :
  710. "server-to-client "), ciphername);
  711. title = dupprintf(mbtitle, appname);
  712. mbret = MessageBox(NULL, message, title,
  713. MB_ICONWARNING | MB_YESNO);
  714. sfree(message);
  715. sfree(title);
  716. if (mbret == IDYES)
  717. return;
  718. else
  719. cleanup_exit(0);
  720. }
  721. /*
  722. * Ask whether to wipe a session log file before writing to it.
  723. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
  724. */
  725. int askappend(void *frontend, Filename filename)
  726. {
  727. static const char msgtemplate[] =
  728. "The session log file \"%.*s\" already exists.\n"
  729. "You can overwrite it with a new session log,\n"
  730. "append your session log to the end of it,\n"
  731. "or disable session logging for this session.\n"
  732. "Hit Yes to wipe the file, No to append to it,\n"
  733. "or Cancel to disable logging.";
  734. char *message;
  735. char *mbtitle;
  736. int mbret;
  737. message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
  738. mbtitle = dupprintf("%s Log to File", appname);
  739. mbret = MessageBox(NULL, message, mbtitle,
  740. MB_ICONQUESTION | MB_YESNOCANCEL);
  741. sfree(message);
  742. sfree(mbtitle);
  743. if (mbret == IDYES)
  744. return 2;
  745. else if (mbret == IDNO)
  746. return 1;
  747. else
  748. return 0;
  749. }
  750. /*
  751. * Warn about the obsolescent key file format.
  752. *
  753. * Uniquely among these functions, this one does _not_ expect a
  754. * frontend handle. This means that if PuTTY is ported to a
  755. * platform which requires frontend handles, this function will be
  756. * an anomaly. Fortunately, the problem it addresses will not have
  757. * been present on that platform, so it can plausibly be
  758. * implemented as an empty function.
  759. */
  760. void old_keyfile_warning(void)
  761. {
  762. static const char mbtitle[] = "%s Key File Warning";
  763. static const char message[] =
  764. "You are loading an SSH 2 private key which has an\n"
  765. "old version of the file format. This means your key\n"
  766. "file is not fully tamperproof. Future versions of\n"
  767. "%s may stop supporting this private key format,\n"
  768. "so we recommend you convert your key to the new\n"
  769. "format.\n"
  770. "\n"
  771. "You can perform this conversion by loading the key\n"
  772. "into PuTTYgen and then saving it again.";
  773. char *msg, *title;
  774. msg = dupprintf(message, appname);
  775. title = dupprintf(mbtitle, appname);
  776. MessageBox(NULL, msg, title, MB_OK);
  777. sfree(msg);
  778. sfree(title);
  779. }