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