Loose end from r5031: the Kex panel should only be displayed in
[u/mdw/putty] / windows / windlg.c
CommitLineData
374330e2 1#include <stdio.h>
2#include <stdlib.h>
fe8abbf4 3#include <limits.h>
4#include <assert.h>
b6c680d4 5#include <ctype.h>
71346075 6#include <time.h>
374330e2 7
bea1ef5f 8#include "putty.h"
7440fd44 9#include "ssh.h"
374330e2 10#include "win_res.h"
d5859615 11#include "storage.h"
fe8abbf4 12#include "dialog.h"
374330e2 13
7440fd44 14#include <commctrl.h>
15#include <commdlg.h>
4e95095a 16#include <shellapi.h>
7440fd44 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:
c4c04fac 178 case IDCANCEL:
97749503 179 EndDialog(hwnd, 1);
d57835ab 180 return 0;
181 }
182 return 0;
183 case WM_CLOSE:
97749503 184 EndDialog(hwnd, 1);
d57835ab 185 return 0;
186 }
187 return 0;
188}
189
32874aea 190static int CALLBACK AboutProc(HWND hwnd, UINT msg,
191 WPARAM wParam, LPARAM lParam)
192{
f6f450e2 193 char *str;
194
374330e2 195 switch (msg) {
196 case WM_INITDIALOG:
f6f450e2 197 str = dupprintf("About %s", appname);
198 SetWindowText(hwnd, str);
199 sfree(str);
200 SetDlgItemText(hwnd, IDA_TEXT1, appname);
32874aea 201 SetDlgItemText(hwnd, IDA_VERSION, ver);
374330e2 202 return 1;
374330e2 203 case WM_COMMAND:
204 switch (LOWORD(wParam)) {
205 case IDOK:
32874aea 206 case IDCANCEL:
207 EndDialog(hwnd, TRUE);
374330e2 208 return 0;
209 case IDA_LICENCE:
210 EnableWindow(hwnd, 0);
32874aea 211 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
c4c04fac 212 hwnd, LicenceProc);
374330e2 213 EnableWindow(hwnd, 1);
32874aea 214 SetActiveWindow(hwnd);
374330e2 215 return 0;
defab6b8 216
32874aea 217 case IDA_WEB:
218 /* Load web browser */
219 ShellExecute(hwnd, "open",
220 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
221 0, 0, SW_SHOWDEFAULT);
222 return 0;
374330e2 223 }
224 return 0;
225 case WM_CLOSE:
32874aea 226 EndDialog(hwnd, TRUE);
374330e2 227 return 0;
228 }
229 return 0;
230}
231
301b66db 232/*
233 * Null dialog procedure.
234 */
32874aea 235static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
236 WPARAM wParam, LPARAM lParam)
237{
301b66db 238 return 0;
239}
240
fe8abbf4 241enum {
242 IDCX_ABOUT = IDC_ABOUT,
243 IDCX_TVSTATIC,
244 IDCX_TREEVIEW,
245 IDCX_STDBASE,
246 IDCX_PANELBASE = IDCX_STDBASE + 32
c96a8fef 247};
c96a8fef 248
927d4fc5 249struct treeview_faff {
250 HWND treeview;
251 HTREEITEM lastat[4];
252};
253
254static HTREEITEM treeview_insert(struct treeview_faff *faff,
fe8abbf4 255 int level, char *text, char *path)
32874aea 256{
927d4fc5 257 TVINSERTSTRUCT ins;
258 int i;
259 HTREEITEM newitem;
32874aea 260 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
927d4fc5 261 ins.hInsertAfter = faff->lastat[level];
06a03685 262#if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
263#define INSITEM DUMMYUNIONNAME.item
264#else
265#define INSITEM item
266#endif
fe8abbf4 267 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
06a03685 268 ins.INSITEM.pszText = text;
fe8abbf4 269 ins.INSITEM.cchTextMax = strlen(text)+1;
270 ins.INSITEM.lParam = (LPARAM)path;
927d4fc5 271 newitem = TreeView_InsertItem(faff->treeview, &ins);
272 if (level > 0)
32874aea 273 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
274 TVE_EXPAND);
927d4fc5 275 faff->lastat[level] = newitem;
32874aea 276 for (i = level + 1; i < 4; i++)
277 faff->lastat[i] = NULL;
927d4fc5 278 return newitem;
279}
280
c96a8fef 281/*
3ac9cd9f 282 * Create the panelfuls of controls in the configuration box.
283 */
fe8abbf4 284static void create_controls(HWND hwnd, char *path)
32874aea 285{
fe8abbf4 286 struct ctlpos cp;
287 int index;
288 int base_id;
289 struct winctrls *wc;
3ac9cd9f 290
fe8abbf4 291 if (!path[0]) {
292 /*
293 * Here we must create the basic standard controls.
294 */
295 ctlposinit(&cp, hwnd, 3, 3, 235);
296 wc = &ctrls_base;
297 base_id = IDCX_STDBASE;
298 } else {
299 /*
300 * Otherwise, we're creating the controls for a particular
301 * panel.
302 */
32874aea 303 ctlposinit(&cp, hwnd, 80, 3, 13);
fe8abbf4 304 wc = &ctrls_panel;
305 base_id = IDCX_PANELBASE;
3ac9cd9f 306 }
3ac9cd9f 307
fe8abbf4 308 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
309 struct controlset *s = ctrlbox->ctrlsets[index];
310 winctrl_layout(&dp, wc, &cp, s, &base_id);
c8d7378d 311 }
c8d7378d 312}
313
3ac9cd9f 314/*
315 * This function is the configuration box.
c96a8fef 316 */
fe8abbf4 317static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
318 WPARAM wParam, LPARAM lParam)
32874aea 319{
927d4fc5 320 HWND hw, treeview;
321 struct treeview_faff tvfaff;
fe8abbf4 322 int ret;
374330e2 323
324 switch (msg) {
325 case WM_INITDIALOG:
fe8abbf4 326 dp.hwnd = hwnd;
327 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
f6f450e2 328 SetWindowText(hwnd, dp.wintitle);
c96a8fef 329 SetWindowLong(hwnd, GWL_USERDATA, 0);
70133c0e 330 if (help_path)
331 SetWindowLong(hwnd, GWL_EXSTYLE,
332 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
333 else {
334 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
335 if (item)
336 DestroyWindow(item);
337 }
338 requested_help = FALSE;
648947d4 339 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
340 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
c96a8fef 341 /*
342 * Centre the window.
343 */
344 { /* centre the window */
345 RECT rs, rd;
346
347 hw = GetDesktopWindow();
32874aea 348 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
349 MoveWindow(hwnd,
350 (rs.right + rs.left + rd.left - rd.right) / 2,
351 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
352 rd.right - rd.left, rd.bottom - rd.top, TRUE);
c96a8fef 353 }
354
355 /*
927d4fc5 356 * Create the tree view.
c96a8fef 357 */
32874aea 358 {
359 RECT r;
c96a8fef 360 WPARAM font;
32874aea 361 HWND tvstatic;
362
363 r.left = 3;
364 r.right = r.left + 75;
365 r.top = 3;
366 r.bottom = r.top + 10;
367 MapDialogRect(hwnd, &r);
368 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
369 WS_CHILD | WS_VISIBLE,
370 r.left, r.top,
371 r.right - r.left, r.bottom - r.top,
372 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
373 NULL);
c96a8fef 374 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
927d4fc5 375 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
376
32874aea 377 r.left = 3;
378 r.right = r.left + 75;
379 r.top = 13;
a401e5f3 380 r.bottom = r.top + 219;
32874aea 381 MapDialogRect(hwnd, &r);
382 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
383 WS_CHILD | WS_VISIBLE |
384 WS_TABSTOP | TVS_HASLINES |
385 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
386 | TVS_LINESATROOT |
387 TVS_SHOWSELALWAYS, r.left, r.top,
388 r.right - r.left, r.bottom - r.top,
389 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
390 NULL);
927d4fc5 391 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
392 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
32874aea 393 tvfaff.treeview = treeview;
394 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
395 }
c96a8fef 396
397 /*
3ac9cd9f 398 * Set up the tree view contents.
c96a8fef 399 */
fe8abbf4 400 {
401 HTREEITEM hfirst = NULL;
402 int i;
403 char *path = NULL;
404
405 for (i = 0; i < ctrlbox->nctrlsets; i++) {
406 struct controlset *s = ctrlbox->ctrlsets[i];
407 HTREEITEM item;
408 int j;
409 char *c;
410
411 if (!s->pathname[0])
412 continue;
413 j = path ? ctrl_path_compare(s->pathname, path) : 0;
414 if (j == INT_MAX)
415 continue; /* same path, nothing to add to tree */
416
417 /*
418 * We expect never to find an implicit path
419 * component. For example, we expect never to see
420 * A/B/C followed by A/D/E, because that would
421 * _implicitly_ create A/D. All our path prefixes
422 * are expected to contain actual controls and be
423 * selectable in the treeview; so we would expect
424 * to see A/D _explicitly_ before encountering
425 * A/D/E.
426 */
427 assert(j == ctrl_path_elements(s->pathname) - 1);
428
429 c = strrchr(s->pathname, '/');
430 if (!c)
431 c = s->pathname;
432 else
433 c++;
434
435 item = treeview_insert(&tvfaff, j, c, s->pathname);
436 if (!hfirst)
437 hfirst = item;
438
439 path = s->pathname;
32874aea 440 }
32874aea 441
fe8abbf4 442 /*
443 * Put the treeview selection on to the Session panel.
444 * This should also cause creation of the relevant
445 * controls.
446 */
447 TreeView_SelectItem(treeview, hfirst);
448 }
32874aea 449
450 /*
451 * Set focus into the first available control.
452 */
453 {
fe8abbf4 454 int i;
455 struct winctrl *c;
456
457 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
458 i++) {
459 if (c->ctrl) {
460 dlg_set_focus(c->ctrl, &dp);
461 break;
462 }
463 }
32874aea 464 }
c96a8fef 465
466 SetWindowLong(hwnd, GWL_USERDATA, 1);
467 return 0;
1cd246bb 468 case WM_LBUTTONUP:
32874aea 469 /*
470 * Button release should trigger WM_OK if there was a
471 * previous double click on the session list.
472 */
473 ReleaseCapture();
fe8abbf4 474 if (dp.ended)
8ee3ff16 475 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
32874aea 476 break;
c96a8fef 477 case WM_NOTIFY:
927d4fc5 478 if (LOWORD(wParam) == IDCX_TREEVIEW &&
32874aea 479 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
480 HTREEITEM i =
481 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
927d4fc5 482 TVITEM item;
c96a8fef 483 char buffer[64];
78a79d97 484
485 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
486
32874aea 487 item.hItem = i;
c96a8fef 488 item.pszText = buffer;
489 item.cchTextMax = sizeof(buffer);
fe8abbf4 490 item.mask = TVIF_TEXT | TVIF_PARAM;
32874aea 491 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
fe8abbf4 492 {
493 /* Destroy all controls in the currently visible panel. */
494 int k;
495 HWND item;
496 struct winctrl *c;
497
498 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
499 for (k = 0; k < c->num_ids; k++) {
500 item = GetDlgItem(hwnd, c->base_id + k);
501 if (item)
502 DestroyWindow(item);
503 }
504 winctrl_rem_shortcuts(&dp, c);
505 winctrl_remove(&ctrls_panel, c);
506 sfree(c->data);
507 sfree(c);
508 }
32874aea 509 }
fe8abbf4 510 create_controls(hwnd, (char *)item.lParam);
511
512 dlg_refresh(NULL, &dp); /* set up control values */
78a79d97 513
514 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
515 InvalidateRect (hwnd, NULL, TRUE);
c96a8fef 516
32874aea 517 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
c96a8fef 518 return 0;
519 }
520 break;
374330e2 521 case WM_COMMAND:
fe8abbf4 522 case WM_DRAWITEM:
523 default: /* also handle drag list msg here */
c96a8fef 524 /*
525 * Only process WM_COMMAND once the dialog is fully formed.
526 */
fe8abbf4 527 if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
528 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
529 if (dp.ended && GetCapture() != hwnd)
8ee3ff16 530 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
fe8abbf4 531 } else
532 ret = 0;
533 return ret;
70133c0e 534 case WM_HELP:
535 if (help_path) {
fe8abbf4 536 if (winctrl_context_help(&dp, hwnd,
537 ((LPHELPINFO)lParam)->iCtrlId))
70133c0e 538 requested_help = TRUE;
fe8abbf4 539 else
70133c0e 540 MessageBeep(0);
70133c0e 541 }
542 break;
374330e2 543 case WM_CLOSE:
70133c0e 544 if (requested_help) {
545 WinHelp(hwnd, help_path, HELP_QUIT, 0);
546 requested_help = FALSE;
547 }
8ee3ff16 548 SaneEndDialog(hwnd, 0);
374330e2 549 return 0;
c9def1b8 550
551 /* Grrr Explorer will maximize Dialogs! */
552 case WM_SIZE:
553 if (wParam == SIZE_MAXIMIZED)
32874aea 554 force_normal(hwnd);
c9def1b8 555 return 0;
ca20bfcf 556
374330e2 557 }
558 return 0;
559}
560
fe8abbf4 561void modal_about_box(HWND hwnd)
32874aea 562{
fe8abbf4 563 EnableWindow(hwnd, 0);
564 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
565 EnableWindow(hwnd, 1);
566 SetActiveWindow(hwnd);
374330e2 567}
568
fe8abbf4 569void show_help(HWND hwnd)
32874aea 570{
fe8abbf4 571 if (help_path) {
572 WinHelp(hwnd, help_path,
573 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
574 0);
575 requested_help = TRUE;
576 }
374330e2 577}
578
32874aea 579void defuse_showwindow(void)
580{
301b66db 581 /*
582 * Work around the fact that the app's first call to ShowWindow
583 * will ignore the default in favour of the shell-provided
584 * setting.
585 */
586 {
32874aea 587 HWND hwnd;
588 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
589 NULL, NullDlgProc);
590 ShowWindow(hwnd, SW_HIDE);
f5eca4f8 591 SetActiveWindow(hwnd);
32874aea 592 DestroyWindow(hwnd);
301b66db 593 }
594}
595
32874aea 596int do_config(void)
597{
374330e2 598 int ret;
599
fe8abbf4 600 ctrlbox = ctrl_new_box();
f89c3294 601 setup_config_box(ctrlbox, &sesslist, FALSE, 0, 0);
fe8abbf4 602 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
4e6d4091 603 dp_init(&dp);
fe8abbf4 604 winctrl_init(&ctrls_base);
605 winctrl_init(&ctrls_panel);
4e6d4091 606 dp_add_tree(&dp, &ctrls_base);
607 dp_add_tree(&dp, &ctrls_panel);
f6f450e2 608 dp.wintitle = dupprintf("%s Configuration", appname);
609 dp.errtitle = dupprintf("%s Error", appname);
fe8abbf4 610 dp.data = &cfg;
fe8abbf4 611 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
612
0b4f0bc0 613 get_sesslist(&sesslist, TRUE);
32874aea 614 ret =
8ee3ff16 615 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
fe8abbf4 616 GenericMainDlgProc);
0b4f0bc0 617 get_sesslist(&sesslist, FALSE);
374330e2 618
fe8abbf4 619 ctrl_free_box(ctrlbox);
fe8abbf4 620 winctrl_cleanup(&ctrls_panel);
4e6d4091 621 winctrl_cleanup(&ctrls_base);
622 dp_cleanup(&dp);
fe8abbf4 623
374330e2 624 return ret;
625}
626
f89c3294 627int do_reconfig(HWND hwnd, int protcfginfo)
32874aea 628{
374330e2 629 Config backup_cfg;
630 int ret;
631
632 backup_cfg = cfg; /* structure copy */
fe8abbf4 633
634 ctrlbox = ctrl_new_box();
f89c3294 635 setup_config_box(ctrlbox, &sesslist, TRUE, cfg.protocol, protcfginfo);
fe8abbf4 636 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
4e6d4091 637 dp_init(&dp);
fe8abbf4 638 winctrl_init(&ctrls_base);
639 winctrl_init(&ctrls_panel);
4e6d4091 640 dp_add_tree(&dp, &ctrls_base);
641 dp_add_tree(&dp, &ctrls_panel);
f6f450e2 642 dp.wintitle = dupprintf("%s Reconfiguration", appname);
643 dp.errtitle = dupprintf("%s Error", appname);
fe8abbf4 644 dp.data = &cfg;
fe8abbf4 645 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
646
8ee3ff16 647 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
fe8abbf4 648 GenericMainDlgProc);
649
650 ctrl_free_box(ctrlbox);
651 winctrl_cleanup(&ctrls_base);
652 winctrl_cleanup(&ctrls_panel);
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"
65f66c88 714 "The server's %s key fingerprint is:\n"
32874aea 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"
65f66c88 731 "The new %s key fingerprint is:\n"
32874aea 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;
65f66c88 752 message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname);
f6f450e2 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;
65f66c88 766 message = dupprintf(absentmsg, keytype, fingerprint, appname);
f6f450e2 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/*
83e7d008 780 * Ask whether the selected algorithm is acceptable (since it was
ca20bfcf 781 * below the configured 'warn' threshold).
ca20bfcf 782 */
83e7d008 783void askalg(void *frontend, const char *algtype, const char *algname)
ca20bfcf 784{
f6f450e2 785 static const char mbtitle[] = "%s Security Alert";
ca20bfcf 786 static const char msg[] =
83e7d008 787 "The first %s supported by the server\n"
ca20bfcf 788 "is %.64s, which is below the configured\n"
789 "warning threshold.\n"
790 "Do you want to continue with this connection?\n";
f6f450e2 791 char *message, *title;
ca20bfcf 792 int mbret;
793
83e7d008 794 message = dupprintf(msg, algtype, algname);
f6f450e2 795 title = dupprintf(mbtitle, appname);
796 mbret = MessageBox(NULL, message, title,
ca20bfcf 797 MB_ICONWARNING | MB_YESNO);
f6f450e2 798 sfree(message);
799 sfree(title);
ca20bfcf 800 if (mbret == IDYES)
801 return;
802 else
93b581bd 803 cleanup_exit(0);
ca20bfcf 804}
805
806/*
e1c8e0ed 807 * Ask whether to wipe a session log file before writing to it.
808 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
809 */
9a30e26b 810int askappend(void *frontend, Filename filename)
32874aea 811{
e1c8e0ed 812 static const char msgtemplate[] =
813 "The session log file \"%.*s\" already exists.\n"
814 "You can overwrite it with a new session log,\n"
815 "append your session log to the end of it,\n"
816 "or disable session logging for this session.\n"
817 "Hit Yes to wipe the file, No to append to it,\n"
818 "or Cancel to disable logging.";
f6f450e2 819 char *message;
820 char *mbtitle;
e1c8e0ed 821 int mbret;
bf61b566 822
f6f450e2 823 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
824 mbtitle = dupprintf("%s Log to File", appname);
e1c8e0ed 825
826 mbret = MessageBox(NULL, message, mbtitle,
32874aea 827 MB_ICONQUESTION | MB_YESNOCANCEL);
f6f450e2 828
829 sfree(message);
830 sfree(mbtitle);
831
e1c8e0ed 832 if (mbret == IDYES)
833 return 2;
834 else if (mbret == IDNO)
835 return 1;
836 else
837 return 0;
838}
7bedb13c 839
840/*
841 * Warn about the obsolescent key file format.
a8327734 842 *
843 * Uniquely among these functions, this one does _not_ expect a
844 * frontend handle. This means that if PuTTY is ported to a
845 * platform which requires frontend handles, this function will be
846 * an anomaly. Fortunately, the problem it addresses will not have
847 * been present on that platform, so it can plausibly be
848 * implemented as an empty function.
7bedb13c 849 */
850void old_keyfile_warning(void)
851{
f6f450e2 852 static const char mbtitle[] = "%s Key File Warning";
7bedb13c 853 static const char message[] =
854 "You are loading an SSH 2 private key which has an\n"
855 "old version of the file format. This means your key\n"
856 "file is not fully tamperproof. Future versions of\n"
f6f450e2 857 "%s may stop supporting this private key format,\n"
7bedb13c 858 "so we recommend you convert your key to the new\n"
859 "format.\n"
860 "\n"
861 "You can perform this conversion by loading the key\n"
862 "into PuTTYgen and then saving it again.";
863
f6f450e2 864 char *msg, *title;
865 msg = dupprintf(message, appname);
866 title = dupprintf(mbtitle, appname);
867
868 MessageBox(NULL, msg, title, MB_OK);
869
870 sfree(msg);
871 sfree(title);
7bedb13c 872}