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