source: filezilla/trunk/fuentes/src/putty/conf.c @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

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