We shouldn't fork off a utmp helper subprocess when we aren't setuid,
[u/mdw/putty] / conf.c
CommitLineData
4a693cfc 1/*
2 * conf.c: implementation of the internal storage format used for
3 * the configuration of a PuTTY session.
4 */
5
6#include <stdio.h>
7#include <stddef.h>
8#include <assert.h>
9
10#include "tree234.h"
11#include "putty.h"
12
13/*
14 * Enumeration of types used in keys and values.
15 */
16typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
17
18/*
19 * Arrays which allow us to look up the subkey and value types for a
20 * given primary key id.
21 */
22#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
23static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
24#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
25static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
26
27/*
28 * Configuration keys are primarily integers (big enum of all the
29 * different configurable options); some keys have string-designated
30 * subkeys, such as the list of environment variables (subkeys
31 * defined by the variable names); some have integer-designated
32 * subkeys (wordness, colours, preference lists).
33 */
34struct key {
35 int primary;
36 union {
37 int i;
38 char *s;
39 } secondary;
40};
41
42struct value {
43 union {
44 int intval;
45 char *stringval;
46 Filename fileval;
47 FontSpec fontval;
48 } u;
49};
50
51struct conf_entry {
52 struct key key;
53 struct value value;
54};
55
56struct conf_tag {
57 tree234 *tree;
58};
59
60/*
61 * Because 'struct key' is the first element in 'struct conf_entry',
62 * it's safe (guaranteed by the C standard) to cast arbitrarily back
63 * and forth between the two types. Therefore, we only need one
64 * comparison function, which can double as a main sort function for
65 * the tree (comparing two conf_entry structures with each other)
66 * and a search function (looking up an externally supplied key).
67 */
68static int conf_cmp(void *av, void *bv)
69{
70 struct key *a = (struct key *)av;
71 struct key *b = (struct key *)bv;
72
73 if (a->primary < b->primary)
74 return -1;
75 else if (a->primary > b->primary)
76 return +1;
77 switch (subkeytypes[a->primary]) {
78 case TYPE_INT:
79 if (a->secondary.i < b->secondary.i)
80 return -1;
81 else if (a->secondary.i > b->secondary.i)
82 return +1;
83 return 0;
84 case TYPE_STR:
85 return strcmp(a->secondary.s, b->secondary.s);
86 default:
87 return 0;
88 }
89}
90
91/*
92 * Free any dynamic data items pointed to by a 'struct key'. We
93 * don't free the structure itself, since it's probably part of a
94 * larger allocated block.
95 */
96static void free_key(struct key *key)
97{
98 if (subkeytypes[key->primary] == TYPE_STR)
99 sfree(key->secondary.s);
100}
101
102/*
103 * Copy a 'struct key' into another one, copying its dynamic data
104 * if necessary.
105 */
106static void copy_key(struct key *to, struct key *from)
107{
108 to->primary = from->primary;
109 switch (subkeytypes[to->primary]) {
110 case TYPE_INT:
111 to->secondary.i = from->secondary.i;
112 break;
113 case TYPE_STR:
114 to->secondary.s = dupstr(from->secondary.s);
115 break;
116 }
117}
118
119/*
120 * Free any dynamic data items pointed to by a 'struct value'. We
121 * don't free the value itself, since it's probably part of a larger
122 * allocated block.
123 */
124static void free_value(struct value *val, int type)
125{
126 if (type == TYPE_STR)
127 sfree(val->u.stringval);
128}
129
130/*
131 * Copy a 'struct value' into another one, copying its dynamic data
132 * if necessary.
133 */
134static void copy_value(struct value *to, struct value *from, int type)
135{
136 switch (type) {
137 case TYPE_INT:
138 to->u.intval = from->u.intval;
139 break;
140 case TYPE_STR:
141 to->u.stringval = dupstr(from->u.stringval);
142 break;
143 case TYPE_FILENAME:
144 to->u.fileval = from->u.fileval;
145 break;
146 case TYPE_FONT:
147 to->u.fontval = from->u.fontval;
148 break;
149 }
150}
151
152/*
153 * Free an entire 'struct conf_entry' and its dynamic data.
154 */
155static void free_entry(struct conf_entry *entry)
156{
157 free_key(&entry->key);
158 free_value(&entry->value, valuetypes[entry->key.primary]);
159 sfree(entry);
160}
161
162Conf *conf_new(void)
163{
164 Conf *conf = snew(struct conf_tag);
165
166 conf->tree = newtree234(conf_cmp);
167
168 return conf;
169}
170
171static void conf_clear(Conf *conf)
172{
173 struct conf_entry *entry;
174
175 while ((entry = delpos234(conf->tree, 0)) != NULL)
176 free_entry(entry);
177}
178
179void conf_free(Conf *conf)
180{
181 conf_clear(conf);
182 freetree234(conf->tree);
183 sfree(conf);
184}
185
186static void conf_insert(Conf *conf, struct conf_entry *entry)
187{
188 struct conf_entry *oldentry = add234(conf->tree, entry);
189 if (oldentry && oldentry != entry) {
190 del234(conf->tree, oldentry);
191 free_entry(oldentry);
192 oldentry = add234(conf->tree, entry);
193 assert(oldentry == entry);
194 }
195}
196
197void conf_copy_into(Conf *newconf, Conf *oldconf)
198{
199 struct conf_entry *entry, *entry2;
200 int i;
201
cfce7239 202 conf_clear(newconf);
203
4a693cfc 204 for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
205 entry2 = snew(struct conf_entry);
206 copy_key(&entry2->key, &entry->key);
207 copy_value(&entry2->value, &entry->value,
208 valuetypes[entry->key.primary]);
209 add234(newconf->tree, entry2);
210 }
211}
212
213Conf *conf_copy(Conf *oldconf)
214{
215 Conf *newconf = conf_new();
216
217 conf_copy_into(newconf, oldconf);
218
219 return newconf;
220}
221
222int conf_get_int(Conf *conf, int primary)
223{
224 struct key key;
225 struct conf_entry *entry;
226
227 assert(subkeytypes[primary] == TYPE_NONE);
228 assert(valuetypes[primary] == TYPE_INT);
229 key.primary = primary;
230 entry = find234(conf->tree, &key, NULL);
231 assert(entry);
232 return entry->value.u.intval;
233}
234
235int conf_get_int_int(Conf *conf, int primary, int secondary)
236{
237 struct key key;
238 struct conf_entry *entry;
239
240 assert(subkeytypes[primary] == TYPE_INT);
241 assert(valuetypes[primary] == TYPE_INT);
242 key.primary = primary;
243 key.secondary.i = secondary;
244 entry = find234(conf->tree, &key, NULL);
245 assert(entry);
246 return entry->value.u.intval;
247}
248
249char *conf_get_str(Conf *conf, int primary)
250{
251 struct key key;
252 struct conf_entry *entry;
253
254 assert(subkeytypes[primary] == TYPE_NONE);
255 assert(valuetypes[primary] == TYPE_STR);
256 key.primary = primary;
257 entry = find234(conf->tree, &key, NULL);
258 assert(entry);
259 return entry->value.u.stringval;
260}
261
262char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
263{
264 struct key key;
265 struct conf_entry *entry;
266
267 assert(subkeytypes[primary] == TYPE_STR);
268 assert(valuetypes[primary] == TYPE_STR);
269 key.primary = primary;
270 key.secondary.s = (char *)secondary;
271 entry = find234(conf->tree, &key, NULL);
272 return entry ? entry->value.u.stringval : NULL;
273}
274
275char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
276{
277 char *ret = conf_get_str_str_opt(conf, primary, secondary);
278 assert(ret);
279 return ret;
280}
281
282char *conf_get_str_strs(Conf *conf, int primary,
283 char *subkeyin, char **subkeyout)
284{
285 struct key key;
286 struct conf_entry *entry;
287
288 assert(subkeytypes[primary] == TYPE_STR);
289 assert(valuetypes[primary] == TYPE_STR);
290 key.primary = primary;
291 if (subkeyin) {
292 key.secondary.s = subkeyin;
293 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
294 } else {
295 key.secondary.s = "";
296 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
297 }
298 if (!entry || entry->key.primary != primary)
299 return NULL;
300 *subkeyout = entry->key.secondary.s;
301 return entry->value.u.stringval;
302}
303
304char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
305{
306 struct key key;
307 struct conf_entry *entry;
308 int index;
309
310 assert(subkeytypes[primary] == TYPE_STR);
311 assert(valuetypes[primary] == TYPE_STR);
312 key.primary = primary;
313 key.secondary.s = "";
314 entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
315 if (!entry || entry->key.primary != primary)
316 return NULL;
317 entry = index234(conf->tree, index + n);
318 if (!entry || entry->key.primary != primary)
319 return NULL;
320 return entry->key.secondary.s;
321}
322
323Filename *conf_get_filename(Conf *conf, int primary)
324{
325 struct key key;
326 struct conf_entry *entry;
327
328 assert(subkeytypes[primary] == TYPE_NONE);
329 assert(valuetypes[primary] == TYPE_FILENAME);
330 key.primary = primary;
331 entry = find234(conf->tree, &key, NULL);
332 assert(entry);
333 return &entry->value.u.fileval;
334}
335
336FontSpec *conf_get_fontspec(Conf *conf, int primary)
337{
338 struct key key;
339 struct conf_entry *entry;
340
341 assert(subkeytypes[primary] == TYPE_NONE);
342 assert(valuetypes[primary] == TYPE_FONT);
343 key.primary = primary;
344 entry = find234(conf->tree, &key, NULL);
345 assert(entry);
346 return &entry->value.u.fontval;
347}
348
349void conf_set_int(Conf *conf, int primary, int value)
350{
351 struct conf_entry *entry = snew(struct conf_entry);
352
353 assert(subkeytypes[primary] == TYPE_NONE);
354 assert(valuetypes[primary] == TYPE_INT);
355 entry->key.primary = primary;
356 entry->value.u.intval = value;
357 conf_insert(conf, entry);
358}
359
360void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
361{
362 struct conf_entry *entry = snew(struct conf_entry);
363
364 assert(subkeytypes[primary] == TYPE_INT);
365 assert(valuetypes[primary] == TYPE_INT);
366 entry->key.primary = primary;
367 entry->key.secondary.i = secondary;
368 entry->value.u.intval = value;
369 conf_insert(conf, entry);
370}
371
372void conf_set_str(Conf *conf, int primary, const char *value)
373{
374 struct conf_entry *entry = snew(struct conf_entry);
375
376 assert(subkeytypes[primary] == TYPE_NONE);
377 assert(valuetypes[primary] == TYPE_STR);
378 entry->key.primary = primary;
379 entry->value.u.stringval = dupstr(value);
380 conf_insert(conf, entry);
381}
382
383void conf_set_str_str(Conf *conf, int primary, const char *secondary,
384 const char *value)
385{
386 struct conf_entry *entry = snew(struct conf_entry);
387
388 assert(subkeytypes[primary] == TYPE_STR);
389 assert(valuetypes[primary] == TYPE_STR);
390 entry->key.primary = primary;
391 entry->key.secondary.s = dupstr(secondary);
392 entry->value.u.stringval = dupstr(value);
393 conf_insert(conf, entry);
394}
395
396void conf_del_str_str(Conf *conf, int primary, const char *secondary)
397{
398 struct key key;
399 struct conf_entry *entry;
400
401 assert(subkeytypes[primary] == TYPE_STR);
402 assert(valuetypes[primary] == TYPE_STR);
403 key.primary = primary;
404 key.secondary.s = (char *)secondary;
405 entry = find234(conf->tree, &key, NULL);
406 if (entry) {
407 del234(conf->tree, entry);
408 free_entry(entry);
409 }
410 }
411
412void conf_set_filename(Conf *conf, int primary, const Filename *value)
413{
414 struct conf_entry *entry = snew(struct conf_entry);
415
416 assert(subkeytypes[primary] == TYPE_NONE);
417 assert(valuetypes[primary] == TYPE_FILENAME);
418 entry->key.primary = primary;
419 entry->value.u.fileval = *value; /* structure copy */
420 conf_insert(conf, entry);
421}
422
423void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
424{
425 struct conf_entry *entry = snew(struct conf_entry);
426
427 assert(subkeytypes[primary] == TYPE_NONE);
428 assert(valuetypes[primary] == TYPE_FONT);
429 entry->key.primary = primary;
430 entry->value.u.fontval = *value; /* structure copy */
431 conf_insert(conf, entry);
432}
433
434int conf_serialised_size(Conf *conf)
435{
436 int i;
437 struct conf_entry *entry;
438 int size = 0;
439
440 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
441 size += 4; /* primary key */
442 switch (subkeytypes[entry->key.primary]) {
443 case TYPE_INT:
444 size += 4;
445 break;
446 case TYPE_STR:
447 size += 1 + strlen(entry->key.secondary.s);
448 break;
449 }
450 switch (valuetypes[entry->key.primary]) {
451 case TYPE_INT:
452 size += 4;
453 break;
454 case TYPE_STR:
455 size += 1 + strlen(entry->value.u.stringval);
456 break;
457 case TYPE_FILENAME:
458 size += sizeof(entry->value.u.fileval);
459 break;
460 case TYPE_FONT:
461 size += sizeof(entry->value.u.fontval);
462 break;
463 }
464 }
465
466 size += 4; /* terminator value */
467
468 return size;
469}
470
471void conf_serialise(Conf *conf, void *vdata)
472{
473 unsigned char *data = (unsigned char *)vdata;
474 int i, len;
475 struct conf_entry *entry;
476
477 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
478 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
479 data += 4;
480
481 switch (subkeytypes[entry->key.primary]) {
482 case TYPE_INT:
483 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
484 data += 4;
485 break;
486 case TYPE_STR:
487 len = strlen(entry->key.secondary.s);
488 memcpy(data, entry->key.secondary.s, len);
489 data += len;
490 *data++ = 0;
491 break;
492 }
493 switch (valuetypes[entry->key.primary]) {
494 case TYPE_INT:
495 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
496 data += 4;
497 break;
498 case TYPE_STR:
499 len = strlen(entry->value.u.stringval);
500 memcpy(data, entry->value.u.stringval, len);
501 data += len;
502 *data++ = 0;
503 break;
504 case TYPE_FILENAME:
505 memcpy(data, &entry->value.u.fileval,
506 sizeof(entry->value.u.fileval));
507 data += sizeof(entry->value.u.fileval);
508 break;
509 case TYPE_FONT:
510 memcpy(data, &entry->value.u.fontval,
511 sizeof(entry->value.u.fontval));
512 data += sizeof(entry->value.u.fontval);
513 break;
514 }
515 }
516
517 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
518}
519
520int conf_deserialise(Conf *conf, void *vdata, int maxsize)
521{
522 unsigned char *data = (unsigned char *)vdata;
523 unsigned char *start = data;
524 struct conf_entry *entry;
525 int primary;
526 unsigned char *zero;
527
528 while (maxsize >= 4) {
529 primary = GET_32BIT_MSB_FIRST(data);
530 data += 4, maxsize -= 4;
531
532 if ((unsigned)primary >= N_CONFIG_OPTIONS)
533 break;
534
535 entry = snew(struct conf_entry);
536 entry->key.primary = primary;
537
538 switch (subkeytypes[entry->key.primary]) {
539 case TYPE_INT:
540 if (maxsize < 4) {
541 sfree(entry);
542 goto done;
543 }
544 entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
545 data += 4, maxsize -= 4;
546 break;
547 case TYPE_STR:
548 zero = memchr(data, 0, maxsize);
549 if (!zero) {
550 sfree(entry);
551 goto done;
552 }
553 entry->key.secondary.s = dupstr((char *)data);
554 maxsize -= (zero + 1 - data);
555 data = zero + 1;
556 break;
557 }
558
559 switch (valuetypes[entry->key.primary]) {
560 case TYPE_INT:
561 if (maxsize < 4) {
562 if (subkeytypes[entry->key.primary] == TYPE_STR)
563 sfree(entry->key.secondary.s);
564 sfree(entry);
565 goto done;
566 }
567 entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
568 data += 4, maxsize -= 4;
569 break;
570 case TYPE_STR:
571 zero = memchr(data, 0, maxsize);
572 if (!zero) {
573 if (subkeytypes[entry->key.primary] == TYPE_STR)
574 sfree(entry->key.secondary.s);
575 sfree(entry);
576 goto done;
577 }
578 entry->value.u.stringval = dupstr((char *)data);
579 maxsize -= (zero + 1 - data);
580 data = zero + 1;
581 break;
582 case TYPE_FILENAME:
583 if (maxsize < sizeof(entry->value.u.fileval)) {
584 if (subkeytypes[entry->key.primary] == TYPE_STR)
585 sfree(entry->key.secondary.s);
586 sfree(entry);
587 goto done;
588 }
589 memcpy(&entry->value.u.fileval, data,
590 sizeof(entry->value.u.fileval));
591 data += sizeof(entry->value.u.fileval);
592 maxsize -= sizeof(entry->value.u.fileval);
593 break;
594 case TYPE_FONT:
595 if (maxsize < sizeof(entry->value.u.fontval)) {
596 if (subkeytypes[entry->key.primary] == TYPE_STR)
597 sfree(entry->key.secondary.s);
598 sfree(entry);
599 goto done;
600 }
601 memcpy(&entry->value.u.fontval, data,
602 sizeof(entry->value.u.fontval));
603 data += sizeof(entry->value.u.fontval);
604 maxsize -= sizeof(entry->value.u.fontval);
605 break;
606 }
607 conf_insert(conf, entry);
608 }
609
610 done:
611 return (int)(data - start);
612}