]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/macstore.c
Cleanups of the GSSAPI support. On Windows, standard GSS libraries
[PuTTY.git] / mac / macstore.c
1 /* $Id$ */
2
3 /*
4  * macstore.c: Macintosh-specific impementation of the interface
5  * defined in storage.h
6  */
7
8 #include <MacTypes.h>
9 #include <Folders.h>
10 #include <Memory.h>
11 #include <Resources.h>
12 #include <TextUtils.h>
13
14 #include <stdio.h>
15 #include <string.h>
16
17 #include "putty.h"
18 #include "storage.h"
19 #include "mac.h"
20 #include "macresid.h"
21
22
23 OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit);
24
25 /*
26  * We store each session as a file in the "PuTTY" sub-directory of the
27  * preferences folder.  Each (key,value) pair is stored as a resource.
28  */
29
30 OSErr get_putty_dir(Boolean makeit, short *pVRefNum, long *pDirID)
31 {
32     OSErr error = noErr;
33     short prefVRefNum;
34     FSSpec puttydir;
35     long prefDirID, puttyDirID;
36
37     error = FindFolder(kOnSystemDisk, kPreferencesFolderType, makeit,
38                        &prefVRefNum, &prefDirID);
39     if (error != noErr) goto out;
40
41     error = FSMakeFSSpec(prefVRefNum, prefDirID, "\pPuTTY", &puttydir);
42     if (error != noErr && error != fnfErr) goto out;
43     error = FSpGetDirID(&puttydir, &puttyDirID, makeit);
44     if (error != noErr) goto out;
45
46     *pVRefNum = prefVRefNum;
47     *pDirID = puttyDirID;
48
49   out:
50     return error;
51 }
52
53 OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) {
54     OSErr error = noErr;
55     short puttyVRefNum;
56     FSSpec sessdir;
57     long puttyDirID, sessDirID;
58
59     error = get_putty_dir(makeit, &puttyVRefNum, &puttyDirID);
60     if (error != noErr) goto out;
61     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSaved Sessions",
62                          &sessdir);
63     if (error != noErr && error != fnfErr) goto out;
64     error = FSpGetDirID(&sessdir, &sessDirID, makeit);
65     if (error != noErr) goto out;
66
67     *pVRefNum = puttyVRefNum;
68     *pDirID = sessDirID;
69
70   out:
71     return error;
72 }
73
74 OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit) {
75     CInfoPBRec pb;
76     OSErr error = noErr;
77
78     pb.dirInfo.ioNamePtr = f->name;
79     pb.dirInfo.ioVRefNum = f->vRefNum;
80     pb.dirInfo.ioDrDirID = f->parID;
81     pb.dirInfo.ioFDirIndex = 0;
82     error = PBGetCatInfoSync(&pb);
83     if (error == fnfErr && makeit)
84         return FSpDirCreate(f, smSystemScript, idp);
85     if (error != noErr) goto out;
86     if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) {
87         error = dirNFErr;
88         goto out;
89     }
90     *idp = pb.dirInfo.ioDrDirID;
91
92   out:
93     return error;
94 }
95
96 /* Copy a resource into the current resource file */
97 static OSErr copy_resource(ResType restype, short resid)
98 {
99     Handle h;
100     Str255 resname;
101
102     h = GetResource(restype, resid);
103     if (h != NULL) {
104         GetResInfo(h, &resid, &restype, resname);
105         DetachResource(h);
106         AddResource(h, restype, resid, resname);
107         if (ResError() == noErr)
108             WriteResource(h);
109     }
110     return ResError();
111 }
112
113 struct write_settings {
114     int fd;
115     FSSpec tmpfile;
116     FSSpec dstfile;
117 };
118
119 void *open_settings_w(char const *sessionname, char **errmsg) {
120     short sessVRefNum;
121     long sessDirID;
122     OSErr error;
123     Str255 psessionname;
124     FSSpec dstfile;
125
126     *errmsg = NULL;
127
128     error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
129     if (error != noErr) return NULL;
130
131     if (!sessionname || !*sessionname)
132         sessionname = "Default Settings";
133     c2pstrcpy(psessionname, sessionname);
134     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &dstfile);
135     if (error == fnfErr) {
136         FSpCreateResFile(&dstfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
137         if ((error = ResError()) != noErr) return NULL;
138     } else if (error != noErr) return NULL;
139
140     return open_settings_w_fsp(&dstfile);
141 }
142
143 /*
144  * NB: Destination file must exist.
145  */
146 void *open_settings_w_fsp(FSSpec *dstfile)
147 {
148     short tmpVRefNum;
149     long tmpDirID;
150     struct write_settings *ws;
151     OSErr error;
152     Str255 tmpname;
153
154     ws = snew(struct write_settings);
155     ws->dstfile = *dstfile;
156
157     /* Create a temporary file to save to first. */
158     error = FindFolder(ws->dstfile.vRefNum, kTemporaryFolderType,
159                        kCreateFolder, &tmpVRefNum, &tmpDirID);
160     if (error != noErr) goto out;
161     c2pstrcpy(tmpname, tmpnam(NULL));
162     error = FSMakeFSSpec(tmpVRefNum, tmpDirID, tmpname, &ws->tmpfile);
163     if (error != noErr && error != fnfErr) goto out;
164     if (error == noErr) {
165         error = FSpDelete(&ws->tmpfile);
166         if (error != noErr) goto out;
167     }
168     FSpCreateResFile(&ws->tmpfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
169     if ((error = ResError()) != noErr) goto out;
170
171     ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm);
172     if (ws->fd == -1) {error = ResError(); goto out;}
173
174     /* Set up standard resources.  Doesn't matter if these fail. */
175     copy_resource('STR ', -16396);
176     copy_resource('TMPL', TMPL_Int);
177
178     return ws;
179
180   out:
181     safefree(ws);
182     fatalbox("Failed to open session for write (%d)", error);
183 }
184
185 void write_setting_s(void *handle, char const *key, char const *value) {
186     int fd = *(int *)handle;
187     Handle h;
188     int id;
189     OSErr error;
190     Str255 pkey;
191
192     UseResFile(fd);
193     if (ResError() != noErr)
194         fatalbox("Failed to open saved session (%d)", ResError());
195
196     error = PtrToHand(value, &h, strlen(value));
197     if (error != noErr)
198         fatalbox("Failed to allocate memory");
199     /* Put the data in a resource. */
200     id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
201     if (ResError() != noErr)
202         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
203     c2pstrcpy(pkey, key);
204     AddResource(h, FOUR_CHAR_CODE('TEXT'), id, pkey);
205     if (ResError() != noErr)
206         fatalbox("Failed to add resource %s (%d)", key, ResError());
207 }
208
209 void write_setting_i(void *handle, char const *key, int value) {
210     int fd = *(int *)handle;
211     Handle h;
212     int id;
213     OSErr error;
214     Str255 pkey;
215
216     UseResFile(fd);
217     if (ResError() != noErr)
218         fatalbox("Failed to open saved session (%d)", ResError());
219
220     /* XXX assume all systems have the same "int" format */
221     error = PtrToHand(&value, &h, sizeof(int));
222     if (error != noErr)
223         fatalbox("Failed to allocate memory (%d)", error);
224
225     /* Put the data in a resource. */
226     id = Unique1ID(FOUR_CHAR_CODE('Int '));
227     if (ResError() != noErr)
228         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
229     c2pstrcpy(pkey, key);
230     AddResource(h, FOUR_CHAR_CODE('Int '), id, pkey);
231     if (ResError() != noErr)
232         fatalbox("Failed to add resource %s (%d)", key, ResError());
233 }
234
235 void close_settings_w(void *handle) {
236     struct write_settings *ws = handle;
237     OSErr error;
238
239     CloseResFile(ws->fd);
240     if ((error = ResError()) != noErr)
241         goto out;
242     error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile);
243     if (error != noErr) goto out;
244     error = FSpDelete(&ws->tmpfile);
245     if (error != noErr) goto out;
246     return;
247
248   out:
249     fatalbox("Close of saved session failed (%d)", error);
250     safefree(handle);
251 }
252
253 void *open_settings_r(char const *sessionname)
254 {
255     short sessVRefNum;
256     long sessDirID;
257     FSSpec sessfile;
258     OSErr error;
259     Str255 psessionname;
260
261     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
262
263     if (!sessionname || !*sessionname)
264         sessionname = "Default Settings";
265     c2pstrcpy(psessionname, sessionname);
266     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
267     if (error != noErr) goto out;
268     return open_settings_r_fsp(&sessfile);
269
270   out:
271     return NULL;
272 }
273
274 void *open_settings_r_fsp(FSSpec *sessfile)
275 {
276     OSErr error;
277     int fd;
278     int *handle;
279
280     fd = FSpOpenResFile(sessfile, fsRdPerm);
281     if (fd == 0) {error = ResError(); goto out;}
282
283     handle = snew(int);
284     *handle = fd;
285     return handle;
286
287   out:
288     return NULL;
289 }
290
291 char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
292     int fd;
293     Handle h;
294     size_t len;
295     Str255 pkey;
296
297     if (handle == NULL) goto out;
298     fd = *(int *)handle;
299     UseResFile(fd);
300     if (ResError() != noErr) goto out;
301     c2pstrcpy(pkey, key);
302     h = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), pkey);
303     if (h == NULL) goto out;
304
305     len = GetHandleSize(h);
306     if (len + 1 > buflen) goto out;
307     memcpy(buffer, *h, len);
308     buffer[len] = '\0';
309
310     ReleaseResource(h);
311     if (ResError() != noErr) goto out;
312     return buffer;
313
314   out:
315     return NULL;
316 }
317
318 int read_setting_i(void *handle, char const *key, int defvalue) {
319     int fd;
320     Handle h;
321     int value;
322     Str255 pkey;
323
324     if (handle == NULL) goto out;
325     fd = *(int *)handle;
326     UseResFile(fd);
327     if (ResError() != noErr) goto out;
328     c2pstrcpy(pkey, key);
329     h = Get1NamedResource(FOUR_CHAR_CODE('Int '), pkey);
330     if (h == NULL) goto out;
331     value = *(int *)*h;
332     ReleaseResource(h);
333     if (ResError() != noErr) goto out;
334     return value;
335
336   out:
337     return defvalue;
338 }
339
340 int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
341 {
342     char *settingname;
343     FontSpec ret;
344     char tmp[256];
345
346     if (!read_setting_s(handle, name, tmp, sizeof(tmp)))
347         return 0;
348     c2pstrcpy(ret.name, tmp);
349     settingname = dupcat(name, "Face", NULL);
350     ret.face = read_setting_i(handle, settingname, 0);
351     sfree(settingname);
352     settingname = dupcat(name, "Height", NULL);
353     ret.size = read_setting_i(handle, settingname, 0);
354     sfree(settingname);
355     if (ret.size == 0) return 0;
356     *result = ret;
357     return 1;
358 }
359
360 void write_setting_fontspec(void *handle, const char *name, FontSpec font)
361 {
362     char *settingname;
363     char tmp[256];
364
365     p2cstrcpy(tmp, font.name);
366     write_setting_s(handle, name, tmp);
367     settingname = dupcat(name, "Face", NULL);
368     write_setting_i(handle, settingname, font.face);
369     sfree(settingname);
370     settingname = dupcat(name, "Size", NULL);
371     write_setting_i(handle, settingname, font.size);
372     sfree(settingname);
373 }
374
375 int read_setting_filename(void *handle, const char *key, Filename *result)
376 {
377     int fd;
378     AliasHandle h;
379     Boolean changed;
380     OSErr err;
381     Str255 pkey;
382
383     if (handle == NULL) goto out;
384     fd = *(int *)handle;
385     UseResFile(fd);
386     if (ResError() != noErr) goto out;
387     c2pstrcpy(pkey, key);
388     h = (AliasHandle)Get1NamedResource(rAliasType, pkey);
389     if (h == NULL) goto out;
390     if ((*h)->userType == 'pTTY' && (*h)->aliasSize == sizeof(**h))
391         memset(result, 0, sizeof(*result));
392     else {
393         err = ResolveAlias(NULL, h, &result->fss, &changed);
394         if (err != noErr && err != fnfErr) goto out;
395         if ((*h)->userType == 'pTTY') {
396             long dirid;
397             StrFileName fname;
398
399             /* Tail of record is pascal string contaning leafname */
400             if (FSpGetDirID(&result->fss, &dirid, FALSE) != noErr) goto out;
401             memcpy(fname, (char *)*h + (*h)->aliasSize,
402                    GetHandleSize((Handle)h) - (*h)->aliasSize);
403             err = FSMakeFSSpec(result->fss.vRefNum, dirid, fname,
404                                &result->fss);
405             if (err != noErr && err != fnfErr) goto out;
406         }
407     }
408     ReleaseResource((Handle)h);
409     if (ResError() != noErr) goto out;
410     return 1;
411
412   out:
413     return 0;
414 }
415
416 void write_setting_filename(void *handle, const char *key, Filename fn)
417 {
418     int fd = *(int *)handle;
419     AliasHandle h;
420     int id;
421     OSErr error;
422     Str255 pkey;
423
424     UseResFile(fd);
425     if (ResError() != noErr)
426         fatalbox("Failed to open saved session (%d)", ResError());
427
428     if (filename_is_null(fn)) {
429         /* Generate a special "null" alias */
430         h = (AliasHandle)NewHandle(sizeof(**h));
431         if (h == NULL)
432             fatalbox("Failed to create fake alias");
433         (*h)->userType = 'pTTY';
434         (*h)->aliasSize = sizeof(**h);
435     } else {
436         error = NewAlias(NULL, &fn.fss, &h);
437         if (error == fnfErr) {
438             /*
439              * NewAlias can't create an alias for a nonexistent file.
440              * Create an alias for the directory, and record the
441              * filename as well.
442              */
443             FSSpec tmpfss;
444
445             FSMakeFSSpec(fn.fss.vRefNum, fn.fss.parID, NULL, &tmpfss);
446             error = NewAlias(NULL, &tmpfss, &h);
447             if (error != noErr)
448                 fatalbox("Failed to create alias");
449             (*h)->userType = 'pTTY';
450             SetHandleSize((Handle)h, (*h)->aliasSize + fn.fss.name[0] + 1);
451             if (MemError() != noErr)
452                 fatalbox("Failed to create alias");
453             memcpy((char *)*h + (*h)->aliasSize, fn.fss.name,
454                    fn.fss.name[0] + 1);
455         }
456         if (error != noErr)
457             fatalbox("Failed to create alias");
458     }
459     /* Put the data in a resource. */
460     id = Unique1ID(rAliasType);
461     if (ResError() != noErr)
462         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
463     c2pstrcpy(pkey, key);
464     AddResource((Handle)h, rAliasType, id, pkey);
465     if (ResError() != noErr)
466         fatalbox("Failed to add resource %s (%d)", key, ResError());
467 }
468
469 void close_settings_r(void *handle) {
470     int fd;
471
472     if (handle == NULL) return;
473     fd = *(int *)handle;
474     CloseResFile(fd);
475     if (ResError() != noErr)
476         fatalbox("Close of saved session failed (%d)", ResError());
477     sfree(handle);
478 }
479
480 void del_settings(char const *sessionname) {
481     OSErr error;
482     FSSpec sessfile;
483     short sessVRefNum;
484     long sessDirID;
485     Str255 psessionname;
486
487     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
488
489     c2pstrcpy(psessionname, sessionname);
490     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
491     if (error != noErr) goto out;
492
493     error = FSpDelete(&sessfile);
494     return;
495   out:
496     fatalbox("Delete session failed (%d)", error);
497 }
498
499 struct enum_settings_state {
500     short vRefNum;
501     long dirID;
502     int index;
503 };
504
505 void *enum_settings_start(void) {
506     OSErr error;
507     struct enum_settings_state *state;
508
509     state = snew(struct enum_settings_state);
510     error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID);
511     if (error != noErr) {
512         sfree(state);
513         return NULL;
514     }
515     state->index = 1;
516     return state;
517 }
518
519 char *enum_settings_next(void *handle, char *buffer, int buflen) {
520     struct enum_settings_state *e = handle;
521     CInfoPBRec pb;
522     OSErr error = noErr;
523     Str255 name;
524
525     if (e == NULL) return NULL;
526     do {
527         pb.hFileInfo.ioNamePtr = name;
528         pb.hFileInfo.ioVRefNum = e->vRefNum;
529         pb.hFileInfo.ioDirID = e->dirID;
530         pb.hFileInfo.ioFDirIndex = e->index++;
531         error = PBGetCatInfoSync(&pb);
532         if (error != noErr) return NULL;
533     } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 &&
534                pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR &&
535                pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE &&
536                name[0] < buflen));
537
538     p2cstrcpy(buffer, name);
539     return buffer;
540 }
541
542 void enum_settings_finish(void *handle) {
543
544     safefree(handle);
545 }
546
547 #define SEED_SIZE 512
548
549 void read_random_seed(noise_consumer_t consumer)
550 {
551     short puttyVRefNum;
552     long puttyDirID;
553     OSErr error;
554     char buf[SEED_SIZE];
555     short refnum;
556     long count = SEED_SIZE;
557
558     if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
559         return;
560     if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm,
561                 &refnum) != noErr)
562         return;
563     error = FSRead(refnum, &count, buf);
564     if (error != noErr && error != eofErr)
565         return;
566     (*consumer)(buf, count);
567     FSClose(refnum);
568 }
569
570 /*
571  * We don't bother with the usual FSpExchangeFiles dance here because
572  * it doesn't really matter if the old random seed gets lost.
573  */
574 void write_random_seed(void *data, int len)
575 {
576     short puttyVRefNum;
577     long puttyDirID;
578     OSErr error;
579     FSSpec dstfile;
580     short refnum;
581     long count = len;
582
583     if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
584         return;
585
586     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed",
587                          &dstfile);
588     if (error == fnfErr) {
589         /* Set up standard resources */
590         FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman);
591         refnum = FSpOpenResFile(&dstfile, fsWrPerm);
592         if (ResError() == noErr) {
593             copy_resource('STR ', -16397);
594             CloseResFile(refnum);
595         }
596     } else if (error != noErr) return;
597
598     if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
599     FSWrite(refnum, &count, data);
600     FSClose(refnum);
601
602     return;
603 }
604
605 /*
606  * This host key cache uses a file in the PuTTY Preferences folder and
607  * stores keys as individual TEXT resources in the resource fork of
608  * that file.  This has two problems.  Firstly, a resource fork can
609  * contain no more than 2727 resources.  Secondly, the Resource
610  * Manager uses a linear search to find a particular resource, which
611  * could make having lots of host keys quite slow.
612  */
613
614 int verify_host_key(const char *hostname, int port,
615                     const char *keytype, const char *key)
616 {
617     short puttyVRefNum;
618     long puttyDirID;
619     OSErr error;
620     FSSpec keyfile;
621     short refnum;
622     char *resname;
623     Str255 presname;
624     char *resvalue;
625     Handle reshandle;
626     int len, compare;
627
628     if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
629         return 1;
630
631     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSSH Host Keys",
632                          &keyfile);
633     if (error == fnfErr) {
634         /* Keys file doesn't exist yet, so we can't match the key */
635         return 1;
636     }
637
638     refnum = FSpOpenResFile(&keyfile, fsRdPerm);
639
640     if (refnum == -1) {
641         /* We couldn't open the resource fork, so we can't match the key */
642         return 1;
643     }
644
645     UseResFile(refnum);
646
647     resname = dupprintf("%s@%d:%s", keytype, port, hostname);
648     c2pstrcpy(presname, resname);
649     reshandle = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), presname);
650     if (ResError() != noErr) {
651         /* Couldn't open the specific resource */
652         return 1;
653     }
654
655     len = GetHandleSize(reshandle);
656     resvalue = snewn(len+1, char);
657     memcpy(resvalue, *reshandle, len);
658     resvalue[len]='\0';
659     ReleaseResource(reshandle);
660     CloseResFile(refnum);
661
662     compare = strncmp(resvalue, key, strlen(resvalue));
663     sfree(resname);
664     sfree(resvalue);
665
666     if (compare) {
667         /* Key different */
668         return 2;
669     } else {
670         /* Key matched */
671         return 0;
672     }
673 }
674
675 void store_host_key(const char *hostname, int port,
676                     const char *keytype, const char *key)
677 {
678     short puttyVRefNum;
679     long puttyDirID;
680     OSErr error;
681     FSSpec keyfile;
682     short keyrefnum;
683     char *resname;
684     Str255 presname;
685     Handle resvalue;
686     Handle reshandle;
687     int id;
688
689     /* Open the host key file */
690
691     if (get_putty_dir(~kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
692         goto out;
693
694     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSSH Host Keys",
695                          &keyfile);
696     if (error == fnfErr) {
697         /* It doesn't exist, so create it */
698         FSpCreateResFile(&keyfile, INTERNAL_CREATOR, HKYS_TYPE, smRoman);
699         keyrefnum = FSpOpenResFile(&keyfile, fsWrPerm);
700         if (ResError() == noErr) {
701             copy_resource('STR', -16397); /* XXX: wtf is this? */
702             CloseResFile(keyrefnum);
703         }
704     } else if (error != noErr) goto out;
705
706     keyrefnum = FSpOpenResFile(&keyfile, fsWrPerm);
707     if (keyrefnum == -1) goto out;
708
709     UseResFile(keyrefnum);
710     resname = dupprintf("%s@%d:%s", keytype, port, hostname);
711     c2pstrcpy(presname, resname);
712
713     reshandle = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), presname);
714     if (reshandle != NULL) {
715         /* The resource exists, we're replacing a host key */
716         RemoveResource(reshandle);
717     }
718     error = PtrToHand(key, &resvalue, strlen(key));
719     if (error != noErr) goto out;
720
721     id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
722     if (ResError() != noErr) goto out;
723     AddResource(resvalue, FOUR_CHAR_CODE('TEXT'), id, presname);
724     if (ResError() != noErr) goto out;
725
726     CloseResFile(keyrefnum);
727     return;
728
729   out:
730     fatalbox("Writing host key failed (%d)", error);
731     sfree(resname);
732 }
733   
734 /*
735  * Emacs magic:
736  * Local Variables:
737  * c-file-style: "simon"
738  * End:
739  */