1/***
2  This file is part of avahi.
3
4  avahi is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Lesser General Public License as
6  published by the Free Software Foundation; either version 2.1 of the
7  License, or (at your option) any later version.
8
9  avahi is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12  Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public
15  License along with avahi; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  USA.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <string.h>
25#include <stdarg.h>
26#include <assert.h>
27#include <stdio.h>
28#include <stdlib.h>
29
30#include "strlst.h"
31#include "malloc.h"
32#include "defs.h"
33
34AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size) {
35    AvahiStringList *n;
36
37    if (!(n = avahi_malloc(sizeof(AvahiStringList) + size)))
38        return NULL;
39
40    n->next = l;
41    n->size = size;
42
43    /* NUL terminate strings, just to make sure */
44    n->text[size] = 0;
45
46    return n;
47}
48
49AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t*text, size_t size) {
50    AvahiStringList *n;
51
52    assert(size == 0 || text);
53
54    if (!(n = avahi_string_list_add_anonymous(l, size)))
55        return NULL;
56
57    if (size > 0)
58        memcpy(n->text, text, size);
59
60    return n;
61}
62
63AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text) {
64    assert(text);
65
66    return avahi_string_list_add_arbitrary(l, (const uint8_t*) text, strlen(text));
67}
68
69int avahi_string_list_parse(const void* data, size_t size, AvahiStringList **ret) {
70    const uint8_t *c;
71    AvahiStringList *r = NULL;
72
73    assert(data);
74    assert(ret);
75
76    c = data;
77    while (size > 0) {
78        size_t k;
79
80        k = *(c++);
81        size--;
82
83        if (k > size)
84            goto fail; /* Overflow */
85
86        if (k > 0) { /* Ignore empty strings */
87            AvahiStringList *n;
88
89            if (!(n = avahi_string_list_add_arbitrary(r, c, k)))
90                goto fail; /* OOM */
91
92            r = n;
93        }
94
95        c += k;
96        size -= k;
97    }
98
99    *ret = r;
100
101    return 0;
102
103fail:
104    avahi_string_list_free(r);
105    return -1;
106}
107
108void avahi_string_list_free(AvahiStringList *l) {
109    AvahiStringList *n;
110
111    while (l) {
112        n = l->next;
113        avahi_free(l);
114        l = n;
115    }
116}
117
118AvahiStringList* avahi_string_list_reverse(AvahiStringList *l) {
119    AvahiStringList *r = NULL, *n;
120
121    while (l) {
122        n = l->next;
123        l->next = r;
124        r = l;
125        l = n;
126    }
127
128    return r;
129}
130
131char* avahi_string_list_to_string(AvahiStringList *l) {
132    AvahiStringList *n;
133    size_t s = 0;
134    char *t, *e;
135
136    for (n = l; n; n = n->next) {
137        if (n != l)
138            s ++;
139
140        s += n->size+2;
141    }
142
143    if (!(t = e = avahi_new(char, s+1)))
144        return NULL;
145
146    l = avahi_string_list_reverse(l);
147
148    for (n = l; n; n = n->next) {
149        if (n != l)
150            *(e++) = ' ';
151
152        *(e++) = '"';
153        strncpy(e, (char*) n->text, n->size);
154        e[n->size] = 0;
155        e = strchr(e, 0);
156        *(e++) = '"';
157
158        assert(e);
159    }
160
161    l = avahi_string_list_reverse(l);
162
163    *e = 0;
164
165    return t;
166}
167
168size_t avahi_string_list_serialize(AvahiStringList *l, void *data, size_t size) {
169    size_t used = 0;
170
171    if (data) {
172        AvahiStringList *n;
173        uint8_t *c;
174
175        l = avahi_string_list_reverse(l);
176        c = data;
177
178        for (n = l; size > 1 && n; n = n->next) {
179            size_t k;
180
181            if ((k = n->size) == 0)
182                /* Skip empty strings */
183                continue;
184
185            if (k > 255)
186                /* Truncate strings at 255 characters */
187                k = 255;
188
189            if (k > size-1)
190                /* Make sure this string fits in */
191                k = size-1;
192
193            *(c++) = (uint8_t) k;
194            memcpy(c, n->text, k);
195            c += k;
196
197            used += 1 + k;
198            size -= 1 + k;
199        }
200
201        l = avahi_string_list_reverse(l);
202
203        if (used == 0 && size > 0) {
204
205            /* Empty lists are treated specially. To comply with
206             * section 6.1 of the DNS-SD spec, we return a single
207             * empty string (i.e. a NUL byte)*/
208
209            *(uint8_t*) data = 0;
210            used = 1;
211        }
212
213    } else {
214        AvahiStringList *n;
215
216        for (n = l; n; n = n->next) {
217            size_t k;
218
219            if ((k = n->size) == 0)
220                continue;
221
222            if (k > 255)
223                k = 255;
224
225            used += 1+k;
226        }
227
228        if (used == 0)
229            used = 1;
230    }
231
232    return used;
233}
234
235int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b) {
236
237    for (;;) {
238        if (!a && !b)
239            return 1;
240
241        if (!a || !b)
242            return 0;
243
244        if (a->size != b->size)
245            return 0;
246
247        if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0)
248            return 0;
249
250        a = a->next;
251        b = b->next;
252    }
253}
254
255AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) {
256    va_list va;
257
258    va_start(va, r);
259    r = avahi_string_list_add_many_va(r, va);
260    va_end(va);
261
262    return r;
263}
264
265AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) {
266    const char *txt;
267
268    while ((txt = va_arg(va, const char*)))
269        r = avahi_string_list_add(r, txt);
270
271    return r;
272}
273
274AvahiStringList *avahi_string_list_new(const char *txt, ...) {
275    va_list va;
276    AvahiStringList *r = NULL;
277
278    if (txt) {
279        r = avahi_string_list_add(r, txt);
280
281        va_start(va, txt);
282        r = avahi_string_list_add_many_va(r, va);
283        va_end(va);
284    }
285
286    return r;
287}
288
289AvahiStringList *avahi_string_list_new_va(va_list va) {
290    return avahi_string_list_add_many_va(NULL, va);
291}
292
293AvahiStringList *avahi_string_list_copy(const AvahiStringList *l) {
294    AvahiStringList *r = NULL;
295
296    for (; l; l = l->next)
297        if (!(r = avahi_string_list_add_arbitrary(r, l->text, l->size))) {
298            avahi_string_list_free(r);
299            return NULL;
300        }
301
302    return avahi_string_list_reverse(r);
303}
304
305AvahiStringList *avahi_string_list_new_from_array(const char *array[], int length) {
306    AvahiStringList *r = NULL;
307    int i;
308
309    assert(array);
310
311    for (i = 0; length >= 0 ? i < length : !!array[i]; i++)
312        r = avahi_string_list_add(r, array[i]);
313
314    return r;
315}
316
317unsigned avahi_string_list_length(const AvahiStringList *l) {
318    unsigned n = 0;
319
320    for (; l; l = l->next)
321        n++;
322
323    return n;
324}
325
326AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va) {
327    size_t len = 80;
328    AvahiStringList *r;
329
330    assert(format);
331
332    if (!(r = avahi_malloc(sizeof(AvahiStringList) + len)))
333        return NULL;
334
335    for (;;) {
336        int n;
337        AvahiStringList *nr;
338        va_list va2;
339
340        va_copy(va2, va);
341        n = vsnprintf((char*) r->text, len, format, va2);
342        va_end(va2);
343
344        if (n >= 0 && n < (int) len)
345            break;
346
347        if (n >= 0)
348            len = n+1;
349        else
350            len *= 2;
351
352        if (!(nr = avahi_realloc(r, sizeof(AvahiStringList) + len))) {
353            avahi_free(r);
354            return NULL;
355        }
356
357        r = nr;
358    }
359
360    r->next = l;
361    r->size = strlen((char*) r->text);
362
363    return r;
364}
365
366AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) {
367    va_list va;
368
369    assert(format);
370
371    va_start(va, format);
372    l  = avahi_string_list_add_vprintf(l, format, va);
373    va_end(va);
374
375    return l;
376}
377
378AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key) {
379    size_t n;
380
381    assert(key);
382    n = strlen(key);
383
384    for (; l; l = l->next) {
385        if (strcasecmp((char*) l->text, key) == 0)
386            return l;
387
388        if (strncasecmp((char*) l->text, key, n) == 0 && l->text[n] == '=')
389            return l;
390    }
391
392    return NULL;
393}
394
395AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value) {
396    assert(key);
397
398    if (value)
399        return avahi_string_list_add_printf(l, "%s=%s", key, value);
400    else
401        return avahi_string_list_add(l, key);
402}
403
404AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size) {
405    size_t n;
406    assert(key);
407
408    if (!value)
409        return avahi_string_list_add(l, key);
410
411    n = strlen(key);
412
413    if (!(l = avahi_string_list_add_anonymous(l, n + 1 + size)))
414        return NULL;
415
416    memcpy(l->text, key, n);
417    l->text[n] = '=';
418    memcpy(l->text + n + 1, value, size);
419
420    return l;
421}
422
423int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size) {
424    char *e;
425
426    assert(l);
427
428    if (!(e = memchr(l->text, '=', l->size))) {
429
430        if (key)
431            if (!(*key = avahi_strdup((char*) l->text)))
432                return -1;
433
434        if (value)
435            *value = NULL;
436
437        if (size)
438            *size = 0;
439
440    } else {
441        size_t n;
442
443        if (key)
444            if (!(*key = avahi_strndup((char*) l->text, e - (char *) l->text)))
445                return -1;
446
447        e++; /* Advance after '=' */
448
449        n = l->size - (e - (char*) l->text);
450
451        if (value) {
452
453            if (!(*value = avahi_memdup(e, n+1))) {
454                if (key)
455                    avahi_free(*key);
456                return -1;
457            }
458
459            (*value)[n] = 0;
460        }
461
462        if (size)
463            *size = n;
464    }
465
466    return 0;
467}
468
469AvahiStringList *avahi_string_list_get_next(AvahiStringList *l) {
470    assert(l);
471    return l->next;
472}
473
474uint8_t *avahi_string_list_get_text(AvahiStringList *l) {
475    assert(l);
476    return l->text;
477}
478
479size_t avahi_string_list_get_size(AvahiStringList *l) {
480    assert(l);
481    return l->size;
482}
483
484uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l) {
485    AvahiStringList *f;
486    char *value = NULL, *end = NULL;
487    uint32_t ret;
488
489    if (!(f = avahi_string_list_find(l, AVAHI_SERVICE_COOKIE)))
490        return AVAHI_SERVICE_COOKIE_INVALID;
491
492    if (avahi_string_list_get_pair(f, NULL, &value, NULL) < 0 || !value)
493        return AVAHI_SERVICE_COOKIE_INVALID;
494
495    ret = (uint32_t) strtoll(value, &end, 0);
496
497    if (*value && end && *end != 0) {
498        avahi_free(value);
499        return AVAHI_SERVICE_COOKIE_INVALID;
500    }
501
502    avahi_free(value);
503
504    return ret;
505}
506