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