OSDN Git Service

BranchView: Show commit subject and date
[tortoisegit/TortoiseGitJp.git] / src / TortoisePlink / Windows / WINSTORE.C
1 /*\r
2  * winstore.c: Windows-specific implementation of the interface\r
3  * defined in storage.h.\r
4  */\r
5 \r
6 #include <stdio.h>\r
7 #include <stdlib.h>\r
8 #include <limits.h>\r
9 #include "putty.h"\r
10 #include "storage.h"\r
11 \r
12 #include <shlobj.h>\r
13 #ifndef CSIDL_APPDATA\r
14 #define CSIDL_APPDATA 0x001a\r
15 #endif\r
16 #ifndef CSIDL_LOCAL_APPDATA\r
17 #define CSIDL_LOCAL_APPDATA 0x001c\r
18 #endif\r
19 \r
20 static const char *const puttystr = PUTTY_REG_POS "\\Sessions";\r
21 \r
22 static const char hex[16] = "0123456789ABCDEF";\r
23 \r
24 static int tried_shgetfolderpath = FALSE;\r
25 static HMODULE shell32_module = NULL;\r
26 typedef HRESULT (WINAPI *p_SHGetFolderPath_t)\r
27     (HWND, int, HANDLE, DWORD, LPTSTR);\r
28 static p_SHGetFolderPath_t p_SHGetFolderPath = NULL;\r
29 \r
30 static void mungestr(const char *in, char *out)\r
31 {\r
32     int candot = 0;\r
33 \r
34     while (*in) {\r
35         if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||\r
36             *in == '%' || *in < ' ' || *in > '~' || (*in == '.'\r
37                                                      && !candot)) {\r
38             *out++ = '%';\r
39             *out++ = hex[((unsigned char) *in) >> 4];\r
40             *out++ = hex[((unsigned char) *in) & 15];\r
41         } else\r
42             *out++ = *in;\r
43         in++;\r
44         candot = 1;\r
45     }\r
46     *out = '\0';\r
47     return;\r
48 }\r
49 \r
50 static void unmungestr(const char *in, char *out, int outlen)\r
51 {\r
52     while (*in) {\r
53         if (*in == '%' && in[1] && in[2]) {\r
54             int i, j;\r
55 \r
56             i = in[1] - '0';\r
57             i -= (i > 9 ? 7 : 0);\r
58             j = in[2] - '0';\r
59             j -= (j > 9 ? 7 : 0);\r
60 \r
61             *out++ = (i << 4) + j;\r
62             if (!--outlen)\r
63                 return;\r
64             in += 3;\r
65         } else {\r
66             *out++ = *in++;\r
67             if (!--outlen)\r
68                 return;\r
69         }\r
70     }\r
71     *out = '\0';\r
72     return;\r
73 }\r
74 \r
75 void *open_settings_w(const char *sessionname, char **errmsg)\r
76 {\r
77     HKEY subkey1, sesskey;\r
78     int ret;\r
79     char *p;\r
80 \r
81     *errmsg = NULL;\r
82 \r
83     if (!sessionname || !*sessionname)\r
84         sessionname = "Default Settings";\r
85 \r
86     p = snewn(3 * strlen(sessionname) + 1, char);\r
87     mungestr(sessionname, p);\r
88 \r
89     ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);\r
90     if (ret != ERROR_SUCCESS) {\r
91         sfree(p);\r
92         *errmsg = dupprintf("Unable to create registry key\n"\r
93                             "HKEY_CURRENT_USER\\%s", puttystr);\r
94         return NULL;\r
95     }\r
96     ret = RegCreateKey(subkey1, p, &sesskey);\r
97     RegCloseKey(subkey1);\r
98     if (ret != ERROR_SUCCESS) {\r
99         *errmsg = dupprintf("Unable to create registry key\n"\r
100                             "HKEY_CURRENT_USER\\%s\\%s", puttystr, p);\r
101         sfree(p);\r
102         return NULL;\r
103     }\r
104     sfree(p);\r
105     return (void *) sesskey;\r
106 }\r
107 \r
108 void write_setting_s(void *handle, const char *key, const char *value)\r
109 {\r
110     if (handle)\r
111         RegSetValueEx((HKEY) handle, key, 0, REG_SZ, value,\r
112                       1 + strlen(value));\r
113 }\r
114 \r
115 void write_setting_i(void *handle, const char *key, int value)\r
116 {\r
117     if (handle)\r
118         RegSetValueEx((HKEY) handle, key, 0, REG_DWORD,\r
119                       (CONST BYTE *) &value, sizeof(value));\r
120 }\r
121 \r
122 void close_settings_w(void *handle)\r
123 {\r
124     RegCloseKey((HKEY) handle);\r
125 }\r
126 \r
127 void *open_settings_r(const char *sessionname)\r
128 {\r
129     HKEY subkey1, sesskey;\r
130     char *p;\r
131 \r
132     if (!sessionname || !*sessionname)\r
133         sessionname = "Default Settings";\r
134 \r
135     p = snewn(3 * strlen(sessionname) + 1, char);\r
136     mungestr(sessionname, p);\r
137 \r
138     if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {\r
139         sesskey = NULL;\r
140     } else {\r
141         if (RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {\r
142             sesskey = NULL;\r
143         }\r
144         RegCloseKey(subkey1);\r
145     }\r
146 \r
147     sfree(p);\r
148 \r
149     return (void *) sesskey;\r
150 }\r
151 \r
152 char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)\r
153 {\r
154     DWORD type, size;\r
155     size = buflen;\r
156 \r
157     if (!handle ||\r
158         RegQueryValueEx((HKEY) handle, key, 0,\r
159                         &type, buffer, &size) != ERROR_SUCCESS ||\r
160         type != REG_SZ) return NULL;\r
161     else\r
162         return buffer;\r
163 }\r
164 \r
165 int read_setting_i(void *handle, const char *key, int defvalue)\r
166 {\r
167     DWORD type, val, size;\r
168     size = sizeof(val);\r
169 \r
170     if (!handle ||\r
171         RegQueryValueEx((HKEY) handle, key, 0, &type,\r
172                         (BYTE *) &val, &size) != ERROR_SUCCESS ||\r
173         size != sizeof(val) || type != REG_DWORD)\r
174         return defvalue;\r
175     else\r
176         return val;\r
177 }\r
178 \r
179 int read_setting_fontspec(void *handle, const char *name, FontSpec *result)\r
180 {\r
181     char *settingname;\r
182     FontSpec ret;\r
183 \r
184     if (!read_setting_s(handle, name, ret.name, sizeof(ret.name)))\r
185         return 0;\r
186     settingname = dupcat(name, "IsBold", NULL);\r
187     ret.isbold = read_setting_i(handle, settingname, -1);\r
188     sfree(settingname);\r
189     if (ret.isbold == -1) return 0;\r
190     settingname = dupcat(name, "CharSet", NULL);\r
191     ret.charset = read_setting_i(handle, settingname, -1);\r
192     sfree(settingname);\r
193     if (ret.charset == -1) return 0;\r
194     settingname = dupcat(name, "Height", NULL);\r
195     ret.height = read_setting_i(handle, settingname, INT_MIN);\r
196     sfree(settingname);\r
197     if (ret.height == INT_MIN) return 0;\r
198     *result = ret;\r
199     return 1;\r
200 }\r
201 \r
202 void write_setting_fontspec(void *handle, const char *name, FontSpec font)\r
203 {\r
204     char *settingname;\r
205 \r
206     write_setting_s(handle, name, font.name);\r
207     settingname = dupcat(name, "IsBold", NULL);\r
208     write_setting_i(handle, settingname, font.isbold);\r
209     sfree(settingname);\r
210     settingname = dupcat(name, "CharSet", NULL);\r
211     write_setting_i(handle, settingname, font.charset);\r
212     sfree(settingname);\r
213     settingname = dupcat(name, "Height", NULL);\r
214     write_setting_i(handle, settingname, font.height);\r
215     sfree(settingname);\r
216 }\r
217 \r
218 int read_setting_filename(void *handle, const char *name, Filename *result)\r
219 {\r
220     return !!read_setting_s(handle, name, result->path, sizeof(result->path));\r
221 }\r
222 \r
223 void write_setting_filename(void *handle, const char *name, Filename result)\r
224 {\r
225     write_setting_s(handle, name, result.path);\r
226 }\r
227 \r
228 void close_settings_r(void *handle)\r
229 {\r
230     RegCloseKey((HKEY) handle);\r
231 }\r
232 \r
233 void del_settings(const char *sessionname)\r
234 {\r
235     HKEY subkey1;\r
236     char *p;\r
237 \r
238     if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)\r
239         return;\r
240 \r
241     p = snewn(3 * strlen(sessionname) + 1, char);\r
242     mungestr(sessionname, p);\r
243     RegDeleteKey(subkey1, p);\r
244     sfree(p);\r
245 \r
246     RegCloseKey(subkey1);\r
247 }\r
248 \r
249 struct enumsettings {\r
250     HKEY key;\r
251     int i;\r
252 };\r
253 \r
254 void *enum_settings_start(void)\r
255 {\r
256     struct enumsettings *ret;\r
257     HKEY key;\r
258 \r
259     if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)\r
260         return NULL;\r
261 \r
262     ret = snew(struct enumsettings);\r
263     if (ret) {\r
264         ret->key = key;\r
265         ret->i = 0;\r
266     }\r
267 \r
268     return ret;\r
269 }\r
270 \r
271 char *enum_settings_next(void *handle, char *buffer, int buflen)\r
272 {\r
273     struct enumsettings *e = (struct enumsettings *) handle;\r
274     char *otherbuf;\r
275     otherbuf = snewn(3 * buflen, char);\r
276     if (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) {\r
277         unmungestr(otherbuf, buffer, buflen);\r
278         sfree(otherbuf);\r
279         return buffer;\r
280     } else {\r
281         sfree(otherbuf);\r
282         return NULL;\r
283     }\r
284 }\r
285 \r
286 void enum_settings_finish(void *handle)\r
287 {\r
288     struct enumsettings *e = (struct enumsettings *) handle;\r
289     RegCloseKey(e->key);\r
290     sfree(e);\r
291 }\r
292 \r
293 static void hostkey_regname(char *buffer, const char *hostname,\r
294                             int port, const char *keytype)\r
295 {\r
296     int len;\r
297     strcpy(buffer, keytype);\r
298     strcat(buffer, "@");\r
299     len = strlen(buffer);\r
300     len += sprintf(buffer + len, "%d:", port);\r
301     mungestr(hostname, buffer + strlen(buffer));\r
302 }\r
303 \r
304 int verify_host_key(const char *hostname, int port,\r
305                     const char *keytype, const char *key)\r
306 {\r
307     char *otherstr, *regname;\r
308     int len;\r
309     HKEY rkey;\r
310     DWORD readlen;\r
311     DWORD type;\r
312     int ret, compare;\r
313 \r
314     len = 1 + strlen(key);\r
315 \r
316     /*\r
317      * Now read a saved key in from the registry and see what it\r
318      * says.\r
319      */\r
320     otherstr = snewn(len, char);\r
321     regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);\r
322 \r
323     hostkey_regname(regname, hostname, port, keytype);\r
324 \r
325     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",\r
326                    &rkey) != ERROR_SUCCESS)\r
327         return 1;                      /* key does not exist in registry */\r
328 \r
329     readlen = len;\r
330     ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen);\r
331 \r
332     if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&\r
333         !strcmp(keytype, "rsa")) {\r
334         /*\r
335          * Key didn't exist. If the key type is RSA, we'll try\r
336          * another trick, which is to look up the _old_ key format\r
337          * under just the hostname and translate that.\r
338          */\r
339         char *justhost = regname + 1 + strcspn(regname, ":");\r
340         char *oldstyle = snewn(len + 10, char); /* safety margin */\r
341         readlen = len;\r
342         ret = RegQueryValueEx(rkey, justhost, NULL, &type,\r
343                               oldstyle, &readlen);\r
344 \r
345         if (ret == ERROR_SUCCESS && type == REG_SZ) {\r
346             /*\r
347              * The old format is two old-style bignums separated by\r
348              * a slash. An old-style bignum is made of groups of\r
349              * four hex digits: digits are ordered in sensible\r
350              * (most to least significant) order within each group,\r
351              * but groups are ordered in silly (least to most)\r
352              * order within the bignum. The new format is two\r
353              * ordinary C-format hex numbers (0xABCDEFG...XYZ, with\r
354              * A nonzero except in the special case 0x0, which\r
355              * doesn't appear anyway in RSA keys) separated by a\r
356              * comma. All hex digits are lowercase in both formats.\r
357              */\r
358             char *p = otherstr;\r
359             char *q = oldstyle;\r
360             int i, j;\r
361 \r
362             for (i = 0; i < 2; i++) {\r
363                 int ndigits, nwords;\r
364                 *p++ = '0';\r
365                 *p++ = 'x';\r
366                 ndigits = strcspn(q, "/");      /* find / or end of string */\r
367                 nwords = ndigits / 4;\r
368                 /* now trim ndigits to remove leading zeros */\r
369                 while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)\r
370                     ndigits--;\r
371                 /* now move digits over to new string */\r
372                 for (j = 0; j < ndigits; j++)\r
373                     p[ndigits - 1 - j] = q[j ^ 3];\r
374                 p += ndigits;\r
375                 q += nwords * 4;\r
376                 if (*q) {\r
377                     q++;               /* eat the slash */\r
378                     *p++ = ',';        /* add a comma */\r
379                 }\r
380                 *p = '\0';             /* terminate the string */\r
381             }\r
382 \r
383             /*\r
384              * Now _if_ this key matches, we'll enter it in the new\r
385              * format. If not, we'll assume something odd went\r
386              * wrong, and hyper-cautiously do nothing.\r
387              */\r
388             if (!strcmp(otherstr, key))\r
389                 RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr,\r
390                               strlen(otherstr) + 1);\r
391         }\r
392     }\r
393 \r
394     RegCloseKey(rkey);\r
395 \r
396     compare = strcmp(otherstr, key);\r
397 \r
398     sfree(otherstr);\r
399     sfree(regname);\r
400 \r
401     if (ret == ERROR_MORE_DATA ||\r
402         (ret == ERROR_SUCCESS && type == REG_SZ && compare))\r
403         return 2;                      /* key is different in registry */\r
404     else if (ret != ERROR_SUCCESS || type != REG_SZ)\r
405         return 1;                      /* key does not exist in registry */\r
406     else\r
407         return 0;                      /* key matched OK in registry */\r
408 }\r
409 \r
410 void store_host_key(const char *hostname, int port,\r
411                     const char *keytype, const char *key)\r
412 {\r
413     char *regname;\r
414     HKEY rkey;\r
415 \r
416     regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);\r
417 \r
418     hostkey_regname(regname, hostname, port, keytype);\r
419 \r
420     if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",\r
421                      &rkey) == ERROR_SUCCESS) {\r
422         RegSetValueEx(rkey, regname, 0, REG_SZ, key, strlen(key) + 1);\r
423         RegCloseKey(rkey);\r
424     } /* else key does not exist in registry */\r
425 \r
426     sfree(regname);\r
427 }\r
428 \r
429 /*\r
430  * Open (or delete) the random seed file.\r
431  */\r
432 enum { DEL, OPEN_R, OPEN_W };\r
433 static int try_random_seed(char const *path, int action, HANDLE *ret)\r
434 {\r
435     if (action == DEL) {\r
436         remove(path);\r
437         *ret = INVALID_HANDLE_VALUE;\r
438         return FALSE;                  /* so we'll do the next ones too */\r
439     }\r
440 \r
441     *ret = CreateFile(path,\r
442                       action == OPEN_W ? GENERIC_WRITE : GENERIC_READ,\r
443                       action == OPEN_W ? 0 : (FILE_SHARE_READ |\r
444                                               FILE_SHARE_WRITE),\r
445                       NULL,\r
446                       action == OPEN_W ? CREATE_ALWAYS : OPEN_EXISTING,\r
447                       action == OPEN_W ? FILE_ATTRIBUTE_NORMAL : 0,\r
448                       NULL);\r
449 \r
450     return (*ret != INVALID_HANDLE_VALUE);\r
451 }\r
452 \r
453 static HANDLE access_random_seed(int action)\r
454 {\r
455     HKEY rkey;\r
456     DWORD type, size;\r
457     HANDLE rethandle;\r
458     char seedpath[2 * MAX_PATH + 10] = "\0";\r
459 \r
460     /*\r
461      * Iterate over a selection of possible random seed paths until\r
462      * we find one that works.\r
463      * \r
464      * We do this iteration separately for reading and writing,\r
465      * meaning that we will automatically migrate random seed files\r
466      * if a better location becomes available (by reading from the\r
467      * best location in which we actually find one, and then\r
468      * writing to the best location in which we can _create_ one).\r
469      */\r
470 \r
471     /*\r
472      * First, try the location specified by the user in the\r
473      * Registry, if any.\r
474      */\r
475     size = sizeof(seedpath);\r
476     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==\r
477         ERROR_SUCCESS) {\r
478         int ret = RegQueryValueEx(rkey, "RandSeedFile",\r
479                                   0, &type, seedpath, &size);\r
480         if (ret != ERROR_SUCCESS || type != REG_SZ)\r
481             seedpath[0] = '\0';\r
482         RegCloseKey(rkey);\r
483 \r
484         if (*seedpath && try_random_seed(seedpath, action, &rethandle))\r
485             return rethandle;\r
486     }\r
487 \r
488     /*\r
489      * Next, try the user's local Application Data directory,\r
490      * followed by their non-local one. This is found using the\r
491      * SHGetFolderPath function, which won't be present on all\r
492      * versions of Windows.\r
493      */\r
494     if (!tried_shgetfolderpath) {\r
495         /* This is likely only to bear fruit on systems with IE5+\r
496          * installed, or WinMe/2K+. There is some faffing with\r
497          * SHFOLDER.DLL we could do to try to find an equivalent\r
498          * on older versions of Windows if we cared enough.\r
499          * However, the invocation below requires IE5+ anyway,\r
500          * so stuff that. */\r
501         shell32_module = LoadLibrary("SHELL32.DLL");\r
502         if (shell32_module) {\r
503             p_SHGetFolderPath = (p_SHGetFolderPath_t)\r
504                 GetProcAddress(shell32_module, "SHGetFolderPathA");\r
505         }\r
506     }\r
507     if (p_SHGetFolderPath) {\r
508         if (SUCCEEDED(p_SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA,\r
509                                         NULL, SHGFP_TYPE_CURRENT, seedpath))) {\r
510             strcat(seedpath, "\\PUTTY.RND");\r
511             if (try_random_seed(seedpath, action, &rethandle))\r
512                 return rethandle;\r
513         }\r
514 \r
515         if (SUCCEEDED(p_SHGetFolderPath(NULL, CSIDL_APPDATA,\r
516                                         NULL, SHGFP_TYPE_CURRENT, seedpath))) {\r
517             strcat(seedpath, "\\PUTTY.RND");\r
518             if (try_random_seed(seedpath, action, &rethandle))\r
519                 return rethandle;\r
520         }\r
521     }\r
522 \r
523     /*\r
524      * Failing that, try %HOMEDRIVE%%HOMEPATH% as a guess at the\r
525      * user's home directory.\r
526      */\r
527     {\r
528         int len, ret;\r
529 \r
530         len =\r
531             GetEnvironmentVariable("HOMEDRIVE", seedpath,\r
532                                    sizeof(seedpath));\r
533         ret =\r
534             GetEnvironmentVariable("HOMEPATH", seedpath + len,\r
535                                    sizeof(seedpath) - len);\r
536         if (ret != 0) {\r
537             strcat(seedpath, "\\PUTTY.RND");\r
538             if (try_random_seed(seedpath, action, &rethandle))\r
539                 return rethandle;\r
540         }\r
541     }\r
542 \r
543     /*\r
544      * And finally, fall back to C:\WINDOWS.\r
545      */\r
546     GetWindowsDirectory(seedpath, sizeof(seedpath));\r
547     strcat(seedpath, "\\PUTTY.RND");\r
548     if (try_random_seed(seedpath, action, &rethandle))\r
549         return rethandle;\r
550 \r
551     /*\r
552      * If even that failed, give up.\r
553      */\r
554     return INVALID_HANDLE_VALUE;\r
555 }\r
556 \r
557 void read_random_seed(noise_consumer_t consumer)\r
558 {\r
559     HANDLE seedf = access_random_seed(OPEN_R);\r
560 \r
561     if (seedf != INVALID_HANDLE_VALUE) {\r
562         while (1) {\r
563             char buf[1024];\r
564             DWORD len;\r
565 \r
566             if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)\r
567                 consumer(buf, len);\r
568             else\r
569                 break;\r
570         }\r
571         CloseHandle(seedf);\r
572     }\r
573 }\r
574 \r
575 void write_random_seed(void *data, int len)\r
576 {\r
577     HANDLE seedf = access_random_seed(OPEN_W);\r
578 \r
579     if (seedf != INVALID_HANDLE_VALUE) {\r
580         DWORD lenwritten;\r
581 \r
582         WriteFile(seedf, data, len, &lenwritten, NULL);\r
583         CloseHandle(seedf);\r
584     }\r
585 }\r
586 \r
587 /*\r
588  * Recursively delete a registry key and everything under it.\r
589  */\r
590 static void registry_recursive_remove(HKEY key)\r
591 {\r
592     DWORD i;\r
593     char name[MAX_PATH + 1];\r
594     HKEY subkey;\r
595 \r
596     i = 0;\r
597     while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {\r
598         if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {\r
599             registry_recursive_remove(subkey);\r
600             RegCloseKey(subkey);\r
601         }\r
602         RegDeleteKey(key, name);\r
603     }\r
604 }\r
605 \r
606 void cleanup_all(void)\r
607 {\r
608     HKEY key;\r
609     int ret;\r
610     char name[MAX_PATH + 1];\r
611 \r
612     /* ------------------------------------------------------------\r
613      * Wipe out the random seed file, in all of its possible\r
614      * locations.\r
615      */\r
616     access_random_seed(DEL);\r
617 \r
618     /* ------------------------------------------------------------\r
619      * Destroy all registry information associated with PuTTY.\r
620      */\r
621 \r
622     /*\r
623      * Open the main PuTTY registry key and remove everything in it.\r
624      */\r
625     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==\r
626         ERROR_SUCCESS) {\r
627         registry_recursive_remove(key);\r
628         RegCloseKey(key);\r
629     }\r
630     /*\r
631      * Now open the parent key and remove the PuTTY main key. Once\r
632      * we've done that, see if the parent key has any other\r
633      * children.\r
634      */\r
635     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,\r
636                    &key) == ERROR_SUCCESS) {\r
637         RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);\r
638         ret = RegEnumKey(key, 0, name, sizeof(name));\r
639         RegCloseKey(key);\r
640         /*\r
641          * If the parent key had no other children, we must delete\r
642          * it in its turn. That means opening the _grandparent_\r
643          * key.\r
644          */\r
645         if (ret != ERROR_SUCCESS) {\r
646             if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,\r
647                            &key) == ERROR_SUCCESS) {\r
648                 RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);\r
649                 RegCloseKey(key);\r
650             }\r
651         }\r
652     }\r
653     /*\r
654      * Now we're done.\r
655      */\r
656 }\r