Consistently use a single notation to refer to SSH protocol versions, as
[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
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.
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();
f89c3294 650 setup_config_box(ctrlbox, &sesslist, 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
0b4f0bc0 662 get_sesslist(&sesslist, TRUE);
32874aea 663 ret =
8ee3ff16 664 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
fe8abbf4 665 GenericMainDlgProc);
0b4f0bc0 666 get_sesslist(&sesslist, FALSE);
374330e2 667
fe8abbf4 668 ctrl_free_box(ctrlbox);
fe8abbf4 669 winctrl_cleanup(&ctrls_panel);
4e6d4091 670 winctrl_cleanup(&ctrls_base);
671 dp_cleanup(&dp);
fe8abbf4 672
374330e2 673 return ret;
674}
675
f89c3294 676int do_reconfig(HWND hwnd, int protcfginfo)
32874aea 677{
374330e2 678 Config backup_cfg;
679 int ret;
680
681 backup_cfg = cfg; /* structure copy */
fe8abbf4 682
683 ctrlbox = ctrl_new_box();
f89c3294 684 setup_config_box(ctrlbox, &sesslist, TRUE, cfg.protocol, protcfginfo);
fe8abbf4 685 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
4e6d4091 686 dp_init(&dp);
fe8abbf4 687 winctrl_init(&ctrls_base);
688 winctrl_init(&ctrls_panel);
4e6d4091 689 dp_add_tree(&dp, &ctrls_base);
690 dp_add_tree(&dp, &ctrls_panel);
f6f450e2 691 dp.wintitle = dupprintf("%s Reconfiguration", appname);
692 dp.errtitle = dupprintf("%s Error", appname);
fe8abbf4 693 dp.data = &cfg;
fe8abbf4 694 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
695
8ee3ff16 696 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
fe8abbf4 697 GenericMainDlgProc);
698
699 ctrl_free_box(ctrlbox);
700 winctrl_cleanup(&ctrls_base);
701 winctrl_cleanup(&ctrls_panel);
4e6d4091 702 dp_cleanup(&dp);
fe8abbf4 703
374330e2 704 if (!ret)
705 cfg = backup_cfg; /* structure copy */
c9def1b8 706
374330e2 707 return ret;
708}
709
cbe2d68f 710void logevent(void *frontend, const char *string)
32874aea 711{
71346075 712 char timebuf[40];
aca589d9 713 struct tm tm;
71346075 714
a8327734 715 log_eventlog(logctx, string);
fb89f7ff 716
c5e9c988 717 if (nevents >= negsize) {
374330e2 718 negsize += 64;
3d88e64d 719 events = sresize(events, negsize, char *);
374330e2 720 }
71346075 721
aca589d9 722 tm=ltime();
723 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
71346075 724
3d88e64d 725 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
71346075 726 strcpy(events[nevents], timebuf);
727 strcat(events[nevents], string);
9ad90448 728 if (logbox) {
32874aea 729 int count;
730 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
731 0, (LPARAM) events[nevents]);
732 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
733 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
9ad90448 734 }
bce816e7 735 nevents++;
374330e2 736}
737
32874aea 738void showeventlog(HWND hwnd)
739{
374330e2 740 if (!logbox) {
32874aea 741 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
742 hwnd, LogProc);
743 ShowWindow(logbox, SW_SHOWNORMAL);
374330e2 744 }
9ecf8e5a 745 SetActiveWindow(logbox);
374330e2 746}
747
32874aea 748void showabout(HWND hwnd)
749{
750 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
374330e2 751}
752
3d9449a1 753int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
754 char *keystr, char *fingerprint,
755 void (*callback)(void *ctx, int result), void *ctx)
32874aea 756{
d5859615 757 int ret;
374330e2 758
d5859615 759 static const char absentmsg[] =
32874aea 760 "The server's host key is not cached in the registry. You\n"
761 "have no guarantee that the server is the computer you\n"
762 "think it is.\n"
65f66c88 763 "The server's %s key fingerprint is:\n"
32874aea 764 "%s\n"
765 "If you trust this host, hit Yes to add the key to\n"
f6f450e2 766 "%s's cache and carry on connecting.\n"
d0718310 767 "If you want to carry on connecting just once, without\n"
768 "adding the key to the cache, hit No.\n"
769 "If you do not trust this host, hit Cancel to abandon the\n"
32874aea 770 "connection.\n";
d5859615 771
772 static const char wrongmsg[] =
32874aea 773 "WARNING - POTENTIAL SECURITY BREACH!\n"
774 "\n"
f6f450e2 775 "The server's host key does not match the one %s has\n"
32874aea 776 "cached in the registry. This means that either the\n"
777 "server administrator has changed the host key, or you\n"
778 "have actually connected to another computer pretending\n"
779 "to be the server.\n"
65f66c88 780 "The new %s key fingerprint is:\n"
32874aea 781 "%s\n"
782 "If you were expecting this change and trust the new key,\n"
f6f450e2 783 "hit Yes to update %s's cache and continue connecting.\n"
32874aea 784 "If you want to carry on connecting but without updating\n"
785 "the cache, hit No.\n"
786 "If you want to abandon the connection completely, hit\n"
787 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
d5859615 788
f6f450e2 789 static const char mbtitle[] = "%s Security Alert";
de3df031 790
791 /*
d5859615 792 * Verify the key against the registry.
de3df031 793 */
d4857987 794 ret = verify_host_key(host, port, keytype, keystr);
d5859615 795
32874aea 796 if (ret == 0) /* success - key matched OK */
3d9449a1 797 return 1;
32874aea 798 if (ret == 2) { /* key was different */
799 int mbret;
690695e0 800 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
801 appname);
802 char *caption = dupprintf(mbtitle, appname);
803 mbret = message_box(text, caption,
804 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
805 HELPCTXID(errors_hostkey_changed));
b3f3c051 806 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
690695e0 807 sfree(text);
808 sfree(caption);
13d40433 809 if (mbret == IDYES) {
32874aea 810 store_host_key(host, port, keytype, keystr);
13d40433 811 return 1;
812 } else if (mbret == IDNO)
b3f3c051 813 return 1;
814 return 0;
de3df031 815 }
32874aea 816 if (ret == 1) { /* key was absent */
817 int mbret;
690695e0 818 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
819 char *caption = dupprintf(mbtitle, appname);
820 mbret = message_box(text, caption,
821 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
822 HELPCTXID(errors_hostkey_absent));
b3f3c051 823 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
690695e0 824 sfree(text);
825 sfree(caption);
826 if (mbret == IDYES) {
d0718310 827 store_host_key(host, port, keytype, keystr);
690695e0 828 return 1;
829 } else if (mbret == IDNO)
b3f3c051 830 return 1;
831 return 0;
de3df031 832 }
de3df031 833}
e1c8e0ed 834
835/*
83e7d008 836 * Ask whether the selected algorithm is acceptable (since it was
ca20bfcf 837 * below the configured 'warn' threshold).
ca20bfcf 838 */
3d9449a1 839int askalg(void *frontend, const char *algtype, const char *algname,
840 void (*callback)(void *ctx, int result), void *ctx)
ca20bfcf 841{
f6f450e2 842 static const char mbtitle[] = "%s Security Alert";
ca20bfcf 843 static const char msg[] =
83e7d008 844 "The first %s supported by the server\n"
ca20bfcf 845 "is %.64s, which is below the configured\n"
846 "warning threshold.\n"
847 "Do you want to continue with this connection?\n";
f6f450e2 848 char *message, *title;
ca20bfcf 849 int mbret;
850
83e7d008 851 message = dupprintf(msg, algtype, algname);
f6f450e2 852 title = dupprintf(mbtitle, appname);
853 mbret = MessageBox(NULL, message, title,
76347f46 854 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
f6f450e2 855 sfree(message);
856 sfree(title);
ca20bfcf 857 if (mbret == IDYES)
3d9449a1 858 return 1;
ca20bfcf 859 else
3d9449a1 860 return 0;
ca20bfcf 861}
862
863/*
e1c8e0ed 864 * Ask whether to wipe a session log file before writing to it.
865 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
866 */
919baedb 867int askappend(void *frontend, Filename filename,
868 void (*callback)(void *ctx, int result), void *ctx)
32874aea 869{
e1c8e0ed 870 static const char msgtemplate[] =
871 "The session log file \"%.*s\" already exists.\n"
872 "You can overwrite it with a new session log,\n"
873 "append your session log to the end of it,\n"
874 "or disable session logging for this session.\n"
875 "Hit Yes to wipe the file, No to append to it,\n"
876 "or Cancel to disable logging.";
f6f450e2 877 char *message;
878 char *mbtitle;
e1c8e0ed 879 int mbret;
bf61b566 880
f6f450e2 881 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
882 mbtitle = dupprintf("%s Log to File", appname);
e1c8e0ed 883
884 mbret = MessageBox(NULL, message, mbtitle,
76347f46 885 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
f6f450e2 886
887 sfree(message);
888 sfree(mbtitle);
889
e1c8e0ed 890 if (mbret == IDYES)
891 return 2;
892 else if (mbret == IDNO)
893 return 1;
894 else
895 return 0;
896}
7bedb13c 897
898/*
899 * Warn about the obsolescent key file format.
a8327734 900 *
901 * Uniquely among these functions, this one does _not_ expect a
902 * frontend handle. This means that if PuTTY is ported to a
903 * platform which requires frontend handles, this function will be
904 * an anomaly. Fortunately, the problem it addresses will not have
905 * been present on that platform, so it can plausibly be
906 * implemented as an empty function.
7bedb13c 907 */
908void old_keyfile_warning(void)
909{
f6f450e2 910 static const char mbtitle[] = "%s Key File Warning";
7bedb13c 911 static const char message[] =
2e85c969 912 "You are loading an SSH-2 private key which has an\n"
7bedb13c 913 "old version of the file format. This means your key\n"
914 "file is not fully tamperproof. Future versions of\n"
f6f450e2 915 "%s may stop supporting this private key format,\n"
7bedb13c 916 "so we recommend you convert your key to the new\n"
917 "format.\n"
918 "\n"
919 "You can perform this conversion by loading the key\n"
920 "into PuTTYgen and then saving it again.";
921
f6f450e2 922 char *msg, *title;
923 msg = dupprintf(message, appname);
924 title = dupprintf(mbtitle, appname);
925
926 MessageBox(NULL, msg, title, MB_OK);
927
928 sfree(msg);
929 sfree(title);
7bedb13c 930}