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(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);
302 fp_len = strlen(listentry);
307 pos += strcspn(listentry + pos, " :");
308 if (listentry[pos] == ':')
310 listentry[pos++] = '\t';
313 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
317 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
321 static void answer_msg(void *msgv)
323 unsigned char *msg = (unsigned char *)msgv;
328 msglen = GET_32BIT(msg);
329 if (msglen > AGENT_MAX_MSGLEN) {
330 reply = pageant_failure_msg(&replylen);
332 reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
333 if (replylen > AGENT_MAX_MSGLEN) {
334 smemclr(reply, replylen);
336 reply = pageant_failure_msg(&replylen);
341 * Windows Pageant answers messages in place, by overwriting the
342 * input message buffer.
344 memcpy(msg, reply, replylen);
345 smemclr(reply, replylen);
349 static void win_add_keyfile(Filename *filename)
353 char *passphrase = NULL;
356 * Try loading the key without a passphrase. (Or rather, without a
357 * _new_ passphrase; pageant_add_keyfile will take care of trying
358 * all the passphrases we've already stored.)
360 ret = pageant_add_keyfile(filename, NULL, &err);
361 if (ret == PAGEANT_ACTION_OK) {
363 } else if (ret == PAGEANT_ACTION_FAILURE) {
368 * OK, a passphrase is needed, and we've been given the key
369 * comment to use in the passphrase prompt.
373 struct PassphraseProcStruct pps;
375 pps.passphrase = &passphrase;
377 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
378 NULL, PassphraseProc, (LPARAM) &pps);
379 passphrase_box = NULL;
384 goto done; /* operation cancelled */
386 assert(passphrase != NULL);
388 ret = pageant_add_keyfile(filename, passphrase, &err);
389 if (ret == PAGEANT_ACTION_OK) {
391 } else if (ret == PAGEANT_ACTION_FAILURE) {
395 smemclr(passphrase, strlen(passphrase));
401 message_box(err, APPNAME, MB_OK | MB_ICONERROR,
402 HELPCTXID(errors_cantloadkey));
405 smemclr(passphrase, strlen(passphrase));
413 * Prompt for a key file to add, and add it.
415 static void prompt_add_keyfile(void)
418 char *filelist = snewn(8192, char);
420 if (!keypath) keypath = filereq_new();
421 memset(&of, 0, sizeof(of));
423 of.lpstrFilter = FILTER_KEY_FILES;
424 of.lpstrCustomFilter = NULL;
426 of.lpstrFile = filelist;
429 of.lpstrFileTitle = NULL;
430 of.lpstrTitle = "Select Private Key File";
431 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
432 if (request_file(keypath, &of, TRUE, FALSE)) {
433 if(strlen(filelist) > of.nFileOffset) {
434 /* Only one filename returned? */
435 Filename *fn = filename_from_str(filelist);
439 /* we are returned a bunch of strings, end to
440 * end. first string is the directory, the
441 * rest the filenames. terminated with an
444 char *dir = filelist;
445 char *filewalker = filelist + strlen(dir) + 1;
446 while (*filewalker != '\0') {
447 char *filename = dupcat(dir, "\\", filewalker, NULL);
448 Filename *fn = filename_from_str(filename);
452 filewalker += strlen(filewalker) + 1;
457 pageant_forget_passphrases();
463 * Dialog-box function for the key list box.
465 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
466 WPARAM wParam, LPARAM lParam)
469 struct ssh2_userkey *skey;
476 { /* centre the window */
480 hw = GetDesktopWindow();
481 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
483 (rs.right + rs.left + rd.left - rd.right) / 2,
484 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
485 rd.right - rd.left, rd.bottom - rd.top, TRUE);
489 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
490 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
493 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
500 static int tabs[] = { 35, 75, 250 };
501 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
502 sizeof(tabs) / sizeof(*tabs),
508 switch (LOWORD(wParam)) {
514 case 101: /* add key */
515 if (HIWORD(wParam) == BN_CLICKED ||
516 HIWORD(wParam) == BN_DOUBLECLICKED) {
517 if (passphrase_box) {
518 MessageBeep(MB_ICONERROR);
519 SetForegroundWindow(passphrase_box);
522 prompt_add_keyfile();
525 case 102: /* remove key */
526 if (HIWORD(wParam) == BN_CLICKED ||
527 HIWORD(wParam) == BN_DOUBLECLICKED) {
532 /* our counter within the array of selected items */
535 /* get the number of items selected in the list */
537 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
539 /* none selected? that was silly */
540 if (numSelected == 0) {
545 /* get item indices in an array */
546 selectedArray = snewn(numSelected, int);
547 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
548 numSelected, (WPARAM)selectedArray);
550 itemNum = numSelected - 1;
551 rCount = pageant_count_ssh1_keys();
552 sCount = pageant_count_ssh2_keys();
554 /* go through the non-rsakeys until we've covered them all,
555 * and/or we're out of selected items to check. note that
556 * we go *backwards*, to avoid complications from deleting
557 * things hence altering the offset of subsequent items
559 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
560 skey = pageant_nth_ssh2_key(i);
562 if (selectedArray[itemNum] == rCount + i) {
563 pageant_delete_ssh2_key(skey);
564 skey->alg->freekey(skey->data);
570 /* do the same for the rsa keys */
571 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
572 rkey = pageant_nth_ssh1_key(i);
574 if(selectedArray[itemNum] == i) {
575 pageant_delete_ssh1_key(rkey);
582 sfree(selectedArray);
587 if (HIWORD(wParam) == BN_CLICKED ||
588 HIWORD(wParam) == BN_DOUBLECLICKED) {
589 launch_help(hwnd, WINHELP_CTX_pageant_general);
596 int id = ((LPHELPINFO)lParam)->iCtrlId;
599 case 100: topic = WINHELP_CTX_pageant_keylist; break;
600 case 101: topic = WINHELP_CTX_pageant_addkey; break;
601 case 102: topic = WINHELP_CTX_pageant_remkey; break;
604 launch_help(hwnd, topic);
618 /* Set up a system tray icon */
619 static BOOL AddTrayIcon(HWND hwnd)
625 #ifdef NIM_SETVERSION
627 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
630 tnid.cbSize = sizeof(NOTIFYICONDATA);
632 tnid.uID = 1; /* unique within this systray use */
633 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
634 tnid.uCallbackMessage = WM_SYSTRAY;
635 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
636 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
638 res = Shell_NotifyIcon(NIM_ADD, &tnid);
640 if (hicon) DestroyIcon(hicon);
645 /* Update the saved-sessions menu. */
646 static void update_sessions(void)
650 TCHAR buf[MAX_PATH + 1];
653 int index_key, index_menu;
658 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
661 for(num_entries = GetMenuItemCount(session_menu);
662 num_entries > initial_menuitems_count;
664 RemoveMenu(session_menu, 0, MF_BYPOSITION);
669 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
670 TCHAR session_name[MAX_PATH + 1];
671 unmungestr(buf, session_name, MAX_PATH);
672 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
673 memset(&mii, 0, sizeof(mii));
674 mii.cbSize = sizeof(mii);
675 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
676 mii.fType = MFT_STRING;
677 mii.fState = MFS_ENABLED;
678 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
679 mii.dwTypeData = session_name;
680 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
688 if(index_menu == 0) {
689 mii.cbSize = sizeof(mii);
690 mii.fMask = MIIM_TYPE | MIIM_STATE;
691 mii.fType = MFT_STRING;
692 mii.fState = MFS_GRAYED;
693 mii.dwTypeData = _T("(No sessions)");
694 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
700 * Versions of Pageant prior to 0.61 expected this SID on incoming
701 * communications. For backwards compatibility, and more particularly
702 * for compatibility with derived works of PuTTY still using the old
703 * Pageant client code, we accept it as an alternative to the one
704 * returned from get_user_sid() in winpgntc.c.
706 PSID get_default_sid(void)
710 PSECURITY_DESCRIPTOR psd = NULL;
711 PSID sid = NULL, copy = NULL, ret = NULL;
713 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
714 GetCurrentProcessId())) == NULL)
717 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
718 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
721 sidlen = GetLengthSid(sid);
723 copy = (PSID)smalloc(sidlen);
725 if (!CopySid(sidlen, copy, sid))
728 /* Success. Move sid into the return value slot, and null it out
729 * to stop the cleanup code freeing it. */
745 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
746 WPARAM wParam, LPARAM lParam)
749 static int menuinprogress;
750 static UINT msgTaskbarCreated = 0;
754 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
757 if (message==msgTaskbarCreated) {
759 * Explorer has been restarted, so the tray icon will
767 if (lParam == WM_RBUTTONUP) {
769 GetCursorPos(&cursorpos);
770 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
771 } else if (lParam == WM_LBUTTONDBLCLK) {
772 /* Run the default menu item. */
773 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
775 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
779 if (!menuinprogress) {
782 SetForegroundWindow(hwnd);
783 ret = TrackPopupMenu(systray_menu,
784 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
786 wParam, lParam, 0, hwnd, NULL);
792 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
794 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
796 MessageBox(NULL, "Unable to execute PuTTY!",
797 "Error", MB_OK | MB_ICONERROR);
802 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
803 SendMessage(hwnd, WM_CLOSE, 0, 0);
807 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
809 ShowWindow(keylist, SW_SHOWNORMAL);
812 * Sometimes the window comes up minimised / hidden for
813 * no obvious reason. Prevent this. This also brings it
814 * to the front if it's already present (the user
815 * selected View Keys because they wanted to _see_ the
818 SetForegroundWindow(keylist);
819 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
820 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
823 if (passphrase_box) {
824 MessageBeep(MB_ICONERROR);
825 SetForegroundWindow(passphrase_box);
828 prompt_add_keyfile();
832 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
834 ShowWindow(aboutbox, SW_SHOWNORMAL);
836 * Sometimes the window comes up minimised / hidden
837 * for no obvious reason. Prevent this.
839 SetForegroundWindow(aboutbox);
840 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
841 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
845 launch_help(hwnd, WINHELP_CTX_pageant_general);
849 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
851 TCHAR buf[MAX_PATH + 1];
852 TCHAR param[MAX_PATH + 1];
853 memset(&mii, 0, sizeof(mii));
854 mii.cbSize = sizeof(mii);
855 mii.fMask = MIIM_TYPE;
857 mii.dwTypeData = buf;
858 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
860 strcat(param, mii.dwTypeData);
861 if((int)ShellExecute(hwnd, NULL, putty_path, param,
862 _T(""), SW_SHOW) <= 32) {
863 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
864 MB_OK | MB_ICONERROR);
882 PSID mapowner, ourself, ourself2;
884 PSECURITY_DESCRIPTOR psd = NULL;
887 cds = (COPYDATASTRUCT *) lParam;
888 if (cds->dwData != AGENT_COPYDATA_ID)
889 return 0; /* not our message, mate */
890 mapname = (char *) cds->lpData;
891 if (mapname[cds->cbData - 1] != '\0')
892 return 0; /* failure to be ASCIZ! */
894 debug(("mapname is :%s:\n", mapname));
896 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
898 debug(("filemap is %p\n", filemap));
900 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
904 if ((ourself = get_user_sid()) == NULL) {
906 debug(("couldn't get user SID\n"));
908 CloseHandle(filemap);
912 if ((ourself2 = get_default_sid()) == NULL) {
914 debug(("couldn't get default SID\n"));
916 CloseHandle(filemap);
921 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
922 OWNER_SECURITY_INFORMATION,
923 &mapowner, NULL, NULL, NULL,
924 &psd) != ERROR_SUCCESS)) {
926 debug(("couldn't get owner info for filemap: %d\n",
929 CloseHandle(filemap);
936 LPTSTR ours, ours2, theirs;
937 ConvertSidToStringSid(mapowner, &theirs);
938 ConvertSidToStringSid(ourself, &ours);
939 ConvertSidToStringSid(ourself2, &ours2);
940 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
941 " theirs=%s\n", ours, ours2, theirs));
947 if (!EqualSid(mapowner, ourself) &&
948 !EqualSid(mapowner, ourself2)) {
949 CloseHandle(filemap);
953 return 0; /* security ID mismatch! */
956 debug(("security stuff matched\n"));
963 debug(("security APIs not present\n"));
967 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
969 debug(("p is %p\n", p));
972 for (i = 0; i < 5; i++)
973 debug(("p[%d]=%02x\n", i,
974 ((unsigned char *) p)[i]));
981 CloseHandle(filemap);
986 return DefWindowProc(hwnd, message, wParam, lParam);
990 * Fork and Exec the command in cmdline. [DBW]
992 void spawn_cmd(char *cmdline, char * args, int show)
994 if (ShellExecute(NULL, _T("open"), cmdline,
995 args, NULL, show) <= (HINSTANCE) 32) {
997 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
998 (int)GetLastError());
999 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1005 * This is a can't-happen stub, since Pageant never makes
1006 * asynchronous agent requests.
1008 void agent_schedule_callback(void (*callback)(void *, void *, int),
1009 void *callback_ctx, void *data, int len)
1011 assert(!"We shouldn't get here");
1014 void cleanup_exit(int code)
1020 int flags = FLAG_SYNCAGENT;
1022 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1026 char *command = NULL;
1029 char **argv, **argstart;
1035 * Determine whether we're an NT system (should have security
1036 * APIs) or a non-NT system (don't do security).
1040 modalfatalbox("Windows refuses to report a version");
1042 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1043 has_security = TRUE;
1045 has_security = FALSE;
1050 * Attempt to get the security API we need.
1052 if (!got_advapi()) {
1054 "Unable to access security APIs. Pageant will\n"
1055 "not run, in case it causes a security breach.",
1056 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1061 "This program has been compiled for Win9X and will\n"
1062 "not run on NT, in case it causes a security breach.",
1063 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1069 * See if we can find our Help file.
1074 * Look for the PuTTY binary (we will enable the saved session
1075 * submenu if we find it).
1078 char b[2048], *p, *q, *r;
1080 GetModuleFileName(NULL, b, sizeof(b) - 16);
1082 p = strrchr(b, '\\');
1083 if (p && p >= r) r = p+1;
1084 q = strrchr(b, ':');
1085 if (q && q >= r) r = q+1;
1086 strcpy(r, "putty.exe");
1087 if ( (fp = fopen(b, "r")) != NULL) {
1088 putty_path = dupstr(b);
1095 * Find out if Pageant is already running.
1097 already_running = agent_exists();
1100 * Initialise the cross-platform Pageant code.
1102 if (!already_running) {
1107 * Process the command line and add keys as listed on it.
1109 split_into_argv(cmdline, &argc, &argv, &argstart);
1110 for (i = 0; i < argc; i++) {
1111 if (!strcmp(argv[i], "-pgpfp")) {
1114 } else if (!strcmp(argv[i], "-c")) {
1116 * If we see `-c', then the rest of the
1117 * command line should be treated as a
1118 * command to be spawned.
1121 command = argstart[i+1];
1126 Filename *fn = filename_from_str(argv[i]);
1127 win_add_keyfile(fn);
1134 * Forget any passphrase that we retained while going over
1135 * command line keyfiles.
1137 pageant_forget_passphrases();
1141 if (command[0] == '"')
1142 args = strchr(++command, '"');
1144 args = strchr(command, ' ');
1147 while(*args && isspace(*args)) args++;
1149 spawn_cmd(command, args, show);
1153 * If Pageant was already running, we leave now. If we haven't
1154 * even taken any auxiliary action (spawned a command or added
1157 if (already_running) {
1158 if (!command && !added_keys) {
1159 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1160 MB_ICONERROR | MB_OK);
1167 wndclass.lpfnWndProc = WndProc;
1168 wndclass.cbClsExtra = 0;
1169 wndclass.cbWndExtra = 0;
1170 wndclass.hInstance = inst;
1171 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1172 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1173 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1174 wndclass.lpszMenuName = NULL;
1175 wndclass.lpszClassName = APPNAME;
1177 RegisterClass(&wndclass);
1182 hwnd = CreateWindow(APPNAME, APPNAME,
1183 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1184 CW_USEDEFAULT, CW_USEDEFAULT,
1185 100, 100, NULL, NULL, inst, NULL);
1187 /* Set up a system tray icon */
1190 /* Accelerators used: nsvkxa */
1191 systray_menu = CreatePopupMenu();
1193 session_menu = CreateMenu();
1194 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1195 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1196 (UINT) session_menu, "&Saved Sessions");
1197 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1199 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1201 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1202 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1204 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1205 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1206 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1207 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1208 initial_menuitems_count = GetMenuItemCount(session_menu);
1210 /* Set the default menu item. */
1211 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1213 ShowWindow(hwnd, SW_HIDE);
1216 * Main message loop.
1218 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1219 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1220 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1221 TranslateMessage(&msg);
1222 DispatchMessage(&msg);
1226 /* Clean up the system tray icon */
1228 NOTIFYICONDATA tnid;
1230 tnid.cbSize = sizeof(NOTIFYICONDATA);
1234 Shell_NotifyIcon(NIM_DELETE, &tnid);
1236 DestroyMenu(systray_menu);
1239 if (keypath) filereq_free(keypath);
1241 cleanup_exit(msg.wParam);
1242 return msg.wParam; /* just in case optimiser complains */