Add a configuration option for TCP keepalives (SO_KEEPALIVE), default off.
[u/mdw/putty] / config.c
index f41e9ec..d3d76f5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -110,7 +110,7 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
                    break;
                }
            }
-           dlg_listbox_addwithindex(ctrl, dlg, cstr, c);
+           dlg_listbox_addwithid(ctrl, dlg, cstr, c);
        }
        dlg_update_done(ctrl, dlg);
 
@@ -133,12 +133,18 @@ static void printerbox_handler(union control *ctrl, void *dlg,
        printer_enum *pe;
 
        dlg_update_start(ctrl, dlg);
-       dlg_listbox_clear(ctrl, dlg);
-       dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
-       pe = printer_start_enum(&nprinters);
-       for (i = 0; i < nprinters; i++)
-           dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
-       printer_finish_enum(pe);
+       /*
+        * Some backends may wish to disable the drop-down list on
+        * this edit box. Be prepared for this.
+        */
+       if (ctrl->editbox.has_list) {
+           dlg_listbox_clear(ctrl, dlg);
+           dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
+           pe = printer_start_enum(&nprinters);
+           for (i = 0; i < nprinters; i++)
+               dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
+           printer_finish_enum(pe);
+       }
        dlg_editbox_set(ctrl, dlg,
                        (*cfg->printer ? cfg->printer :
                         PRINTER_DISABLED_STRING));
@@ -156,7 +162,7 @@ static void codepage_handler(union control *ctrl, void *dlg,
     Config *cfg = (Config *)data;
     if (event == EVENT_REFRESH) {
        int i;
-       char *cp;
+       const char *cp;
        dlg_update_start(ctrl, dlg);
        strcpy(cfg->line_codepage,
               cp_name(decode_codepage(cfg->line_codepage)));
@@ -179,9 +185,9 @@ static void sshbug_handler(union control *ctrl, void *dlg,
     if (event == EVENT_REFRESH) {
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
-       dlg_listbox_addwithindex(ctrl, dlg, "Auto", AUTO);
-       dlg_listbox_addwithindex(ctrl, dlg, "Off", FORCE_OFF);
-       dlg_listbox_addwithindex(ctrl, dlg, "On", FORCE_ON);
+       dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
+       dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
+       dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
        switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) {
          case AUTO:      dlg_listbox_select(ctrl, dlg, 0); break;
          case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
@@ -198,10 +204,11 @@ static void sshbug_handler(union control *ctrl, void *dlg,
     }
 }
 
+#define SAVEDSESSION_LEN 2048
+
 struct sessionsaver_data {
     union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
     union control *okbutton, *cancelbutton;
-    char savedsession[2048];
     struct sesslist *sesslist;
 };
 
@@ -211,6 +218,7 @@ struct sessionsaver_data {
  * failure.
  */
 static int load_selected_session(struct sessionsaver_data *ssd,
+                                char *savedsession,
                                 void *dlg, Config *cfg)
 {
     int i = dlg_listbox_index(ssd->listbox, dlg);
@@ -222,11 +230,11 @@ static int load_selected_session(struct sessionsaver_data *ssd,
     isdef = !strcmp(ssd->sesslist->sessions[i], "Default Settings");
     load_settings(ssd->sesslist->sessions[i], !isdef, cfg);
     if (!isdef) {
-       strncpy(ssd->savedsession, ssd->sesslist->sessions[i],
-               sizeof(ssd->savedsession));
-       ssd->savedsession[sizeof(ssd->savedsession)-1] = '\0';
+       strncpy(savedsession, ssd->sesslist->sessions[i],
+               SAVEDSESSION_LEN);
+       savedsession[SAVEDSESSION_LEN-1] = '\0';
     } else {
-       ssd->savedsession[0] = '\0';
+       savedsession[0] = '\0';
     }
     dlg_refresh(NULL, dlg);
     /* Restore the selection, which might have been clobbered by
@@ -241,10 +249,29 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
     Config *cfg = (Config *)data;
     struct sessionsaver_data *ssd =
        (struct sessionsaver_data *)ctrl->generic.context.p;
+    char *savedsession;
+
+    /*
+     * The first time we're called in a new dialog, we must
+     * allocate space to store the current contents of the saved
+     * session edit box (since it must persist even when we switch
+     * panels, but is not part of the Config).
+     * 
+     * Of course, this doesn't need to be done mid-session.
+     */
+    if (!ssd->editbox) {
+        savedsession = NULL;
+    } else if (!dlg_get_privdata(ssd->editbox, dlg)) {
+       savedsession = (char *)
+           dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);
+       savedsession[0] = '\0';
+    } else {
+       savedsession = dlg_get_privdata(ssd->editbox, dlg);
+    }
 
     if (event == EVENT_REFRESH) {
        if (ctrl == ssd->editbox) {
-           dlg_editbox_set(ctrl, dlg, ssd->savedsession);
+           dlg_editbox_set(ctrl, dlg, savedsession);
        } else if (ctrl == ssd->listbox) {
            int i;
            dlg_update_start(ctrl, dlg);
@@ -255,8 +282,8 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
        }
     } else if (event == EVENT_VALCHANGE) {
        if (ctrl == ssd->editbox) {
-           dlg_editbox_get(ctrl, dlg, ssd->savedsession,
-                           sizeof(ssd->savedsession));
+           dlg_editbox_get(ctrl, dlg, savedsession,
+                           SAVEDSESSION_LEN);
        }
     } else if (event == EVENT_ACTION) {
        if (ctrl == ssd->listbox || ctrl == ssd->loadbutton) {
@@ -267,13 +294,13 @@ 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, dlg, cfg) &&
+           if (load_selected_session(ssd, savedsession, dlg, cfg) &&
                (ctrl == ssd->listbox && cfg->host[0])) {
                dlg_end(dlg, 1);       /* it's all over, and succeeded */
            }
        } else if (ctrl == ssd->savebutton) {
-           int isdef = !strcmp(ssd->savedsession, "Default Settings");
-           if (!ssd->savedsession[0]) {
+           int isdef = !strcmp(savedsession, "Default Settings");
+           if (!savedsession[0]) {
                int i = dlg_listbox_index(ssd->listbox, dlg);
                if (i < 0) {
                    dlg_beep(dlg);
@@ -281,14 +308,20 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
                }
                isdef = !strcmp(ssd->sesslist->sessions[i], "Default Settings");
                if (!isdef) {
-                   strncpy(ssd->savedsession, ssd->sesslist->sessions[i],
-                           sizeof(ssd->savedsession));
-                   ssd->savedsession[sizeof(ssd->savedsession)-1] = '\0';
+                   strncpy(savedsession, ssd->sesslist->sessions[i],
+                           SAVEDSESSION_LEN);
+                   savedsession[SAVEDSESSION_LEN-1] = '\0';
                } else {
-                   ssd->savedsession[0] = '\0';
+                   savedsession[0] = '\0';
                }
            }
-           save_settings(ssd->savedsession, !isdef, cfg);
+            {
+                char *errmsg = save_settings(savedsession, !isdef, cfg);
+                if (errmsg) {
+                    dlg_error_msg(dlg, errmsg);
+                    sfree(errmsg);
+                }
+            }
            get_sesslist(ssd->sesslist, FALSE);
            get_sesslist(ssd->sesslist, TRUE);
            dlg_refresh(ssd->editbox, dlg);
@@ -304,6 +337,11 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
                dlg_refresh(ssd->listbox, dlg);
            }
        } else if (ctrl == ssd->okbutton) {
+            if (!savedsession) {
+                /* In a mid-session Change Settings, Apply is always OK. */
+               dlg_end(dlg, 1);
+                return;
+            }
            /*
             * Annoying special case. If the `Open' button is
             * pressed while no host name is currently set, _and_
@@ -311,15 +349,16 @@ 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(dlg) == ssd->listbox && !*cfg->host) {
+           if (dlg_last_focused(ctrl, dlg) == ssd->listbox && !*cfg->host) {
                Config cfg2;
-               if (!load_selected_session(ssd, dlg, &cfg2)) {
+               if (!load_selected_session(ssd, savedsession, dlg, &cfg2)) {
                    dlg_beep(dlg);
                    return;
                }
                /* If at this point we have a valid session, go! */
                if (*cfg2.host) {
                    *cfg = cfg2;       /* structure copy */
+                   cfg->remote_cmd_ptr = cfg->remote_cmd; /* nasty */
                    dlg_end(dlg, 1);
                } else
                    dlg_beep(dlg);
@@ -379,7 +418,7 @@ static void charclass_handler(union control *ctrl, void *dlg,
 }
 
 struct colour_data {
-    union control *listbox, *rgbtext, *button;
+    union control *listbox, *redit, *gedit, *bedit, *button;
 };
 
 static const char *const colours[] = {
@@ -412,7 +451,9 @@ 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_text_set(cd->rgbtext, dlg, "");
+           dlg_editbox_set(cd->redit, dlg, "");
+           dlg_editbox_set(cd->gedit, dlg, "");
+           dlg_editbox_set(cd->bedit, dlg, "");
        }
     } else if (event == EVENT_SELCHANGE) {
        if (ctrl == cd->listbox) {
@@ -427,6 +468,25 @@ static void colour_handler(union control *ctrl, void *dlg,
            b = cfg->colours[i][2];
            update = TRUE;
        }
+    } else if (event == EVENT_VALCHANGE) {
+       if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
+           /* The user has changed the colour using the edit boxes. */
+           char buf[80];
+           int i, cval;
+
+           dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
+           cval = atoi(buf) & 255;
+
+           i = dlg_listbox_index(cd->listbox, dlg);
+           if (i >= 0) {
+               if (ctrl == cd->redit)
+                   cfg->colours[i][0] = cval;
+               else if (ctrl == cd->gedit)
+                   cfg->colours[i][1] = cval;
+               else if (ctrl == cd->bedit)
+                   cfg->colours[i][2] = cval;
+           }
+       }
     } else if (event == EVENT_ACTION) {
        if (ctrl == cd->button) {
            int i = dlg_listbox_index(cd->listbox, dlg);
@@ -463,8 +523,9 @@ static void colour_handler(union control *ctrl, void *dlg,
 
     if (update) {
        char buf[40];
-       sprintf(buf, "%02x/%02x/%02x", r, g, b);
-       dlg_text_set(cd->rgbtext, dlg, buf);
+       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);
     }
 }
 
@@ -579,29 +640,41 @@ static void portfwd_handler(union control *ctrl, void *dlg,
                p += strlen(p) + 1;
            }
            dlg_update_done(ctrl, dlg);
+       } else if (ctrl == pfd->direction) {
+           /*
+            * Default is Local.
+            */
+           dlg_radiobutton_set(ctrl, dlg, 0);
        }
     } else if (event == EVENT_ACTION) {
        if (ctrl == pfd->addbutton) {
            char str[sizeof(cfg->portfwd)];
            char *p;
-           if (dlg_radiobutton_get(pfd->direction, dlg) == 0)
+           int whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
+           if (whichbutton == 0)
                str[0] = 'L';
-           else
+           else if (whichbutton == 1)
                str[0] = 'R';
+           else
+               str[0] = 'D';
            dlg_editbox_get(pfd->sourcebox, dlg, str+1, sizeof(str) - 2);
            if (!str[1]) {
                dlg_error_msg(dlg, "You need to specify a source port number");
                return;
            }
            p = str + strlen(str);
-           *p++ = '\t';
-           dlg_editbox_get(pfd->destbox, dlg, p, sizeof(str)-1 - (p - str));
-           if (!*p || !strchr(p, ':')) {
-               dlg_error_msg(dlg,
-                             "You need to specify a destination address\n"
-                             "in the form \"host.name:port\"");
-               return;
-           }
+           if (str[0] != 'D') {
+               *p++ = '\t';
+               dlg_editbox_get(pfd->destbox, dlg, p,
+                               sizeof(str)-1 - (p - str));
+               if (!*p || !strchr(p, ':')) {
+                   dlg_error_msg(dlg,
+                                 "You need to specify a destination address\n"
+                                 "in the form \"host.name:port\"");
+                   return;
+               }
+           } else
+               *p = '\0';
            p = cfg->portfwd;
            while (*p) {
                while (*p)
@@ -663,9 +736,11 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     struct environ_data *ed;
     struct portfwd_data *pfd;
     union control *c;
+    char *str;
 
     ssd = (struct sessionsaver_data *)
        ctrl_alloc(b, sizeof(struct sessionsaver_data));
+    memset(ssd, 0, sizeof(*ssd));
     ssd->sesslist = (midsession ? NULL : sesslist);
 
     /*
@@ -683,6 +758,7 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     ssd->okbutton->generic.column = 3;
     ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
                                        sessionsaver_handler, P(ssd));
+    ssd->cancelbutton->button.iscancel = TRUE;
     ssd->cancelbutton->generic.column = 4;
     /* We carefully don't close the 5-column part, so that platform-
      * specific add-ons can put extra buttons alongside Open and Cancel. */
@@ -690,7 +766,9 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     /*
      * The Session panel.
      */
-    ctrl_settitle(b, "Session", "Basic options for your PuTTY session");
+    str = dupprintf("Basic options for your %s session", appname);
+    ctrl_settitle(b, "Session", str);
+    sfree(str);
 
     if (!midsession) {
        s = ctrl_getset(b, "Session", "hostport",
@@ -706,7 +784,7 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                         I(offsetof(Config,port)), I(-1));
        c->generic.column = 1;
        ctrl_columns(s, 1, 100);
-       if (backends[3].backend == NULL) {
+       if (backends[3].name == NULL) {
            ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 3,
                              HELPCTX(session_hostname),
                              protocolbuttons_handler, P(c),
@@ -728,7 +806,6 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
        s = ctrl_getset(b, "Session", "savedsessions",
                        "Load, save or delete a stored session");
        ctrl_columns(s, 2, 75, 25);
-       ssd->savedsession[0] = '\0';
        ssd->sesslist = sesslist;
        ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
                                    HELPCTX(session_saved),
@@ -738,6 +815,11 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
         * than alongside that edit box. */
        ctrl_columns(s, 1, 100);
        ctrl_columns(s, 2, 75, 25);
+       ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
+                                   HELPCTX(session_saved),
+                                   sessionsaver_handler, P(ssd));
+       ssd->listbox->generic.column = 0;
+       ssd->listbox->listbox.height = 7;
        ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
                                          HELPCTX(session_saved),
                                          sessionsaver_handler, P(ssd));
@@ -750,11 +832,6 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                                         HELPCTX(session_saved),
                                         sessionsaver_handler, P(ssd));
        ssd->delbutton->generic.column = 1;
-       ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
-                                   HELPCTX(session_saved),
-                                   sessionsaver_handler, P(ssd));
-       ssd->listbox->generic.column = 0;
-       ssd->listbox->listbox.height = 7;
        ctrl_columns(s, 1, 100);
     }
 
@@ -773,14 +850,27 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
 
     s = ctrl_getset(b, "Session/Logging", "main", NULL);
-    ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 1,
-                     HELPCTX(logging_main),
-                     dlg_stdradiobutton_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),
-                     "Log SSH packet data", 's', I(LGTYP_PACKETS),
-                     NULL);
+    /*
+     * The logging buttons change depending on whether SSH packet
+     * logging can sensibly be available.
+     */
+    {
+       char *sshlogname;
+       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,
+                         HELPCTX(logging_main),
+                         dlg_stdradiobutton_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),
+                         sshlogname, 's', I(LGTYP_PACKETS),
+                         NULL);
+    }
     ctrl_filesel(s, "Log file name:", 'f',
                 NULL, TRUE, "Select session log file name",
                 HELPCTX(logging_filename),
@@ -903,13 +993,13 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     ctrl_editbox(s, "... in this many seconds", 't', 20,
                 HELPCTX(bell_overload),
                 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
-                I(-1000));
+                I(-TICKSPERSEC));
     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
              HELPCTX(bell_overload));
     ctrl_editbox(s, "Seconds of silence required", 's', 20,
                 HELPCTX(bell_overload),
                 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
-                I(-1000));
+                I(-TICKSPERSEC));
 
     /*
      * The Terminal/Features panel.
@@ -938,17 +1028,28 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                  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_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
                  HELPCTX(features_dbackspace),
                  dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
                  'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
                  I(offsetof(Config,no_remote_charset)));
+    ctrl_checkbox(s, "Disable Arabic text shaping",
+                 'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
+                 I(offsetof(Config, arabicshaping)));
+    ctrl_checkbox(s, "Disable bidirectional text display",
+                 'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
+                 I(offsetof(Config, bidi)));
 
     /*
      * The Window panel.
      */
-    ctrl_settitle(b, "Window", "Options controlling PuTTY's window");
+    str = dupprintf("Options controlling %s's window", appname);
+    ctrl_settitle(b, "Window", str);
+    sfree(str);
 
     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
     ctrl_columns(s, 2, 50, 50);
@@ -970,10 +1071,6 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     ctrl_checkbox(s, "Display scrollbar", 'd',
                  HELPCTX(window_scrollback),
                  dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
-    ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i',
-                 HELPCTX(window_scrollback),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,scrollbar_in_fullscreen)));
     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
                  HELPCTX(window_scrollback),
                  dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
@@ -988,8 +1085,9 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     /*
      * The Window/Appearance panel.
      */
-    ctrl_settitle(b, "Window/Appearance",
-                 "Configure the appearance of PuTTY's window");
+    str = dupprintf("Configure the appearance of %s's window", appname);
+    ctrl_settitle(b, "Window/Appearance", str);
+    sfree(str);
 
     s = ctrl_getset(b, "Window/Appearance", "cursor",
                    "Adjust the use of the cursor");
@@ -1026,8 +1124,9 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     /*
      * The Window/Behaviour panel.
      */
-    ctrl_settitle(b, "Window/Behaviour",
-                 "Configure the behaviour of PuTTY's window");
+    str = dupprintf("Configure the behaviour of %s's window", appname);
+    ctrl_settitle(b, "Window/Behaviour", str);
+    sfree(str);
 
     s = ctrl_getset(b, "Window/Behaviour", "title",
                    "Adjust the behaviour of the window title");
@@ -1057,26 +1156,24 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                  'r', 100, HELPCTX(translation_codepage),
                  codepage_handler, P(NULL), P(NULL));
 
-    s = ctrl_getset(b, "Window/Translation", "linedraw",
-                   "Adjust how PuTTY displays line drawing characters");
+    str = dupprintf("Adjust how %s handles line drawing characters", appname);
+    s = ctrl_getset(b, "Window/Translation", "linedraw", str);
+    sfree(str);
     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
                      HELPCTX(translation_linedraw),
                      dlg_stdradiobutton_handler,
                      I(offsetof(Config, vtmode)),
-                     "Font has XWindows encoding", 'x', I(VT_XWINDOWS),
+                     "Use Unicode line drawing code points",'u',I(VT_UNICODE),
                      "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
-                     "Unicode mode", 'u', I(VT_UNICODE), NULL);
+                     NULL);
+    ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
+                 HELPCTX(selection_linedraw),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
 
     /*
      * The Window/Selection panel.
      */
     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
-
-    s = ctrl_getset(b, "Window/Selection", "trans",
-                   "Translation of pasted characters");
-    ctrl_checkbox(s, "Don't translate line drawing chars into +, - and |",'d',
-                 HELPCTX(selection_linedraw),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
        
     s = ctrl_getset(b, "Window/Selection", "mouse",
                    "Control use of mouse");
@@ -1101,7 +1198,7 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                                charclass_handler, P(ccd));
     ccd->listbox->listbox.multisel = 1;
     ccd->listbox->listbox.ncols = 4;
-    ccd->listbox->listbox.percentages = smalloc(4*sizeof(int));
+    ccd->listbox->listbox.percentages = snewn(4, int);
     ccd->listbox->listbox.percentages[0] = 15;
     ccd->listbox->listbox.percentages[1] = 25;
     ccd->listbox->listbox.percentages[2] = 20;
@@ -1128,8 +1225,9 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                  HELPCTX(colours_bold),
                  dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
 
-    s = ctrl_getset(b, "Window/Colours", "adjust",
-                   "Adjust the precise colours PuTTY displays");
+    str = dupprintf("Adjust the precise colours %s displays", appname);
+    s = ctrl_getset(b, "Window/Colours", "adjust", str);
+    sfree(str);
     ctrl_text(s, "Select a colour from the list, and then click the"
              " Modify button to change its appearance.",
              HELPCTX(colours_config));
@@ -1138,46 +1236,68 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
                               HELPCTX(colours_config), colour_handler, P(cd));
     cd->listbox->generic.column = 0;
+    cd->listbox->listbox.height = 7;
     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
     c->generic.column = 1;
-    cd->rgbtext = ctrl_text(s, "00/00/00", HELPCTX(colours_config));
-    cd->rgbtext->generic.column = 1;
+    cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
+                            colour_handler, P(cd), P(NULL));
+    cd->redit->generic.column = 1;
+    cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
+                            colour_handler, P(cd), P(NULL));
+    cd->gedit->generic.column = 1;
+    cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
+                            colour_handler, P(cd), P(NULL));
+    cd->bedit->generic.column = 1;
     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
                                 colour_handler, P(cd));
     cd->button->generic.column = 1;
     ctrl_columns(s, 1, 100);
 
     /*
-     * The Connection panel.
+     * The Connection panel. This doesn't show up if we're in a
+     * non-network utility such as pterm. We tell this by being
+     * passed a protocol < 0.
      */
-    ctrl_settitle(b, "Connection", "Options controlling the connection");
+    if (protocol >= 0) {
+       ctrl_settitle(b, "Connection", "Options controlling the connection");
 
-    if (!midsession) {
-       s = ctrl_getset(b, "Connection", "data", "Data to send to the server");
-       ctrl_editbox(s, "Terminal-type string", 't', 50,
-                    HELPCTX(connection_termtype),
-                    dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
-                    I(sizeof(((Config *)0)->termtype)));
-       ctrl_editbox(s, "Auto-login username", 'u', 50,
-                    HELPCTX(connection_username),
-                    dlg_stdeditbox_handler, I(offsetof(Config,username)),
-                    I(sizeof(((Config *)0)->username)));
-    }
+       if (!midsession) {
+           s = ctrl_getset(b, "Connection", "data",
+                           "Data to send to the server");
+           ctrl_editbox(s, "Terminal-type string", 't', 50,
+                        HELPCTX(connection_termtype),
+                        dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
+                        I(sizeof(((Config *)0)->termtype)));
+           ctrl_editbox(s, "Terminal speeds", 's', 50,
+                        HELPCTX(connection_termspeed),
+                        dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
+                        I(sizeof(((Config *)0)->termspeed)));
+           ctrl_editbox(s, "Auto-login username", 'u', 50,
+                        HELPCTX(connection_username),
+                        dlg_stdeditbox_handler, I(offsetof(Config,username)),
+                        I(sizeof(((Config *)0)->username)));
+       }
 
-    s = ctrl_getset(b, "Connection", "keepalive",
-                   "Sending of null packets to keep session active");
-    ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
-                HELPCTX(connection_keepalive),
-                dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
-                I(-1));
+       s = ctrl_getset(b, "Connection", "keepalive",
+                       "Sending of null packets to keep session active");
+       ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
+                    HELPCTX(connection_keepalive),
+                    dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
+                    I(-1));
+
+       if (!midsession) {
+           s = ctrl_getset(b, "Connection", "tcp",
+                           "Low-level TCP connection options");
+           ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
+                         'n', HELPCTX(connection_nodelay),
+                         dlg_stdcheckbox_handler,
+                         I(offsetof(Config,tcp_nodelay)));
+           ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
+                         'p', HELPCTX(connection_tcpkeepalive),
+                         dlg_stdcheckbox_handler,
+                         I(offsetof(Config,tcp_keepalives)));
+       }
 
-    if (!midsession) {
-       s = ctrl_getset(b, "Connection", "tcp",
-                       "Low-level TCP connection options");
-       ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n',
-                     HELPCTX(connection_nodelay),
-                     dlg_stdcheckbox_handler,
-                     I(offsetof(Config,tcp_nodelay)));
     }
 
     if (!midsession) {
@@ -1187,15 +1307,16 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
        ctrl_settitle(b, "Connection/Proxy",
                      "Options controlling proxy usage");
 
-       s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics");
-       ctrl_radiobuttons(s, "Proxy type:", NO_SHORTCUT, 4,
+       s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
+       ctrl_radiobuttons(s, "Proxy type:", 't', 3,
                          HELPCTX(proxy_type),
                          dlg_stdradiobutton_handler,
                          I(offsetof(Config, proxy_type)),
-                         "None", 'n', I(PROXY_NONE),
-                         "HTTP", 't', I(PROXY_HTTP),
-                         "SOCKS", 's', I(PROXY_SOCKS),
-                         "Telnet", 'l', I(PROXY_TELNET),
+                         "None", I(PROXY_NONE),
+                         "SOCKS 4", I(PROXY_SOCKS4),
+                         "SOCKS 5", I(PROXY_SOCKS5),
+                         "HTTP", I(PROXY_HTTP),
+                         "Telnet", I(PROXY_TELNET),
                          NULL);
        ctrl_columns(s, 2, 80, 20);
        c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
@@ -1238,19 +1359,11 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                         I(offsetof(Config,proxy_password)),
                         I(sizeof(((Config *)0)->proxy_password)));
        c->editbox.password = 1;
-
-       s = ctrl_getset(b, "Connection/Proxy", "misc",
-                       "Miscellaneous proxy settings");
        ctrl_editbox(s, "Telnet command", 'm', 100,
                     HELPCTX(proxy_command),
                     dlg_stdeditbox_handler,
                     I(offsetof(Config,proxy_telnet_command)),
                     I(sizeof(((Config *)0)->proxy_telnet_command)));
-       ctrl_radiobuttons(s, "SOCKS Version", 'v', 2,
-                         HELPCTX(proxy_socksver),
-                         dlg_stdradiobutton_handler,
-                         I(offsetof(Config, proxy_socks_version)),
-                         "Version 5", I(5), "Version 4", I(4), NULL);
     }
 
     /*
@@ -1267,10 +1380,6 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
        if (!midsession) {
            s = ctrl_getset(b, "Connection/Telnet", "data",
                            "Data to send to the server");
-           ctrl_editbox(s, "Terminal-speed string", 's', 50,
-                        HELPCTX(telnet_termspeed),
-                        dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
-                        I(sizeof(((Config *)0)->termspeed)));
            ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
            ctrl_columns(s, 2, 80, 20);
            ed = (struct environ_data *)
@@ -1296,6 +1405,10 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                                       HELPCTX(telnet_environ),
                                       environ_handler, P(ed));
            ed->listbox->listbox.height = 3;
+           ed->listbox->listbox.ncols = 2;
+           ed->listbox->listbox.percentages = snewn(2, int);
+           ed->listbox->listbox.percentages[0] = 30;
+           ed->listbox->listbox.percentages[1] = 70;
        }
 
        s = ctrl_getset(b, "Connection/Telnet", "protocol",
@@ -1335,10 +1448,6 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
 
        s = ctrl_getset(b, "Connection/Rlogin", "data",
                        "Data to send to the server");
-       ctrl_editbox(s, "Terminal-speed string", 's', 50,
-                    HELPCTX(rlogin_termspeed),
-                    dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
-                    I(sizeof(((Config *)0)->termspeed)));
        ctrl_editbox(s, "Local username:", 'l', 50,
                     HELPCTX(rlogin_localuser),
                     dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
@@ -1350,7 +1459,7 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
      * All the SSH stuff is omitted in PuTTYtel.
      */
 
-    if (!midsession && backends[3].backend != NULL) {
+    if (!midsession && backends[3].name != NULL) {
 
        /*
         * The Connection/SSH panel.
@@ -1384,10 +1493,12 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                          "2 only", 'n', I(3), NULL);
 
        s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
-       ctrl_draglist(s, "Encryption cipher selection policy:", 's',
-                     HELPCTX(ssh_ciphers),
-                     cipherlist_handler, P(NULL));
-       ctrl_checkbox(s, "Enable non-standard use of single-DES in SSH 2", 'i',
+       c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
+                         HELPCTX(ssh_ciphers),
+                         cipherlist_handler, P(NULL));
+       c->listbox.height = 6;
+       
+       ctrl_checkbox(s, "Enable legacy use of single-DES in SSH 2", 'i',
                      HELPCTX(ssh_ciphers),
                      dlg_stdcheckbox_handler,
                      I(offsetof(Config,ssh2_des_cbc)));
@@ -1470,6 +1581,10 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                                    HELPCTX(ssh_tunnels_portfwd),
                                    portfwd_handler, P(pfd));
        pfd->listbox->listbox.height = 3;
+       pfd->listbox->listbox.ncols = 2;
+       pfd->listbox->listbox.percentages = snewn(2, int);
+       pfd->listbox->listbox.percentages[0] = 20;
+       pfd->listbox->listbox.percentages[1] = 80;
        ctrl_tabdelay(s, pfd->rembutton);
        ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
        /* You want to enter source, destination and type, _then_ hit Add.
@@ -1486,11 +1601,13 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
        pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
                                    HELPCTX(ssh_tunnels_portfwd),
                                    portfwd_handler, P(pfd), P(NULL));
-       pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
+       pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
                                           HELPCTX(ssh_tunnels_portfwd),
                                           portfwd_handler, P(pfd),
                                           "Local", 'l', P(NULL),
-                                          "Remote", 'm', P(NULL), NULL);
+                                          "Remote", 'm', P(NULL),
+                                          "Dynamic", 'y', P(NULL),
+                                          NULL);
        ctrl_tabdelay(s, pfd->addbutton);
        ctrl_columns(s, 1, 100);