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