2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
25 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
30 #define IDI_MAINICON 200
31 #define IDI_TRAYICON 201
33 #define WM_SYSTRAY (WM_APP + 6)
34 #define WM_SYSTRAY2 (WM_APP + 7)
36 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
38 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
39 * wParam are used by Windows, and should be masked off, so we shouldn't
40 * attempt to store information in them. Hence all these identifiers have
41 * the low 4 bits clear. Also, identifiers should < 0xF000. */
43 #define IDM_CLOSE 0x0010
44 #define IDM_VIEWKEYS 0x0020
45 #define IDM_ADDKEY 0x0030
46 #define IDM_HELP 0x0040
47 #define IDM_ABOUT 0x0050
49 #define APPNAME "Pageant"
55 static HMENU systray_menu, session_menu;
56 static int already_running;
58 static char *putty_path;
60 /* CWD for "add key" file requester. */
61 static filereq *keypath = NULL;
63 #define IDM_PUTTY 0x0060
64 #define IDM_SESSIONS_BASE 0x1000
65 #define IDM_SESSIONS_MAX 0x2000
66 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
67 #define PUTTY_DEFAULT "Default%20Settings"
68 static int initial_menuitems_count;
71 * Print a modal (Really Bad) message box and perform a fatal exit.
73 void modalfatalbox(const char *fmt, ...)
79 buf = dupvprintf(fmt, ap);
81 MessageBox(hwnd, buf, "Pageant Fatal Error",
82 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
87 /* Un-munge session names out of the registry. */
88 static void unmungestr(char *in, char *out, int outlen)
91 if (*in == '%' && in[1] && in[2]) {
99 *out++ = (i << 4) + j;
113 static int has_security;
115 struct PassphraseProcStruct {
121 * Dialog-box function for the Licence box.
123 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
124 WPARAM wParam, LPARAM lParam)
128 SetDlgItemText(hwnd, 1000,
129 "Copyright 1997-2015 Simon Tatham.\r\n\r\n"
131 "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
132 "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
133 "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
134 "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\r\n\r\n"
136 "Permission is hereby granted, free of charge, to any person "
137 "obtaining a copy of this software and associated documentation "
138 "files (the ""Software""), to deal in the Software without restriction, "
139 "including without limitation the rights to use, copy, modify, merge, "
140 "publish, distribute, sublicense, and/or sell copies of the Software, "
141 "and to permit persons to whom the Software is furnished to do so, "
142 "subject to the following conditions:\r\n\r\n"
144 "The above copyright notice and this permission notice shall be "
145 "included in all copies or substantial portions of the Software.\r\n\r\n"
147 "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
148 "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
149 "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
150 "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
151 "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
152 "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
153 "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
154 "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
155 "CONNECTION WITH THE SOFTWARE OR THE USE OR "
156 "OTHER DEALINGS IN THE SOFTWARE."
160 switch (LOWORD(wParam)) {
175 * Dialog-box function for the About box.
177 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
178 WPARAM wParam, LPARAM lParam)
183 char *text = dupprintf
184 ("Pageant\r\n\r\n%s\r\n\r\n%s",
186 "\251 1997-2015 Simon Tatham. All rights reserved.");
187 SetDlgItemText(hwnd, 1000, text);
192 switch (LOWORD(wParam)) {
199 EnableWindow(hwnd, 0);
200 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
201 EnableWindow(hwnd, 1);
202 SetActiveWindow(hwnd);
214 static HWND passphrase_box;
217 * Dialog-box function for the passphrase box.
219 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
220 WPARAM wParam, LPARAM lParam)
222 static char **passphrase = NULL;
223 struct PassphraseProcStruct *p;
227 passphrase_box = hwnd;
231 { /* centre the window */
235 hw = GetDesktopWindow();
236 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
238 (rs.right + rs.left + rd.left - rd.right) / 2,
239 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
240 rd.right - rd.left, rd.bottom - rd.top, TRUE);
243 SetForegroundWindow(hwnd);
244 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
245 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
246 p = (struct PassphraseProcStruct *) lParam;
247 passphrase = p->passphrase;
249 SetDlgItemText(hwnd, 101, p->comment);
250 burnstr(*passphrase);
251 *passphrase = dupstr("");
252 SetDlgItemText(hwnd, 102, *passphrase);
255 switch (LOWORD(wParam)) {
265 case 102: /* edit box */
266 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
267 burnstr(*passphrase);
268 *passphrase = GetDlgItemText_alloc(hwnd, 102);
281 * Warn about the obsolescent key file format.
283 void old_keyfile_warning(void)
285 static const char mbtitle[] = "PuTTY Key File Warning";
286 static const char message[] =
287 "You are loading an SSH-2 private key which has an\n"
288 "old version of the file format. This means your key\n"
289 "file is not fully tamperproof. Future versions of\n"
290 "PuTTY may stop supporting this private key format,\n"
291 "so we recommend you convert your key to the new\n"
294 "You can perform this conversion by loading the key\n"
295 "into PuTTYgen and then saving it again.";
297 MessageBox(NULL, message, mbtitle, MB_OK);
301 * Update the visible key list.
303 void keylist_update(void)
306 struct ssh2_userkey *skey;
310 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
311 for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
312 char listentry[512], *p;
314 * Replace two spaces in the fingerprint with tabs, for
315 * nice alignment in the box.
317 strcpy(listentry, "ssh1\t");
318 p = listentry + strlen(listentry);
319 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
320 p = strchr(listentry, ' ');
323 p = strchr(listentry, ' ');
326 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
327 0, (LPARAM) listentry);
329 for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
333 * Replace spaces with tabs in the fingerprint prefix, for
334 * nice alignment in the list box, until we encounter a :
335 * meaning we're into the fingerprint proper.
337 p = ssh2_fingerprint(skey->alg, skey->data);
338 listentry = dupprintf("%s\t%s", p, skey->comment);
343 pos += strcspn(listentry + pos, " :");
344 if (listentry[pos] == ':' || !listentry[pos])
346 listentry[pos++] = '\t';
349 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
353 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
357 static void answer_msg(void *msgv)
359 unsigned char *msg = (unsigned char *)msgv;
364 msglen = GET_32BIT(msg);
365 if (msglen > AGENT_MAX_MSGLEN) {
366 reply = pageant_failure_msg(&replylen);
368 reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
369 if (replylen > AGENT_MAX_MSGLEN) {
370 smemclr(reply, replylen);
372 reply = pageant_failure_msg(&replylen);
377 * Windows Pageant answers messages in place, by overwriting the
378 * input message buffer.
380 memcpy(msg, reply, replylen);
381 smemclr(reply, replylen);
385 static void win_add_keyfile(Filename *filename)
389 char *passphrase = NULL;
392 * Try loading the key without a passphrase. (Or rather, without a
393 * _new_ passphrase; pageant_add_keyfile will take care of trying
394 * all the passphrases we've already stored.)
396 ret = pageant_add_keyfile(filename, NULL, &err);
397 if (ret == PAGEANT_ACTION_OK) {
399 } else if (ret == PAGEANT_ACTION_FAILURE) {
404 * OK, a passphrase is needed, and we've been given the key
405 * comment to use in the passphrase prompt.
409 struct PassphraseProcStruct pps;
411 pps.passphrase = &passphrase;
413 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
414 NULL, PassphraseProc, (LPARAM) &pps);
415 passphrase_box = NULL;
418 goto done; /* operation cancelled */
422 assert(passphrase != NULL);
424 ret = pageant_add_keyfile(filename, passphrase, &err);
425 if (ret == PAGEANT_ACTION_OK) {
427 } else if (ret == PAGEANT_ACTION_FAILURE) {
431 smemclr(passphrase, strlen(passphrase));
437 message_box(err, APPNAME, MB_OK | MB_ICONERROR,
438 HELPCTXID(errors_cantloadkey));
441 smemclr(passphrase, strlen(passphrase));
449 * Prompt for a key file to add, and add it.
451 static void prompt_add_keyfile(void)
454 char *filelist = snewn(8192, char);
456 if (!keypath) keypath = filereq_new();
457 memset(&of, 0, sizeof(of));
459 of.lpstrFilter = FILTER_KEY_FILES;
460 of.lpstrCustomFilter = NULL;
462 of.lpstrFile = filelist;
465 of.lpstrFileTitle = NULL;
466 of.lpstrTitle = "Select Private Key File";
467 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
468 if (request_file(keypath, &of, TRUE, FALSE)) {
469 if(strlen(filelist) > of.nFileOffset) {
470 /* Only one filename returned? */
471 Filename *fn = filename_from_str(filelist);
475 /* we are returned a bunch of strings, end to
476 * end. first string is the directory, the
477 * rest the filenames. terminated with an
480 char *dir = filelist;
481 char *filewalker = filelist + strlen(dir) + 1;
482 while (*filewalker != '\0') {
483 char *filename = dupcat(dir, "\\", filewalker, NULL);
484 Filename *fn = filename_from_str(filename);
488 filewalker += strlen(filewalker) + 1;
493 pageant_forget_passphrases();
499 * Dialog-box function for the key list box.
501 static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
502 WPARAM wParam, LPARAM lParam)
505 struct ssh2_userkey *skey;
512 { /* centre the window */
516 hw = GetDesktopWindow();
517 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
519 (rs.right + rs.left + rd.left - rd.right) / 2,
520 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
521 rd.right - rd.left, rd.bottom - rd.top, TRUE);
525 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
526 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
529 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
536 static int tabs[] = { 35, 75, 250 };
537 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
538 sizeof(tabs) / sizeof(*tabs),
544 switch (LOWORD(wParam)) {
550 case 101: /* add key */
551 if (HIWORD(wParam) == BN_CLICKED ||
552 HIWORD(wParam) == BN_DOUBLECLICKED) {
553 if (passphrase_box) {
554 MessageBeep(MB_ICONERROR);
555 SetForegroundWindow(passphrase_box);
558 prompt_add_keyfile();
561 case 102: /* remove key */
562 if (HIWORD(wParam) == BN_CLICKED ||
563 HIWORD(wParam) == BN_DOUBLECLICKED) {
568 /* our counter within the array of selected items */
571 /* get the number of items selected in the list */
573 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
575 /* none selected? that was silly */
576 if (numSelected == 0) {
581 /* get item indices in an array */
582 selectedArray = snewn(numSelected, int);
583 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
584 numSelected, (WPARAM)selectedArray);
586 itemNum = numSelected - 1;
587 rCount = pageant_count_ssh1_keys();
588 sCount = pageant_count_ssh2_keys();
590 /* go through the non-rsakeys until we've covered them all,
591 * and/or we're out of selected items to check. note that
592 * we go *backwards*, to avoid complications from deleting
593 * things hence altering the offset of subsequent items
595 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
596 skey = pageant_nth_ssh2_key(i);
598 if (selectedArray[itemNum] == rCount + i) {
599 pageant_delete_ssh2_key(skey);
600 skey->alg->freekey(skey->data);
606 /* do the same for the rsa keys */
607 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
608 rkey = pageant_nth_ssh1_key(i);
610 if(selectedArray[itemNum] == i) {
611 pageant_delete_ssh1_key(rkey);
618 sfree(selectedArray);
623 if (HIWORD(wParam) == BN_CLICKED ||
624 HIWORD(wParam) == BN_DOUBLECLICKED) {
625 launch_help(hwnd, WINHELP_CTX_pageant_general);
632 int id = ((LPHELPINFO)lParam)->iCtrlId;
633 const char *topic = NULL;
635 case 100: topic = WINHELP_CTX_pageant_keylist; break;
636 case 101: topic = WINHELP_CTX_pageant_addkey; break;
637 case 102: topic = WINHELP_CTX_pageant_remkey; break;
640 launch_help(hwnd, topic);
654 /* Set up a system tray icon */
655 static BOOL AddTrayIcon(HWND hwnd)
661 #ifdef NIM_SETVERSION
663 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
666 tnid.cbSize = sizeof(NOTIFYICONDATA);
668 tnid.uID = 1; /* unique within this systray use */
669 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
670 tnid.uCallbackMessage = WM_SYSTRAY;
671 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
672 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
674 res = Shell_NotifyIcon(NIM_ADD, &tnid);
676 if (hicon) DestroyIcon(hicon);
681 /* Update the saved-sessions menu. */
682 static void update_sessions(void)
686 TCHAR buf[MAX_PATH + 1];
689 int index_key, index_menu;
694 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
697 for(num_entries = GetMenuItemCount(session_menu);
698 num_entries > initial_menuitems_count;
700 RemoveMenu(session_menu, 0, MF_BYPOSITION);
705 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
706 TCHAR session_name[MAX_PATH + 1];
707 unmungestr(buf, session_name, MAX_PATH);
708 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
709 memset(&mii, 0, sizeof(mii));
710 mii.cbSize = sizeof(mii);
711 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
712 mii.fType = MFT_STRING;
713 mii.fState = MFS_ENABLED;
714 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
715 mii.dwTypeData = session_name;
716 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
724 if(index_menu == 0) {
725 mii.cbSize = sizeof(mii);
726 mii.fMask = MIIM_TYPE | MIIM_STATE;
727 mii.fType = MFT_STRING;
728 mii.fState = MFS_GRAYED;
729 mii.dwTypeData = _T("(No sessions)");
730 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
736 * Versions of Pageant prior to 0.61 expected this SID on incoming
737 * communications. For backwards compatibility, and more particularly
738 * for compatibility with derived works of PuTTY still using the old
739 * Pageant client code, we accept it as an alternative to the one
740 * returned from get_user_sid() in winpgntc.c.
742 PSID get_default_sid(void)
746 PSECURITY_DESCRIPTOR psd = NULL;
747 PSID sid = NULL, copy = NULL, ret = NULL;
749 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
750 GetCurrentProcessId())) == NULL)
753 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
754 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
757 sidlen = GetLengthSid(sid);
759 copy = (PSID)smalloc(sidlen);
761 if (!CopySid(sidlen, copy, sid))
764 /* Success. Move sid into the return value slot, and null it out
765 * to stop the cleanup code freeing it. */
781 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
782 WPARAM wParam, LPARAM lParam)
784 static int menuinprogress;
785 static UINT msgTaskbarCreated = 0;
789 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
792 if (message==msgTaskbarCreated) {
794 * Explorer has been restarted, so the tray icon will
802 if (lParam == WM_RBUTTONUP) {
804 GetCursorPos(&cursorpos);
805 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
806 } else if (lParam == WM_LBUTTONDBLCLK) {
807 /* Run the default menu item. */
808 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
810 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
814 if (!menuinprogress) {
817 SetForegroundWindow(hwnd);
818 TrackPopupMenu(systray_menu,
819 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
821 wParam, lParam, 0, hwnd, NULL);
827 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
829 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
831 MessageBox(NULL, "Unable to execute PuTTY!",
832 "Error", MB_OK | MB_ICONERROR);
837 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
838 SendMessage(hwnd, WM_CLOSE, 0, 0);
842 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
844 ShowWindow(keylist, SW_SHOWNORMAL);
847 * Sometimes the window comes up minimised / hidden for
848 * no obvious reason. Prevent this. This also brings it
849 * to the front if it's already present (the user
850 * selected View Keys because they wanted to _see_ the
853 SetForegroundWindow(keylist);
854 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
855 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
858 if (passphrase_box) {
859 MessageBeep(MB_ICONERROR);
860 SetForegroundWindow(passphrase_box);
863 prompt_add_keyfile();
867 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
869 ShowWindow(aboutbox, SW_SHOWNORMAL);
871 * Sometimes the window comes up minimised / hidden
872 * for no obvious reason. Prevent this.
874 SetForegroundWindow(aboutbox);
875 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
876 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
880 launch_help(hwnd, WINHELP_CTX_pageant_general);
884 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
886 TCHAR buf[MAX_PATH + 1];
887 TCHAR param[MAX_PATH + 1];
888 memset(&mii, 0, sizeof(mii));
889 mii.cbSize = sizeof(mii);
890 mii.fMask = MIIM_TYPE;
892 mii.dwTypeData = buf;
893 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
895 strcat(param, mii.dwTypeData);
896 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param,
897 _T(""), SW_SHOW) <= 32) {
898 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
899 MB_OK | MB_ICONERROR);
917 PSID mapowner, ourself, ourself2;
919 PSECURITY_DESCRIPTOR psd = NULL;
922 cds = (COPYDATASTRUCT *) lParam;
923 if (cds->dwData != AGENT_COPYDATA_ID)
924 return 0; /* not our message, mate */
925 mapname = (char *) cds->lpData;
926 if (mapname[cds->cbData - 1] != '\0')
927 return 0; /* failure to be ASCIZ! */
929 debug(("mapname is :%s:\n", mapname));
931 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
933 debug(("filemap is %p\n", filemap));
935 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
939 if ((ourself = get_user_sid()) == NULL) {
941 debug(("couldn't get user SID\n"));
943 CloseHandle(filemap);
947 if ((ourself2 = get_default_sid()) == NULL) {
949 debug(("couldn't get default SID\n"));
951 CloseHandle(filemap);
956 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
957 OWNER_SECURITY_INFORMATION,
958 &mapowner, NULL, NULL, NULL,
959 &psd) != ERROR_SUCCESS)) {
961 debug(("couldn't get owner info for filemap: %d\n",
964 CloseHandle(filemap);
971 LPTSTR ours, ours2, theirs;
972 ConvertSidToStringSid(mapowner, &theirs);
973 ConvertSidToStringSid(ourself, &ours);
974 ConvertSidToStringSid(ourself2, &ours2);
975 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
976 " theirs=%s\n", ours, ours2, theirs));
982 if (!EqualSid(mapowner, ourself) &&
983 !EqualSid(mapowner, ourself2)) {
984 CloseHandle(filemap);
988 return 0; /* security ID mismatch! */
991 debug(("security stuff matched\n"));
998 debug(("security APIs not present\n"));
1002 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1004 debug(("p is %p\n", p));
1007 for (i = 0; i < 5; i++)
1008 debug(("p[%d]=%02x\n", i,
1009 ((unsigned char *) p)[i]));
1016 CloseHandle(filemap);
1021 return DefWindowProc(hwnd, message, wParam, lParam);
1025 * Fork and Exec the command in cmdline. [DBW]
1027 void spawn_cmd(const char *cmdline, const char *args, int show)
1029 if (ShellExecute(NULL, _T("open"), cmdline,
1030 args, NULL, show) <= (HINSTANCE) 32) {
1032 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1033 (int)GetLastError());
1034 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1040 * This is a can't-happen stub, since Pageant never makes
1041 * asynchronous agent requests.
1043 void agent_schedule_callback(void (*callback)(void *, void *, int),
1044 void *callback_ctx, void *data, int len)
1046 assert(!"We shouldn't get here");
1049 void cleanup_exit(int code)
1055 int flags = FLAG_SYNCAGENT;
1057 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1061 const char *command = NULL;
1064 char **argv, **argstart;
1070 * Determine whether we're an NT system (should have security
1071 * APIs) or a non-NT system (don't do security).
1075 modalfatalbox("Windows refuses to report a version");
1077 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1078 has_security = TRUE;
1080 has_security = FALSE;
1085 * Attempt to get the security API we need.
1087 if (!got_advapi()) {
1089 "Unable to access security APIs. Pageant will\n"
1090 "not run, in case it causes a security breach.",
1091 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1096 "This program has been compiled for Win9X and will\n"
1097 "not run on NT, in case it causes a security breach.",
1098 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1104 * See if we can find our Help file.
1109 * Look for the PuTTY binary (we will enable the saved session
1110 * submenu if we find it).
1113 char b[2048], *p, *q, *r;
1115 GetModuleFileName(NULL, b, sizeof(b) - 16);
1117 p = strrchr(b, '\\');
1118 if (p && p >= r) r = p+1;
1119 q = strrchr(b, ':');
1120 if (q && q >= r) r = q+1;
1121 strcpy(r, "putty.exe");
1122 if ( (fp = fopen(b, "r")) != NULL) {
1123 putty_path = dupstr(b);
1130 * Find out if Pageant is already running.
1132 already_running = agent_exists();
1135 * Initialise the cross-platform Pageant code.
1137 if (!already_running) {
1142 * Process the command line and add keys as listed on it.
1144 split_into_argv(cmdline, &argc, &argv, &argstart);
1145 for (i = 0; i < argc; i++) {
1146 if (!strcmp(argv[i], "-pgpfp")) {
1149 } else if (!strcmp(argv[i], "-c")) {
1151 * If we see `-c', then the rest of the
1152 * command line should be treated as a
1153 * command to be spawned.
1156 command = argstart[i+1];
1161 Filename *fn = filename_from_str(argv[i]);
1162 win_add_keyfile(fn);
1169 * Forget any passphrase that we retained while going over
1170 * command line keyfiles.
1172 pageant_forget_passphrases();
1176 if (command[0] == '"')
1177 args = strchr(++command, '"');
1179 args = strchr(command, ' ');
1182 while(*args && isspace(*args)) args++;
1184 spawn_cmd(command, args, show);
1188 * If Pageant was already running, we leave now. If we haven't
1189 * even taken any auxiliary action (spawned a command or added
1192 if (already_running) {
1193 if (!command && !added_keys) {
1194 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1195 MB_ICONERROR | MB_OK);
1202 wndclass.lpfnWndProc = WndProc;
1203 wndclass.cbClsExtra = 0;
1204 wndclass.cbWndExtra = 0;
1205 wndclass.hInstance = inst;
1206 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1207 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1208 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1209 wndclass.lpszMenuName = NULL;
1210 wndclass.lpszClassName = APPNAME;
1212 RegisterClass(&wndclass);
1217 hwnd = CreateWindow(APPNAME, APPNAME,
1218 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1219 CW_USEDEFAULT, CW_USEDEFAULT,
1220 100, 100, NULL, NULL, inst, NULL);
1222 /* Set up a system tray icon */
1225 /* Accelerators used: nsvkxa */
1226 systray_menu = CreatePopupMenu();
1228 session_menu = CreateMenu();
1229 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1230 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1231 (UINT_PTR) session_menu, "&Saved Sessions");
1232 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1234 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1236 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1237 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1239 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1240 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1241 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1242 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1243 initial_menuitems_count = GetMenuItemCount(session_menu);
1245 /* Set the default menu item. */
1246 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1248 ShowWindow(hwnd, SW_HIDE);
1251 * Main message loop.
1253 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1254 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1255 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1256 TranslateMessage(&msg);
1257 DispatchMessage(&msg);
1261 /* Clean up the system tray icon */
1263 NOTIFYICONDATA tnid;
1265 tnid.cbSize = sizeof(NOTIFYICONDATA);
1269 Shell_NotifyIcon(NIM_DELETE, &tnid);
1271 DestroyMenu(systray_menu);
1274 if (keypath) filereq_free(keypath);
1276 cleanup_exit(msg.wParam);
1277 return msg.wParam; /* just in case optimiser complains */