diff --git a/windows/winpgnt.c b/windows/winpgnt.c index 49cd311f..0786f7a6 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -59,6 +59,9 @@ static int requested_help; char *help_path; static char *putty_path; +/* CWD for "add key" file requester. */ +static filereq *keypath = NULL; + #define IDM_PUTTY 0x0060 #define IDM_SESSIONS_BASE 0x1000 #define IDM_SESSIONS_MAX 0x2000 @@ -1410,30 +1413,21 @@ static int cmpkeys_ssh2_asymm(void *av, void *bv) static void prompt_add_keyfile(void) { OPENFILENAME of; - char filename[FILENAME_MAX]; char *filelist = snewn(8192, char); - char *filewalker; - int n, dirlen; + if (!keypath) keypath = filereq_new(); memset(&of, 0, sizeof(of)); -#ifdef OPENFILENAME_SIZE_VERSION_400 - of.lStructSize = OPENFILENAME_SIZE_VERSION_400; -#else - of.lStructSize = sizeof(of); -#endif of.hwndOwner = main_hwnd; - of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0" - "All Files (*.*)\0*\0\0\0"; + of.lpstrFilter = FILTER_KEY_FILES; of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; of.lpstrFile = filelist; *filelist = '\0'; - of.nMaxFile = FILENAME_MAX; + of.nMaxFile = 8192; of.lpstrFileTitle = NULL; - of.lpstrInitialDir = NULL; of.lpstrTitle = "Select Private Key File"; of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER; - if (GetOpenFileName(&of)) { + if (request_file(keypath, &of, TRUE, FALSE)) { if(strlen(filelist) > of.nFileOffset) /* Only one filename returned? */ add_keyfile(filename_from_str(filelist)); @@ -1443,28 +1437,13 @@ static void prompt_add_keyfile(void) * rest the filenames. terminated with an * empty string. */ - filewalker = filelist; - dirlen = strlen(filewalker); - if(dirlen > FILENAME_MAX - 8) return; - memcpy(filename, filewalker, dirlen); - - filewalker += dirlen + 1; - filename[dirlen++] = '\\'; - - /* then go over names one by one */ - for(;;) { - n = strlen(filewalker) + 1; - /* end of the list */ - if(n == 1) - break; - /* too big, shouldn't happen */ - if(n + dirlen > FILENAME_MAX) - break; - - memcpy(filename + dirlen, filewalker, n); - filewalker += n; - + char *dir = filelist; + char *filewalker = filelist + strlen(dir) + 1; + while (*filewalker != '\0') { + char *filename = dupcat(dir, "\\", filewalker, NULL); add_keyfile(filename_from_str(filename)); + sfree(filename); + filewalker += strlen(filewalker) + 1; } } @@ -2223,6 +2202,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) DestroyMenu(systray_menu); } + if (keypath) filereq_free(keypath); + if (advapi) FreeLibrary(advapi); return msg.wParam; diff --git a/windows/winstuff.h b/windows/winstuff.h index a6b22a8d..f6fae661 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -173,6 +173,10 @@ struct ctlpos { /* * Exports from winutils.c. */ +typedef struct filereq_tag filereq; /* cwd for file requester */ +BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save); +filereq *filereq_new(void); +void filereq_free(filereq *state); void split_into_argv(char *, int *, char ***, char ***); /* diff --git a/windows/winutils.c b/windows/winutils.c index b2921245..2e91d8bb 100644 --- a/windows/winutils.c +++ b/windows/winutils.c @@ -5,16 +5,89 @@ #include #include #include +#include +#include "winstuff.h" #include "misc.h" #ifdef TESTMODE -/* Definitions to allow this module to be compiled standalone for testing. */ +/* Definitions to allow this module to be compiled standalone for testing + * split_into_argv(). */ #define smalloc malloc #define srealloc realloc #define sfree free #endif +/* + * GetOpenFileName/GetSaveFileName tend to muck around with the process' + * working directory on at least some versions of Windows. + * Here's a wrapper that gives more control over this, and hides a little + * bit of other grottiness. + */ + +struct filereq_tag { + TCHAR cwd[PATH_MAX]; +}; + +/* + * `of' is expected to be initialised with most interesting fields, but + * this function does some administrivia. (assume `of' was memset to 0) + * save==1 -> GetSaveFileName; save==0 -> GetOpenFileName + * `state' is optional. + */ +BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save) +{ + TCHAR cwd[PATH_MAX]; /* process CWD */ + BOOL ret; + + /* Get process CWD */ + if (preserve) { + DWORD r = GetCurrentDirectory(lenof(cwd), cwd); + if (r == 0 || r >= lenof(cwd)) + /* Didn't work, oh well. Stop trying to be clever. */ + preserve = 0; + } + + /* Open the file requester, maybe setting lpstrInitialDir */ + { +#ifdef OPENFILENAME_SIZE_VERSION_400 + of->lStructSize = OPENFILENAME_SIZE_VERSION_400; +#else + of->lStructSize = sizeof(*of); +#endif + of->lpstrInitialDir = (state && state->cwd[0]) ? state->cwd : NULL; + /* Actually put up the requester. */ + ret = save ? GetSaveFileName(of) : GetOpenFileName(of); + } + + /* Get CWD left by requester */ + if (state) { + DWORD r = GetCurrentDirectory(lenof(state->cwd), state->cwd); + if (r == 0 || r >= lenof(state->cwd)) + /* Didn't work, oh well. */ + state->cwd[0] = '\0'; + } + + /* Restore process CWD */ + if (preserve) + /* If it fails, there's not much we can do. */ + (void) SetCurrentDirectory(cwd); + + return ret; +} + +filereq *filereq_new(void) +{ + filereq *ret = snew(filereq); + ret->cwd[0] = '\0'; + return ret; +} + +void filereq_free(filereq *state) +{ + sfree(state); +} + /* * Split a complete command line into argc/argv, attempting to do * it exactly the same way Windows itself would do it (so that