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 int requested_help
;
26 static char *cmdline_keyfile
= NULL
;
29 * Print a modal (Really Bad) message box and perform a fatal exit.
31 void modalfatalbox(char *fmt
, ...)
37 stuff
= dupvprintf(fmt
, ap
);
39 MessageBox(NULL
, stuff
, "PuTTYgen Fatal Error",
40 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
45 /* ----------------------------------------------------------------------
46 * Progress report code. This is really horrible :-)
48 #define PROGRESSRANGE 65535
54 unsigned startpoint
, total
;
55 unsigned param
, current
, n
; /* if exponential */
56 unsigned mult
; /* if linear */
58 unsigned total
, divisor
, range
;
62 static void progress_update(void *param
, int action
, int phase
, int iprogress
)
64 struct progress
*p
= (struct progress
*) param
;
65 unsigned progress
= iprogress
;
68 if (action
< PROGFN_READY
&& p
->nphases
< phase
)
71 case PROGFN_INITIALISE
:
74 case PROGFN_LIN_PHASE
:
75 p
->phases
[phase
-1].exponential
= 0;
76 p
->phases
[phase
-1].mult
= p
->phases
[phase
].total
/ progress
;
78 case PROGFN_EXP_PHASE
:
79 p
->phases
[phase
-1].exponential
= 1;
80 p
->phases
[phase
-1].param
= 0x10000 + progress
;
81 p
->phases
[phase
-1].current
= p
->phases
[phase
-1].total
;
82 p
->phases
[phase
-1].n
= 0;
84 case PROGFN_PHASE_EXTENT
:
85 p
->phases
[phase
-1].total
= progress
;
91 for (i
= 0; i
< p
->nphases
; i
++) {
92 p
->phases
[i
].startpoint
= total
;
93 total
+= p
->phases
[i
].total
;
96 p
->divisor
= ((p
->total
+ PROGRESSRANGE
- 1) / PROGRESSRANGE
);
97 p
->range
= p
->total
/ p
->divisor
;
98 SendMessage(p
->progbar
, PBM_SETRANGE
, 0, MAKELPARAM(0, p
->range
));
101 case PROGFN_PROGRESS
:
102 if (p
->phases
[phase
-1].exponential
) {
103 while (p
->phases
[phase
-1].n
< progress
) {
104 p
->phases
[phase
-1].n
++;
105 p
->phases
[phase
-1].current
*= p
->phases
[phase
-1].param
;
106 p
->phases
[phase
-1].current
/= 0x10000;
108 position
= (p
->phases
[phase
-1].startpoint
+
109 p
->phases
[phase
-1].total
- p
->phases
[phase
-1].current
);
111 position
= (p
->phases
[phase
-1].startpoint
+
112 progress
* p
->phases
[phase
-1].mult
);
114 SendMessage(p
->progbar
, PBM_SETPOS
, position
/ p
->divisor
, 0);
121 #define PASSPHRASE_MAXLEN 512
123 struct PassphraseProcStruct
{
129 * Dialog-box function for the passphrase box.
131 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
132 WPARAM wParam
, LPARAM lParam
)
134 static char *passphrase
= NULL
;
135 struct PassphraseProcStruct
*p
;
139 SetForegroundWindow(hwnd
);
140 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
141 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
146 { /* centre the window */
150 hw
= GetDesktopWindow();
151 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
153 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
154 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
155 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
158 p
= (struct PassphraseProcStruct
*) lParam
;
159 passphrase
= p
->passphrase
;
161 SetDlgItemText(hwnd
, 101, p
->comment
);
163 SetDlgItemText(hwnd
, 102, passphrase
);
166 switch (LOWORD(wParam
)) {
176 case 102: /* edit box */
177 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
178 GetDlgItemText(hwnd
, 102, passphrase
,
179 PASSPHRASE_MAXLEN
- 1);
180 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
193 * Prompt for a key file. Assumes the filename buffer is of size
196 static int prompt_keyfile(HWND hwnd
, char *dlgtitle
,
197 char *filename
, int save
, int ppk
)
200 memset(&of
, 0, sizeof(of
));
203 of
.lpstrFilter
= "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
204 "All Files (*.*)\0*\0\0\0";
205 of
.lpstrDefExt
= ".ppk";
207 of
.lpstrFilter
= "All Files (*.*)\0*\0\0\0";
209 of
.lpstrCustomFilter
= NULL
;
211 of
.lpstrFile
= filename
;
213 of
.nMaxFile
= FILENAME_MAX
;
214 of
.lpstrFileTitle
= NULL
;
215 of
.lpstrTitle
= dlgtitle
;
217 return request_file(NULL
, &of
, FALSE
, save
);
221 * Dialog-box function for the Licence box.
223 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
224 WPARAM wParam
, LPARAM lParam
)
231 { /* centre the window */
235 hw
= GetDesktopWindow();
236 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
238 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
239 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
240 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
245 switch (LOWORD(wParam
)) {
260 * Dialog-box function for the About box.
262 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
263 WPARAM wParam
, LPARAM lParam
)
270 { /* centre the window */
274 hw
= GetDesktopWindow();
275 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
277 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
278 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
279 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
282 SetDlgItemText(hwnd
, 100, ver
);
285 switch (LOWORD(wParam
)) {
291 EnableWindow(hwnd
, 0);
292 DialogBox(hinst
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
293 EnableWindow(hwnd
, 1);
294 SetActiveWindow(hwnd
);
306 * Thread to generate a key.
308 struct rsa_key_thread_params
{
309 HWND progressbar
; /* notify this with progress */
310 HWND dialog
; /* notify this on completion */
311 int keysize
; /* bits in key */
314 struct dss_key
*dsskey
;
316 static DWORD WINAPI
generate_rsa_key_thread(void *param
)
318 struct rsa_key_thread_params
*params
=
319 (struct rsa_key_thread_params
*) param
;
320 struct progress prog
;
321 prog
.progbar
= params
->progressbar
;
323 progress_update(&prog
, PROGFN_INITIALISE
, 0, 0);
326 dsa_generate(params
->dsskey
, params
->keysize
, progress_update
, &prog
);
328 rsa_generate(params
->key
, params
->keysize
, progress_update
, &prog
);
330 PostMessage(params
->dialog
, WM_DONEKEY
, 0, 0);
336 struct MainDlgState
{
337 int collecting_entropy
;
338 int generation_thread_exists
;
340 int entropy_got
, entropy_required
, entropy_size
;
343 char **commentptr
; /* points to key.comment or ssh2key.comment */
344 struct ssh2_userkey ssh2key
;
347 struct dss_key dsskey
;
348 HMENU filemenu
, keymenu
, cvtmenu
;
351 static void hidemany(HWND hwnd
, const int *ids
, int hideit
)
354 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
358 static void setupbigedit1(HWND hwnd
, int id
, int idstatic
, struct RSAKey
*key
)
363 dec1
= bignum_decimal(key
->exponent
);
364 dec2
= bignum_decimal(key
->modulus
);
365 buffer
= dupprintf("%d %s %s %s", bignum_bitcount(key
->modulus
),
366 dec1
, dec2
, key
->comment
);
367 SetDlgItemText(hwnd
, id
, buffer
);
368 SetDlgItemText(hwnd
, idstatic
,
369 "&Public key for pasting into authorized_keys file:");
375 static void setupbigedit2(HWND hwnd
, int id
, int idstatic
,
376 struct ssh2_userkey
*key
)
378 unsigned char *pub_blob
;
383 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
384 buffer
= snewn(strlen(key
->alg
->name
) + 4 * ((pub_len
+ 2) / 3) +
385 strlen(key
->comment
) + 3, char);
386 strcpy(buffer
, key
->alg
->name
);
387 p
= buffer
+ strlen(buffer
);
390 while (i
< pub_len
) {
391 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
392 base64_encode_atom(pub_blob
+ i
, n
, p
);
397 strcpy(p
, key
->comment
);
398 SetDlgItemText(hwnd
, id
, buffer
);
399 SetDlgItemText(hwnd
, idstatic
, "&Public key for pasting into "
400 "OpenSSH authorized_keys file:");
405 static int save_ssh1_pubkey(char *filename
, struct RSAKey
*key
)
410 dec1
= bignum_decimal(key
->exponent
);
411 dec2
= bignum_decimal(key
->modulus
);
412 fp
= fopen(filename
, "wb");
415 fprintf(fp
, "%d %s %s %s\n",
416 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
424 * Warn about the obsolescent key file format.
426 void old_keyfile_warning(void)
428 static const char mbtitle
[] = "PuTTY Key File Warning";
429 static const char message
[] =
430 "You are loading an SSH 2 private key which has an\n"
431 "old version of the file format. This means your key\n"
432 "file is not fully tamperproof. Future versions of\n"
433 "PuTTY may stop supporting this private key format,\n"
434 "so we recommend you convert your key to the new\n"
437 "Once the key is loaded into PuTTYgen, you can perform\n"
438 "this conversion simply by saving it again.";
440 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
443 static int save_ssh2_pubkey(char *filename
, struct ssh2_userkey
*key
)
445 unsigned char *pub_blob
;
451 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
453 fp
= fopen(filename
, "wb");
457 fprintf(fp
, "---- BEGIN SSH2 PUBLIC KEY ----\n");
459 fprintf(fp
, "Comment: \"");
460 for (p
= key
->comment
; *p
; p
++) {
461 if (*p
== '\\' || *p
== '\"')
469 while (i
< pub_len
) {
471 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
472 base64_encode_atom(pub_blob
+ i
, n
, buf
);
476 if (++column
>= 16) {
484 fprintf(fp
, "---- END SSH2 PUBLIC KEY ----\n");
491 controlidstart
= 100,
498 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
499 IDC_FPSTATIC
, IDC_FINGERPRINT
,
500 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
501 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
502 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
504 IDC_GENSTATIC
, IDC_GENERATE
,
505 IDC_LOADSTATIC
, IDC_LOAD
,
506 IDC_SAVESTATIC
, IDC_SAVE
, IDC_SAVEPUB
,
508 IDC_TYPESTATIC
, IDC_KEYSSH1
, IDC_KEYSSH2RSA
, IDC_KEYSSH2DSA
,
509 IDC_BITSSTATIC
, IDC_BITS
,
512 IDC_IMPORT
, IDC_EXPORT_OPENSSH
, IDC_EXPORT_SSHCOM
515 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
516 static const int generating_ids
[] = { IDC_GENERATING
, IDC_PROGRESS
, 0 };
517 static const int gotkey_ids
[] = {
518 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
519 IDC_FPSTATIC
, IDC_FINGERPRINT
,
520 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
521 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
522 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
526 * Small UI helper function to switch the state of the main dialog
527 * by enabling and disabling controls and menu items.
529 void ui_set_state(HWND hwnd
, struct MainDlgState
*state
, int status
)
535 hidemany(hwnd
, nokey_ids
, FALSE
);
536 hidemany(hwnd
, generating_ids
, TRUE
);
537 hidemany(hwnd
, gotkey_ids
, TRUE
);
538 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
539 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
540 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
541 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
542 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
543 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
544 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
545 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
546 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
547 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
548 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
549 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
550 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
551 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_ENABLED
|MF_BYCOMMAND
);
552 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_ENABLED
|MF_BYCOMMAND
);
553 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
554 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
555 MF_GRAYED
|MF_BYCOMMAND
);
556 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
557 MF_GRAYED
|MF_BYCOMMAND
);
559 case 1: /* generating key */
560 hidemany(hwnd
, nokey_ids
, TRUE
);
561 hidemany(hwnd
, generating_ids
, FALSE
);
562 hidemany(hwnd
, gotkey_ids
, TRUE
);
563 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
564 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
565 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
566 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
567 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
568 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
569 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
570 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
571 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_GRAYED
|MF_BYCOMMAND
);
572 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
573 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
574 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_GRAYED
|MF_BYCOMMAND
);
575 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_GRAYED
|MF_BYCOMMAND
);
576 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_GRAYED
|MF_BYCOMMAND
);
577 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_GRAYED
|MF_BYCOMMAND
);
578 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_GRAYED
|MF_BYCOMMAND
);
579 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
580 MF_GRAYED
|MF_BYCOMMAND
);
581 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
582 MF_GRAYED
|MF_BYCOMMAND
);
585 hidemany(hwnd
, nokey_ids
, TRUE
);
586 hidemany(hwnd
, generating_ids
, TRUE
);
587 hidemany(hwnd
, gotkey_ids
, FALSE
);
588 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
589 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
590 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
591 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
592 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
593 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
594 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
595 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
596 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
597 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_ENABLED
|MF_BYCOMMAND
);
598 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_ENABLED
|MF_BYCOMMAND
);
599 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
600 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
601 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
,MF_ENABLED
|MF_BYCOMMAND
);
602 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
,MF_ENABLED
|MF_BYCOMMAND
);
603 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
605 * Enable export menu items if and only if the key type
606 * supports this kind of export.
608 type
= state
->ssh2 ? SSH_KEYTYPE_SSH2
: SSH_KEYTYPE_SSH1
;
609 #define do_export_menuitem(x,y) \
610 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
611 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
612 do_export_menuitem(IDC_EXPORT_OPENSSH
, SSH_KEYTYPE_OPENSSH
);
613 do_export_menuitem(IDC_EXPORT_SSHCOM
, SSH_KEYTYPE_SSHCOM
);
614 #undef do_export_menuitem
619 void load_key_file(HWND hwnd
, struct MainDlgState
*state
,
620 Filename filename
, int was_import_cmd
)
622 char passphrase
[PASSPHRASE_MAXLEN
];
626 const char *errmsg
= NULL
;
628 struct PassphraseProcStruct pps
;
629 struct RSAKey newkey1
;
630 struct ssh2_userkey
*newkey2
= NULL
;
632 type
= realtype
= key_type(&filename
);
633 if (type
!= SSH_KEYTYPE_SSH1
&&
634 type
!= SSH_KEYTYPE_SSH2
&&
635 !import_possible(type
)) {
636 char *msg
= dupprintf("Couldn't load private key (%s)",
637 key_type_to_str(type
));
638 MessageBox(NULL
, msg
,
639 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
644 if (type
!= SSH_KEYTYPE_SSH1
&&
645 type
!= SSH_KEYTYPE_SSH2
) {
647 type
= import_target_type(type
);
651 if (realtype
== SSH_KEYTYPE_SSH1
)
652 needs_pass
= rsakey_encrypted(&filename
, &comment
);
653 else if (realtype
== SSH_KEYTYPE_SSH2
)
655 ssh2_userkey_encrypted(&filename
, &comment
);
657 needs_pass
= import_encrypted(&filename
, realtype
,
659 pps
.passphrase
= passphrase
;
660 pps
.comment
= comment
;
664 dlgret
= DialogBoxParam(hinst
,
665 MAKEINTRESOURCE(210),
666 NULL
, PassphraseProc
,
674 if (type
== SSH_KEYTYPE_SSH1
) {
675 if (realtype
== type
)
676 ret
= loadrsakey(&filename
, &newkey1
,
677 passphrase
, &errmsg
);
679 ret
= import_ssh1(&filename
, realtype
,
680 &newkey1
, passphrase
, &errmsg
);
682 if (realtype
== type
)
683 newkey2
= ssh2_load_userkey(&filename
,
684 passphrase
, &errmsg
);
686 newkey2
= import_ssh2(&filename
, realtype
,
687 passphrase
, &errmsg
);
688 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
699 char *msg
= dupprintf("Couldn't load private key (%s)", errmsg
);
700 MessageBox(NULL
, msg
, "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
702 } else if (ret
== 1) {
704 * Now update the key controls with all the
708 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
710 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
712 if (type
== SSH_KEYTYPE_SSH1
) {
717 state
->commentptr
= &state
->key
.comment
;
718 state
->key
= newkey1
;
721 * Set the key fingerprint.
723 savecomment
= state
->key
.comment
;
724 state
->key
.comment
= NULL
;
725 rsa_fingerprint(buf
, sizeof(buf
),
727 state
->key
.comment
= savecomment
;
729 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
731 * Construct a decimal representation
732 * of the key, for pasting into
733 * .ssh/authorized_keys on a Unix box.
735 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
736 IDC_PKSTATIC
, &state
->key
);
743 &state
->ssh2key
.comment
;
744 state
->ssh2key
= *newkey2
; /* structure copy */
747 savecomment
= state
->ssh2key
.comment
;
748 state
->ssh2key
.comment
= NULL
;
751 fingerprint(state
->ssh2key
.data
);
752 state
->ssh2key
.comment
= savecomment
;
754 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
757 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
758 IDC_PKSTATIC
, &state
->ssh2key
);
760 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
764 * Finally, hide the progress bar and show
767 ui_set_state(hwnd
, state
, 2);
768 state
->key_exists
= TRUE
;
771 * If the user has imported a foreign key
772 * using the Load command, let them know.
773 * If they've used the Import command, be
776 if (realtype
!= type
&& !was_import_cmd
) {
778 sprintf(msg
, "Successfully imported foreign key\n"
780 "To use this key with PuTTY, you need to\n"
781 "use the \"Save private key\" command to\n"
782 "save it in PuTTY's own format.",
783 key_type_to_str(realtype
));
784 MessageBox(NULL
, msg
, "PuTTYgen Notice",
785 MB_OK
| MB_ICONINFORMATION
);
791 * Dialog-box function for the main PuTTYgen dialog box.
793 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
794 WPARAM wParam
, LPARAM lParam
)
796 static const char generating_msg
[] =
797 "Please wait while a key is generated...";
798 static const char entropy_msg
[] =
799 "Please generate some randomness by moving the mouse over the blank area.";
800 struct MainDlgState
*state
;
805 SetWindowLong(hwnd
, GWL_EXSTYLE
,
806 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
809 * If we add a Help button, this is where we destroy it
810 * if the help file isn't present.
813 requested_help
= FALSE
;
814 SendMessage(hwnd
, WM_SETICON
, (WPARAM
) ICON_BIG
,
815 (LPARAM
) LoadIcon(hinst
, MAKEINTRESOURCE(200)));
817 state
= snew(struct MainDlgState
);
818 state
->generation_thread_exists
= FALSE
;
819 state
->collecting_entropy
= FALSE
;
820 state
->entropy
= NULL
;
821 state
->key_exists
= FALSE
;
822 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
828 menu1
= CreateMenu();
829 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
830 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
831 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
832 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
833 AppendMenu(menu1
, MF_ENABLED
, IDC_QUIT
, "E&xit");
834 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
835 state
->filemenu
= menu1
;
837 menu1
= CreateMenu();
838 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
839 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
840 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH1
, "SSH&1 key (RSA)");
841 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2RSA
, "SSH2 &RSA key");
842 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2DSA
, "SSH2 &DSA key");
843 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Key");
844 state
->keymenu
= menu1
;
846 menu1
= CreateMenu();
847 AppendMenu(menu1
, MF_ENABLED
, IDC_IMPORT
, "&Import key");
848 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
849 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
850 "Export &OpenSSH key");
851 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
852 "Export &ssh.com key");
853 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
855 state
->cvtmenu
= menu1
;
857 menu1
= CreateMenu();
858 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
860 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
861 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
869 { /* centre the window */
873 hw
= GetDesktopWindow();
874 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
876 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
877 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
878 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
882 struct ctlpos cp
, cp2
;
884 /* Accelerators used: acglops1rbd */
886 ctlposinit(&cp
, hwnd
, 4, 4, 4);
887 beginbox(&cp
, "Key", IDC_BOX_KEY
);
889 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
891 statictext(&cp2
, "", 1, IDC_GENERATING
);
892 progressbar(&cp2
, IDC_PROGRESS
);
894 "&Public key for pasting into authorized_keys file:",
895 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
896 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
897 staticedit(&cp
, "Key f&ingerprint:", IDC_FPSTATIC
,
898 IDC_FINGERPRINT
, 75);
899 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
901 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
902 IDC_COMMENTEDIT
, 75);
903 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
904 IDC_PASSPHRASE1EDIT
, 75);
905 staticpassedit(&cp
, "C&onfirm passphrase:",
906 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
908 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
909 staticbtn(&cp
, "Generate a public/private key pair",
910 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
911 staticbtn(&cp
, "Load an existing private key file",
912 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
913 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
914 "Save p&ublic key", IDC_SAVEPUB
,
915 "&Save private key", IDC_SAVE
);
917 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
918 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
919 "SSH&1 (RSA)", IDC_KEYSSH1
,
920 "SSH2 &RSA", IDC_KEYSSH2RSA
,
921 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
922 staticedit(&cp
, "Number of &bits in a generated key:",
923 IDC_BITSSTATIC
, IDC_BITS
, 20);
926 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH2RSA
);
927 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
928 IDC_KEYSSH2RSA
, MF_BYCOMMAND
);
929 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
932 * Initially, hide the progress bar and the key display,
933 * and show the no-key display. Also disable the Save
934 * buttons, because with no key we obviously can't save
937 ui_set_state(hwnd
, state
, 0);
940 * Load a key file if one was provided on the command line.
943 load_key_file(hwnd
, state
, filename_from_str(cmdline_keyfile
), 0);
947 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
948 if (state
->collecting_entropy
&&
949 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
950 state
->entropy
[state
->entropy_got
++] = lParam
;
951 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
952 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
953 state
->entropy_got
, 0);
954 if (state
->entropy_got
>= state
->entropy_required
) {
955 struct rsa_key_thread_params
*params
;
959 * Seed the entropy pool
961 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
962 memset(state
->entropy
, 0, state
->entropy_size
);
963 sfree(state
->entropy
);
964 state
->collecting_entropy
= FALSE
;
966 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
967 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
968 MAKELPARAM(0, PROGRESSRANGE
));
969 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
971 params
= snew(struct rsa_key_thread_params
);
972 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
973 params
->dialog
= hwnd
;
974 params
->keysize
= state
->keysize
;
975 params
->is_dsa
= state
->is_dsa
;
976 params
->key
= &state
->key
;
977 params
->dsskey
= &state
->dsskey
;
979 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
980 params
, 0, &threadid
)) {
981 MessageBox(hwnd
, "Out of thread resources",
982 "Key generation error",
983 MB_OK
| MB_ICONERROR
);
986 state
->generation_thread_exists
= TRUE
;
992 switch (LOWORD(wParam
)) {
997 state
= (struct MainDlgState
*)
998 GetWindowLong(hwnd
, GWL_USERDATA
);
999 if (!IsDlgButtonChecked(hwnd
, LOWORD(wParam
)))
1000 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
1002 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
1003 LOWORD(wParam
), MF_BYCOMMAND
);
1007 PostMessage(hwnd
, WM_CLOSE
, 0, 0);
1009 case IDC_COMMENTEDIT
:
1010 if (HIWORD(wParam
) == EN_CHANGE
) {
1011 state
= (struct MainDlgState
*)
1012 GetWindowLong(hwnd
, GWL_USERDATA
);
1013 if (state
->key_exists
) {
1014 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
1015 int len
= GetWindowTextLength(editctl
);
1016 if (*state
->commentptr
)
1017 sfree(*state
->commentptr
);
1018 *state
->commentptr
= snewn(len
+ 1, char);
1019 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
1021 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
1024 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
1031 EnableWindow(hwnd
, 0);
1032 DialogBox(hinst
, MAKEINTRESOURCE(213), hwnd
, AboutProc
);
1033 EnableWindow(hwnd
, 1);
1034 SetActiveWindow(hwnd
);
1037 if (HIWORD(wParam
) == BN_CLICKED
||
1038 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1040 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
1041 (DWORD
)"JI(`',`puttygen.general')");
1042 requested_help
= TRUE
;
1047 if (HIWORD(wParam
) != BN_CLICKED
&&
1048 HIWORD(wParam
) != BN_DOUBLECLICKED
)
1051 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1052 if (!state
->generation_thread_exists
) {
1054 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
1056 state
->keysize
= DEFAULT_KEYSIZE
;
1057 /* If we ever introduce a new key type, check it here! */
1058 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
1059 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
1060 if (state
->keysize
< 256) {
1061 int ret
= MessageBox(hwnd
,
1062 "PuTTYgen will not generate a key"
1063 " smaller than 256 bits.\n"
1064 "Key length reset to 256. Continue?",
1066 MB_ICONWARNING
| MB_OKCANCEL
);
1069 state
->keysize
= 256;
1070 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
1072 ui_set_state(hwnd
, state
, 1);
1073 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
1074 state
->key_exists
= FALSE
;
1075 state
->collecting_entropy
= TRUE
;
1078 * My brief statistical tests on mouse movements
1079 * suggest that there are about 2.5 bits of
1080 * randomness in the x position, 2.5 in the y
1081 * position, and 1.7 in the message time, making
1082 * 5.7 bits of unpredictability per mouse movement.
1083 * However, other people have told me it's far less
1084 * than that, so I'm going to be stupidly cautious
1085 * and knock that down to a nice round 2. With this
1086 * method, we require two words per mouse movement,
1087 * so with 2 bits per mouse movement we expect 2
1088 * bits every 2 words.
1090 state
->entropy_required
= (state
->keysize
/ 2) * 2;
1091 state
->entropy_got
= 0;
1092 state
->entropy_size
= (state
->entropy_required
*
1094 state
->entropy
= snewn(state
->entropy_required
, unsigned);
1096 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1097 MAKELPARAM(0, state
->entropy_required
));
1098 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
1102 case IDC_EXPORT_OPENSSH
:
1103 case IDC_EXPORT_SSHCOM
:
1104 if (HIWORD(wParam
) != BN_CLICKED
)
1107 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1108 if (state
->key_exists
) {
1109 char filename
[FILENAME_MAX
];
1110 char passphrase
[PASSPHRASE_MAXLEN
];
1111 char passphrase2
[PASSPHRASE_MAXLEN
];
1115 realtype
= SSH_KEYTYPE_SSH2
;
1117 realtype
= SSH_KEYTYPE_SSH1
;
1119 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
1120 type
= SSH_KEYTYPE_OPENSSH
;
1121 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
1122 type
= SSH_KEYTYPE_SSHCOM
;
1126 if (type
!= realtype
&&
1127 import_target_type(type
) != realtype
) {
1129 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
1130 " format", (state
->ssh2 ?
2 : 1),
1131 (state
->ssh2 ?
1 : 2));
1132 MessageBox(hwnd
, msg
,
1133 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1137 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1138 passphrase
, sizeof(passphrase
));
1139 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1140 passphrase2
, sizeof(passphrase2
));
1141 if (strcmp(passphrase
, passphrase2
)) {
1143 "The two passphrases given do not match.",
1144 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1149 ret
= MessageBox(hwnd
,
1150 "Are you sure you want to save this key\n"
1151 "without a passphrase to protect it?",
1153 MB_YESNO
| MB_ICONWARNING
);
1157 if (prompt_keyfile(hwnd
, "Save private key as:",
1158 filename
, 1, (type
== realtype
))) {
1160 FILE *fp
= fopen(filename
, "r");
1164 buffer
= dupprintf("Overwrite existing file\n%s?",
1166 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1167 MB_YESNO
| MB_ICONWARNING
);
1174 Filename fn
= filename_from_str(filename
);
1175 if (type
!= realtype
)
1176 ret
= export_ssh2(&fn
, type
, &state
->ssh2key
,
1177 *passphrase ? passphrase
: NULL
);
1179 ret
= ssh2_save_userkey(&fn
, &state
->ssh2key
,
1180 *passphrase ? passphrase
:
1183 Filename fn
= filename_from_str(filename
);
1184 if (type
!= realtype
)
1185 ret
= export_ssh1(&fn
, type
, &state
->key
,
1186 *passphrase ? passphrase
: NULL
);
1188 ret
= saversakey(&fn
, &state
->key
,
1189 *passphrase ? passphrase
: NULL
);
1192 MessageBox(hwnd
, "Unable to save key file",
1193 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1199 if (HIWORD(wParam
) != BN_CLICKED
)
1202 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1203 if (state
->key_exists
) {
1204 char filename
[FILENAME_MAX
];
1205 if (prompt_keyfile(hwnd
, "Save public key as:",
1208 FILE *fp
= fopen(filename
, "r");
1212 buffer
= dupprintf("Overwrite existing file\n%s?",
1214 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1215 MB_YESNO
| MB_ICONWARNING
);
1221 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
1223 ret
= save_ssh1_pubkey(filename
, &state
->key
);
1226 MessageBox(hwnd
, "Unable to save key file",
1227 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1234 if (HIWORD(wParam
) != BN_CLICKED
)
1237 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1238 if (!state
->generation_thread_exists
) {
1239 char filename
[FILENAME_MAX
];
1240 if (prompt_keyfile(hwnd
, "Load private key:",
1241 filename
, 0, LOWORD(wParam
)==IDC_LOAD
))
1242 load_key_file(hwnd
, state
, filename_from_str(filename
),
1243 LOWORD(wParam
) != IDC_LOAD
);
1249 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1250 state
->generation_thread_exists
= FALSE
;
1251 state
->key_exists
= TRUE
;
1252 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1253 MAKELPARAM(0, PROGRESSRANGE
));
1254 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1256 if (state
->is_dsa
) {
1257 state
->ssh2key
.data
= &state
->dsskey
;
1258 state
->ssh2key
.alg
= &ssh_dss
;
1260 state
->ssh2key
.data
= &state
->key
;
1261 state
->ssh2key
.alg
= &ssh_rsa
;
1263 state
->commentptr
= &state
->ssh2key
.comment
;
1265 state
->commentptr
= &state
->key
.comment
;
1268 * Invent a comment for the key. We'll do this by including
1269 * the date in it. This will be so horrifyingly ugly that
1270 * the user will immediately want to change it, which is
1273 *state
->commentptr
= snewn(30, char);
1278 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", &tm
);
1280 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", &tm
);
1284 * Now update the key controls with all the key data.
1289 * Blank passphrase, initially. This isn't dangerous,
1290 * because we will warn (Are You Sure?) before allowing
1291 * the user to save an unprotected private key.
1293 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1294 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1298 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1300 * Set the key fingerprint.
1302 savecomment
= *state
->commentptr
;
1303 *state
->commentptr
= NULL
;
1306 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1307 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1311 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1312 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1314 *state
->commentptr
= savecomment
;
1316 * Construct a decimal representation of the key, for
1317 * pasting into .ssh/authorized_keys or
1318 * .ssh/authorized_keys2 on a Unix box.
1321 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1322 IDC_PKSTATIC
, &state
->ssh2key
);
1324 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1325 IDC_PKSTATIC
, &state
->key
);
1329 * Finally, hide the progress bar and show the key data.
1331 ui_set_state(hwnd
, state
, 2);
1335 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1338 case IDC_GENERATING
:
1342 topic
= "puttygen.generate"; break;
1344 case IDC_KEYDISPLAY
:
1345 topic
= "puttygen.pastekey"; break;
1347 case IDC_FINGERPRINT
:
1348 topic
= "puttygen.fingerprint"; break;
1349 case IDC_COMMENTSTATIC
:
1350 case IDC_COMMENTEDIT
:
1351 topic
= "puttygen.comment"; break;
1352 case IDC_PASSPHRASE1STATIC
:
1353 case IDC_PASSPHRASE1EDIT
:
1354 case IDC_PASSPHRASE2STATIC
:
1355 case IDC_PASSPHRASE2EDIT
:
1356 topic
= "puttygen.passphrase"; break;
1357 case IDC_LOADSTATIC
:
1359 topic
= "puttygen.load"; break;
1360 case IDC_SAVESTATIC
:
1362 topic
= "puttygen.savepriv"; break;
1364 topic
= "puttygen.savepub"; break;
1365 case IDC_TYPESTATIC
:
1367 case IDC_KEYSSH2RSA
:
1368 case IDC_KEYSSH2DSA
:
1369 topic
= "puttygen.keytype"; break;
1370 case IDC_BITSSTATIC
:
1372 topic
= "puttygen.bits"; break;
1374 case IDC_EXPORT_OPENSSH
:
1375 case IDC_EXPORT_SSHCOM
:
1376 topic
= "puttygen.conversions"; break;
1379 char *cmd
= dupprintf("JI(`',`%s')", topic
);
1380 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1382 requested_help
= TRUE
;
1389 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1391 if (requested_help
) {
1392 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1393 requested_help
= FALSE
;
1401 void cleanup_exit(int code
) { exit(code
); }
1403 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1408 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
1412 * Assume the first argument to be a private key file, and
1413 * attempt to load it.
1415 cmdline_keyfile
= argv
[0];
1418 InitCommonControls();
1422 * See if we can find our Help file.
1425 char b
[2048], *p
, *q
, *r
;
1427 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1429 p
= strrchr(b
, '\\');
1430 if (p
&& p
>= r
) r
= p
+1;
1431 q
= strrchr(b
, ':');
1432 if (q
&& q
>= r
) r
= q
+1;
1433 strcpy(r
, PUTTY_HELP_FILE
);
1434 if ( (fp
= fopen(b
, "r")) != NULL
) {
1435 help_path
= dupstr(b
);
1442 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1443 MainDlgProc
) != IDOK
;