4 * macstore.c: Macintosh-specific impementation of the interface
11 #include <Resources.h>
12 #include <TextUtils.h>
23 OSErr
FSpGetDirID(FSSpec
*f
, long *idp
, Boolean makeit
);
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.
30 OSErr
get_putty_dir(Boolean makeit
, short *pVRefNum
, long *pDirID
)
35 long prefDirID
, puttyDirID
;
37 error
= FindFolder(kOnSystemDisk
, kPreferencesFolderType
, makeit
,
38 &prefVRefNum
, &prefDirID
);
39 if (error
!= noErr
) goto out
;
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
;
46 *pVRefNum
= prefVRefNum
;
53 OSErr
get_session_dir(Boolean makeit
, short *pVRefNum
, long *pDirID
) {
57 long puttyDirID
, sessDirID
;
59 error
= get_putty_dir(makeit
, &puttyVRefNum
, &puttyDirID
);
60 if (error
!= noErr
) goto out
;
61 error
= FSMakeFSSpec(puttyVRefNum
, puttyDirID
, "\pSaved Sessions",
63 if (error
!= noErr
&& error
!= fnfErr
) goto out
;
64 error
= FSpGetDirID(&sessdir
, &sessDirID
, makeit
);
65 if (error
!= noErr
) goto out
;
67 *pVRefNum
= puttyVRefNum
;
74 OSErr
FSpGetDirID(FSSpec
*f
, long *idp
, Boolean makeit
) {
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) {
90 *idp
= pb
.dirInfo
.ioDrDirID
;
96 /* Copy a resource into the current resource file */
97 static OSErr
copy_resource(ResType restype
, short resid
)
102 h
= GetResource(restype
, resid
);
104 GetResInfo(h
, &resid
, &restype
, resname
);
106 AddResource(h
, restype
, resid
, resname
);
107 if (ResError() == noErr
)
113 struct write_settings
{
119 void *open_settings_w(char const *sessionname
, char **errmsg
) {
128 error
= get_session_dir(kCreateFolder
, &sessVRefNum
, &sessDirID
);
129 if (error
!= noErr
) return NULL
;
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
;
140 return open_settings_w_fsp(&dstfile
);
144 * NB: Destination file must exist.
146 void *open_settings_w_fsp(FSSpec
*dstfile
)
150 struct write_settings
*ws
;
154 ws
= snew(struct write_settings
);
155 ws
->dstfile
= *dstfile
;
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
;
168 FSpCreateResFile(&ws
->tmpfile
, PUTTY_CREATOR
, SESS_TYPE
, smSystemScript
);
169 if ((error
= ResError()) != noErr
) goto out
;
171 ws
->fd
= FSpOpenResFile(&ws
->tmpfile
, fsWrPerm
);
172 if (ws
->fd
== -1) {error
= ResError(); goto out
;}
174 /* Set up standard resources. Doesn't matter if these fail. */
175 copy_resource('STR ', -16396);
176 copy_resource('TMPL', TMPL_Int
);
182 fatalbox("Failed to open session for write (%d)", error
);
185 void write_setting_s(void *handle
, char const *key
, char const *value
) {
186 int fd
= *(int *)handle
;
193 if (ResError() != noErr
)
194 fatalbox("Failed to open saved session (%d)", ResError());
196 error
= PtrToHand(value
, &h
, strlen(value
));
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());
209 void write_setting_i(void *handle
, char const *key
, int value
) {
210 int fd
= *(int *)handle
;
217 if (ResError() != noErr
)
218 fatalbox("Failed to open saved session (%d)", ResError());
220 /* XXX assume all systems have the same "int" format */
221 error
= PtrToHand(&value
, &h
, sizeof(int));
223 fatalbox("Failed to allocate memory (%d)", error
);
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());
235 void close_settings_w(void *handle
) {
236 struct write_settings
*ws
= handle
;
239 CloseResFile(ws
->fd
);
240 if ((error
= ResError()) != noErr
)
242 error
= FSpExchangeFiles(&ws
->tmpfile
, &ws
->dstfile
);
243 if (error
!= noErr
) goto out
;
244 error
= FSpDelete(&ws
->tmpfile
);
245 if (error
!= noErr
) goto out
;
249 fatalbox("Close of saved session failed (%d)", error
);
253 void *open_settings_r(char const *sessionname
)
261 error
= get_session_dir(kDontCreateFolder
, &sessVRefNum
, &sessDirID
);
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
);
274 void *open_settings_r_fsp(FSSpec
*sessfile
)
280 fd
= FSpOpenResFile(sessfile
, fsRdPerm
);
281 if (fd
== 0) {error
= ResError(); goto out
;}
291 char *read_setting_s(void *handle
, char const *key
, char *buffer
, int buflen
) {
297 if (handle
== NULL
) goto out
;
300 if (ResError() != noErr
) goto out
;
301 c2pstrcpy(pkey
, key
);
302 h
= Get1NamedResource(FOUR_CHAR_CODE('TEXT'), pkey
);
303 if (h
== NULL
) goto out
;
305 len
= GetHandleSize(h
);
306 if (len
+ 1 > buflen
) goto out
;
307 memcpy(buffer
, *h
, len
);
311 if (ResError() != noErr
) goto out
;
318 int read_setting_i(void *handle
, char const *key
, int defvalue
) {
324 if (handle
== NULL
) goto out
;
327 if (ResError() != noErr
) goto out
;
328 c2pstrcpy(pkey
, key
);
329 h
= Get1NamedResource(FOUR_CHAR_CODE('Int '), pkey
);
330 if (h
== NULL
) goto out
;
333 if (ResError() != noErr
) goto out
;
340 int read_setting_fontspec(void *handle
, const char *name
, FontSpec
*result
)
346 if (!read_setting_s(handle
, name
, tmp
, sizeof(tmp
)))
348 c2pstrcpy(ret
.name
, tmp
);
349 settingname
= dupcat(name
, "Face", NULL
);
350 ret
.face
= read_setting_i(handle
, settingname
, 0);
352 settingname
= dupcat(name
, "Height", NULL
);
353 ret
.size
= read_setting_i(handle
, settingname
, 0);
355 if (ret
.size
== 0) return 0;
360 void write_setting_fontspec(void *handle
, const char *name
, FontSpec font
)
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
);
370 settingname
= dupcat(name
, "Size", NULL
);
371 write_setting_i(handle
, settingname
, font
.size
);
375 int read_setting_filename(void *handle
, const char *key
, Filename
*result
)
383 if (handle
== NULL
) goto out
;
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
));
393 err
= ResolveAlias(NULL
, h
, &result
->fss
, &changed
);
394 if (err
!= noErr
&& err
!= fnfErr
) goto out
;
395 if ((*h
)->userType
== 'pTTY') {
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
,
405 if (err
!= noErr
&& err
!= fnfErr
) goto out
;
408 ReleaseResource((Handle
)h
);
409 if (ResError() != noErr
) goto out
;
416 void write_setting_filename(void *handle
, const char *key
, Filename fn
)
418 int fd
= *(int *)handle
;
425 if (ResError() != noErr
)
426 fatalbox("Failed to open saved session (%d)", ResError());
428 if (filename_is_null(fn
)) {
429 /* Generate a special "null" alias */
430 h
= (AliasHandle
)NewHandle(sizeof(**h
));
432 fatalbox("Failed to create fake alias");
433 (*h
)->userType
= 'pTTY';
434 (*h
)->aliasSize
= sizeof(**h
);
436 error
= NewAlias(NULL
, &fn
.fss
, &h
);
437 if (error
== fnfErr
) {
439 * NewAlias can't create an alias for a nonexistent file.
440 * Create an alias for the directory, and record the
445 FSMakeFSSpec(fn
.fss
.vRefNum
, fn
.fss
.parID
, NULL
, &tmpfss
);
446 error
= NewAlias(NULL
, &tmpfss
, &h
);
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
,
457 fatalbox("Failed to create alias");
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());
469 void close_settings_r(void *handle
) {
472 if (handle
== NULL
) return;
475 if (ResError() != noErr
)
476 fatalbox("Close of saved session failed (%d)", ResError());
480 void del_settings(char const *sessionname
) {
487 error
= get_session_dir(kDontCreateFolder
, &sessVRefNum
, &sessDirID
);
489 c2pstrcpy(psessionname
, sessionname
);
490 error
= FSMakeFSSpec(sessVRefNum
, sessDirID
, psessionname
, &sessfile
);
491 if (error
!= noErr
) goto out
;
493 error
= FSpDelete(&sessfile
);
496 fatalbox("Delete session failed (%d)", error
);
499 struct enum_settings_state
{
505 void *enum_settings_start(void) {
507 struct enum_settings_state
*state
;
509 state
= safemalloc(sizeof(*state
));
510 error
= get_session_dir(kDontCreateFolder
, &state
->vRefNum
, &state
->dirID
);
511 if (error
!= noErr
) {
519 char *enum_settings_next(void *handle
, char *buffer
, int buflen
) {
520 struct enum_settings_state
*e
= handle
;
525 if (e
== NULL
) return NULL
;
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
&&
538 p2cstrcpy(buffer
, name
);
542 void enum_settings_finish(void *handle
) {
547 #define SEED_SIZE 512
549 void read_random_seed(noise_consumer_t consumer
)
556 long count
= SEED_SIZE
;
558 if (get_putty_dir(kDontCreateFolder
, &puttyVRefNum
, &puttyDirID
) != noErr
)
560 if (HOpenDF(puttyVRefNum
, puttyDirID
, "\pPuTTY Random Seed", fsRdPerm
,
563 error
= FSRead(refnum
, &count
, buf
);
564 if (error
!= noErr
&& error
!= eofErr
)
566 (*consumer
)(buf
, count
);
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.
574 void write_random_seed(void *data
, int len
)
583 if (get_putty_dir(kCreateFolder
, &puttyVRefNum
, &puttyDirID
) != noErr
)
586 error
= FSMakeFSSpec(puttyVRefNum
, puttyDirID
, "\pPuTTY Random Seed",
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
);
596 } else if (error
!= noErr
) return;
598 if (FSpOpenDF(&dstfile
, fsWrPerm
, &refnum
) != noErr
) return;
599 FSWrite(refnum
, &count
, data
);
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.
614 int verify_host_key(const char *hostname
, int port
,
615 const char *keytype
, const char *key
)
628 if (get_putty_dir(kCreateFolder
, &puttyVRefNum
, &puttyDirID
) != noErr
)
631 error
= FSMakeFSSpec(puttyVRefNum
, puttyDirID
, "\pSSH Host Keys",
633 if (error
== fnfErr
) {
634 /* Keys file doesn't exist yet, so we can't match the key */
638 refnum
= FSpOpenResFile(&keyfile
, fsRdPerm
);
641 /* We couldn't open the resource fork, so we can't match the key */
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 */
655 len
= GetHandleSize(reshandle
);
656 resvalue
= snewn(len
+1, char);
657 memcpy(resvalue
, *reshandle
, len
);
659 ReleaseResource(reshandle
);
660 CloseResFile(refnum
);
662 compare
= strncmp(resvalue
, key
, strlen(resvalue
));
675 void store_host_key(const char *hostname
, int port
,
676 const char *keytype
, const char *key
)
688 /* Open the host key file */
690 if (get_putty_dir(~kCreateFolder
, &puttyVRefNum
, &puttyDirID
) != noErr
)
693 error
= FSMakeFSSpec(puttyVRefNum
, puttyDirID
, "\pSSH Host Keys",
695 if (error
== fnfErr
) {
696 /* It doesn't exist, so create it */
697 FSpCreateResFile(&keyfile
, INTERNAL_CREATOR
, HKYS_TYPE
, smRoman
);
698 keyrefnum
= FSpOpenResFile(&keyfile
, fsWrPerm
);
699 if (ResError() == noErr
) {
700 copy_resource('STR', -16397); /* XXX: wtf is this? */
701 CloseResFile(keyrefnum
);
703 } else if (error
!= noErr
) goto out
;
705 keyrefnum
= FSpOpenResFile(&keyfile
, fsWrPerm
);
706 if (keyrefnum
== -1) goto out
;
708 UseResFile(keyrefnum
);
709 resname
= dupprintf("%s@%d:%s", keytype
, port
, hostname
);
710 c2pstrcpy(presname
, resname
);
712 error
= PtrToHand(key
, &resvalue
, strlen(key
));
713 if (error
!= noErr
) goto out
;
715 id
= Unique1ID(FOUR_CHAR_CODE('TEXT'));
716 if (ResError() != noErr
) goto out
;
717 AddResource(resvalue
, FOUR_CHAR_CODE('TEXT'), id
, presname
);
718 if (ResError() != noErr
) goto out
;
720 CloseResFile(keyrefnum
);
724 fatalbox("Writing host key failed (%d)", error
);
731 * c-file-style: "simon"