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] == ':')
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)
748 static int menuinprogress;
749 static UINT msgTaskbarCreated = 0;
753 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
756 if (message==msgTaskbarCreated) {
758 * Explorer has been restarted, so the tray icon will
766 if (lParam == WM_RBUTTONUP) {
768 GetCursorPos(&cursorpos);
769 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
770 } else if (lParam == WM_LBUTTONDBLCLK) {
771 /* Run the default menu item. */
772 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
774 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
778 if (!menuinprogress) {
781 SetForegroundWindow(hwnd);
782 ret = TrackPopupMenu(systray_menu,
783 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
785 wParam, lParam, 0, hwnd, NULL);
791 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
793 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
795 MessageBox(NULL, "Unable to execute PuTTY!",
796 "Error", MB_OK | MB_ICONERROR);
801 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
802 SendMessage(hwnd, WM_CLOSE, 0, 0);
806 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
808 ShowWindow(keylist, SW_SHOWNORMAL);
811 * Sometimes the window comes up minimised / hidden for
812 * no obvious reason. Prevent this. This also brings it
813 * to the front if it's already present (the user
814 * selected View Keys because they wanted to _see_ the
817 SetForegroundWindow(keylist);
818 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
819 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
822 if (passphrase_box) {
823 MessageBeep(MB_ICONERROR);
824 SetForegroundWindow(passphrase_box);
827 prompt_add_keyfile();
831 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
833 ShowWindow(aboutbox, SW_SHOWNORMAL);
835 * Sometimes the window comes up minimised / hidden
836 * for no obvious reason. Prevent this.
838 SetForegroundWindow(aboutbox);
839 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
840 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
844 launch_help(hwnd, WINHELP_CTX_pageant_general);
848 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
850 TCHAR buf[MAX_PATH + 1];
851 TCHAR param[MAX_PATH + 1];
852 memset(&mii, 0, sizeof(mii));
853 mii.cbSize = sizeof(mii);
854 mii.fMask = MIIM_TYPE;
856 mii.dwTypeData = buf;
857 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
859 strcat(param, mii.dwTypeData);
860 if((int)ShellExecute(hwnd, NULL, putty_path, param,
861 _T(""), SW_SHOW) <= 32) {
862 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
863 MB_OK | MB_ICONERROR);
881 PSID mapowner, ourself, ourself2;
883 PSECURITY_DESCRIPTOR psd = NULL;
886 cds = (COPYDATASTRUCT *) lParam;
887 if (cds->dwData != AGENT_COPYDATA_ID)
888 return 0; /* not our message, mate */
889 mapname = (char *) cds->lpData;
890 if (mapname[cds->cbData - 1] != '\0')
891 return 0; /* failure to be ASCIZ! */
893 debug(("mapname is :%s:\n", mapname));
895 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
897 debug(("filemap is %p\n", filemap));
899 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
903 if ((ourself = get_user_sid()) == NULL) {
905 debug(("couldn't get user SID\n"));
907 CloseHandle(filemap);
911 if ((ourself2 = get_default_sid()) == NULL) {
913 debug(("couldn't get default SID\n"));
915 CloseHandle(filemap);
920 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
921 OWNER_SECURITY_INFORMATION,
922 &mapowner, NULL, NULL, NULL,
923 &psd) != ERROR_SUCCESS)) {
925 debug(("couldn't get owner info for filemap: %d\n",
928 CloseHandle(filemap);
935 LPTSTR ours, ours2, theirs;
936 ConvertSidToStringSid(mapowner, &theirs);
937 ConvertSidToStringSid(ourself, &ours);
938 ConvertSidToStringSid(ourself2, &ours2);
939 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
940 " theirs=%s\n", ours, ours2, theirs));
946 if (!EqualSid(mapowner, ourself) &&
947 !EqualSid(mapowner, ourself2)) {
948 CloseHandle(filemap);
952 return 0; /* security ID mismatch! */
955 debug(("security stuff matched\n"));
962 debug(("security APIs not present\n"));
966 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
968 debug(("p is %p\n", p));
971 for (i = 0; i < 5; i++)
972 debug(("p[%d]=%02x\n", i,
973 ((unsigned char *) p)[i]));
980 CloseHandle(filemap);
985 return DefWindowProc(hwnd, message, wParam, lParam);
989 * Fork and Exec the command in cmdline. [DBW]
991 void spawn_cmd(const char *cmdline, const char *args, int show)
993 if (ShellExecute(NULL, _T("open"), cmdline,
994 args, NULL, show) <= (HINSTANCE) 32) {
996 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
997 (int)GetLastError());
998 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1004 * This is a can't-happen stub, since Pageant never makes
1005 * asynchronous agent requests.
1007 void agent_schedule_callback(void (*callback)(void *, void *, int),
1008 void *callback_ctx, void *data, int len)
1010 assert(!"We shouldn't get here");
1013 void cleanup_exit(int code)
1019 int flags = FLAG_SYNCAGENT;
1021 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1025 const char *command = NULL;
1028 char **argv, **argstart;
1034 * Determine whether we're an NT system (should have security
1035 * APIs) or a non-NT system (don't do security).
1039 modalfatalbox("Windows refuses to report a version");
1041 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1042 has_security = TRUE;
1044 has_security = FALSE;
1049 * Attempt to get the security API we need.
1051 if (!got_advapi()) {
1053 "Unable to access security APIs. Pageant will\n"
1054 "not run, in case it causes a security breach.",
1055 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1060 "This program has been compiled for Win9X and will\n"
1061 "not run on NT, in case it causes a security breach.",
1062 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1068 * See if we can find our Help file.
1073 * Look for the PuTTY binary (we will enable the saved session
1074 * submenu if we find it).
1077 char b[2048], *p, *q, *r;
1079 GetModuleFileName(NULL, b, sizeof(b) - 16);
1081 p = strrchr(b, '\\');
1082 if (p && p >= r) r = p+1;
1083 q = strrchr(b, ':');
1084 if (q && q >= r) r = q+1;
1085 strcpy(r, "putty.exe");
1086 if ( (fp = fopen(b, "r")) != NULL) {
1087 putty_path = dupstr(b);
1094 * Find out if Pageant is already running.
1096 already_running = agent_exists();
1099 * Initialise the cross-platform Pageant code.
1101 if (!already_running) {
1106 * Process the command line and add keys as listed on it.
1108 split_into_argv(cmdline, &argc, &argv, &argstart);
1109 for (i = 0; i < argc; i++) {
1110 if (!strcmp(argv[i], "-pgpfp")) {
1113 } else if (!strcmp(argv[i], "-c")) {
1115 * If we see `-c', then the rest of the
1116 * command line should be treated as a
1117 * command to be spawned.
1120 command = argstart[i+1];
1125 Filename *fn = filename_from_str(argv[i]);
1126 win_add_keyfile(fn);
1133 * Forget any passphrase that we retained while going over
1134 * command line keyfiles.
1136 pageant_forget_passphrases();
1140 if (command[0] == '"')
1141 args = strchr(++command, '"');
1143 args = strchr(command, ' ');
1146 while(*args && isspace(*args)) args++;
1148 spawn_cmd(command, args, show);
1152 * If Pageant was already running, we leave now. If we haven't
1153 * even taken any auxiliary action (spawned a command or added
1156 if (already_running) {
1157 if (!command && !added_keys) {
1158 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1159 MB_ICONERROR | MB_OK);
1166 wndclass.lpfnWndProc = WndProc;
1167 wndclass.cbClsExtra = 0;
1168 wndclass.cbWndExtra = 0;
1169 wndclass.hInstance = inst;
1170 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1171 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1172 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1173 wndclass.lpszMenuName = NULL;
1174 wndclass.lpszClassName = APPNAME;
1176 RegisterClass(&wndclass);
1181 hwnd = CreateWindow(APPNAME, APPNAME,
1182 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1183 CW_USEDEFAULT, CW_USEDEFAULT,
1184 100, 100, NULL, NULL, inst, NULL);
1186 /* Set up a system tray icon */
1189 /* Accelerators used: nsvkxa */
1190 systray_menu = CreatePopupMenu();
1192 session_menu = CreateMenu();
1193 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1194 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1195 (UINT) session_menu, "&Saved Sessions");
1196 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1198 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1200 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1201 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1203 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1204 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1205 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1206 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1207 initial_menuitems_count = GetMenuItemCount(session_menu);
1209 /* Set the default menu item. */
1210 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1212 ShowWindow(hwnd, SW_HIDE);
1215 * Main message loop.
1217 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1218 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1219 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1220 TranslateMessage(&msg);
1221 DispatchMessage(&msg);
1225 /* Clean up the system tray icon */
1227 NOTIFYICONDATA tnid;
1229 tnid.cbSize = sizeof(NOTIFYICONDATA);
1233 Shell_NotifyIcon(NIM_DELETE, &tnid);
1235 DestroyMenu(systray_menu);
1238 if (keypath) filereq_free(keypath);
1240 cleanup_exit(msg.wParam);
1241 return msg.wParam; /* just in case optimiser complains */