X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/dacbd0e88298088e0506eb653ae3d1f596085f67..ded53fce5af6818acc2da88653b56c5c94377b8e:/pageant.c diff --git a/pageant.c b/pageant.c index 66392753..888dd430 100644 --- a/pageant.c +++ b/pageant.c @@ -3,8 +3,12 @@ */ #include -#include /* FIXME */ -#include "putty.h" /* FIXME */ +#ifndef NO_SECURITY +#include +#endif +#include +#include +#include #include "ssh.h" #include "tree234.h" @@ -14,10 +18,18 @@ #define WM_XUSER (WM_USER + 0x2000) #define WM_SYSTRAY (WM_XUSER + 6) #define WM_SYSTRAY2 (WM_XUSER + 7) -#define WM_CLOSEMEM (WM_XUSER + 10) + +#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ + +/* + * FIXME: maybe some day we can sort this out ... + */ +#define AGENT_MAX_MSGLEN 8192 #define IDM_CLOSE 0x0010 #define IDM_VIEWKEYS 0x0020 +#define IDM_ADDKEY 0x0030 +#define IDM_ABOUT 0x0040 #define APPNAME "Pageant" @@ -30,12 +42,24 @@ #define SSH_AGENTC_ADD_RSA_IDENTITY 7 #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 -HINSTANCE instance; -HWND hwnd; -HWND keylist; -HMENU systray_menu; +extern char ver[]; -tree234 *rsakeys; +static HINSTANCE instance; +static HWND hwnd; +static HWND keylist; +static HWND aboutbox; +static HMENU systray_menu; + +static tree234 *rsakeys; + +static int has_security; +#ifndef NO_SECURITY +typedef DWORD (WINAPI *gsi_fn_t) + (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, + PSID *, PSID *, PACL *, PACL *, + PSECURITY_DESCRIPTOR *); +static gsi_fn_t getsecurityinfo; +#endif /* * We need this to link with the RSA code, because rsaencrypt() @@ -72,16 +96,81 @@ void logevent(char *msg) { #define PASSPHRASE_MAXLEN 512 +struct PassphraseProcStruct { + char *passphrase; + char *comment; +}; + +/* + * Dialog-box function for the Licence box. + */ +static int CALLBACK LicenceProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: + return 1; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 1); + return 0; + } + return 0; + case WM_CLOSE: + EndDialog(hwnd, 1); + return 0; + } + return 0; +} + +/* + * Dialog-box function for the About box. + */ +static int CALLBACK AboutProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: + SetDlgItemText (hwnd, 100, ver); + return 1; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + aboutbox = NULL; + DestroyWindow (hwnd); + return 0; + case 101: + EnableWindow(hwnd, 0); + DialogBox (instance, MAKEINTRESOURCE(214), NULL, LicenceProc); + EnableWindow(hwnd, 1); + SetActiveWindow(hwnd); + return 0; + } + return 0; + case WM_CLOSE: + aboutbox = NULL; + DestroyWindow (hwnd); + return 0; + } + return 0; +} + /* * Dialog-box function for the passphrase box. */ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static char *passphrase; + struct PassphraseProcStruct *p; switch (msg) { case WM_INITDIALOG: - passphrase = (char *)lParam; + SetForegroundWindow(hwnd); + SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + p = (struct PassphraseProcStruct *)lParam; + passphrase = p->passphrase; + if (p->comment) + SetDlgItemText(hwnd, 101, p->comment); *passphrase = 0; return 0; case WM_COMMAND: @@ -111,66 +200,88 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, } /* + * Update the visible key list. + */ +static void keylist_update(void) { + struct RSAKey *key; + enum234 e; + + if (keylist) { + SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0); + for (key = first234(rsakeys, &e); key; key = next234(&e)) { + char listentry[512], *p; + /* + * Replace two spaces in the fingerprint with tabs, for + * nice alignment in the box. + */ + rsa_fingerprint(listentry, sizeof(listentry), key); + p = strchr(listentry, ' '); if (p) *p = '\t'; + p = strchr(listentry, ' '); if (p) *p = '\t'; + SendDlgItemMessage (keylist, 100, LB_ADDSTRING, + 0, (LPARAM)listentry); + } + SendDlgItemMessage (keylist, 100, LB_SETCURSEL, (WPARAM) -1, 0); + } +} + +/* * This function loads a key from a file and adds it. */ -void add_keyfile(char *filename) { +static void add_keyfile(char *filename) { char passphrase[PASSPHRASE_MAXLEN]; struct RSAKey *key; int needs_pass; int ret; int attempts; + char *comment; + struct PassphraseProcStruct pps; - needs_pass = rsakey_encrypted(filename); + needs_pass = rsakey_encrypted(filename, &comment); attempts = 0; - key = malloc(sizeof(*key)); + key = smalloc(sizeof(*key)); + pps.passphrase = passphrase; + pps.comment = comment; do { if (needs_pass) { int dlgret; dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210), NULL, PassphraseProc, - (LPARAM)passphrase); + (LPARAM)&pps); if (!dlgret) { - free(key); + if (comment) sfree(comment); + sfree(key); return; /* operation cancelled */ } } else *passphrase = '\0'; - ret = loadrsakey(filename, key, passphrase); + ret = loadrsakey(filename, key, NULL, passphrase); attempts++; } while (ret == -1); + if (comment) sfree(comment); if (ret == 0) { - MessageBox(NULL, "Couldn't load public key.", APPNAME, + MessageBox(NULL, "Couldn't load private key.", APPNAME, MB_OK | MB_ICONERROR); - free(key); + sfree(key); return; } if (add234(rsakeys, key) != key) - free(key); /* already present, don't waste RAM */ + sfree(key); /* already present, don't waste RAM */ } /* * This is the main agent function that answers messages. */ -void answer_msg(void *in, int inlen, void **out, int *outlen) { - unsigned char *ret; - unsigned char *p = in; +static void answer_msg(void *msg) { + unsigned char *p = msg; + unsigned char *ret = msg; int type; - *out = NULL; /* default `no go' response */ - - /* - * Basic sanity checks. len >= 5, and len[0:4] holds len-4. - */ - if (inlen < 5 || GET_32BIT(p) != (unsigned long)(inlen-4)) - return; - /* * Get the message type. */ type = p[4]; p += 5; - switch (type) { case SSH_AGENTC_REQUEST_RSA_IDENTITIES: /* @@ -198,20 +309,20 @@ void answer_msg(void *in, int inlen, void **out, int *outlen) { * bytes for the key count. */ len += 5 + 4; - if ((ret = malloc(len)) != NULL) { - PUT_32BIT(ret, len-4); - ret[4] = SSH_AGENT_RSA_IDENTITIES_ANSWER; - PUT_32BIT(ret+5, nkeys); - p = ret + 5 + 4; - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus)); - p += 4; - p += ssh1_write_bignum(p, key->exponent); - p += ssh1_write_bignum(p, key->modulus); - PUT_32BIT(p, strlen(key->comment)); - memcpy(p+4, key->comment, strlen(key->comment)); - p += 4 + strlen(key->comment); - } + if (len > AGENT_MAX_MSGLEN) + goto failure; /* aaargh! too much stuff! */ + PUT_32BIT(ret, len-4); + ret[4] = SSH_AGENT_RSA_IDENTITIES_ANSWER; + PUT_32BIT(ret+5, nkeys); + p = ret + 5 + 4; + for (key = first234(rsakeys, &e); key; key = next234(&e)) { + PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus)); + p += 4; + p += ssh1_write_bignum(p, key->exponent); + p += ssh1_write_bignum(p, key->modulus); + PUT_32BIT(p, strlen(key->comment)); + memcpy(p+4, key->comment, strlen(key->comment)); + p += 4 + strlen(key->comment); } } break; @@ -258,19 +369,41 @@ void answer_msg(void *in, int inlen, void **out, int *outlen) { * bytes of MD5. */ len = 5 + 16; - if ((ret = malloc(len)) != NULL) { - PUT_32BIT(ret, len-4); - ret[4] = SSH_AGENT_RSA_RESPONSE; - memcpy(ret+5, response_md5, 16); - } + PUT_32BIT(ret, len-4); + ret[4] = SSH_AGENT_RSA_RESPONSE; + memcpy(ret+5, response_md5, 16); } break; -#if 0 /* FIXME: implement these */ case SSH_AGENTC_ADD_RSA_IDENTITY: /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ + { + struct RSAKey *key; + char *comment; + key = smalloc(sizeof(struct RSAKey)); + memset(key, 0, sizeof(key)); + p += makekey(p, key, NULL, 1); + p += makeprivate(p, key); + p += ssh1_read_bignum(p, NULL); /* p^-1 mod q */ + p += ssh1_read_bignum(p, NULL); /* p */ + p += ssh1_read_bignum(p, NULL); /* q */ + comment = smalloc(GET_32BIT(p)); + if (comment) { + memcpy(comment, p+4, GET_32BIT(p)); + key->comment = comment; + } + PUT_32BIT(ret, 1); + ret[4] = SSH_AGENT_FAILURE; + if (add234(rsakeys, key) == key) { + keylist_update(); + ret[4] = SSH_AGENT_SUCCESS; + } else { + freersakey(key); + sfree(key); + } + } break; case SSH_AGENTC_REMOVE_RSA_IDENTITY: /* @@ -278,30 +411,38 @@ void answer_msg(void *in, int inlen, void **out, int *outlen) { * perhaps SSH_AGENT_FAILURE if it wasn't in the list to * start with. */ + { + struct RSAKey reqkey, *key; + + p += makekey(p, &reqkey, NULL, 0); + key = find234(rsakeys, &reqkey, NULL); + freebn(reqkey.exponent); + freebn(reqkey.modulus); + PUT_32BIT(ret, 1); + ret[4] = SSH_AGENT_FAILURE; + if (key) { + del234(rsakeys, key); + keylist_update(); + freersakey(key); + ret[4] = SSH_AGENT_SUCCESS; + } + } break; -#endif default: failure: /* * Unrecognised message. Return SSH_AGENT_FAILURE. */ - if ((ret = malloc(5)) != NULL) { - PUT_32BIT(ret, 1); - ret[4] = SSH_AGENT_FAILURE; - } + PUT_32BIT(ret, 1); + ret[4] = SSH_AGENT_FAILURE; break; } - - if (ret) { - *out = ret; - *outlen = 4 + GET_32BIT(ret); - } } /* * Key comparison function for the 2-3-4 tree of RSA keys. */ -int cmpkeys(void *av, void *bv) { +static int cmpkeys(void *av, void *bv) { struct RSAKey *a = (struct RSAKey *)av; struct RSAKey *b = (struct RSAKey *)bv; Bignum am, bm; @@ -336,21 +477,50 @@ static void error(char *s) { } /* + * Prompt for a key file to add, and add it. + */ +static void prompt_add_keyfile(void) { + OPENFILENAME of; + char filename[FILENAME_MAX]; + memset(&of, 0, sizeof(of)); +#ifdef OPENFILENAME_SIZE_VERSION_400 + of.lStructSize = OPENFILENAME_SIZE_VERSION_400; +#else + of.lStructSize = sizeof(of); +#endif + of.hwndOwner = hwnd; + of.lpstrFilter = "All Files\0*\0\0\0"; + of.lpstrCustomFilter = NULL; + of.nFilterIndex = 1; + of.lpstrFile = filename; *filename = '\0'; + of.nMaxFile = sizeof(filename); + of.lpstrFileTitle = NULL; + of.lpstrInitialDir = NULL; + of.lpstrTitle = "Select Private Key File"; + of.Flags = 0; + if (GetOpenFileName(&of)) { + add_keyfile(filename); + keylist_update(); + } +} + +/* * Dialog-box function for the key list box. */ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { enum234 e; struct RSAKey *key; - OPENFILENAME of; - char filename[FILENAME_MAX]; switch (msg) { case WM_INITDIALOG: - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - SendDlgItemMessage (hwnd, 100, LB_ADDSTRING, - 0, (LPARAM) key->comment); - } + keylist = hwnd; + { + static int tabs[2] = {25, 175}; + SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS, 2, + (LPARAM) tabs); + } + keylist_update(); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -362,38 +532,14 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, case 101: /* add key */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { - memset(&of, 0, sizeof(of)); -#ifdef OPENFILENAME_SIZE_VERSION_400 - of.lStructSize = OPENFILENAME_SIZE_VERSION_400; -#else - of.lStructSize = sizeof(of); -#endif - of.hwndOwner = hwnd; - of.lpstrFilter = "All Files\0*\0\0\0"; - of.lpstrCustomFilter = NULL; - of.nFilterIndex = 1; - of.lpstrFile = filename; *filename = '\0'; - of.nMaxFile = sizeof(filename); - of.lpstrFileTitle = NULL; - of.lpstrInitialDir = NULL; - of.lpstrTitle = "Select Public Key File"; - of.Flags = 0; - if (GetOpenFileName(&of)) { - add_keyfile(filename); - } - SendDlgItemMessage(hwnd, 100, LB_RESETCONTENT, 0, 0); - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - SendDlgItemMessage (hwnd, 100, LB_ADDSTRING, - 0, (LPARAM) key->comment); - } - SendDlgItemMessage (hwnd, 100, LB_SETCURSEL, (WPARAM) -1, 0); + prompt_add_keyfile(); } return 0; case 102: /* remove key */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0); - if (n == LB_ERR || n == 0) { + if (n == LB_ERR) { MessageBeep(0); break; } @@ -402,12 +548,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, break; del234(rsakeys, key); freersakey(key); free(key); - SendDlgItemMessage(hwnd, 100, LB_RESETCONTENT, 0, 0); - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - SendDlgItemMessage (hwnd, 100, LB_ADDSTRING, - 0, (LPARAM) key->comment); - } - SendDlgItemMessage (hwnd, 100, LB_SETCURSEL, (WPARAM) -1, 0); + keylist_update(); } return 0; } @@ -431,6 +572,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, POINT cursorpos; GetCursorPos(&cursorpos); PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y); + } else if (lParam == WM_LBUTTONDBLCLK) { + /* Equivalent to IDM_VIEWKEYS. */ + PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0); } break; case WM_SYSTRAY2: @@ -455,6 +599,30 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, keylist = CreateDialog (instance, MAKEINTRESOURCE(211), NULL, KeyListProc); ShowWindow (keylist, SW_SHOWNORMAL); + /* + * Sometimes the window comes up minimised / hidden + * for no obvious reason. Prevent this. + */ + SetForegroundWindow(keylist); + SetWindowPos (keylist, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + } + break; + case IDM_ADDKEY: + prompt_add_keyfile(); + break; + case IDM_ABOUT: + if (!aboutbox) { + aboutbox = CreateDialog (instance, MAKEINTRESOURCE(213), + NULL, AboutProc); + ShowWindow (aboutbox, SW_SHOWNORMAL); + /* + * Sometimes the window comes up minimised / hidden + * for no obvious reason. Prevent this. + */ + SetForegroundWindow(aboutbox); + SetWindowPos (aboutbox, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } break; } @@ -465,45 +633,85 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, case WM_COPYDATA: { COPYDATASTRUCT *cds; - void *in, *out, *ret; - int inlen, outlen; - HANDLE filemap; - char mapname[64]; - int id; + char *mapname; + void *p; + HANDLE filemap, proc; + PSID mapowner, procowner; + PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL; + int ret = 0; cds = (COPYDATASTRUCT *)lParam; - /* - * FIXME: use dwData somehow. - */ - in = cds->lpData; - inlen = cds->cbData; - answer_msg(in, inlen, &out, &outlen); - if (out) { - id = 0; - do { - sprintf(mapname, "PageantReply%08x", ++id); - filemap = CreateFileMapping(INVALID_HANDLE_VALUE, - NULL, PAGE_READWRITE, - 0, outlen+sizeof(int), - mapname); - } while (filemap == INVALID_HANDLE_VALUE); - ret = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, - outlen+sizeof(int)); - if (ret) { - *((int *)ret) = outlen; - memcpy(((int *)ret)+1, out, outlen); - UnmapViewOfFile(ret); - return id; + if (cds->dwData != AGENT_COPYDATA_ID) + return 0; /* not our message, mate */ + mapname = (char *)cds->lpData; + if (mapname[cds->cbData - 1] != '\0') + return 0; /* failure to be ASCIZ! */ +#ifdef DEBUG_IPC + debug(("mapname is :%s:\r\n", mapname)); +#endif + filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname); +#ifdef DEBUG_IPC + debug(("filemap is %p\r\n", filemap)); +#endif + if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) { + int rc; +#ifndef NO_SECURITY + if (has_security) { + if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE, + GetCurrentProcessId())) == NULL) { +#ifdef DEBUG_IPC + debug(("couldn't get handle for process\r\n")); +#endif + return 0; + } + if (getsecurityinfo(proc, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION, + &procowner, NULL, NULL, NULL, + &psd2) != ERROR_SUCCESS) { +#ifdef DEBUG_IPC + debug(("couldn't get owner info for process\r\n")); +#endif + CloseHandle(proc); + return 0; /* unable to get security info */ + } + CloseHandle(proc); + if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION, + &mapowner, NULL, NULL, NULL, + &psd1) != ERROR_SUCCESS)) { +#ifdef DEBUG_IPC + debug(("couldn't get owner info for filemap: %d\r\n", rc)); +#endif + return 0; + } +#ifdef DEBUG_IPC + debug(("got security stuff\r\n")); +#endif + if (!EqualSid(mapowner, procowner)) + return 0; /* security ID mismatch! */ +#ifdef DEBUG_IPC + debug(("security stuff matched\r\n")); +#endif + LocalFree(psd1); + LocalFree(psd2); + } else { +#ifdef DEBUG_IPC + debug(("security APIs not present\r\n")); +#endif } - } else - return 0; /* invalid request */ +#endif + p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); +#ifdef DEBUG_IPC + debug(("p is %p\r\n", p)); + {int i; for(i=0;i<5;i++)debug(("p[%d]=%02x\r\n", i, ((unsigned char *)p)[i]));} +#endif + answer_msg(p); + ret = 1; + UnmapViewOfFile(p); + } + CloseHandle(filemap); + return ret; } - break; - case WM_CLOSEMEM: - /* - * FIXME! - */ - break; } return DefWindowProc (hwnd, message, wParam, lParam); @@ -512,6 +720,53 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; + OSVERSIONINFO osi; + HMODULE advapi; + + /* + * Determine whether we're an NT system (should have security + * APIs) or a non-NT system (don't do security). + */ + memset(&osi, 0, sizeof(OSVERSIONINFO)); + osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (GetVersionEx(&osi) && osi.dwPlatformId==VER_PLATFORM_WIN32_NT) { + has_security = TRUE; + } else + has_security = FALSE; + + if (has_security) { +#ifndef NO_SECURITY + /* + * Attempt to ge the security API we need. + */ + advapi = LoadLibrary("ADVAPI32.DLL"); + getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo"); + if (!getsecurityinfo) { + MessageBox(NULL, + "Unable to access security APIs. Pageant will\n" + "not run, in case it causes a security breach.", + "Pageant Fatal Error", MB_ICONERROR | MB_OK); + return 1; + } +#else + MessageBox(NULL, + "This program has been compiled for Win9X and will\n" + "not run on NT, in case it causes a security breach.", + "Pageant Fatal Error", MB_ICONERROR | MB_OK); + return 1; +#endif + } else + advapi = NULL; + + /* + * First bomb out totally if we are already running. + */ + if (FindWindow("Pageant", "Pageant")) { + MessageBox(NULL, "Pageant is already running", "Pageant Error", + MB_ICONERROR | MB_OK); + if (advapi) FreeLibrary(advapi); + return 0; + } instance = inst; @@ -563,8 +818,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { DestroyIcon(hicon); systray_menu = CreatePopupMenu(); - AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "View Keys"); - AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "Terminate"); + /* accelerators used: vkxa */ + AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys"); + AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key"); + AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About"); + AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit"); } ShowWindow (hwnd, SW_HIDE); @@ -576,16 +834,28 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { /* * Process the command line and add RSA keys as listed on it. - * FIXME: we don't support spaces in filenames here. We should. */ { - char *p = cmdline; + char *p; + int inquotes = 0; + p = cmdline; while (*p) { while (*p && isspace(*p)) p++; if (*p && !isspace(*p)) { - char *q = p; - while (*p && !isspace(*p)) p++; - if (*p) *p++ = '\0'; + char *q = p, *pp = p; + while (*p && (inquotes || !isspace(*p))) + { + if (*p == '"') { + inquotes = !inquotes; + p++; + continue; + } + *pp++ = *p++; + } + if (*pp) { + if (*p) p++; + *pp++ = '\0'; + } add_keyfile(q); } } @@ -612,5 +882,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { DestroyMenu(systray_menu); } + if (advapi) FreeLibrary(advapi); exit(msg.wParam); }