1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-31 02:32:49 -05:00

Add ability to specify custom load and save separately.

This allows a couple more settings to be treated automatically on
save, which are more complicated on load because they still honour
older alternative save keywords.

In particular, CONF_proxy_type and CONF_remote_qtitle_action now have
explicit enum mappings. These were needed for the automated save code,
but also, I've rewritten the custom load code to use them too. This
decouples the storage format of those settings from the order of
values in the internal enum, which is generally an advantage of
specifying storage enums explicitly.

Those two settings weren't already tested by test_conf, because I
wasn't changing them in previous commits. Now I've added extra code
that does test them, and verified it works when backported to commit
b567c9b2b5e159f where I introduced test_conf before beginning the main
refactoring.

A setting can also be specified explicitly as not loaded and saved at
all. There were quite a few commented that way, but now there's a
machine-readable indication of it.

test_conf will now check that all these settings make sense together -
things shouldn't have a save keyword unless they use it, and should
have one if they don't, and shouldn't specify combinations of options
that conflict.

(For that reason, test_conf is now also running the consistency check
before the main test, so that a missing keyword will cause an error
message _before_ it causes a segfault, saving some debugging!)
This commit is contained in:
Simon Tatham 2023-09-22 16:04:25 +01:00
parent 374bb4872e
commit b29758c1b6
6 changed files with 272 additions and 158 deletions

View File

@ -81,6 +81,26 @@ CONF_ENUM(supdup_charset,
VALUE(SUPDUP_CHARSET_WAITS, 2), VALUE(SUPDUP_CHARSET_WAITS, 2),
) )
CONF_ENUM(proxy_type,
VALUE(PROXY_NONE, 0),
VALUE(PROXY_SOCKS4, 1),
VALUE(PROXY_SOCKS5, 2),
VALUE(PROXY_HTTP, 3),
VALUE(PROXY_TELNET, 4),
VALUE(PROXY_CMD, 5),
VALUE(PROXY_SSH_TCPIP, 6),
VALUE(PROXY_SSH_EXEC, 7),
VALUE(PROXY_SSH_SUBSYSTEM, 8),
)
CONF_ENUM(old_proxy_type,
VALUE(PROXY_NONE, 0),
VALUE(PROXY_HTTP, 1),
VALUE(PROXY_SOCKS5, 2), /* really, both SOCKS 4 and 5 */
VALUE(PROXY_TELNET, 3),
VALUE(PROXY_CMD, 4),
)
CONF_ENUM(funky_type, CONF_ENUM(funky_type,
VALUE(FUNKY_TILDE, 0), VALUE(FUNKY_TILDE, 0),
VALUE(FUNKY_LINUX, 1), VALUE(FUNKY_LINUX, 1),
@ -96,6 +116,12 @@ CONF_ENUM(sharrow_type,
VALUE(SHARROW_BITMAP, 1), VALUE(SHARROW_BITMAP, 1),
) )
CONF_ENUM(remote_qtitle_action,
VALUE(TITLE_NONE, 0),
VALUE(TITLE_EMPTY, 1),
VALUE(TITLE_REAL, 2),
)
CONF_ENUM(cursor_type, CONF_ENUM(cursor_type,
VALUE(CURSOR_BLOCK, 0), VALUE(CURSOR_BLOCK, 0),
VALUE(CURSOR_UNDERLINE, 1), VALUE(CURSOR_UNDERLINE, 1),

131
conf.h
View File

@ -18,14 +18,12 @@ CONF_OPTION(host,
) )
CONF_OPTION(port, CONF_OPTION(port,
VALUE_TYPE(INT), VALUE_TYPE(INT),
/* SAVE_KEYWORD("PortNumber"),
* Notionally SAVE_KEYWORD("PortNumber"), but saving/loading is LOAD_CUSTOM, /* default value depends on the value of CONF_protocol */
* handled by custom code because the default value depends on the
* value of CONF_protocol.
*/
) )
CONF_OPTION(protocol, CONF_OPTION(protocol,
VALUE_TYPE(INT), /* PROT_SSH, PROT_TELNET etc */ VALUE_TYPE(INT), /* PROT_SSH, PROT_TELNET etc */
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Notionally SAVE_KEYWORD("Protocol"), but saving/loading is handled by * Notionally SAVE_KEYWORD("Protocol"), but saving/loading is handled by
* custom code because the stored value is a string representation * custom code because the stored value is a string representation
@ -51,6 +49,7 @@ CONF_OPTION(warn_on_close,
) )
CONF_OPTION(ping_interval, CONF_OPTION(ping_interval,
VALUE_TYPE(INT), /* in seconds */ VALUE_TYPE(INT), /* in seconds */
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Saving/loading is handled by custom code because for historical * Saving/loading is handled by custom code because for historical
* reasons this value corresponds to two save keywords, * reasons this value corresponds to two save keywords,
@ -95,11 +94,14 @@ CONF_OPTION(even_proxy_localhost,
) )
CONF_OPTION(proxy_type, CONF_OPTION(proxy_type,
VALUE_TYPE(INT), /* PROXY_NONE, PROXY_SOCKS4, ... */ VALUE_TYPE(INT), /* PROXY_NONE, PROXY_SOCKS4, ... */
STORAGE_ENUM(proxy_type),
SAVE_KEYWORD("ProxyMethod"),
LOAD_CUSTOM,
/* /*
* SAVE_KEYWORD("ProxyMethod"), for the current configuration * Custom load code: there was an earlier keyword "ProxyType"
* format. But there was an earlier keyword "ProxyType" using a * using a different enumeration, in which SOCKS4 and SOCKS5
* different enumeration, in which SOCKS4 and SOCKS5 shared a * shared a value, and a second keyword "ProxySOCKSVersion"
* value, and a second keyword "ProxySOCKSVersion" disambiguated. * disambiguated.
*/ */
) )
CONF_OPTION(proxy_host, CONF_OPTION(proxy_host,
@ -148,6 +150,7 @@ CONF_OPTION(remote_cmd2,
* by user configuration, or loaded or saved. * by user configuration, or loaded or saved.
*/ */
VALUE_TYPE(STR), VALUE_TYPE(STR),
NOT_SAVED,
) )
CONF_OPTION(nopty, CONF_OPTION(nopty,
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
@ -163,19 +166,13 @@ CONF_OPTION(ssh_kexlist,
SUBKEY_TYPE(INT), /* indices in preference order: 0,...,KEX_MAX-1 SUBKEY_TYPE(INT), /* indices in preference order: 0,...,KEX_MAX-1
* (lower is more preferred) */ * (lower is more preferred) */
VALUE_TYPE(INT), /* KEX_* enum values */ VALUE_TYPE(INT), /* KEX_* enum values */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for preference lists */
* Loading and saving is done by custom code for all preference
* lists
*/
) )
CONF_OPTION(ssh_hklist, CONF_OPTION(ssh_hklist,
SUBKEY_TYPE(INT), /* indices in preference order: 0,...,HK_MAX-1 SUBKEY_TYPE(INT), /* indices in preference order: 0,...,HK_MAX-1
* (lower is more preferred) */ * (lower is more preferred) */
VALUE_TYPE(INT), /* HK_* enum values */ VALUE_TYPE(INT), /* HK_* enum values */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for preference lists */
* Loading and saving is done by custom code for all preference
* lists
*/
) )
CONF_OPTION(ssh_prefer_known_hostkeys, CONF_OPTION(ssh_prefer_known_hostkeys,
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
@ -211,10 +208,7 @@ CONF_OPTION(ssh_cipherlist,
SUBKEY_TYPE(INT), /* indices in preference order: 0,...,CIPHER_MAX-1 SUBKEY_TYPE(INT), /* indices in preference order: 0,...,CIPHER_MAX-1
* (lower is more preferred) */ * (lower is more preferred) */
VALUE_TYPE(INT), /* CIPHER_* enum values */ VALUE_TYPE(INT), /* CIPHER_* enum values */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for preference lists */
* Loading and saving is done by custom code for all preference
* lists
*/
) )
CONF_OPTION(keyfile, CONF_OPTION(keyfile,
VALUE_TYPE(FILENAME), VALUE_TYPE(FILENAME),
@ -261,6 +255,7 @@ CONF_OPTION(ssh_simple,
* user configuration, or loaded or saved. * user configuration, or loaded or saved.
*/ */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
NOT_SAVED,
) )
CONF_OPTION(ssh_connection_sharing, CONF_OPTION(ssh_connection_sharing,
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
@ -288,9 +283,7 @@ CONF_OPTION(ssh_manual_hostkeys,
*/ */
SUBKEY_TYPE(STR), SUBKEY_TYPE(STR),
VALUE_TYPE(STR), VALUE_TYPE(STR),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for mappings */
* Loading and saving is done by custom code for all mappings
*/
) )
CONF_OPTION(ssh2_des_cbc, /* "des-cbc" unrecommended SSH-2 cipher */ CONF_OPTION(ssh2_des_cbc, /* "des-cbc" unrecommended SSH-2 cipher */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
@ -324,49 +317,31 @@ CONF_OPTION(try_ki_auth,
) )
CONF_OPTION(try_gssapi_auth, /* attempt gssapi via ssh userauth */ CONF_OPTION(try_gssapi_auth, /* attempt gssapi via ssh userauth */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifndef NO_GSSAPI */
* Loading and saving is done by custom code because it's under
* #ifndef NO_GSSAPI
*/
) )
CONF_OPTION(try_gssapi_kex, /* attempt gssapi via ssh kex */ CONF_OPTION(try_gssapi_kex, /* attempt gssapi via ssh kex */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifndef NO_GSSAPI */
* Loading and saving is done by custom code because it's under
* #ifndef NO_GSSAPI
*/
) )
CONF_OPTION(gssapifwd, /* forward tgt via gss */ CONF_OPTION(gssapifwd, /* forward tgt via gss */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifndef NO_GSSAPI */
* Loading and saving is done by custom code because it's under
* #ifndef NO_GSSAPI
*/
) )
CONF_OPTION(gssapirekey, /* KEXGSS refresh interval (mins) */ CONF_OPTION(gssapirekey, /* KEXGSS refresh interval (mins) */
VALUE_TYPE(INT), VALUE_TYPE(INT),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifndef NO_GSSAPI */
* Loading and saving is done by custom code because it's under
* #ifndef NO_GSSAPI
*/
) )
CONF_OPTION(ssh_gsslist, CONF_OPTION(ssh_gsslist,
SUBKEY_TYPE(INT), /* indices in preference order: 0,...,ngsslibs SUBKEY_TYPE(INT), /* indices in preference order: 0,...,ngsslibs
* (lower is more preferred; ngsslibs is a platform- * (lower is more preferred; ngsslibs is a platform-
* dependent value) */ * dependent value) */
VALUE_TYPE(INT), /* indices of GSSAPI lib types (platform-dependent) */ VALUE_TYPE(INT), /* indices of GSSAPI lib types (platform-dependent) */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for preference lists, also this
* Loading and saving is done by custom code because it's under * setting is under #ifndef NO_GSSAPI */
* #ifndef NO_GSSAPI, and also because all preference lists are
* handled by custom code
*/
) )
CONF_OPTION(ssh_gss_custom, CONF_OPTION(ssh_gss_custom,
VALUE_TYPE(FILENAME), VALUE_TYPE(FILENAME),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifndef NO_GSSAPI */
* Loading and saving is done by custom code because it's under
* #ifndef NO_GSSAPI
*/
) )
CONF_OPTION(ssh_subsys, /* run a subsystem rather than a command */ CONF_OPTION(ssh_subsys, /* run a subsystem rather than a command */
/* /*
@ -374,6 +349,7 @@ CONF_OPTION(ssh_subsys, /* run a subsystem rather than a command */
* configuration, or loaded or saved. * configuration, or loaded or saved.
*/ */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
NOT_SAVED,
) )
CONF_OPTION(ssh_subsys2, /* fallback to go with remote_cmd_ptr2 */ CONF_OPTION(ssh_subsys2, /* fallback to go with remote_cmd_ptr2 */
/* /*
@ -381,6 +357,7 @@ CONF_OPTION(ssh_subsys2, /* fallback to go with remote_cmd_ptr2 */
* configuration, or loaded or saved. * configuration, or loaded or saved.
*/ */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
NOT_SAVED,
) )
CONF_OPTION(ssh_no_shell, /* avoid running a shell */ CONF_OPTION(ssh_no_shell, /* avoid running a shell */
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
@ -394,6 +371,7 @@ CONF_OPTION(ssh_nc_host, /* host to connect to in `nc' mode */
* also never loaded or saved. * also never loaded or saved.
*/ */
VALUE_TYPE(STR), VALUE_TYPE(STR),
NOT_SAVED,
) )
CONF_OPTION(ssh_nc_port, /* port to connect to in `nc' mode */ CONF_OPTION(ssh_nc_port, /* port to connect to in `nc' mode */
/* /*
@ -402,6 +380,7 @@ CONF_OPTION(ssh_nc_port, /* port to connect to in `nc' mode */
* also never loaded or saved. * also never loaded or saved.
*/ */
VALUE_TYPE(INT), VALUE_TYPE(INT),
NOT_SAVED,
) )
/* Telnet options */ /* Telnet options */
@ -418,16 +397,12 @@ CONF_OPTION(termspeed,
CONF_OPTION(ttymodes, CONF_OPTION(ttymodes,
SUBKEY_TYPE(STR), /* subkeys are listed in ttymodes[] in settings.c */ SUBKEY_TYPE(STR), /* subkeys are listed in ttymodes[] in settings.c */
VALUE_TYPE(STR), /* values are "Vvalue" or "A" */ VALUE_TYPE(STR), /* values are "Vvalue" or "A" */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for mappings */
* Loading and saving is done by custom code for all mappings
*/
) )
CONF_OPTION(environmt, CONF_OPTION(environmt,
SUBKEY_TYPE(STR), /* environment variable name */ SUBKEY_TYPE(STR), /* environment variable name */
VALUE_TYPE(STR), /* environment variable value */ VALUE_TYPE(STR), /* environment variable value */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for mappings */
* Loading and saving is done by custom code for all mappings
*/
) )
CONF_OPTION(username, CONF_OPTION(username,
VALUE_TYPE(STR), VALUE_TYPE(STR),
@ -582,11 +557,10 @@ CONF_OPTION(no_remote_charset, /* disable remote charset config */
) )
CONF_OPTION(remote_qtitle_action, /* handling of remote window title queries */ CONF_OPTION(remote_qtitle_action, /* handling of remote window title queries */
VALUE_TYPE(INT), VALUE_TYPE(INT),
/* STORAGE_ENUM(remote_qtitle_action),
* SAVE_KEYWORD("RemoteQTitleAction"), but loading and saving is SAVE_KEYWORD("RemoteQTitleAction"),
* done in custom code, because older versions had a boolean LOAD_CUSTOM, /* older versions had a boolean "NoRemoteQTitle"
* "NoRemoteQTitle" before we ended up with three options. * before we ended up with three options */
*/
) )
CONF_OPTION(app_cursor, CONF_OPTION(app_cursor,
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
@ -677,17 +651,11 @@ CONF_OPTION(ctrlaltkeys,
) )
CONF_OPTION(osx_option_meta, CONF_OPTION(osx_option_meta,
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifdef OSX_META_KEY_CONFIG */
* Loading and saving is done in custom code because this is under
* #ifdef OSX_META_KEY_CONFIG
*/
) )
CONF_OPTION(osx_command_meta, CONF_OPTION(osx_command_meta,
VALUE_TYPE(BOOL), VALUE_TYPE(BOOL),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* under #ifdef OSX_META_KEY_CONFIG */
* Loading and saving is done in custom code because this is under
* #ifdef OSX_META_KEY_CONFIG
*/
) )
CONF_OPTION(wintitle, /* initial window title */ CONF_OPTION(wintitle, /* initial window title */
VALUE_TYPE(STR), VALUE_TYPE(STR),
@ -750,6 +718,7 @@ CONF_OPTION(bellovl_n, /* number of bells to cause overload */
) )
CONF_OPTION(bellovl_t, /* time interval for overload (ticks) */ CONF_OPTION(bellovl_t, /* time interval for overload (ticks) */
VALUE_TYPE(INT), VALUE_TYPE(INT),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Loading and saving is done in custom code because the format is * Loading and saving is done in custom code because the format is
* platform-dependent for historical reasons: on Unix, the stored * platform-dependent for historical reasons: on Unix, the stored
@ -760,6 +729,7 @@ CONF_OPTION(bellovl_t, /* time interval for overload (ticks) */
) )
CONF_OPTION(bellovl_s, /* period of silence to re-enable bell (s) */ CONF_OPTION(bellovl_s, /* period of silence to re-enable bell (s) */
VALUE_TYPE(INT), VALUE_TYPE(INT),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Loading and saving is done in custom code because the format is * Loading and saving is done in custom code because the format is
* platform-dependent for historical reasons: on Unix, the stored * platform-dependent for historical reasons: on Unix, the stored
@ -930,9 +900,7 @@ CONF_OPTION(bold_style,
CONF_OPTION(colours, CONF_OPTION(colours,
SUBKEY_TYPE(INT), /* indexed by CONF_COLOUR_* enum encoding */ SUBKEY_TYPE(INT), /* indexed by CONF_COLOUR_* enum encoding */
VALUE_TYPE(INT), VALUE_TYPE(INT),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for mappings */
* Loading and saving is done by custom code for all mappings
*/
) )
/* Selection options */ /* Selection options */
@ -975,9 +943,7 @@ CONF_OPTION(mouse_override,
CONF_OPTION(wordness, CONF_OPTION(wordness,
SUBKEY_TYPE(INT), /* ASCII character codes (literally, just 00-7F) */ SUBKEY_TYPE(INT), /* ASCII character codes (literally, just 00-7F) */
VALUE_TYPE(INT), /* arbitrary equivalence-class value for that char */ VALUE_TYPE(INT), /* arbitrary equivalence-class value for that char */
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for mappings */
* Loading and saving is done by custom code for all mappings
*/
) )
CONF_OPTION(mouseautocopy, CONF_OPTION(mouseautocopy,
/* /*
@ -991,6 +957,7 @@ CONF_OPTION(mouseautocopy,
) )
CONF_OPTION(mousepaste, /* clipboard used by one-mouse-click paste actions */ CONF_OPTION(mousepaste, /* clipboard used by one-mouse-click paste actions */
VALUE_TYPE(INT), VALUE_TYPE(INT),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* SAVE_KEYWORD("MousePaste"), but loading and saving is done by * SAVE_KEYWORD("MousePaste"), but loading and saving is done by
* custom code, because the saved value is a string, and also sets * custom code, because the saved value is a string, and also sets
@ -999,6 +966,7 @@ CONF_OPTION(mousepaste, /* clipboard used by one-mouse-click paste actions */
) )
CONF_OPTION(ctrlshiftins, /* clipboard used by Ctrl+Ins and Shift+Ins */ CONF_OPTION(ctrlshiftins, /* clipboard used by Ctrl+Ins and Shift+Ins */
VALUE_TYPE(INT), VALUE_TYPE(INT),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* SAVE_KEYWORD("CtrlShiftIns"), but loading and saving is done by * SAVE_KEYWORD("CtrlShiftIns"), but loading and saving is done by
* custom code, because the saved value is a string, and also sets * custom code, because the saved value is a string, and also sets
@ -1007,6 +975,7 @@ CONF_OPTION(ctrlshiftins, /* clipboard used by Ctrl+Ins and Shift+Ins */
) )
CONF_OPTION(ctrlshiftcv, /* clipboard used by Ctrl+Shift+C and Ctrl+Shift+V */ CONF_OPTION(ctrlshiftcv, /* clipboard used by Ctrl+Shift+C and Ctrl+Shift+V */
VALUE_TYPE(INT), VALUE_TYPE(INT),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* SAVE_KEYWORD("CtrlShiftCV"), but loading and saving is done by * SAVE_KEYWORD("CtrlShiftCV"), but loading and saving is done by
* custom code, because the saved value is a string, and also sets * custom code, because the saved value is a string, and also sets
@ -1016,6 +985,7 @@ CONF_OPTION(ctrlshiftcv, /* clipboard used by Ctrl+Shift+C and Ctrl+Shift+V */
CONF_OPTION(mousepaste_custom, CONF_OPTION(mousepaste_custom,
/* Custom clipboard name if CONF_mousepaste is set to CLIPUI_CUSTOM */ /* Custom clipboard name if CONF_mousepaste is set to CLIPUI_CUSTOM */
VALUE_TYPE(STR), VALUE_TYPE(STR),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Loading and saving is handled by custom code in conjunction * Loading and saving is handled by custom code in conjunction
* with CONF_mousepaste * with CONF_mousepaste
@ -1024,6 +994,7 @@ CONF_OPTION(mousepaste_custom,
CONF_OPTION(ctrlshiftins_custom, CONF_OPTION(ctrlshiftins_custom,
/* Custom clipboard name if CONF_ctrlshiftins is set to CLIPUI_CUSTOM */ /* Custom clipboard name if CONF_ctrlshiftins is set to CLIPUI_CUSTOM */
VALUE_TYPE(STR), VALUE_TYPE(STR),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Loading and saving is handled by custom code in conjunction * Loading and saving is handled by custom code in conjunction
* with CONF_ctrlshiftins * with CONF_ctrlshiftins
@ -1032,6 +1003,7 @@ CONF_OPTION(ctrlshiftins_custom,
CONF_OPTION(ctrlshiftcv_custom, CONF_OPTION(ctrlshiftcv_custom,
/* Custom clipboard name if CONF_ctrlshiftcv is set to CLIPUI_CUSTOM */ /* Custom clipboard name if CONF_ctrlshiftcv is set to CLIPUI_CUSTOM */
VALUE_TYPE(STR), VALUE_TYPE(STR),
LOAD_CUSTOM, SAVE_CUSTOM,
/* /*
* Loading and saving is handled by custom code in conjunction * Loading and saving is handled by custom code in conjunction
* with CONF_ctrlshiftcv * with CONF_ctrlshiftcv
@ -1112,9 +1084,7 @@ CONF_OPTION(portfwd,
*/ */
SUBKEY_TYPE(STR), SUBKEY_TYPE(STR),
VALUE_TYPE(STR), VALUE_TYPE(STR),
/* LOAD_CUSTOM, SAVE_CUSTOM, /* necessary for mappings */
* Loading and saving is done by custom code for all mappings
*/
) )
/* SSH bug compatibility modes. All FORCE_ON/FORCE_OFF/AUTO */ /* SSH bug compatibility modes. All FORCE_ON/FORCE_OFF/AUTO */
@ -1210,11 +1180,10 @@ CONF_OPTION(sshbug_rsa_sha2_cert_userauth,
) )
CONF_OPTION(sshbug_hmac2, CONF_OPTION(sshbug_hmac2,
VALUE_TYPE(INT), VALUE_TYPE(INT),
DEFAULT_INT(AUTO),
SAVE_KEYWORD("BugHMAC2"),
STORAGE_ENUM(auto_off_on), STORAGE_ENUM(auto_off_on),
/* LOAD_CUSTOM, /* there was an earlier keyword called "BuggyMAC" */
* Loading and saving is done by custom code because there was an
* earlier keyword called "BuggyMAC"
*/
) )
/* Options for Unix. Should split out into platform-dependent part. */ /* Options for Unix. Should split out into platform-dependent part. */

View File

@ -1820,6 +1820,10 @@ struct ConfKeyInfo {
const char *sval; const char *sval;
} default_value; } default_value;
bool save_custom : 1;
bool load_custom : 1;
bool not_saved : 1;
const char *save_keyword; const char *save_keyword;
const ConfSaveEnumType *storage_enum; const ConfSaveEnumType *storage_enum;
}; };

View File

@ -525,6 +525,11 @@ char *save_settings(const char *section, Conf *conf)
return NULL; return NULL;
} }
/* Declare extern references to conf_enum_* types */
#define CONF_ENUM(name, ...) extern const ConfSaveEnumType conf_enum_##name;
#include "conf-enums.h"
#undef CONF_ENUM
void save_open_settings(settings_w *sesskey, Conf *conf) void save_open_settings(settings_w *sesskey, Conf *conf)
{ {
int i; int i;
@ -533,7 +538,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
/* Save the settings simple enough to handle automatically */ /* Save the settings simple enough to handle automatically */
for (size_t key = 0; key < N_CONFIG_OPTIONS; key++) { for (size_t key = 0; key < N_CONFIG_OPTIONS; key++) {
const ConfKeyInfo *info = &conf_key_info[key]; const ConfKeyInfo *info = &conf_key_info[key];
if (info->save_keyword) { if (!info->save_custom && !info->not_saved) {
/* Mappings are handled individually below */ /* Mappings are handled individually below */
assert(info->subkey_type == CONF_TYPE_NONE); assert(info->subkey_type == CONF_TYPE_NONE);
switch (info->value_type) { switch (info->value_type) {
@ -578,13 +583,11 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
p = vt->id; p = vt->id;
} }
write_setting_s(sesskey, "Protocol", p); write_setting_s(sesskey, "Protocol", p);
write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port));
write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */ write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */
write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */ write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */
wmap(sesskey, "TerminalModes", conf, CONF_ttymodes, true); wmap(sesskey, "TerminalModes", conf, CONF_ttymodes, true);
/* proxy settings */ /* proxy settings */
write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type));
wmap(sesskey, "Environment", conf, CONF_environmt, true); wmap(sesskey, "Environment", conf, CONF_environmt, true);
#ifndef NO_GSSAPI #ifndef NO_GSSAPI
write_setting_b(sesskey, "GssapiFwd", conf_get_bool(conf, CONF_gssapifwd)); write_setting_b(sesskey, "GssapiFwd", conf_get_bool(conf, CONF_gssapifwd));
@ -601,7 +604,6 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist);
write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_ssh_gss_custom)); write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_ssh_gss_custom));
#endif #endif
write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action));
#ifdef OSX_META_KEY_CONFIG #ifdef OSX_META_KEY_CONFIG
write_setting_b(sesskey, "OSXOptionMeta", conf_get_bool(conf, CONF_osx_option_meta)); write_setting_b(sesskey, "OSXOptionMeta", conf_get_bool(conf, CONF_osx_option_meta));
write_setting_b(sesskey, "OSXCommandMeta", conf_get_bool(conf, CONF_osx_command_meta)); write_setting_b(sesskey, "OSXCommandMeta", conf_get_bool(conf, CONF_osx_command_meta));
@ -645,7 +647,6 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
write_clip_setting(sesskey, "CtrlShiftCV", conf, write_clip_setting(sesskey, "CtrlShiftCV", conf,
CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom); CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom);
wmap(sesskey, "PortForwardings", conf, CONF_portfwd, true); wmap(sesskey, "PortForwardings", conf, CONF_portfwd, true);
write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2));
wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, false); wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, false);
} }
@ -677,7 +678,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
/* Load the settings simple enough to handle automatically */ /* Load the settings simple enough to handle automatically */
for (size_t key = 0; key < N_CONFIG_OPTIONS; key++) { for (size_t key = 0; key < N_CONFIG_OPTIONS; key++) {
const ConfKeyInfo *info = &conf_key_info[key]; const ConfKeyInfo *info = &conf_key_info[key];
if (info->save_keyword) { if (!info->load_custom && !info->not_saved) {
/* Mappings are handled individually below */ /* Mappings are handled individually below */
assert(info->subkey_type == CONF_TYPE_NONE); assert(info->subkey_type == CONF_TYPE_NONE);
switch (info->value_type) { switch (info->value_type) {
@ -804,26 +805,25 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
} }
/* proxy settings */ /* proxy settings */
gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type); {
if (conf_get_int(conf, CONF_proxy_type) == -1) { int storageval = gppi_raw(sesskey, "ProxyMethod", -1);
int i; int confval;
i = gppi_raw(sesskey, "ProxyType", 0); if (!conf_enum_map_from_storage(&conf_enum_proxy_type,
if (i == 0) storageval, &confval)) {
conf_set_int(conf, CONF_proxy_type, PROXY_NONE); /*
else if (i == 1) * Fall back to older ProxyType and ProxySOCKSVersion format
conf_set_int(conf, CONF_proxy_type, PROXY_HTTP); */
else if (i == 3) storageval = gppi_raw(sesskey, "ProxyType", 0);
conf_set_int(conf, CONF_proxy_type, PROXY_TELNET); if (conf_enum_map_from_storage(&conf_enum_old_proxy_type,
else if (i == 4) storageval, &confval)) {
conf_set_int(conf, CONF_proxy_type, PROXY_CMD); if (confval == PROXY_SOCKS5 &&
else { gppi_raw(sesskey, "ProxySOCKSVersion", 5) == 4)
i = gppi_raw(sesskey, "ProxySOCKSVersion", 5); confval = PROXY_SOCKS4;
if (i == 5) }
conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5);
else
conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4);
} }
conf_set_int(conf, CONF_proxy_type, confval);
} }
gppmap(sesskey, "Environment", conf, CONF_environmt); gppmap(sesskey, "Environment", conf, CONF_environmt);
#ifndef NO_GSSAPI #ifndef NO_GSSAPI
gppb(sesskey, "GssapiFwd", false, conf, CONF_gssapifwd); gppb(sesskey, "GssapiFwd", false, conf, CONF_gssapifwd);
@ -881,8 +881,6 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
hknames, HK_MAX, conf, CONF_ssh_hklist); hknames, HK_MAX, conf, CONF_ssh_hklist);
#ifndef NO_GSSAPI #ifndef NO_GSSAPI
gppi(sesskey, "GssapiRekey", GSS_DEF_REKEY_MINS, conf, CONF_gssapirekey); gppi(sesskey, "GssapiRekey", GSS_DEF_REKEY_MINS, conf, CONF_gssapirekey);
#endif
#ifndef NO_GSSAPI
gppb(sesskey, "AuthGSSAPI", true, conf, CONF_try_gssapi_auth); gppb(sesskey, "AuthGSSAPI", true, conf, CONF_try_gssapi_auth);
gppb(sesskey, "AuthGSSAPIKEX", true, conf, CONF_try_gssapi_kex); gppb(sesskey, "AuthGSSAPIKEX", true, conf, CONF_try_gssapi_kex);
gprefs(sesskey, "GSSLibs", "\0", gprefs(sesskey, "GSSLibs", "\0",
@ -890,14 +888,20 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom);
#endif #endif
{ {
/* Backward compatibility */ int storageval = gppi_raw(sesskey, "RemoteQTitleAction", -1);
int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1); int confval;
/* We deliberately interpret the old setting of "no response" as if (!conf_enum_map_from_storage(&conf_enum_remote_qtitle_action,
* "empty string". This changes the behaviour, but hopefully for storageval, &confval)) {
* the better; the user can always recover the old behaviour. */ /*
gppi(sesskey, "RemoteQTitleAction", * Fall back to older NoRemoteQTitle format
no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL, */
conf, CONF_remote_qtitle_action); storageval = gppi_raw(sesskey, "NoRemoteQTitle", 1);
/* We deliberately interpret the old setting of "no response" as
* "empty string". This changes the behaviour, but hopefully for
* the better; the user can always recover the old behaviour. */
confval = storageval ? TITLE_EMPTY : TITLE_REAL;
}
conf_set_int(conf, CONF_remote_qtitle_action, confval);
} }
#ifdef OSX_META_KEY_CONFIG #ifdef OSX_META_KEY_CONFIG
gppb(sesskey, "OSXOptionMeta", true, conf, CONF_osx_option_meta); gppb(sesskey, "OSXOptionMeta", true, conf, CONF_osx_option_meta);

View File

@ -323,7 +323,9 @@ void test_int_simple(int confid, const char *saveid, int defexp)
conf_free(conf); conf_free(conf);
} }
void test_int_translated(int confid, const char *saveid, int defexp, ...) void test_int_translated_internal(
int confid, const char *saveid, bool test_save, bool test_load,
void (*load_prepare)(settings_r *), int defexp, va_list ap)
{ {
Conf *conf = conf_new(); Conf *conf = conf_new();
@ -335,52 +337,76 @@ void test_int_translated(int confid, const char *saveid, int defexp, ...)
nfails++; nfails++;
} }
va_list ap;
va_start(ap, defexp);
int confval = va_arg(ap, int); int confval = va_arg(ap, int);
while (confval != -1) { while (confval != -1) {
int storageval = va_arg(ap, int); int storageval = va_arg(ap, int);
settings_w sw = {
.n = 1, if (test_save) {
.si[0].key = saveid, settings_w sw = {
.si[0].type = SAVE_UNSET, .n = 1,
}; .si[0].key = saveid,
conf_set_int(conf, confid, confval); .si[0].type = SAVE_UNSET,
save_open_settings(&sw, conf); };
if (sw.si[0].type != SAVE_I) { conf_set_int(conf, confid, confval);
printf("fail test_int_translated(%s): " save_open_settings(&sw, conf);
"saved type = %d, expected %d\n", if (sw.si[0].type != SAVE_I) {
saveid, sw.si[0].type, SAVE_I); printf("fail test_int_translated(%s): "
nfails++; "saved type = %d, expected %d\n",
} else if (sw.si[0].ival != storageval) { saveid, sw.si[0].type, SAVE_I);
printf("fail test_int_translated(%s.%d.%d): " nfails++;
"saved integer = %d, expected %d\n", } else if (sw.si[0].ival != storageval) {
saveid, confval, storageval, sw.si[0].ival, storageval); printf("fail test_int_translated(%s.%d.%d): "
nfails++; "saved integer = %d, expected %d\n",
saveid, confval, storageval, sw.si[0].ival, storageval);
nfails++;
}
} }
conf_clear(conf); if (test_load) {
settings_r sr = { conf_clear(conf);
.n = 1, settings_r sr = {
.si[0].key = saveid, .n = 1,
.si[0].type = SAVE_I, .si[0].key = saveid,
.si[0].ival = storageval, .si[0].type = SAVE_I,
}; .si[0].ival = storageval,
load_open_settings(&sr, conf); };
int loaded = conf_get_int(conf, confid); if (load_prepare)
if (loaded != confval) { load_prepare(&sr);
printf("fail test_int_translated(%s.%d.%d): " load_open_settings(&sr, conf);
"loaded integer = %d, expected %d\n", int loaded = conf_get_int(conf, confid);
saveid, confval, storageval, loaded, confval); if (loaded != confval) {
nfails++; printf("fail test_int_translated(%s.%d.%d): "
"loaded integer = %d, expected %d\n",
saveid, confval, storageval, loaded, confval);
nfails++;
}
} }
confval = va_arg(ap, int); confval = va_arg(ap, int);
} }
va_end(ap);
conf_free(conf); conf_free(conf);
} }
void test_int_translated(int confid, const char *saveid, int defexp, ...)
{
va_list ap;
va_start(ap, defexp);
test_int_translated_internal(confid, saveid, true, true, NULL, defexp, ap);
va_end(ap);
}
void test_int_translated_load_legacy(
int confid, const char *saveid, void (*load_prepare)(settings_r *),
int defexp, ...)
{
va_list ap;
va_start(ap, defexp);
test_int_translated_internal(confid, saveid, false, true, load_prepare,
defexp, ap);
va_end(ap);
}
void test_bool_simple(int confid, const char *saveid, bool defexp) void test_bool_simple(int confid, const char *saveid, bool defexp)
{ {
Conf *conf = conf_new(); Conf *conf = conf_new();
@ -532,6 +558,14 @@ void test_font_simple(int confid, const char *saveid)
conf_free(conf); conf_free(conf);
} }
static void load_prepare_socks4(settings_r *sr)
{
size_t pos = sr->n++;
sr->si[pos].key = "ProxySOCKSVersion";
sr->si[pos].type = SAVE_I;
sr->si[pos].ival = 4;
}
void test_simple(void) void test_simple(void)
{ {
test_str_simple(CONF_host, "HostName", ""); test_str_simple(CONF_host, "HostName", "");
@ -764,6 +798,22 @@ void test_simple(void)
FORCE_OFF, 1, FORCE_ON, 2, -1); FORCE_OFF, 1, FORCE_ON, 2, -1);
test_int_translated(CONF_sshbug_rsa_sha2_cert_userauth, "BugRSASHA2CertUserauth", AUTO, test_int_translated(CONF_sshbug_rsa_sha2_cert_userauth, "BugRSASHA2CertUserauth", AUTO,
AUTO, 0, FORCE_OFF, 1, FORCE_ON, 2, -1); AUTO, 0, FORCE_OFF, 1, FORCE_ON, 2, -1);
test_int_translated(CONF_proxy_type, "ProxyMethod", PROXY_NONE,
PROXY_NONE, 0, PROXY_SOCKS4, 1, PROXY_SOCKS5, 2,
PROXY_HTTP, 3, PROXY_TELNET, 4, PROXY_CMD, 5,
PROXY_SSH_TCPIP, 6, PROXY_SSH_EXEC, 7,
PROXY_SSH_SUBSYSTEM, 8, -1);
test_int_translated_load_legacy(
CONF_proxy_type, "ProxyType", NULL, PROXY_NONE,
PROXY_HTTP, 1, PROXY_SOCKS5, 2, PROXY_TELNET, 3, PROXY_CMD, 4, -1);
test_int_translated_load_legacy(
CONF_proxy_type, "ProxyType", load_prepare_socks4, PROXY_NONE,
PROXY_HTTP, 1, PROXY_SOCKS4, 2, PROXY_TELNET, 3, PROXY_CMD, 4, -1);
test_int_translated(CONF_remote_qtitle_action, "RemoteQTitleAction", TITLE_EMPTY,
TITLE_NONE, 0, TITLE_EMPTY, 1, TITLE_REAL, 2, -1);
test_int_translated_load_legacy(
CONF_remote_qtitle_action, "NoRemoteQTitle", NULL, TITLE_EMPTY,
TITLE_REAL, 0, TITLE_EMPTY, 1, -1);
} }
void test_conf_key_info(void) void test_conf_key_info(void)
@ -777,6 +827,9 @@ void test_conf_key_info(void)
bool got_default_bool : 1; bool got_default_bool : 1;
bool got_save_keyword : 1; bool got_save_keyword : 1;
bool got_storage_enum : 1; bool got_storage_enum : 1;
bool save_custom : 1;
bool load_custom : 1;
bool not_saved : 1;
}; };
#define CONF_OPTION(id, ...) { .name = "CONF_" #id, __VA_ARGS__ }, #define CONF_OPTION(id, ...) { .name = "CONF_" #id, __VA_ARGS__ },
@ -787,6 +840,9 @@ void test_conf_key_info(void)
#define DEFAULT_BOOL(x) .got_default_bool = true #define DEFAULT_BOOL(x) .got_default_bool = true
#define SAVE_KEYWORD(x) .got_save_keyword = true #define SAVE_KEYWORD(x) .got_save_keyword = true
#define STORAGE_ENUM(x) .got_storage_enum = true #define STORAGE_ENUM(x) .got_storage_enum = true
#define SAVE_CUSTOM .save_custom = true
#define LOAD_CUSTOM .load_custom = true
#define NOT_SAVED .not_saved = true
static const struct test_data conf_key_test_data[] = { static const struct test_data conf_key_test_data[] = {
#include "conf.h" #include "conf.h"
@ -807,12 +863,64 @@ void test_conf_key_info(void)
fprintf(stderr, "%s: default doesn't match type\n", td->name); fprintf(stderr, "%s: default doesn't match type\n", td->name);
nfails++; nfails++;
} }
if (td->got_storage_enum && info->value_type != CONF_TYPE_INT) {
fprintf(stderr, "%s: has STORAGE_ENUM but isn't an int\n",
td->name);
nfails++;
}
if (td->not_saved) {
if (td->got_save_keyword) {
fprintf(stderr, "%s: not saved but has SAVE_KEYWORD\n",
td->name);
nfails++;
}
if (td->save_custom) {
fprintf(stderr, "%s: not saved but has SAVE_CUSTOM\n",
td->name);
nfails++;
}
if (td->load_custom) {
fprintf(stderr, "%s: not saved but has LOAD_CUSTOM\n",
td->name);
nfails++;
}
if (td->got_storage_enum) {
fprintf(stderr, "%s: not saved but has STORAGE_ENUM\n",
td->name);
nfails++;
}
} else {
if (td->load_custom && td->save_custom) {
if (td->got_save_keyword) {
fprintf(stderr, "%s: no automatic save or load but has "
"SAVE_KEYWORD\n", td->name);
nfails++;
}
if (td->got_storage_enum) {
fprintf(stderr, "%s: no automatic save or load but has "
"STORAGE_ENUM\n", td->name);
nfails++;
}
} else {
if (!td->got_save_keyword) {
fprintf(stderr, "%s: missing SAVE_KEYWORD\n", td->name);
nfails++;
}
}
}
} }
} }
int main(void) int main(void)
{ {
test_simple();
test_conf_key_info(); test_conf_key_info();
test_simple();
return nfails != 0; return nfails != 0;
} }

View File

@ -1,11 +1,11 @@
#include "putty.h" #include "putty.h"
#define CONF_ENUM(name, ...) \ #define CONF_ENUM(name, ...) \
static const ConfSaveEnumValue enum_values_##name[] = { \ static const ConfSaveEnumValue conf_enum_values_##name[] = { \
__VA_ARGS__ \ __VA_ARGS__ \
}; static const ConfSaveEnumType enum_##name = { \ }; const ConfSaveEnumType conf_enum_##name = { \
.values = enum_values_##name, \ .values = conf_enum_values_##name, \
.nvalues = lenof(enum_values_##name), \ .nvalues = lenof(conf_enum_values_##name), \
}; };
#define VALUE(eval, sval) { eval, sval, false } #define VALUE(eval, sval) { eval, sval, false }
@ -43,7 +43,10 @@ bool conf_enum_map_from_storage(const ConfSaveEnumType *etype,
#define DEFAULT_STR(x) .default_value.sval = x #define DEFAULT_STR(x) .default_value.sval = x
#define DEFAULT_BOOL(x) .default_value.bval = x #define DEFAULT_BOOL(x) .default_value.bval = x
#define SAVE_KEYWORD(x) .save_keyword = x #define SAVE_KEYWORD(x) .save_keyword = x
#define STORAGE_ENUM(x) .storage_enum = &enum_ ## x #define STORAGE_ENUM(x) .storage_enum = &conf_enum_ ## x
#define SAVE_CUSTOM .save_custom = true
#define LOAD_CUSTOM .load_custom = true
#define NOT_SAVED .not_saved = true
const ConfKeyInfo conf_key_info[] = { const ConfKeyInfo conf_key_info[] = {
#include "conf.h" #include "conf.h"