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