2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
26 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
31 #define IDI_MAINICON 200
32 #define IDI_TRAYICON 201
34 #define WM_SYSTRAY (WM_APP + 6)
35 #define WM_SYSTRAY2 (WM_APP + 7)
37 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
39 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
40 * wParam are used by Windows, and should be masked off, so we shouldn't
41 * attempt to store information in them. Hence all these identifiers have
42 * the low 4 bits clear. Also, identifiers should < 0xF000. */
44 #define IDM_CLOSE 0x0010
45 #define IDM_VIEWKEYS 0x0020
46 #define IDM_ADDKEY 0x0030
47 #define IDM_HELP 0x0040
48 #define IDM_ABOUT 0x0050
50 #define APPNAME "Pageant"
56 static HMENU systray_menu, session_menu;
57 static int already_running;
59 static char *putty_path;
61 /* CWD for "add key" file requester. */
62 static filereq *keypath = NULL;
64 #define IDM_PUTTY 0x0060
65 #define IDM_SESSIONS_BASE 0x1000
66 #define IDM_SESSIONS_MAX 0x2000
67 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
68 #define PUTTY_DEFAULT "Default%20Settings"
69 static int initial_menuitems_count;
72 * Print a modal (Really Bad) message box and perform a fatal exit.
74 void modalfatalbox(const char *fmt, ...)
80 buf = dupvprintf(fmt, ap);
82 MessageBox(hwnd, buf, "Pageant Fatal Error",
83 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
88 /* Un-munge session names out of the registry. */
89 static void unmungestr(char *in, char *out, int outlen)
92 if (*in == '%' && in[1] && in[2]) {
100 *out++ = (i << 4) + j;
114 static int has_security;
116 struct PassphraseProcStruct {
122 * Dialog-box function for the Licence box.
124 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
125 WPARAM wParam, LPARAM lParam)
129 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
132 switch (LOWORD(wParam)) {
147 * Dialog-box function for the About box.
149 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
150 WPARAM wParam, LPARAM lParam)
155 char *text = dupprintf
156 ("Pageant\r\n\r\n%s\r\n\r\n%s",
158 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
159 SetDlgItemText(hwnd, 1000, text);
164 switch (LOWORD(wParam)) {
171 EnableWindow(hwnd, 0);
172 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
173 EnableWindow(hwnd, 1);
174 SetActiveWindow(hwnd);
186 static HWND passphrase_box;
189 * Dialog-box function for the passphrase box.
191 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
192 WPARAM wParam, LPARAM lParam)
194 static char **passphrase = NULL;
195 struct PassphraseProcStruct *p;
199 passphrase_box = hwnd;
203 { /* centre the window */
207 hw = GetDesktopWindow();
208 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
210 (rs.right + rs.left + rd.left - rd.right) / 2,
211 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
212 rd.right - rd.left, rd.bottom - rd.top, TRUE);
215 SetForegroundWindow(hwnd);
216 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
217 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
218 p = (struct PassphraseProcStruct *) lParam;
219 passphrase = p->passphrase;
221 SetDlgItemText(hwnd, 101, p->comment);
222 burnstr(*passphrase);
223 *passphrase = dupstr("");
224 SetDlgItemText(hwnd, 102, *passphrase);
227 switch (LOWORD(wParam)) {
237 case 102: /* edit box */
238 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
239 burnstr(*passphrase);
240 *passphrase = GetDlgItemText_alloc(hwnd, 102);
253 * Warn about the obsolescent key file format.
255 void old_keyfile_warning(void)
257 static const char mbtitle[] = "PuTTY Key File Warning";
258 static const char message[] =
259 "You are loading an SSH-2 private key which has an\n"
260 "old version of the file format. This means your key\n"
261 "file is not fully tamperproof. Future versions of\n"
262 "PuTTY may stop supporting this private key format,\n"
263 "so we recommend you convert your key to the new\n"
266 "You can perform this conversion by loading the key\n"
267 "into PuTTYgen and then saving it again.";
269 MessageBox(NULL, message, mbtitle, MB_OK);
273 * Update the visible key list.
275 void keylist_update(void)
278 struct ssh2_userkey *skey;
282 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
283 for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
284 char listentry[512], *p;
286 * Replace two spaces in the fingerprint with tabs, for
287 * nice alignment in the box.
289 strcpy(listentry, "ssh1\t");
290 p = listentry + strlen(listentry);
291 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
292 p = strchr(listentry, ' ');
295 p = strchr(listentry, ' ');
298 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
299 0, (LPARAM) listentry);
301 for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
305 * Replace spaces with tabs in the fingerprint prefix, for
306 * nice alignment in the list box, until we encounter a :
307 * meaning we're into the fingerprint proper.
309 p = ssh2_fingerprint(skey->alg, skey->data);
310 listentry = dupprintf("%s\t%s", p, skey->comment);
315 pos += strcspn(listentry + pos, " :");
316 if (listentry[pos] == ':' || !listentry[pos])
318 listentry[pos++] = '\t';
321 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
325 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
329 static void answer_msg(void *msgv)
331 unsigned char *msg = (unsigned char *)msgv;
336 msglen = GET_32BIT(msg);
337 if (msglen > AGENT_MAX_MSGLEN) {
338 reply = pageant_failure_msg(&replylen);
340 reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
341 if (replylen > AGENT_MAX_MSGLEN) {
342 smemclr(reply, replylen);
344 reply = pageant_failure_msg(&replylen);
349 * Windows Pageant answers messages in place, by overwriting the
350 * input message buffer.
352 memcpy(msg, reply, replylen);
353 smemclr(reply, replylen);
357 static void win_add_keyfile(Filename *filename)
361 char *passphrase = NULL;
364 * Try loading the key without a passphrase. (Or rather, without a
365 * _new_ passphrase; pageant_add_keyfile will take care of trying
366 * all the passphrases we've already stored.)
368 ret = pageant_add_keyfile(filename, NULL, &err);
369 if (ret == PAGEANT_ACTION_OK) {
371 } else if (ret == PAGEANT_ACTION_FAILURE) {
376 * OK, a passphrase is needed, and we've been given the key
377 * comment to use in the passphrase prompt.
381 struct PassphraseProcStruct pps;
383 pps.passphrase = &passphrase;
385 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
386 NULL, PassphraseProc, (LPARAM) &pps);
387 passphrase_box = NULL;
390 goto done; /* operation cancelled */
394 assert(passphrase != NULL);
396 ret = pageant_add_keyfile(filename, passphrase, &err);
397 if (ret == PAGEANT_ACTION_OK) {
399 } else if (ret == PAGEANT_ACTION_FAILURE) {
403 smemclr(passphrase, strlen(passphrase));
409 message_box(err, APPNAME, MB_OK | MB_ICONERROR,
410 HELPCTXID(errors_cantloadkey));
413 smemclr(passphrase, strlen(passphrase));
421 * Prompt for a key file to add, and add it.
423 static void prompt_add_keyfile(void)
426 char *filelist = snewn(8192, char);
428 if (!keypath) keypath = filereq_new();
429 memset(&of, 0, sizeof(of));
431 of.lpstrFilter = FILTER_KEY_FILES;
432 of.lpstrCustomFilter = NULL;
434 of.lpstrFile = filelist;
437 of.lpstrFileTitle = NULL;
438 of.lpstrTitle = "Select Private Key File";
439 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
440 if (request_file(keypath, &of, TRUE, FALSE)) {
441 if(strlen(filelist) > of.nFileOffset) {
442 /* Only one filename returned? */
443 Filename *fn = filename_from_str(filelist);
447 /* we are returned a bunch of strings, end to
448 * end. first string is the directory, the
449 * rest the filenames. terminated with an
452 char *dir = filelist;
453 char *filewalker = filelist + strlen(dir) + 1;
454 while (*filewalker != '\0') {
455 char *filename = dupcat(dir, "\\", filewalker, NULL);
456 Filename *fn = filename_from_str(filename);
460 filewalker += strlen(filewalker) + 1;
465 pageant_forget_passphrases();
471 * Dialog-box function for the key list box.
473 static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
474 WPARAM wParam, LPARAM lParam)
477 struct ssh2_userkey *skey;
484 { /* centre the window */
488 hw = GetDesktopWindow();
489 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
491 (rs.right + rs.left + rd.left - rd.right) / 2,
492 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
493 rd.right - rd.left, rd.bottom - rd.top, TRUE);
497 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
498 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
501 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
508 static int tabs[] = { 35, 75, 250 };
509 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
510 sizeof(tabs) / sizeof(*tabs),
516 switch (LOWORD(wParam)) {
522 case 101: /* add key */
523 if (HIWORD(wParam) == BN_CLICKED ||
524 HIWORD(wParam) == BN_DOUBLECLICKED) {
525 if (passphrase_box) {
526 MessageBeep(MB_ICONERROR);
527 SetForegroundWindow(passphrase_box);
530 prompt_add_keyfile();
533 case 102: /* remove key */
534 if (HIWORD(wParam) == BN_CLICKED ||
535 HIWORD(wParam) == BN_DOUBLECLICKED) {
540 /* our counter within the array of selected items */
543 /* get the number of items selected in the list */
545 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
547 /* none selected? that was silly */
548 if (numSelected == 0) {
553 /* get item indices in an array */
554 selectedArray = snewn(numSelected, int);
555 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
556 numSelected, (WPARAM)selectedArray);
558 itemNum = numSelected - 1;
559 rCount = pageant_count_ssh1_keys();
560 sCount = pageant_count_ssh2_keys();
562 /* go through the non-rsakeys until we've covered them all,
563 * and/or we're out of selected items to check. note that
564 * we go *backwards*, to avoid complications from deleting
565 * things hence altering the offset of subsequent items
567 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
568 skey = pageant_nth_ssh2_key(i);
570 if (selectedArray[itemNum] == rCount + i) {
571 pageant_delete_ssh2_key(skey);
572 skey->alg->freekey(skey->data);
578 /* do the same for the rsa keys */
579 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
580 rkey = pageant_nth_ssh1_key(i);
582 if(selectedArray[itemNum] == i) {
583 pageant_delete_ssh1_key(rkey);
590 sfree(selectedArray);
595 if (HIWORD(wParam) == BN_CLICKED ||
596 HIWORD(wParam) == BN_DOUBLECLICKED) {
597 launch_help(hwnd, WINHELP_CTX_pageant_general);
604 int id = ((LPHELPINFO)lParam)->iCtrlId;
605 const char *topic = NULL;
607 case 100: topic = WINHELP_CTX_pageant_keylist; break;
608 case 101: topic = WINHELP_CTX_pageant_addkey; break;
609 case 102: topic = WINHELP_CTX_pageant_remkey; break;
612 launch_help(hwnd, topic);
626 /* Set up a system tray icon */
627 static BOOL AddTrayIcon(HWND hwnd)
633 #ifdef NIM_SETVERSION
635 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
638 tnid.cbSize = sizeof(NOTIFYICONDATA);
640 tnid.uID = 1; /* unique within this systray use */
641 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
642 tnid.uCallbackMessage = WM_SYSTRAY;
643 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
644 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
646 res = Shell_NotifyIcon(NIM_ADD, &tnid);
648 if (hicon) DestroyIcon(hicon);
653 /* Update the saved-sessions menu. */
654 static void update_sessions(void)
658 TCHAR buf[MAX_PATH + 1];
661 int index_key, index_menu;
666 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
669 for(num_entries = GetMenuItemCount(session_menu);
670 num_entries > initial_menuitems_count;
672 RemoveMenu(session_menu, 0, MF_BYPOSITION);
677 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
678 TCHAR session_name[MAX_PATH + 1];
679 unmungestr(buf, session_name, MAX_PATH);
680 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
681 memset(&mii, 0, sizeof(mii));
682 mii.cbSize = sizeof(mii);
683 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
684 mii.fType = MFT_STRING;
685 mii.fState = MFS_ENABLED;
686 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
687 mii.dwTypeData = session_name;
688 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
696 if(index_menu == 0) {
697 mii.cbSize = sizeof(mii);
698 mii.fMask = MIIM_TYPE | MIIM_STATE;
699 mii.fType = MFT_STRING;
700 mii.fState = MFS_GRAYED;
701 mii.dwTypeData = _T("(No sessions)");
702 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
708 * Versions of Pageant prior to 0.61 expected this SID on incoming
709 * communications. For backwards compatibility, and more particularly
710 * for compatibility with derived works of PuTTY still using the old
711 * Pageant client code, we accept it as an alternative to the one
712 * returned from get_user_sid() in winpgntc.c.
714 PSID get_default_sid(void)
718 PSECURITY_DESCRIPTOR psd = NULL;
719 PSID sid = NULL, copy = NULL, ret = NULL;
721 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
722 GetCurrentProcessId())) == NULL)
725 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
726 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
729 sidlen = GetLengthSid(sid);
731 copy = (PSID)smalloc(sidlen);
733 if (!CopySid(sidlen, copy, sid))
736 /* Success. Move sid into the return value slot, and null it out
737 * to stop the cleanup code freeing it. */
753 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
754 WPARAM wParam, LPARAM lParam)
756 static int menuinprogress;
757 static UINT msgTaskbarCreated = 0;
761 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
764 if (message==msgTaskbarCreated) {
766 * Explorer has been restarted, so the tray icon will
774 if (lParam == WM_RBUTTONUP) {
776 GetCursorPos(&cursorpos);
777 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
778 } else if (lParam == WM_LBUTTONDBLCLK) {
779 /* Run the default menu item. */
780 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
782 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
786 if (!menuinprogress) {
789 SetForegroundWindow(hwnd);
790 TrackPopupMenu(systray_menu,
791 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
793 wParam, lParam, 0, hwnd, NULL);
799 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
801 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
803 MessageBox(NULL, "Unable to execute PuTTY!",
804 "Error", MB_OK | MB_ICONERROR);
809 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
810 SendMessage(hwnd, WM_CLOSE, 0, 0);
814 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
816 ShowWindow(keylist, SW_SHOWNORMAL);
819 * Sometimes the window comes up minimised / hidden for
820 * no obvious reason. Prevent this. This also brings it
821 * to the front if it's already present (the user
822 * selected View Keys because they wanted to _see_ the
825 SetForegroundWindow(keylist);
826 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
827 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
830 if (passphrase_box) {
831 MessageBeep(MB_ICONERROR);
832 SetForegroundWindow(passphrase_box);
835 prompt_add_keyfile();
839 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
841 ShowWindow(aboutbox, SW_SHOWNORMAL);
843 * Sometimes the window comes up minimised / hidden
844 * for no obvious reason. Prevent this.
846 SetForegroundWindow(aboutbox);
847 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
848 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
852 launch_help(hwnd, WINHELP_CTX_pageant_general);
856 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
858 TCHAR buf[MAX_PATH + 1];
859 TCHAR param[MAX_PATH + 1];
860 memset(&mii, 0, sizeof(mii));
861 mii.cbSize = sizeof(mii);
862 mii.fMask = MIIM_TYPE;
864 mii.dwTypeData = buf;
865 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
867 strcat(param, mii.dwTypeData);
868 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param,
869 _T(""), SW_SHOW) <= 32) {
870 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
871 MB_OK | MB_ICONERROR);
889 PSID mapowner, ourself, ourself2;
891 PSECURITY_DESCRIPTOR psd = NULL;
894 cds = (COPYDATASTRUCT *) lParam;
895 if (cds->dwData != AGENT_COPYDATA_ID)
896 return 0; /* not our message, mate */
897 mapname = (char *) cds->lpData;
898 if (mapname[cds->cbData - 1] != '\0')
899 return 0; /* failure to be ASCIZ! */
901 debug(("mapname is :%s:\n", mapname));
903 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
905 debug(("filemap is %p\n", filemap));
907 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
911 if ((ourself = get_user_sid()) == NULL) {
913 debug(("couldn't get user SID\n"));
915 CloseHandle(filemap);
919 if ((ourself2 = get_default_sid()) == NULL) {
921 debug(("couldn't get default SID\n"));
923 CloseHandle(filemap);
927 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
928 OWNER_SECURITY_INFORMATION,
929 &mapowner, NULL, NULL, NULL,
930 &psd) != ERROR_SUCCESS)) {
932 debug(("couldn't get owner info for filemap: %d\n",
935 CloseHandle(filemap);
941 LPTSTR ours, ours2, theirs;
942 ConvertSidToStringSid(mapowner, &theirs);
943 ConvertSidToStringSid(ourself, &ours);
944 ConvertSidToStringSid(ourself2, &ours2);
945 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
946 " theirs=%s\n", ours, ours2, theirs));
952 if (!EqualSid(mapowner, ourself) &&
953 !EqualSid(mapowner, ourself2)) {
954 CloseHandle(filemap);
957 return 0; /* security ID mismatch! */
960 debug(("security stuff matched\n"));
966 debug(("security APIs not present\n"));
970 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
972 debug(("p is %p\n", p));
975 for (i = 0; i < 5; i++)
976 debug(("p[%d]=%02x\n", i,
977 ((unsigned char *) p)[i]));
984 CloseHandle(filemap);
989 return DefWindowProc(hwnd, message, wParam, lParam);
993 * Fork and Exec the command in cmdline. [DBW]
995 void spawn_cmd(const char *cmdline, const char *args, int show)
997 if (ShellExecute(NULL, _T("open"), cmdline,
998 args, NULL, show) <= (HINSTANCE) 32) {
1000 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1001 (int)GetLastError());
1002 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1008 * This is a can't-happen stub, since Pageant never makes
1009 * asynchronous agent requests.
1011 void agent_schedule_callback(void (*callback)(void *, void *, int),
1012 void *callback_ctx, void *data, int len)
1014 assert(!"We shouldn't get here");
1017 void cleanup_exit(int code)
1023 int flags = FLAG_SYNCAGENT;
1025 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1029 const char *command = NULL;
1032 char **argv, **argstart;
1038 * Determine whether we're an NT system (should have security
1039 * APIs) or a non-NT system (don't do security).
1043 modalfatalbox("Windows refuses to report a version");
1045 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1046 has_security = TRUE;
1048 has_security = FALSE;
1053 * Attempt to get the security API we need.
1055 if (!got_advapi()) {
1057 "Unable to access security APIs. Pageant will\n"
1058 "not run, in case it causes a security breach.",
1059 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1064 "This program has been compiled for Win9X and will\n"
1065 "not run on NT, in case it causes a security breach.",
1066 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1072 * See if we can find our Help file.
1077 * Look for the PuTTY binary (we will enable the saved session
1078 * submenu if we find it).
1081 char b[2048], *p, *q, *r;
1083 GetModuleFileName(NULL, b, sizeof(b) - 16);
1085 p = strrchr(b, '\\');
1086 if (p && p >= r) r = p+1;
1087 q = strrchr(b, ':');
1088 if (q && q >= r) r = q+1;
1089 strcpy(r, "putty.exe");
1090 if ( (fp = fopen(b, "r")) != NULL) {
1091 putty_path = dupstr(b);
1098 * Find out if Pageant is already running.
1100 already_running = agent_exists();
1103 * Initialise the cross-platform Pageant code.
1105 if (!already_running) {
1110 * Process the command line and add keys as listed on it.
1112 split_into_argv(cmdline, &argc, &argv, &argstart);
1113 for (i = 0; i < argc; i++) {
1114 if (!strcmp(argv[i], "-pgpfp")) {
1117 } else if (!strcmp(argv[i], "-c")) {
1119 * If we see `-c', then the rest of the
1120 * command line should be treated as a
1121 * command to be spawned.
1124 command = argstart[i+1];
1129 Filename *fn = filename_from_str(argv[i]);
1130 win_add_keyfile(fn);
1137 * Forget any passphrase that we retained while going over
1138 * command line keyfiles.
1140 pageant_forget_passphrases();
1144 if (command[0] == '"')
1145 args = strchr(++command, '"');
1147 args = strchr(command, ' ');
1150 while(*args && isspace(*args)) args++;
1152 spawn_cmd(command, args, show);
1156 * If Pageant was already running, we leave now. If we haven't
1157 * even taken any auxiliary action (spawned a command or added
1160 if (already_running) {
1161 if (!command && !added_keys) {
1162 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1163 MB_ICONERROR | MB_OK);
1170 wndclass.lpfnWndProc = WndProc;
1171 wndclass.cbClsExtra = 0;
1172 wndclass.cbWndExtra = 0;
1173 wndclass.hInstance = inst;
1174 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1175 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1176 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1177 wndclass.lpszMenuName = NULL;
1178 wndclass.lpszClassName = APPNAME;
1180 RegisterClass(&wndclass);
1185 hwnd = CreateWindow(APPNAME, APPNAME,
1186 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1187 CW_USEDEFAULT, CW_USEDEFAULT,
1188 100, 100, NULL, NULL, inst, NULL);
1190 /* Set up a system tray icon */
1193 /* Accelerators used: nsvkxa */
1194 systray_menu = CreatePopupMenu();
1196 session_menu = CreateMenu();
1197 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1198 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1199 (UINT_PTR) session_menu, "&Saved Sessions");
1200 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1202 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1204 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1205 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1207 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1208 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1209 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1210 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1211 initial_menuitems_count = GetMenuItemCount(session_menu);
1213 /* Set the default menu item. */
1214 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1216 ShowWindow(hwnd, SW_HIDE);
1219 * Main message loop.
1221 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1222 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1223 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1224 TranslateMessage(&msg);
1225 DispatchMessage(&msg);
1229 /* Clean up the system tray icon */
1231 NOTIFYICONDATA tnid;
1233 tnid.cbSize = sizeof(NOTIFYICONDATA);
1237 Shell_NotifyIcon(NIM_DELETE, &tnid);
1239 DestroyMenu(systray_menu);
1242 if (keypath) filereq_free(keypath);
1244 cleanup_exit(msg.wParam);
1245 return msg.wParam; /* just in case optimiser complains */