diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 79a09eda..9a6a4ec6 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -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 diff --git a/windows/help.c b/windows/help.c index bff2ee5c..d087c722 100644 --- a/windows/help.c +++ b/windows/help.c @@ -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) { diff --git a/windows/platform.h b/windows/platform.h index 07a82f81..5760c7c6 100644 --- a/windows/platform.h +++ b/windows/platform.h @@ -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, diff --git a/windows/storage.c b/windows/storage.c index 077f0890..bf46cce0 100644 --- a/windows/storage.c +++ b/windows/storage.c @@ -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); } } } diff --git a/windows/utils/registry.c b/windows/utils/registry.c new file mode 100644 index 00000000..1f50e67a --- /dev/null +++ b/windows/utils/registry.c @@ -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; +} diff --git a/windows/utils/registry_get_string.c b/windows/utils/registry_get_string.c deleted file mode 100644 index c3745b92..00000000 --- a/windows/utils/registry_get_string.c +++ /dev/null @@ -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; -}