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