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 CALLBACK LicenceProc(HWND hwnd, UINT msg,
124 WPARAM wParam, LPARAM lParam)
130 switch (LOWORD(wParam)) {
145 * Dialog-box function for the About box.
147 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
148 WPARAM wParam, LPARAM lParam)
152 SetDlgItemText(hwnd, 100, ver);
155 switch (LOWORD(wParam)) {
162 EnableWindow(hwnd, 0);
163 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
164 EnableWindow(hwnd, 1);
165 SetActiveWindow(hwnd);
177 static HWND passphrase_box;
180 * Dialog-box function for the passphrase box.
182 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
183 WPARAM wParam, LPARAM lParam)
185 static char **passphrase = NULL;
186 struct PassphraseProcStruct *p;
190 passphrase_box = hwnd;
194 { /* centre the window */
198 hw = GetDesktopWindow();
199 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
201 (rs.right + rs.left + rd.left - rd.right) / 2,
202 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
203 rd.right - rd.left, rd.bottom - rd.top, TRUE);
206 SetForegroundWindow(hwnd);
207 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
208 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
209 p = (struct PassphraseProcStruct *) lParam;
210 passphrase = p->passphrase;
212 SetDlgItemText(hwnd, 101, p->comment);
213 burnstr(*passphrase);
214 *passphrase = dupstr("");
215 SetDlgItemText(hwnd, 102, *passphrase);
218 switch (LOWORD(wParam)) {
228 case 102: /* edit box */
229 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
230 burnstr(*passphrase);
231 *passphrase = GetDlgItemText_alloc(hwnd, 102);
244 * Warn about the obsolescent key file format.
246 void old_keyfile_warning(void)
248 static const char mbtitle[] = "PuTTY Key File Warning";
249 static const char message[] =
250 "You are loading an SSH-2 private key which has an\n"
251 "old version of the file format. This means your key\n"
252 "file is not fully tamperproof. Future versions of\n"
253 "PuTTY may stop supporting this private key format,\n"
254 "so we recommend you convert your key to the new\n"
257 "You can perform this conversion by loading the key\n"
258 "into PuTTYgen and then saving it again.";
260 MessageBox(NULL, message, mbtitle, MB_OK);
264 * Update the visible key list.
266 void keylist_update(void)
269 struct ssh2_userkey *skey;
273 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
274 for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
275 char listentry[512], *p;
277 * Replace two spaces in the fingerprint with tabs, for
278 * nice alignment in the box.
280 strcpy(listentry, "ssh1\t");
281 p = listentry + strlen(listentry);
282 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
283 p = strchr(listentry, ' ');
286 p = strchr(listentry, ' ');
289 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
290 0, (LPARAM) listentry);
292 for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
296 * Replace spaces with tabs in the fingerprint prefix, for
297 * nice alignment in the list box, until we encounter a :
298 * meaning we're into the fingerprint proper.
300 p = ssh2_fingerprint(skey->alg, skey->data);
301 listentry = dupprintf("%s\t%s", p, skey->comment);
306 pos += strcspn(listentry + pos, " :");
307 if (listentry[pos] == ':' || !listentry[pos])
309 listentry[pos++] = '\t';
312 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
316 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
320 static void answer_msg(void *msgv)
322 unsigned char *msg = (unsigned char *)msgv;
327 msglen = GET_32BIT(msg);
328 if (msglen > AGENT_MAX_MSGLEN) {
329 reply = pageant_failure_msg(&replylen);
331 reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
332 if (replylen > AGENT_MAX_MSGLEN) {
333 smemclr(reply, replylen);
335 reply = pageant_failure_msg(&replylen);
340 * Windows Pageant answers messages in place, by overwriting the
341 * input message buffer.
343 memcpy(msg, reply, replylen);
344 smemclr(reply, replylen);
348 static void win_add_keyfile(Filename *filename)
352 char *passphrase = NULL;
355 * Try loading the key without a passphrase. (Or rather, without a
356 * _new_ passphrase; pageant_add_keyfile will take care of trying
357 * all the passphrases we've already stored.)
359 ret = pageant_add_keyfile(filename, NULL, &err);
360 if (ret == PAGEANT_ACTION_OK) {
362 } else if (ret == PAGEANT_ACTION_FAILURE) {
367 * OK, a passphrase is needed, and we've been given the key
368 * comment to use in the passphrase prompt.
372 struct PassphraseProcStruct pps;
374 pps.passphrase = &passphrase;
376 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
377 NULL, PassphraseProc, (LPARAM) &pps);
378 passphrase_box = NULL;
383 goto done; /* operation cancelled */
385 assert(passphrase != NULL);
387 ret = pageant_add_keyfile(filename, passphrase, &err);
388 if (ret == PAGEANT_ACTION_OK) {
390 } else if (ret == PAGEANT_ACTION_FAILURE) {
394 smemclr(passphrase, strlen(passphrase));
400 message_box(err, APPNAME, MB_OK | MB_ICONERROR,
401 HELPCTXID(errors_cantloadkey));
404 smemclr(passphrase, strlen(passphrase));
412 * Prompt for a key file to add, and add it.
414 static void prompt_add_keyfile(void)
417 char *filelist = snewn(8192, char);
419 if (!keypath) keypath = filereq_new();
420 memset(&of, 0, sizeof(of));
422 of.lpstrFilter = FILTER_KEY_FILES;
423 of.lpstrCustomFilter = NULL;
425 of.lpstrFile = filelist;
428 of.lpstrFileTitle = NULL;
429 of.lpstrTitle = "Select Private Key File";
430 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
431 if (request_file(keypath, &of, TRUE, FALSE)) {
432 if(strlen(filelist) > of.nFileOffset) {
433 /* Only one filename returned? */
434 Filename *fn = filename_from_str(filelist);
438 /* we are returned a bunch of strings, end to
439 * end. first string is the directory, the
440 * rest the filenames. terminated with an
443 char *dir = filelist;
444 char *filewalker = filelist + strlen(dir) + 1;
445 while (*filewalker != '\0') {
446 char *filename = dupcat(dir, "\\", filewalker, NULL);
447 Filename *fn = filename_from_str(filename);
451 filewalker += strlen(filewalker) + 1;
456 pageant_forget_passphrases();
462 * Dialog-box function for the key list box.
464 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
465 WPARAM wParam, LPARAM lParam)
468 struct ssh2_userkey *skey;
475 { /* centre the window */
479 hw = GetDesktopWindow();
480 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
482 (rs.right + rs.left + rd.left - rd.right) / 2,
483 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
484 rd.right - rd.left, rd.bottom - rd.top, TRUE);
488 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
489 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
492 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
499 static int tabs[] = { 35, 75, 250 };
500 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
501 sizeof(tabs) / sizeof(*tabs),
507 switch (LOWORD(wParam)) {
513 case 101: /* add key */
514 if (HIWORD(wParam) == BN_CLICKED ||
515 HIWORD(wParam) == BN_DOUBLECLICKED) {
516 if (passphrase_box) {
517 MessageBeep(MB_ICONERROR);
518 SetForegroundWindow(passphrase_box);
521 prompt_add_keyfile();
524 case 102: /* remove key */
525 if (HIWORD(wParam) == BN_CLICKED ||
526 HIWORD(wParam) == BN_DOUBLECLICKED) {
531 /* our counter within the array of selected items */
534 /* get the number of items selected in the list */
536 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
538 /* none selected? that was silly */
539 if (numSelected == 0) {
544 /* get item indices in an array */
545 selectedArray = snewn(numSelected, int);
546 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
547 numSelected, (WPARAM)selectedArray);
549 itemNum = numSelected - 1;
550 rCount = pageant_count_ssh1_keys();
551 sCount = pageant_count_ssh2_keys();
553 /* go through the non-rsakeys until we've covered them all,
554 * and/or we're out of selected items to check. note that
555 * we go *backwards*, to avoid complications from deleting
556 * things hence altering the offset of subsequent items
558 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
559 skey = pageant_nth_ssh2_key(i);
561 if (selectedArray[itemNum] == rCount + i) {
562 pageant_delete_ssh2_key(skey);
563 skey->alg->freekey(skey->data);
569 /* do the same for the rsa keys */
570 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
571 rkey = pageant_nth_ssh1_key(i);
573 if(selectedArray[itemNum] == i) {
574 pageant_delete_ssh1_key(rkey);
581 sfree(selectedArray);
586 if (HIWORD(wParam) == BN_CLICKED ||
587 HIWORD(wParam) == BN_DOUBLECLICKED) {
588 launch_help(hwnd, WINHELP_CTX_pageant_general);
595 int id = ((LPHELPINFO)lParam)->iCtrlId;
596 const char *topic = NULL;
598 case 100: topic = WINHELP_CTX_pageant_keylist; break;
599 case 101: topic = WINHELP_CTX_pageant_addkey; break;
600 case 102: topic = WINHELP_CTX_pageant_remkey; break;
603 launch_help(hwnd, topic);
617 /* Set up a system tray icon */
618 static BOOL AddTrayIcon(HWND hwnd)
624 #ifdef NIM_SETVERSION
626 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
629 tnid.cbSize = sizeof(NOTIFYICONDATA);
631 tnid.uID = 1; /* unique within this systray use */
632 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
633 tnid.uCallbackMessage = WM_SYSTRAY;
634 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
635 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
637 res = Shell_NotifyIcon(NIM_ADD, &tnid);
639 if (hicon) DestroyIcon(hicon);
644 /* Update the saved-sessions menu. */
645 static void update_sessions(void)
649 TCHAR buf[MAX_PATH + 1];
652 int index_key, index_menu;
657 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
660 for(num_entries = GetMenuItemCount(session_menu);
661 num_entries > initial_menuitems_count;
663 RemoveMenu(session_menu, 0, MF_BYPOSITION);
668 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
669 TCHAR session_name[MAX_PATH + 1];
670 unmungestr(buf, session_name, MAX_PATH);
671 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
672 memset(&mii, 0, sizeof(mii));
673 mii.cbSize = sizeof(mii);
674 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
675 mii.fType = MFT_STRING;
676 mii.fState = MFS_ENABLED;
677 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
678 mii.dwTypeData = session_name;
679 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
687 if(index_menu == 0) {
688 mii.cbSize = sizeof(mii);
689 mii.fMask = MIIM_TYPE | MIIM_STATE;
690 mii.fType = MFT_STRING;
691 mii.fState = MFS_GRAYED;
692 mii.dwTypeData = _T("(No sessions)");
693 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
699 * Versions of Pageant prior to 0.61 expected this SID on incoming
700 * communications. For backwards compatibility, and more particularly
701 * for compatibility with derived works of PuTTY still using the old
702 * Pageant client code, we accept it as an alternative to the one
703 * returned from get_user_sid() in winpgntc.c.
705 PSID get_default_sid(void)
709 PSECURITY_DESCRIPTOR psd = NULL;
710 PSID sid = NULL, copy = NULL, ret = NULL;
712 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
713 GetCurrentProcessId())) == NULL)
716 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
717 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
720 sidlen = GetLengthSid(sid);
722 copy = (PSID)smalloc(sidlen);
724 if (!CopySid(sidlen, copy, sid))
727 /* Success. Move sid into the return value slot, and null it out
728 * to stop the cleanup code freeing it. */
744 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
745 WPARAM wParam, LPARAM lParam)
747 static int menuinprogress;
748 static UINT msgTaskbarCreated = 0;
752 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
755 if (message==msgTaskbarCreated) {
757 * Explorer has been restarted, so the tray icon will
765 if (lParam == WM_RBUTTONUP) {
767 GetCursorPos(&cursorpos);
768 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
769 } else if (lParam == WM_LBUTTONDBLCLK) {
770 /* Run the default menu item. */
771 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
773 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
777 if (!menuinprogress) {
780 SetForegroundWindow(hwnd);
781 TrackPopupMenu(systray_menu,
782 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
784 wParam, lParam, 0, hwnd, NULL);
790 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
792 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
794 MessageBox(NULL, "Unable to execute PuTTY!",
795 "Error", MB_OK | MB_ICONERROR);
800 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
801 SendMessage(hwnd, WM_CLOSE, 0, 0);
805 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
807 ShowWindow(keylist, SW_SHOWNORMAL);
810 * Sometimes the window comes up minimised / hidden for
811 * no obvious reason. Prevent this. This also brings it
812 * to the front if it's already present (the user
813 * selected View Keys because they wanted to _see_ the
816 SetForegroundWindow(keylist);
817 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
818 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
821 if (passphrase_box) {
822 MessageBeep(MB_ICONERROR);
823 SetForegroundWindow(passphrase_box);
826 prompt_add_keyfile();
830 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
832 ShowWindow(aboutbox, SW_SHOWNORMAL);
834 * Sometimes the window comes up minimised / hidden
835 * for no obvious reason. Prevent this.
837 SetForegroundWindow(aboutbox);
838 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
839 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
843 launch_help(hwnd, WINHELP_CTX_pageant_general);
847 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
849 TCHAR buf[MAX_PATH + 1];
850 TCHAR param[MAX_PATH + 1];
851 memset(&mii, 0, sizeof(mii));
852 mii.cbSize = sizeof(mii);
853 mii.fMask = MIIM_TYPE;
855 mii.dwTypeData = buf;
856 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
858 strcat(param, mii.dwTypeData);
859 if((int)ShellExecute(hwnd, NULL, putty_path, param,
860 _T(""), SW_SHOW) <= 32) {
861 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
862 MB_OK | MB_ICONERROR);
880 PSID mapowner, ourself, ourself2;
882 PSECURITY_DESCRIPTOR psd = NULL;
885 cds = (COPYDATASTRUCT *) lParam;
886 if (cds->dwData != AGENT_COPYDATA_ID)
887 return 0; /* not our message, mate */
888 mapname = (char *) cds->lpData;
889 if (mapname[cds->cbData - 1] != '\0')
890 return 0; /* failure to be ASCIZ! */
892 debug(("mapname is :%s:\n", mapname));
894 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
896 debug(("filemap is %p\n", filemap));
898 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
902 if ((ourself = get_user_sid()) == NULL) {
904 debug(("couldn't get user SID\n"));
906 CloseHandle(filemap);
910 if ((ourself2 = get_default_sid()) == NULL) {
912 debug(("couldn't get default SID\n"));
914 CloseHandle(filemap);
919 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
920 OWNER_SECURITY_INFORMATION,
921 &mapowner, NULL, NULL, NULL,
922 &psd) != ERROR_SUCCESS)) {
924 debug(("couldn't get owner info for filemap: %d\n",
927 CloseHandle(filemap);
934 LPTSTR ours, ours2, theirs;
935 ConvertSidToStringSid(mapowner, &theirs);
936 ConvertSidToStringSid(ourself, &ours);
937 ConvertSidToStringSid(ourself2, &ours2);
938 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
939 " theirs=%s\n", ours, ours2, theirs));
945 if (!EqualSid(mapowner, ourself) &&
946 !EqualSid(mapowner, ourself2)) {
947 CloseHandle(filemap);
951 return 0; /* security ID mismatch! */
954 debug(("security stuff matched\n"));
961 debug(("security APIs not present\n"));
965 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
967 debug(("p is %p\n", p));
970 for (i = 0; i < 5; i++)
971 debug(("p[%d]=%02x\n", i,
972 ((unsigned char *) p)[i]));
979 CloseHandle(filemap);
984 return DefWindowProc(hwnd, message, wParam, lParam);
988 * Fork and Exec the command in cmdline. [DBW]
990 void spawn_cmd(const char *cmdline, const char *args, int show)
992 if (ShellExecute(NULL, _T("open"), cmdline,
993 args, NULL, show) <= (HINSTANCE) 32) {
995 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
996 (int)GetLastError());
997 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1003 * This is a can't-happen stub, since Pageant never makes
1004 * asynchronous agent requests.
1006 void agent_schedule_callback(void (*callback)(void *, void *, int),
1007 void *callback_ctx, void *data, int len)
1009 assert(!"We shouldn't get here");
1012 void cleanup_exit(int code)
1018 int flags = FLAG_SYNCAGENT;
1020 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1024 const char *command = NULL;
1027 char **argv, **argstart;
1033 * Determine whether we're an NT system (should have security
1034 * APIs) or a non-NT system (don't do security).
1038 modalfatalbox("Windows refuses to report a version");
1040 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1041 has_security = TRUE;
1043 has_security = FALSE;
1048 * Attempt to get the security API we need.
1050 if (!got_advapi()) {
1052 "Unable to access security APIs. Pageant will\n"
1053 "not run, in case it causes a security breach.",
1054 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1059 "This program has been compiled for Win9X and will\n"
1060 "not run on NT, in case it causes a security breach.",
1061 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1067 * See if we can find our Help file.
1072 * Look for the PuTTY binary (we will enable the saved session
1073 * submenu if we find it).
1076 char b[2048], *p, *q, *r;
1078 GetModuleFileName(NULL, b, sizeof(b) - 16);
1080 p = strrchr(b, '\\');
1081 if (p && p >= r) r = p+1;
1082 q = strrchr(b, ':');
1083 if (q && q >= r) r = q+1;
1084 strcpy(r, "putty.exe");
1085 if ( (fp = fopen(b, "r")) != NULL) {
1086 putty_path = dupstr(b);
1093 * Find out if Pageant is already running.
1095 already_running = agent_exists();
1098 * Initialise the cross-platform Pageant code.
1100 if (!already_running) {
1105 * Process the command line and add keys as listed on it.
1107 split_into_argv(cmdline, &argc, &argv, &argstart);
1108 for (i = 0; i < argc; i++) {
1109 if (!strcmp(argv[i], "-pgpfp")) {
1112 } else if (!strcmp(argv[i], "-c")) {
1114 * If we see `-c', then the rest of the
1115 * command line should be treated as a
1116 * command to be spawned.
1119 command = argstart[i+1];
1124 Filename *fn = filename_from_str(argv[i]);
1125 win_add_keyfile(fn);
1132 * Forget any passphrase that we retained while going over
1133 * command line keyfiles.
1135 pageant_forget_passphrases();
1139 if (command[0] == '"')
1140 args = strchr(++command, '"');
1142 args = strchr(command, ' ');
1145 while(*args && isspace(*args)) args++;
1147 spawn_cmd(command, args, show);
1151 * If Pageant was already running, we leave now. If we haven't
1152 * even taken any auxiliary action (spawned a command or added
1155 if (already_running) {
1156 if (!command && !added_keys) {
1157 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1158 MB_ICONERROR | MB_OK);
1165 wndclass.lpfnWndProc = WndProc;
1166 wndclass.cbClsExtra = 0;
1167 wndclass.cbWndExtra = 0;
1168 wndclass.hInstance = inst;
1169 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1170 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1171 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1172 wndclass.lpszMenuName = NULL;
1173 wndclass.lpszClassName = APPNAME;
1175 RegisterClass(&wndclass);
1180 hwnd = CreateWindow(APPNAME, APPNAME,
1181 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1182 CW_USEDEFAULT, CW_USEDEFAULT,
1183 100, 100, NULL, NULL, inst, NULL);
1185 /* Set up a system tray icon */
1188 /* Accelerators used: nsvkxa */
1189 systray_menu = CreatePopupMenu();
1191 session_menu = CreateMenu();
1192 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1193 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1194 (UINT) session_menu, "&Saved Sessions");
1195 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1197 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1199 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1200 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1202 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1203 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1204 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1205 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1206 initial_menuitems_count = GetMenuItemCount(session_menu);
1208 /* Set the default menu item. */
1209 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1211 ShowWindow(hwnd, SW_HIDE);
1214 * Main message loop.
1216 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1217 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1218 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1219 TranslateMessage(&msg);
1220 DispatchMessage(&msg);
1224 /* Clean up the system tray icon */
1226 NOTIFYICONDATA tnid;
1228 tnid.cbSize = sizeof(NOTIFYICONDATA);
1232 Shell_NotifyIcon(NIM_DELETE, &tnid);
1234 DestroyMenu(systray_menu);
1237 if (keypath) filereq_free(keypath);
1239 cleanup_exit(msg.wParam);
1240 return msg.wParam; /* just in case optimiser complains */