"Fossies" - the Fresh Open Source Software Archive

Member "putty-0.73/windows/winpgnt.c" (22 Sep 2019, 41470 Bytes) of package /linux/misc/putty-0.73.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "winpgnt.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.72_vs_0.73.

    1 /*
    2  * Pageant: the PuTTY Authentication Agent.
    3  */
    4 
    5 #include <stdio.h>
    6 #include <stdlib.h>
    7 #include <stddef.h>
    8 #include <ctype.h>
    9 #include <assert.h>
   10 #include <tchar.h>
   11 
   12 #define PUTTY_DO_GLOBALS
   13 
   14 #include "putty.h"
   15 #include "ssh.h"
   16 #include "misc.h"
   17 #include "tree234.h"
   18 #include "winsecur.h"
   19 #include "pageant.h"
   20 #include "licence.h"
   21 
   22 #include <shellapi.h>
   23 
   24 #ifndef NO_SECURITY
   25 #include <aclapi.h>
   26 #ifdef DEBUG_IPC
   27 #define _WIN32_WINNT 0x0500            /* for ConvertSidToStringSid */
   28 #include <sddl.h>
   29 #endif
   30 #endif
   31 
   32 #define IDI_MAINICON 200
   33 #define IDI_TRAYICON 201
   34 
   35 #define WM_SYSTRAY   (WM_APP + 6)
   36 #define WM_SYSTRAY2  (WM_APP + 7)
   37 
   38 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
   39 
   40 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
   41  * wParam are used by Windows, and should be masked off, so we shouldn't
   42  * attempt to store information in them. Hence all these identifiers have
   43  * the low 4 bits clear. Also, identifiers should < 0xF000. */
   44 
   45 #define IDM_CLOSE    0x0010
   46 #define IDM_VIEWKEYS 0x0020
   47 #define IDM_ADDKEY   0x0030
   48 #define IDM_HELP     0x0040
   49 #define IDM_ABOUT    0x0050
   50 
   51 #define APPNAME "Pageant"
   52 
   53 static HWND keylist;
   54 static HWND aboutbox;
   55 static HMENU systray_menu, session_menu;
   56 static bool already_running;
   57 
   58 static char *putty_path;
   59 static bool restrict_putty_acl = false;
   60 
   61 /* CWD for "add key" file requester. */
   62 static filereq *keypath = NULL;
   63 
   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;
   70 
   71 /*
   72  * Print a modal (Really Bad) message box and perform a fatal exit.
   73  */
   74 void modalfatalbox(const char *fmt, ...)
   75 {
   76     va_list ap;
   77     char *buf;
   78 
   79     va_start(ap, fmt);
   80     buf = dupvprintf(fmt, ap);
   81     va_end(ap);
   82     MessageBox(hwnd, buf, "Pageant Fatal Error",
   83                MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
   84     sfree(buf);
   85     exit(1);
   86 }
   87 
   88 static bool has_security;
   89 
   90 struct PassphraseProcStruct {
   91     char **passphrase;
   92     char *comment;
   93 };
   94 
   95 /*
   96  * Dialog-box function for the Licence box.
   97  */
   98 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
   99                                 WPARAM wParam, LPARAM lParam)
  100 {
  101     switch (msg) {
  102       case WM_INITDIALOG:
  103         SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
  104         return 1;
  105       case WM_COMMAND:
  106         switch (LOWORD(wParam)) {
  107           case IDOK:
  108           case IDCANCEL:
  109             EndDialog(hwnd, 1);
  110             return 0;
  111         }
  112         return 0;
  113       case WM_CLOSE:
  114         EndDialog(hwnd, 1);
  115         return 0;
  116     }
  117     return 0;
  118 }
  119 
  120 /*
  121  * Dialog-box function for the About box.
  122  */
  123 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
  124                               WPARAM wParam, LPARAM lParam)
  125 {
  126     switch (msg) {
  127       case WM_INITDIALOG:
  128         {
  129             char *buildinfo_text = buildinfo("\r\n");
  130             char *text = dupprintf
  131                 ("Pageant\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
  132                  ver, buildinfo_text,
  133                  "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
  134             sfree(buildinfo_text);
  135             SetDlgItemText(hwnd, 1000, text);
  136             sfree(text);
  137         }
  138         return 1;
  139       case WM_COMMAND:
  140         switch (LOWORD(wParam)) {
  141           case IDOK:
  142           case IDCANCEL:
  143             aboutbox = NULL;
  144             DestroyWindow(hwnd);
  145             return 0;
  146           case 101:
  147             EnableWindow(hwnd, 0);
  148             DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
  149             EnableWindow(hwnd, 1);
  150             SetActiveWindow(hwnd);
  151             return 0;
  152           case 102:
  153             /* Load web browser */
  154             ShellExecute(hwnd, "open",
  155                          "https://www.chiark.greenend.org.uk/~sgtatham/putty/",
  156                          0, 0, SW_SHOWDEFAULT);
  157             return 0;
  158         }
  159         return 0;
  160       case WM_CLOSE:
  161         aboutbox = NULL;
  162         DestroyWindow(hwnd);
  163         return 0;
  164     }
  165     return 0;
  166 }
  167 
  168 static HWND passphrase_box;
  169 
  170 /*
  171  * Dialog-box function for the passphrase box.
  172  */
  173 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
  174                                    WPARAM wParam, LPARAM lParam)
  175 {
  176     static char **passphrase = NULL;
  177     struct PassphraseProcStruct *p;
  178 
  179     switch (msg) {
  180       case WM_INITDIALOG:
  181         passphrase_box = hwnd;
  182         /*
  183          * Centre the window.
  184          */
  185         {                              /* centre the window */
  186             RECT rs, rd;
  187             HWND hw;
  188 
  189             hw = GetDesktopWindow();
  190             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
  191                 MoveWindow(hwnd,
  192                            (rs.right + rs.left + rd.left - rd.right) / 2,
  193                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
  194                            rd.right - rd.left, rd.bottom - rd.top, true);
  195         }
  196 
  197         SetForegroundWindow(hwnd);
  198         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
  199                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
  200         p = (struct PassphraseProcStruct *) lParam;
  201         passphrase = p->passphrase;
  202         if (p->comment)
  203             SetDlgItemText(hwnd, 101, p->comment);
  204         burnstr(*passphrase);
  205         *passphrase = dupstr("");
  206         SetDlgItemText(hwnd, 102, *passphrase);
  207         return 0;
  208       case WM_COMMAND:
  209         switch (LOWORD(wParam)) {
  210           case IDOK:
  211             if (*passphrase)
  212                 EndDialog(hwnd, 1);
  213             else
  214                 MessageBeep(0);
  215             return 0;
  216           case IDCANCEL:
  217             EndDialog(hwnd, 0);
  218             return 0;
  219           case 102:                    /* edit box */
  220             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
  221                 burnstr(*passphrase);
  222                 *passphrase = GetDlgItemText_alloc(hwnd, 102);
  223             }
  224             return 0;
  225         }
  226         return 0;
  227       case WM_CLOSE:
  228         EndDialog(hwnd, 0);
  229         return 0;
  230     }
  231     return 0;
  232 }
  233 
  234 /*
  235  * Warn about the obsolescent key file format.
  236  */
  237 void old_keyfile_warning(void)
  238 {
  239     static const char mbtitle[] = "PuTTY Key File Warning";
  240     static const char message[] =
  241         "You are loading an SSH-2 private key which has an\n"
  242         "old version of the file format. This means your key\n"
  243         "file is not fully tamperproof. Future versions of\n"
  244         "PuTTY may stop supporting this private key format,\n"
  245         "so we recommend you convert your key to the new\n"
  246         "format.\n"
  247         "\n"
  248         "You can perform this conversion by loading the key\n"
  249         "into PuTTYgen and then saving it again.";
  250 
  251     MessageBox(NULL, message, mbtitle, MB_OK);
  252 }
  253 
  254 /*
  255  * Update the visible key list.
  256  */
  257 void keylist_update(void)
  258 {
  259     RSAKey *rkey;
  260     ssh2_userkey *skey;
  261     int i;
  262 
  263     if (keylist) {
  264         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
  265         for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
  266             char *listentry, *fp, *p;
  267 
  268             fp = rsa_ssh1_fingerprint(rkey);
  269             listentry = dupprintf("ssh1\t%s", fp);
  270             sfree(fp);
  271 
  272             /*
  273              * Replace two spaces in the fingerprint with tabs, for
  274              * nice alignment in the box.
  275              */
  276             p = strchr(listentry, ' ');
  277             if (p)
  278                 *p = '\t';
  279             p = strchr(listentry, ' ');
  280             if (p)
  281                 *p = '\t';
  282             SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
  283                                0, (LPARAM) listentry);
  284             sfree(listentry);
  285         }
  286         for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
  287             char *listentry, *p;
  288             int pos;
  289 
  290             /*
  291              * For nice alignment in the list box, we would ideally
  292              * want every entry to align to the tab stop settings, and
  293              * have a column for algorithm name, one for bit count,
  294              * one for hex fingerprint, and one for key comment.
  295              *
  296              * Unfortunately, some of the algorithm names are so long
  297              * that they overflow into the bit-count field.
  298              * Fortunately, at the moment, those are _precisely_ the
  299              * algorithm names that don't need a bit count displayed
  300              * anyway (because for NIST-style ECDSA the bit count is
  301              * mentioned in the algorithm name, and for ssh-ed25519
  302              * there is only one possible value anyway). So we fudge
  303              * this by simply omitting the bit count field in that
  304              * situation.
  305              *
  306              * This is fragile not only in the face of further key
  307              * types that don't follow this pattern, but also in the
  308              * face of font metrics changes - the Windows semantics
  309              * for list box tab stops is that \t aligns to the next
  310              * one you haven't already exceeded, so I have to guess
  311              * when the key type will overflow past the bit-count tab
  312              * stop and leave out a tab character. Urgh.
  313              */
  314 
  315             p = ssh2_fingerprint(skey->key);
  316             listentry = dupprintf("%s\t%s", p, skey->comment);
  317             sfree(p);
  318 
  319             pos = 0;
  320             while (1) {
  321                 pos += strcspn(listentry + pos, " :");
  322                 if (listentry[pos] == ':' || !listentry[pos])
  323                     break;
  324                 listentry[pos++] = '\t';
  325             }
  326             if (ssh_key_alg(skey->key) != &ssh_dss &&
  327                 ssh_key_alg(skey->key) != &ssh_rsa) {
  328                 /*
  329                  * Remove the bit-count field, which is between the
  330                  * first and second \t.
  331                  */
  332                 int outpos;
  333                 pos = 0;
  334                 while (listentry[pos] && listentry[pos] != '\t')
  335                     pos++;
  336                 outpos = pos;
  337                 pos++;
  338                 while (listentry[pos] && listentry[pos] != '\t')
  339                     pos++;
  340                 while (1) {
  341                     if ((listentry[outpos] = listentry[pos]) == '\0')
  342                         break;
  343                     outpos++;
  344                     pos++;
  345                 }
  346             }
  347 
  348             SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
  349                                (LPARAM) listentry);
  350             sfree(listentry);
  351         }
  352         SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
  353     }
  354 }
  355 
  356 static void win_add_keyfile(Filename *filename)
  357 {
  358     char *err;
  359     int ret;
  360     char *passphrase = NULL;
  361 
  362     /*
  363      * Try loading the key without a passphrase. (Or rather, without a
  364      * _new_ passphrase; pageant_add_keyfile will take care of trying
  365      * all the passphrases we've already stored.)
  366      */
  367     ret = pageant_add_keyfile(filename, NULL, &err);
  368     if (ret == PAGEANT_ACTION_OK) {
  369         goto done;
  370     } else if (ret == PAGEANT_ACTION_FAILURE) {
  371         goto error;
  372     }
  373 
  374     /*
  375      * OK, a passphrase is needed, and we've been given the key
  376      * comment to use in the passphrase prompt.
  377      */
  378     while (1) {
  379         INT_PTR dlgret;
  380         struct PassphraseProcStruct pps;
  381 
  382         pps.passphrase = &passphrase;
  383         pps.comment = err;
  384         dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
  385                                 NULL, PassphraseProc, (LPARAM) &pps);
  386         passphrase_box = NULL;
  387 
  388         if (!dlgret)
  389             goto done;                 /* operation cancelled */
  390 
  391         sfree(err);
  392 
  393         assert(passphrase != NULL);
  394 
  395         ret = pageant_add_keyfile(filename, passphrase, &err);
  396         if (ret == PAGEANT_ACTION_OK) {
  397             goto done;
  398         } else if (ret == PAGEANT_ACTION_FAILURE) {
  399             goto error;
  400         }
  401 
  402         smemclr(passphrase, strlen(passphrase));
  403         sfree(passphrase);
  404         passphrase = NULL;
  405     }
  406 
  407   error:
  408     message_box(err, APPNAME, MB_OK | MB_ICONERROR,
  409                 HELPCTXID(errors_cantloadkey));
  410   done:
  411     if (passphrase) {
  412         smemclr(passphrase, strlen(passphrase));
  413         sfree(passphrase);
  414     }
  415     sfree(err);
  416     return;
  417 }
  418 
  419 /*
  420  * Prompt for a key file to add, and add it.
  421  */
  422 static void prompt_add_keyfile(void)
  423 {
  424     OPENFILENAME of;
  425     char *filelist = snewn(8192, char);
  426 
  427     if (!keypath) keypath = filereq_new();
  428     memset(&of, 0, sizeof(of));
  429     of.hwndOwner = hwnd;
  430     of.lpstrFilter = FILTER_KEY_FILES;
  431     of.lpstrCustomFilter = NULL;
  432     of.nFilterIndex = 1;
  433     of.lpstrFile = filelist;
  434     *filelist = '\0';
  435     of.nMaxFile = 8192;
  436     of.lpstrFileTitle = NULL;
  437     of.lpstrTitle = "Select Private Key File";
  438     of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
  439     if (request_file(keypath, &of, true, false)) {
  440         if(strlen(filelist) > of.nFileOffset) {
  441             /* Only one filename returned? */
  442             Filename *fn = filename_from_str(filelist);
  443             win_add_keyfile(fn);
  444             filename_free(fn);
  445         } else {
  446             /* we are returned a bunch of strings, end to
  447              * end. first string is the directory, the
  448              * rest the filenames. terminated with an
  449              * empty string.
  450              */
  451             char *dir = filelist;
  452             char *filewalker = filelist + strlen(dir) + 1;
  453             while (*filewalker != '\0') {
  454                 char *filename = dupcat(dir, "\\", filewalker, NULL);
  455                 Filename *fn = filename_from_str(filename);
  456                 win_add_keyfile(fn);
  457                 filename_free(fn);
  458                 sfree(filename);
  459                 filewalker += strlen(filewalker) + 1;
  460             }
  461         }
  462 
  463         keylist_update();
  464         pageant_forget_passphrases();
  465     }
  466     sfree(filelist);
  467 }
  468 
  469 /*
  470  * Dialog-box function for the key list box.
  471  */
  472 static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
  473                                 WPARAM wParam, LPARAM lParam)
  474 {
  475     RSAKey *rkey;
  476     ssh2_userkey *skey;
  477 
  478     switch (msg) {
  479       case WM_INITDIALOG:
  480         /*
  481          * Centre the window.
  482          */
  483         {                              /* centre the window */
  484             RECT rs, rd;
  485             HWND hw;
  486 
  487             hw = GetDesktopWindow();
  488             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
  489                 MoveWindow(hwnd,
  490                            (rs.right + rs.left + rd.left - rd.right) / 2,
  491                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
  492                            rd.right - rd.left, rd.bottom - rd.top, true);
  493         }
  494 
  495         if (has_help())
  496             SetWindowLongPtr(hwnd, GWL_EXSTYLE,
  497                              GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
  498                              WS_EX_CONTEXTHELP);
  499         else {
  500             HWND item = GetDlgItem(hwnd, 103);   /* the Help button */
  501             if (item)
  502                 DestroyWindow(item);
  503         }
  504 
  505         keylist = hwnd;
  506         {
  507             static int tabs[] = { 35, 75, 250 };
  508             SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
  509                                sizeof(tabs) / sizeof(*tabs),
  510                                (LPARAM) tabs);
  511         }
  512         keylist_update();
  513         return 0;
  514       case WM_COMMAND:
  515         switch (LOWORD(wParam)) {
  516           case IDOK:
  517           case IDCANCEL:
  518             keylist = NULL;
  519             DestroyWindow(hwnd);
  520             return 0;
  521           case 101:                    /* add key */
  522             if (HIWORD(wParam) == BN_CLICKED ||
  523                 HIWORD(wParam) == BN_DOUBLECLICKED) {
  524                 if (passphrase_box) {
  525                     MessageBeep(MB_ICONERROR);
  526                     SetForegroundWindow(passphrase_box);
  527                     break;
  528                 }
  529                 prompt_add_keyfile();
  530             }
  531             return 0;
  532           case 102:                    /* remove key */
  533             if (HIWORD(wParam) == BN_CLICKED ||
  534                 HIWORD(wParam) == BN_DOUBLECLICKED) {
  535                 int i;
  536                 int rCount, sCount;
  537                 int *selectedArray;
  538 
  539                 /* our counter within the array of selected items */
  540                 int itemNum;
  541 
  542                 /* get the number of items selected in the list */
  543                 int numSelected =
  544                         SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
  545 
  546                 /* none selected? that was silly */
  547                 if (numSelected == 0) {
  548                     MessageBeep(0);
  549                     break;
  550                 }
  551 
  552                 /* get item indices in an array */
  553                 selectedArray = snewn(numSelected, int);
  554                 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
  555                                 numSelected, (WPARAM)selectedArray);
  556 
  557                 itemNum = numSelected - 1;
  558                 rCount = pageant_count_ssh1_keys();
  559                 sCount = pageant_count_ssh2_keys();
  560 
  561                 /* go through the non-rsakeys until we've covered them all,
  562                  * and/or we're out of selected items to check. note that
  563                  * we go *backwards*, to avoid complications from deleting
  564                  * things hence altering the offset of subsequent items
  565                  */
  566                 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
  567                     skey = pageant_nth_ssh2_key(i);
  568 
  569                     if (selectedArray[itemNum] == rCount + i) {
  570                         pageant_delete_ssh2_key(skey);
  571                         ssh_key_free(skey->key);
  572                         sfree(skey);
  573                         itemNum--;
  574                     }
  575                 }
  576 
  577                 /* do the same for the rsa keys */
  578                 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
  579                     rkey = pageant_nth_ssh1_key(i);
  580 
  581                     if(selectedArray[itemNum] == i) {
  582                         pageant_delete_ssh1_key(rkey);
  583                         freersakey(rkey);
  584                         sfree(rkey);
  585                         itemNum--;
  586                     }
  587                 }
  588 
  589                 sfree(selectedArray);
  590                 keylist_update();
  591             }
  592             return 0;
  593           case 103:                    /* help */
  594             if (HIWORD(wParam) == BN_CLICKED ||
  595                 HIWORD(wParam) == BN_DOUBLECLICKED) {
  596                 launch_help(hwnd, WINHELP_CTX_pageant_general);
  597             }
  598             return 0;
  599         }
  600         return 0;
  601       case WM_HELP:
  602         {
  603             int id = ((LPHELPINFO)lParam)->iCtrlId;
  604             const char *topic = NULL;
  605             switch (id) {
  606               case 100: topic = WINHELP_CTX_pageant_keylist; break;
  607               case 101: topic = WINHELP_CTX_pageant_addkey; break;
  608               case 102: topic = WINHELP_CTX_pageant_remkey; break;
  609             }
  610             if (topic) {
  611                 launch_help(hwnd, topic);
  612             } else {
  613                 MessageBeep(0);
  614             }
  615         }
  616         break;
  617       case WM_CLOSE:
  618         keylist = NULL;
  619         DestroyWindow(hwnd);
  620         return 0;
  621     }
  622     return 0;
  623 }
  624 
  625 /* Set up a system tray icon */
  626 static BOOL AddTrayIcon(HWND hwnd)
  627 {
  628     BOOL res;
  629     NOTIFYICONDATA tnid;
  630     HICON hicon;
  631 
  632 #ifdef NIM_SETVERSION
  633     tnid.uVersion = 0;
  634     res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
  635 #endif
  636 
  637     tnid.cbSize = sizeof(NOTIFYICONDATA);
  638     tnid.hWnd = hwnd;
  639     tnid.uID = 1;              /* unique within this systray use */
  640     tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  641     tnid.uCallbackMessage = WM_SYSTRAY;
  642     tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
  643     strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
  644 
  645     res = Shell_NotifyIcon(NIM_ADD, &tnid);
  646 
  647     if (hicon) DestroyIcon(hicon);
  648 
  649     return res;
  650 }
  651 
  652 /* Update the saved-sessions menu. */
  653 static void update_sessions(void)
  654 {
  655     int num_entries;
  656     HKEY hkey;
  657     TCHAR buf[MAX_PATH + 1];
  658     MENUITEMINFO mii;
  659     strbuf *sb;
  660 
  661     int index_key, index_menu;
  662 
  663     if (!putty_path)
  664         return;
  665 
  666     if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
  667         return;
  668 
  669     for(num_entries = GetMenuItemCount(session_menu);
  670         num_entries > initial_menuitems_count;
  671         num_entries--)
  672         RemoveMenu(session_menu, 0, MF_BYPOSITION);
  673 
  674     index_key = 0;
  675     index_menu = 0;
  676 
  677     sb = strbuf_new();
  678     while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
  679         if(strcmp(buf, PUTTY_DEFAULT) != 0) {
  680             sb->len = 0;
  681             unescape_registry_key(buf, sb);
  682 
  683             memset(&mii, 0, sizeof(mii));
  684             mii.cbSize = sizeof(mii);
  685             mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
  686             mii.fType = MFT_STRING;
  687             mii.fState = MFS_ENABLED;
  688             mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
  689             mii.dwTypeData = sb->s;
  690             InsertMenuItem(session_menu, index_menu, true, &mii);
  691             index_menu++;
  692         }
  693         index_key++;
  694     }
  695     strbuf_free(sb);
  696 
  697     RegCloseKey(hkey);
  698 
  699     if(index_menu == 0) {
  700         mii.cbSize = sizeof(mii);
  701         mii.fMask = MIIM_TYPE | MIIM_STATE;
  702         mii.fType = MFT_STRING;
  703         mii.fState = MFS_GRAYED;
  704         mii.dwTypeData = _T("(No sessions)");
  705         InsertMenuItem(session_menu, index_menu, true, &mii);
  706     }
  707 }
  708 
  709 #ifndef NO_SECURITY
  710 /*
  711  * Versions of Pageant prior to 0.61 expected this SID on incoming
  712  * communications. For backwards compatibility, and more particularly
  713  * for compatibility with derived works of PuTTY still using the old
  714  * Pageant client code, we accept it as an alternative to the one
  715  * returned from get_user_sid() in winpgntc.c.
  716  */
  717 PSID get_default_sid(void)
  718 {
  719     HANDLE proc = NULL;
  720     DWORD sidlen;
  721     PSECURITY_DESCRIPTOR psd = NULL;
  722     PSID sid = NULL, copy = NULL, ret = NULL;
  723 
  724     if ((proc = OpenProcess(MAXIMUM_ALLOWED, false,
  725                             GetCurrentProcessId())) == NULL)
  726         goto cleanup;
  727 
  728     if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
  729                           &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
  730         goto cleanup;
  731 
  732     sidlen = GetLengthSid(sid);
  733 
  734     copy = (PSID)smalloc(sidlen);
  735 
  736     if (!CopySid(sidlen, copy, sid))
  737         goto cleanup;
  738 
  739     /* Success. Move sid into the return value slot, and null it out
  740      * to stop the cleanup code freeing it. */
  741     ret = copy;
  742     copy = NULL;
  743 
  744   cleanup:
  745     if (proc != NULL)
  746         CloseHandle(proc);
  747     if (psd != NULL)
  748         LocalFree(psd);
  749     if (copy != NULL)
  750         sfree(copy);
  751 
  752     return ret;
  753 }
  754 #endif
  755 
  756 struct PageantReply {
  757     char *buf;
  758     size_t size, len;
  759     bool overflowed;
  760     BinarySink_IMPLEMENTATION;
  761 };
  762 
  763 static void pageant_reply_BinarySink_write(
  764     BinarySink *bs, const void *data, size_t len)
  765 {
  766     struct PageantReply *rep = BinarySink_DOWNCAST(bs, struct PageantReply);
  767     if (!rep->overflowed && len <= rep->size - rep->len) {
  768         memcpy(rep->buf + rep->len, data, len);
  769         rep->len += len;
  770     } else {
  771         rep->overflowed = true;
  772     }
  773 }
  774 
  775 static char *answer_filemapping_message(const char *mapname)
  776 {
  777     HANDLE maphandle = INVALID_HANDLE_VALUE;
  778     void *mapaddr = NULL;
  779     char *err = NULL;
  780     size_t mapsize;
  781     unsigned msglen;
  782     struct PageantReply reply;
  783 
  784 #ifndef NO_SECURITY
  785     PSID mapsid = NULL;
  786     PSID expectedsid = NULL;
  787     PSID expectedsid_bc = NULL;
  788     PSECURITY_DESCRIPTOR psd = NULL;
  789 #endif
  790 
  791     reply.buf = NULL;
  792 
  793 #ifdef DEBUG_IPC
  794     debug("mapname = \"%s\"\n", mapname);
  795 #endif
  796 
  797     maphandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, mapname);
  798     if (maphandle == NULL || maphandle == INVALID_HANDLE_VALUE) {
  799         err = dupprintf("OpenFileMapping(\"%s\"): %s",
  800                         mapname, win_strerror(GetLastError()));
  801         goto cleanup;
  802     }
  803 
  804 #ifdef DEBUG_IPC
  805     debug("maphandle = %p\n", maphandle);
  806 #endif
  807 
  808 #ifndef NO_SECURITY
  809     if (has_security) {
  810         DWORD retd;
  811 
  812         if ((expectedsid = get_user_sid()) == NULL) {
  813             err = dupstr("unable to get user SID");
  814             goto cleanup;
  815         }
  816 
  817         if ((expectedsid_bc = get_default_sid()) == NULL) {
  818             err = dupstr("unable to get default SID");
  819             goto cleanup;
  820         }
  821 
  822         if ((retd = p_GetSecurityInfo(
  823                  maphandle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
  824                  &mapsid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)) {
  825             err = dupprintf("unable to get owner of file mapping: "
  826                             "GetSecurityInfo returned: %s",
  827                             win_strerror(retd));
  828             goto cleanup;
  829         }
  830 
  831 #ifdef DEBUG_IPC
  832         {
  833             LPTSTR ours, ours2, theirs;
  834             ConvertSidToStringSid(mapsid, &theirs);
  835             ConvertSidToStringSid(expectedsid, &ours);
  836             ConvertSidToStringSid(expectedsid_bc, &ours2);
  837             debug("got sids:\n  oursnew=%s\n  oursold=%s\n"
  838                   "  theirs=%s\n", ours, ours2, theirs);
  839             LocalFree(ours);
  840             LocalFree(ours2);
  841             LocalFree(theirs);
  842         }
  843 #endif
  844 
  845         if (!EqualSid(mapsid, expectedsid) &&
  846             !EqualSid(mapsid, expectedsid_bc)) {
  847             err = dupstr("wrong owning SID of file mapping");
  848             goto cleanup;
  849         }
  850     } else
  851 #endif /* NO_SECURITY */
  852     {
  853 #ifdef DEBUG_IPC
  854         debug("security APIs not present\n");
  855 #endif
  856     }
  857 
  858     mapaddr = MapViewOfFile(maphandle, FILE_MAP_WRITE, 0, 0, 0);
  859     if (!mapaddr) {
  860         err = dupprintf("unable to obtain view of file mapping: %s",
  861                         win_strerror(GetLastError()));
  862         goto cleanup;
  863     }
  864 
  865 #ifdef DEBUG_IPC
  866     debug("mapped address = %p\n", mapaddr);
  867 #endif
  868 
  869     {
  870         MEMORY_BASIC_INFORMATION mbi;
  871         size_t mbiSize = VirtualQuery(mapaddr, &mbi, sizeof(mbi));
  872         if (mbiSize == 0) {
  873             err = dupprintf("unable to query view of file mapping: %s",
  874                             win_strerror(GetLastError()));
  875             goto cleanup;
  876         }
  877         if (mbiSize < (offsetof(MEMORY_BASIC_INFORMATION, RegionSize) +
  878                        sizeof(mbi.RegionSize))) {
  879             err = dupstr("VirtualQuery returned too little data to get "
  880                          "region size");
  881             goto cleanup;
  882         }
  883 
  884         mapsize = mbi.RegionSize;
  885     }
  886 #ifdef DEBUG_IPC
  887     debug("region size = %zd\n", mapsize);
  888 #endif
  889     if (mapsize < 5) {
  890         err = dupstr("mapping smaller than smallest possible request");
  891         goto cleanup;
  892     }
  893 
  894     msglen = GET_32BIT_MSB_FIRST((unsigned char *)mapaddr);
  895 
  896 #ifdef DEBUG_IPC
  897     debug("msg length=%08x, msg type=%02x\n",
  898           msglen, (unsigned)((unsigned char *) mapaddr)[4]);
  899 #endif
  900 
  901     reply.buf = (char *)mapaddr + 4;
  902     reply.size = mapsize - 4;
  903     reply.len = 0;
  904     reply.overflowed = false;
  905     BinarySink_INIT(&reply, pageant_reply_BinarySink_write);
  906 
  907     if (msglen > mapsize - 4) {
  908         pageant_failure_msg(BinarySink_UPCAST(&reply),
  909                             "incoming length field too large", NULL, NULL);
  910     } else {
  911         pageant_handle_msg(BinarySink_UPCAST(&reply),
  912                            (unsigned char *)mapaddr + 4, msglen, NULL, NULL);
  913         if (reply.overflowed) {
  914             reply.len = 0;
  915             reply.overflowed = false;
  916             pageant_failure_msg(BinarySink_UPCAST(&reply),
  917                                 "output would overflow message buffer",
  918                                 NULL, NULL);
  919         }
  920     }
  921 
  922     if (reply.overflowed) {
  923         err = dupstr("even failure message overflows buffer");
  924         goto cleanup;
  925     }
  926 
  927     /* Write in the initial length field, and we're done. */
  928     PUT_32BIT_MSB_FIRST(((unsigned char *)mapaddr), reply.len);
  929 
  930   cleanup:
  931     /* expectedsid has the lifetime of the program, so we don't free it */
  932     sfree(expectedsid_bc);
  933     if (psd)
  934         LocalFree(psd);
  935     if (mapaddr)
  936         UnmapViewOfFile(mapaddr);
  937     if (maphandle != NULL && maphandle != INVALID_HANDLE_VALUE)
  938         CloseHandle(maphandle);
  939     return err;
  940 }
  941 
  942 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
  943                                 WPARAM wParam, LPARAM lParam)
  944 {
  945     static bool menuinprogress;
  946     static UINT msgTaskbarCreated = 0;
  947 
  948     switch (message) {
  949       case WM_CREATE:
  950         msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
  951         break;
  952       default:
  953         if (message==msgTaskbarCreated) {
  954             /*
  955              * Explorer has been restarted, so the tray icon will
  956              * have been lost.
  957              */
  958             AddTrayIcon(hwnd);
  959         }
  960         break;
  961 
  962       case WM_SYSTRAY:
  963         if (lParam == WM_RBUTTONUP) {
  964             POINT cursorpos;
  965             GetCursorPos(&cursorpos);
  966             PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
  967         } else if (lParam == WM_LBUTTONDBLCLK) {
  968             /* Run the default menu item. */
  969             UINT menuitem = GetMenuDefaultItem(systray_menu, false, 0);
  970             if (menuitem != -1)
  971                 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
  972         }
  973         break;
  974       case WM_SYSTRAY2:
  975         if (!menuinprogress) {
  976             menuinprogress = true;
  977             update_sessions();
  978             SetForegroundWindow(hwnd);
  979             TrackPopupMenu(systray_menu,
  980                            TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
  981                            TPM_RIGHTBUTTON,
  982                            wParam, lParam, 0, hwnd, NULL);
  983             menuinprogress = false;
  984         }
  985         break;
  986       case WM_COMMAND:
  987       case WM_SYSCOMMAND:
  988         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
  989           case IDM_PUTTY:
  990             {
  991                 TCHAR cmdline[10];
  992                 cmdline[0] = '\0';
  993                 if (restrict_putty_acl)
  994                     strcat(cmdline, "&R");
  995 
  996                 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline,
  997                                          _T(""), SW_SHOW) <= 32) {
  998                     MessageBox(NULL, "Unable to execute PuTTY!",
  999                                "Error", MB_OK | MB_ICONERROR);
 1000                 }
 1001             }
 1002             break;
 1003           case IDM_CLOSE:
 1004             if (passphrase_box)
 1005                 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
 1006             SendMessage(hwnd, WM_CLOSE, 0, 0);
 1007             break;
 1008           case IDM_VIEWKEYS:
 1009             if (!keylist) {
 1010                 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
 1011                                        NULL, KeyListProc);
 1012                 ShowWindow(keylist, SW_SHOWNORMAL);
 1013             }
 1014             /*
 1015              * Sometimes the window comes up minimised / hidden for
 1016              * no obvious reason. Prevent this. This also brings it
 1017              * to the front if it's already present (the user
 1018              * selected View Keys because they wanted to _see_ the
 1019              * thing).
 1020              */
 1021             SetForegroundWindow(keylist);
 1022             SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
 1023                          SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
 1024             break;
 1025           case IDM_ADDKEY:
 1026             if (passphrase_box) {
 1027                 MessageBeep(MB_ICONERROR);
 1028                 SetForegroundWindow(passphrase_box);
 1029                 break;
 1030             }
 1031             prompt_add_keyfile();
 1032             break;
 1033           case IDM_ABOUT:
 1034             if (!aboutbox) {
 1035                 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
 1036                                         NULL, AboutProc);
 1037                 ShowWindow(aboutbox, SW_SHOWNORMAL);
 1038                 /*
 1039                  * Sometimes the window comes up minimised / hidden
 1040                  * for no obvious reason. Prevent this.
 1041                  */
 1042                 SetForegroundWindow(aboutbox);
 1043                 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
 1044                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
 1045             }
 1046             break;
 1047           case IDM_HELP:
 1048             launch_help(hwnd, WINHELP_CTX_pageant_general);
 1049             break;
 1050           default:
 1051             {
 1052                 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
 1053                     MENUITEMINFO mii;
 1054                     TCHAR buf[MAX_PATH + 1];
 1055                     TCHAR param[MAX_PATH + 1];
 1056                     memset(&mii, 0, sizeof(mii));
 1057                     mii.cbSize = sizeof(mii);
 1058                     mii.fMask = MIIM_TYPE;
 1059                     mii.cch = MAX_PATH;
 1060                     mii.dwTypeData = buf;
 1061                     GetMenuItemInfo(session_menu, wParam, false, &mii);
 1062                     param[0] = '\0';
 1063                     if (restrict_putty_acl)
 1064                         strcat(param, "&R");
 1065                     strcat(param, "@");
 1066                     strcat(param, mii.dwTypeData);
 1067                     if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param,
 1068                                          _T(""), SW_SHOW) <= 32) {
 1069                         MessageBox(NULL, "Unable to execute PuTTY!", "Error",
 1070                                    MB_OK | MB_ICONERROR);
 1071                     }
 1072                 }
 1073             }
 1074             break;
 1075         }
 1076         break;
 1077       case WM_DESTROY:
 1078         quit_help(hwnd);
 1079         PostQuitMessage(0);
 1080         return 0;
 1081       case WM_COPYDATA:
 1082         {
 1083             COPYDATASTRUCT *cds;
 1084             char *mapname, *err;
 1085 
 1086             cds = (COPYDATASTRUCT *) lParam;
 1087             if (cds->dwData != AGENT_COPYDATA_ID)
 1088                 return 0;              /* not our message, mate */
 1089             mapname = (char *) cds->lpData;
 1090             if (mapname[cds->cbData - 1] != '\0')
 1091                 return 0;              /* failure to be ASCIZ! */
 1092             err = answer_filemapping_message(mapname);
 1093             if (err) {
 1094 #ifdef DEBUG_IPC
 1095                 debug("IPC failed: %s\n", err);
 1096 #endif
 1097                 sfree(err);
 1098                 return 0;
 1099             }
 1100             return 1;
 1101         }
 1102     }
 1103 
 1104     return DefWindowProc(hwnd, message, wParam, lParam);
 1105 }
 1106 
 1107 /*
 1108  * Fork and Exec the command in cmdline. [DBW]
 1109  */
 1110 void spawn_cmd(const char *cmdline, const char *args, int show)
 1111 {
 1112     if (ShellExecute(NULL, _T("open"), cmdline,
 1113                      args, NULL, show) <= (HINSTANCE) 32) {
 1114         char *msg;
 1115         msg = dupprintf("Failed to run \"%s\": %s", cmdline,
 1116                         win_strerror(GetLastError()));
 1117         MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
 1118         sfree(msg);
 1119     }
 1120 }
 1121 
 1122 void agent_schedule_callback(void (*callback)(void *, void *, int),
 1123                              void *callback_ctx, void *data, int len)
 1124 {
 1125     unreachable("all Pageant's own agent requests should be synchronous");
 1126 }
 1127 
 1128 void cleanup_exit(int code)
 1129 {
 1130     shutdown_help();
 1131     exit(code);
 1132 }
 1133 
 1134 int flags = FLAG_SYNCAGENT;
 1135 
 1136 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 1137 {
 1138     WNDCLASS wndclass;
 1139     MSG msg;
 1140     const char *command = NULL;
 1141     bool added_keys = false;
 1142     int argc, i;
 1143     char **argv, **argstart;
 1144 
 1145     dll_hijacking_protection();
 1146 
 1147     hinst = inst;
 1148     hwnd = NULL;
 1149 
 1150     /*
 1151      * Determine whether we're an NT system (should have security
 1152      * APIs) or a non-NT system (don't do security).
 1153      */
 1154     init_winver();
 1155     has_security = (osPlatformId == VER_PLATFORM_WIN32_NT);
 1156 
 1157     if (has_security) {
 1158 #ifndef NO_SECURITY
 1159         /*
 1160          * Attempt to get the security API we need.
 1161          */
 1162         if (!got_advapi()) {
 1163             MessageBox(NULL,
 1164                        "Unable to access security APIs. Pageant will\n"
 1165                        "not run, in case it causes a security breach.",
 1166                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
 1167             return 1;
 1168         }
 1169 #else
 1170         MessageBox(NULL,
 1171                    "This program has been compiled for Win9X and will\n"
 1172                    "not run on NT, in case it causes a security breach.",
 1173                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
 1174         return 1;
 1175 #endif
 1176     }
 1177 
 1178     /*
 1179      * See if we can find our Help file.
 1180      */
 1181     init_help();
 1182 
 1183     /*
 1184      * Look for the PuTTY binary (we will enable the saved session
 1185      * submenu if we find it).
 1186      */
 1187     {
 1188         char b[2048], *p, *q, *r;
 1189         FILE *fp;
 1190         GetModuleFileName(NULL, b, sizeof(b) - 16);
 1191         r = b;
 1192         p = strrchr(b, '\\');
 1193         if (p && p >= r) r = p+1;
 1194         q = strrchr(b, ':');
 1195         if (q && q >= r) r = q+1;
 1196         strcpy(r, "putty.exe");
 1197         if ( (fp = fopen(b, "r")) != NULL) {
 1198             putty_path = dupstr(b);
 1199             fclose(fp);
 1200         } else
 1201             putty_path = NULL;
 1202     }
 1203 
 1204     /*
 1205      * Find out if Pageant is already running.
 1206      */
 1207     already_running = agent_exists();
 1208 
 1209     /*
 1210      * Initialise the cross-platform Pageant code.
 1211      */
 1212     if (!already_running) {
 1213         pageant_init();
 1214     }
 1215 
 1216     /*
 1217      * Process the command line and add keys as listed on it.
 1218      */
 1219     split_into_argv(cmdline, &argc, &argv, &argstart);
 1220     for (i = 0; i < argc; i++) {
 1221         if (!strcmp(argv[i], "-pgpfp")) {
 1222             pgp_fingerprints();
 1223             return 1;
 1224         } else if (!strcmp(argv[i], "-restrict-acl") ||
 1225                    !strcmp(argv[i], "-restrict_acl") ||
 1226                    !strcmp(argv[i], "-restrictacl")) {
 1227             restrict_process_acl();
 1228         } else if (!strcmp(argv[i], "-restrict-putty-acl") ||
 1229                    !strcmp(argv[i], "-restrict_putty_acl")) {
 1230             restrict_putty_acl = true;
 1231         } else if (!strcmp(argv[i], "-c")) {
 1232             /*
 1233              * If we see `-c', then the rest of the
 1234              * command line should be treated as a
 1235              * command to be spawned.
 1236              */
 1237             if (i < argc-1)
 1238                 command = argstart[i+1];
 1239             else
 1240                 command = "";
 1241             break;
 1242         } else {
 1243             Filename *fn = filename_from_str(argv[i]);
 1244             win_add_keyfile(fn);
 1245             filename_free(fn);
 1246             added_keys = true;
 1247         }
 1248     }
 1249 
 1250     /*
 1251      * Forget any passphrase that we retained while going over
 1252      * command line keyfiles.
 1253      */
 1254     pageant_forget_passphrases();
 1255 
 1256     if (command) {
 1257         char *args;
 1258         if (command[0] == '"')
 1259             args = strchr(++command, '"');
 1260         else
 1261             args = strchr(command, ' ');
 1262         if (args) {
 1263             *args++ = 0;
 1264             while(*args && isspace(*args)) args++;
 1265         }
 1266         spawn_cmd(command, args, show);
 1267     }
 1268 
 1269     /*
 1270      * If Pageant was already running, we leave now. If we haven't
 1271      * even taken any auxiliary action (spawned a command or added
 1272      * keys), complain.
 1273      */
 1274     if (already_running) {
 1275         if (!command && !added_keys) {
 1276             MessageBox(NULL, "Pageant is already running", "Pageant Error",
 1277                        MB_ICONERROR | MB_OK);
 1278         }
 1279         return 0;
 1280     }
 1281 
 1282     if (!prev) {
 1283         wndclass.style = 0;
 1284         wndclass.lpfnWndProc = WndProc;
 1285         wndclass.cbClsExtra = 0;
 1286         wndclass.cbWndExtra = 0;
 1287         wndclass.hInstance = inst;
 1288         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
 1289         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
 1290         wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
 1291         wndclass.lpszMenuName = NULL;
 1292         wndclass.lpszClassName = APPNAME;
 1293 
 1294         RegisterClass(&wndclass);
 1295     }
 1296 
 1297     keylist = NULL;
 1298 
 1299     hwnd = CreateWindow(APPNAME, APPNAME,
 1300                         WS_OVERLAPPEDWINDOW | WS_VSCROLL,
 1301                         CW_USEDEFAULT, CW_USEDEFAULT,
 1302                         100, 100, NULL, NULL, inst, NULL);
 1303 
 1304     /* Set up a system tray icon */
 1305     AddTrayIcon(hwnd);
 1306 
 1307     /* Accelerators used: nsvkxa */
 1308     systray_menu = CreatePopupMenu();
 1309     if (putty_path) {
 1310         session_menu = CreateMenu();
 1311         AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
 1312         AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
 1313                    (UINT_PTR) session_menu, "&Saved Sessions");
 1314         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
 1315     }
 1316     AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
 1317            "&View Keys");
 1318     AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
 1319     AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
 1320     if (has_help())
 1321         AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
 1322     AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
 1323     AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
 1324     AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
 1325     initial_menuitems_count = GetMenuItemCount(session_menu);
 1326 
 1327     /* Set the default menu item. */
 1328     SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, false);
 1329 
 1330     ShowWindow(hwnd, SW_HIDE);
 1331 
 1332     /*
 1333      * Main message loop.
 1334      */
 1335     while (GetMessage(&msg, NULL, 0, 0) == 1) {
 1336         if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
 1337             !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
 1338             TranslateMessage(&msg);
 1339             DispatchMessage(&msg);
 1340         }
 1341     }
 1342 
 1343     /* Clean up the system tray icon */
 1344     {
 1345         NOTIFYICONDATA tnid;
 1346 
 1347         tnid.cbSize = sizeof(NOTIFYICONDATA);
 1348         tnid.hWnd = hwnd;
 1349         tnid.uID = 1;
 1350 
 1351         Shell_NotifyIcon(NIM_DELETE, &tnid);
 1352 
 1353         DestroyMenu(systray_menu);
 1354     }
 1355 
 1356     if (keypath) filereq_free(keypath);
 1357 
 1358     cleanup_exit(msg.wParam);
 1359     return msg.wParam;                 /* just in case optimiser complains */
 1360 }