X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/803efcdfac42854ceb72d0cdf1b149071a452e16..9f77212d7f268f8380781727b7fff520349891cd:/mac/macstore.c diff --git a/mac/macstore.c b/mac/macstore.c index ecd0355b..35f31bdb 100644 --- a/mac/macstore.c +++ b/mac/macstore.c @@ -1,4 +1,4 @@ -/* $Id: macstore.c,v 1.5 2002/12/28 22:44:27 ben Exp $ */ +/* $Id$ */ /* * macstore.c: Macintosh-specific impementation of the interface @@ -11,16 +11,15 @@ #include #include +#include #include #include "putty.h" #include "storage.h" +#include "mac.h" +#include "macresid.h" -#define PUTTY_CREATOR FOUR_CHAR_CODE('pTTY') -#define SESS_TYPE FOUR_CHAR_CODE('Sess') - -OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID); OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit); /* @@ -28,11 +27,12 @@ OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit); * preferences folder. Each (key,value) pair is stored as a resource. */ -OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) { +OSErr get_putty_dir(Boolean makeit, short *pVRefNum, long *pDirID) +{ OSErr error = noErr; short prefVRefNum; - FSSpec puttydir, sessdir; - long prefDirID, puttyDirID, sessDirID; + FSSpec puttydir; + long prefDirID, puttyDirID; error = FindFolder(kOnSystemDisk, kPreferencesFolderType, makeit, &prefVRefNum, &prefDirID); @@ -43,13 +43,28 @@ OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) { error = FSpGetDirID(&puttydir, &puttyDirID, makeit); if (error != noErr) goto out; - error = FSMakeFSSpec(prefVRefNum, puttyDirID, "\pSaved Sessions", + *pVRefNum = prefVRefNum; + *pDirID = puttyDirID; + + out: + return error; +} + +OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) { + OSErr error = noErr; + short puttyVRefNum; + FSSpec sessdir; + long puttyDirID, sessDirID; + + error = get_putty_dir(makeit, &puttyVRefNum, &puttyDirID); + if (error != noErr) goto out; + error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSaved Sessions", &sessdir); if (error != noErr && error != fnfErr) goto out; error = FSpGetDirID(&sessdir, &sessDirID, makeit); if (error != noErr) goto out; - *pVRefNum = prefVRefNum; + *pVRefNum = puttyVRefNum; *pDirID = sessDirID; out: @@ -78,37 +93,73 @@ OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit) { return error; } +/* Copy a resource into the current resource file */ +static OSErr copy_resource(ResType restype, short resid) +{ + Handle h; + Str255 resname; + + h = GetResource(restype, resid); + if (h != NULL) { + GetResInfo(h, &resid, &restype, resname); + DetachResource(h); + AddResource(h, restype, resid, resname); + if (ResError() == noErr) + WriteResource(h); + } + return ResError(); +} + struct write_settings { int fd; FSSpec tmpfile; FSSpec dstfile; }; -void *open_settings_w(char *sessionname) { - short sessVRefNum, tmpVRefNum; - long sessDirID, tmpDirID; +void *open_settings_w(char const *sessionname, char **errmsg) { + short sessVRefNum; + long sessDirID; OSErr error; Str255 psessionname; - struct write_settings *ws; - - ws = safemalloc(sizeof *ws); + FSSpec dstfile; + + *errmsg = NULL; + error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID); - if (error != noErr) goto out; + if (error != noErr) return NULL; + if (!sessionname || !*sessionname) + sessionname = "Default Settings"; c2pstrcpy(psessionname, sessionname); - error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &ws->dstfile); - if (error != noErr && error != fnfErr) goto out; + error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &dstfile); if (error == fnfErr) { - FSpCreateResFile(&ws->dstfile, PUTTY_CREATOR, SESS_TYPE, - smSystemScript); - if ((error = ResError()) != noErr) goto out; - } + FSpCreateResFile(&dstfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript); + if ((error = ResError()) != noErr) return NULL; + } else if (error != noErr) return NULL; + + return open_settings_w_fsp(&dstfile); +} + +/* + * NB: Destination file must exist. + */ +void *open_settings_w_fsp(FSSpec *dstfile) +{ + short tmpVRefNum; + long tmpDirID; + struct write_settings *ws; + OSErr error; + Str255 tmpname; + + ws = snew(struct write_settings); + ws->dstfile = *dstfile; /* Create a temporary file to save to first. */ - error = FindFolder(sessVRefNum, kTemporaryFolderType, kCreateFolder, - &tmpVRefNum, &tmpDirID); + error = FindFolder(ws->dstfile.vRefNum, kTemporaryFolderType, + kCreateFolder, &tmpVRefNum, &tmpDirID); if (error != noErr) goto out; - error = FSMakeFSSpec(tmpVRefNum, tmpDirID, psessionname, &ws->tmpfile); + c2pstrcpy(tmpname, tmpnam(NULL)); + error = FSMakeFSSpec(tmpVRefNum, tmpDirID, tmpname, &ws->tmpfile); if (error != noErr && error != fnfErr) goto out; if (error == noErr) { error = FSpDelete(&ws->tmpfile); @@ -120,6 +171,10 @@ void *open_settings_w(char *sessionname) { ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm); if (ws->fd == -1) {error = ResError(); goto out;} + /* Set up standard resources. Doesn't matter if these fail. */ + copy_resource('STR ', -16396); + copy_resource('TMPL', TMPL_Int); + return ws; out: @@ -127,11 +182,12 @@ void *open_settings_w(char *sessionname) { fatalbox("Failed to open session for write (%d)", error); } -void write_setting_s(void *handle, char *key, char *value) { +void write_setting_s(void *handle, char const *key, char const *value) { int fd = *(int *)handle; Handle h; int id; OSErr error; + Str255 pkey; UseResFile(fd); if (ResError() != noErr) @@ -144,16 +200,18 @@ void write_setting_s(void *handle, char *key, char *value) { id = Unique1ID(FOUR_CHAR_CODE('TEXT')); if (ResError() != noErr) fatalbox("Failed to get ID for resource %s (%d)", key, ResError()); - addresource(h, FOUR_CHAR_CODE('TEXT'), id, key); + c2pstrcpy(pkey, key); + AddResource(h, FOUR_CHAR_CODE('TEXT'), id, pkey); if (ResError() != noErr) fatalbox("Failed to add resource %s (%d)", key, ResError()); } -void write_setting_i(void *handle, char *key, int value) { +void write_setting_i(void *handle, char const *key, int value) { int fd = *(int *)handle; Handle h; int id; OSErr error; + Str255 pkey; UseResFile(fd); if (ResError() != noErr) @@ -168,7 +226,8 @@ void write_setting_i(void *handle, char *key, int value) { id = Unique1ID(FOUR_CHAR_CODE('Int ')); if (ResError() != noErr) fatalbox("Failed to get ID for resource %s (%d)", key, ResError()); - addresource(h, FOUR_CHAR_CODE('Int '), id, key); + c2pstrcpy(pkey, key); + AddResource(h, FOUR_CHAR_CODE('Int '), id, pkey); if (ResError() != noErr) fatalbox("Failed to add resource %s (%d)", key, ResError()); } @@ -191,24 +250,37 @@ void close_settings_w(void *handle) { safefree(handle); } -void *open_settings_r(char *sessionname) { +void *open_settings_r(char const *sessionname) +{ short sessVRefNum; long sessDirID; FSSpec sessfile; OSErr error; Str255 psessionname; - int fd; - int *handle; error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID); + if (!sessionname || !*sessionname) + sessionname = "Default Settings"; c2pstrcpy(psessionname, sessionname); error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile); if (error != noErr) goto out; - fd = FSpOpenResFile(&sessfile, fsRdPerm); + return open_settings_r_fsp(&sessfile); + + out: + return NULL; +} + +void *open_settings_r_fsp(FSSpec *sessfile) +{ + OSErr error; + int fd; + int *handle; + + fd = FSpOpenResFile(sessfile, fsRdPerm); if (fd == 0) {error = ResError(); goto out;} - handle = safemalloc(sizeof *handle); + handle = snew(int); *handle = fd; return handle; @@ -216,17 +288,18 @@ void *open_settings_r(char *sessionname) { return NULL; } - -char *read_setting_s(void *handle, char *key, char *buffer, int buflen) { +char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) { int fd; Handle h; size_t len; + Str255 pkey; if (handle == NULL) goto out; fd = *(int *)handle; UseResFile(fd); if (ResError() != noErr) goto out; - h = get1namedresource(FOUR_CHAR_CODE('TEXT'), key); + c2pstrcpy(pkey, key); + h = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), pkey); if (h == NULL) goto out; len = GetHandleSize(h); @@ -242,16 +315,18 @@ char *read_setting_s(void *handle, char *key, char *buffer, int buflen) { return NULL; } -int read_setting_i(void *handle, char *key, int defvalue) { +int read_setting_i(void *handle, char const *key, int defvalue) { int fd; Handle h; int value; + Str255 pkey; if (handle == NULL) goto out; fd = *(int *)handle; UseResFile(fd); if (ResError() != noErr) goto out; - h = get1namedresource(FOUR_CHAR_CODE('Int '), key); + c2pstrcpy(pkey, key); + h = Get1NamedResource(FOUR_CHAR_CODE('Int '), pkey); if (h == NULL) goto out; value = *(int *)*h; ReleaseResource(h); @@ -262,6 +337,135 @@ int read_setting_i(void *handle, char *key, int defvalue) { return defvalue; } +int read_setting_fontspec(void *handle, const char *name, FontSpec *result) +{ + char *settingname; + FontSpec ret; + char tmp[256]; + + if (!read_setting_s(handle, name, tmp, sizeof(tmp))) + return 0; + c2pstrcpy(ret.name, tmp); + settingname = dupcat(name, "Face", NULL); + ret.face = read_setting_i(handle, settingname, 0); + sfree(settingname); + settingname = dupcat(name, "Height", NULL); + ret.size = read_setting_i(handle, settingname, 0); + sfree(settingname); + if (ret.size == 0) return 0; + *result = ret; + return 1; +} + +void write_setting_fontspec(void *handle, const char *name, FontSpec font) +{ + char *settingname; + char tmp[256]; + + p2cstrcpy(tmp, font.name); + write_setting_s(handle, name, tmp); + settingname = dupcat(name, "Face", NULL); + write_setting_i(handle, settingname, font.face); + sfree(settingname); + settingname = dupcat(name, "Size", NULL); + write_setting_i(handle, settingname, font.size); + sfree(settingname); +} + +int read_setting_filename(void *handle, const char *key, Filename *result) +{ + int fd; + AliasHandle h; + Boolean changed; + OSErr err; + Str255 pkey; + + if (handle == NULL) goto out; + fd = *(int *)handle; + UseResFile(fd); + if (ResError() != noErr) goto out; + c2pstrcpy(pkey, key); + h = (AliasHandle)Get1NamedResource(rAliasType, pkey); + if (h == NULL) goto out; + if ((*h)->userType == 'pTTY' && (*h)->aliasSize == sizeof(**h)) + memset(result, 0, sizeof(*result)); + else { + err = ResolveAlias(NULL, h, &result->fss, &changed); + if (err != noErr && err != fnfErr) goto out; + if ((*h)->userType == 'pTTY') { + long dirid; + StrFileName fname; + + /* Tail of record is pascal string contaning leafname */ + if (FSpGetDirID(&result->fss, &dirid, FALSE) != noErr) goto out; + memcpy(fname, (char *)*h + (*h)->aliasSize, + GetHandleSize((Handle)h) - (*h)->aliasSize); + err = FSMakeFSSpec(result->fss.vRefNum, dirid, fname, + &result->fss); + if (err != noErr && err != fnfErr) goto out; + } + } + ReleaseResource((Handle)h); + if (ResError() != noErr) goto out; + return 1; + + out: + return 0; +} + +void write_setting_filename(void *handle, const char *key, Filename fn) +{ + int fd = *(int *)handle; + AliasHandle h; + int id; + OSErr error; + Str255 pkey; + + UseResFile(fd); + if (ResError() != noErr) + fatalbox("Failed to open saved session (%d)", ResError()); + + if (filename_is_null(fn)) { + /* Generate a special "null" alias */ + h = (AliasHandle)NewHandle(sizeof(**h)); + if (h == NULL) + fatalbox("Failed to create fake alias"); + (*h)->userType = 'pTTY'; + (*h)->aliasSize = sizeof(**h); + } else { + error = NewAlias(NULL, &fn.fss, &h); + if (error == fnfErr) { + /* + * NewAlias can't create an alias for a nonexistent file. + * Create an alias for the directory, and record the + * filename as well. + */ + FSSpec tmpfss; + + FSMakeFSSpec(fn.fss.vRefNum, fn.fss.parID, NULL, &tmpfss); + error = NewAlias(NULL, &tmpfss, &h); + if (error != noErr) + fatalbox("Failed to create alias"); + (*h)->userType = 'pTTY'; + SetHandleSize((Handle)h, (*h)->aliasSize + fn.fss.name[0] + 1); + if (MemError() != noErr) + fatalbox("Failed to create alias"); + memcpy((char *)*h + (*h)->aliasSize, fn.fss.name, + fn.fss.name[0] + 1); + } + if (error != noErr) + fatalbox("Failed to create alias"); + } + /* Put the data in a resource. */ + id = Unique1ID(rAliasType); + if (ResError() != noErr) + fatalbox("Failed to get ID for resource %s (%d)", key, ResError()); + c2pstrcpy(pkey, key); + AddResource((Handle)h, rAliasType, id, pkey); + if (ResError() != noErr) + fatalbox("Failed to add resource %s (%d)", key, ResError()); +} + void close_settings_r(void *handle) { int fd; @@ -270,10 +474,10 @@ void close_settings_r(void *handle) { CloseResFile(fd); if (ResError() != noErr) fatalbox("Close of saved session failed (%d)", ResError()); - safefree(handle); + sfree(handle); } -void del_settings(char *sessionname) { +void del_settings(char const *sessionname) { OSErr error; FSSpec sessfile; short sessVRefNum; @@ -302,10 +506,10 @@ void *enum_settings_start(void) { OSErr error; struct enum_settings_state *state; - state = safemalloc(sizeof(*state)); + state = snew(struct enum_settings_state); error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID); if (error != noErr) { - safefree(state); + sfree(state); return NULL; } state->index = 1; @@ -340,7 +544,193 @@ void enum_settings_finish(void *handle) { safefree(handle); } +#define SEED_SIZE 512 + +void read_random_seed(noise_consumer_t consumer) +{ + short puttyVRefNum; + long puttyDirID; + OSErr error; + char buf[SEED_SIZE]; + short refnum; + long count = SEED_SIZE; + + if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr) + return; + if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm, + &refnum) != noErr) + return; + error = FSRead(refnum, &count, buf); + if (error != noErr && error != eofErr) + return; + (*consumer)(buf, count); + FSClose(refnum); +} + +/* + * We don't bother with the usual FSpExchangeFiles dance here because + * it doesn't really matter if the old random seed gets lost. + */ +void write_random_seed(void *data, int len) +{ + short puttyVRefNum; + long puttyDirID; + OSErr error; + FSSpec dstfile; + short refnum; + long count = len; + + if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr) + return; + + error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", + &dstfile); + if (error == fnfErr) { + /* Set up standard resources */ + FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman); + refnum = FSpOpenResFile(&dstfile, fsWrPerm); + if (ResError() == noErr) { + copy_resource('STR ', -16397); + CloseResFile(refnum); + } + } else if (error != noErr) return; + + if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return; + FSWrite(refnum, &count, data); + FSClose(refnum); + + return; +} + +/* + * This host key cache uses a file in the PuTTY Preferences folder and + * stores keys as individual TEXT resources in the resource fork of + * that file. This has two problems. Firstly, a resource fork can + * contain no more than 2727 resources. Secondly, the Resource + * Manager uses a linear search to find a particular resource, which + * could make having lots of host keys quite slow. + */ + +int verify_host_key(const char *hostname, int port, + const char *keytype, const char *key) +{ + short puttyVRefNum; + long puttyDirID; + OSErr error; + FSSpec keyfile; + short refnum; + char *resname; + Str255 presname; + char *resvalue; + Handle reshandle; + int len, compare; + + if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr) + return 1; + + error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSSH Host Keys", + &keyfile); + if (error == fnfErr) { + /* Keys file doesn't exist yet, so we can't match the key */ + return 1; + } + + refnum = FSpOpenResFile(&keyfile, fsRdPerm); + + if (refnum == -1) { + /* We couldn't open the resource fork, so we can't match the key */ + return 1; + } + + UseResFile(refnum); + + resname = dupprintf("%s@%d:%s", keytype, port, hostname); + c2pstrcpy(presname, resname); + reshandle = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), presname); + if (ResError() != noErr) { + /* Couldn't open the specific resource */ + return 1; + } + + len = GetHandleSize(reshandle); + resvalue = snewn(len+1, char); + memcpy(resvalue, *reshandle, len); + resvalue[len]='\0'; + ReleaseResource(reshandle); + CloseResFile(refnum); + + compare = strncmp(resvalue, key, strlen(resvalue)); + sfree(resname); + sfree(resvalue); + + if (compare) { + /* Key different */ + return 2; + } else { + /* Key matched */ + return 0; + } +} + +void store_host_key(const char *hostname, int port, + const char *keytype, const char *key) +{ + short puttyVRefNum; + long puttyDirID; + OSErr error; + FSSpec keyfile; + short keyrefnum; + char *resname; + Str255 presname; + Handle resvalue; + Handle reshandle; + int id; + + /* Open the host key file */ + + if (get_putty_dir(~kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr) + goto out; + + error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSSH Host Keys", + &keyfile); + if (error == fnfErr) { + /* It doesn't exist, so create it */ + FSpCreateResFile(&keyfile, INTERNAL_CREATOR, HKYS_TYPE, smRoman); + keyrefnum = FSpOpenResFile(&keyfile, fsWrPerm); + if (ResError() == noErr) { + copy_resource('STR', -16397); /* XXX: wtf is this? */ + CloseResFile(keyrefnum); + } + } else if (error != noErr) goto out; + + keyrefnum = FSpOpenResFile(&keyfile, fsWrPerm); + if (keyrefnum == -1) goto out; + + UseResFile(keyrefnum); + resname = dupprintf("%s@%d:%s", keytype, port, hostname); + c2pstrcpy(presname, resname); + + reshandle = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), presname); + if (reshandle != NULL) { + /* The resource exists, we're replacing a host key */ + RemoveResource(reshandle); + } + error = PtrToHand(key, &resvalue, strlen(key)); + if (error != noErr) goto out; + + id = Unique1ID(FOUR_CHAR_CODE('TEXT')); + if (ResError() != noErr) goto out; + AddResource(resvalue, FOUR_CHAR_CODE('TEXT'), id, presname); + if (ResError() != noErr) goto out; + + CloseResFile(keyrefnum); + return; + out: + fatalbox("Writing host key failed (%d)", error); + sfree(resname); +} + /* * Emacs magic: * Local Variables: