Patch from Alejandro Sedeno, somewhat modified by me, which
[u/mdw/putty] / config.c
index 39da6c2..d689ede 100644 (file)
--- a/config.c
+++ b/config.c
 
 #define PRINTER_DISABLED_STRING "None (printing disabled)"
 
-static void protocolbuttons_handler(union control *ctrl, void *dlg,
+#define HOST_BOX_TITLE "Host Name (or IP address)"
+#define PORT_BOX_TITLE "Port"
+
+static void config_host_handler(union control *ctrl, void *dlg,
+                               void *data, int event)
+{
+    Config *cfg = (Config *)data;
+
+    /*
+     * This function works just like the standard edit box handler,
+     * only it has to choose the control's label and text from two
+     * different places depending on the protocol.
+     */
+    if (event == EVENT_REFRESH) {
+       if (cfg->protocol == PROT_SERIAL) {
+           /*
+            * This label text is carefully chosen to contain an n,
+            * since that's the shortcut for the host name control.
+            */
+           dlg_label_change(ctrl, dlg, "Serial line");
+           dlg_editbox_set(ctrl, dlg, cfg->serline);
+       } else {
+           dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
+           dlg_editbox_set(ctrl, dlg, cfg->host);
+       }
+    } else if (event == EVENT_VALCHANGE) {
+       if (cfg->protocol == PROT_SERIAL)
+           dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline));
+       else
+           dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host));
+    }
+}
+
+static void config_port_handler(union control *ctrl, void *dlg,
+                               void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    char buf[80];
+
+    /*
+     * This function works just like the standard edit box handler,
+     * only it has to choose the control's label and text from two
+     * different places depending on the protocol.
+     */
+    if (event == EVENT_REFRESH) {
+       if (cfg->protocol == PROT_SERIAL) {
+           /*
+            * This label text is carefully chosen to contain a p,
+            * since that's the shortcut for the port control.
+            */
+           dlg_label_change(ctrl, dlg, "Speed");
+           sprintf(buf, "%d", cfg->serspeed);
+       } else {
+           dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
+           sprintf(buf, "%d", cfg->port);
+       }
+       dlg_editbox_set(ctrl, dlg, buf);
+    } else if (event == EVENT_VALCHANGE) {
+       dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
+       if (cfg->protocol == PROT_SERIAL)
+           cfg->serspeed = atoi(buf);
+       else
+           cfg->port = atoi(buf);
+    }
+}
+
+struct hostport {
+    union control *host, *port;
+};
+
+/*
+ * We export this function so that platform-specific config
+ * routines can use it to conveniently identify the protocol radio
+ * buttons in order to add to them.
+ */
+void config_protocolbuttons_handler(union control *ctrl, void *dlg,
                                    void *data, int event)
 {
-    int button, defport;
+    int button;
     Config *cfg = (Config *)data;
+    struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
+
     /*
      * This function works just like the standard radio-button
      * handler, except that it also has to change the setting of
-     * the port box. We expect the context parameter to point at
-     * the `union control' structure for the port box.
+     * the port box, and refresh both host and port boxes when. We
+     * expect the context parameter to point at a hostport
+     * structure giving the `union control's for both.
      */
     if (event == EVENT_REFRESH) {
        for (button = 0; button < ctrl->radio.nbuttons; button++)
@@ -36,17 +114,23 @@ static void protocolbuttons_handler(union control *ctrl, void *dlg,
        assert(button >= 0 && button < ctrl->radio.nbuttons);
        cfg->protocol = ctrl->radio.buttondata[button].i;
        if (oldproto != cfg->protocol) {
-           defport = -1;
-           switch (cfg->protocol) {
-             case PROT_SSH: defport = 22; break;
-             case PROT_TELNET: defport = 23; break;
-             case PROT_RLOGIN: defport = 513; break;
-           }
-           if (defport > 0 && cfg->port != defport) {
-               cfg->port = defport;
-               dlg_refresh((union control *)ctrl->radio.context.p, dlg);
-           }
+           Backend *ob = backend_from_proto(oldproto);
+           Backend *nb = backend_from_proto(cfg->protocol);
+           assert(ob);
+           assert(nb);
+           /* Iff the user hasn't changed the port from the protocol
+            * default (if any), update it with the new protocol's
+            * default.
+            * (XXX: this isn't perfect; a default can become permanent
+            * by going via the serial backend. However, it helps with
+            * the common case of tabbing through the controls in order
+            * and setting a non-default port.) */
+           if (cfg->port == ob->default_port &&
+               cfg->port > 0 && nb->default_port > 0)
+               cfg->port = nb->default_port;
        }
+       dlg_refresh(hp->host, dlg);
+       dlg_refresh(hp->port, dlg);
     }
 }
 
@@ -152,6 +236,33 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
     }
 }
 
+#ifndef NO_GSSAPI
+static void gsslist_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    if (event == EVENT_REFRESH) {
+       int i;
+
+       dlg_update_start(ctrl, dlg);
+       dlg_listbox_clear(ctrl, dlg);
+       for (i = 0; i < ngsslibs; i++) {
+           int id = cfg->ssh_gsslist[i];
+           assert(id >= 0 && id < ngsslibs);
+           dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
+       }
+       dlg_update_done(ctrl, dlg);
+
+    } else if (event == EVENT_VALCHANGE) {
+       int i;
+
+       /* Update array to match the list box. */
+       for (i=0; i < ngsslibs; i++)
+           cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i);
+    }
+}
+#endif
+
 static void kexlist_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
@@ -163,6 +274,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
            { "Diffie-Hellman group 1",         KEX_DHGROUP1 },
            { "Diffie-Hellman group 14",        KEX_DHGROUP14 },
            { "Diffie-Hellman group exchange",  KEX_DHGEX },
+           { "RSA-based key exchange",         KEX_RSA },
            { "-- warn below here --",          KEX_WARN }
        };
 
@@ -232,14 +344,14 @@ static void codepage_handler(union control *ctrl, void *dlg,
     Config *cfg = (Config *)data;
     if (event == EVENT_REFRESH) {
        int i;
-       const char *cp;
+       const char *cp, *thiscp;
        dlg_update_start(ctrl, dlg);
-       strcpy(cfg->line_codepage,
-              cp_name(decode_codepage(cfg->line_codepage)));
+       thiscp = cp_name(decode_codepage(cfg->line_codepage));
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
            dlg_listbox_add(ctrl, dlg, cp);
-       dlg_editbox_set(ctrl, dlg, cfg->line_codepage);
+       dlg_editbox_set(ctrl, dlg, thiscp);
+       strcpy(cfg->line_codepage, thiscp);
        dlg_update_done(ctrl, dlg);
     } else if (event == EVENT_VALCHANGE) {
        dlg_editbox_get(ctrl, dlg, cfg->line_codepage,
@@ -290,7 +402,7 @@ struct sessionsaver_data {
  */
 static int load_selected_session(struct sessionsaver_data *ssd,
                                 char *savedsession,
-                                void *dlg, Config *cfg)
+                                void *dlg, Config *cfg, int *maybe_launch)
 {
     int i = dlg_listbox_index(ssd->listbox, dlg);
     int isdef;
@@ -299,13 +411,17 @@ static int load_selected_session(struct sessionsaver_data *ssd,
        return 0;
     }
     isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
-    load_settings(ssd->sesslist.sessions[i], !isdef, cfg);
+    load_settings(ssd->sesslist.sessions[i], cfg);
     if (!isdef) {
        strncpy(savedsession, ssd->sesslist.sessions[i],
                SAVEDSESSION_LEN);
        savedsession[SAVEDSESSION_LEN-1] = '\0';
+       if (maybe_launch)
+           *maybe_launch = TRUE;
     } else {
        savedsession[0] = '\0';
+       if (maybe_launch)
+           *maybe_launch = FALSE;
     }
     dlg_refresh(NULL, dlg);
     /* Restore the selection, which might have been clobbered by
@@ -371,6 +487,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
            dlg_listbox_select(ssd->listbox, dlg, top);
        }
     } else if (event == EVENT_ACTION) {
+       int mbl = FALSE;
        if (!ssd->midsession &&
            (ctrl == ssd->listbox ||
             (ssd->loadbutton && ctrl == ssd->loadbutton))) {
@@ -381,8 +498,8 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * double-click on the list box _and_ that session
             * contains a hostname.
             */
-           if (load_selected_session(ssd, savedsession, dlg, cfg) &&
-               (ctrl == ssd->listbox && cfg->host[0])) {
+           if (load_selected_session(ssd, savedsession, dlg, cfg, &mbl) &&
+               (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) {
                dlg_end(dlg, 1);       /* it's all over, and succeeded */
            }
        } else if (ctrl == ssd->savebutton) {
@@ -403,7 +520,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
                }
            }
             {
-                char *errmsg = save_settings(savedsession, !isdef, cfg);
+                char *errmsg = save_settings(savedsession, cfg);
                 if (errmsg) {
                     dlg_error_msg(dlg, errmsg);
                     sfree(errmsg);
@@ -437,14 +554,17 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * there was a session selected in that which had a
             * valid host name in it, then load it and go.
             */
-           if (dlg_last_focused(ctrl, dlg) == ssd->listbox && !*cfg->host) {
+           if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
+               !cfg_launchable(cfg)) {
                Config cfg2;
-               if (!load_selected_session(ssd, savedsession, dlg, &cfg2)) {
+               int mbl = FALSE;
+               if (!load_selected_session(ssd, savedsession, dlg,
+                                          &cfg2, &mbl)) {
                    dlg_beep(dlg);
                    return;
                }
                /* If at this point we have a valid session, go! */
-               if (*cfg2.host) {
+               if (mbl && cfg_launchable(&cfg2)) {
                    *cfg = cfg2;       /* structure copy */
                    cfg->remote_cmd_ptr = NULL;
                    dlg_end(dlg, 1);
@@ -457,7 +577,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * Otherwise, do the normal thing: if we have a valid
             * session, get going.
             */
-           if (*cfg->host) {
+           if (cfg_launchable(cfg)) {
                dlg_end(dlg, 1);
            } else
                dlg_beep(dlg);
@@ -530,7 +650,7 @@ static void colour_handler(union control *ctrl, void *dlg,
     Config *cfg = (Config *)data;
     struct colour_data *cd =
        (struct colour_data *)ctrl->generic.context.p;
-    int update = FALSE, r, g, b;
+    int update = FALSE, clear = FALSE, r, g, b;
 
     if (event == EVENT_REFRESH) {
        if (ctrl == cd->listbox) {
@@ -540,21 +660,21 @@ static void colour_handler(union control *ctrl, void *dlg,
            for (i = 0; i < lenof(colours); i++)
                dlg_listbox_add(ctrl, dlg, colours[i]);
            dlg_update_done(ctrl, dlg);
-           dlg_editbox_set(cd->redit, dlg, "");
-           dlg_editbox_set(cd->gedit, dlg, "");
-           dlg_editbox_set(cd->bedit, dlg, "");
+           clear = TRUE;
+           update = TRUE;
        }
     } else if (event == EVENT_SELCHANGE) {
        if (ctrl == cd->listbox) {
            /* The user has selected a colour. Update the RGB text. */
            int i = dlg_listbox_index(ctrl, dlg);
            if (i < 0) {
-               dlg_beep(dlg);
-               return;
+               clear = TRUE;
+           } else {
+               clear = FALSE;
+               r = cfg->colours[i][0];
+               g = cfg->colours[i][1];
+               b = cfg->colours[i][2];
            }
-           r = cfg->colours[i][0];
-           g = cfg->colours[i][1];
-           b = cfg->colours[i][2];
            update = TRUE;
        }
     } else if (event == EVENT_VALCHANGE) {
@@ -564,7 +684,9 @@ static void colour_handler(union control *ctrl, void *dlg,
            int i, cval;
 
            dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
-           cval = atoi(buf) & 255;
+           cval = atoi(buf);
+           if (cval > 255) cval = 255;
+           if (cval < 0)   cval = 0;
 
            i = dlg_listbox_index(cd->listbox, dlg);
            if (i >= 0) {
@@ -605,16 +727,23 @@ static void colour_handler(union control *ctrl, void *dlg,
                cfg->colours[i][0] = r;
                cfg->colours[i][1] = g;
                cfg->colours[i][2] = b;
+               clear = FALSE;
                update = TRUE;
            }
        }
     }
 
     if (update) {
-       char buf[40];
-       sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
-       sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
-       sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
+       if (clear) {
+           dlg_editbox_set(cd->redit, dlg, "");
+           dlg_editbox_set(cd->gedit, dlg, "");
+           dlg_editbox_set(cd->bedit, dlg, "");
+       } else {
+           char buf[40];
+           sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
+           sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
+           sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
+       }
     }
 }
 
@@ -696,7 +825,29 @@ static void ttymodes_handler(union control *ctrl, void *dlg,
            char *p = cfg->ttymodes;
            int i = 0, len = lenof(cfg->ttymodes);
            while (*p) {
+               int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
                if (dlg_listbox_issel(td->listbox, dlg, i)) {
+                   if (!multisel) {
+                       /* Populate controls with entry we're about to
+                        * delete, for ease of editing.
+                        * (If multiple entries were selected, don't
+                        * touch the controls.) */
+                       char *val = strchr(p, '\t');
+                       if (val) {
+                           int ind = 0;
+                           val++;
+                           while (ttymodes[ind]) {
+                               if (strlen(ttymodes[ind]) == val-p-1 &&
+                                   !strncmp(ttymodes[ind], p, val-p-1))
+                                   break;
+                               ind++;
+                           }
+                           dlg_listbox_select(td->modelist, dlg, ind);
+                           dlg_radiobutton_set(td->valradio, dlg,
+                                               (*val == 'V'));
+                           dlg_editbox_set(td->valbox, dlg, val+1);
+                       }
+                   }
                    memmove(p, p+strlen(p)+1, len - (strlen(p)+1));
                    i++;
                    continue;
@@ -770,7 +921,7 @@ static void environ_handler(union control *ctrl, void *dlg,
            if (i < 0) {
                dlg_beep(dlg);
            } else {
-               char *p, *q;
+               char *p, *q, *str;
 
                dlg_listbox_del(ed->listbox, dlg, i);
                p = cfg->environmt;
@@ -785,8 +936,20 @@ static void environ_handler(union control *ctrl, void *dlg,
                q = p;
                if (!*p)
                    goto disaster;
-               while (*p)
-                   p++;
+               /* Populate controls with the entry we're about to delete
+                * for ease of editing */
+               str = p;
+               p = strchr(p, '\t');
+               if (!p)
+                   goto disaster;
+               *p = '\0';
+               dlg_editbox_set(ed->varbox, dlg, str);
+               p++;
+               str = p;
+               dlg_editbox_set(ed->valbox, dlg, str);
+               p = strchr(p, '\0');
+               if (!p)
+                   goto disaster;
                p++;
                while (*p) {
                    while (*p)
@@ -880,26 +1043,33 @@ static void portfwd_handler(union control *ctrl, void *dlg,
                *p = '\0';
            p = cfg->portfwd;
            while (*p) {
+               if (strcmp(p,str) == 0) {
+                   dlg_error_msg(dlg, "Specified forwarding already exists");
+                   break;
+               }
                while (*p)
                    p++;
                p++;
            }
-           if ((p - cfg->portfwd) + strlen(str) + 2 <=
-               sizeof(cfg->portfwd)) {
-               strcpy(p, str);
-               p[strlen(str) + 1] = '\0';
-               dlg_listbox_add(pfd->listbox, dlg, str);
-               dlg_editbox_set(pfd->sourcebox, dlg, "");
-               dlg_editbox_set(pfd->destbox, dlg, "");
-           } else {
-               dlg_error_msg(dlg, "Too many forwardings");
+           if (!*p) {
+               if ((p - cfg->portfwd) + strlen(str) + 2 <=
+                   sizeof(cfg->portfwd)) {
+                   strcpy(p, str);
+                   p[strlen(str) + 1] = '\0';
+                   dlg_listbox_add(pfd->listbox, dlg, str);
+                   dlg_editbox_set(pfd->sourcebox, dlg, "");
+                   dlg_editbox_set(pfd->destbox, dlg, "");
+               } else {
+                   dlg_error_msg(dlg, "Too many forwardings");
+               }
            }
        } else if (ctrl == pfd->rembutton) {
            int i = dlg_listbox_index(pfd->listbox, dlg);
            if (i < 0)
                dlg_beep(dlg);
            else {
-               char *p, *q;
+               char *p, *q, *src, *dst;
+               char dir;
 
                dlg_listbox_del(pfd->listbox, dlg, i);
                p = cfg->portfwd;
@@ -914,8 +1084,44 @@ static void portfwd_handler(union control *ctrl, void *dlg,
                q = p;
                if (!*p)
                    goto disaster2;
-               while (*p)
+               /* Populate the controls with the entry we're about to
+                * delete, for ease of editing. */
+               {
+                   static const char *const afs = "A46";
+                   char *afp = strchr(afs, *p);
+#ifndef NO_IPV6
+                   int idx = afp ? afp-afs : 0;
+#endif
+                   if (afp)
+                       p++;
+#ifndef NO_IPV6
+                   dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
+#endif
+               }
+               {
+                   static const char *const dirs = "LRD";
+                   dir = *p;
+                   dlg_radiobutton_set(pfd->direction, dlg,
+                                       strchr(dirs, dir) - dirs);
+               }
+               p++;
+               if (dir != 'D') {
+                   src = p;
+                   p = strchr(p, '\t');
+                   if (!p)
+                       goto disaster2;
+                   *p = '\0';
                    p++;
+                   dst = p;
+               } else {
+                   src = p;
+                   dst = "";
+               }
+               p = strchr(p, '\0');
+               if (!p)
+                   goto disaster2;
+               dlg_editbox_set(pfd->sourcebox, dlg, src);
+               dlg_editbox_set(pfd->destbox, dlg, dst);
                p++;
                while (*p) {
                    while (*p)
@@ -975,32 +1181,37 @@ void setup_config_box(struct controlbox *b, int midsession,
     sfree(str);
 
     if (!midsession) {
+       struct hostport *hp = (struct hostport *)
+           ctrl_alloc(b, sizeof(struct hostport));
+
        s = ctrl_getset(b, "Session", "hostport",
-                       "Specify your connection by host name or IP address");
+                       "Specify the destination you want to connect to");
        ctrl_columns(s, 2, 75, 25);
-       c = ctrl_editbox(s, "Host Name (or IP address)", 'n', 100,
+       c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
                         HELPCTX(session_hostname),
-                        dlg_stdeditbox_handler, I(offsetof(Config,host)),
-                        I(sizeof(((Config *)0)->host)));
+                        config_host_handler, I(0), I(0));
        c->generic.column = 0;
-       c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(session_hostname),
-                        dlg_stdeditbox_handler,
-                        I(offsetof(Config,port)), I(-1));
+       hp->host = c;
+       c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
+                        HELPCTX(session_hostname),
+                        config_port_handler, I(0), I(0));
        c->generic.column = 1;
+       hp->port = c;
        ctrl_columns(s, 1, 100);
-       if (backends[3].name == NULL) {
-           ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 3,
+
+       if (!backend_from_proto(PROT_SSH)) {
+           ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
                              HELPCTX(session_hostname),
-                             protocolbuttons_handler, P(c),
-                             "Raw", 'r', I(PROT_RAW),
+                             config_protocolbuttons_handler, P(hp),
+                             "Raw", 'w', I(PROT_RAW),
                              "Telnet", 't', I(PROT_TELNET),
                              "Rlogin", 'i', I(PROT_RLOGIN),
                              NULL);
        } else {
-           ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 4,
+           ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
                              HELPCTX(session_hostname),
-                             protocolbuttons_handler, P(c),
-                             "Raw", 'r', I(PROT_RAW),
+                             config_protocolbuttons_handler, P(hp),
+                             "Raw", 'w', I(PROT_RAW),
                              "Telnet", 't', I(PROT_TELNET),
                              "Rlogin", 'i', I(PROT_RLOGIN),
                              "SSH", 's', I(PROT_SSH),
@@ -1058,7 +1269,7 @@ void setup_config_box(struct controlbox *b, int midsession,
     ctrl_columns(s, 1, 100);
 
     s = ctrl_getset(b, "Session", "otheropts", NULL);
-    c = ctrl_radiobuttons(s, "Close window on exit:", 'w', 4,
+    c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
                          HELPCTX(session_coe),
                          dlg_stdradiobutton_handler,
                          I(offsetof(Config, close_on_exit)),
@@ -1077,20 +1288,24 @@ void setup_config_box(struct controlbox *b, int midsession,
      * logging can sensibly be available.
      */
     {
-       char *sshlogname;
+       char *sshlogname, *sshrawlogname;
        if ((midsession && protocol == PROT_SSH) ||
-           (!midsession && backends[3].name != NULL))
-           sshlogname = "Log SSH packet data";
-       else
-           sshlogname = NULL;         /* this will disable the button */
-       ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 1,
+           (!midsession && backend_from_proto(PROT_SSH))) {
+           sshlogname = "SSH packets";
+           sshrawlogname = "SSH packets and raw data";
+        } else {
+           sshlogname = NULL;         /* this will disable both buttons */
+           sshrawlogname = NULL;      /* this will just placate optimisers */
+        }
+       ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
                          HELPCTX(logging_main),
                          loggingbuttons_handler,
                          I(offsetof(Config, logtype)),
-                         "Logging turned off completely", 't', I(LGTYP_NONE),
-                         "Log printable output only", 'p', I(LGTYP_ASCII),
-                         "Log all session output", 'l', I(LGTYP_DEBUG),
+                         "None", 't', I(LGTYP_NONE),
+                         "Printable output", 'p', I(LGTYP_ASCII),
+                         "All session output", 'l', I(LGTYP_DEBUG),
                          sshlogname, 's', I(LGTYP_PACKETS),
+                         sshrawlogname, 'r', I(LGTYP_SSHRAW),
                          NULL);
     }
     ctrl_filesel(s, "Log file name:", 'f',
@@ -1111,7 +1326,7 @@ void setup_config_box(struct controlbox *b, int midsession,
                 dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
 
     if ((midsession && protocol == PROT_SSH) ||
-       (!midsession && backends[3].name != NULL)) {
+       (!midsession && backend_from_proto(PROT_SSH))) {
        s = ctrl_getset(b, "Session/Logging", "ssh",
                        "Options specific to SSH packet logging");
        ctrl_checkbox(s, "Omit known password fields", 'k',
@@ -1137,6 +1352,9 @@ void setup_config_box(struct controlbox *b, int midsession,
     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
                  HELPCTX(terminal_lfhascr),
                  dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
+    ctrl_checkbox(s, "Implicit LF in every CR", 'f',
+                 HELPCTX(terminal_crhaslf),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf)));
     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
                  HELPCTX(terminal_bce),
                  dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
@@ -1265,9 +1483,13 @@ void setup_config_box(struct controlbox *b, int midsession,
                  HELPCTX(features_retitle),
                  dlg_stdcheckbox_handler,
                  I(offsetof(Config,no_remote_wintitle)));
-    ctrl_checkbox(s, "Disable remote window title querying (SECURITY)",
-                 'q', HELPCTX(features_qtitle), dlg_stdcheckbox_handler,
-                 I(offsetof(Config,no_remote_qtitle)));
+    ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
+                     HELPCTX(features_qtitle),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config,remote_qtitle_action)),
+                     "None", I(TITLE_NONE),
+                     "Empty string", I(TITLE_EMPTY),
+                     "Window title", I(TITLE_REAL), NULL);
     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
                  HELPCTX(features_dbackspace),
                  dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
@@ -1290,13 +1512,13 @@ void setup_config_box(struct controlbox *b, int midsession,
 
     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
     ctrl_columns(s, 2, 50, 50);
-    c = ctrl_editbox(s, "Rows", 'r', 100,
-                    HELPCTX(window_size),
-                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
-    c->generic.column = 0;
     c = ctrl_editbox(s, "Columns", 'm', 100,
                     HELPCTX(window_size),
                     dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
+    c->generic.column = 0;
+    c = ctrl_editbox(s, "Rows", 'r', 100,
+                    HELPCTX(window_size),
+                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
     c->generic.column = 1;
     ctrl_columns(s, 1, 100);
 
@@ -1388,8 +1610,8 @@ void setup_config_box(struct controlbox *b, int midsession,
                  "Options controlling character set translation");
 
     s = ctrl_getset(b, "Window/Translation", "trans",
-                   "Character set translation on received data");
-    ctrl_combobox(s, "Received data assumed to be in which character set:",
+                   "Character set translation");
+    ctrl_combobox(s, "Remote character set:",
                  'r', 100, HELPCTX(translation_codepage),
                  codepage_handler, P(NULL), P(NULL));
 
@@ -1539,6 +1761,18 @@ void setup_config_box(struct controlbox *b, int midsession,
                          "IPv6", '6', I(ADDRTYPE_IPV6),
                          NULL);
 #endif
+
+           {
+               char *label = backend_from_proto(PROT_SSH) ?
+                   "Logical name of remote host (e.g. for SSH key lookup):" :
+                   "Logical name of remote host:";
+               s = ctrl_getset(b, "Connection", "identity",
+                               "Logical name of remote host");
+               ctrl_editbox(s, label, 'm', 100,
+                            HELPCTX(connection_loghost),
+                            dlg_stdeditbox_handler, I(offsetof(Config,loghost)),
+                            I(sizeof(((Config *)0)->loghost)));
+           }
        }
 
        /*
@@ -1554,6 +1788,22 @@ void setup_config_box(struct controlbox *b, int midsession,
                         HELPCTX(connection_username),
                         dlg_stdeditbox_handler, I(offsetof(Config,username)),
                         I(sizeof(((Config *)0)->username)));
+           {
+               /* We assume the local username is sufficiently stable
+                * to include on the dialog box. */
+               char *user = get_username();
+               char *userlabel = dupprintf("Use system username (%s)",
+                                           user ? user : "");
+               sfree(user);
+               ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
+                                 HELPCTX(connection_username_from_env),
+                                 dlg_stdradiobutton_handler,
+                                 I(offsetof(Config, username_from_env)),
+                                 "Prompt", I(FALSE),
+                                 userlabel, I(TRUE),
+                                 NULL);
+               sfree(userlabel);
+           }
 
            s = ctrl_getset(b, "Connection/Data", "term",
                            "Terminal details");
@@ -1726,7 +1976,7 @@ void setup_config_box(struct controlbox *b, int midsession,
      * when we're not doing SSH.
      */
 
-    if (backends[3].name != NULL && (!midsession || protocol == PROT_SSH)) {
+    if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
 
        /*
         * The Connection/SSH panel.
@@ -1841,6 +2091,10 @@ void setup_config_box(struct controlbox *b, int midsession,
 
            s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
                            "Authentication methods");
+           ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
+                         HELPCTX(ssh_auth_pageant),
+                         dlg_stdcheckbox_handler,
+                         I(offsetof(Config,tryagent)));
            ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
                          HELPCTX(ssh_auth_tis),
                          dlg_stdcheckbox_handler,
@@ -1850,19 +2104,44 @@ void setup_config_box(struct controlbox *b, int midsession,
                          dlg_stdcheckbox_handler,
                          I(offsetof(Config,try_ki_auth)));
 
+#ifndef NO_GSSAPI
+           ctrl_checkbox(s, "Attempt GSSAPI auth (SSH-2)",
+                         NO_SHORTCUT, HELPCTX(no_help),
+                         dlg_stdcheckbox_handler,
+                         I(offsetof(Config,try_gssapi_auth)));
+#endif
+
            s = ctrl_getset(b, "Connection/SSH/Auth", "params",
                            "Authentication parameters");
            ctrl_checkbox(s, "Allow agent forwarding", 'f',
                          HELPCTX(ssh_auth_agentfwd),
                          dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
-           ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", 'u',
+           ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
                          HELPCTX(ssh_auth_changeuser),
                          dlg_stdcheckbox_handler,
                          I(offsetof(Config,change_username)));
+#ifndef NO_GSSAPI
+           ctrl_checkbox(s, "Allow GSSAPI credential delegation in SSH-2", NO_SHORTCUT,
+                         HELPCTX(no_help),
+                         dlg_stdcheckbox_handler,
+                         I(offsetof(Config,gssapifwd)));
+#endif
            ctrl_filesel(s, "Private key file for authentication:", 'k',
                         FILTER_KEY_FILES, FALSE, "Select private key file",
                         HELPCTX(ssh_auth_privkey),
                         dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
+
+#ifndef NO_GSSAPI
+           /*
+            * GSSAPI library selection.
+            */
+           if (ngsslibs > 1) {
+               c = ctrl_draglist(s, "Preference order for GSSAPI libraries:", NO_SHORTCUT,
+                                 HELPCTX(no_help),
+                                 gsslist_handler, P(NULL));
+               c->listbox.height = ngsslibs;
+           }
+#endif
        }
 
        if (!midsession) {
@@ -2044,6 +2323,9 @@ void setup_config_box(struct controlbox *b, int midsession,
            ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
                          HELPCTX(ssh_bugs_rsa1),
                          sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
+           ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
+                         HELPCTX(ssh_bugs_ignore2),
+                         sshbug_handler, I(offsetof(Config,sshbug_ignore2)));
            ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
                          HELPCTX(ssh_bugs_hmac2),
                          sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
@@ -2059,6 +2341,9 @@ void setup_config_box(struct controlbox *b, int midsession,
            ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
                          HELPCTX(ssh_bugs_rekey2),
                          sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
+           ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
+                         HELPCTX(ssh_bugs_maxpkt2),
+                         sshbug_handler, I(offsetof(Config,sshbug_maxpkt2)));
        }
     }
 }