2 * PuTTY key generation front end (Windows).
9 #define PUTTY_DO_GLOBALS
20 #define WM_DONEKEY (WM_XUSER + 1)
22 #define DEFAULT_KEYSIZE 1024
24 static char *cmdline_keyfile
= NULL
;
27 * Print a modal (Really Bad) message box and perform a fatal exit.
29 void modalfatalbox(char *fmt
, ...)
35 stuff
= dupvprintf(fmt
, ap
);
37 MessageBox(NULL
, stuff
, "PuTTYgen Fatal Error",
38 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
43 /* ----------------------------------------------------------------------
44 * Progress report code. This is really horrible :-)
46 #define PROGRESSRANGE 65535
52 unsigned startpoint
, total
;
53 unsigned param
, current
, n
; /* if exponential */
54 unsigned mult
; /* if linear */
56 unsigned total
, divisor
, range
;
60 static void progress_update(void *param
, int action
, int phase
, int iprogress
)
62 struct progress
*p
= (struct progress
*) param
;
63 unsigned progress
= iprogress
;
66 if (action
< PROGFN_READY
&& p
->nphases
< phase
)
69 case PROGFN_INITIALISE
:
72 case PROGFN_LIN_PHASE
:
73 p
->phases
[phase
-1].exponential
= 0;
74 p
->phases
[phase
-1].mult
= p
->phases
[phase
].total
/ progress
;
76 case PROGFN_EXP_PHASE
:
77 p
->phases
[phase
-1].exponential
= 1;
78 p
->phases
[phase
-1].param
= 0x10000 + progress
;
79 p
->phases
[phase
-1].current
= p
->phases
[phase
-1].total
;
80 p
->phases
[phase
-1].n
= 0;
82 case PROGFN_PHASE_EXTENT
:
83 p
->phases
[phase
-1].total
= progress
;
89 for (i
= 0; i
< p
->nphases
; i
++) {
90 p
->phases
[i
].startpoint
= total
;
91 total
+= p
->phases
[i
].total
;
94 p
->divisor
= ((p
->total
+ PROGRESSRANGE
- 1) / PROGRESSRANGE
);
95 p
->range
= p
->total
/ p
->divisor
;
96 SendMessage(p
->progbar
, PBM_SETRANGE
, 0, MAKELPARAM(0, p
->range
));
100 if (p
->phases
[phase
-1].exponential
) {
101 while (p
->phases
[phase
-1].n
< progress
) {
102 p
->phases
[phase
-1].n
++;
103 p
->phases
[phase
-1].current
*= p
->phases
[phase
-1].param
;
104 p
->phases
[phase
-1].current
/= 0x10000;
106 position
= (p
->phases
[phase
-1].startpoint
+
107 p
->phases
[phase
-1].total
- p
->phases
[phase
-1].current
);
109 position
= (p
->phases
[phase
-1].startpoint
+
110 progress
* p
->phases
[phase
-1].mult
);
112 SendMessage(p
->progbar
, PBM_SETPOS
, position
/ p
->divisor
, 0);
119 #define PASSPHRASE_MAXLEN 512
121 struct PassphraseProcStruct
{
127 * Dialog-box function for the passphrase box.
129 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
130 WPARAM wParam
, LPARAM lParam
)
132 static char *passphrase
= NULL
;
133 struct PassphraseProcStruct
*p
;
137 SetForegroundWindow(hwnd
);
138 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
139 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
144 { /* centre the window */
148 hw
= GetDesktopWindow();
149 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
151 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
152 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
153 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
156 p
= (struct PassphraseProcStruct
*) lParam
;
157 passphrase
= p
->passphrase
;
159 SetDlgItemText(hwnd
, 101, p
->comment
);
161 SetDlgItemText(hwnd
, 102, passphrase
);
164 switch (LOWORD(wParam
)) {
174 case 102: /* edit box */
175 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
176 GetDlgItemText(hwnd
, 102, passphrase
,
177 PASSPHRASE_MAXLEN
- 1);
178 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
191 * Prompt for a key file. Assumes the filename buffer is of size
194 static int prompt_keyfile(HWND hwnd
, char *dlgtitle
,
195 char *filename
, int save
, int ppk
)
198 memset(&of
, 0, sizeof(of
));
201 of
.lpstrFilter
= "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
202 "All Files (*.*)\0*\0\0\0";
203 of
.lpstrDefExt
= ".ppk";
205 of
.lpstrFilter
= "All Files (*.*)\0*\0\0\0";
207 of
.lpstrCustomFilter
= NULL
;
209 of
.lpstrFile
= filename
;
211 of
.nMaxFile
= FILENAME_MAX
;
212 of
.lpstrFileTitle
= NULL
;
213 of
.lpstrTitle
= dlgtitle
;
215 return request_file(NULL
, &of
, FALSE
, save
);
219 * Dialog-box function for the Licence box.
221 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
222 WPARAM wParam
, LPARAM lParam
)
229 { /* centre the window */
233 hw
= GetDesktopWindow();
234 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
236 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
237 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
238 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
243 switch (LOWORD(wParam
)) {
258 * Dialog-box function for the About box.
260 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
261 WPARAM wParam
, LPARAM lParam
)
268 { /* centre the window */
272 hw
= GetDesktopWindow();
273 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
275 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
276 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
277 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
280 SetDlgItemText(hwnd
, 100, ver
);
283 switch (LOWORD(wParam
)) {
289 EnableWindow(hwnd
, 0);
290 DialogBox(hinst
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
291 EnableWindow(hwnd
, 1);
292 SetActiveWindow(hwnd
);
304 * Thread to generate a key.
306 struct rsa_key_thread_params
{
307 HWND progressbar
; /* notify this with progress */
308 HWND dialog
; /* notify this on completion */
309 int keysize
; /* bits in key */
312 struct dss_key
*dsskey
;
314 static DWORD WINAPI
generate_rsa_key_thread(void *param
)
316 struct rsa_key_thread_params
*params
=
317 (struct rsa_key_thread_params
*) param
;
318 struct progress prog
;
319 prog
.progbar
= params
->progressbar
;
321 progress_update(&prog
, PROGFN_INITIALISE
, 0, 0);
324 dsa_generate(params
->dsskey
, params
->keysize
, progress_update
, &prog
);
326 rsa_generate(params
->key
, params
->keysize
, progress_update
, &prog
);
328 PostMessage(params
->dialog
, WM_DONEKEY
, 0, 0);
334 struct MainDlgState
{
335 int collecting_entropy
;
336 int generation_thread_exists
;
338 int entropy_got
, entropy_required
, entropy_size
;
341 char **commentptr
; /* points to key.comment or ssh2key.comment */
342 struct ssh2_userkey ssh2key
;
345 struct dss_key dsskey
;
346 HMENU filemenu
, keymenu
, cvtmenu
;
349 static void hidemany(HWND hwnd
, const int *ids
, int hideit
)
352 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
356 static void setupbigedit1(HWND hwnd
, int id
, int idstatic
, struct RSAKey
*key
)
361 dec1
= bignum_decimal(key
->exponent
);
362 dec2
= bignum_decimal(key
->modulus
);
363 buffer
= dupprintf("%d %s %s %s", bignum_bitcount(key
->modulus
),
364 dec1
, dec2
, key
->comment
);
365 SetDlgItemText(hwnd
, id
, buffer
);
366 SetDlgItemText(hwnd
, idstatic
,
367 "&Public key for pasting into authorized_keys file:");
373 static void setupbigedit2(HWND hwnd
, int id
, int idstatic
,
374 struct ssh2_userkey
*key
)
376 unsigned char *pub_blob
;
381 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
382 buffer
= snewn(strlen(key
->alg
->name
) + 4 * ((pub_len
+ 2) / 3) +
383 strlen(key
->comment
) + 3, char);
384 strcpy(buffer
, key
->alg
->name
);
385 p
= buffer
+ strlen(buffer
);
388 while (i
< pub_len
) {
389 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
390 base64_encode_atom(pub_blob
+ i
, n
, p
);
395 strcpy(p
, key
->comment
);
396 SetDlgItemText(hwnd
, id
, buffer
);
397 SetDlgItemText(hwnd
, idstatic
, "&Public key for pasting into "
398 "OpenSSH authorized_keys file:");
403 static int save_ssh1_pubkey(char *filename
, struct RSAKey
*key
)
408 dec1
= bignum_decimal(key
->exponent
);
409 dec2
= bignum_decimal(key
->modulus
);
410 fp
= fopen(filename
, "wb");
413 fprintf(fp
, "%d %s %s %s\n",
414 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
422 * Warn about the obsolescent key file format.
424 void old_keyfile_warning(void)
426 static const char mbtitle
[] = "PuTTY Key File Warning";
427 static const char message
[] =
428 "You are loading an SSH 2 private key which has an\n"
429 "old version of the file format. This means your key\n"
430 "file is not fully tamperproof. Future versions of\n"
431 "PuTTY may stop supporting this private key format,\n"
432 "so we recommend you convert your key to the new\n"
435 "Once the key is loaded into PuTTYgen, you can perform\n"
436 "this conversion simply by saving it again.";
438 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
441 static int save_ssh2_pubkey(char *filename
, struct ssh2_userkey
*key
)
443 unsigned char *pub_blob
;
449 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
451 fp
= fopen(filename
, "wb");
455 fprintf(fp
, "---- BEGIN SSH2 PUBLIC KEY ----\n");
457 fprintf(fp
, "Comment: \"");
458 for (p
= key
->comment
; *p
; p
++) {
459 if (*p
== '\\' || *p
== '\"')
467 while (i
< pub_len
) {
469 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
470 base64_encode_atom(pub_blob
+ i
, n
, buf
);
474 if (++column
>= 16) {
482 fprintf(fp
, "---- END SSH2 PUBLIC KEY ----\n");
489 controlidstart
= 100,
496 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
497 IDC_FPSTATIC
, IDC_FINGERPRINT
,
498 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
499 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
500 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
502 IDC_GENSTATIC
, IDC_GENERATE
,
503 IDC_LOADSTATIC
, IDC_LOAD
,
504 IDC_SAVESTATIC
, IDC_SAVE
, IDC_SAVEPUB
,
506 IDC_TYPESTATIC
, IDC_KEYSSH1
, IDC_KEYSSH2RSA
, IDC_KEYSSH2DSA
,
507 IDC_BITSSTATIC
, IDC_BITS
,
510 IDC_IMPORT
, IDC_EXPORT_OPENSSH
, IDC_EXPORT_SSHCOM
513 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
514 static const int generating_ids
[] = { IDC_GENERATING
, IDC_PROGRESS
, 0 };
515 static const int gotkey_ids
[] = {
516 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
517 IDC_FPSTATIC
, IDC_FINGERPRINT
,
518 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
519 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
520 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
524 * Small UI helper function to switch the state of the main dialog
525 * by enabling and disabling controls and menu items.
527 void ui_set_state(HWND hwnd
, struct MainDlgState
*state
, int status
)
533 hidemany(hwnd
, nokey_ids
, FALSE
);
534 hidemany(hwnd
, generating_ids
, TRUE
);
535 hidemany(hwnd
, gotkey_ids
, TRUE
);
536 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
537 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
538 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
539 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
540 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
541 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
542 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
543 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
544 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
545 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
546 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
547 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
548 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
549 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_ENABLED
|MF_BYCOMMAND
);
550 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_ENABLED
|MF_BYCOMMAND
);
551 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
552 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
553 MF_GRAYED
|MF_BYCOMMAND
);
554 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
555 MF_GRAYED
|MF_BYCOMMAND
);
557 case 1: /* generating key */
558 hidemany(hwnd
, nokey_ids
, TRUE
);
559 hidemany(hwnd
, generating_ids
, FALSE
);
560 hidemany(hwnd
, gotkey_ids
, TRUE
);
561 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
562 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
563 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
564 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
565 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
566 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
567 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
568 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
569 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_GRAYED
|MF_BYCOMMAND
);
570 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
571 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
572 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_GRAYED
|MF_BYCOMMAND
);
573 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_GRAYED
|MF_BYCOMMAND
);
574 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_GRAYED
|MF_BYCOMMAND
);
575 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_GRAYED
|MF_BYCOMMAND
);
576 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_GRAYED
|MF_BYCOMMAND
);
577 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
578 MF_GRAYED
|MF_BYCOMMAND
);
579 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
580 MF_GRAYED
|MF_BYCOMMAND
);
583 hidemany(hwnd
, nokey_ids
, TRUE
);
584 hidemany(hwnd
, generating_ids
, TRUE
);
585 hidemany(hwnd
, gotkey_ids
, FALSE
);
586 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
587 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
588 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
589 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
590 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
591 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
592 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
593 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
594 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
595 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_ENABLED
|MF_BYCOMMAND
);
596 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_ENABLED
|MF_BYCOMMAND
);
597 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
598 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
599 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
,MF_ENABLED
|MF_BYCOMMAND
);
600 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
,MF_ENABLED
|MF_BYCOMMAND
);
601 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
603 * Enable export menu items if and only if the key type
604 * supports this kind of export.
606 type
= state
->ssh2 ? SSH_KEYTYPE_SSH2
: SSH_KEYTYPE_SSH1
;
607 #define do_export_menuitem(x,y) \
608 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
609 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
610 do_export_menuitem(IDC_EXPORT_OPENSSH
, SSH_KEYTYPE_OPENSSH
);
611 do_export_menuitem(IDC_EXPORT_SSHCOM
, SSH_KEYTYPE_SSHCOM
);
612 #undef do_export_menuitem
617 void load_key_file(HWND hwnd
, struct MainDlgState
*state
,
618 Filename filename
, int was_import_cmd
)
620 char passphrase
[PASSPHRASE_MAXLEN
];
624 const char *errmsg
= NULL
;
626 struct PassphraseProcStruct pps
;
627 struct RSAKey newkey1
;
628 struct ssh2_userkey
*newkey2
= NULL
;
630 type
= realtype
= key_type(&filename
);
631 if (type
!= SSH_KEYTYPE_SSH1
&&
632 type
!= SSH_KEYTYPE_SSH2
&&
633 !import_possible(type
)) {
634 char *msg
= dupprintf("Couldn't load private key (%s)",
635 key_type_to_str(type
));
636 MessageBox(NULL
, msg
,
637 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
642 if (type
!= SSH_KEYTYPE_SSH1
&&
643 type
!= SSH_KEYTYPE_SSH2
) {
645 type
= import_target_type(type
);
649 if (realtype
== SSH_KEYTYPE_SSH1
)
650 needs_pass
= rsakey_encrypted(&filename
, &comment
);
651 else if (realtype
== SSH_KEYTYPE_SSH2
)
653 ssh2_userkey_encrypted(&filename
, &comment
);
655 needs_pass
= import_encrypted(&filename
, realtype
,
657 pps
.passphrase
= passphrase
;
658 pps
.comment
= comment
;
662 dlgret
= DialogBoxParam(hinst
,
663 MAKEINTRESOURCE(210),
664 NULL
, PassphraseProc
,
672 if (type
== SSH_KEYTYPE_SSH1
) {
673 if (realtype
== type
)
674 ret
= loadrsakey(&filename
, &newkey1
,
675 passphrase
, &errmsg
);
677 ret
= import_ssh1(&filename
, realtype
,
678 &newkey1
, passphrase
, &errmsg
);
680 if (realtype
== type
)
681 newkey2
= ssh2_load_userkey(&filename
,
682 passphrase
, &errmsg
);
684 newkey2
= import_ssh2(&filename
, realtype
,
685 passphrase
, &errmsg
);
686 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
697 char *msg
= dupprintf("Couldn't load private key (%s)", errmsg
);
698 MessageBox(NULL
, msg
, "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
700 } else if (ret
== 1) {
702 * Now update the key controls with all the
706 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
708 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
710 if (type
== SSH_KEYTYPE_SSH1
) {
715 state
->commentptr
= &state
->key
.comment
;
716 state
->key
= newkey1
;
719 * Set the key fingerprint.
721 savecomment
= state
->key
.comment
;
722 state
->key
.comment
= NULL
;
723 rsa_fingerprint(buf
, sizeof(buf
),
725 state
->key
.comment
= savecomment
;
727 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
729 * Construct a decimal representation
730 * of the key, for pasting into
731 * .ssh/authorized_keys on a Unix box.
733 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
734 IDC_PKSTATIC
, &state
->key
);
741 &state
->ssh2key
.comment
;
742 state
->ssh2key
= *newkey2
; /* structure copy */
745 savecomment
= state
->ssh2key
.comment
;
746 state
->ssh2key
.comment
= NULL
;
749 fingerprint(state
->ssh2key
.data
);
750 state
->ssh2key
.comment
= savecomment
;
752 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
755 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
756 IDC_PKSTATIC
, &state
->ssh2key
);
758 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
762 * Finally, hide the progress bar and show
765 ui_set_state(hwnd
, state
, 2);
766 state
->key_exists
= TRUE
;
769 * If the user has imported a foreign key
770 * using the Load command, let them know.
771 * If they've used the Import command, be
774 if (realtype
!= type
&& !was_import_cmd
) {
776 sprintf(msg
, "Successfully imported foreign key\n"
778 "To use this key with PuTTY, you need to\n"
779 "use the \"Save private key\" command to\n"
780 "save it in PuTTY's own format.",
781 key_type_to_str(realtype
));
782 MessageBox(NULL
, msg
, "PuTTYgen Notice",
783 MB_OK
| MB_ICONINFORMATION
);
789 * Dialog-box function for the main PuTTYgen dialog box.
791 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
792 WPARAM wParam
, LPARAM lParam
)
794 static const char generating_msg
[] =
795 "Please wait while a key is generated...";
796 static const char entropy_msg
[] =
797 "Please generate some randomness by moving the mouse over the blank area.";
798 struct MainDlgState
*state
;
803 SetWindowLong(hwnd
, GWL_EXSTYLE
,
804 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
807 * If we add a Help button, this is where we destroy it
808 * if the help file isn't present.
811 requested_help
= FALSE
;
812 SendMessage(hwnd
, WM_SETICON
, (WPARAM
) ICON_BIG
,
813 (LPARAM
) LoadIcon(hinst
, MAKEINTRESOURCE(200)));
815 state
= snew(struct MainDlgState
);
816 state
->generation_thread_exists
= FALSE
;
817 state
->collecting_entropy
= FALSE
;
818 state
->entropy
= NULL
;
819 state
->key_exists
= FALSE
;
820 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
826 menu1
= CreateMenu();
827 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
828 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
829 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
830 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
831 AppendMenu(menu1
, MF_ENABLED
, IDC_QUIT
, "E&xit");
832 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
833 state
->filemenu
= menu1
;
835 menu1
= CreateMenu();
836 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
837 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
838 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH1
, "SSH&1 key (RSA)");
839 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2RSA
, "SSH2 &RSA key");
840 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2DSA
, "SSH2 &DSA key");
841 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Key");
842 state
->keymenu
= menu1
;
844 menu1
= CreateMenu();
845 AppendMenu(menu1
, MF_ENABLED
, IDC_IMPORT
, "&Import key");
846 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
847 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
848 "Export &OpenSSH key");
849 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
850 "Export &ssh.com key");
851 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
853 state
->cvtmenu
= menu1
;
855 menu1
= CreateMenu();
856 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
858 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
859 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
867 { /* centre the window */
871 hw
= GetDesktopWindow();
872 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
874 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
875 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
876 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
880 struct ctlpos cp
, cp2
;
882 /* Accelerators used: acglops1rbd */
884 ctlposinit(&cp
, hwnd
, 4, 4, 4);
885 beginbox(&cp
, "Key", IDC_BOX_KEY
);
887 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
889 statictext(&cp2
, "", 1, IDC_GENERATING
);
890 progressbar(&cp2
, IDC_PROGRESS
);
892 "&Public key for pasting into authorized_keys file:",
893 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
894 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
895 staticedit(&cp
, "Key f&ingerprint:", IDC_FPSTATIC
,
896 IDC_FINGERPRINT
, 75);
897 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
899 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
900 IDC_COMMENTEDIT
, 75);
901 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
902 IDC_PASSPHRASE1EDIT
, 75);
903 staticpassedit(&cp
, "C&onfirm passphrase:",
904 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
906 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
907 staticbtn(&cp
, "Generate a public/private key pair",
908 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
909 staticbtn(&cp
, "Load an existing private key file",
910 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
911 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
912 "Save p&ublic key", IDC_SAVEPUB
,
913 "&Save private key", IDC_SAVE
);
915 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
916 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
917 "SSH&1 (RSA)", IDC_KEYSSH1
,
918 "SSH2 &RSA", IDC_KEYSSH2RSA
,
919 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
920 staticedit(&cp
, "Number of &bits in a generated key:",
921 IDC_BITSSTATIC
, IDC_BITS
, 20);
924 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH2RSA
);
925 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
926 IDC_KEYSSH2RSA
, MF_BYCOMMAND
);
927 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
930 * Initially, hide the progress bar and the key display,
931 * and show the no-key display. Also disable the Save
932 * buttons, because with no key we obviously can't save
935 ui_set_state(hwnd
, state
, 0);
938 * Load a key file if one was provided on the command line.
941 load_key_file(hwnd
, state
, filename_from_str(cmdline_keyfile
), 0);
945 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
946 if (state
->collecting_entropy
&&
947 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
948 state
->entropy
[state
->entropy_got
++] = lParam
;
949 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
950 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
951 state
->entropy_got
, 0);
952 if (state
->entropy_got
>= state
->entropy_required
) {
953 struct rsa_key_thread_params
*params
;
957 * Seed the entropy pool
959 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
960 memset(state
->entropy
, 0, state
->entropy_size
);
961 sfree(state
->entropy
);
962 state
->collecting_entropy
= FALSE
;
964 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
965 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
966 MAKELPARAM(0, PROGRESSRANGE
));
967 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
969 params
= snew(struct rsa_key_thread_params
);
970 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
971 params
->dialog
= hwnd
;
972 params
->keysize
= state
->keysize
;
973 params
->is_dsa
= state
->is_dsa
;
974 params
->key
= &state
->key
;
975 params
->dsskey
= &state
->dsskey
;
977 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
978 params
, 0, &threadid
)) {
979 MessageBox(hwnd
, "Out of thread resources",
980 "Key generation error",
981 MB_OK
| MB_ICONERROR
);
984 state
->generation_thread_exists
= TRUE
;
990 switch (LOWORD(wParam
)) {
995 state
= (struct MainDlgState
*)
996 GetWindowLong(hwnd
, GWL_USERDATA
);
997 if (!IsDlgButtonChecked(hwnd
, LOWORD(wParam
)))
998 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
1000 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
1001 LOWORD(wParam
), MF_BYCOMMAND
);
1005 PostMessage(hwnd
, WM_CLOSE
, 0, 0);
1007 case IDC_COMMENTEDIT
:
1008 if (HIWORD(wParam
) == EN_CHANGE
) {
1009 state
= (struct MainDlgState
*)
1010 GetWindowLong(hwnd
, GWL_USERDATA
);
1011 if (state
->key_exists
) {
1012 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
1013 int len
= GetWindowTextLength(editctl
);
1014 if (*state
->commentptr
)
1015 sfree(*state
->commentptr
);
1016 *state
->commentptr
= snewn(len
+ 1, char);
1017 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
1019 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
1022 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
1029 EnableWindow(hwnd
, 0);
1030 DialogBox(hinst
, MAKEINTRESOURCE(213), hwnd
, AboutProc
);
1031 EnableWindow(hwnd
, 1);
1032 SetActiveWindow(hwnd
);
1035 if (HIWORD(wParam
) == BN_CLICKED
||
1036 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1038 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
1039 (DWORD
)"JI(`',`puttygen.general')");
1040 requested_help
= TRUE
;
1045 if (HIWORD(wParam
) != BN_CLICKED
&&
1046 HIWORD(wParam
) != BN_DOUBLECLICKED
)
1049 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1050 if (!state
->generation_thread_exists
) {
1052 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
1054 state
->keysize
= DEFAULT_KEYSIZE
;
1055 /* If we ever introduce a new key type, check it here! */
1056 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
1057 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
1058 if (state
->keysize
< 256) {
1059 int ret
= MessageBox(hwnd
,
1060 "PuTTYgen will not generate a key"
1061 " smaller than 256 bits.\n"
1062 "Key length reset to 256. Continue?",
1064 MB_ICONWARNING
| MB_OKCANCEL
);
1067 state
->keysize
= 256;
1068 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
1070 ui_set_state(hwnd
, state
, 1);
1071 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
1072 state
->key_exists
= FALSE
;
1073 state
->collecting_entropy
= TRUE
;
1076 * My brief statistical tests on mouse movements
1077 * suggest that there are about 2.5 bits of
1078 * randomness in the x position, 2.5 in the y
1079 * position, and 1.7 in the message time, making
1080 * 5.7 bits of unpredictability per mouse movement.
1081 * However, other people have told me it's far less
1082 * than that, so I'm going to be stupidly cautious
1083 * and knock that down to a nice round 2. With this
1084 * method, we require two words per mouse movement,
1085 * so with 2 bits per mouse movement we expect 2
1086 * bits every 2 words.
1088 state
->entropy_required
= (state
->keysize
/ 2) * 2;
1089 state
->entropy_got
= 0;
1090 state
->entropy_size
= (state
->entropy_required
*
1092 state
->entropy
= snewn(state
->entropy_required
, unsigned);
1094 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1095 MAKELPARAM(0, state
->entropy_required
));
1096 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
1100 case IDC_EXPORT_OPENSSH
:
1101 case IDC_EXPORT_SSHCOM
:
1102 if (HIWORD(wParam
) != BN_CLICKED
)
1105 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1106 if (state
->key_exists
) {
1107 char filename
[FILENAME_MAX
];
1108 char passphrase
[PASSPHRASE_MAXLEN
];
1109 char passphrase2
[PASSPHRASE_MAXLEN
];
1113 realtype
= SSH_KEYTYPE_SSH2
;
1115 realtype
= SSH_KEYTYPE_SSH1
;
1117 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
1118 type
= SSH_KEYTYPE_OPENSSH
;
1119 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
1120 type
= SSH_KEYTYPE_SSHCOM
;
1124 if (type
!= realtype
&&
1125 import_target_type(type
) != realtype
) {
1127 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
1128 " format", (state
->ssh2 ?
2 : 1),
1129 (state
->ssh2 ?
1 : 2));
1130 MessageBox(hwnd
, msg
,
1131 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1135 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1136 passphrase
, sizeof(passphrase
));
1137 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1138 passphrase2
, sizeof(passphrase2
));
1139 if (strcmp(passphrase
, passphrase2
)) {
1141 "The two passphrases given do not match.",
1142 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1147 ret
= MessageBox(hwnd
,
1148 "Are you sure you want to save this key\n"
1149 "without a passphrase to protect it?",
1151 MB_YESNO
| MB_ICONWARNING
);
1155 if (prompt_keyfile(hwnd
, "Save private key as:",
1156 filename
, 1, (type
== realtype
))) {
1158 FILE *fp
= fopen(filename
, "r");
1162 buffer
= dupprintf("Overwrite existing file\n%s?",
1164 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1165 MB_YESNO
| MB_ICONWARNING
);
1172 Filename fn
= filename_from_str(filename
);
1173 if (type
!= realtype
)
1174 ret
= export_ssh2(&fn
, type
, &state
->ssh2key
,
1175 *passphrase ? passphrase
: NULL
);
1177 ret
= ssh2_save_userkey(&fn
, &state
->ssh2key
,
1178 *passphrase ? passphrase
:
1181 Filename fn
= filename_from_str(filename
);
1182 if (type
!= realtype
)
1183 ret
= export_ssh1(&fn
, type
, &state
->key
,
1184 *passphrase ? passphrase
: NULL
);
1186 ret
= saversakey(&fn
, &state
->key
,
1187 *passphrase ? passphrase
: NULL
);
1190 MessageBox(hwnd
, "Unable to save key file",
1191 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1197 if (HIWORD(wParam
) != BN_CLICKED
)
1200 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1201 if (state
->key_exists
) {
1202 char filename
[FILENAME_MAX
];
1203 if (prompt_keyfile(hwnd
, "Save public key as:",
1206 FILE *fp
= fopen(filename
, "r");
1210 buffer
= dupprintf("Overwrite existing file\n%s?",
1212 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1213 MB_YESNO
| MB_ICONWARNING
);
1219 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
1221 ret
= save_ssh1_pubkey(filename
, &state
->key
);
1224 MessageBox(hwnd
, "Unable to save key file",
1225 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1232 if (HIWORD(wParam
) != BN_CLICKED
)
1235 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1236 if (!state
->generation_thread_exists
) {
1237 char filename
[FILENAME_MAX
];
1238 if (prompt_keyfile(hwnd
, "Load private key:",
1239 filename
, 0, LOWORD(wParam
)==IDC_LOAD
))
1240 load_key_file(hwnd
, state
, filename_from_str(filename
),
1241 LOWORD(wParam
) != IDC_LOAD
);
1247 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1248 state
->generation_thread_exists
= FALSE
;
1249 state
->key_exists
= TRUE
;
1250 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1251 MAKELPARAM(0, PROGRESSRANGE
));
1252 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1254 if (state
->is_dsa
) {
1255 state
->ssh2key
.data
= &state
->dsskey
;
1256 state
->ssh2key
.alg
= &ssh_dss
;
1258 state
->ssh2key
.data
= &state
->key
;
1259 state
->ssh2key
.alg
= &ssh_rsa
;
1261 state
->commentptr
= &state
->ssh2key
.comment
;
1263 state
->commentptr
= &state
->key
.comment
;
1266 * Invent a comment for the key. We'll do this by including
1267 * the date in it. This will be so horrifyingly ugly that
1268 * the user will immediately want to change it, which is
1271 *state
->commentptr
= snewn(30, char);
1276 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", &tm
);
1278 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", &tm
);
1282 * Now update the key controls with all the key data.
1287 * Blank passphrase, initially. This isn't dangerous,
1288 * because we will warn (Are You Sure?) before allowing
1289 * the user to save an unprotected private key.
1291 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1292 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1296 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1298 * Set the key fingerprint.
1300 savecomment
= *state
->commentptr
;
1301 *state
->commentptr
= NULL
;
1304 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1305 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1309 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1310 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1312 *state
->commentptr
= savecomment
;
1314 * Construct a decimal representation of the key, for
1315 * pasting into .ssh/authorized_keys or
1316 * .ssh/authorized_keys2 on a Unix box.
1319 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1320 IDC_PKSTATIC
, &state
->ssh2key
);
1322 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1323 IDC_PKSTATIC
, &state
->key
);
1327 * Finally, hide the progress bar and show the key data.
1329 ui_set_state(hwnd
, state
, 2);
1333 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1336 case IDC_GENERATING
:
1340 topic
= "puttygen.generate"; break;
1342 case IDC_KEYDISPLAY
:
1343 topic
= "puttygen.pastekey"; break;
1345 case IDC_FINGERPRINT
:
1346 topic
= "puttygen.fingerprint"; break;
1347 case IDC_COMMENTSTATIC
:
1348 case IDC_COMMENTEDIT
:
1349 topic
= "puttygen.comment"; break;
1350 case IDC_PASSPHRASE1STATIC
:
1351 case IDC_PASSPHRASE1EDIT
:
1352 case IDC_PASSPHRASE2STATIC
:
1353 case IDC_PASSPHRASE2EDIT
:
1354 topic
= "puttygen.passphrase"; break;
1355 case IDC_LOADSTATIC
:
1357 topic
= "puttygen.load"; break;
1358 case IDC_SAVESTATIC
:
1360 topic
= "puttygen.savepriv"; break;
1362 topic
= "puttygen.savepub"; break;
1363 case IDC_TYPESTATIC
:
1365 case IDC_KEYSSH2RSA
:
1366 case IDC_KEYSSH2DSA
:
1367 topic
= "puttygen.keytype"; break;
1368 case IDC_BITSSTATIC
:
1370 topic
= "puttygen.bits"; break;
1372 case IDC_EXPORT_OPENSSH
:
1373 case IDC_EXPORT_SSHCOM
:
1374 topic
= "puttygen.conversions"; break;
1377 char *cmd
= dupprintf("JI(`',`%s')", topic
);
1378 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1380 requested_help
= TRUE
;
1387 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1389 if (requested_help
) {
1390 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1391 requested_help
= FALSE
;
1399 void cleanup_exit(int code
) { exit(code
); }
1401 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1406 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
1410 * Assume the first argument to be a private key file, and
1411 * attempt to load it.
1413 cmdline_keyfile
= argv
[0];
1416 InitCommonControls();
1420 * See if we can find our Help file.
1423 char b
[2048], *p
, *q
, *r
;
1425 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1427 p
= strrchr(b
, '\\');
1428 if (p
&& p
>= r
) r
= p
+1;
1429 q
= strrchr(b
, ':');
1430 if (q
&& q
>= r
) r
= q
+1;
1431 strcpy(r
, PUTTY_HELP_FILE
);
1432 if ( (fp
= fopen(b
, "r")) != NULL
) {
1433 help_path
= dupstr(b
);
1440 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1441 MainDlgProc
) != IDOK
;