1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

windows/storage.c: factor out low-level Registry access.

All the fiddly business where you have to check that a thing exists,
make sure of its type, find its size, allocate some memory, and then
read it again properly (or, alternatively, loop round dealing with
ERROR_MORE_DATA) just doesn't belong at every call site. It's crying
out to be moved out into some separate utility functions that present
a more ergonomic API, so that the code that decides _which_ Registry
entries to read and what to do with them can concentrate on that.

So I've written a fresh set of registry API wrappers in windows/utils,
and simplified windows/storage.c as a result. The jump-list handling
code in particular is almost legible now!
This commit is contained in:
Simon Tatham 2022-04-22 10:01:01 +01:00
parent ffa25be185
commit 6143a50ed2
6 changed files with 319 additions and 321 deletions

View File

@ -26,7 +26,7 @@ add_sources_from_current_dir(utils
utils/open_for_write_would_lose_data.c
utils/pgp_fingerprints_msgbox.c
utils/platform_get_x_display.c
utils/registry_get_string.c
utils/registry.c
utils/request_file.c
utils/screenshot.c
utils/security.c

View File

@ -149,7 +149,7 @@ static bool find_chm_from_installation(void)
};
for (size_t i = 0; i < lenof(reg_paths); i++) {
char *filename = registry_get_string(
char *filename = get_reg_sz_simple(
HKEY_LOCAL_MACHINE, reg_paths[i], NULL);
if (filename) {

View File

@ -716,7 +716,20 @@ char *get_jumplist_registry_entries(void);
#define CLIPUI_DEFAULT_INS CLIPUI_EXPLICIT
/* In utils */
char *registry_get_string(HKEY root, const char *path, const char *leaf);
HKEY open_regkey_fn(bool create, HKEY base, const char *path, ...);
#define open_regkey(create, base, ...) \
open_regkey_fn(create, base, __VA_ARGS__, (const char *)NULL)
void close_regkey(HKEY key);
void del_regkey(HKEY key, const char *name);
char *enum_regkey(HKEY key, int index);
bool get_reg_dword(HKEY key, const char *name, DWORD *out);
bool put_reg_dword(HKEY key, const char *name, DWORD value);
char *get_reg_sz(HKEY key, const char *name);
bool put_reg_sz(HKEY key, const char *name, const char *str);
strbuf *get_reg_multi_sz(HKEY key, const char *name);
bool put_reg_multi_sz(HKEY key, const char *name, strbuf *str);
char *get_reg_sz_simple(HKEY key, const char *name, const char *leaf);
/* In cliloop.c */
typedef bool (*cliloop_pre_t)(void *vctx, const HANDLE **extra_handles,

View File

@ -33,28 +33,16 @@ struct settings_w {
settings_w *open_settings_w(const char *sessionname, char **errmsg)
{
HKEY subkey1, sesskey;
int ret;
strbuf *sb;
*errmsg = NULL;
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
sb = strbuf_new();
strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);
if (ret != ERROR_SUCCESS) {
strbuf_free(sb);
*errmsg = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s", puttystr);
return NULL;
}
ret = RegCreateKey(subkey1, sb->s, &sesskey);
RegCloseKey(subkey1);
if (ret != ERROR_SUCCESS) {
HKEY sesskey = open_regkey(true, HKEY_CURRENT_USER, puttystr, sb->s);
if (!sesskey) {
*errmsg = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s);
strbuf_free(sb);
@ -70,20 +58,18 @@ settings_w *open_settings_w(const char *sessionname, char **errmsg)
void write_setting_s(settings_w *handle, const char *key, const char *value)
{
if (handle)
RegSetValueEx(handle->sesskey, key, 0, REG_SZ, (CONST BYTE *)value,
1 + strlen(value));
put_reg_sz(handle->sesskey, key, value);
}
void write_setting_i(settings_w *handle, const char *key, int value)
{
if (handle)
RegSetValueEx(handle->sesskey, key, 0, REG_DWORD,
(CONST BYTE *) &value, sizeof(value));
put_reg_dword(handle->sesskey, key, value);
}
void close_settings_w(settings_w *handle)
{
RegCloseKey(handle->sesskey);
close_regkey(handle->sesskey);
sfree(handle);
}
@ -93,24 +79,12 @@ struct settings_r {
settings_r *open_settings_r(const char *sessionname)
{
HKEY subkey1, sesskey;
strbuf *sb;
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
sb = strbuf_new();
strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {
sesskey = NULL;
} else {
if (RegOpenKey(subkey1, sb->s, &sesskey) != ERROR_SUCCESS) {
sesskey = NULL;
}
RegCloseKey(subkey1);
}
HKEY sesskey = open_regkey(false, HKEY_CURRENT_USER, puttystr, sb->s);
strbuf_free(sb);
if (!sesskey)
@ -123,42 +97,15 @@ settings_r *open_settings_r(const char *sessionname)
char *read_setting_s(settings_r *handle, const char *key)
{
DWORD type, allocsize, size;
char *ret;
if (!handle)
return NULL;
/* Find out the type and size of the data. */
if (RegQueryValueEx(handle->sesskey, key, 0,
&type, NULL, &size) != ERROR_SUCCESS ||
type != REG_SZ)
return NULL;
allocsize = size+1; /* allow for an extra NUL if needed */
ret = snewn(allocsize, char);
if (RegQueryValueEx(handle->sesskey, key, 0,
&type, (BYTE *)ret, &size) != ERROR_SUCCESS ||
type != REG_SZ) {
sfree(ret);
return NULL;
}
assert(size < allocsize);
ret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx
* didn't supply one */
return ret;
return get_reg_sz(handle->sesskey, key);
}
int read_setting_i(settings_r *handle, const char *key, int defvalue)
{
DWORD type, val, size;
size = sizeof(val);
if (!handle ||
RegQueryValueEx(handle->sesskey, key, 0, &type,
(BYTE *) &val, &size) != ERROR_SUCCESS ||
size != sizeof(val) || type != REG_DWORD)
DWORD val;
if (!handle || !get_reg_dword(handle->sesskey, key, &val))
return defvalue;
else
return val;
@ -241,25 +188,23 @@ void write_setting_filename(settings_w *handle,
void close_settings_r(settings_r *handle)
{
if (handle) {
RegCloseKey(handle->sesskey);
close_regkey(handle->sesskey);
sfree(handle);
}
}
void del_settings(const char *sessionname)
{
HKEY subkey1;
strbuf *sb;
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, puttystr);
if (!rkey)
return;
sb = strbuf_new();
strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
RegDeleteKey(subkey1, sb->s);
del_regkey(rkey, sb->s);
strbuf_free(sb);
RegCloseKey(subkey1);
close_regkey(rkey);
remove_session_from_jumplist(sessionname);
}
@ -271,13 +216,11 @@ struct settings_e {
settings_e *enum_settings_start(void)
{
settings_e *ret;
HKEY key;
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)
HKEY key = open_regkey(false, HKEY_CURRENT_USER, puttystr);
if (!key)
return NULL;
ret = snew(settings_e);
settings_e *ret = snew(settings_e);
if (ret) {
ret->key = key;
ret->i = 0;
@ -288,30 +231,19 @@ settings_e *enum_settings_start(void)
bool enum_settings_next(settings_e *e, strbuf *sb)
{
size_t regbuf_size = MAX_PATH + 1;
char *regbuf = snewn(regbuf_size, char);
bool success;
while (1) {
DWORD retd = RegEnumKey(e->key, e->i, regbuf, regbuf_size);
if (retd != ERROR_MORE_DATA) {
success = (retd == ERROR_SUCCESS);
break;
}
sgrowarray(regbuf, regbuf_size, regbuf_size);
}
if (success)
unescape_registry_key(regbuf, sb);
char *name = enum_regkey(e->key, e->i);
if (!name)
return false;
unescape_registry_key(name, sb);
sfree(name);
e->i++;
sfree(regbuf);
return success;
return true;
}
void enum_settings_finish(settings_e *e)
{
RegCloseKey(e->key);
close_regkey(e->key);
sfree(e);
}
@ -325,48 +257,30 @@ static void hostkey_regname(strbuf *sb, const char *hostname,
int check_stored_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
char *otherstr;
strbuf *regname;
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.
* Read a saved key in from the registry and see what it says.
*/
regname = strbuf_new();
strbuf *regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) != ERROR_SUCCESS) {
HKEY rkey = open_regkey(false, HKEY_CURRENT_USER,
PUTTY_REG_POS "\\SshHostKeys");
if (!rkey) {
strbuf_free(regname);
return 1; /* key does not exist in registry */
}
readlen = len;
otherstr = snewn(len, char);
ret = RegQueryValueEx(rkey, regname->s, NULL,
&type, (BYTE *)otherstr, &readlen);
if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
!strcmp(keytype, "rsa")) {
char *otherstr = get_reg_sz(rkey, regname->s);
if (!otherstr && !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);
char *oldstyle = get_reg_sz(rkey, justhost);
if (ret == ERROR_SUCCESS && type == REG_SZ) {
if (oldstyle) {
/*
* The old format is two old-style bignums separated by
* a slash. An old-style bignum is made of groups of
@ -410,25 +324,23 @@ int check_stored_host_key(const char *hostname, int port,
* wrong, and hyper-cautiously do nothing.
*/
if (!strcmp(otherstr, key))
RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)otherstr,
strlen(otherstr) + 1);
put_reg_sz(rkey, regname->s, otherstr);
}
sfree(oldstyle);
}
RegCloseKey(rkey);
close_regkey(rkey);
compare = strcmp(otherstr, key);
int compare = otherstr ? strcmp(otherstr, key) : -1;
sfree(otherstr);
strbuf_free(regname);
if (ret == ERROR_MORE_DATA ||
(ret == ERROR_SUCCESS && type == REG_SZ && compare))
return 2; /* key is different in registry */
else if (ret != ERROR_SUCCESS || type != REG_SZ)
if (!otherstr)
return 1; /* key does not exist in registry */
else if (compare)
return 2; /* key is different in registry */
else
return 0; /* key matched OK in registry */
}
@ -446,17 +358,14 @@ bool have_ssh_host_key(const char *hostname, int port,
void store_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
strbuf *regname;
HKEY rkey;
regname = strbuf_new();
strbuf *regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) == ERROR_SUCCESS) {
RegSetValueEx(rkey, regname->s, 0, REG_SZ,
(BYTE *)key, strlen(key) + 1);
RegCloseKey(rkey);
HKEY rkey = open_regkey(true, HKEY_CURRENT_USER,
PUTTY_REG_POS "\\SshHostKeys");
if (rkey) {
put_reg_sz(rkey, regname->s, key);
close_regkey(rkey);
} /* else key does not exist in registry */
strbuf_free(regname);
@ -498,7 +407,6 @@ static bool try_random_seed_and_free(char *path, int action, HANDLE *hout)
static HANDLE access_random_seed(int action)
{
HKEY rkey;
HANDLE rethandle;
/*
@ -517,16 +425,16 @@ static HANDLE access_random_seed(int action)
* Registry, if any.
*/
{
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;
HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, PUTTY_REG_POS);
if (rkey) {
char *regpath = get_reg_sz(rkey, "RandSeedFile");
close_regkey(rkey);
if (regpath) {
bool success = try_random_seed(regpath, action, &rethandle);
sfree(regpath);
if (success)
return rethandle;
}
}
}
@ -644,129 +552,66 @@ void write_random_seed(void *data, int len)
static int transform_jumplist_registry
(const char *add, const char *rem, char **out)
{
int ret;
HKEY pjumplist_key;
DWORD type;
DWORD value_length;
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) {
HKEY rkey = open_regkey(true, HKEY_CURRENT_USER, reg_jumplist_key);
if (!rkey)
return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE;
}
/* 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,
(BYTE *)old_value, &value_length);
/* 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,
(BYTE *)old_value, &value_length);
}
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';
strbuf *oldlist = get_reg_multi_sz(rkey, reg_jumplist_value);
if (!oldlist) {
/* Start again with the empty list. */
oldlist = strbuf_new();
put_data(oldlist, "\0\0", 2);
}
/*
* Modify the list, if we're modifying.
*/
bool write_failure = false;
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;
BinarySource src[1];
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(oldlist));
strbuf *newlist = strbuf_new();
/* First add the new item to the beginning of the list. */
if (add) {
strcpy(piterator_new, add);
piterator_new += strlen(piterator_new) + 1;
}
if (add)
put_asciz(newlist, add);
/* 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) {
while (true) {
const char *olditem = get_asciz(src);
if (get_err(src))
break;
if (!rem || strcmp(olditem, rem) != 0) {
/* Check if this is a valid session, otherwise don't add. */
settings_r *psettings_tmp = open_settings_r(piterator_old);
settings_r *psettings_tmp = open_settings_r(olditem);
if (psettings_tmp != NULL) {
close_settings_r(psettings_tmp);
strcpy(piterator_new, piterator_old);
piterator_new += strlen(piterator_new) + 1;
put_asciz(newlist, olditem);
}
}
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,
(BYTE *)new_value, piterator_new - new_value);
write_failure = !put_reg_multi_sz(rkey, reg_jumplist_value, newlist);
sfree(old_value);
old_value = new_value;
} else
ret = ERROR_SUCCESS;
/*
* Either return or free the result.
*/
if (out && ret == ERROR_SUCCESS)
*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;
strbuf_free(oldlist);
oldlist = newlist;
}
close_regkey(rkey);
if (out && !write_failure)
*out = strbuf_to_str(oldlist);
else
strbuf_free(oldlist);
if (write_failure)
return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;
else
return JUMPLISTREG_OK;
}
/* Adds a new entry to the jumplist entries in the registry. */
@ -800,26 +645,22 @@ char *get_jumplist_registry_entries (void)
*/
static void registry_recursive_remove(HKEY key)
{
DWORD i;
char name[MAX_PATH + 1];
HKEY subkey;
char *name;
i = 0;
while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
DWORD i = 0;
while ((name = enum_regkey(key, i)) != NULL) {
HKEY subkey = open_regkey(false, key, name);
if (subkey) {
registry_recursive_remove(subkey);
RegCloseKey(subkey);
close_regkey(subkey);
}
RegDeleteKey(key, name);
del_regkey(key, name);
sfree(name);
}
}
void cleanup_all(void)
{
HKEY key;
int ret;
char name[MAX_PATH + 1];
/* ------------------------------------------------------------
* Wipe out the random seed file, in all of its possible
* locations.
@ -839,31 +680,34 @@ void cleanup_all(void)
/*
* Open the main PuTTY registry key and remove everything in it.
*/
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==
ERROR_SUCCESS) {
HKEY key = open_regkey(false, HKEY_CURRENT_USER, PUTTY_REG_POS);
if (key) {
registry_recursive_remove(key);
RegCloseKey(key);
close_regkey(key);
}
/*
* 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,
&key) == ERROR_SUCCESS) {
RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
ret = RegEnumKey(key, 0, name, sizeof(name));
RegCloseKey(key);
if ((key = open_regkey(false, HKEY_CURRENT_USER,
PUTTY_REG_PARENT)) != NULL) {
del_regkey(key, PUTTY_REG_PARENT_CHILD);
char *name = enum_regkey(key, 0);
close_regkey(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);
if (name) {
sfree(name);
} else {
if ((key = open_regkey(false, HKEY_CURRENT_USER,
PUTTY_REG_GPARENT)) != NULL) {
del_regkey(key, PUTTY_REG_GPARENT_CHILD);
close_regkey(key);
}
}
}

184
windows/utils/registry.c Normal file
View File

@ -0,0 +1,184 @@
/*
* Implement convenience wrappers on the awkward low-level functions
* for accessing the Windows registry.
*/
#include "putty.h"
HKEY open_regkey_fn(bool create, HKEY hk, const char *path, ...)
{
HKEY toret = NULL;
bool hk_needs_close = false;
va_list ap;
va_start(ap, path);
for (; path; path = va_arg(ap, const char *)) {
HKEY hk_sub = NULL;
LONG status;
if (create)
status = RegCreateKeyEx(
hk, path, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hk_sub, NULL);
else
status = RegOpenKeyEx(
hk, path, 0, KEY_READ | KEY_WRITE, &hk_sub);
if (status != ERROR_SUCCESS)
goto out;
if (hk_needs_close)
RegCloseKey(hk);
hk = hk_sub;
hk_needs_close = true;
}
toret = hk;
hk = NULL;
hk_needs_close = false;
out:
va_end(ap);
if (hk_needs_close)
RegCloseKey(hk);
return toret;
}
void close_regkey(HKEY key)
{
RegCloseKey(key);
}
void del_regkey(HKEY key, const char *name)
{
RegDeleteKey(key, name);
}
char *enum_regkey(HKEY key, int index)
{
size_t regbuf_size = MAX_PATH + 1;
char *regbuf = snewn(regbuf_size, char);
while (1) {
LONG status = RegEnumKey(key, index, regbuf, regbuf_size);
if (status == ERROR_SUCCESS)
return regbuf;
if (status != ERROR_MORE_DATA) {
sfree(regbuf);
return NULL;
}
sgrowarray(regbuf, regbuf_size, regbuf_size);
}
}
bool get_reg_dword(HKEY key, const char *name, DWORD *out)
{
DWORD type, size;
size = sizeof(*out);
if (RegQueryValueEx(key, name, 0, &type,
(BYTE *)out, &size) != ERROR_SUCCESS ||
size != sizeof(*out) || type != REG_DWORD)
return false;
else
return true;
}
bool put_reg_dword(HKEY key, const char *name, DWORD value)
{
return RegSetValueEx(key, name, 0, REG_DWORD, (CONST BYTE *) &value,
sizeof(value)) == ERROR_SUCCESS;
}
char *get_reg_sz(HKEY key, const char *name)
{
DWORD type, size;
if (RegQueryValueEx(key, name, 0, &type, NULL,
&size) != ERROR_SUCCESS || type != REG_SZ)
return NULL; /* not a string */
size_t allocsize = size+1; /* allow for an extra NUL if needed */
char *toret = snewn(allocsize, char);
if (RegQueryValueEx(key, name, 0, &type, (BYTE *)toret,
&size) != ERROR_SUCCESS || type != REG_SZ) {
sfree(toret);
return NULL;
}
assert(size < allocsize);
toret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx
* didn't supply one */
return toret;
}
bool put_reg_sz(HKEY key, const char *name, const char *str)
{
/* You have to store the trailing NUL as well */
return RegSetValueEx(key, name, 0, REG_SZ, (CONST BYTE *)str,
1 + strlen(str)) == ERROR_SUCCESS;
}
/*
* REG_MULTI_SZ items are stored as a concatenation of NUL-terminated
* strings, terminated in turn with an empty string, i.e. a second
* consecutive NUL.
*
* We represent these in their storage format, as a strbuf - but
* *without* the second consecutive NUL.
*
* So you can build up a new MULTI_SZ value in a strbuf by calling
* put_asciz once per output string and then put_reg_multi_sz; and you
* can consume one by initialising a BinarySource to the result of
* get_reg_multi_sz, and then calling get_asciz on it and assuming
* that !get_err(src) means you have a real output string.
*
* Also, calling strbuf_to_str on one of these will give you back a
* bare 'char *' with the same double-NUL termination, to pass back to
* a caller.
*/
strbuf *get_reg_multi_sz(HKEY key, const char *name)
{
DWORD type, size;
if (RegQueryValueEx(key, name, 0, &type, NULL,
&size) != ERROR_SUCCESS || type != REG_MULTI_SZ)
return NULL; /* not a string */
strbuf *toret = strbuf_new();
void *ptr = strbuf_append(toret, (size_t)size + 2);
if (RegQueryValueEx(key, name, 0, &type, (BYTE *)ptr,
&size) != ERROR_SUCCESS || type != REG_MULTI_SZ) {
strbuf_free(toret);
return NULL;
}
strbuf_shrink_to(toret, size);
/* Ensure we end with exactly one \0 */
while (strbuf_chomp(toret, '\0'));
put_byte(toret, '\0');
return toret;
}
bool put_reg_multi_sz(HKEY key, const char *name, strbuf *str)
{
/*
* Of course, to write our string list into the registry, we _do_
* have to include both trailing NULs. But this is easy, because a
* strbuf is also designed to hold a single string and make it
* conveniently accessible in NUL-terminated form, so it stores a
* NUL in its buffer just beyond its formal length. So we just
* include that extra byte in the data we write.
*/
return RegSetValueEx(key, name, 0, REG_MULTI_SZ, (CONST BYTE *)str->s,
str->len + 1) == ERROR_SUCCESS;
}
char *get_reg_sz_simple(HKEY key, const char *name, const char *leaf)
{
HKEY subkey = open_regkey(false, key, name);
if (!subkey)
return NULL;
char *toret = get_reg_sz(subkey, leaf);
RegCloseKey(subkey);
return toret;
}

View File

@ -1,43 +0,0 @@
/*
* Self-contained function to try to fetch a single string value from
* the Registry, and return it as a dynamically allocated C string.
*/
#include "putty.h"
char *registry_get_string(HKEY root, const char *path, const char *leaf)
{
HKEY key = root;
bool need_close_key = false;
char *toret = NULL, *str = NULL;
if (path) {
if (RegCreateKey(key, path, &key) != ERROR_SUCCESS)
goto out;
need_close_key = true;
}
DWORD type, size;
if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS)
goto out;
if (type != REG_SZ)
goto out;
str = snewn(size + 1, char);
DWORD size_got = size;
if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str,
&size_got) != ERROR_SUCCESS)
goto out;
if (type != REG_SZ || size_got > size)
goto out;
str[size_got] = '\0';
toret = str;
str = NULL;
out:
if (need_close_key)
RegCloseKey(key);
sfree(str);
return toret;
}