]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - windows/windlg.c
first pass
[PuTTY.git] / windows / windlg.c
index 04470cd66802ce812ab18d6bd8463601c1c816ba..e29f12914e3428e0b54257a6ae837802cb0ef466 100644 (file)
@@ -1,3 +1,7 @@
+/*
+ * windlg.c - dialogs for PuTTY(tel), including the configuration dialog.
+ */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
@@ -10,6 +14,7 @@
 #include "win_res.h"
 #include "storage.h"
 #include "dialog.h"
+#include "licence.h"
 
 #include <commctrl.h>
 #include <commdlg.h>
@@ -40,11 +45,7 @@ static struct dlgparam dp;
 static char **events = NULL;
 static int nevents = 0, negsize = 0;
 
-static int requested_help;
-
-extern Config cfg;                    /* defined in window.c */
-
-struct sesslist sesslist;             /* exported to window.c */
+extern Conf *conf;                    /* defined in window.c */
 
 #define PRINTER_DISABLED_STRING "None (printing disabled)"
 
@@ -66,8 +67,8 @@ void force_normal(HWND hwnd)
     recurse = 0;
 }
 
-static int CALLBACK LogProc(HWND hwnd, UINT msg,
-                           WPARAM wParam, LPARAM lParam)
+static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg,
+                                WPARAM wParam, LPARAM lParam)
 {
     int i;
 
@@ -161,8 +162,8 @@ static int CALLBACK LogProc(HWND hwnd, UINT msg,
     return 0;
 }
 
-static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
-                               WPARAM wParam, LPARAM lParam)
+static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
+                                    WPARAM wParam, LPARAM lParam)
 {
     switch (msg) {
       case WM_INITDIALOG:
@@ -170,6 +171,7 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
            char *str = dupprintf("%s Licence", appname);
            SetWindowText(hwnd, str);
            sfree(str);
+            SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n"));
        }
        return 1;
       case WM_COMMAND:
@@ -187,8 +189,8 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
     return 0;
 }
 
-static int CALLBACK AboutProc(HWND hwnd, UINT msg,
-                             WPARAM wParam, LPARAM lParam)
+static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
+                                  WPARAM wParam, LPARAM lParam)
 {
     char *str;
 
@@ -197,8 +199,16 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg,
        str = dupprintf("About %s", appname);
        SetWindowText(hwnd, str);
        sfree(str);
-       SetDlgItemText(hwnd, IDA_TEXT1, appname);
-       SetDlgItemText(hwnd, IDA_VERSION, ver);
+        {
+            char *buildinfo_text = buildinfo("\r\n");
+            char *text = dupprintf
+                ("%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
+                 appname, ver, buildinfo_text,
+                 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
+            sfree(buildinfo_text);
+            SetDlgItemText(hwnd, IDA_TEXT, text);
+            sfree(text);
+        }
        return 1;
       case WM_COMMAND:
        switch (LOWORD(wParam)) {
@@ -229,11 +239,62 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg,
     return 0;
 }
 
+static int SaneDialogBox(HINSTANCE hinst,
+                        LPCTSTR tmpl,
+                        HWND hwndparent,
+                        DLGPROC lpDialogFunc)
+{
+    WNDCLASS wc;
+    HWND hwnd;
+    MSG msg;
+    int flags;
+    int ret;
+    int gm;
+
+    wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
+    wc.lpfnWndProc = DefDlgProc;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);
+    wc.hInstance = hinst;
+    wc.hIcon = NULL;
+    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = "PuTTYConfigBox";
+    RegisterClass(&wc);
+
+    hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
+
+    SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */
+    SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
+
+    while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
+       flags=GetWindowLongPtr(hwnd, BOXFLAGS);
+       if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
+           DispatchMessage(&msg);
+       if (flags & DF_END)
+           break;
+    }
+
+    if (gm == 0)
+        PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
+
+    ret=GetWindowLongPtr(hwnd, BOXRESULT);
+    DestroyWindow(hwnd);
+    return ret;
+}
+
+static void SaneEndDialog(HWND hwnd, int ret)
+{
+    SetWindowLongPtr(hwnd, BOXRESULT, ret);
+    SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);
+}
+
 /*
  * Null dialog procedure.
  */
-static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
-                               WPARAM wParam, LPARAM lParam)
+static INT_PTR CALLBACK NullDlgProc(HWND hwnd, UINT msg,
+                                    WPARAM wParam, LPARAM lParam)
 {
     return 0;
 }
@@ -271,7 +332,7 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff,
     newitem = TreeView_InsertItem(faff->treeview, &ins);
     if (level > 0)
        TreeView_Expand(faff->treeview, faff->lastat[level - 1],
-                       TVE_EXPAND);
+                       (level > 1 ? TVE_COLLAPSE : TVE_EXPAND));
     faff->lastat[level] = newitem;
     for (i = level + 1; i < 4; i++)
        faff->lastat[i] = NULL;
@@ -313,9 +374,11 @@ static void create_controls(HWND hwnd, char *path)
 
 /*
  * This function is the configuration box.
+ * (Being a dialog procedure, in general it returns 0 if the default
+ * dialog processing should be performed, and 1 if it should not.)
  */
-static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
-                                      WPARAM wParam, LPARAM lParam)
+static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
+                                           WPARAM wParam, LPARAM lParam)
 {
     HWND hw, treeview;
     struct treeview_faff tvfaff;
@@ -326,16 +389,16 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
        dp.hwnd = hwnd;
        create_controls(hwnd, "");     /* Open and Cancel buttons etc */
        SetWindowText(hwnd, dp.wintitle);
-       SetWindowLong(hwnd, GWL_USERDATA, 0);
-        if (help_path)
-            SetWindowLong(hwnd, GWL_EXSTYLE,
-                          GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
+       SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+        if (has_help())
+            SetWindowLongPtr(hwnd, GWL_EXSTYLE,
+                            GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
+                            WS_EX_CONTEXTHELP);
         else {
             HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
             if (item)
                 DestroyWindow(item);
         }
-        requested_help = FALSE;
        SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
                    (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
        /*
@@ -401,6 +464,7 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
            HTREEITEM hfirst = NULL;
            int i;
            char *path = NULL;
+            char *firstpath = NULL;
 
            for (i = 0; i < ctrlbox->nctrlsets; i++) {
                struct controlset *s = ctrlbox->ctrlsets[i];
@@ -433,18 +497,27 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
                        c++;
 
                item = treeview_insert(&tvfaff, j, c, s->pathname);
-               if (!hfirst)
+               if (!hfirst) {
                    hfirst = item;
+                    firstpath = s->pathname;
+                }
 
                path = s->pathname;
            }
 
            /*
-            * Put the treeview selection on to the Session panel.
-            * This should also cause creation of the relevant
-            * controls.
+            * Put the treeview selection on to the first panel in the
+            * ctrlbox.
             */
            TreeView_SelectItem(treeview, hfirst);
+
+            /*
+             * And create the actual control set for that panel, to
+             * match the initial treeview selection.
+             */
+            assert(firstpath);   /* config.c must have given us _something_ */
+            create_controls(hwnd, firstpath);
+           dlg_refresh(NULL, &dp);    /* and set up control values */
        }
 
        /*
@@ -463,7 +536,19 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
            }
        }
 
-       SetWindowLong(hwnd, GWL_USERDATA, 1);
+        /*
+         * Now we've finished creating our initial set of controls,
+         * it's safe to actually show the window without risking setup
+         * flicker.
+         */
+        ShowWindow(hwnd, SW_SHOWNORMAL);
+
+        /*
+         * Set the flag that activates a couple of the other message
+         * handlers below, which were disabled until now to avoid
+         * spurious firing during the above setup procedure.
+         */
+       SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
        return 0;
       case WM_LBUTTONUP:
        /*
@@ -477,10 +562,21 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
       case WM_NOTIFY:
        if (LOWORD(wParam) == IDCX_TREEVIEW &&
            ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
-           HTREEITEM i =
-               TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
+            /*
+             * Selection-change events on the treeview cause us to do
+             * a flurry of control deletion and creation - but only
+             * after WM_INITDIALOG has finished. The initial
+             * selection-change event(s) during treeview setup are
+             * ignored.
+             */
+           HTREEITEM i;
            TVITEM item;
            char buffer[64];
+
+            if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1)
+                return 0;
+
+            i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
  
            SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
  
@@ -524,7 +620,7 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
        /*
         * Only process WM_COMMAND once the dialog is fully formed.
         */
-       if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
+       if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {
            ret = winctrl_handle_command(&dp, msg, wParam, lParam);
            if (dp.ended && GetCapture() != hwnd)
                SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
@@ -532,19 +628,12 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
            ret = 0;
        return ret;
       case WM_HELP:
-        if (help_path) {
-           if (winctrl_context_help(&dp, hwnd,
-                                    ((LPHELPINFO)lParam)->iCtrlId))
-                requested_help = TRUE;
-           else
-                MessageBeep(0);
-        }
+       if (!winctrl_context_help(&dp, hwnd,
+                                ((LPHELPINFO)lParam)->iCtrlId))
+           MessageBeep(0);
         break;
       case WM_CLOSE:
-        if (requested_help) {
-            WinHelp(hwnd, help_path, HELP_QUIT, 0);
-            requested_help = FALSE;
-        }
+       quit_help(hwnd);
        SaneEndDialog(hwnd, 0);
        return 0;
 
@@ -568,12 +657,7 @@ void modal_about_box(HWND hwnd)
 
 void show_help(HWND hwnd)
 {
-    if (help_path) {
-       WinHelp(hwnd, help_path,
-               help_has_contents ? HELP_FINDER : HELP_CONTENTS,
-               0);
-       requested_help = TRUE;
-    }
+    launch_help(hwnd, NULL);
 }
 
 void defuse_showwindow(void)
@@ -598,8 +682,8 @@ int do_config(void)
     int ret;
 
     ctrlbox = ctrl_new_box();
-    setup_config_box(ctrlbox, &sesslist, FALSE, 0, 0);
-    win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
+    setup_config_box(ctrlbox, FALSE, 0, 0);
+    win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), FALSE, 0);
     dp_init(&dp);
     winctrl_init(&ctrls_base);
     winctrl_init(&ctrls_panel);
@@ -607,14 +691,13 @@ int do_config(void)
     dp_add_tree(&dp, &ctrls_panel);
     dp.wintitle = dupprintf("%s Configuration", appname);
     dp.errtitle = dupprintf("%s Error", appname);
-    dp.data = &cfg;
+    dp.data = conf;
+    dlg_auto_set_fixed_pitch_flag(&dp);
     dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */
 
-    get_sesslist(&sesslist, TRUE);
     ret =
        SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
                  GenericMainDlgProc);
-    get_sesslist(&sesslist, FALSE);
 
     ctrl_free_box(ctrlbox);
     winctrl_cleanup(&ctrls_panel);
@@ -626,14 +709,15 @@ int do_config(void)
 
 int do_reconfig(HWND hwnd, int protcfginfo)
 {
-    Config backup_cfg;
-    int ret;
+    Conf *backup_conf;
+    int ret, protocol;
 
-    backup_cfg = cfg;                 /* structure copy */
+    backup_conf = conf_copy(conf);
 
     ctrlbox = ctrl_new_box();
-    setup_config_box(ctrlbox, &sesslist, TRUE, cfg.protocol, protcfginfo);
-    win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
+    protocol = conf_get_int(conf, CONF_protocol);
+    setup_config_box(ctrlbox, TRUE, protocol, protcfginfo);
+    win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol);
     dp_init(&dp);
     winctrl_init(&ctrls_base);
     winctrl_init(&ctrls_panel);
@@ -641,7 +725,8 @@ int do_reconfig(HWND hwnd, int protcfginfo)
     dp_add_tree(&dp, &ctrls_panel);
     dp.wintitle = dupprintf("%s Reconfiguration", appname);
     dp.errtitle = dupprintf("%s Error", appname);
-    dp.data = &cfg;
+    dp.data = conf;
+    dlg_auto_set_fixed_pitch_flag(&dp);
     dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */
 
     ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
@@ -653,7 +738,9 @@ int do_reconfig(HWND hwnd, int protcfginfo)
     dp_cleanup(&dp);
 
     if (!ret)
-       cfg = backup_cfg;              /* structure copy */
+       conf_copy_into(conf, backup_conf);
+
+    conf_free(backup_conf);
 
     return ret;
 }
@@ -701,8 +788,9 @@ void showabout(HWND hwnd)
     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port,
+                        const char *keytype, char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     int ret;
 
@@ -744,42 +832,48 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)                     /* success - key matched OK */
-       return;
-    if (ret == 2) {                   /* key was different */
+       return 1;
+    else if (ret == 2) {              /* key was different */
        int mbret;
-       char *message, *title;
-       message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname);
-       title = dupprintf(mbtitle, appname);
-       mbret = MessageBox(NULL, message, title,
-                          MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3);
-       sfree(message);
-       sfree(title);
-       if (mbret == IDYES)
+       char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
+                              appname);
+       char *caption = dupprintf(mbtitle, appname);
+       mbret = message_box(text, caption,
+                           MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
+                           HELPCTXID(errors_hostkey_changed));
+       assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
+       sfree(text);
+       sfree(caption);
+       if (mbret == IDYES) {
            store_host_key(host, port, keytype, keystr);
-       if (mbret == IDCANCEL)
-           cleanup_exit(0);
-    }
-    if (ret == 1) {                   /* key was absent */
+           return 1;
+       } else if (mbret == IDNO)
+           return 1;
+    } else if (ret == 1) {            /* key was absent */
        int mbret;
-       char *message, *title;
-       message = dupprintf(absentmsg, keytype, fingerprint, appname);
-       title = dupprintf(mbtitle, appname);
-       mbret = MessageBox(NULL, message, title,
-                          MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3);
-       sfree(message);
-       sfree(title);
-       if (mbret == IDYES)
+       char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
+       char *caption = dupprintf(mbtitle, appname);
+       mbret = message_box(text, caption,
+                           MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
+                           HELPCTXID(errors_hostkey_absent));
+       assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
+       sfree(text);
+       sfree(caption);
+       if (mbret == IDYES) {
            store_host_key(host, port, keytype, keystr);
-       if (mbret == IDCANCEL)
-           cleanup_exit(0);
+           return 1;
+       } else if (mbret == IDNO)
+           return 1;
     }
+    return 0;  /* abandon the connection */
 }
 
 /*
  * Ask whether the selected algorithm is acceptable (since it was
  * below the configured 'warn' threshold).
  */
-void askalg(void *frontend, const char *algtype, const char *algname)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
     static const char mbtitle[] = "%s Security Alert";
     static const char msg[] =
@@ -794,19 +888,48 @@ void askalg(void *frontend, const char *algtype, const char *algname)
     title = dupprintf(mbtitle, appname);
     mbret = MessageBox(NULL, message, title,
                       MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
+    socket_reselect_all();
     sfree(message);
     sfree(title);
     if (mbret == IDYES)
-       return;
+       return 1;
     else
-       cleanup_exit(0);
+       return 0;
+}
+
+int askhk(void *frontend, const char *algname, const char *betteralgs,
+          void (*callback)(void *ctx, int result), void *ctx)
+{
+    static const char mbtitle[] = "%s Security Alert";
+    static const char msg[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Do you want to continue with this connection?\n";
+    char *message, *title;
+    int mbret;
+
+    message = dupprintf(msg, algname, betteralgs);
+    title = dupprintf(mbtitle, appname);
+    mbret = MessageBox(NULL, message, title,
+                      MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
+    socket_reselect_all();
+    sfree(message);
+    sfree(title);
+    if (mbret == IDYES)
+       return 1;
+    else
+       return 0;
 }
 
 /*
  * Ask whether to wipe a session log file before writing to it.
  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
  */
-int askappend(void *frontend, Filename filename)
+int askappend(void *frontend, Filename *filename,
+             void (*callback)(void *ctx, int result), void *ctx)
 {
     static const char msgtemplate[] =
        "The session log file \"%.*s\" already exists.\n"
@@ -819,12 +942,14 @@ int askappend(void *frontend, Filename filename)
     char *mbtitle;
     int mbret;
 
-    message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
+    message = dupprintf(msgtemplate, FILENAME_MAX, filename->path);
     mbtitle = dupprintf("%s Log to File", appname);
 
     mbret = MessageBox(NULL, message, mbtitle,
                       MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
 
+    socket_reselect_all();
+
     sfree(message);
     sfree(mbtitle);
 
@@ -850,7 +975,7 @@ void old_keyfile_warning(void)
 {
     static const char mbtitle[] = "%s Key File Warning";
     static const char message[] =
-       "You are loading an SSH 2 private key which has an\n"
+       "You are loading an SSH-2 private key which has an\n"
        "old version of the file format. This means your key\n"
        "file is not fully tamperproof. Future versions of\n"
        "%s may stop supporting this private key format,\n"
@@ -866,6 +991,8 @@ void old_keyfile_warning(void)
 
     MessageBox(NULL, msg, title, MB_OK);
 
+    socket_reselect_all();
+
     sfree(msg);
     sfree(title);
 }