66e82014e33d41155cc6d14357cfed1d077e3df1
[u/mdw/putty] / mac / macstore.c
1 /* $Id: macstore.c,v 1.10 2003/01/18 16:10: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 <string.h>
15
16 #include "putty.h"
17 #include "storage.h"
18 #include "mac.h"
19 #include "macresid.h"
20
21 #define PUTTY_CREATOR FOUR_CHAR_CODE('pTTY')
22 #define INTERNAL_CREATOR FOUR_CHAR_CODE('pTTI')
23 #define SESS_TYPE FOUR_CHAR_CODE('Sess')
24 #define SEED_TYPE FOUR_CHAR_CODE('Seed')
25
26
27 OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit);
28
29 /*
30 * We store each session as a file in the "PuTTY" sub-directory of the
31 * preferences folder. Each (key,value) pair is stored as a resource.
32 */
33
34 OSErr get_putty_dir(Boolean makeit, short *pVRefNum, long *pDirID)
35 {
36 OSErr error = noErr;
37 short prefVRefNum;
38 FSSpec puttydir;
39 long prefDirID, puttyDirID;
40
41 error = FindFolder(kOnSystemDisk, kPreferencesFolderType, makeit,
42 &prefVRefNum, &prefDirID);
43 if (error != noErr) goto out;
44
45 error = FSMakeFSSpec(prefVRefNum, prefDirID, "\pPuTTY", &puttydir);
46 if (error != noErr && error != fnfErr) goto out;
47 error = FSpGetDirID(&puttydir, &puttyDirID, makeit);
48 if (error != noErr) goto out;
49
50 *pVRefNum = prefVRefNum;
51 *pDirID = puttyDirID;
52
53 out:
54 return error;
55 }
56
57 OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) {
58 OSErr error = noErr;
59 short puttyVRefNum;
60 FSSpec sessdir;
61 long puttyDirID, sessDirID;
62
63 error = get_putty_dir(makeit, &puttyVRefNum, &puttyDirID);
64 if (error != noErr) goto out;
65 error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSaved Sessions",
66 &sessdir);
67 if (error != noErr && error != fnfErr) goto out;
68 error = FSpGetDirID(&sessdir, &sessDirID, makeit);
69 if (error != noErr) goto out;
70
71 *pVRefNum = puttyVRefNum;
72 *pDirID = sessDirID;
73
74 out:
75 return error;
76 }
77
78 OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit) {
79 CInfoPBRec pb;
80 OSErr error = noErr;
81
82 pb.dirInfo.ioNamePtr = f->name;
83 pb.dirInfo.ioVRefNum = f->vRefNum;
84 pb.dirInfo.ioDrDirID = f->parID;
85 pb.dirInfo.ioFDirIndex = 0;
86 error = PBGetCatInfoSync(&pb);
87 if (error == fnfErr && makeit)
88 return FSpDirCreate(f, smSystemScript, idp);
89 if (error != noErr) goto out;
90 if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) {
91 error = dirNFErr;
92 goto out;
93 }
94 *idp = pb.dirInfo.ioDrDirID;
95
96 out:
97 return error;
98 }
99
100 /* Copy a resource into the current resource file */
101 static OSErr copy_resource(ResType restype, short resid)
102 {
103 Handle h;
104 Str255 resname;
105
106 fprintf(stderr, "getting resource %x, id %d\n", restype, resid);
107 h = GetResource(restype, resid);
108 if (h != NULL) {
109 GetResInfo(h, &resid, &restype, resname);
110 DetachResource(h);
111 AddResource(h, restype, resid, resname);
112 if (ResError() == noErr)
113 WriteResource(h);
114 }
115 fprintf(stderr, "ResError() == %d\n", ResError());
116 return ResError();
117 }
118
119 struct write_settings {
120 int fd;
121 FSSpec tmpfile;
122 FSSpec dstfile;
123 };
124
125 void *open_settings_w(char const *sessionname) {
126 short sessVRefNum, tmpVRefNum;
127 long sessDirID, tmpDirID;
128 OSErr error;
129 Str255 psessionname;
130 struct write_settings *ws;
131
132 ws = safemalloc(sizeof *ws);
133 error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
134 if (error != noErr) goto out;
135
136 c2pstrcpy(psessionname, sessionname);
137 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &ws->dstfile);
138 if (error != noErr && error != fnfErr) goto out;
139 if (error == fnfErr) {
140 FSpCreateResFile(&ws->dstfile, PUTTY_CREATOR, SESS_TYPE,
141 smSystemScript);
142 if ((error = ResError()) != noErr) goto out;
143 }
144
145 /* Create a temporary file to save to first. */
146 error = FindFolder(sessVRefNum, kTemporaryFolderType, kCreateFolder,
147 &tmpVRefNum, &tmpDirID);
148 if (error != noErr) goto out;
149 error = FSMakeFSSpec(tmpVRefNum, tmpDirID, psessionname, &ws->tmpfile);
150 if (error != noErr && error != fnfErr) goto out;
151 if (error == noErr) {
152 error = FSpDelete(&ws->tmpfile);
153 if (error != noErr) goto out;
154 }
155 FSpCreateResFile(&ws->tmpfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
156 if ((error = ResError()) != noErr) goto out;
157
158 ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm);
159 if (ws->fd == -1) {error = ResError(); goto out;}
160
161 /* Set up standard resources. Doesn't matter if these fail. */
162 copy_resource('STR ', -16396);
163 copy_resource('TMPL', TMPL_Int);
164
165 return ws;
166
167 out:
168 safefree(ws);
169 fatalbox("Failed to open session for write (%d)", error);
170 }
171
172 void write_setting_s(void *handle, char const *key, char const *value) {
173 int fd = *(int *)handle;
174 Handle h;
175 int id;
176 OSErr error;
177
178 UseResFile(fd);
179 if (ResError() != noErr)
180 fatalbox("Failed to open saved session (%d)", ResError());
181
182 error = PtrToHand(value, &h, strlen(value));
183 if (error != noErr)
184 fatalbox("Failed to allocate memory");
185 /* Put the data in a resource. */
186 id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
187 if (ResError() != noErr)
188 fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
189 addresource(h, FOUR_CHAR_CODE('TEXT'), id, key);
190 if (ResError() != noErr)
191 fatalbox("Failed to add resource %s (%d)", key, ResError());
192 }
193
194 void write_setting_i(void *handle, char const *key, int value) {
195 int fd = *(int *)handle;
196 Handle h;
197 int id;
198 OSErr error;
199
200 UseResFile(fd);
201 if (ResError() != noErr)
202 fatalbox("Failed to open saved session (%d)", ResError());
203
204 /* XXX assume all systems have the same "int" format */
205 error = PtrToHand(&value, &h, sizeof(int));
206 if (error != noErr)
207 fatalbox("Failed to allocate memory (%d)", error);
208
209 /* Put the data in a resource. */
210 id = Unique1ID(FOUR_CHAR_CODE('Int '));
211 if (ResError() != noErr)
212 fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
213 addresource(h, FOUR_CHAR_CODE('Int '), id, key);
214 if (ResError() != noErr)
215 fatalbox("Failed to add resource %s (%d)", key, ResError());
216 }
217
218 void close_settings_w(void *handle) {
219 struct write_settings *ws = handle;
220 OSErr error;
221
222 CloseResFile(ws->fd);
223 if ((error = ResError()) != noErr)
224 goto out;
225 error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile);
226 if (error != noErr) goto out;
227 error = FSpDelete(&ws->tmpfile);
228 if (error != noErr) goto out;
229 return;
230
231 out:
232 fatalbox("Close of saved session failed (%d)", error);
233 safefree(handle);
234 }
235
236 void *open_settings_r(char const *sessionname)
237 {
238 short sessVRefNum;
239 long sessDirID;
240 FSSpec sessfile;
241 OSErr error;
242 Str255 psessionname;
243
244 error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
245
246 c2pstrcpy(psessionname, sessionname);
247 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
248 if (error != noErr) goto out;
249 return open_settings_r_fsp(&sessfile);
250
251 out:
252 return NULL;
253 }
254
255 void *open_settings_r_fsp(FSSpec *sessfile)
256 {
257 OSErr error;
258 int fd;
259 int *handle;
260
261 fd = FSpOpenResFile(sessfile, fsRdPerm);
262 if (fd == 0) {error = ResError(); goto out;}
263
264 handle = safemalloc(sizeof *handle);
265 *handle = fd;
266 return handle;
267
268 out:
269 return NULL;
270 }
271
272 char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
273 int fd;
274 Handle h;
275 size_t len;
276
277 if (handle == NULL) goto out;
278 fd = *(int *)handle;
279 UseResFile(fd);
280 if (ResError() != noErr) goto out;
281 h = get1namedresource(FOUR_CHAR_CODE('TEXT'), key);
282 if (h == NULL) goto out;
283
284 len = GetHandleSize(h);
285 if (len + 1 > buflen) goto out;
286 memcpy(buffer, *h, len);
287 buffer[len] = '\0';
288
289 ReleaseResource(h);
290 if (ResError() != noErr) goto out;
291 return buffer;
292
293 out:
294 return NULL;
295 }
296
297 int read_setting_i(void *handle, char const *key, int defvalue) {
298 int fd;
299 Handle h;
300 int value;
301
302 if (handle == NULL) goto out;
303 fd = *(int *)handle;
304 UseResFile(fd);
305 if (ResError() != noErr) goto out;
306 h = get1namedresource(FOUR_CHAR_CODE('Int '), key);
307 if (h == NULL) goto out;
308 value = *(int *)*h;
309 ReleaseResource(h);
310 if (ResError() != noErr) goto out;
311 return value;
312
313 out:
314 return defvalue;
315 }
316
317 void close_settings_r(void *handle) {
318 int fd;
319
320 if (handle == NULL) return;
321 fd = *(int *)handle;
322 CloseResFile(fd);
323 if (ResError() != noErr)
324 fatalbox("Close of saved session failed (%d)", ResError());
325 safefree(handle);
326 }
327
328 void del_settings(char const *sessionname) {
329 OSErr error;
330 FSSpec sessfile;
331 short sessVRefNum;
332 long sessDirID;
333 Str255 psessionname;
334
335 error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
336
337 c2pstrcpy(psessionname, sessionname);
338 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
339 if (error != noErr) goto out;
340
341 error = FSpDelete(&sessfile);
342 return;
343 out:
344 fatalbox("Delete session failed (%d)", error);
345 }
346
347 struct enum_settings_state {
348 short vRefNum;
349 long dirID;
350 int index;
351 };
352
353 void *enum_settings_start(void) {
354 OSErr error;
355 struct enum_settings_state *state;
356
357 state = safemalloc(sizeof(*state));
358 error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID);
359 if (error != noErr) {
360 safefree(state);
361 return NULL;
362 }
363 state->index = 1;
364 return state;
365 }
366
367 char *enum_settings_next(void *handle, char *buffer, int buflen) {
368 struct enum_settings_state *e = handle;
369 CInfoPBRec pb;
370 OSErr error = noErr;
371 Str255 name;
372
373 if (e == NULL) return NULL;
374 do {
375 pb.hFileInfo.ioNamePtr = name;
376 pb.hFileInfo.ioVRefNum = e->vRefNum;
377 pb.hFileInfo.ioDirID = e->dirID;
378 pb.hFileInfo.ioFDirIndex = e->index++;
379 error = PBGetCatInfoSync(&pb);
380 if (error != noErr) return NULL;
381 } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 &&
382 pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR &&
383 pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE &&
384 name[0] < buflen));
385
386 p2cstrcpy(buffer, name);
387 return buffer;
388 }
389
390 void enum_settings_finish(void *handle) {
391
392 safefree(handle);
393 }
394
395 #define SEED_SIZE 512
396
397 void read_random_seed(noise_consumer_t consumer)
398 {
399 short puttyVRefNum;
400 long puttyDirID;
401 OSErr error;
402 char buf[SEED_SIZE];
403 short refnum;
404 long count = SEED_SIZE;
405
406 if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
407 return;
408 if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm,
409 &refnum) != noErr)
410 return;
411 error = FSRead(refnum, &count, buf);
412 if (error != noErr && error != eofErr)
413 return;
414 (*consumer)(buf, count);
415 FSClose(refnum);
416 }
417
418 /*
419 * We don't bother with the usual FSpExchangeFiles dance here because
420 * it doesn't really matter if the old random seed gets lost.
421 */
422 void write_random_seed(void *data, int len)
423 {
424 short puttyVRefNum;
425 long puttyDirID;
426 OSErr error;
427 FSSpec dstfile;
428 short refnum;
429 long count = len;
430
431 if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
432 return;
433
434 error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed",
435 &dstfile);
436 if (error == fnfErr) {
437 /* Set up standard resources */
438 FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman);
439 refnum = FSpOpenResFile(&dstfile, fsWrPerm);
440 if (ResError() == noErr) {
441 copy_resource('STR ', -16397);
442 CloseResFile(refnum);
443 }
444 } else if (error != noErr) return;
445
446 if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
447 FSWrite(refnum, &count, data);
448 FSClose(refnum);
449
450 return;
451 }
452
453 /*
454 * Emacs magic:
455 * Local Variables:
456 * c-file-style: "simon"
457 * End:
458 */