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