Fix a few more memory and resource leaks.
[sgt/putty] / windows / winpgen.c
index 1e2075d..33d76c6 100644 (file)
@@ -5,6 +5,7 @@
 #include <time.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #define PUTTY_DO_GLOBALS
 
 #define ICON_BIG        1
 #endif
 
-#define WM_DONEKEY (WM_XUSER + 1)
+#define WM_DONEKEY (WM_APP + 1)
 
-#define DEFAULT_KEYSIZE 1024
-
-static int requested_help;
+#define DEFAULT_KEYSIZE 2048
 
 static char *cmdline_keyfile = NULL;
 
@@ -42,6 +41,22 @@ void modalfatalbox(char *fmt, ...)
     exit(1);
 }
 
+/*
+ * Print a non-fatal message box and do not exit.
+ */
+void nonfatal(char *fmt, ...)
+{
+    va_list ap;
+    char *stuff;
+
+    va_start(ap, fmt);
+    stuff = dupvprintf(fmt, ap);
+    va_end(ap);
+    MessageBox(NULL, stuff, "PuTTYgen Error",
+              MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+    sfree(stuff);
+}
+
 /* ----------------------------------------------------------------------
  * Progress report code. This is really horrible :-)
  */
@@ -118,10 +133,8 @@ static void progress_update(void *param, int action, int phase, int iprogress)
 
 extern char ver[];
 
-#define PASSPHRASE_MAXLEN 512
-
 struct PassphraseProcStruct {
-    char *passphrase;
+    char **passphrase;
     char *comment;
 };
 
@@ -131,7 +144,7 @@ struct PassphraseProcStruct {
 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
                                   WPARAM wParam, LPARAM lParam)
 {
-    static char *passphrase = NULL;
+    static char **passphrase = NULL;
     struct PassphraseProcStruct *p;
 
     switch (msg) {
@@ -159,8 +172,9 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
        passphrase = p->passphrase;
        if (p->comment)
            SetDlgItemText(hwnd, 101, p->comment);
-       *passphrase = 0;
-       SetDlgItemText(hwnd, 102, passphrase);
+        burnstr(*passphrase);
+        *passphrase = dupstr("");
+       SetDlgItemText(hwnd, 102, *passphrase);
        return 0;
       case WM_COMMAND:
        switch (LOWORD(wParam)) {
@@ -175,9 +189,8 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
            return 0;
          case 102:                    /* edit box */
            if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
-               GetDlgItemText(hwnd, 102, passphrase,
-                              PASSPHRASE_MAXLEN - 1);
-               passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
+                burnstr(*passphrase);
+                *passphrase = GetDlgItemText_alloc(hwnd, 102);
            }
            return 0;
        }
@@ -407,11 +420,11 @@ static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
     char *dec1, *dec2;
     FILE *fp;
 
-    dec1 = bignum_decimal(key->exponent);
-    dec2 = bignum_decimal(key->modulus);
     fp = fopen(filename, "wb");
     if (!fp)
        return 0;
+    dec1 = bignum_decimal(key->exponent);
+    dec2 = bignum_decimal(key->modulus);
     fprintf(fp, "%d %s %s %s\n",
            bignum_bitcount(key->modulus), dec1, dec2, key->comment);
     fclose(fp);
@@ -427,7 +440,7 @@ void old_keyfile_warning(void)
 {
     static const char mbtitle[] = "PuTTY Key File Warning";
     static const char message[] =
-       "You are loading an SSH 2 private key which has an\n"
+       "You are loading an SSH-2 private key which has an\n"
        "old version of the file format. This means your key\n"
        "file is not fully tamperproof. Future versions of\n"
        "PuTTY may stop supporting this private key format,\n"
@@ -617,26 +630,25 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
 }
 
 void load_key_file(HWND hwnd, struct MainDlgState *state,
-                  Filename filename, int was_import_cmd)
+                  Filename *filename, int was_import_cmd)
 {
-    char passphrase[PASSPHRASE_MAXLEN];
+    char *passphrase;
     int needs_pass;
     int type, realtype;
     int ret;
     const char *errmsg = NULL;
     char *comment;
-    struct PassphraseProcStruct pps;
     struct RSAKey newkey1;
     struct ssh2_userkey *newkey2 = NULL;
 
-    type = realtype = key_type(&filename);
+    type = realtype = key_type(filename);
     if (type != SSH_KEYTYPE_SSH1 &&
        type != SSH_KEYTYPE_SSH2 &&
        !import_possible(type)) {
        char *msg = dupprintf("Couldn't load private key (%s)",
                              key_type_to_str(type));
-       MessageBox(NULL, msg,
-                  "PuTTYgen Error", MB_OK | MB_ICONERROR);
+       message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+                   HELPCTXID(errors_cantloadkey));
        sfree(msg);
        return;
     }
@@ -648,43 +660,44 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
     }
 
     comment = NULL;
+    passphrase = NULL;
     if (realtype == SSH_KEYTYPE_SSH1)
-       needs_pass = rsakey_encrypted(&filename, &comment);
+       needs_pass = rsakey_encrypted(filename, &comment);
     else if (realtype == SSH_KEYTYPE_SSH2)
-       needs_pass =
-       ssh2_userkey_encrypted(&filename, &comment);
+       needs_pass = ssh2_userkey_encrypted(filename, &comment);
     else
-       needs_pass = import_encrypted(&filename, realtype,
-                                     &comment);
-    pps.passphrase = passphrase;
-    pps.comment = comment;
+       needs_pass = import_encrypted(filename, realtype, &comment);
     do {
+        burnstr(passphrase);
+        passphrase = NULL;
+
        if (needs_pass) {
            int dlgret;
+            struct PassphraseProcStruct pps;
+            pps.passphrase = &passphrase;
+            pps.comment = comment;
            dlgret = DialogBoxParam(hinst,
                                    MAKEINTRESOURCE(210),
                                    NULL, PassphraseProc,
-                                   (LPARAM) & pps);
+                                   (LPARAM) &pps);
            if (!dlgret) {
                ret = -2;
                break;
            }
+            assert(passphrase != NULL);
        } else
-           *passphrase = '\0';
+           passphrase = dupstr("");
        if (type == SSH_KEYTYPE_SSH1) {
            if (realtype == type)
-               ret = loadrsakey(&filename, &newkey1,
-                                passphrase, &errmsg);
+               ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
            else
-               ret = import_ssh1(&filename, realtype,
-                                 &newkey1, passphrase, &errmsg);
+               ret = import_ssh1(filename, realtype, &newkey1,
+                                  passphrase, &errmsg);
        } else {
            if (realtype == type)
-               newkey2 = ssh2_load_userkey(&filename,
-                                           passphrase, &errmsg);
+               newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
            else
-               newkey2 = import_ssh2(&filename, realtype,
-                                     passphrase, &errmsg);
+               newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
            if (newkey2 == SSH2_WRONG_PASSPHRASE)
                ret = -1;
            else if (!newkey2)
@@ -697,7 +710,8 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
        sfree(comment);
     if (ret == 0) {
        char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
-       MessageBox(NULL, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR);
+       message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+                   HELPCTXID(errors_cantloadkey));
        sfree(msg);
     } else if (ret == 1) {
        /*
@@ -785,6 +799,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
                       MB_OK | MB_ICONINFORMATION);
        }
     }
+    burnstr(passphrase);
 }
 
 /*
@@ -801,16 +816,16 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
 
     switch (msg) {
       case WM_INITDIALOG:
-        if (help_path)
-            SetWindowLong(hwnd, GWL_EXSTYLE,
-                          GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
+        if (has_help())
+            SetWindowLongPtr(hwnd, GWL_EXSTYLE,
+                            GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
+                            WS_EX_CONTEXTHELP);
         else {
             /*
              * If we add a Help button, this is where we destroy it
              * if the help file isn't present.
              */
         }
-        requested_help = FALSE;
        SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
                    (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
 
@@ -819,7 +834,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        state->collecting_entropy = FALSE;
        state->entropy = NULL;
        state->key_exists = FALSE;
-       SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
+       SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
        {
            HMENU menu, menu1;
 
@@ -837,9 +852,9 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            menu1 = CreateMenu();
            AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
            AppendMenu(menu1, MF_SEPARATOR, 0, 0);
-           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
-           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
-           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
+           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
+           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
+           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
            AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
            state->keymenu = menu1;
 
@@ -856,7 +871,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
 
            menu1 = CreateMenu();
            AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
-           if (help_path)
+           if (has_help())
                AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
            AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
 
@@ -916,9 +931,9 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            endbox(&cp);
            beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
            radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
-                     "SSH&1 (RSA)", IDC_KEYSSH1,
-                     "SSH2 &RSA", IDC_KEYSSH2RSA,
-                     "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
+                     "SSH-&1 (RSA)", IDC_KEYSSH1,
+                     "SSH-2 &RSA", IDC_KEYSSH2RSA,
+                     "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
            staticedit(&cp, "Number of &bits in a generated key:",
                       IDC_BITSSTATIC, IDC_BITS, 20);
            endbox(&cp);
@@ -939,12 +954,15 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        /*
         * Load a key file if one was provided on the command line.
         */
-       if (cmdline_keyfile)
-           load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
+       if (cmdline_keyfile) {
+            Filename *fn = filename_from_str(cmdline_keyfile);
+           load_key_file(hwnd, state, fn, 0);
+            filename_free(fn);
+        }
 
        return 1;
       case WM_MOUSEMOVE:
-       state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+       state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
        if (state->collecting_entropy &&
            state->entropy && state->entropy_got < state->entropy_required) {
            state->entropy[state->entropy_got++] = lParam;
@@ -959,7 +977,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                 * Seed the entropy pool
                 */
                random_add_heavynoise(state->entropy, state->entropy_size);
-               memset(state->entropy, 0, state->entropy_size);
+               smemclr(state->entropy, state->entropy_size);
                sfree(state->entropy);
                state->collecting_entropy = FALSE;
 
@@ -995,7 +1013,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
          case IDC_KEYSSH2DSA:
            {
                state = (struct MainDlgState *)
-                   GetWindowLong(hwnd, GWL_USERDATA);
+                   GetWindowLongPtr(hwnd, GWLP_USERDATA);
                if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
                    CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
                                     LOWORD(wParam));
@@ -1009,7 +1027,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
          case IDC_COMMENTEDIT:
            if (HIWORD(wParam) == EN_CHANGE) {
                state = (struct MainDlgState *)
-                   GetWindowLong(hwnd, GWL_USERDATA);
+                   GetWindowLongPtr(hwnd, GWLP_USERDATA);
                if (state->key_exists) {
                    HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
                    int len = GetWindowTextLength(editctl);
@@ -1036,11 +1054,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
          case IDC_GIVEHELP:
             if (HIWORD(wParam) == BN_CLICKED ||
                 HIWORD(wParam) == BN_DOUBLECLICKED) {
-                if (help_path) {
-                    WinHelp(hwnd, help_path, HELP_COMMAND,
-                            (DWORD)"JI(`',`puttygen.general')");
-                    requested_help = TRUE;
-                }
+               launch_help(hwnd, WINHELP_CTX_puttygen_general);
             }
            return 0;
          case IDC_GENERATE:
@@ -1048,7 +1062,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                 HIWORD(wParam) != BN_DOUBLECLICKED)
                break;
            state =
-               (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
            if (!state->generation_thread_exists) {
                BOOL ok;
                state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
@@ -1104,11 +1118,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            if (HIWORD(wParam) != BN_CLICKED)
                break;
            state =
-               (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
            if (state->key_exists) {
                char filename[FILENAME_MAX];
-               char passphrase[PASSPHRASE_MAXLEN];
-               char passphrase2[PASSPHRASE_MAXLEN];
+               char *passphrase, *passphrase2;
                 int type, realtype;
 
                 if (state->ssh2)
@@ -1126,7 +1139,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                 if (type != realtype &&
                     import_target_type(type) != realtype) {
                     char msg[256];
-                    sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
+                    sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
                             " format", (state->ssh2 ? 2 : 1),
                             (state->ssh2 ? 1 : 2));
                    MessageBox(hwnd, msg,
@@ -1134,16 +1147,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                    break;
                 }
 
-               GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
-                              passphrase, sizeof(passphrase));
-               GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
-                              passphrase2, sizeof(passphrase2));
+               passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
+               passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
                if (strcmp(passphrase, passphrase2)) {
                    MessageBox(hwnd,
                               "The two passphrases given do not match.",
                               "PuTTYgen Error", MB_OK | MB_ICONERROR);
+                    burnstr(passphrase);
+                    burnstr(passphrase2);
                    break;
                }
+                burnstr(passphrase2);
                if (!*passphrase) {
                    int ret;
                    ret = MessageBox(hwnd,
@@ -1151,8 +1165,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                                     "without a passphrase to protect it?",
                                     "PuTTYgen Warning",
                                     MB_YESNO | MB_ICONWARNING);
-                   if (ret != IDYES)
-                       break;
+                   if (ret != IDYES) {
+                        burnstr(passphrase);
+                        break;
+                    }
                }
                if (prompt_keyfile(hwnd, "Save private key as:",
                                   filename, 1, (type == realtype))) {
@@ -1166,40 +1182,45 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                        ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
                                         MB_YESNO | MB_ICONWARNING);
                        sfree(buffer);
-                       if (ret != IDYES)
+                       if (ret != IDYES) {
+                            burnstr(passphrase);
                            break;
+                        }
                    }
 
                    if (state->ssh2) {
-                       Filename fn = filename_from_str(filename);
+                       Filename *fn = filename_from_str(filename);
                         if (type != realtype)
-                            ret = export_ssh2(&fn, type, &state->ssh2key,
+                            ret = export_ssh2(fn, type, &state->ssh2key,
                                               *passphrase ? passphrase : NULL);
                         else
-                            ret = ssh2_save_userkey(&fn, &state->ssh2key,
+                            ret = ssh2_save_userkey(fn, &state->ssh2key,
                                                     *passphrase ? passphrase :
                                                     NULL);
+                        filename_free(fn);
                    } else {
-                       Filename fn = filename_from_str(filename);
+                       Filename *fn = filename_from_str(filename);
                         if (type != realtype)
-                            ret = export_ssh1(&fn, type, &state->key,
+                            ret = export_ssh1(fn, type, &state->key,
                                               *passphrase ? passphrase : NULL);
                         else
-                            ret = saversakey(&fn, &state->key,
+                            ret = saversakey(fn, &state->key,
                                              *passphrase ? passphrase : NULL);
+                        filename_free(fn);
                    }
                    if (ret <= 0) {
                        MessageBox(hwnd, "Unable to save key file",
                                   "PuTTYgen Error", MB_OK | MB_ICONERROR);
                    }
                }
+                burnstr(passphrase);
            }
            break;
          case IDC_SAVEPUB:
            if (HIWORD(wParam) != BN_CLICKED)
                break;
            state =
-               (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
            if (state->key_exists) {
                char filename[FILENAME_MAX];
                if (prompt_keyfile(hwnd, "Save public key as:",
@@ -1234,19 +1255,21 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            if (HIWORD(wParam) != BN_CLICKED)
                break;
            state =
-               (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
            if (!state->generation_thread_exists) {
                char filename[FILENAME_MAX];
                if (prompt_keyfile(hwnd, "Load private key:",
-                                  filename, 0, LOWORD(wParam)==IDC_LOAD))
-                   load_key_file(hwnd, state, filename_from_str(filename),
-                                 LOWORD(wParam) != IDC_LOAD);
+                                  filename, 0, LOWORD(wParam)==IDC_LOAD)) {
+                    Filename *fn = filename_from_str(filename);
+                   load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
+                    filename_free(fn);
+                }
            }
            break;
        }
        return 0;
       case WM_DONEKEY:
-       state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+       state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
        state->generation_thread_exists = FALSE;
        state->key_exists = TRUE;
        SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
@@ -1331,7 +1354,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        ui_set_state(hwnd, state, 2);
        break;
       case WM_HELP:
-        if (help_path) {
+        {
             int id = ((LPHELPINFO)lParam)->iCtrlId;
             char *topic = NULL;
             switch (id) {
@@ -1339,106 +1362,98 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
               case IDC_PROGRESS:
               case IDC_GENSTATIC:
               case IDC_GENERATE:
-                topic = "puttygen.generate"; break;
+                topic = WINHELP_CTX_puttygen_generate; break;
               case IDC_PKSTATIC:
               case IDC_KEYDISPLAY:
-                topic = "puttygen.pastekey"; break;
+                topic = WINHELP_CTX_puttygen_pastekey; break;
               case IDC_FPSTATIC:
               case IDC_FINGERPRINT:
-                topic = "puttygen.fingerprint"; break;
+                topic = WINHELP_CTX_puttygen_fingerprint; break;
               case IDC_COMMENTSTATIC:
               case IDC_COMMENTEDIT:
-                topic = "puttygen.comment"; break;
+                topic = WINHELP_CTX_puttygen_comment; break;
               case IDC_PASSPHRASE1STATIC:
               case IDC_PASSPHRASE1EDIT:
               case IDC_PASSPHRASE2STATIC:
               case IDC_PASSPHRASE2EDIT:
-                topic = "puttygen.passphrase"; break;
+                topic = WINHELP_CTX_puttygen_passphrase; break;
               case IDC_LOADSTATIC:
               case IDC_LOAD:
-                topic = "puttygen.load"; break;
+                topic = WINHELP_CTX_puttygen_load; break;
               case IDC_SAVESTATIC:
               case IDC_SAVE:
-                topic = "puttygen.savepriv"; break;
+                topic = WINHELP_CTX_puttygen_savepriv; break;
               case IDC_SAVEPUB:
-                topic = "puttygen.savepub"; break;
+                topic = WINHELP_CTX_puttygen_savepub; break;
               case IDC_TYPESTATIC:
               case IDC_KEYSSH1:
               case IDC_KEYSSH2RSA:
               case IDC_KEYSSH2DSA:
-                topic = "puttygen.keytype"; break;
+                topic = WINHELP_CTX_puttygen_keytype; break;
               case IDC_BITSSTATIC:
               case IDC_BITS:
-                topic = "puttygen.bits"; break;
+                topic = WINHELP_CTX_puttygen_bits; break;
               case IDC_IMPORT:
               case IDC_EXPORT_OPENSSH:
               case IDC_EXPORT_SSHCOM:
-                topic = "puttygen.conversions"; break;
+                topic = WINHELP_CTX_puttygen_conversions; break;
             }
             if (topic) {
-               char *cmd = dupprintf("JI(`',`%s')", topic);
-                WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
-               sfree(cmd);
-                requested_help = TRUE;
+                launch_help(hwnd, topic);
             } else {
                 MessageBeep(0);
             }
         }
         break;
       case WM_CLOSE:
-       state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
+       state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
        sfree(state);
-        if (requested_help) {
-            WinHelp(hwnd, help_path, HELP_QUIT, 0);
-            requested_help = FALSE;
-        }
+       quit_help(hwnd);
        EndDialog(hwnd, 1);
        return 0;
     }
     return 0;
 }
 
-void cleanup_exit(int code) { exit(code); }
+void cleanup_exit(int code)
+{
+    shutdown_help();
+    exit(code);
+}
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
     int argc;
     char **argv;
-
-    split_into_argv(cmdline, &argc, &argv, NULL);
-
-    if (argc > 0) {
-       /*
-        * Assume the first argument to be a private key file, and
-        * attempt to load it.
-        */
-       cmdline_keyfile = argv[0];
-    }
+    int ret;
 
     InitCommonControls();
     hinst = inst;
+    hwnd = NULL;
 
     /*
      * See if we can find our Help file.
      */
-    {
-        char b[2048], *p, *q, *r;
-        FILE *fp;
-        GetModuleFileName(NULL, b, sizeof(b) - 1);
-        r = b;
-        p = strrchr(b, '\\');
-        if (p && p >= r) r = p+1;
-        q = strrchr(b, ':');
-        if (q && q >= r) r = q+1;
-        strcpy(r, PUTTY_HELP_FILE);
-        if ( (fp = fopen(b, "r")) != NULL) {
-            help_path = dupstr(b);
-            fclose(fp);
-        } else
-            help_path = NULL;
+    init_help();
+
+    split_into_argv(cmdline, &argc, &argv, NULL);
+
+    if (argc > 0) {
+       if (!strcmp(argv[0], "-pgpfp")) {
+           pgp_fingerprints();
+           exit(1);
+       } else {
+           /*
+            * Assume the first argument to be a private key file, and
+            * attempt to load it.
+            */
+           cmdline_keyfile = argv[0];
+       }
     }
 
     random_ref();
-    return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
-                    MainDlgProc) != IDOK;
+    ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
+
+    cleanup_exit(ret);
+    return ret;                               /* just in case optimiser complains */
 }