]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/macstore.c
5425efa3c326615af54061253b23dca30ee4962a
[PuTTY.git] / mac / macstore.c
1 /* $Id: macstore.c,v 1.18 2003/03/29 23:07:55 ben Exp $ */
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) {
120     short sessVRefNum;
121     long sessDirID;
122     OSErr error;
123     Str255 psessionname;
124     FSSpec dstfile;
125     
126     error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
127     if (error != noErr) return NULL;
128
129     if (!sessionname || !*sessionname)
130         sessionname = "Default Settings";
131     c2pstrcpy(psessionname, sessionname);
132     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &dstfile);
133     if (error == fnfErr) {
134         FSpCreateResFile(&dstfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
135         if ((error = ResError()) != noErr) return NULL;
136     } else if (error != noErr) return NULL;
137
138     return open_settings_w_fsp(&dstfile);
139 }
140
141 /*
142  * NB: Destination file must exist.
143  */
144 void *open_settings_w_fsp(FSSpec *dstfile)
145 {
146     short tmpVRefNum;
147     long tmpDirID;
148     struct write_settings *ws;
149     OSErr error;
150     Str255 tmpname;
151
152     ws = snew(struct write_settings);
153     ws->dstfile = *dstfile;
154
155     /* Create a temporary file to save to first. */
156     error = FindFolder(ws->dstfile.vRefNum, kTemporaryFolderType,
157                        kCreateFolder, &tmpVRefNum, &tmpDirID);
158     if (error != noErr) goto out;
159     c2pstrcpy(tmpname, tmpnam(NULL));
160     error = FSMakeFSSpec(tmpVRefNum, tmpDirID, tmpname, &ws->tmpfile);
161     if (error != noErr && error != fnfErr) goto out;
162     if (error == noErr) {
163         error = FSpDelete(&ws->tmpfile);
164         if (error != noErr) goto out;
165     }
166     FSpCreateResFile(&ws->tmpfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
167     if ((error = ResError()) != noErr) goto out;
168
169     ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm);
170     if (ws->fd == -1) {error = ResError(); goto out;}
171
172     /* Set up standard resources.  Doesn't matter if these fail. */
173     copy_resource('STR ', -16396);
174     copy_resource('TMPL', TMPL_Int);
175
176     return ws;
177
178   out:
179     safefree(ws);
180     fatalbox("Failed to open session for write (%d)", error);
181 }
182
183 void write_setting_s(void *handle, char const *key, char const *value) {
184     int fd = *(int *)handle;
185     Handle h;
186     int id;
187     OSErr error;
188     Str255 pkey;
189
190     UseResFile(fd);
191     if (ResError() != noErr)
192         fatalbox("Failed to open saved session (%d)", ResError());
193
194     error = PtrToHand(value, &h, strlen(value));
195     if (error != noErr)
196         fatalbox("Failed to allocate memory");
197     /* Put the data in a resource. */
198     id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
199     if (ResError() != noErr)
200         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
201     c2pstrcpy(pkey, key);
202     AddResource(h, FOUR_CHAR_CODE('TEXT'), id, pkey);
203     if (ResError() != noErr)
204         fatalbox("Failed to add resource %s (%d)", key, ResError());
205 }
206
207 void write_setting_i(void *handle, char const *key, int value) {
208     int fd = *(int *)handle;
209     Handle h;
210     int id;
211     OSErr error;
212     Str255 pkey;
213
214     UseResFile(fd);
215     if (ResError() != noErr)
216         fatalbox("Failed to open saved session (%d)", ResError());
217
218     /* XXX assume all systems have the same "int" format */
219     error = PtrToHand(&value, &h, sizeof(int));
220     if (error != noErr)
221         fatalbox("Failed to allocate memory (%d)", error);
222
223     /* Put the data in a resource. */
224     id = Unique1ID(FOUR_CHAR_CODE('Int '));
225     if (ResError() != noErr)
226         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
227     c2pstrcpy(pkey, key);
228     AddResource(h, FOUR_CHAR_CODE('Int '), id, pkey);
229     if (ResError() != noErr)
230         fatalbox("Failed to add resource %s (%d)", key, ResError());
231 }
232
233 void close_settings_w(void *handle) {
234     struct write_settings *ws = handle;
235     OSErr error;
236
237     CloseResFile(ws->fd);
238     if ((error = ResError()) != noErr)
239         goto out;
240     error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile);
241     if (error != noErr) goto out;
242     error = FSpDelete(&ws->tmpfile);
243     if (error != noErr) goto out;
244     return;
245
246   out:
247     fatalbox("Close of saved session failed (%d)", error);
248     safefree(handle);
249 }
250
251 void *open_settings_r(char const *sessionname)
252 {
253     short sessVRefNum;
254     long sessDirID;
255     FSSpec sessfile;
256     OSErr error;
257     Str255 psessionname;
258
259     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
260
261     if (!sessionname || !*sessionname)
262         sessionname = "Default Settings";
263     c2pstrcpy(psessionname, sessionname);
264     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
265     if (error != noErr) goto out;
266     return open_settings_r_fsp(&sessfile);
267
268   out:
269     return NULL;
270 }
271
272 void *open_settings_r_fsp(FSSpec *sessfile)
273 {
274     OSErr error;
275     int fd;
276     int *handle;
277
278     fd = FSpOpenResFile(sessfile, fsRdPerm);
279     if (fd == 0) {error = ResError(); goto out;}
280
281     handle = snew(int);
282     *handle = fd;
283     return handle;
284
285   out:
286     return NULL;
287 }
288
289 char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
290     int fd;
291     Handle h;
292     size_t len;
293     Str255 pkey;
294
295     if (handle == NULL) goto out;
296     fd = *(int *)handle;
297     UseResFile(fd);
298     if (ResError() != noErr) goto out;
299     c2pstrcpy(pkey, key);
300     h = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), pkey);
301     if (h == NULL) goto out;
302
303     len = GetHandleSize(h);
304     if (len + 1 > buflen) goto out;
305     memcpy(buffer, *h, len);
306     buffer[len] = '\0';
307
308     ReleaseResource(h);
309     if (ResError() != noErr) goto out;
310     return buffer;
311
312   out:
313     return NULL;
314 }
315
316 int read_setting_i(void *handle, char const *key, int defvalue) {
317     int fd;
318     Handle h;
319     int value;
320     Str255 pkey;
321
322     if (handle == NULL) goto out;
323     fd = *(int *)handle;
324     UseResFile(fd);
325     if (ResError() != noErr) goto out;
326     c2pstrcpy(pkey, key);
327     h = Get1NamedResource(FOUR_CHAR_CODE('Int '), pkey);
328     if (h == NULL) goto out;
329     value = *(int *)*h;
330     ReleaseResource(h);
331     if (ResError() != noErr) goto out;
332     return value;
333
334   out:
335     return defvalue;
336 }
337
338 int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
339 {
340     char *settingname;
341     FontSpec ret;
342     char tmp[256];
343
344     if (!read_setting_s(handle, name, tmp, sizeof(tmp)))
345         return 0;
346     c2pstrcpy(ret.name, tmp);
347     settingname = dupcat(name, "Face", NULL);
348     ret.face = read_setting_i(handle, settingname, 0);
349     sfree(settingname);
350     settingname = dupcat(name, "Height", NULL);
351     ret.size = read_setting_i(handle, settingname, 0);
352     sfree(settingname);
353     if (ret.size == 0) return 0;
354     *result = ret;
355     return 1;
356 }
357
358 void write_setting_fontspec(void *handle, const char *name, FontSpec font)
359 {
360     char *settingname;
361     char tmp[256];
362
363     p2cstrcpy(tmp, font.name);
364     write_setting_s(handle, name, tmp);
365     settingname = dupcat(name, "Face", NULL);
366     write_setting_i(handle, settingname, font.face);
367     sfree(settingname);
368     settingname = dupcat(name, "Size", NULL);
369     write_setting_i(handle, settingname, font.size);
370     sfree(settingname);
371 }
372
373 int read_setting_filename(void *handle, const char *key, Filename *result)
374 {
375     int fd;
376     AliasHandle h;
377     Boolean changed;
378     OSErr err;
379     Str255 pkey;
380
381     if (handle == NULL) goto out;
382     fd = *(int *)handle;
383     UseResFile(fd);
384     if (ResError() != noErr) goto out;
385     c2pstrcpy(pkey, key);
386     h = (AliasHandle)Get1NamedResource(rAliasType, pkey);
387     if (h == NULL) goto out;
388     if ((*h)->userType == 'pTTY' && (*h)->aliasSize == sizeof(**h))
389         memset(result, 0, sizeof(*result));
390     else {
391         err = ResolveAlias(NULL, h, &result->fss, &changed);
392         if (err != noErr && err != fnfErr) goto out;
393         if ((*h)->userType == 'pTTY') {
394             long dirid;
395             StrFileName fname;
396
397             /* Tail of record is pascal string contaning leafname */
398             if (FSpGetDirID(&result->fss, &dirid, FALSE) != noErr) goto out;
399             memcpy(fname, (char *)*h + (*h)->aliasSize,
400                    GetHandleSize((Handle)h) - (*h)->aliasSize);
401             err = FSMakeFSSpec(result->fss.vRefNum, dirid, fname,
402                                &result->fss);
403             if (err != noErr && err != fnfErr) goto out;
404         }
405     }
406     ReleaseResource((Handle)h);
407     if (ResError() != noErr) goto out;
408     return 1;
409
410   out:
411     return 0;
412 }
413
414 void write_setting_filename(void *handle, const char *key, Filename fn)
415 {
416     int fd = *(int *)handle;
417     AliasHandle h;
418     int id;
419     OSErr error;
420     Str255 pkey;
421
422     UseResFile(fd);
423     if (ResError() != noErr)
424         fatalbox("Failed to open saved session (%d)", ResError());
425
426     if (filename_is_null(fn)) {
427         /* Generate a special "null" alias */
428         h = (AliasHandle)NewHandle(sizeof(**h));
429         if (h == NULL)
430             fatalbox("Failed to create fake alias");
431         (*h)->userType = 'pTTY';
432         (*h)->aliasSize = sizeof(**h);
433     } else {
434         error = NewAlias(NULL, &fn.fss, &h);
435         if (error == fnfErr) {
436             /*
437              * NewAlias can't create an alias for a nonexistent file.
438              * Create an alias for the directory, and record the
439              * filename as well.
440              */
441             FSSpec tmpfss;
442
443             FSMakeFSSpec(fn.fss.vRefNum, fn.fss.parID, NULL, &tmpfss);
444             error = NewAlias(NULL, &tmpfss, &h);
445             if (error != noErr)
446                 fatalbox("Failed to create alias");
447             (*h)->userType = 'pTTY';
448             SetHandleSize((Handle)h, (*h)->aliasSize + fn.fss.name[0] + 1);
449             if (MemError() != noErr)
450                 fatalbox("Failed to create alias");
451             memcpy((char *)*h + (*h)->aliasSize, fn.fss.name,
452                    fn.fss.name[0] + 1);
453         }
454         if (error != noErr)
455             fatalbox("Failed to create alias");
456     }
457     /* Put the data in a resource. */
458     id = Unique1ID(rAliasType);
459     if (ResError() != noErr)
460         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
461     c2pstrcpy(pkey, key);
462     AddResource((Handle)h, rAliasType, id, pkey);
463     if (ResError() != noErr)
464         fatalbox("Failed to add resource %s (%d)", key, ResError());
465 }
466
467 void close_settings_r(void *handle) {
468     int fd;
469
470     if (handle == NULL) return;
471     fd = *(int *)handle;
472     CloseResFile(fd);
473     if (ResError() != noErr)
474         fatalbox("Close of saved session failed (%d)", ResError());
475     sfree(handle);
476 }
477
478 void del_settings(char const *sessionname) {
479     OSErr error;
480     FSSpec sessfile;
481     short sessVRefNum;
482     long sessDirID;
483     Str255 psessionname;
484
485     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
486
487     c2pstrcpy(psessionname, sessionname);
488     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
489     if (error != noErr) goto out;
490
491     error = FSpDelete(&sessfile);
492     return;
493   out:
494     fatalbox("Delete session failed (%d)", error);
495 }
496
497 struct enum_settings_state {
498     short vRefNum;
499     long dirID;
500     int index;
501 };
502
503 void *enum_settings_start(void) {
504     OSErr error;
505     struct enum_settings_state *state;
506
507     state = safemalloc(sizeof(*state));
508     error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID);
509     if (error != noErr) {
510         safefree(state);
511         return NULL;
512     }
513     state->index = 1;
514     return state;
515 }
516
517 char *enum_settings_next(void *handle, char *buffer, int buflen) {
518     struct enum_settings_state *e = handle;
519     CInfoPBRec pb;
520     OSErr error = noErr;
521     Str255 name;
522
523     if (e == NULL) return NULL;
524     do {
525         pb.hFileInfo.ioNamePtr = name;
526         pb.hFileInfo.ioVRefNum = e->vRefNum;
527         pb.hFileInfo.ioDirID = e->dirID;
528         pb.hFileInfo.ioFDirIndex = e->index++;
529         error = PBGetCatInfoSync(&pb);
530         if (error != noErr) return NULL;
531     } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 &&
532                pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR &&
533                pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE &&
534                name[0] < buflen));
535
536     p2cstrcpy(buffer, name);
537     return buffer;
538 }
539
540 void enum_settings_finish(void *handle) {
541
542     safefree(handle);
543 }
544
545 #define SEED_SIZE 512
546
547 void read_random_seed(noise_consumer_t consumer)
548 {
549     short puttyVRefNum;
550     long puttyDirID;
551     OSErr error;
552     char buf[SEED_SIZE];
553     short refnum;
554     long count = SEED_SIZE;
555
556     if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
557         return;
558     if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm,
559                 &refnum) != noErr)
560         return;
561     error = FSRead(refnum, &count, buf);
562     if (error != noErr && error != eofErr)
563         return;
564     (*consumer)(buf, count);
565     FSClose(refnum);
566 }
567
568 /*
569  * We don't bother with the usual FSpExchangeFiles dance here because
570  * it doesn't really matter if the old random seed gets lost.
571  */
572 void write_random_seed(void *data, int len)
573 {
574     short puttyVRefNum;
575     long puttyDirID;
576     OSErr error;
577     FSSpec dstfile;
578     short refnum;
579     long count = len;
580
581     if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
582         return;
583
584     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed",
585                          &dstfile);
586     if (error == fnfErr) {
587         /* Set up standard resources */
588         FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman);
589         refnum = FSpOpenResFile(&dstfile, fsWrPerm);
590         if (ResError() == noErr) {
591             copy_resource('STR ', -16397);
592             CloseResFile(refnum);
593         }
594     } else if (error != noErr) return;
595
596     if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
597     FSWrite(refnum, &count, data);
598     FSClose(refnum);
599
600     return;
601 }
602
603 /*
604  * Emacs magic:
605  * Local Variables:
606  * c-file-style: "simon"
607  * End:
608  */