]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
Added export of ssh.com key files.
[PuTTY.git] / puttygen.c
1 /*
2  * PuTTY key generation front end.
3  */
4
5 #include <windows.h>
6 #include <commctrl.h>
7 #include <time.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #define PUTTY_DO_GLOBALS
12
13 #include "putty.h"
14 #include "ssh.h"
15 #include "winstuff.h"
16
17 #define WM_DONEKEY (WM_XUSER + 1)
18
19 #define DEFAULT_KEYSIZE 1024
20
21 static int requested_help;
22
23 /* ----------------------------------------------------------------------
24  * Progress report code. This is really horrible :-)
25  */
26 #define PROGRESSRANGE 65535
27 #define MAXPHASE 5
28 struct progress {
29     int nphases;
30     struct {
31         int exponential;
32         unsigned startpoint, total;
33         unsigned param, current, n;    /* if exponential */
34         unsigned mult;                 /* if linear */
35     } phases[MAXPHASE];
36     unsigned total, divisor, range;
37     HWND progbar;
38 };
39
40 static void progress_update(void *param, int action, int phase, int iprogress)
41 {
42     struct progress *p = (struct progress *) param;
43     unsigned progress = iprogress;
44     int position;
45
46     if (action < PROGFN_READY && p->nphases < phase)
47         p->nphases = phase;
48     switch (action) {
49       case PROGFN_INITIALISE:
50         p->nphases = 0;
51         break;
52       case PROGFN_LIN_PHASE:
53         p->phases[phase-1].exponential = 0;
54         p->phases[phase-1].mult = p->phases[phase].total / progress;
55         break;
56       case PROGFN_EXP_PHASE:
57         p->phases[phase-1].exponential = 1;
58         p->phases[phase-1].param = 0x10000 + progress;
59         p->phases[phase-1].current = p->phases[phase-1].total;
60         p->phases[phase-1].n = 0;
61         break;
62       case PROGFN_PHASE_EXTENT:
63         p->phases[phase-1].total = progress;
64         break;
65       case PROGFN_READY:
66         {
67             unsigned total = 0;
68             int i;
69             for (i = 0; i < p->nphases; i++) {
70                 p->phases[i].startpoint = total;
71                 total += p->phases[i].total;
72             }
73             p->total = total;
74             p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
75             p->range = p->total / p->divisor;
76             SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
77         }
78         break;
79       case PROGFN_PROGRESS:
80         if (p->phases[phase-1].exponential) {
81             while (p->phases[phase-1].n < progress) {
82                 p->phases[phase-1].n++;
83                 p->phases[phase-1].current *= p->phases[phase-1].param;
84                 p->phases[phase-1].current /= 0x10000;
85             }
86             position = (p->phases[phase-1].startpoint +
87                         p->phases[phase-1].total - p->phases[phase-1].current);
88         } else {
89             position = (p->phases[phase-1].startpoint +
90                         progress * p->phases[phase-1].mult);
91         }
92         SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
93         break;
94     }
95 }
96
97 extern char ver[];
98
99 #define PASSPHRASE_MAXLEN 512
100
101 struct PassphraseProcStruct {
102     char *passphrase;
103     char *comment;
104 };
105
106 /*
107  * Dialog-box function for the passphrase box.
108  */
109 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
110                                    WPARAM wParam, LPARAM lParam)
111 {
112     static char *passphrase = NULL;
113     struct PassphraseProcStruct *p;
114
115     switch (msg) {
116       case WM_INITDIALOG:
117         SetForegroundWindow(hwnd);
118         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
119                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
120
121         /*
122          * Centre the window.
123          */
124         {                              /* centre the window */
125             RECT rs, rd;
126             HWND hw;
127
128             hw = GetDesktopWindow();
129             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
130                 MoveWindow(hwnd,
131                            (rs.right + rs.left + rd.left - rd.right) / 2,
132                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
133                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
134         }
135
136         p = (struct PassphraseProcStruct *) lParam;
137         passphrase = p->passphrase;
138         if (p->comment)
139             SetDlgItemText(hwnd, 101, p->comment);
140         *passphrase = 0;
141         SetDlgItemText(hwnd, 102, passphrase);
142         return 0;
143       case WM_COMMAND:
144         switch (LOWORD(wParam)) {
145           case IDOK:
146             if (*passphrase)
147                 EndDialog(hwnd, 1);
148             else
149                 MessageBeep(0);
150             return 0;
151           case IDCANCEL:
152             EndDialog(hwnd, 0);
153             return 0;
154           case 102:                    /* edit box */
155             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
156                 GetDlgItemText(hwnd, 102, passphrase,
157                                PASSPHRASE_MAXLEN - 1);
158                 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
159             }
160             return 0;
161         }
162         return 0;
163       case WM_CLOSE:
164         EndDialog(hwnd, 0);
165         return 0;
166     }
167     return 0;
168 }
169
170 /*
171  * Prompt for a key file. Assumes the filename buffer is of size
172  * FILENAME_MAX.
173  */
174 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
175                           char *filename, int save)
176 {
177     OPENFILENAME of;
178     memset(&of, 0, sizeof(of));
179 #ifdef OPENFILENAME_SIZE_VERSION_400
180     of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
181 #else
182     of.lStructSize = sizeof(of);
183 #endif
184     of.hwndOwner = hwnd;
185     of.lpstrFilter = "All Files\0*\0\0\0";
186     of.lpstrCustomFilter = NULL;
187     of.nFilterIndex = 1;
188     of.lpstrFile = filename;
189     *filename = '\0';
190     of.nMaxFile = FILENAME_MAX;
191     of.lpstrFileTitle = NULL;
192     of.lpstrInitialDir = NULL;
193     of.lpstrTitle = dlgtitle;
194     of.Flags = 0;
195     if (save)
196         return GetSaveFileName(&of);
197     else
198         return GetOpenFileName(&of);
199 }
200
201 /*
202  * This function is needed to link with the DES code. We need not
203  * have it do anything at all.
204  */
205 void logevent(char *msg)
206 {
207 }
208
209 /*
210  * Dialog-box function for the Licence box.
211  */
212 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
213                                 WPARAM wParam, LPARAM lParam)
214 {
215     switch (msg) {
216       case WM_INITDIALOG:
217         /*
218          * Centre the window.
219          */
220         {                              /* centre the window */
221             RECT rs, rd;
222             HWND hw;
223
224             hw = GetDesktopWindow();
225             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
226                 MoveWindow(hwnd,
227                            (rs.right + rs.left + rd.left - rd.right) / 2,
228                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
229                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
230         }
231
232         return 1;
233       case WM_COMMAND:
234         switch (LOWORD(wParam)) {
235           case IDOK:
236             EndDialog(hwnd, 1);
237             return 0;
238         }
239         return 0;
240       case WM_CLOSE:
241         EndDialog(hwnd, 1);
242         return 0;
243     }
244     return 0;
245 }
246
247 /*
248  * Dialog-box function for the About box.
249  */
250 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
251                               WPARAM wParam, LPARAM lParam)
252 {
253     switch (msg) {
254       case WM_INITDIALOG:
255         /*
256          * Centre the window.
257          */
258         {                              /* centre the window */
259             RECT rs, rd;
260             HWND hw;
261
262             hw = GetDesktopWindow();
263             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
264                 MoveWindow(hwnd,
265                            (rs.right + rs.left + rd.left - rd.right) / 2,
266                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
267                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
268         }
269
270         SetDlgItemText(hwnd, 100, ver);
271         return 1;
272       case WM_COMMAND:
273         switch (LOWORD(wParam)) {
274           case IDOK:
275             EndDialog(hwnd, 1);
276             return 0;
277           case 101:
278             EnableWindow(hwnd, 0);
279             DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
280             EnableWindow(hwnd, 1);
281             SetActiveWindow(hwnd);
282             return 0;
283         }
284         return 0;
285       case WM_CLOSE:
286         EndDialog(hwnd, 1);
287         return 0;
288     }
289     return 0;
290 }
291
292 /*
293  * Thread to generate a key.
294  */
295 struct rsa_key_thread_params {
296     HWND progressbar;                  /* notify this with progress */
297     HWND dialog;                       /* notify this on completion */
298     int keysize;                       /* bits in key */
299     int is_dsa;
300     struct RSAKey *key;
301     struct dss_key *dsskey;
302 };
303 static DWORD WINAPI generate_rsa_key_thread(void *param)
304 {
305     struct rsa_key_thread_params *params =
306         (struct rsa_key_thread_params *) param;
307     struct progress prog;
308     prog.progbar = params->progressbar;
309
310     progress_update(&prog, PROGFN_INITIALISE, 0, 0);
311
312     if (params->is_dsa)
313         dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
314     else
315         rsa_generate(params->key, params->keysize, progress_update, &prog);
316
317     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
318
319     sfree(params);
320     return 0;
321 }
322
323 struct MainDlgState {
324     int collecting_entropy;
325     int generation_thread_exists;
326     int key_exists;
327     int entropy_got, entropy_required, entropy_size;
328     int keysize;
329     int ssh2, is_dsa;
330     char **commentptr;                 /* points to key.comment or ssh2key.comment */
331     struct ssh2_userkey ssh2key;
332     unsigned *entropy;
333     struct RSAKey key;
334     struct dss_key dsskey;
335 };
336
337 static void hidemany(HWND hwnd, const int *ids, int hideit)
338 {
339     while (*ids) {
340         ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
341     }
342 }
343
344 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
345 {
346     char *buffer;
347     char *dec1, *dec2;
348
349     dec1 = bignum_decimal(key->exponent);
350     dec2 = bignum_decimal(key->modulus);
351     buffer = smalloc(strlen(dec1) + strlen(dec2) +
352                      strlen(key->comment) + 30);
353     sprintf(buffer, "%d %s %s %s",
354             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
355     SetDlgItemText(hwnd, id, buffer);
356     SetDlgItemText(hwnd, idstatic,
357                    "&Public key for pasting into authorized_keys file:");
358     sfree(dec1);
359     sfree(dec2);
360     sfree(buffer);
361 }
362
363 static void setupbigedit2(HWND hwnd, int id, int idstatic,
364                           struct ssh2_userkey *key)
365 {
366     unsigned char *pub_blob;
367     char *buffer, *p;
368     int pub_len;
369     int i;
370
371     pub_blob = key->alg->public_blob(key->data, &pub_len);
372     buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
373                      strlen(key->comment) + 3);
374     strcpy(buffer, key->alg->name);
375     p = buffer + strlen(buffer);
376     *p++ = ' ';
377     i = 0;
378     while (i < pub_len) {
379         int n = (pub_len - i < 3 ? pub_len - i : 3);
380         base64_encode_atom(pub_blob + i, n, p);
381         i += n;
382         p += 4;
383     }
384     *p++ = ' ';
385     strcpy(p, key->comment);
386     SetDlgItemText(hwnd, id, buffer);
387     SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
388                    "OpenSSH authorized_keys2 file:");
389     sfree(pub_blob);
390     sfree(buffer);
391 }
392
393 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
394 {
395     char *dec1, *dec2;
396     FILE *fp;
397
398     dec1 = bignum_decimal(key->exponent);
399     dec2 = bignum_decimal(key->modulus);
400     fp = fopen(filename, "wb");
401     if (!fp)
402         return 0;
403     fprintf(fp, "%d %s %s %s\n",
404             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
405     fclose(fp);
406     sfree(dec1);
407     sfree(dec2);
408     return 1;
409 }
410
411 /*
412  * Warn about the obsolescent key file format.
413  */
414 void old_keyfile_warning(void)
415 {
416     static const char mbtitle[] = "PuTTY Key File Warning";
417     static const char message[] =
418         "You are loading an SSH 2 private key which has an\n"
419         "old version of the file format. This means your key\n"
420         "file is not fully tamperproof. Future versions of\n"
421         "PuTTY may stop supporting this private key format,\n"
422         "so we recommend you convert your key to the new\n"
423         "format.\n"
424         "\n"
425         "Once the key is loaded into PuTTYgen, you can perform\n"
426         "this conversion simply by saving it again.";
427
428     MessageBox(NULL, message, mbtitle, MB_OK);
429 }
430
431 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
432 {
433     unsigned char *pub_blob;
434     char *p;
435     int pub_len;
436     int i, column;
437     FILE *fp;
438
439     pub_blob = key->alg->public_blob(key->data, &pub_len);
440
441     fp = fopen(filename, "wb");
442     if (!fp)
443         return 0;
444
445     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
446
447     fprintf(fp, "Comment: \"");
448     for (p = key->comment; *p; p++) {
449         if (*p == '\\' || *p == '\"')
450             fputc('\\', fp);
451         fputc(*p, fp);
452     }
453     fprintf(fp, "\"\n");
454
455     i = 0;
456     column = 0;
457     while (i < pub_len) {
458         char buf[5];
459         int n = (pub_len - i < 3 ? pub_len - i : 3);
460         base64_encode_atom(pub_blob + i, n, buf);
461         i += n;
462         buf[4] = '\0';
463         fputs(buf, fp);
464         if (++column >= 16) {
465             fputc('\n', fp);
466             column = 0;
467         }
468     }
469     if (column > 0)
470         fputc('\n', fp);
471     
472     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
473     fclose(fp);
474     sfree(pub_blob);
475     return 1;
476 }
477
478 /*
479  * Dialog-box function for the main PuTTYgen dialog box.
480  */
481 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
482                                 WPARAM wParam, LPARAM lParam)
483 {
484     enum {
485         controlidstart = 100,
486         IDC_TITLE,
487         IDC_BOX_KEY,
488         IDC_NOKEY,
489         IDC_GENERATING,
490         IDC_PROGRESS,
491         IDC_PKSTATIC, IDC_KEYDISPLAY,
492         IDC_FPSTATIC, IDC_FINGERPRINT,
493         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
494         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
495         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
496         IDC_BOX_ACTIONS,
497         IDC_GENSTATIC, IDC_GENERATE,
498         IDC_LOADSTATIC, IDC_LOAD,
499         IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
500         IDC_BOX_PARAMS,
501         IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
502         IDC_BITSSTATIC, IDC_BITS,
503         IDC_ABOUT,
504         IDC_GIVEHELP,
505         IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
506     };
507     static const int nokey_ids[] = { IDC_NOKEY, 0 };
508     static const int generating_ids[] =
509         { IDC_GENERATING, IDC_PROGRESS, 0 };
510     static const int gotkey_ids[] = {
511         IDC_PKSTATIC, IDC_KEYDISPLAY,
512         IDC_FPSTATIC, IDC_FINGERPRINT,
513         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
514         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
515         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
516     };
517     static const char generating_msg[] =
518         "Please wait while a key is generated...";
519     static const char entropy_msg[] =
520         "Please generate some randomness by moving the mouse over the blank area.";
521     struct MainDlgState *state;
522
523     switch (msg) {
524       case WM_INITDIALOG:
525         if (help_path)
526             SetWindowLong(hwnd, GWL_EXSTYLE,
527                           GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
528         else {
529             /*
530              * If we add a Help button, this is where we destroy it
531              * if the help file isn't present.
532              */
533         }
534         requested_help = FALSE;
535
536         {
537             HMENU menu, menu1;
538
539             menu = CreateMenu();
540
541             menu1 = CreateMenu();
542             AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
543             AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
544             AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
545             AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
546
547             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
548
549             menu1 = CreateMenu();
550             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
551                        "Export &OpenSSH key");
552             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
553                        "Export &ssh.com key");
554
555             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
556                        "&Export");
557
558             menu1 = CreateMenu();
559             AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
560             if (help_path)
561                 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
562
563             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
564
565             SetMenu(hwnd, menu);
566         }
567
568         /*
569          * Centre the window.
570          */
571         {                              /* centre the window */
572             RECT rs, rd;
573             HWND hw;
574
575             hw = GetDesktopWindow();
576             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
577                 MoveWindow(hwnd,
578                            (rs.right + rs.left + rd.left - rd.right) / 2,
579                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
580                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
581         }
582
583         state = smalloc(sizeof(*state));
584         state->generation_thread_exists = FALSE;
585         state->collecting_entropy = FALSE;
586         state->entropy = NULL;
587         state->key_exists = FALSE;
588         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
589         {
590             struct ctlpos cp, cp2;
591
592             /* Accelerators used: acglops1rbd */
593
594             ctlposinit(&cp, hwnd, 4, 4, 4);
595             beginbox(&cp, "Key", IDC_BOX_KEY);
596             cp2 = cp;
597             statictext(&cp2, "No key.", 1, IDC_NOKEY);
598             cp2 = cp;
599             statictext(&cp2, "", 1, IDC_GENERATING);
600             progressbar(&cp2, IDC_PROGRESS);
601             bigeditctrl(&cp,
602                         "&Public key for pasting into authorized_keys file:",
603                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
604             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
605             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
606                        IDC_FINGERPRINT, 75);
607             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
608                                0);
609             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
610                        IDC_COMMENTEDIT, 75);
611             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
612                            IDC_PASSPHRASE1EDIT, 75);
613             staticpassedit(&cp, "C&onfirm passphrase:",
614                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
615             endbox(&cp);
616             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
617             staticbtn(&cp, "Generate a public/private key pair",
618                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
619             staticbtn(&cp, "Load an existing private key file",
620                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
621             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
622                        "Save p&ublic key", IDC_SAVEPUB,
623                        "&Save private key", IDC_SAVE);
624             endbox(&cp);
625             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
626             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
627                       "SSH&1 (RSA)", IDC_KEYSSH1,
628                       "SSH2 &RSA", IDC_KEYSSH2RSA,
629                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
630             staticedit(&cp, "Number of &bits in a generated key:",
631                        IDC_BITSSTATIC, IDC_BITS, 20);
632             endbox(&cp);
633         }
634         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
635         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
636
637         /*
638          * Initially, hide the progress bar and the key display,
639          * and show the no-key display. Also disable the Save
640          * buttons, because with no key we obviously can't save
641          * anything.
642          */
643         hidemany(hwnd, nokey_ids, FALSE);
644         hidemany(hwnd, generating_ids, TRUE);
645         hidemany(hwnd, gotkey_ids, TRUE);
646         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
647         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
648
649         return 1;
650       case WM_MOUSEMOVE:
651         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
652         if (state->collecting_entropy &&
653             state->entropy && state->entropy_got < state->entropy_required) {
654             state->entropy[state->entropy_got++] = lParam;
655             state->entropy[state->entropy_got++] = GetMessageTime();
656             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
657                                state->entropy_got, 0);
658             if (state->entropy_got >= state->entropy_required) {
659                 struct rsa_key_thread_params *params;
660                 DWORD threadid;
661
662                 /*
663                  * Seed the entropy pool
664                  */
665                 random_add_heavynoise(state->entropy, state->entropy_size);
666                 memset(state->entropy, 0, state->entropy_size);
667                 sfree(state->entropy);
668                 state->collecting_entropy = FALSE;
669
670                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
671                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
672                                    MAKELPARAM(0, PROGRESSRANGE));
673                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
674
675                 params = smalloc(sizeof(*params));
676                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
677                 params->dialog = hwnd;
678                 params->keysize = state->keysize;
679                 params->is_dsa = state->is_dsa;
680                 params->key = &state->key;
681                 params->dsskey = &state->dsskey;
682
683                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
684                                   params, 0, &threadid)) {
685                     MessageBox(hwnd, "Out of thread resources",
686                                "Key generation error",
687                                MB_OK | MB_ICONERROR);
688                     sfree(params);
689                 } else {
690                     state->generation_thread_exists = TRUE;
691                 }
692             }
693         }
694         break;
695       case WM_COMMAND:
696         switch (LOWORD(wParam)) {
697           case IDC_COMMENTEDIT:
698             if (HIWORD(wParam) == EN_CHANGE) {
699                 state = (struct MainDlgState *)
700                     GetWindowLong(hwnd, GWL_USERDATA);
701                 if (state->key_exists) {
702                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
703                     int len = GetWindowTextLength(editctl);
704                     if (*state->commentptr)
705                         sfree(*state->commentptr);
706                     *state->commentptr = smalloc(len + 1);
707                     GetWindowText(editctl, *state->commentptr, len + 1);
708                     if (state->ssh2) {
709                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
710                                       &state->ssh2key);
711                     } else {
712                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
713                                       &state->key);
714                     }
715                 }
716             }
717             break;
718           case IDC_ABOUT:
719             EnableWindow(hwnd, 0);
720             DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
721             EnableWindow(hwnd, 1);
722             SetActiveWindow(hwnd);
723             return 0;
724           case IDC_GIVEHELP:
725             if (HIWORD(wParam) == BN_CLICKED ||
726                 HIWORD(wParam) == BN_DOUBLECLICKED) {
727                 if (help_path) {
728                     WinHelp(hwnd, help_path, HELP_COMMAND,
729                             (DWORD)"JI(`',`puttygen.general')");
730                     requested_help = TRUE;
731                 }
732             }
733             return 0;
734           case IDC_GENERATE:
735             state =
736                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
737             if (!state->generation_thread_exists) {
738                 BOOL ok;
739                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
740                 if (!ok)
741                     state->keysize = DEFAULT_KEYSIZE;
742                 /* If we ever introduce a new key type, check it here! */
743                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
744                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
745                 if (state->keysize < 256) {
746                     int ret = MessageBox(hwnd,
747                                          "PuTTYgen will not generate a key"
748                                          " smaller than 256 bits.\n"
749                                          "Key length reset to 256. Continue?",
750                                          "PuTTYgen Warning",
751                                          MB_ICONWARNING | MB_OKCANCEL);
752                     if (ret != IDOK)
753                         break;
754                     state->keysize = 256;
755                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
756                 }
757                 hidemany(hwnd, nokey_ids, TRUE);
758                 hidemany(hwnd, generating_ids, FALSE);
759                 hidemany(hwnd, gotkey_ids, TRUE);
760                 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
761                 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
762                 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
763                 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
764                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
765                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
766                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
767                 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
768                 state->key_exists = FALSE;
769                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
770                 state->collecting_entropy = TRUE;
771
772                 /*
773                  * My brief statistical tests on mouse movements
774                  * suggest that there are about 2.5 bits of
775                  * randomness in the x position, 2.5 in the y
776                  * position, and 1.7 in the message time, making
777                  * 5.7 bits of unpredictability per mouse movement.
778                  * However, other people have told me it's far less
779                  * than that, so I'm going to be stupidly cautious
780                  * and knock that down to a nice round 2. With this
781                  * method, we require two words per mouse movement,
782                  * so with 2 bits per mouse movement we expect 2
783                  * bits every 2 words.
784                  */
785                 state->entropy_required = (state->keysize / 2) * 2;
786                 state->entropy_got = 0;
787                 state->entropy_size = (state->entropy_required *
788                                        sizeof(*state->entropy));
789                 state->entropy = smalloc(state->entropy_size);
790
791                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
792                                    MAKELPARAM(0, state->entropy_required));
793                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
794             }
795             break;
796           case IDC_SAVE:
797           case IDC_EXPORT_OPENSSH:
798           case IDC_EXPORT_SSHCOM:
799             state =
800                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
801             if (state->key_exists) {
802                 char filename[FILENAME_MAX];
803                 char passphrase[PASSPHRASE_MAXLEN];
804                 char passphrase2[PASSPHRASE_MAXLEN];
805                 int type, realtype;
806
807                 if (state->ssh2)
808                     realtype = SSH_KEYTYPE_SSH2;
809                 else
810                     realtype = SSH_KEYTYPE_SSH1;
811
812                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
813                     type = SSH_KEYTYPE_OPENSSH;
814                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
815                     type = SSH_KEYTYPE_SSHCOM;
816                 else
817                     type = realtype;
818
819                 if (type != realtype &&
820                     import_target_type(type) != realtype) {
821                     char msg[256];
822                     sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
823                             " format", (state->ssh2 ? 2 : 1),
824                             (state->ssh2 ? 1 : 2));
825                     MessageBox(hwnd, msg,
826                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
827                     break;
828                 }
829
830                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
831                                passphrase, sizeof(passphrase));
832                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
833                                passphrase2, sizeof(passphrase2));
834                 if (strcmp(passphrase, passphrase2)) {
835                     MessageBox(hwnd,
836                                "The two passphrases given do not match.",
837                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
838                     break;
839                 }
840                 if (!*passphrase) {
841                     int ret;
842                     ret = MessageBox(hwnd,
843                                      "Are you sure you want to save this key\n"
844                                      "without a passphrase to protect it?",
845                                      "PuTTYgen Warning",
846                                      MB_YESNO | MB_ICONWARNING);
847                     if (ret != IDYES)
848                         break;
849                 }
850                 if (prompt_keyfile(hwnd, "Save private key as:",
851                                    filename, 1)) {
852                     int ret;
853                     FILE *fp = fopen(filename, "r");
854                     if (fp) {
855                         char buffer[FILENAME_MAX + 80];
856                         fclose(fp);
857                         sprintf(buffer, "Overwrite existing file\n%.*s?",
858                                 FILENAME_MAX, filename);
859                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
860                                          MB_YESNO | MB_ICONWARNING);
861                         if (ret != IDYES)
862                             break;
863                     }
864
865                     if (state->ssh2) {
866                         if (type != realtype)
867                             ret = export_ssh2(filename, type, &state->ssh2key,
868                                               *passphrase ? passphrase : NULL);
869                         else
870                             ret = ssh2_save_userkey(filename, &state->ssh2key,
871                                                     *passphrase ? passphrase :
872                                                     NULL);
873                     } else {
874                         if (type != realtype)
875                             ret = export_ssh1(filename, type, &state->key,
876                                               *passphrase ? passphrase : NULL);
877                         else
878                             ret = saversakey(filename, &state->key,
879                                              *passphrase ? passphrase : NULL);
880                     }
881                     if (ret <= 0) {
882                         MessageBox(hwnd, "Unable to save key file",
883                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
884                     }
885                 }
886             }
887             break;
888           case IDC_SAVEPUB:
889             state =
890                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
891             if (state->key_exists) {
892                 char filename[FILENAME_MAX];
893                 if (prompt_keyfile(hwnd, "Save public key as:",
894                                    filename, 1)) {
895                     int ret;
896                     FILE *fp = fopen(filename, "r");
897                     if (fp) {
898                         char buffer[FILENAME_MAX + 80];
899                         fclose(fp);
900                         sprintf(buffer, "Overwrite existing file\n%.*s?",
901                                 FILENAME_MAX, filename);
902                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
903                                          MB_YESNO | MB_ICONWARNING);
904                         if (ret != IDYES)
905                             break;
906                     }
907                     if (state->ssh2) {
908                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
909                     } else {
910                         ret = save_ssh1_pubkey(filename, &state->key);
911                     }
912                     if (ret <= 0) {
913                         MessageBox(hwnd, "Unable to save key file",
914                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
915                     }
916                 }
917             }
918             break;
919           case IDC_LOAD:
920             state =
921                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
922             if (!state->generation_thread_exists) {
923                 char filename[FILENAME_MAX];
924                 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
925                     char passphrase[PASSPHRASE_MAXLEN];
926                     int needs_pass;
927                     int type, realtype;
928                     int ret;
929                     char *comment;
930                     struct PassphraseProcStruct pps;
931                     struct RSAKey newkey1;
932                     struct ssh2_userkey *newkey2 = NULL;
933
934                     type = realtype = key_type(filename);
935                     if (type != SSH_KEYTYPE_SSH1 &&
936                         type != SSH_KEYTYPE_SSH2 &&
937                         !import_possible(type)) {
938                         char msg[256];
939                         sprintf(msg, "Couldn't load private key (%s)",
940                                 key_type_to_str(type));
941                         MessageBox(NULL, msg,
942                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
943                         break;
944                     }
945
946                     if (type != SSH_KEYTYPE_SSH1 &&
947                         type != SSH_KEYTYPE_SSH2) {
948                         realtype = type;
949                         type = import_target_type(type);
950                     }
951
952                     comment = NULL;
953                     if (realtype == SSH_KEYTYPE_SSH1)
954                         needs_pass = rsakey_encrypted(filename, &comment);
955                     else if (realtype == SSH_KEYTYPE_SSH2)
956                         needs_pass =
957                             ssh2_userkey_encrypted(filename, &comment);
958                     else
959                         needs_pass = import_encrypted(filename, realtype,
960                                                       &comment);
961                     pps.passphrase = passphrase;
962                     pps.comment = comment;
963                     do {
964                         if (needs_pass) {
965                             int dlgret;
966                             dlgret = DialogBoxParam(hinst,
967                                                     MAKEINTRESOURCE(210),
968                                                     NULL, PassphraseProc,
969                                                     (LPARAM) & pps);
970                             if (!dlgret) {
971                                 ret = -2;
972                                 break;
973                             }
974                         } else
975                             *passphrase = '\0';
976                         if (type == SSH_KEYTYPE_SSH1) {
977                             if (realtype == type)
978                                 ret = loadrsakey(filename, &newkey1,
979                                                  passphrase);
980                             else
981                                 ret = import_ssh1(filename, realtype,
982                                                   &newkey1, passphrase);
983                         } else {
984                             if (realtype == type)
985                                 newkey2 = ssh2_load_userkey(filename,
986                                                             passphrase);
987                             else
988                                 newkey2 = import_ssh2(filename, realtype,
989                                                       passphrase);
990                             if (newkey2 == SSH2_WRONG_PASSPHRASE)
991                                 ret = -1;
992                             else if (!newkey2)
993                                 ret = 0;
994                             else
995                                 ret = 1;
996                         }
997                     } while (ret == -1);
998                     if (comment)
999                         sfree(comment);
1000                     if (ret == 0) {
1001                         MessageBox(NULL, "Couldn't load private key.",
1002                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1003                     } else if (ret == 1) {
1004                         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1005                         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1006                         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
1007                         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1008                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1009                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
1010                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1011                         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1012                         /*
1013                          * Now update the key controls with all the
1014                          * key data.
1015                          */
1016                         {
1017                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1018                                            passphrase);
1019                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1020                                            passphrase);
1021                             if (type == SSH_KEYTYPE_SSH1) {
1022                                 char buf[128];
1023                                 char *savecomment;
1024
1025                                 state->ssh2 = FALSE;
1026                                 state->commentptr = &state->key.comment;
1027                                 state->key = newkey1;
1028
1029                                 /*
1030                                  * Set the key fingerprint.
1031                                  */
1032                                 savecomment = state->key.comment;
1033                                 state->key.comment = NULL;
1034                                 rsa_fingerprint(buf, sizeof(buf),
1035                                                 &state->key);
1036                                 state->key.comment = savecomment;
1037
1038                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1039                                 /*
1040                                  * Construct a decimal representation
1041                                  * of the key, for pasting into
1042                                  * .ssh/authorized_keys on a Unix box.
1043                                  */
1044                                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1045                                               IDC_PKSTATIC, &state->key);
1046                             } else {
1047                                 char *fp;
1048                                 char *savecomment;
1049
1050                                 state->ssh2 = TRUE;
1051                                 state->commentptr =
1052                                     &state->ssh2key.comment;
1053                                 state->ssh2key = *newkey2;      /* structure copy */
1054                                 sfree(newkey2);
1055
1056                                 savecomment = state->ssh2key.comment;
1057                                 state->ssh2key.comment = NULL;
1058                                 fp =
1059                                     state->ssh2key.alg->
1060                                     fingerprint(state->ssh2key.data);
1061                                 state->ssh2key.comment = savecomment;
1062
1063                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1064                                 sfree(fp);
1065
1066                                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1067                                               IDC_PKSTATIC, &state->ssh2key);
1068                             }
1069                             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1070                                            *state->commentptr);
1071                         }
1072                         /*
1073                          * Finally, hide the progress bar and show
1074                          * the key data.
1075                          */
1076                         hidemany(hwnd, nokey_ids, TRUE);
1077                         hidemany(hwnd, generating_ids, TRUE);
1078                         hidemany(hwnd, gotkey_ids, FALSE);
1079                         state->key_exists = TRUE;
1080                     }
1081                 }
1082             }
1083             break;
1084         }
1085         return 0;
1086       case WM_DONEKEY:
1087         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1088         state->generation_thread_exists = FALSE;
1089         state->key_exists = TRUE;
1090         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1091                            MAKELPARAM(0, PROGRESSRANGE));
1092         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1093         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1094         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1095         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
1096         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1097         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1098         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
1099         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1100         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1101         if (state->ssh2) {
1102             if (state->is_dsa) {
1103                 state->ssh2key.data = &state->dsskey;
1104                 state->ssh2key.alg = &ssh_dss;
1105             } else {
1106                 state->ssh2key.data = &state->key;
1107                 state->ssh2key.alg = &ssh_rsa;
1108             }
1109             state->commentptr = &state->ssh2key.comment;
1110         } else {
1111             state->commentptr = &state->key.comment;
1112         }
1113         /*
1114          * Invent a comment for the key. We'll do this by including
1115          * the date in it. This will be so horrifyingly ugly that
1116          * the user will immediately want to change it, which is
1117          * what we want :-)
1118          */
1119         *state->commentptr = smalloc(30);
1120         {
1121             time_t t;
1122             struct tm *tm;
1123             time(&t);
1124             tm = localtime(&t);
1125             if (state->is_dsa)
1126                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1127             else
1128                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1129         }
1130
1131         /*
1132          * Now update the key controls with all the key data.
1133          */
1134         {
1135             char *savecomment;
1136             /*
1137              * Blank passphrase, initially. This isn't dangerous,
1138              * because we will warn (Are You Sure?) before allowing
1139              * the user to save an unprotected private key.
1140              */
1141             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1142             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1143             /*
1144              * Set the comment.
1145              */
1146             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1147             /*
1148              * Set the key fingerprint.
1149              */
1150             savecomment = *state->commentptr;
1151             *state->commentptr = NULL;
1152             if (state->ssh2) {
1153                 char *fp;
1154                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1155                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1156                 sfree(fp);
1157             } else {
1158                 char buf[128];
1159                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1160                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1161             }
1162             *state->commentptr = savecomment;
1163             /*
1164              * Construct a decimal representation of the key, for
1165              * pasting into .ssh/authorized_keys or
1166              * .ssh/authorized_keys2 on a Unix box.
1167              */
1168             if (state->ssh2) {
1169                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1170                               IDC_PKSTATIC, &state->ssh2key);
1171             } else {
1172                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1173                               IDC_PKSTATIC, &state->key);
1174             }
1175         }
1176         /*
1177          * Finally, hide the progress bar and show the key data.
1178          */
1179         hidemany(hwnd, nokey_ids, TRUE);
1180         hidemany(hwnd, generating_ids, TRUE);
1181         hidemany(hwnd, gotkey_ids, FALSE);
1182         break;
1183       case WM_HELP:
1184         if (help_path) {
1185             int id = ((LPHELPINFO)lParam)->iCtrlId;
1186             char *cmd = NULL;
1187             switch (id) {
1188               case IDC_GENERATING:
1189               case IDC_PROGRESS:
1190               case IDC_GENSTATIC:
1191               case IDC_GENERATE:
1192                 cmd = "JI(`',`puttygen.generate')"; break;
1193               case IDC_PKSTATIC:
1194               case IDC_KEYDISPLAY:
1195                 cmd = "JI(`',`puttygen.pastekey')"; break;
1196               case IDC_FPSTATIC:
1197               case IDC_FINGERPRINT:
1198                 cmd = "JI(`',`puttygen.fingerprint')"; break;
1199               case IDC_COMMENTSTATIC:
1200               case IDC_COMMENTEDIT:
1201                 cmd = "JI(`',`puttygen.comment')"; break;
1202               case IDC_PASSPHRASE1STATIC:
1203               case IDC_PASSPHRASE1EDIT:
1204               case IDC_PASSPHRASE2STATIC:
1205               case IDC_PASSPHRASE2EDIT:
1206                 cmd = "JI(`',`puttygen.passphrase')"; break;
1207               case IDC_LOADSTATIC:
1208               case IDC_LOAD:
1209                 cmd = "JI(`',`puttygen.load')"; break;
1210               case IDC_SAVESTATIC:
1211               case IDC_SAVE:
1212                 cmd = "JI(`',`puttygen.savepriv')"; break;
1213               case IDC_SAVEPUB:
1214                 cmd = "JI(`',`puttygen.savepub')"; break;
1215               case IDC_TYPESTATIC:
1216               case IDC_KEYSSH1:
1217               case IDC_KEYSSH2RSA:
1218               case IDC_KEYSSH2DSA:
1219                 cmd = "JI(`',`puttygen.keytype')"; break;
1220               case IDC_BITSSTATIC:
1221               case IDC_BITS:
1222                 cmd = "JI(`',`puttygen.bits')"; break;
1223             }
1224             if (cmd) {
1225                 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1226                 requested_help = TRUE;
1227             } else {
1228                 MessageBeep(0);
1229             }
1230         }
1231         break;
1232       case WM_CLOSE:
1233         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1234         sfree(state);
1235         if (requested_help) {
1236             WinHelp(hwnd, help_path, HELP_QUIT, 0);
1237             requested_help = FALSE;
1238         }
1239         EndDialog(hwnd, 1);
1240         return 0;
1241     }
1242     return 0;
1243 }
1244
1245 void cleanup_exit(int code) { exit(code); }
1246
1247 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1248 {
1249     InitCommonControls();
1250     hinst = inst;
1251
1252     /*
1253      * See if we can find our Help file.
1254      */
1255     {
1256         char b[2048], *p, *q, *r;
1257         FILE *fp;
1258         GetModuleFileName(NULL, b, sizeof(b) - 1);
1259         r = b;
1260         p = strrchr(b, '\\');
1261         if (p && p >= r) r = p+1;
1262         q = strrchr(b, ':');
1263         if (q && q >= r) r = q+1;
1264         strcpy(r, "putty.hlp");
1265         if ( (fp = fopen(b, "r")) != NULL) {
1266             help_path = dupstr(b);
1267             fclose(fp);
1268         } else
1269             help_path = NULL;
1270     }
1271
1272     random_init();
1273     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1274                      MainDlgProc) != IDOK;
1275 }