2000-09-27 15:21:04 +00:00
|
|
|
/*
|
|
|
|
* winstore.c: Windows-specific implementation of the interface
|
|
|
|
* defined in storage.h.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2000-10-24 10:47:49 +00:00
|
|
|
#include <stdlib.h>
|
2003-02-01 12:54:40 +00:00
|
|
|
#include <limits.h>
|
2014-09-07 13:06:50 +00:00
|
|
|
#include <assert.h>
|
2000-09-27 15:21:04 +00:00
|
|
|
#include "putty.h"
|
|
|
|
#include "storage.h"
|
|
|
|
|
2007-01-09 18:05:17 +00:00
|
|
|
#include <shlobj.h>
|
|
|
|
#ifndef CSIDL_APPDATA
|
|
|
|
#define CSIDL_APPDATA 0x001a
|
|
|
|
#endif
|
|
|
|
#ifndef CSIDL_LOCAL_APPDATA
|
|
|
|
#define CSIDL_LOCAL_APPDATA 0x001c
|
|
|
|
#endif
|
2000-09-27 16:21:52 +00:00
|
|
|
|
2010-12-23 17:32:28 +00:00
|
|
|
static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist";
|
|
|
|
static const char *const reg_jumplist_value = "Recent sessions";
|
2007-01-09 18:05:17 +00:00
|
|
|
static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
|
2000-09-27 15:21:04 +00:00
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
static bool tried_shgetfolderpath = false;
|
2007-01-09 18:05:17 +00:00
|
|
|
static HMODULE shell32_module = NULL;
|
2019-09-08 19:29:00 +00:00
|
|
|
DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA,
|
|
|
|
(HWND, int, HANDLE, DWORD, LPSTR));
|
2007-01-09 18:05:17 +00:00
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
struct settings_w {
|
|
|
|
HKEY sesskey;
|
|
|
|
};
|
|
|
|
|
|
|
|
settings_w *open_settings_w(const char *sessionname, char **errmsg)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
HKEY subkey1, sesskey;
|
|
|
|
int ret;
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf *sb;
|
2000-09-27 16:21:52 +00:00
|
|
|
|
2003-04-01 18:10:25 +00:00
|
|
|
*errmsg = NULL;
|
|
|
|
|
2003-03-22 09:49:20 +00:00
|
|
|
if (!sessionname || !*sessionname)
|
2019-09-08 19:29:00 +00:00
|
|
|
sessionname = "Default Settings";
|
2003-03-22 09:49:20 +00:00
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
sb = strbuf_new();
|
|
|
|
escape_registry_key(sessionname, sb);
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2000-09-27 16:21:52 +00:00
|
|
|
ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
2019-09-08 19:29:00 +00:00
|
|
|
strbuf_free(sb);
|
2003-04-01 18:10:25 +00:00
|
|
|
*errmsg = dupprintf("Unable to create registry key\n"
|
2004-11-20 19:07:34 +00:00
|
|
|
"HKEY_CURRENT_USER\\%s", puttystr);
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
ret = RegCreateKey(subkey1, sb->s, &sesskey);
|
2000-09-27 16:21:52 +00:00
|
|
|
RegCloseKey(subkey1);
|
2003-04-01 18:10:25 +00:00
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
|
|
*errmsg = dupprintf("Unable to create registry key\n"
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
"HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s);
|
2019-09-08 19:29:00 +00:00
|
|
|
strbuf_free(sb);
|
|
|
|
return NULL;
|
2003-04-01 18:10:25 +00:00
|
|
|
}
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf_free(sb);
|
2018-09-14 07:45:42 +00:00
|
|
|
|
|
|
|
settings_w *toret = snew(settings_w);
|
|
|
|
toret->sesskey = sesskey;
|
|
|
|
return toret;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void write_setting_s(settings_w *handle, const char *key, const char *value)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
if (handle)
|
2018-09-14 07:45:42 +00:00
|
|
|
RegSetValueEx(handle->sesskey, key, 0, REG_SZ, (CONST BYTE *)value,
|
2019-09-08 19:29:00 +00:00
|
|
|
1 + strlen(value));
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void write_setting_i(settings_w *handle, const char *key, int value)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
if (handle)
|
2018-09-14 07:45:42 +00:00
|
|
|
RegSetValueEx(handle->sesskey, key, 0, REG_DWORD,
|
2019-09-08 19:29:00 +00:00
|
|
|
(CONST BYTE *) &value, sizeof(value));
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void close_settings_w(settings_w *handle)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-14 07:45:42 +00:00
|
|
|
RegCloseKey(handle->sesskey);
|
|
|
|
sfree(handle);
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
struct settings_r {
|
|
|
|
HKEY sesskey;
|
|
|
|
};
|
|
|
|
|
|
|
|
settings_r *open_settings_r(const char *sessionname)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
HKEY subkey1, sesskey;
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf *sb;
|
2000-09-27 16:21:52 +00:00
|
|
|
|
2003-03-22 09:49:20 +00:00
|
|
|
if (!sessionname || !*sessionname)
|
2019-09-08 19:29:00 +00:00
|
|
|
sessionname = "Default Settings";
|
2003-03-22 09:49:20 +00:00
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
sb = strbuf_new();
|
|
|
|
escape_registry_key(sessionname, sb);
|
2000-09-27 16:21:52 +00:00
|
|
|
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {
|
2019-09-08 19:29:00 +00:00
|
|
|
sesskey = NULL;
|
2000-09-27 16:21:52 +00:00
|
|
|
} else {
|
2019-09-08 19:29:00 +00:00
|
|
|
if (RegOpenKey(subkey1, sb->s, &sesskey) != ERROR_SUCCESS) {
|
|
|
|
sesskey = NULL;
|
|
|
|
}
|
|
|
|
RegCloseKey(subkey1);
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf_free(sb);
|
2000-09-27 16:21:52 +00:00
|
|
|
|
2019-02-27 20:29:13 +00:00
|
|
|
if (!sesskey)
|
|
|
|
return NULL;
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
settings_r *toret = snew(settings_r);
|
|
|
|
toret->sesskey = sesskey;
|
|
|
|
return toret;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
char *read_setting_s(settings_r *handle, const char *key)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2014-09-07 13:06:50 +00:00
|
|
|
DWORD type, allocsize, size;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
char *ret;
|
2000-09-27 16:21:52 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (!handle)
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
|
|
|
|
/* Find out the type and size of the data. */
|
2018-09-14 07:45:42 +00:00
|
|
|
if (RegQueryValueEx(handle->sesskey, key, 0,
|
2019-09-08 19:29:00 +00:00
|
|
|
&type, NULL, &size) != ERROR_SUCCESS ||
|
|
|
|
type != REG_SZ)
|
|
|
|
return NULL;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
|
2014-09-07 13:06:50 +00:00
|
|
|
allocsize = size+1; /* allow for an extra NUL if needed */
|
|
|
|
ret = snewn(allocsize, char);
|
2018-09-14 07:45:42 +00:00
|
|
|
if (RegQueryValueEx(handle->sesskey, key, 0,
|
2019-09-08 19:29:00 +00:00
|
|
|
&type, (BYTE *)ret, &size) != ERROR_SUCCESS ||
|
|
|
|
type != REG_SZ) {
|
2013-07-22 07:11:54 +00:00
|
|
|
sfree(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-09-07 13:06:50 +00:00
|
|
|
assert(size < allocsize);
|
|
|
|
ret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx
|
|
|
|
* didn't supply one */
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
|
|
|
|
return ret;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
int read_setting_i(settings_r *handle, const char *key, int defvalue)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
DWORD type, val, size;
|
|
|
|
size = sizeof(val);
|
|
|
|
|
|
|
|
if (!handle ||
|
2018-09-14 07:45:42 +00:00
|
|
|
RegQueryValueEx(handle->sesskey, key, 0, &type,
|
2019-09-08 19:29:00 +00:00
|
|
|
(BYTE *) &val, &size) != ERROR_SUCCESS ||
|
|
|
|
size != sizeof(val) || type != REG_DWORD)
|
|
|
|
return defvalue;
|
2000-09-27 16:21:52 +00:00
|
|
|
else
|
2019-09-08 19:29:00 +00:00
|
|
|
return val;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
|
|
|
char *settingname;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
char *fontname;
|
2013-07-22 07:11:54 +00:00
|
|
|
FontSpec *ret;
|
2011-10-01 17:38:59 +00:00
|
|
|
int isbold, height, charset;
|
2003-02-01 12:54:40 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
fontname = read_setting_s(handle, name);
|
|
|
|
if (!fontname)
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
settingname = dupcat(name, "IsBold");
|
2011-10-01 17:38:59 +00:00
|
|
|
isbold = read_setting_i(handle, settingname, -1);
|
2003-02-01 12:54:40 +00:00
|
|
|
sfree(settingname);
|
2013-07-22 07:11:54 +00:00
|
|
|
if (isbold == -1) {
|
|
|
|
sfree(fontname);
|
|
|
|
return NULL;
|
|
|
|
}
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
settingname = dupcat(name, "CharSet");
|
2011-10-01 17:38:59 +00:00
|
|
|
charset = read_setting_i(handle, settingname, -1);
|
2003-02-01 12:54:40 +00:00
|
|
|
sfree(settingname);
|
2013-07-22 07:11:54 +00:00
|
|
|
if (charset == -1) {
|
|
|
|
sfree(fontname);
|
|
|
|
return NULL;
|
|
|
|
}
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
settingname = dupcat(name, "Height");
|
2011-10-01 17:38:59 +00:00
|
|
|
height = read_setting_i(handle, settingname, INT_MIN);
|
2003-02-01 12:54:40 +00:00
|
|
|
sfree(settingname);
|
2013-07-22 07:11:54 +00:00
|
|
|
if (height == INT_MIN) {
|
|
|
|
sfree(fontname);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-10-01 17:38:59 +00:00
|
|
|
|
2013-07-22 07:11:54 +00:00
|
|
|
ret = fontspec_new(fontname, isbold, height, charset);
|
|
|
|
sfree(fontname);
|
|
|
|
return ret;
|
2003-02-01 12:54:40 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void write_setting_fontspec(settings_w *handle,
|
|
|
|
const char *name, FontSpec *font)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
|
|
|
char *settingname;
|
|
|
|
|
2011-10-01 17:38:59 +00:00
|
|
|
write_setting_s(handle, name, font->name);
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
settingname = dupcat(name, "IsBold");
|
2011-10-01 17:38:59 +00:00
|
|
|
write_setting_i(handle, settingname, font->isbold);
|
2003-02-01 12:54:40 +00:00
|
|
|
sfree(settingname);
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
settingname = dupcat(name, "CharSet");
|
2011-10-01 17:38:59 +00:00
|
|
|
write_setting_i(handle, settingname, font->charset);
|
2003-02-01 12:54:40 +00:00
|
|
|
sfree(settingname);
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
settingname = dupcat(name, "Height");
|
2011-10-01 17:38:59 +00:00
|
|
|
write_setting_i(handle, settingname, font->height);
|
2003-02-01 12:54:40 +00:00
|
|
|
sfree(settingname);
|
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
Filename *read_setting_filename(settings_r *handle, const char *name)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
char *tmp = read_setting_s(handle, name);
|
|
|
|
if (tmp) {
|
2011-10-02 11:01:57 +00:00
|
|
|
Filename *ret = filename_from_str(tmp);
|
2019-09-08 19:29:00 +00:00
|
|
|
sfree(tmp);
|
|
|
|
return ret;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
} else
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
2003-02-01 12:54:40 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void write_setting_filename(settings_w *handle,
|
|
|
|
const char *name, Filename *result)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
2011-10-02 11:01:57 +00:00
|
|
|
write_setting_s(handle, name, result->path);
|
2003-02-01 12:54:40 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void close_settings_r(settings_r *handle)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2019-02-27 20:29:13 +00:00
|
|
|
if (handle) {
|
|
|
|
RegCloseKey(handle->sesskey);
|
|
|
|
sfree(handle);
|
|
|
|
}
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2003-01-14 18:43:45 +00:00
|
|
|
void del_settings(const char *sessionname)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
HKEY subkey1;
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf *sb;
|
2000-09-27 16:21:52 +00:00
|
|
|
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
|
2019-09-08 19:29:00 +00:00
|
|
|
return;
|
2000-09-27 16:21:52 +00:00
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
sb = strbuf_new();
|
|
|
|
escape_registry_key(sessionname, sb);
|
|
|
|
RegDeleteKey(subkey1, sb->s);
|
|
|
|
strbuf_free(sb);
|
2000-09-27 16:21:52 +00:00
|
|
|
|
|
|
|
RegCloseKey(subkey1);
|
2010-12-23 17:32:28 +00:00
|
|
|
|
|
|
|
remove_session_from_jumplist(sessionname);
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
struct settings_e {
|
2000-09-27 16:21:52 +00:00
|
|
|
HKEY key;
|
|
|
|
int i;
|
|
|
|
};
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
settings_e *enum_settings_start(void)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-14 07:45:42 +00:00
|
|
|
settings_e *ret;
|
2000-09-27 16:21:52 +00:00
|
|
|
HKEY key;
|
|
|
|
|
2001-12-29 14:18:51 +00:00
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)
|
2019-09-08 19:29:00 +00:00
|
|
|
return NULL;
|
2000-09-27 16:21:52 +00:00
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
ret = snew(settings_e);
|
2000-09-27 16:21:52 +00:00
|
|
|
if (ret) {
|
2019-09-08 19:29:00 +00:00
|
|
|
ret->key = key;
|
|
|
|
ret->i = 0;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
bool enum_settings_next(settings_e *e, strbuf *sb)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2019-08-04 08:18:46 +00:00
|
|
|
size_t regbuf_size = MAX_PATH + 1;
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
char *regbuf = snewn(regbuf_size, char);
|
|
|
|
bool success;
|
|
|
|
|
|
|
|
while (1) {
|
2019-08-04 08:18:46 +00:00
|
|
|
DWORD retd = RegEnumKey(e->key, e->i, regbuf, regbuf_size);
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
if (retd != ERROR_MORE_DATA) {
|
|
|
|
success = (retd == ERROR_SUCCESS);
|
|
|
|
break;
|
|
|
|
}
|
New array-growing macros: sgrowarray and sgrowarrayn.
The idea of these is that they centralise the common idiom along the
lines of
if (logical_array_len >= physical_array_size) {
physical_array_size = logical_array_len * 5 / 4 + 256;
array = sresize(array, physical_array_size, ElementType);
}
which happens at a zillion call sites throughout this code base, with
different random choices of the geometric factor and additive
constant, sometimes forgetting them completely, and generally doing a
lot of repeated work.
The new macro sgrowarray(array,size,n) has the semantics: here are the
array pointer and its physical size for you to modify, now please
ensure that the nth element exists, so I can write into it. And
sgrowarrayn(array,size,n,m) is the same except that it ensures that
the array has size at least n+m (so sgrowarray is just the special
case where m=1).
Now that this is a single centralised implementation that will be used
everywhere, I've also gone to more effort in the implementation, with
careful overflow checks that would have been painful to put at all the
previous call sites.
This commit also switches over every use of sresize(), apart from a
few where I really didn't think it would gain anything. A consequence
of that is that a lot of array-size variables have to have their types
changed to size_t, because the macros require that (they address-take
the size to pass to the underlying function).
2019-02-28 20:07:30 +00:00
|
|
|
sgrowarray(regbuf, regbuf_size, regbuf_size);
|
2002-11-17 15:06:16 +00:00
|
|
|
}
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
unescape_registry_key(regbuf, sb);
|
|
|
|
|
2019-08-04 08:18:46 +00:00
|
|
|
e->i++;
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
sfree(regbuf);
|
|
|
|
return success;
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 07:45:42 +00:00
|
|
|
void enum_settings_finish(settings_e *e)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 16:21:52 +00:00
|
|
|
RegCloseKey(e->key);
|
2000-12-12 10:33:13 +00:00
|
|
|
sfree(e);
|
2000-09-27 16:21:52 +00:00
|
|
|
}
|
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
static void hostkey_regname(strbuf *sb, const char *hostname,
|
2019-09-08 19:29:00 +00:00
|
|
|
int port, const char *keytype)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf_catf(sb, "%s@%d:", keytype, port);
|
|
|
|
escape_registry_key(hostname, sb);
|
2000-09-28 08:37:10 +00:00
|
|
|
}
|
|
|
|
|
2003-01-14 18:43:45 +00:00
|
|
|
int verify_host_key(const char *hostname, int port,
|
2019-09-08 19:29:00 +00:00
|
|
|
const char *keytype, const char *key)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
char *otherstr;
|
|
|
|
strbuf *regname;
|
2000-09-27 15:21:04 +00:00
|
|
|
int len;
|
|
|
|
HKEY rkey;
|
|
|
|
DWORD readlen;
|
|
|
|
DWORD type;
|
|
|
|
int ret, compare;
|
|
|
|
|
|
|
|
len = 1 + strlen(key);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now read a saved key in from the registry and see what it
|
|
|
|
* says.
|
|
|
|
*/
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
regname = strbuf_new();
|
2000-09-28 08:37:10 +00:00
|
|
|
hostkey_regname(regname, hostname, port, keytype);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2001-12-29 14:18:51 +00:00
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
|
2019-09-08 19:29:00 +00:00
|
|
|
&rkey) != ERROR_SUCCESS) {
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf_free(regname);
|
2019-09-08 19:29:00 +00:00
|
|
|
return 1; /* key does not exist in registry */
|
2013-07-22 07:11:54 +00:00
|
|
|
}
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
readlen = len;
|
2013-07-22 07:11:54 +00:00
|
|
|
otherstr = snewn(len, char);
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
ret = RegQueryValueEx(rkey, regname->s, NULL,
|
2017-02-03 19:37:59 +00:00
|
|
|
&type, (BYTE *)otherstr, &readlen);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
|
2019-09-08 19:29:00 +00:00
|
|
|
!strcmp(keytype, "rsa")) {
|
|
|
|
/*
|
|
|
|
* Key didn't exist. If the key type is RSA, we'll try
|
|
|
|
* another trick, which is to look up the _old_ key format
|
|
|
|
* under just the hostname and translate that.
|
|
|
|
*/
|
|
|
|
char *justhost = regname->s + 1 + strcspn(regname->s, ":");
|
|
|
|
char *oldstyle = snewn(len + 10, char); /* safety margin */
|
|
|
|
readlen = len;
|
|
|
|
ret = RegQueryValueEx(rkey, justhost, NULL, &type,
|
|
|
|
(BYTE *)oldstyle, &readlen);
|
|
|
|
|
|
|
|
if (ret == ERROR_SUCCESS && type == REG_SZ) {
|
|
|
|
/*
|
|
|
|
* The old format is two old-style bignums separated by
|
|
|
|
* a slash. An old-style bignum is made of groups of
|
|
|
|
* four hex digits: digits are ordered in sensible
|
|
|
|
* (most to least significant) order within each group,
|
|
|
|
* but groups are ordered in silly (least to most)
|
|
|
|
* order within the bignum. The new format is two
|
|
|
|
* ordinary C-format hex numbers (0xABCDEFG...XYZ, with
|
|
|
|
* A nonzero except in the special case 0x0, which
|
|
|
|
* doesn't appear anyway in RSA keys) separated by a
|
|
|
|
* comma. All hex digits are lowercase in both formats.
|
|
|
|
*/
|
|
|
|
char *p = otherstr;
|
|
|
|
char *q = oldstyle;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
int ndigits, nwords;
|
|
|
|
*p++ = '0';
|
|
|
|
*p++ = 'x';
|
|
|
|
ndigits = strcspn(q, "/"); /* find / or end of string */
|
|
|
|
nwords = ndigits / 4;
|
|
|
|
/* now trim ndigits to remove leading zeros */
|
|
|
|
while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)
|
|
|
|
ndigits--;
|
|
|
|
/* now move digits over to new string */
|
|
|
|
for (j = 0; j < ndigits; j++)
|
|
|
|
p[ndigits - 1 - j] = q[j ^ 3];
|
|
|
|
p += ndigits;
|
|
|
|
q += nwords * 4;
|
|
|
|
if (*q) {
|
|
|
|
q++; /* eat the slash */
|
|
|
|
*p++ = ','; /* add a comma */
|
|
|
|
}
|
|
|
|
*p = '\0'; /* terminate the string */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now _if_ this key matches, we'll enter it in the new
|
|
|
|
* format. If not, we'll assume something odd went
|
|
|
|
* wrong, and hyper-cautiously do nothing.
|
|
|
|
*/
|
|
|
|
if (!strcmp(otherstr, key))
|
|
|
|
RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)otherstr,
|
|
|
|
strlen(otherstr) + 1);
|
|
|
|
}
|
2013-07-22 07:11:54 +00:00
|
|
|
|
|
|
|
sfree(oldstyle);
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RegCloseKey(rkey);
|
|
|
|
|
|
|
|
compare = strcmp(otherstr, key);
|
|
|
|
|
|
|
|
sfree(otherstr);
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf_free(regname);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
if (ret == ERROR_MORE_DATA ||
|
2019-09-08 19:29:00 +00:00
|
|
|
(ret == ERROR_SUCCESS && type == REG_SZ && compare))
|
|
|
|
return 2; /* key is different in registry */
|
2000-09-27 15:21:04 +00:00
|
|
|
else if (ret != ERROR_SUCCESS || type != REG_SZ)
|
2019-09-08 19:29:00 +00:00
|
|
|
return 1; /* key does not exist in registry */
|
2000-09-27 15:21:04 +00:00
|
|
|
else
|
2019-09-08 19:29:00 +00:00
|
|
|
return 0; /* key matched OK in registry */
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool have_ssh_host_key(const char *hostname, int port,
|
2019-09-08 19:29:00 +00:00
|
|
|
const char *keytype)
|
2015-05-29 21:40:50 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If we have a host key, verify_host_key will return 0 or 2.
|
|
|
|
* If we don't have one, it'll return 1.
|
|
|
|
*/
|
|
|
|
return verify_host_key(hostname, port, keytype, "") != 1;
|
|
|
|
}
|
|
|
|
|
2003-01-14 18:43:45 +00:00
|
|
|
void store_host_key(const char *hostname, int port,
|
2019-09-08 19:29:00 +00:00
|
|
|
const char *keytype, const char *key)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
strbuf *regname;
|
2000-09-27 15:21:04 +00:00
|
|
|
HKEY rkey;
|
|
|
|
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
regname = strbuf_new();
|
2000-09-28 08:37:10 +00:00
|
|
|
hostkey_regname(regname, hostname, port, keytype);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
|
2019-09-08 19:29:00 +00:00
|
|
|
&rkey) == ERROR_SUCCESS) {
|
|
|
|
RegSetValueEx(rkey, regname->s, 0, REG_SZ,
|
Rework mungestr() and unmungestr().
For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).
Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.
Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
2018-11-03 08:58:41 +00:00
|
|
|
(BYTE *)key, strlen(key) + 1);
|
2019-09-08 19:29:00 +00:00
|
|
|
RegCloseKey(rkey);
|
2005-05-20 21:52:07 +00:00
|
|
|
} /* else key does not exist in registry */
|
|
|
|
|
2018-11-07 21:12:21 +00:00
|
|
|
strbuf_free(regname);
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-01-09 18:05:17 +00:00
|
|
|
* Open (or delete) the random seed file.
|
2000-09-27 15:21:04 +00:00
|
|
|
*/
|
2007-01-09 18:05:17 +00:00
|
|
|
enum { DEL, OPEN_R, OPEN_W };
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
static bool try_random_seed(char const *path, int action, HANDLE *ret)
|
2007-01-09 18:05:17 +00:00
|
|
|
{
|
|
|
|
if (action == DEL) {
|
2013-07-22 07:11:44 +00:00
|
|
|
if (!DeleteFile(path) && GetLastError() != ERROR_FILE_NOT_FOUND) {
|
|
|
|
nonfatal("Unable to delete '%s': %s", path,
|
|
|
|
win_strerror(GetLastError()));
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
*ret = INVALID_HANDLE_VALUE;
|
|
|
|
return false; /* so we'll do the next ones too */
|
2007-01-09 18:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*ret = CreateFile(path,
|
2019-09-08 19:29:00 +00:00
|
|
|
action == OPEN_W ? GENERIC_WRITE : GENERIC_READ,
|
|
|
|
action == OPEN_W ? 0 : (FILE_SHARE_READ |
|
|
|
|
FILE_SHARE_WRITE),
|
|
|
|
NULL,
|
|
|
|
action == OPEN_W ? CREATE_ALWAYS : OPEN_EXISTING,
|
|
|
|
action == OPEN_W ? FILE_ATTRIBUTE_NORMAL : 0,
|
|
|
|
NULL);
|
2007-01-09 18:05:17 +00:00
|
|
|
|
|
|
|
return (*ret != INVALID_HANDLE_VALUE);
|
|
|
|
}
|
|
|
|
|
2019-06-30 14:21:08 +00:00
|
|
|
static bool try_random_seed_and_free(char *path, int action, HANDLE *hout)
|
|
|
|
{
|
|
|
|
bool retd = try_random_seed(path, action, hout);
|
|
|
|
sfree(path);
|
|
|
|
return retd;
|
|
|
|
}
|
|
|
|
|
2007-01-09 18:05:17 +00:00
|
|
|
static HANDLE access_random_seed(int action)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-09-27 15:21:04 +00:00
|
|
|
HKEY rkey;
|
2007-01-09 18:05:17 +00:00
|
|
|
HANDLE rethandle;
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2007-01-09 18:05:17 +00:00
|
|
|
/*
|
|
|
|
* Iterate over a selection of possible random seed paths until
|
|
|
|
* we find one that works.
|
2019-09-08 19:29:00 +00:00
|
|
|
*
|
2007-01-09 18:05:17 +00:00
|
|
|
* We do this iteration separately for reading and writing,
|
|
|
|
* meaning that we will automatically migrate random seed files
|
|
|
|
* if a better location becomes available (by reading from the
|
|
|
|
* best location in which we actually find one, and then
|
|
|
|
* writing to the best location in which we can _create_ one).
|
|
|
|
*/
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2007-01-09 18:05:17 +00:00
|
|
|
/*
|
|
|
|
* First, try the location specified by the user in the
|
|
|
|
* Registry, if any.
|
|
|
|
*/
|
2019-06-30 14:21:08 +00:00
|
|
|
{
|
|
|
|
char regpath[MAX_PATH + 1];
|
|
|
|
DWORD type, size = sizeof(regpath);
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==
|
|
|
|
ERROR_SUCCESS) {
|
|
|
|
int ret = RegQueryValueEx(rkey, "RandSeedFile",
|
|
|
|
0, &type, (BYTE *)regpath, &size);
|
|
|
|
RegCloseKey(rkey);
|
|
|
|
if (ret == ERROR_SUCCESS && type == REG_SZ &&
|
|
|
|
try_random_seed(regpath, action, &rethandle))
|
|
|
|
return rethandle;
|
|
|
|
}
|
2007-01-09 18:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Next, try the user's local Application Data directory,
|
|
|
|
* followed by their non-local one. This is found using the
|
|
|
|
* SHGetFolderPath function, which won't be present on all
|
|
|
|
* versions of Windows.
|
|
|
|
*/
|
|
|
|
if (!tried_shgetfolderpath) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/* This is likely only to bear fruit on systems with IE5+
|
|
|
|
* installed, or WinMe/2K+. There is some faffing with
|
|
|
|
* SHFOLDER.DLL we could do to try to find an equivalent
|
|
|
|
* on older versions of Windows if we cared enough.
|
|
|
|
* However, the invocation below requires IE5+ anyway,
|
|
|
|
* so stuff that. */
|
|
|
|
shell32_module = load_system32_dll("shell32.dll");
|
|
|
|
GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA);
|
|
|
|
tried_shgetfolderpath = true;
|
2007-01-09 18:05:17 +00:00
|
|
|
}
|
2009-11-08 19:22:28 +00:00
|
|
|
if (p_SHGetFolderPathA) {
|
2019-06-30 14:21:08 +00:00
|
|
|
char profile[MAX_PATH + 1];
|
2019-09-08 19:29:00 +00:00
|
|
|
if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
|
|
|
|
NULL, SHGFP_TYPE_CURRENT, profile)) &&
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
|
2019-06-30 14:21:08 +00:00
|
|
|
action, &rethandle))
|
|
|
|
return rethandle;
|
2007-01-09 18:05:17 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
|
|
|
|
NULL, SHGFP_TYPE_CURRENT, profile)) &&
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
|
2019-06-30 14:21:08 +00:00
|
|
|
action, &rethandle))
|
|
|
|
return rethandle;
|
2007-01-09 18:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Failing that, try %HOMEDRIVE%%HOMEPATH% as a guess at the
|
|
|
|
* user's home directory.
|
|
|
|
*/
|
|
|
|
{
|
2019-09-08 19:29:00 +00:00
|
|
|
char drv[MAX_PATH], path[MAX_PATH];
|
2019-06-30 14:21:08 +00:00
|
|
|
|
|
|
|
DWORD drvlen = GetEnvironmentVariable("HOMEDRIVE", drv, sizeof(drv));
|
|
|
|
DWORD pathlen = GetEnvironmentVariable("HOMEPATH", path, sizeof(path));
|
|
|
|
|
|
|
|
/* We permit %HOMEDRIVE% to expand to an empty string, but if
|
|
|
|
* %HOMEPATH% does that, we abort the attempt. Same if either
|
|
|
|
* variable overflows its buffer. */
|
|
|
|
if (drvlen == 0)
|
|
|
|
drv[0] = '\0';
|
|
|
|
|
|
|
|
if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 &&
|
|
|
|
try_random_seed_and_free(
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle))
|
2019-06-30 14:21:08 +00:00
|
|
|
return rethandle;
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
2007-01-09 18:05:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* And finally, fall back to C:\WINDOWS.
|
|
|
|
*/
|
2019-06-30 14:21:08 +00:00
|
|
|
{
|
|
|
|
char windir[MAX_PATH];
|
|
|
|
DWORD len = GetWindowsDirectory(windir, sizeof(windir));
|
|
|
|
if (len < lenof(windir) &&
|
|
|
|
try_random_seed_and_free(
|
Make dupcat() into a variadic macro.
Up until now, it's been a variadic _function_, whose argument list
consists of 'const char *' ASCIZ strings to concatenate, terminated by
one containing a null pointer. Now, that function is dupcat_fn(), and
it's wrapped by a C99 variadic _macro_ called dupcat(), which
automatically suffixes the null-pointer terminating argument.
This has three benefits. Firstly, it's just less effort at every call
site. Secondly, it protects against the risk of accidentally leaving
off the NULL, causing arbitrary words of stack memory to be
dereferenced as char pointers. And thirdly, it protects against the
more subtle risk of writing a bare 'NULL' as the terminating argument,
instead of casting it explicitly to a pointer. That last one is
necessary because C permits the macro NULL to expand to an integer
constant such as 0, so NULL by itself may not have pointer type, and
worse, it may not be marshalled in a variadic argument list in the
same way as a pointer. (For example, on a 64-bit machine it might only
occupy 32 bits. And yet, on another 64-bit platform, it might work
just fine, so that you don't notice the mistake!)
I was inspired to do this by happening to notice one of those bare
NULL terminators, and thinking I'd better check if there were any
more. Turned out there were quite a few. Now there are none.
2019-10-14 18:42:37 +00:00
|
|
|
dupcat(windir, "\\PUTTY.RND"), action, &rethandle))
|
2019-06-30 14:21:08 +00:00
|
|
|
return rethandle;
|
|
|
|
}
|
2007-01-09 18:05:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If even that failed, give up.
|
|
|
|
*/
|
|
|
|
return INVALID_HANDLE_VALUE;
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
void read_random_seed(noise_consumer_t consumer)
|
|
|
|
{
|
2007-01-09 18:05:17 +00:00
|
|
|
HANDLE seedf = access_random_seed(OPEN_R);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
if (seedf != INVALID_HANDLE_VALUE) {
|
2019-09-08 19:29:00 +00:00
|
|
|
while (1) {
|
|
|
|
char buf[1024];
|
|
|
|
DWORD len;
|
|
|
|
|
|
|
|
if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
|
|
|
|
consumer(buf, len);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CloseHandle(seedf);
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
void write_random_seed(void *data, int len)
|
|
|
|
{
|
2007-01-09 18:05:17 +00:00
|
|
|
HANDLE seedf = access_random_seed(OPEN_W);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
if (seedf != INVALID_HANDLE_VALUE) {
|
2019-09-08 19:29:00 +00:00
|
|
|
DWORD lenwritten;
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
WriteFile(seedf, data, len, &lenwritten, NULL);
|
|
|
|
CloseHandle(seedf);
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-23 17:32:28 +00:00
|
|
|
/*
|
|
|
|
* Internal function supporting the jump list registry code. All the
|
|
|
|
* functions to add, remove and read the list have substantially
|
|
|
|
* similar content, so this is a generalisation of all of them which
|
|
|
|
* transforms the list in the registry by prepending 'add' (if
|
|
|
|
* non-null), removing 'rem' from what's left (if non-null), and
|
|
|
|
* returning the resulting concatenated list of strings in 'out' (if
|
|
|
|
* non-null).
|
|
|
|
*/
|
|
|
|
static int transform_jumplist_registry
|
|
|
|
(const char *add, const char *rem, char **out)
|
|
|
|
{
|
|
|
|
int ret;
|
2018-09-14 07:45:42 +00:00
|
|
|
HKEY pjumplist_key;
|
2010-12-23 17:32:28 +00:00
|
|
|
DWORD type;
|
2015-08-11 12:34:11 +00:00
|
|
|
DWORD value_length;
|
2010-12-23 17:32:28 +00:00
|
|
|
char *old_value, *new_value;
|
|
|
|
char *piterator_old, *piterator_new, *piterator_tmp;
|
|
|
|
|
|
|
|
ret = RegCreateKeyEx(HKEY_CURRENT_USER, reg_jumplist_key, 0, NULL,
|
|
|
|
REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL,
|
|
|
|
&pjumplist_key, NULL);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
2019-09-08 19:29:00 +00:00
|
|
|
return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE;
|
2010-12-23 17:32:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get current list of saved sessions in the registry. */
|
|
|
|
value_length = 200;
|
|
|
|
old_value = snewn(value_length, char);
|
|
|
|
ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,
|
2017-02-03 19:37:59 +00:00
|
|
|
(BYTE *)old_value, &value_length);
|
2010-12-23 17:32:28 +00:00
|
|
|
/* When the passed buffer is too small, ERROR_MORE_DATA is
|
|
|
|
* returned and the required size is returned in the length
|
|
|
|
* argument. */
|
|
|
|
if (ret == ERROR_MORE_DATA) {
|
|
|
|
sfree(old_value);
|
|
|
|
old_value = snewn(value_length, char);
|
|
|
|
ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,
|
2017-02-03 19:37:59 +00:00
|
|
|
(BYTE *)old_value, &value_length);
|
2010-12-23 17:32:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == ERROR_FILE_NOT_FOUND) {
|
|
|
|
/* Value doesn't exist yet. Start from an empty value. */
|
|
|
|
*old_value = '\0';
|
|
|
|
*(old_value + 1) = '\0';
|
|
|
|
} else if (ret != ERROR_SUCCESS) {
|
|
|
|
/* Some non-recoverable error occurred. */
|
|
|
|
sfree(old_value);
|
|
|
|
RegCloseKey(pjumplist_key);
|
|
|
|
return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;
|
|
|
|
} else if (type != REG_MULTI_SZ) {
|
|
|
|
/* The value present in the registry has the wrong type: we
|
|
|
|
* try to delete it and start from an empty value. */
|
|
|
|
ret = RegDeleteValue(pjumplist_key, reg_jumplist_value);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
|
|
sfree(old_value);
|
|
|
|
RegCloseKey(pjumplist_key);
|
|
|
|
return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*old_value = '\0';
|
|
|
|
*(old_value + 1) = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check validity of registry data: REG_MULTI_SZ value must end
|
|
|
|
* with \0\0. */
|
|
|
|
piterator_tmp = old_value;
|
|
|
|
while (((piterator_tmp - old_value) < (value_length - 1)) &&
|
|
|
|
!(*piterator_tmp == '\0' && *(piterator_tmp+1) == '\0')) {
|
|
|
|
++piterator_tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((piterator_tmp - old_value) >= (value_length-1)) {
|
|
|
|
/* Invalid value. Start from an empty value. */
|
|
|
|
*old_value = '\0';
|
|
|
|
*(old_value + 1) = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Modify the list, if we're modifying.
|
|
|
|
*/
|
|
|
|
if (add || rem) {
|
|
|
|
/* Walk through the existing list and construct the new list of
|
|
|
|
* saved sessions. */
|
|
|
|
new_value = snewn(value_length + (add ? strlen(add) + 1 : 0), char);
|
|
|
|
piterator_new = new_value;
|
|
|
|
piterator_old = old_value;
|
|
|
|
|
|
|
|
/* First add the new item to the beginning of the list. */
|
|
|
|
if (add) {
|
|
|
|
strcpy(piterator_new, add);
|
|
|
|
piterator_new += strlen(piterator_new) + 1;
|
|
|
|
}
|
|
|
|
/* Now add the existing list, taking care to leave out the removed
|
|
|
|
* item, if it was already in the existing list. */
|
|
|
|
while (*piterator_old != '\0') {
|
|
|
|
if (!rem || strcmp(piterator_old, rem) != 0) {
|
|
|
|
/* Check if this is a valid session, otherwise don't add. */
|
2018-09-14 07:45:42 +00:00
|
|
|
settings_r *psettings_tmp = open_settings_r(piterator_old);
|
2010-12-23 17:32:28 +00:00
|
|
|
if (psettings_tmp != NULL) {
|
|
|
|
close_settings_r(psettings_tmp);
|
|
|
|
strcpy(piterator_new, piterator_old);
|
|
|
|
piterator_new += strlen(piterator_new) + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
piterator_old += strlen(piterator_old) + 1;
|
|
|
|
}
|
|
|
|
*piterator_new = '\0';
|
|
|
|
++piterator_new;
|
|
|
|
|
|
|
|
/* Save the new list to the registry. */
|
|
|
|
ret = RegSetValueEx(pjumplist_key, reg_jumplist_value, 0, REG_MULTI_SZ,
|
2017-02-03 19:37:59 +00:00
|
|
|
(BYTE *)new_value, piterator_new - new_value);
|
2010-12-23 17:32:28 +00:00
|
|
|
|
2010-12-29 13:35:44 +00:00
|
|
|
sfree(old_value);
|
2010-12-23 17:32:28 +00:00
|
|
|
old_value = new_value;
|
|
|
|
} else
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Either return or free the result.
|
|
|
|
*/
|
2013-07-22 07:11:58 +00:00
|
|
|
if (out && ret == ERROR_SUCCESS)
|
2010-12-23 17:32:28 +00:00
|
|
|
*out = old_value;
|
|
|
|
else
|
|
|
|
sfree(old_value);
|
|
|
|
|
|
|
|
/* Clean up and return. */
|
|
|
|
RegCloseKey(pjumplist_key);
|
|
|
|
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
|
|
return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;
|
|
|
|
} else {
|
|
|
|
return JUMPLISTREG_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Adds a new entry to the jumplist entries in the registry. */
|
|
|
|
int add_to_jumplist_registry(const char *item)
|
|
|
|
{
|
|
|
|
return transform_jumplist_registry(item, item, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Removes an item from the jumplist entries in the registry. */
|
|
|
|
int remove_from_jumplist_registry(const char *item)
|
|
|
|
{
|
|
|
|
return transform_jumplist_registry(NULL, item, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns the jumplist entries from the registry. Caller must free
|
|
|
|
* the returned pointer. */
|
|
|
|
char *get_jumplist_registry_entries (void)
|
|
|
|
{
|
|
|
|
char *list_value;
|
|
|
|
|
2013-07-22 07:11:58 +00:00
|
|
|
if (transform_jumplist_registry(NULL,NULL,&list_value) != JUMPLISTREG_OK) {
|
2019-09-08 19:29:00 +00:00
|
|
|
list_value = snewn(2, char);
|
2010-12-23 17:32:28 +00:00
|
|
|
*list_value = '\0';
|
|
|
|
*(list_value + 1) = '\0';
|
|
|
|
}
|
|
|
|
return list_value;
|
|
|
|
}
|
|
|
|
|
2000-09-27 15:21:04 +00:00
|
|
|
/*
|
|
|
|
* Recursively delete a registry key and everything under it.
|
|
|
|
*/
|
2001-05-06 14:35:20 +00:00
|
|
|
static void registry_recursive_remove(HKEY key)
|
|
|
|
{
|
2000-09-27 15:21:04 +00:00
|
|
|
DWORD i;
|
2001-05-06 14:35:20 +00:00
|
|
|
char name[MAX_PATH + 1];
|
2000-09-27 15:21:04 +00:00
|
|
|
HKEY subkey;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
|
2019-09-08 19:29:00 +00:00
|
|
|
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
|
|
|
|
registry_recursive_remove(subkey);
|
|
|
|
RegCloseKey(subkey);
|
|
|
|
}
|
|
|
|
RegDeleteKey(key, name);
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
void cleanup_all(void)
|
|
|
|
{
|
2000-09-27 15:21:04 +00:00
|
|
|
HKEY key;
|
|
|
|
int ret;
|
2001-05-06 14:35:20 +00:00
|
|
|
char name[MAX_PATH + 1];
|
2000-09-27 15:21:04 +00:00
|
|
|
|
|
|
|
/* ------------------------------------------------------------
|
2007-01-09 18:05:17 +00:00
|
|
|
* Wipe out the random seed file, in all of its possible
|
|
|
|
* locations.
|
2000-09-27 15:21:04 +00:00
|
|
|
*/
|
2007-01-09 18:05:17 +00:00
|
|
|
access_random_seed(DEL);
|
2000-09-27 15:21:04 +00:00
|
|
|
|
2010-12-26 20:00:45 +00:00
|
|
|
/* ------------------------------------------------------------
|
|
|
|
* Ask Windows to delete any jump list information associated
|
|
|
|
* with this installation of PuTTY.
|
|
|
|
*/
|
|
|
|
clear_jumplist();
|
|
|
|
|
2000-09-27 15:21:04 +00:00
|
|
|
/* ------------------------------------------------------------
|
|
|
|
* Destroy all registry information associated with PuTTY.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the main PuTTY registry key and remove everything in it.
|
|
|
|
*/
|
2001-05-06 14:35:20 +00:00
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==
|
2019-09-08 19:29:00 +00:00
|
|
|
ERROR_SUCCESS) {
|
|
|
|
registry_recursive_remove(key);
|
|
|
|
RegCloseKey(key);
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Now open the parent key and remove the PuTTY main key. Once
|
|
|
|
* we've done that, see if the parent key has any other
|
|
|
|
* children.
|
|
|
|
*/
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,
|
2019-09-08 19:29:00 +00:00
|
|
|
&key) == ERROR_SUCCESS) {
|
|
|
|
RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
|
|
|
|
ret = RegEnumKey(key, 0, name, sizeof(name));
|
|
|
|
RegCloseKey(key);
|
|
|
|
/*
|
|
|
|
* If the parent key had no other children, we must delete
|
|
|
|
* it in its turn. That means opening the _grandparent_
|
|
|
|
* key.
|
|
|
|
*/
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,
|
|
|
|
&key) == ERROR_SUCCESS) {
|
|
|
|
RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);
|
|
|
|
RegCloseKey(key);
|
|
|
|
}
|
|
|
|
}
|
2000-09-27 15:21:04 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Now we're done.
|
|
|
|
*/
|
|
|
|
}
|