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