PocketPuTTY website moved.
[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;
1e5eefb6 243 wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);
939395a8 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
1e5eefb6 254 SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */
255 SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
939395a8 256
257 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
1e5eefb6 258 flags=GetWindowLongPtr(hwnd, BOXFLAGS);
939395a8 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
1e5eefb6 268 ret=GetWindowLongPtr(hwnd, BOXRESULT);
939395a8 269 DestroyWindow(hwnd);
270 return ret;
271}
272
273static void SaneEndDialog(HWND hwnd, int ret)
274{
1e5eefb6 275 SetWindowLongPtr(hwnd, BOXRESULT, ret);
276 SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);
939395a8 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);
1e5eefb6 378 SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
70133c0e 379 if (help_path)
1e5eefb6 380 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
381 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
382 WS_EX_CONTEXTHELP);
70133c0e 383 else {
384 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
385 if (item)
386 DestroyWindow(item);
387 }
388 requested_help = FALSE;
648947d4 389 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
390 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
c96a8fef 391 /*
392 * Centre the window.
393 */
394 { /* centre the window */
395 RECT rs, rd;
396
397 hw = GetDesktopWindow();
32874aea 398 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
399 MoveWindow(hwnd,
400 (rs.right + rs.left + rd.left - rd.right) / 2,
401 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
402 rd.right - rd.left, rd.bottom - rd.top, TRUE);
c96a8fef 403 }
404
405 /*
927d4fc5 406 * Create the tree view.
c96a8fef 407 */
32874aea 408 {
409 RECT r;
c96a8fef 410 WPARAM font;
32874aea 411 HWND tvstatic;
412
413 r.left = 3;
05581745 414 r.right = r.left + 95;
32874aea 415 r.top = 3;
416 r.bottom = r.top + 10;
417 MapDialogRect(hwnd, &r);
418 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
419 WS_CHILD | WS_VISIBLE,
420 r.left, r.top,
421 r.right - r.left, r.bottom - r.top,
422 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
423 NULL);
c96a8fef 424 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
927d4fc5 425 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
426
32874aea 427 r.left = 3;
05581745 428 r.right = r.left + 95;
32874aea 429 r.top = 13;
a401e5f3 430 r.bottom = r.top + 219;
32874aea 431 MapDialogRect(hwnd, &r);
432 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
433 WS_CHILD | WS_VISIBLE |
434 WS_TABSTOP | TVS_HASLINES |
435 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
436 | TVS_LINESATROOT |
437 TVS_SHOWSELALWAYS, r.left, r.top,
438 r.right - r.left, r.bottom - r.top,
439 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
440 NULL);
927d4fc5 441 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
442 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
32874aea 443 tvfaff.treeview = treeview;
444 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
445 }
c96a8fef 446
447 /*
3ac9cd9f 448 * Set up the tree view contents.
c96a8fef 449 */
fe8abbf4 450 {
451 HTREEITEM hfirst = NULL;
452 int i;
453 char *path = NULL;
454
455 for (i = 0; i < ctrlbox->nctrlsets; i++) {
456 struct controlset *s = ctrlbox->ctrlsets[i];
457 HTREEITEM item;
458 int j;
459 char *c;
460
461 if (!s->pathname[0])
462 continue;
463 j = path ? ctrl_path_compare(s->pathname, path) : 0;
464 if (j == INT_MAX)
465 continue; /* same path, nothing to add to tree */
466
467 /*
468 * We expect never to find an implicit path
469 * component. For example, we expect never to see
470 * A/B/C followed by A/D/E, because that would
471 * _implicitly_ create A/D. All our path prefixes
472 * are expected to contain actual controls and be
473 * selectable in the treeview; so we would expect
474 * to see A/D _explicitly_ before encountering
475 * A/D/E.
476 */
477 assert(j == ctrl_path_elements(s->pathname) - 1);
478
479 c = strrchr(s->pathname, '/');
480 if (!c)
481 c = s->pathname;
482 else
483 c++;
484
485 item = treeview_insert(&tvfaff, j, c, s->pathname);
486 if (!hfirst)
487 hfirst = item;
488
489 path = s->pathname;
32874aea 490 }
32874aea 491
fe8abbf4 492 /*
493 * Put the treeview selection on to the Session panel.
494 * This should also cause creation of the relevant
495 * controls.
496 */
497 TreeView_SelectItem(treeview, hfirst);
498 }
32874aea 499
500 /*
501 * Set focus into the first available control.
502 */
503 {
fe8abbf4 504 int i;
505 struct winctrl *c;
506
507 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
508 i++) {
509 if (c->ctrl) {
510 dlg_set_focus(c->ctrl, &dp);
511 break;
512 }
513 }
32874aea 514 }
c96a8fef 515
1e5eefb6 516 SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
c96a8fef 517 return 0;
1cd246bb 518 case WM_LBUTTONUP:
32874aea 519 /*
520 * Button release should trigger WM_OK if there was a
521 * previous double click on the session list.
522 */
523 ReleaseCapture();
fe8abbf4 524 if (dp.ended)
8ee3ff16 525 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
32874aea 526 break;
c96a8fef 527 case WM_NOTIFY:
927d4fc5 528 if (LOWORD(wParam) == IDCX_TREEVIEW &&
32874aea 529 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
530 HTREEITEM i =
531 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
927d4fc5 532 TVITEM item;
c96a8fef 533 char buffer[64];
78a79d97 534
535 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
536
32874aea 537 item.hItem = i;
c96a8fef 538 item.pszText = buffer;
539 item.cchTextMax = sizeof(buffer);
fe8abbf4 540 item.mask = TVIF_TEXT | TVIF_PARAM;
32874aea 541 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
fe8abbf4 542 {
543 /* Destroy all controls in the currently visible panel. */
544 int k;
545 HWND item;
546 struct winctrl *c;
547
548 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
549 for (k = 0; k < c->num_ids; k++) {
550 item = GetDlgItem(hwnd, c->base_id + k);
551 if (item)
552 DestroyWindow(item);
553 }
554 winctrl_rem_shortcuts(&dp, c);
555 winctrl_remove(&ctrls_panel, c);
556 sfree(c->data);
557 sfree(c);
558 }
32874aea 559 }
fe8abbf4 560 create_controls(hwnd, (char *)item.lParam);
561
562 dlg_refresh(NULL, &dp); /* set up control values */
78a79d97 563
564 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
565 InvalidateRect (hwnd, NULL, TRUE);
c96a8fef 566
32874aea 567 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
c96a8fef 568 return 0;
569 }
570 break;
374330e2 571 case WM_COMMAND:
fe8abbf4 572 case WM_DRAWITEM:
573 default: /* also handle drag list msg here */
c96a8fef 574 /*
575 * Only process WM_COMMAND once the dialog is fully formed.
576 */
1e5eefb6 577 if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {
fe8abbf4 578 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
579 if (dp.ended && GetCapture() != hwnd)
8ee3ff16 580 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
fe8abbf4 581 } else
582 ret = 0;
583 return ret;
70133c0e 584 case WM_HELP:
585 if (help_path) {
fe8abbf4 586 if (winctrl_context_help(&dp, hwnd,
587 ((LPHELPINFO)lParam)->iCtrlId))
70133c0e 588 requested_help = TRUE;
fe8abbf4 589 else
70133c0e 590 MessageBeep(0);
70133c0e 591 }
592 break;
374330e2 593 case WM_CLOSE:
70133c0e 594 if (requested_help) {
595 WinHelp(hwnd, help_path, HELP_QUIT, 0);
596 requested_help = FALSE;
597 }
8ee3ff16 598 SaneEndDialog(hwnd, 0);
374330e2 599 return 0;
c9def1b8 600
601 /* Grrr Explorer will maximize Dialogs! */
602 case WM_SIZE:
603 if (wParam == SIZE_MAXIMIZED)
32874aea 604 force_normal(hwnd);
c9def1b8 605 return 0;
ca20bfcf 606
374330e2 607 }
608 return 0;
609}
610
fe8abbf4 611void modal_about_box(HWND hwnd)
32874aea 612{
fe8abbf4 613 EnableWindow(hwnd, 0);
614 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
615 EnableWindow(hwnd, 1);
616 SetActiveWindow(hwnd);
374330e2 617}
618
fe8abbf4 619void show_help(HWND hwnd)
32874aea 620{
fe8abbf4 621 if (help_path) {
622 WinHelp(hwnd, help_path,
623 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
624 0);
625 requested_help = TRUE;
626 }
374330e2 627}
628
32874aea 629void defuse_showwindow(void)
630{
301b66db 631 /*
632 * Work around the fact that the app's first call to ShowWindow
633 * will ignore the default in favour of the shell-provided
634 * setting.
635 */
636 {
32874aea 637 HWND hwnd;
638 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
639 NULL, NullDlgProc);
640 ShowWindow(hwnd, SW_HIDE);
f5eca4f8 641 SetActiveWindow(hwnd);
32874aea 642 DestroyWindow(hwnd);
301b66db 643 }
644}
645
32874aea 646int do_config(void)
647{
374330e2 648 int ret;
649
fe8abbf4 650 ctrlbox = ctrl_new_box();
12745e35 651 setup_config_box(ctrlbox, FALSE, 0, 0);
fe8abbf4 652 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
4e6d4091 653 dp_init(&dp);
fe8abbf4 654 winctrl_init(&ctrls_base);
655 winctrl_init(&ctrls_panel);
4e6d4091 656 dp_add_tree(&dp, &ctrls_base);
657 dp_add_tree(&dp, &ctrls_panel);
f6f450e2 658 dp.wintitle = dupprintf("%s Configuration", appname);
659 dp.errtitle = dupprintf("%s Error", appname);
fe8abbf4 660 dp.data = &cfg;
fe8abbf4 661 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
662
32874aea 663 ret =
8ee3ff16 664 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
fe8abbf4 665 GenericMainDlgProc);
374330e2 666
fe8abbf4 667 ctrl_free_box(ctrlbox);
fe8abbf4 668 winctrl_cleanup(&ctrls_panel);
4e6d4091 669 winctrl_cleanup(&ctrls_base);
670 dp_cleanup(&dp);
fe8abbf4 671
374330e2 672 return ret;
673}
674
f89c3294 675int do_reconfig(HWND hwnd, int protcfginfo)
32874aea 676{
374330e2 677 Config backup_cfg;
678 int ret;
679
680 backup_cfg = cfg; /* structure copy */
fe8abbf4 681
682 ctrlbox = ctrl_new_box();
12745e35 683 setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);
fe8abbf4 684 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
4e6d4091 685 dp_init(&dp);
fe8abbf4 686 winctrl_init(&ctrls_base);
687 winctrl_init(&ctrls_panel);
4e6d4091 688 dp_add_tree(&dp, &ctrls_base);
689 dp_add_tree(&dp, &ctrls_panel);
f6f450e2 690 dp.wintitle = dupprintf("%s Reconfiguration", appname);
691 dp.errtitle = dupprintf("%s Error", appname);
fe8abbf4 692 dp.data = &cfg;
fe8abbf4 693 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
694
8ee3ff16 695 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
fe8abbf4 696 GenericMainDlgProc);
697
698 ctrl_free_box(ctrlbox);
699 winctrl_cleanup(&ctrls_base);
700 winctrl_cleanup(&ctrls_panel);
4e6d4091 701 dp_cleanup(&dp);
fe8abbf4 702
374330e2 703 if (!ret)
704 cfg = backup_cfg; /* structure copy */
c9def1b8 705
374330e2 706 return ret;
707}
708
cbe2d68f 709void logevent(void *frontend, const char *string)
32874aea 710{
71346075 711 char timebuf[40];
aca589d9 712 struct tm tm;
71346075 713
a8327734 714 log_eventlog(logctx, string);
fb89f7ff 715
c5e9c988 716 if (nevents >= negsize) {
374330e2 717 negsize += 64;
3d88e64d 718 events = sresize(events, negsize, char *);
374330e2 719 }
71346075 720
aca589d9 721 tm=ltime();
722 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
71346075 723
3d88e64d 724 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
71346075 725 strcpy(events[nevents], timebuf);
726 strcat(events[nevents], string);
9ad90448 727 if (logbox) {
32874aea 728 int count;
729 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
730 0, (LPARAM) events[nevents]);
731 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
732 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
9ad90448 733 }
bce816e7 734 nevents++;
374330e2 735}
736
32874aea 737void showeventlog(HWND hwnd)
738{
374330e2 739 if (!logbox) {
32874aea 740 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
741 hwnd, LogProc);
742 ShowWindow(logbox, SW_SHOWNORMAL);
374330e2 743 }
9ecf8e5a 744 SetActiveWindow(logbox);
374330e2 745}
746
32874aea 747void showabout(HWND hwnd)
748{
749 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
374330e2 750}
751
3d9449a1 752int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
753 char *keystr, char *fingerprint,
754 void (*callback)(void *ctx, int result), void *ctx)
32874aea 755{
d5859615 756 int ret;
374330e2 757
d5859615 758 static const char absentmsg[] =
32874aea 759 "The server's host key is not cached in the registry. You\n"
760 "have no guarantee that the server is the computer you\n"
761 "think it is.\n"
65f66c88 762 "The server's %s key fingerprint is:\n"
32874aea 763 "%s\n"
764 "If you trust this host, hit Yes to add the key to\n"
f6f450e2 765 "%s's cache and carry on connecting.\n"
d0718310 766 "If you want to carry on connecting just once, without\n"
767 "adding the key to the cache, hit No.\n"
768 "If you do not trust this host, hit Cancel to abandon the\n"
32874aea 769 "connection.\n";
d5859615 770
771 static const char wrongmsg[] =
32874aea 772 "WARNING - POTENTIAL SECURITY BREACH!\n"
773 "\n"
f6f450e2 774 "The server's host key does not match the one %s has\n"
32874aea 775 "cached in the registry. This means that either the\n"
776 "server administrator has changed the host key, or you\n"
777 "have actually connected to another computer pretending\n"
778 "to be the server.\n"
65f66c88 779 "The new %s key fingerprint is:\n"
32874aea 780 "%s\n"
781 "If you were expecting this change and trust the new key,\n"
f6f450e2 782 "hit Yes to update %s's cache and continue connecting.\n"
32874aea 783 "If you want to carry on connecting but without updating\n"
784 "the cache, hit No.\n"
785 "If you want to abandon the connection completely, hit\n"
786 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
d5859615 787
f6f450e2 788 static const char mbtitle[] = "%s Security Alert";
de3df031 789
790 /*
d5859615 791 * Verify the key against the registry.
de3df031 792 */
d4857987 793 ret = verify_host_key(host, port, keytype, keystr);
d5859615 794
32874aea 795 if (ret == 0) /* success - key matched OK */
3d9449a1 796 return 1;
32874aea 797 if (ret == 2) { /* key was different */
798 int mbret;
690695e0 799 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
800 appname);
801 char *caption = dupprintf(mbtitle, appname);
802 mbret = message_box(text, caption,
803 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
804 HELPCTXID(errors_hostkey_changed));
b3f3c051 805 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
690695e0 806 sfree(text);
807 sfree(caption);
13d40433 808 if (mbret == IDYES) {
32874aea 809 store_host_key(host, port, keytype, keystr);
13d40433 810 return 1;
811 } else if (mbret == IDNO)
b3f3c051 812 return 1;
813 return 0;
de3df031 814 }
32874aea 815 if (ret == 1) { /* key was absent */
816 int mbret;
690695e0 817 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
818 char *caption = dupprintf(mbtitle, appname);
819 mbret = message_box(text, caption,
820 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
821 HELPCTXID(errors_hostkey_absent));
b3f3c051 822 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
690695e0 823 sfree(text);
824 sfree(caption);
825 if (mbret == IDYES) {
d0718310 826 store_host_key(host, port, keytype, keystr);
690695e0 827 return 1;
828 } else if (mbret == IDNO)
b3f3c051 829 return 1;
830 return 0;
de3df031 831 }
de3df031 832}
e1c8e0ed 833
834/*
83e7d008 835 * Ask whether the selected algorithm is acceptable (since it was
ca20bfcf 836 * below the configured 'warn' threshold).
ca20bfcf 837 */
3d9449a1 838int askalg(void *frontend, const char *algtype, const char *algname,
839 void (*callback)(void *ctx, int result), void *ctx)
ca20bfcf 840{
f6f450e2 841 static const char mbtitle[] = "%s Security Alert";
ca20bfcf 842 static const char msg[] =
83e7d008 843 "The first %s supported by the server\n"
ca20bfcf 844 "is %.64s, which is below the configured\n"
845 "warning threshold.\n"
846 "Do you want to continue with this connection?\n";
f6f450e2 847 char *message, *title;
ca20bfcf 848 int mbret;
849
83e7d008 850 message = dupprintf(msg, algtype, algname);
f6f450e2 851 title = dupprintf(mbtitle, appname);
852 mbret = MessageBox(NULL, message, title,
76347f46 853 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
f6f450e2 854 sfree(message);
855 sfree(title);
ca20bfcf 856 if (mbret == IDYES)
3d9449a1 857 return 1;
ca20bfcf 858 else
3d9449a1 859 return 0;
ca20bfcf 860}
861
862/*
e1c8e0ed 863 * Ask whether to wipe a session log file before writing to it.
864 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
865 */
919baedb 866int askappend(void *frontend, Filename filename,
867 void (*callback)(void *ctx, int result), void *ctx)
32874aea 868{
e1c8e0ed 869 static const char msgtemplate[] =
870 "The session log file \"%.*s\" already exists.\n"
871 "You can overwrite it with a new session log,\n"
872 "append your session log to the end of it,\n"
873 "or disable session logging for this session.\n"
874 "Hit Yes to wipe the file, No to append to it,\n"
875 "or Cancel to disable logging.";
f6f450e2 876 char *message;
877 char *mbtitle;
e1c8e0ed 878 int mbret;
bf61b566 879
f6f450e2 880 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
881 mbtitle = dupprintf("%s Log to File", appname);
e1c8e0ed 882
883 mbret = MessageBox(NULL, message, mbtitle,
76347f46 884 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
f6f450e2 885
886 sfree(message);
887 sfree(mbtitle);
888
e1c8e0ed 889 if (mbret == IDYES)
890 return 2;
891 else if (mbret == IDNO)
892 return 1;
893 else
894 return 0;
895}
7bedb13c 896
897/*
898 * Warn about the obsolescent key file format.
a8327734 899 *
900 * Uniquely among these functions, this one does _not_ expect a
901 * frontend handle. This means that if PuTTY is ported to a
902 * platform which requires frontend handles, this function will be
903 * an anomaly. Fortunately, the problem it addresses will not have
904 * been present on that platform, so it can plausibly be
905 * implemented as an empty function.
7bedb13c 906 */
907void old_keyfile_warning(void)
908{
f6f450e2 909 static const char mbtitle[] = "%s Key File Warning";
7bedb13c 910 static const char message[] =
2e85c969 911 "You are loading an SSH-2 private key which has an\n"
7bedb13c 912 "old version of the file format. This means your key\n"
913 "file is not fully tamperproof. Future versions of\n"
f6f450e2 914 "%s may stop supporting this private key format,\n"
7bedb13c 915 "so we recommend you convert your key to the new\n"
916 "format.\n"
917 "\n"
918 "You can perform this conversion by loading the key\n"
919 "into PuTTYgen and then saving it again.";
920
f6f450e2 921 char *msg, *title;
922 msg = dupprintf(message, appname);
923 title = dupprintf(mbtitle, appname);
924
925 MessageBox(NULL, msg, title, MB_OK);
926
927 sfree(msg);
928 sfree(title);
7bedb13c 929}