]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - mac/macstore.c
Support for saving sessions on the Mac. This is slightly useful even in the
[PuTTY_svn.git] / mac / macstore.c
1 /* $Id: macstore.c,v 1.11 2003/01/18 20:09:21 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     fprintf(stderr, "getting resource %x, id %d\n", restype, resid);
103     h = GetResource(restype, resid);
104     if (h != NULL) {
105         GetResInfo(h, &resid, &restype, resname);
106         DetachResource(h);
107         AddResource(h, restype, resid, resname);
108         if (ResError() == noErr)
109             WriteResource(h);
110     }
111     fprintf(stderr, "ResError() == %d\n", ResError());
112     return ResError();
113 }
114
115 struct write_settings {
116     int fd;
117     FSSpec tmpfile;
118     FSSpec dstfile;
119 };
120
121 void *open_settings_w(char const *sessionname) {
122     short sessVRefNum;
123     long sessDirID;
124     OSErr error;
125     Str255 psessionname;
126     FSSpec dstfile;
127     
128     error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
129     if (error != noErr) return NULL;
130
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 = smalloc(sizeof *ws);
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
189     UseResFile(fd);
190     if (ResError() != noErr)
191         fatalbox("Failed to open saved session (%d)", ResError());
192
193     error = PtrToHand(value, &h, strlen(value));
194     if (error != noErr)
195         fatalbox("Failed to allocate memory");
196     /* Put the data in a resource. */
197     id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
198     if (ResError() != noErr)
199         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
200     addresource(h, FOUR_CHAR_CODE('TEXT'), id, key);
201     if (ResError() != noErr)
202         fatalbox("Failed to add resource %s (%d)", key, ResError());
203 }
204
205 void write_setting_i(void *handle, char const *key, int value) {
206     int fd = *(int *)handle;
207     Handle h;
208     int id;
209     OSErr error;
210
211     UseResFile(fd);
212     if (ResError() != noErr)
213         fatalbox("Failed to open saved session (%d)", ResError());
214
215     /* XXX assume all systems have the same "int" format */
216     error = PtrToHand(&value, &h, sizeof(int));
217     if (error != noErr)
218         fatalbox("Failed to allocate memory (%d)", error);
219
220     /* Put the data in a resource. */
221     id = Unique1ID(FOUR_CHAR_CODE('Int '));
222     if (ResError() != noErr)
223         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
224     addresource(h, FOUR_CHAR_CODE('Int '), id, key);
225     if (ResError() != noErr)
226         fatalbox("Failed to add resource %s (%d)", key, ResError());
227 }
228
229 void close_settings_w(void *handle) {
230     struct write_settings *ws = handle;
231     OSErr error;
232
233     CloseResFile(ws->fd);
234     if ((error = ResError()) != noErr)
235         goto out;
236     error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile);
237     if (error != noErr) goto out;
238     error = FSpDelete(&ws->tmpfile);
239     if (error != noErr) goto out;
240     return;
241
242   out:
243     fatalbox("Close of saved session failed (%d)", error);
244     safefree(handle);
245 }
246
247 void *open_settings_r(char const *sessionname)
248 {
249     short sessVRefNum;
250     long sessDirID;
251     FSSpec sessfile;
252     OSErr error;
253     Str255 psessionname;
254
255     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
256
257     c2pstrcpy(psessionname, sessionname);
258     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
259     if (error != noErr) goto out;
260     return open_settings_r_fsp(&sessfile);
261
262   out:
263     return NULL;
264 }
265
266 void *open_settings_r_fsp(FSSpec *sessfile)
267 {
268     OSErr error;
269     int fd;
270     int *handle;
271
272     fd = FSpOpenResFile(sessfile, fsRdPerm);
273     if (fd == 0) {error = ResError(); goto out;}
274
275     handle = safemalloc(sizeof *handle);
276     *handle = fd;
277     return handle;
278
279   out:
280     return NULL;
281 }
282
283 char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
284     int fd;
285     Handle h;
286     size_t len;
287
288     if (handle == NULL) goto out;
289     fd = *(int *)handle;
290     UseResFile(fd);
291     if (ResError() != noErr) goto out;
292     h = get1namedresource(FOUR_CHAR_CODE('TEXT'), key);
293     if (h == NULL) goto out;
294
295     len = GetHandleSize(h);
296     if (len + 1 > buflen) goto out;
297     memcpy(buffer, *h, len);
298     buffer[len] = '\0';
299
300     ReleaseResource(h);
301     if (ResError() != noErr) goto out;
302     return buffer;
303
304   out:
305     return NULL;
306 }
307
308 int read_setting_i(void *handle, char const *key, int defvalue) {
309     int fd;
310     Handle h;
311     int value;
312
313     if (handle == NULL) goto out;
314     fd = *(int *)handle;
315     UseResFile(fd);
316     if (ResError() != noErr) goto out;
317     h = get1namedresource(FOUR_CHAR_CODE('Int '), key);
318     if (h == NULL) goto out;
319     value = *(int *)*h;
320     ReleaseResource(h);
321     if (ResError() != noErr) goto out;
322     return value;
323
324   out:
325     return defvalue;
326 }
327
328 void close_settings_r(void *handle) {
329     int fd;
330
331     if (handle == NULL) return;
332     fd = *(int *)handle;
333     CloseResFile(fd);
334     if (ResError() != noErr)
335         fatalbox("Close of saved session failed (%d)", ResError());
336     safefree(handle);
337 }
338
339 void del_settings(char const *sessionname) {
340     OSErr error;
341     FSSpec sessfile;
342     short sessVRefNum;
343     long sessDirID;
344     Str255 psessionname;
345
346     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
347
348     c2pstrcpy(psessionname, sessionname);
349     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
350     if (error != noErr) goto out;
351
352     error = FSpDelete(&sessfile);
353     return;
354   out:
355     fatalbox("Delete session failed (%d)", error);
356 }
357
358 struct enum_settings_state {
359     short vRefNum;
360     long dirID;
361     int index;
362 };
363
364 void *enum_settings_start(void) {
365     OSErr error;
366     struct enum_settings_state *state;
367
368     state = safemalloc(sizeof(*state));
369     error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID);
370     if (error != noErr) {
371         safefree(state);
372         return NULL;
373     }
374     state->index = 1;
375     return state;
376 }
377
378 char *enum_settings_next(void *handle, char *buffer, int buflen) {
379     struct enum_settings_state *e = handle;
380     CInfoPBRec pb;
381     OSErr error = noErr;
382     Str255 name;
383
384     if (e == NULL) return NULL;
385     do {
386         pb.hFileInfo.ioNamePtr = name;
387         pb.hFileInfo.ioVRefNum = e->vRefNum;
388         pb.hFileInfo.ioDirID = e->dirID;
389         pb.hFileInfo.ioFDirIndex = e->index++;
390         error = PBGetCatInfoSync(&pb);
391         if (error != noErr) return NULL;
392     } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 &&
393                pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR &&
394                pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE &&
395                name[0] < buflen));
396
397     p2cstrcpy(buffer, name);
398     return buffer;
399 }
400
401 void enum_settings_finish(void *handle) {
402
403     safefree(handle);
404 }
405
406 #define SEED_SIZE 512
407
408 void read_random_seed(noise_consumer_t consumer)
409 {
410     short puttyVRefNum;
411     long puttyDirID;
412     OSErr error;
413     char buf[SEED_SIZE];
414     short refnum;
415     long count = SEED_SIZE;
416
417     if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
418         return;
419     if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm,
420                 &refnum) != noErr)
421         return;
422     error = FSRead(refnum, &count, buf);
423     if (error != noErr && error != eofErr)
424         return;
425     (*consumer)(buf, count);
426     FSClose(refnum);
427 }
428
429 /*
430  * We don't bother with the usual FSpExchangeFiles dance here because
431  * it doesn't really matter if the old random seed gets lost.
432  */
433 void write_random_seed(void *data, int len)
434 {
435     short puttyVRefNum;
436     long puttyDirID;
437     OSErr error;
438     FSSpec dstfile;
439     short refnum;
440     long count = len;
441
442     if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
443         return;
444
445     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed",
446                          &dstfile);
447     if (error == fnfErr) {
448         /* Set up standard resources */
449         FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman);
450         refnum = FSpOpenResFile(&dstfile, fsWrPerm);
451         if (ResError() == noErr) {
452             copy_resource('STR ', -16397);
453             CloseResFile(refnum);
454         }
455     } else if (error != noErr) return;
456
457     if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
458     FSWrite(refnum, &count, data);
459     FSClose(refnum);
460
461     return;
462 }
463
464 /*
465  * Emacs magic:
466  * Local Variables:
467  * c-file-style: "simon"
468  * End:
469  */