diff --git a/settings.c b/settings.c index 1e945173..5c3f534a 100644 --- a/settings.c +++ b/settings.c @@ -1268,32 +1268,20 @@ static int sessioncmp(const void *av, const void *bv) void get_sesslist(struct sesslist *list, bool allocate) { - char otherbuf[2048]; - int buflen, bufsize, i; - char *p, *ret; + int i; + char *p; settings_e *handle; if (allocate) { + strbuf *sb = strbuf_new(); - buflen = bufsize = 0; - list->buffer = NULL; if ((handle = enum_settings_start()) != NULL) { - do { - ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf)); - if (ret) { - int len = strlen(otherbuf) + 1; - if (bufsize < buflen + len) { - bufsize = buflen + len + 2048; - list->buffer = sresize(list->buffer, bufsize, char); - } - strcpy(list->buffer + buflen, otherbuf); - buflen += strlen(list->buffer + buflen) + 1; - } - } while (ret); + while (enum_settings_next(handle, sb)) + put_byte(sb, '\0'); enum_settings_finish(handle); } - list->buffer = sresize(list->buffer, buflen + 1, char); - list->buffer[buflen] = '\0'; + put_byte(sb, '\0'); + list->buffer = strbuf_to_str(sb); /* * Now set up the list of sessions. Note that "Default diff --git a/storage.h b/storage.h index 6186e91d..41e4b5c9 100644 --- a/storage.h +++ b/storage.h @@ -69,7 +69,7 @@ void del_settings(const char *sessionname); * Enumerate all saved sessions. */ settings_e *enum_settings_start(void); -char *enum_settings_next(settings_e *handle, char *buffer, int buflen); +bool enum_settings_next(settings_e *handle, strbuf *out); void enum_settings_finish(settings_e *handle); /* ---------------------------------------------------------------------- diff --git a/unix/uxstore.c b/unix/uxstore.c index 0d0c582e..409a5787 100644 --- a/unix/uxstore.c +++ b/unix/uxstore.c @@ -33,15 +33,11 @@ enum { static const char hex[16] = "0123456789ABCDEF"; -static char *mungestr(const char *in) +static void make_session_filename(const char *in, strbuf *out) { - char *out, *ret; - if (!in || !*in) in = "Default Settings"; - ret = out = snewn(3*strlen(in)+1, char); - while (*in) { /* * There are remarkably few punctuation characters that @@ -54,21 +50,17 @@ static char *mungestr(const char *in) !(*in >= '0' && *in <= '9') && !(*in >= 'A' && *in <= 'Z') && !(*in >= 'a' && *in <= 'z')) { - *out++ = '%'; - *out++ = hex[((unsigned char) *in) >> 4]; - *out++ = hex[((unsigned char) *in) & 15]; + put_byte(out, '%'); + put_byte(out, hex[((unsigned char) *in) >> 4]); + put_byte(out, hex[((unsigned char) *in) & 15]); } else - *out++ = *in; + put_byte(out, *in); in++; } - *out = '\0'; - return ret; } -static char *unmungestr(const char *in) +static void decode_session_filename(const char *in, strbuf *out) { - char *out, *ret; - out = ret = snewn(strlen(in)+1, char); while (*in) { if (*in == '%' && in[1] && in[2]) { int i, j; @@ -78,14 +70,12 @@ static char *unmungestr(const char *in) j = in[2] - '0'; j -= (j > 9 ? 7 : 0); - *out++ = (i << 4) + j; + put_byte(out, (i << 4) + j); in += 3; } else { - *out++ = *in++; + put_byte(out, *in++); } } - *out = '\0'; - return ret; } static char *make_filename(int index, const char *subname) @@ -181,12 +171,12 @@ static char *make_filename(int index, const char *subname) return ret; } if (index == INDEX_SESSION) { - char *munged = mungestr(subname); + strbuf *sb = strbuf_new(); tmp = make_filename(INDEX_SESSIONDIR, NULL); - ret = dupprintf("%s/%s", tmp, munged); + strbuf_catf(sb, "%s/", tmp); sfree(tmp); - sfree(munged); - return ret; + make_session_filename(subname, sb); + return strbuf_to_str(sb); } if (index == INDEX_HOSTKEYS) { env = getenv("PUTTYSSHHOSTKEYS"); @@ -554,13 +544,12 @@ settings_e *enum_settings_start(void) return toret; } -char *enum_settings_next(settings_e *handle, char *buffer, int buflen) +bool enum_settings_next(settings_e *handle, strbuf *out) { struct dirent *de; struct stat st; char *fullpath; int maxlen, thislen, len; - char *unmunged; if (!handle->dp) return NULL; @@ -581,16 +570,13 @@ char *enum_settings_next(settings_e *handle, char *buffer, int buflen) if (stat(fullpath, &st) < 0 || !S_ISREG(st.st_mode)) continue; /* try another one */ - unmunged = unmungestr(de->d_name); - strncpy(buffer, unmunged, buflen); - buffer[buflen-1] = '\0'; - sfree(unmunged); + decode_session_filename(de->d_name, out); sfree(fullpath); - return buffer; + return true; } sfree(fullpath); - return NULL; + return false; } void enum_settings_finish(settings_e *handle) diff --git a/windows/winmisc.c b/windows/winmisc.c index bf60d4a5..86f413e8 100644 --- a/windows/winmisc.c +++ b/windows/winmisc.c @@ -625,3 +625,41 @@ bool open_for_write_would_lose_data(const Filename *fn) } return true; } + +void escape_registry_key(const char *in, strbuf *out) +{ + bool candot = false; + static const char hex[16] = "0123456789ABCDEF"; + + while (*in) { + if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' || + *in == '%' || *in < ' ' || *in > '~' || (*in == '.' + && !candot)) { + put_byte(out, '%'); + put_byte(out, hex[((unsigned char) *in) >> 4]); + put_byte(out, hex[((unsigned char) *in) & 15]); + } else + put_byte(out, *in); + in++; + candot = true; + } +} + +void unescape_registry_key(const char *in, strbuf *out) +{ + while (*in) { + if (*in == '%' && in[1] && in[2]) { + int i, j; + + i = in[1] - '0'; + i -= (i > 9 ? 7 : 0); + j = in[2] - '0'; + j -= (j > 9 ? 7 : 0); + + put_byte(out, (i << 4) + j); + in += 3; + } else { + put_byte(out, *in++); + } + } +} diff --git a/windows/winpgnt.c b/windows/winpgnt.c index ff105588..f8320f4a 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -87,32 +87,6 @@ void modalfatalbox(const char *fmt, ...) exit(1); } -/* Un-munge session names out of the registry. */ -static void unmungestr(char *in, char *out, int outlen) -{ - while (*in) { - if (*in == '%' && in[1] && in[2]) { - int i, j; - - i = in[1] - '0'; - i -= (i > 9 ? 7 : 0); - j = in[2] - '0'; - j -= (j > 9 ? 7 : 0); - - *out++ = (i << 4) + j; - if (!--outlen) - return; - in += 3; - } else { - *out++ = *in++; - if (!--outlen) - return; - } - } - *out = '\0'; - return; -} - /* Stubs needed to link against misc.c */ void queue_idempotent_callback(IdempotentCallback *ic) { assert(0); } @@ -687,6 +661,7 @@ static void update_sessions(void) HKEY hkey; TCHAR buf[MAX_PATH + 1]; MENUITEMINFO mii; + strbuf *sb; int index_key, index_menu; @@ -704,22 +679,25 @@ static void update_sessions(void) index_key = 0; index_menu = 0; + sb = strbuf_new(); while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) { - TCHAR session_name[MAX_PATH + 1]; - unmungestr(buf, session_name, MAX_PATH); if(strcmp(buf, PUTTY_DEFAULT) != 0) { + sb->len = 0; + unescape_registry_key(buf, sb); + memset(&mii, 0, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; mii.fType = MFT_STRING; mii.fState = MFS_ENABLED; mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE; - mii.dwTypeData = session_name; + mii.dwTypeData = sb->s; InsertMenuItem(session_menu, index_menu, true, &mii); index_menu++; } index_key++; } + strbuf_free(sb); RegCloseKey(hkey); diff --git a/windows/winstore.c b/windows/winstore.c index 5c9d1704..3b80e147 100644 --- a/windows/winstore.c +++ b/windows/winstore.c @@ -22,58 +22,11 @@ static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist"; static const char *const reg_jumplist_value = "Recent sessions"; static const char *const puttystr = PUTTY_REG_POS "\\Sessions"; -static const char hex[16] = "0123456789ABCDEF"; - static bool tried_shgetfolderpath = false; static HMODULE shell32_module = NULL; DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA, (HWND, int, HANDLE, DWORD, LPSTR)); -static void mungestr(const char *in, char *out) -{ - bool candot = false; - - while (*in) { - if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' || - *in == '%' || *in < ' ' || *in > '~' || (*in == '.' - && !candot)) { - *out++ = '%'; - *out++ = hex[((unsigned char) *in) >> 4]; - *out++ = hex[((unsigned char) *in) & 15]; - } else - *out++ = *in; - in++; - candot = true; - } - *out = '\0'; - return; -} - -static void unmungestr(const char *in, char *out, int outlen) -{ - while (*in) { - if (*in == '%' && in[1] && in[2]) { - int i, j; - - i = in[1] - '0'; - i -= (i > 9 ? 7 : 0); - j = in[2] - '0'; - j -= (j > 9 ? 7 : 0); - - *out++ = (i << 4) + j; - if (!--outlen) - return; - in += 3; - } else { - *out++ = *in++; - if (!--outlen) - return; - } - } - *out = '\0'; - return; -} - struct settings_w { HKEY sesskey; }; @@ -82,32 +35,32 @@ settings_w *open_settings_w(const char *sessionname, char **errmsg) { HKEY subkey1, sesskey; int ret; - char *p; + strbuf *sb; *errmsg = NULL; if (!sessionname || !*sessionname) sessionname = "Default Settings"; - p = snewn(3 * strlen(sessionname) + 1, char); - mungestr(sessionname, p); + sb = strbuf_new(); + escape_registry_key(sessionname, sb); ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1); if (ret != ERROR_SUCCESS) { - sfree(p); + strbuf_free(sb); *errmsg = dupprintf("Unable to create registry key\n" "HKEY_CURRENT_USER\\%s", puttystr); return NULL; } - ret = RegCreateKey(subkey1, p, &sesskey); + ret = RegCreateKey(subkey1, sb->s, &sesskey); RegCloseKey(subkey1); if (ret != ERROR_SUCCESS) { *errmsg = dupprintf("Unable to create registry key\n" - "HKEY_CURRENT_USER\\%s\\%s", puttystr, p); - sfree(p); + "HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s); + strbuf_free(sb); return NULL; } - sfree(p); + strbuf_free(sb); settings_w *toret = snew(settings_w); toret->sesskey = sesskey; @@ -141,24 +94,24 @@ struct settings_r { settings_r *open_settings_r(const char *sessionname) { HKEY subkey1, sesskey; - char *p; + strbuf *sb; if (!sessionname || !*sessionname) sessionname = "Default Settings"; - p = snewn(3 * strlen(sessionname) + 1, char); - mungestr(sessionname, p); + sb = strbuf_new(); + escape_registry_key(sessionname, sb); if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) { sesskey = NULL; } else { - if (RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) { + if (RegOpenKey(subkey1, sb->s, &sesskey) != ERROR_SUCCESS) { sesskey = NULL; } RegCloseKey(subkey1); } - sfree(p); + strbuf_free(sb); settings_r *toret = snew(settings_r); toret->sesskey = sesskey; @@ -291,15 +244,15 @@ void close_settings_r(settings_r *handle) void del_settings(const char *sessionname) { HKEY subkey1; - char *p; + strbuf *sb; if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) return; - p = snewn(3 * strlen(sessionname) + 1, char); - mungestr(sessionname, p); - RegDeleteKey(subkey1, p); - sfree(p); + sb = strbuf_new(); + escape_registry_key(sessionname, sb); + RegDeleteKey(subkey1, sb->s); + strbuf_free(sb); RegCloseKey(subkey1); @@ -328,18 +281,27 @@ settings_e *enum_settings_start(void) return ret; } -char *enum_settings_next(settings_e *e, char *buffer, int buflen) +bool enum_settings_next(settings_e *e, strbuf *sb) { - char *otherbuf; - otherbuf = snewn(3 * buflen, char); - if (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) { - unmungestr(otherbuf, buffer, buflen); - sfree(otherbuf); - return buffer; - } else { - sfree(otherbuf); - return NULL; + size_t regbuf_size = 256; + 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; + } + regbuf_size = regbuf_size * 5 / 4 + 256; + regbuf = sresize(regbuf, regbuf_size, char); } + + if (success) + unescape_registry_key(regbuf, sb); + + sfree(regbuf); + return success; } void enum_settings_finish(settings_e *e) @@ -348,21 +310,18 @@ void enum_settings_finish(settings_e *e) sfree(e); } -static void hostkey_regname(char *buffer, const char *hostname, +static void hostkey_regname(strbuf *sb, const char *hostname, int port, const char *keytype) { - int len; - strcpy(buffer, keytype); - strcat(buffer, "@"); - len = strlen(buffer); - len += sprintf(buffer + len, "%d:", port); - mungestr(hostname, buffer + strlen(buffer)); + strbuf_catf(sb, "%s@%d:", keytype, port); + escape_registry_key(hostname, sb); } int verify_host_key(const char *hostname, int port, const char *keytype, const char *key) { - char *otherstr, *regname; + char *otherstr; + strbuf *regname; int len; HKEY rkey; DWORD readlen; @@ -375,19 +334,18 @@ int verify_host_key(const char *hostname, int port, * Now read a saved key in from the registry and see what it * says. */ - regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char); - + regname = strbuf_new(); hostkey_regname(regname, hostname, port, keytype); if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", &rkey) != ERROR_SUCCESS) { - sfree(regname); + strbuf_free(regname); return 1; /* key does not exist in registry */ } readlen = len; otherstr = snewn(len, char); - ret = RegQueryValueEx(rkey, regname, NULL, + ret = RegQueryValueEx(rkey, regname->s, NULL, &type, (BYTE *)otherstr, &readlen); if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA && @@ -397,7 +355,7 @@ int verify_host_key(const char *hostname, int port, * another trick, which is to look up the _old_ key format * under just the hostname and translate that. */ - char *justhost = regname + 1 + strcspn(regname, ":"); + char *justhost = regname->s + 1 + strcspn(regname->s, ":"); char *oldstyle = snewn(len + 10, char); /* safety margin */ readlen = len; ret = RegQueryValueEx(rkey, justhost, NULL, &type, @@ -447,7 +405,7 @@ int verify_host_key(const char *hostname, int port, * wrong, and hyper-cautiously do nothing. */ if (!strcmp(otherstr, key)) - RegSetValueEx(rkey, regname, 0, REG_SZ, (BYTE *)otherstr, + RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)otherstr, strlen(otherstr) + 1); } @@ -459,7 +417,7 @@ int verify_host_key(const char *hostname, int port, compare = strcmp(otherstr, key); sfree(otherstr); - sfree(regname); + strbuf_free(regname); if (ret == ERROR_MORE_DATA || (ret == ERROR_SUCCESS && type == REG_SZ && compare)) @@ -483,16 +441,16 @@ 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) { - char *regname; + strbuf *regname; HKEY rkey; - regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char); - + regname = strbuf_new(); hostkey_regname(regname, hostname, port, keytype); if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", &rkey) == ERROR_SUCCESS) { - RegSetValueEx(rkey, regname, 0, REG_SZ, (BYTE *)key, strlen(key) + 1); + RegSetValueEx(rkey, regname->s, 0, REG_SZ, + (BYTE *)key, strlen(key) + 1); RegCloseKey(rkey); } /* else key does not exist in registry */ diff --git a/windows/winstuff.h b/windows/winstuff.h index c5d0ba24..4d735af1 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -544,6 +544,8 @@ HMODULE load_system32_dll(const char *libname); const char *win_strerror(int error); void restrict_process_acl(void); GLOBAL bool restricted_acl; +void escape_registry_key(const char *in, strbuf *out); +void unescape_registry_key(const char *in, strbuf *out); /* A few pieces of up-to-date Windows API definition needed for older * compilers. */