AmendHub

Download

jcs

/

wallops

/

settings.c

 

(View History)

jcs   settings: Pushover fields can be blank, but if not, must be 30 Latest amendment: 135 on 2024-10-04

1 /*
2 * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <OSUtils.h>
18 #include <Files.h>
19 #include <Folders.h>
20 #include <GestaltEqu.h>
21 #include <Traps.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "settings.h"
28 #include "util.h"
29
30 enum {
31 SETTING_STRING,
32 SETTING_SHORT,
33 SETTING_USHORT,
34 SETTING_LONG,
35 SETTING_PASSWORD,
36 SETTING_BOOL
37 };
38
39 enum {
40 SETTING_FLAG_ALLOW_BLANK = (1 << 0)
41 };
42
43 struct setting {
44 char name[32];
45 short type;
46 long flags;
47 long min;
48 long max;
49 char sdefault[32];
50 short ditl_id;
51 unsigned long offset;
52 unsigned long size;
53 char password_storage[256];
54 };
55
56 struct setting connect_defs[] = {
57 { "Server Name", SETTING_STRING, 0, 1, 0,
58 DEFAULT_SERVER_NAME, CONNECT_SERVER_ID,
59 offsetof(struct settings, server),
60 member_size(struct settings, server) },
61 { "Server Port", SETTING_USHORT, 0, 1, 65535,
62 DEFAULT_PORT, CONNECT_PORT_ID,
63 offsetof(struct settings, port),
64 member_size(struct settings, port) },
65 { "Server Password", SETTING_PASSWORD, 0, 0, 0,
66 "", CONNECT_PASSWORD_ID,
67 offsetof(struct settings, password),
68 member_size(struct settings, password) },
69 { "Nickname", SETTING_STRING, 0, 1, 0,
70 "", CONNECT_NICK_ID,
71 offsetof(struct settings, nick),
72 member_size(struct settings, nick) },
73 { "Ident", SETTING_STRING, 0, 1, 0,
74 DEFAULT_IDENT, CONNECT_IDENT_ID,
75 offsetof(struct settings, ident),
76 member_size(struct settings, ident) },
77 { "Realname", SETTING_STRING, 0, 1, 0,
78 DEFAULT_REALNAME, CONNECT_REALNAME_ID,
79 offsetof(struct settings, realname),
80 member_size(struct settings, realname) },
81 { "Hide MOTD", SETTING_BOOL, 0, 0, 1,
82 DEFAULT_HIDE_MOTD, CONNECT_HIDE_MOTD_ID,
83 offsetof(struct settings, hide_motd),
84 member_size(struct settings, hide_motd) },
85 { "Channel", SETTING_STRING, 0, 0, 0,
86 DEFAULT_CHANNEL, CONNECT_CHANNEL_ID,
87 offsetof(struct settings, channel),
88 member_size(struct settings, channel) },
89 };
90
91 struct setting settings_defs[] = {
92 { "Pushover API Token", SETTING_STRING, SETTING_FLAG_ALLOW_BLANK,
93 30, 30,
94 "", SETTINGS_PUSHOVER_API_ID,
95 offsetof(struct settings, pushover_api_token),
96 member_size(struct settings, pushover_api_token) },
97 { "Pushover User Key", SETTING_STRING, SETTING_FLAG_ALLOW_BLANK,
98 30, 30,
99 "", SETTINGS_PUSHOVER_USER_ID,
100 offsetof(struct settings, pushover_user_key),
101 member_size(struct settings, pushover_user_key) },
102 { "Other Pushover Options", SETTING_STRING, 0, 0, 0,
103 "", SETTINGS_PUSHOVER_OPTS_ID,
104 offsetof(struct settings, pushover_options),
105 member_size(struct settings, pushover_options) },
106 };
107
108 void settings_find_prefs_folder(short *vrefnum, long *dirid);
109 bool settings_edit(struct setting *defs, size_t defs_count, short dlog_id,
110 bool use_defaults);
111
112 bool
113 settings_load(void)
114 {
115 HParamBlockRec pb = { 0 };
116 struct settings tsettings = { 0 };
117 char fn[] = SETTINGS_FILENAME;
118 char *res;
119 short error, vrefnum, prefrefnum;
120 long len, rlen, dirid;
121 Handle h;
122
123 GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
124
125 pb.ioParam.ioNamePtr = (StringPtr)&fn;
126 pb.ioParam.ioVRefNum = vrefnum;
127 pb.ioParam.ioPermssn = fsRdPerm;
128 pb.fileParam.ioDirID = dirid;
129 error = PBHOpen(&pb, false);
130 if (error) {
131 if (error != fnfErr)
132 panic("Failed reading preferences file %s: %d", PtoCstr(fn),
133 error);
134
135 return false;
136 }
137
138 rlen = len = sizeof(struct settings);
139 FSRead(pb.ioParam.ioRefNum, &rlen, &tsettings);
140
141 while (tsettings.version < SETTINGS_VERSION) {
142 switch (tsettings.version) {
143 default:
144 FSClose(pb.ioParam.ioRefNum);
145
146 if (ask("Unknown preferences version (%d), reset?",
147 tsettings.version)) {
148 return false;
149 }
150
151 ExitToShell();
152 }
153
154 tsettings.version++;
155 }
156
157 FSClose(pb.ioParam.ioRefNum);
158
159 settings = tsettings;
160 return true;
161 }
162
163 void
164 settings_save(struct settings *tsettings)
165 {
166 HParamBlockRec pb = { 0 };
167 char fn[] = SETTINGS_FILENAME;
168 short error, vrefnum, prefrefnum;
169 long dirid, len;
170 Handle h;
171
172 GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
173
174 pb.ioParam.ioNamePtr = (StringPtr)&fn;
175 pb.ioParam.ioVRefNum = vrefnum;
176 pb.fileParam.ioDirID = dirid;
177 error = PBHCreate(&pb, false);
178 if (error && error != dupFNErr)
179 goto create_failed;
180 if (!error) {
181 memset(&pb, 0, sizeof(pb));
182 pb.ioParam.ioNamePtr = (StringPtr)&fn;
183 pb.ioParam.ioVRefNum = vrefnum;
184 pb.fileParam.ioDirID = dirid;
185 if ((error = PBHGetFInfo(&pb, false)))
186 goto create_failed;
187
188 pb.fileParam.ioDirID = dirid; /* required after PBHGetFInfo */
189 pb.fileParam.ioFlFndrInfo.fdType = 'pref';
190 pb.fileParam.ioFlFndrInfo.fdCreator = SETTINGS_FILE_CREATOR;
191 if ((error = PBHSetFInfo(&pb, false)))
192 goto create_failed;
193
194 memset(&pb, 0, sizeof(pb));
195 pb.ioParam.ioVRefNum = vrefnum;
196 PBFlushVol(&pb, false);
197 }
198
199 memset(&pb, 0, sizeof(pb));
200 pb.ioParam.ioNamePtr = (StringPtr)&fn;
201 pb.ioParam.ioVRefNum = vrefnum;
202 pb.ioParam.ioPermssn = fsWrPerm;
203 pb.fileParam.ioDirID = dirid;
204 error = PBHOpen(&pb, false);
205 if (error)
206 goto create_failed;
207
208 len = sizeof(struct settings);
209 tsettings->version = SETTINGS_VERSION;
210 FSWrite(pb.ioParam.ioRefNum, &len, tsettings);
211 FSClose(pb.ioParam.ioRefNum);
212
213 return;
214
215 create_failed:
216 panic("Failed creating preferences file %s: %d", PtoCstr(fn), error);
217 }
218
219 bool
220 connect_dialog(bool use_defaults)
221 {
222 return settings_edit(connect_defs, nitems(connect_defs),
223 CONNECT_DLOG_ID, use_defaults);
224 }
225
226 bool
227 settings_dialog(bool use_defaults)
228 {
229 return settings_edit(settings_defs, nitems(settings_defs),
230 SETTINGS_DLOG_ID, use_defaults);
231 }
232
233 bool
234 settings_edit(struct setting *defs, size_t defs_count, short dlog_id,
235 bool use_defaults)
236 {
237 struct settings tsettings = settings;
238 struct setting *s;
239 DialogTHndl dlgh;
240 DialogPtr dlg;
241 Handle ihandle;
242 Rect irect;
243 size_t size, n, m, slen;
244 long lval;
245 bool bval, save;
246 short hit, itype, ret;
247 char *sdata, *strval;
248 Str255 stmp;
249
250 /* center dialog in screen */
251 dlgh = (DialogTHndl)xGetResource('DLOG', dlog_id);
252 HLock(dlgh);
253 center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left,
254 (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top,
255 true, &(**dlgh).boundsRect);
256 HUnlock(dlgh);
257
258 if ((dlg = GetNewDialog(dlog_id, nil, (WindowPtr)-1)) == NULL)
259 panic("Can't find settings DLOG %d", dlog_id);
260
261 for (n = 0; n < defs_count; n++) {
262 s = &defs[n];
263 sdata = (char *)&tsettings + s->offset;
264
265 GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
266
267 switch (s->type) {
268 case SETTING_PASSWORD:
269 PasswordDialogFieldFilterSetup(s->ditl_id,
270 (char *)s->password_storage, sizeof(s->password_storage));
271
272 strval = (use_defaults ? (char *)s->sdefault : sdata);
273 size = strlen(strval);
274 if (size >= sizeof(s->password_storage) - 1)
275 size = 0;
276
277 /* show a masked value */
278 stmp[0] = size;
279 for (m = 1; m <= size; m++)
280 stmp[m] = '•';
281 SetIText(ihandle, stmp);
282
283 /* but store the actual password */
284 memcpy(s->password_storage, strval, size);
285 s->password_storage[size] = '\0';
286 break;
287 case SETTING_STRING:
288 strval = (use_defaults ? s->sdefault : sdata);
289 memcpy(stmp, strval, sizeof(stmp));
290 stmp[sizeof(stmp) - 1] = '\0';
291 CtoPstr(stmp);
292 SetIText(ihandle, stmp);
293 break;
294 case SETTING_SHORT:
295 case SETTING_USHORT:
296 case SETTING_LONG:
297 if (use_defaults) {
298 memcpy(stmp, s->sdefault, sizeof(stmp));
299 stmp[sizeof(stmp) - 1] = '\0';
300 } else if (s->type == SETTING_SHORT) {
301 snprintf((char *)stmp, sizeof(stmp), "%d",
302 BYTES_TO_SHORT(sdata[0], sdata[1]));
303 } else if (s->type == SETTING_USHORT) {
304 snprintf((char *)stmp, sizeof(stmp), "%u",
305 BYTES_TO_SHORT(sdata[0], sdata[1]));
306 } else if (s->type == SETTING_LONG) {
307 snprintf((char *)stmp, sizeof(stmp), "%ld",
308 BYTES_TO_LONG(sdata[0], sdata[1], sdata[2], sdata[3]));
309 }
310 CtoPstr(stmp);
311 SetIText(ihandle, stmp);
312 break;
313 case SETTING_BOOL:
314 SetCtlValue(ihandle, (use_defaults ? s->sdefault[0] :
315 sdata[0]) == 1);
316 break;
317 default:
318 panic("Unknown setting type %d", s->type);
319 }
320 }
321
322 ret = false;
323 ShowWindow(dlg);
324 SelectWindow(dlg);
325
326 get_input:
327 ModalDialog(PasswordDialogFieldFilter, &hit);
328 switch (hit) {
329 case -1:
330 goto settings_done;
331 case OK:
332 goto verify;
333 default:
334 GetDItem(dlg, hit, &itype, &ihandle, &irect);
335 if (itype == (ctrlItem + chkCtrl))
336 SetCtlValue(ihandle, !GetCtlValue(ihandle));
337 goto get_input;
338 }
339
340 save = false;
341 verify:
342 for (n = 0; n < defs_count; n++) {
343 s = &defs[n];
344 sdata = (char *)&tsettings + s->offset;
345
346 GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
347
348 if (s->type == SETTING_PASSWORD) {
349 memcpy((char *)&stmp, s->password_storage, sizeof(stmp));
350 } else if (s->type == SETTING_BOOL) {
351 snprintf((char *)&stmp, sizeof(stmp), "%d",
352 GetCtlValue(ihandle));
353 } else {
354 GetIText(ihandle, stmp);
355 PtoCstr(stmp);
356 }
357
358 switch (s->type) {
359 case SETTING_STRING:
360 case SETTING_PASSWORD:
361 slen = strlen((char *)stmp);
362
363 if (slen == 0 && (s->flags & SETTING_FLAG_ALLOW_BLANK)) {
364 /* ok */
365 } else {
366 if (s->min && s->max && s->min == s->max &&
367 slen != s->min) {
368 warn("%s must be %ld character%s", s->name, s->min,
369 s->min == 1 ? "" : "s");
370 goto get_input;
371 }
372 if (s->min && slen < s->min) {
373 warn("%s is too short (minimum %ld)", s->name, s->min);
374 goto get_input;
375 }
376 if (s->max && slen > s->max) {
377 warn("%s is too long (maximum %ld)", s->name, s->max);
378 goto get_input;
379 }
380 }
381
382 if (save) {
383 memset(sdata, 0, s->size);
384 strlcpy(sdata, (char *)stmp, s->size);
385 }
386 break;
387 case SETTING_SHORT:
388 case SETTING_USHORT:
389 case SETTING_LONG:
390 lval = atol((char *)stmp);
391 if (lval < s->min) {
392 if (s->type == SETTING_USHORT)
393 warn("%s must be at least %ul", s->name, s->min);
394 else
395 warn("%s must be at least %ld", s->name, s->min);
396 goto get_input;
397 }
398 if (lval > s->max) {
399 if (s->type == SETTING_USHORT)
400 warn("%s must be less than %ul", s->name, s->max);
401 else
402 warn("%s must be less than %ld", s->name, s->max);
403 goto get_input;
404 }
405 if (save) {
406 if (s->type == SETTING_LONG) {
407 sdata[0] = (lval >> 24) & 0xff;
408 sdata[1] = (lval >> 16) & 0xff;
409 sdata[2] = (lval >> 8) & 0xff;
410 sdata[3] = lval & 0xff;
411 } else {
412 sdata[0] = (lval >> 8) & 0xff;
413 sdata[1] = lval & 0xff;
414 }
415 }
416 break;
417 case SETTING_BOOL:
418 if (save)
419 sdata[0] = (stmp[0] == '1');
420 break;
421 }
422 }
423
424 if (!save) {
425 save = true;
426 goto verify;
427 }
428
429 /* all validated ok and fields written */
430 settings_save(&tsettings);
431 settings = tsettings;
432 ret = true;
433
434 settings_done:
435 PasswordDialogFieldFinish();
436 DisposeDialog(dlg);
437 ReleaseResource(dlgh);
438
439 return ret;
440 }