Move SaneDialogBox()/SaneEndDialog() from winmisc.c to windlg.c, since they
[u/mdw/putty] / windows / windlg.c
CommitLineData
374330e2 1#include <stdio.h>
2#include <stdlib.h>
fe8abbf4 3#include <limits.h>
4#include <assert.h>
b6c680d4 5#include <ctype.h>
71346075 6#include <time.h>
374330e2 7
bea1ef5f 8#include "putty.h"
7440fd44 9#include "ssh.h"
374330e2 10#include "win_res.h"
d5859615 11#include "storage.h"
fe8abbf4 12#include "dialog.h"
374330e2 13
7440fd44 14#include <commctrl.h>
15#include <commdlg.h>
4e95095a 16#include <shellapi.h>
7440fd44 17
9a1291fe 18#ifdef MSVC4
19#define TVINSERTSTRUCT TV_INSERTSTRUCT
20#define TVITEM TV_ITEM
21#define ICON_BIG 1
22#endif
23
fe8abbf4 24/*
25 * These are the various bits of data required to handle the
26 * portable-dialog stuff in the config box. Having them at file
27 * scope in here isn't too bad a place to put them; if we were ever
28 * to need more than one config box per process we could always
29 * shift them to a per-config-box structure stored in GWL_USERDATA.
30 */
31static struct controlbox *ctrlbox;
32/*
33 * ctrls_base holds the OK and Cancel buttons: the controls which
34 * are present in all dialog panels. ctrls_panel holds the ones
35 * which change from panel to panel.
36 */
37static struct winctrls ctrls_base, ctrls_panel;
38static struct dlgparam dp;
39
c5e9c988 40static char **events = NULL;
41static int nevents = 0, negsize = 0;
42
70133c0e 43static int requested_help;
1cd246bb 44
3ea863a3 45extern Config cfg; /* defined in window.c */
46
0b4f0bc0 47struct sesslist sesslist; /* exported to window.c */
48
b44b307a 49#define PRINTER_DISABLED_STRING "None (printing disabled)"
50
3da0b1d2 51void force_normal(HWND hwnd)
c9def1b8 52{
a9422f39 53 static int recurse = 0;
c9def1b8 54
55 WINDOWPLACEMENT wp;
56
32874aea 57 if (recurse)
58 return;
c9def1b8 59 recurse = 1;
60
61 wp.length = sizeof(wp);
32874aea 62 if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
c9def1b8 63 wp.showCmd = SW_SHOWNORMAL;
64 SetWindowPlacement(hwnd, &wp);
65 }
66 recurse = 0;
67}
68
32874aea 69static int CALLBACK LogProc(HWND hwnd, UINT msg,
70 WPARAM wParam, LPARAM lParam)
71{
374330e2 72 int i;
73
74 switch (msg) {
75 case WM_INITDIALOG:
32874aea 76 {
f6f450e2 77 char *str = dupprintf("%s Event Log", appname);
78 SetWindowText(hwnd, str);
79 sfree(str);
80 }
81 {
32874aea 82 static int tabs[4] = { 78, 108 };
83 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
84 (LPARAM) tabs);
85 }
86 for (i = 0; i < nevents; i++)
87 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
88 0, (LPARAM) events[i]);
374330e2 89 return 1;
374330e2 90 case WM_COMMAND:
91 switch (LOWORD(wParam)) {
92 case IDOK:
475eebf9 93 case IDCANCEL:
374330e2 94 logbox = NULL;
32874aea 95 SetActiveWindow(GetParent(hwnd));
96 DestroyWindow(hwnd);
374330e2 97 return 0;
32874aea 98 case IDN_COPY:
989b10e9 99 if (HIWORD(wParam) == BN_CLICKED ||
100 HIWORD(wParam) == BN_DOUBLECLICKED) {
32874aea 101 int selcount;
102 int *selitems;
103 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
104 LB_GETSELCOUNT, 0, 0);
105 if (selcount == 0) { /* don't even try to copy zero items */
106 MessageBeep(0);
107 break;
108 }
109
3d88e64d 110 selitems = snewn(selcount, int);
32874aea 111 if (selitems) {
112 int count = SendDlgItemMessage(hwnd, IDN_LIST,
113 LB_GETSELITEMS,
114 selcount,
115 (LPARAM) selitems);
116 int i;
117 int size;
118 char *clipdata;
119 static unsigned char sel_nl[] = SEL_NL;
120
121 if (count == 0) { /* can't copy zero stuff */
122 MessageBeep(0);
123 break;
124 }
125
126 size = 0;
127 for (i = 0; i < count; i++)
128 size +=
129 strlen(events[selitems[i]]) + sizeof(sel_nl);
130
3d88e64d 131 clipdata = snewn(size, char);
32874aea 132 if (clipdata) {
133 char *p = clipdata;
134 for (i = 0; i < count; i++) {
135 char *q = events[selitems[i]];
136 int qlen = strlen(q);
137 memcpy(p, q, qlen);
138 p += qlen;
139 memcpy(p, sel_nl, sizeof(sel_nl));
140 p += sizeof(sel_nl);
141 }
a8327734 142 write_aclip(NULL, clipdata, size, TRUE);
32874aea 143 sfree(clipdata);
144 }
145 sfree(selitems);
146
147 for (i = 0; i < nevents; i++)
148 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
149 FALSE, i);
150 }
151 }
152 return 0;
374330e2 153 }
154 return 0;
155 case WM_CLOSE:
156 logbox = NULL;
32874aea 157 SetActiveWindow(GetParent(hwnd));
158 DestroyWindow(hwnd);
374330e2 159 return 0;
160 }
161 return 0;
162}
163
32874aea 164static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
165 WPARAM wParam, LPARAM lParam)
166{
d57835ab 167 switch (msg) {
168 case WM_INITDIALOG:
f6f450e2 169 {
170 char *str = dupprintf("%s Licence", appname);
171 SetWindowText(hwnd, str);
172 sfree(str);
173 }
d57835ab 174 return 1;
175 case WM_COMMAND:
176 switch (LOWORD(wParam)) {
177 case IDOK:
c4c04fac 178 case IDCANCEL:
97749503 179 EndDialog(hwnd, 1);
d57835ab 180 return 0;
181 }
182 return 0;
183 case WM_CLOSE:
97749503 184 EndDialog(hwnd, 1);
d57835ab 185 return 0;
186 }
187 return 0;
188}
189
32874aea 190static int CALLBACK AboutProc(HWND hwnd, UINT msg,
191 WPARAM wParam, LPARAM lParam)
192{
f6f450e2 193 char *str;
194
374330e2 195 switch (msg) {
196 case WM_INITDIALOG:
f6f450e2 197 str = dupprintf("About %s", appname);
198 SetWindowText(hwnd, str);
199 sfree(str);
200 SetDlgItemText(hwnd, IDA_TEXT1, appname);
32874aea 201 SetDlgItemText(hwnd, IDA_VERSION, ver);
374330e2 202 return 1;
374330e2 203 case WM_COMMAND:
204 switch (LOWORD(wParam)) {
205 case IDOK:
32874aea 206 case IDCANCEL:
207 EndDialog(hwnd, TRUE);
374330e2 208 return 0;
209 case IDA_LICENCE:
210 EnableWindow(hwnd, 0);
32874aea 211 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
c4c04fac 212 hwnd, LicenceProc);
374330e2 213 EnableWindow(hwnd, 1);
32874aea 214 SetActiveWindow(hwnd);
374330e2 215 return 0;
defab6b8 216
32874aea 217 case IDA_WEB:
218 /* Load web browser */
219 ShellExecute(hwnd, "open",
220 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
221 0, 0, SW_SHOWDEFAULT);
222 return 0;
374330e2 223 }
224 return 0;
225 case WM_CLOSE:
32874aea 226 EndDialog(hwnd, TRUE);
374330e2 227 return 0;
228 }
229 return 0;
230}
231
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;
247 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
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
258 SetWindowLong(hwnd, BOXFLAGS, 0); /* flags */
259 SetWindowLong(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
260
261 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
262 flags=GetWindowLong(hwnd, BOXFLAGS);
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
272 ret=GetWindowLong(hwnd, BOXRESULT);
273 DestroyWindow(hwnd);
274 return ret;
275}
276
277static void SaneEndDialog(HWND hwnd, int ret)
278{
279 SetWindowLong(hwnd, BOXRESULT, ret);
280 SetWindowLong(hwnd, BOXFLAGS, DF_END);
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],
325 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.
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
5321c0c6 755/* Helper function for verify_ssh_host_key(). */
756static VOID CALLBACK verify_ssh_host_key_help(LPHELPINFO lpHelpInfo)
757{
758 if (help_path) {
759 char *context = NULL;
760#define CHECK_CTX(name) \
761 do { \
762 if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \
763 context = WINHELP_CTX_ ## name; \
764 } while (0)
765 CHECK_CTX(errors_hostkey_absent);
766 CHECK_CTX(errors_hostkey_changed);
767#undef CHECK_CTX
768 if (context) {
769 char *cmd = dupprintf("JI(`',`%s')", context);
770 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
771 sfree(cmd);
772 requested_help = TRUE;
773 }
774 }
775}
776
3d9449a1 777int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
778 char *keystr, char *fingerprint,
779 void (*callback)(void *ctx, int result), void *ctx)
32874aea 780{
d5859615 781 int ret;
374330e2 782
d5859615 783 static const char absentmsg[] =
32874aea 784 "The server's host key is not cached in the registry. You\n"
785 "have no guarantee that the server is the computer you\n"
786 "think it is.\n"
65f66c88 787 "The server's %s key fingerprint is:\n"
32874aea 788 "%s\n"
789 "If you trust this host, hit Yes to add the key to\n"
f6f450e2 790 "%s's cache and carry on connecting.\n"
d0718310 791 "If you want to carry on connecting just once, without\n"
792 "adding the key to the cache, hit No.\n"
793 "If you do not trust this host, hit Cancel to abandon the\n"
32874aea 794 "connection.\n";
d5859615 795
796 static const char wrongmsg[] =
32874aea 797 "WARNING - POTENTIAL SECURITY BREACH!\n"
798 "\n"
f6f450e2 799 "The server's host key does not match the one %s has\n"
32874aea 800 "cached in the registry. This means that either the\n"
801 "server administrator has changed the host key, or you\n"
802 "have actually connected to another computer pretending\n"
803 "to be the server.\n"
65f66c88 804 "The new %s key fingerprint is:\n"
32874aea 805 "%s\n"
806 "If you were expecting this change and trust the new key,\n"
f6f450e2 807 "hit Yes to update %s's cache and continue connecting.\n"
32874aea 808 "If you want to carry on connecting but without updating\n"
809 "the cache, hit No.\n"
810 "If you want to abandon the connection completely, hit\n"
811 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
d5859615 812
f6f450e2 813 static const char mbtitle[] = "%s Security Alert";
de3df031 814
5321c0c6 815 UINT help_button = 0;
816 MSGBOXPARAMS mbox;
817
818 /*
819 * We use MessageBoxIndirect() because it allows us to specify a
820 * callback function for the Help button.
821 */
822 mbox.cbSize = sizeof(mbox);
b3f3c051 823 mbox.hInstance = hinst;
5321c0c6 824 mbox.hwndOwner = hwnd;
825 mbox.lpfnMsgBoxCallback = &verify_ssh_host_key_help;
826 mbox.dwLanguageId = LANG_NEUTRAL;
827
828 /* Do we have a help file? */
829 if (help_path)
830 help_button = MB_HELP;
831
de3df031 832 /*
d5859615 833 * Verify the key against the registry.
de3df031 834 */
d4857987 835 ret = verify_host_key(host, port, keytype, keystr);
d5859615 836
32874aea 837 if (ret == 0) /* success - key matched OK */
3d9449a1 838 return 1;
32874aea 839 if (ret == 2) { /* key was different */
840 int mbret;
5321c0c6 841 mbox.lpszText = dupprintf(wrongmsg, appname, keytype, fingerprint,
842 appname);
843 mbox.lpszCaption = dupprintf(mbtitle, appname);
844 mbox.dwContextHelpId = HELPCTXID(errors_hostkey_changed);
845 mbox.dwStyle = MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3 |
846 help_button;
847 mbret = MessageBoxIndirect(&mbox);
b3f3c051 848 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
5321c0c6 849 sfree((void *)mbox.lpszText);
850 sfree((void *)mbox.lpszCaption);
13d40433 851 if (mbret == IDYES) {
32874aea 852 store_host_key(host, port, keytype, keystr);
13d40433 853 return 1;
854 } else if (mbret == IDNO)
b3f3c051 855 return 1;
856 return 0;
de3df031 857 }
32874aea 858 if (ret == 1) { /* key was absent */
859 int mbret;
5321c0c6 860 mbox.lpszText = dupprintf(absentmsg, keytype, fingerprint, appname);
861 mbox.lpszCaption = dupprintf(mbtitle, appname);
862 mbox.dwContextHelpId = HELPCTXID(errors_hostkey_absent);
863 mbox.dwStyle = MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3 |
864 help_button;
865 mbret = MessageBoxIndirect(&mbox);
b3f3c051 866 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
5321c0c6 867 sfree((void *)mbox.lpszText);
868 sfree((void *)mbox.lpszCaption);
d0718310 869 if (mbret == IDYES)
870 store_host_key(host, port, keytype, keystr);
b3f3c051 871 if (mbret == IDNO)
872 return 1;
873 return 0;
de3df031 874 }
de3df031 875}
e1c8e0ed 876
877/*
83e7d008 878 * Ask whether the selected algorithm is acceptable (since it was
ca20bfcf 879 * below the configured 'warn' threshold).
ca20bfcf 880 */
3d9449a1 881int askalg(void *frontend, const char *algtype, const char *algname,
882 void (*callback)(void *ctx, int result), void *ctx)
ca20bfcf 883{
f6f450e2 884 static const char mbtitle[] = "%s Security Alert";
ca20bfcf 885 static const char msg[] =
83e7d008 886 "The first %s supported by the server\n"
ca20bfcf 887 "is %.64s, which is below the configured\n"
888 "warning threshold.\n"
889 "Do you want to continue with this connection?\n";
f6f450e2 890 char *message, *title;
ca20bfcf 891 int mbret;
892
83e7d008 893 message = dupprintf(msg, algtype, algname);
f6f450e2 894 title = dupprintf(mbtitle, appname);
895 mbret = MessageBox(NULL, message, title,
76347f46 896 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
f6f450e2 897 sfree(message);
898 sfree(title);
ca20bfcf 899 if (mbret == IDYES)
3d9449a1 900 return 1;
ca20bfcf 901 else
3d9449a1 902 return 0;
ca20bfcf 903}
904
905/*
e1c8e0ed 906 * Ask whether to wipe a session log file before writing to it.
907 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
908 */
919baedb 909int askappend(void *frontend, Filename filename,
910 void (*callback)(void *ctx, int result), void *ctx)
32874aea 911{
e1c8e0ed 912 static const char msgtemplate[] =
913 "The session log file \"%.*s\" already exists.\n"
914 "You can overwrite it with a new session log,\n"
915 "append your session log to the end of it,\n"
916 "or disable session logging for this session.\n"
917 "Hit Yes to wipe the file, No to append to it,\n"
918 "or Cancel to disable logging.";
f6f450e2 919 char *message;
920 char *mbtitle;
e1c8e0ed 921 int mbret;
bf61b566 922
f6f450e2 923 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
924 mbtitle = dupprintf("%s Log to File", appname);
e1c8e0ed 925
926 mbret = MessageBox(NULL, message, mbtitle,
76347f46 927 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
f6f450e2 928
929 sfree(message);
930 sfree(mbtitle);
931
e1c8e0ed 932 if (mbret == IDYES)
933 return 2;
934 else if (mbret == IDNO)
935 return 1;
936 else
937 return 0;
938}
7bedb13c 939
940/*
941 * Warn about the obsolescent key file format.
a8327734 942 *
943 * Uniquely among these functions, this one does _not_ expect a
944 * frontend handle. This means that if PuTTY is ported to a
945 * platform which requires frontend handles, this function will be
946 * an anomaly. Fortunately, the problem it addresses will not have
947 * been present on that platform, so it can plausibly be
948 * implemented as an empty function.
7bedb13c 949 */
950void old_keyfile_warning(void)
951{
f6f450e2 952 static const char mbtitle[] = "%s Key File Warning";
7bedb13c 953 static const char message[] =
954 "You are loading an SSH 2 private key which has an\n"
955 "old version of the file format. This means your key\n"
956 "file is not fully tamperproof. Future versions of\n"
f6f450e2 957 "%s may stop supporting this private key format,\n"
7bedb13c 958 "so we recommend you convert your key to the new\n"
959 "format.\n"
960 "\n"
961 "You can perform this conversion by loading the key\n"
962 "into PuTTYgen and then saving it again.";
963
f6f450e2 964 char *msg, *title;
965 msg = dupprintf(message, appname);
966 title = dupprintf(mbtitle, appname);
967
968 MessageBox(NULL, msg, title, MB_OK);
969
970 sfree(msg);
971 sfree(title);
7bedb13c 972}