1/* 2 * $Id: daap-proto.c,v 1.1 2009-06-30 02:31:08 steven Exp $ 3 * Helper functions for formatting a daap message 4 * 5 * Copyright (C) 2003 Ron Pedde (ron@pedde.com) 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22#ifdef HAVE_CONFIG_H 23# include "config.h" 24#endif 25 26#include <errno.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <assert.h> 31 32#include "daap-proto.h" 33#include "err.h" 34#include "restart.h" 35 36/* Forwards */ 37DAAP_BLOCK *daap_get_new(void); 38DAAP_BLOCK *daap_add_formatted(DAAP_BLOCK *parent, char *tag, 39 int len, char *value); 40 41 42GZIP_STREAM *gzip_alloc(void) { 43 GZIP_STREAM *gz = malloc(sizeof(GZIP_STREAM)); 44 45 if(gz) { 46 memset(gz,0x00,sizeof(GZIP_STREAM)); 47 48 gz->in_size = GZIP_CHUNK; 49 gz->in = malloc(gz->in_size); 50 gz->bytes_in = 0; 51 gz->out = NULL; 52 gz->bytes_out = 0; 53 } 54 return gz; 55} 56 57ssize_t gzip_write(GZIP_STREAM *gz, void *buf, size_t size) { 58 int next_size; 59 char *in2; 60 int new_size; 61 62 if (gz->in == NULL) 63 return -1; 64 65 /* make sure input buffer is big enough */ 66 while (gz->in_size <= gz->bytes_in + size) { 67 new_size = 2*gz->in_size; 68 in2 = malloc(new_size); 69 if (in2 == NULL) { 70 DPRINTF(E_LOG,L_WS|L_DAAP,"out of memory for input buffer\n"); 71 free(gz->in); 72 gz->in = NULL; 73 gz->bytes_in = gz->in_size = 0; 74 return -1; 75 } 76 memcpy(in2, gz->in, gz->in_size); 77 free(gz->in); 78 gz->in = in2; 79 gz->in_size = new_size; 80 } 81 memcpy(gz->in + gz->bytes_in, buf, size); 82 gz->bytes_in += size; 83 return size; 84} 85 86int gzip_compress(GZIP_STREAM *gz) { 87 int out_size; 88 int status; 89 z_stream strm; 90 91 if (gz->in == NULL) 92 return -1; 93 94 out_size = (int)(1.05*gz->in_size) + 40; 95 gz->out = malloc(out_size); 96 if (gz->out == NULL) { 97 DPRINTF(E_INF,L_WS|L_DAAP,"out of memory for output buffer\n"); 98 gz->bytes_out = 0; 99 return -1; 100 } 101 102 memset((void*)&strm,0x00,sizeof(strm)); 103 104 strm.zalloc = Z_NULL; 105 strm.zfree = Z_NULL; 106 strm.opaque = Z_NULL; 107 strm.next_in = gz->in; 108 strm.avail_in = gz->bytes_in; 109 strm.next_out = gz->out; 110 strm.avail_out = out_size; 111 deflateInit2(&strm,GZIP_COMPRESSION_LEVEL,Z_DEFLATED,24,8,Z_DEFAULT_STRATEGY); 112 while ((status = deflate(&strm,Z_FINISH)) == Z_OK) 113 ; 114 if (status != Z_STREAM_END) { 115 DPRINTF(E_LOG,L_WS|L_DAAP,"unable to compress data: %s (%d)\n",strm.msg,status); 116 gz->bytes_out = 0; 117 return -1; 118 } 119 gz->bytes_out = strm.total_out; 120 deflateEnd(&strm); 121 122 return gz->bytes_out; 123} 124 125int gzip_close(GZIP_STREAM *gz, int fd) { 126 int bytes_written = gz->bytes_out; 127 if (r_write(fd,gz->out,gz->bytes_out) != gz->bytes_out) { 128 DPRINTF(E_LOG,L_WS|L_DAAP,"unable to write gzipped data\n"); 129 return -1; 130 } 131 if (gz->in != NULL) 132 free(gz->in); 133 if (gz->out != NULL) 134 free(gz->out); 135 free(gz); 136 return bytes_written; 137} 138 139 140 141 142 143 144/* 145 * daap_get_new 146 * 147 * Initialize a new daap struct 148 */ 149DAAP_BLOCK *daap_get_new(void) { 150 DAAP_BLOCK *pnew; 151 152 pnew=(DAAP_BLOCK*)malloc(sizeof(DAAP_BLOCK)); 153 if(!pnew) { 154 DPRINTF(E_WARN,L_DAAP,"Error mallocing a daap block\n"); 155 return NULL; 156 } 157 158 pnew->free=0; 159 pnew->value=NULL; 160 pnew->parent=NULL; 161 pnew->children=NULL; 162 pnew->last_child=NULL; 163 pnew->next=NULL; 164 165 return pnew; 166} 167 168/* 169 * daap_add_formatted 170 * 171 * add a block exactly as formatted in value. 172 * 173 * Note that value WILL be freed later in daap_free, so 174 * the value paramater must have been malloced 175 */ 176DAAP_BLOCK *daap_add_formatted(DAAP_BLOCK *parent, char *tag, 177 int size, char *value) { 178 DAAP_BLOCK *current,*last; 179 DAAP_BLOCK *pnew; 180 181 DPRINTF(E_SPAM,L_DAAP,"Adding daap tag %s\n",tag); 182 pnew = daap_get_new(); 183 if(!pnew) 184 return NULL; 185 186 pnew->reported_size=size; 187 pnew->parent=parent; 188 pnew->size=size; 189 memcpy(pnew->tag,tag,4); 190 191 if((size <= 4) && (size >= 0)) { /* we can just put it in svalue */ 192 memcpy(pnew->svalue,value,size); 193 pnew->free=0; 194 } else { 195 pnew->value=value; 196 pnew->free=1; 197 } 198 pnew->next=NULL; 199 200 /* walk the child list and put it at the end */ 201 if(parent) { 202 last = parent->last_child; 203 if (last) { 204 last->next = pnew; 205 } else { 206 parent->children = pnew; 207 } 208 parent->last_child = pnew; 209 } 210 211 /* now, walk the chain and update sizes */ 212 current=pnew->parent; 213 while(current) { 214 current->reported_size += (8 + pnew->reported_size); 215 current=current->parent; 216 } 217 218 return pnew; 219} 220 221/* 222 * daap_add_int 223 * 224 * Add an int block to a specific parent 225 */ 226DAAP_BLOCK *daap_add_long(DAAP_BLOCK *parent, char *tag, int v1, int v2) { 227 char *ivalue; 228 ivalue=(char*)malloc(8); 229 if(!ivalue) 230 return NULL; 231 232 ivalue[0]=(v1 >> 24) & 0xFF; 233 ivalue[1]=(v1 >> 16) & 0xFF; 234 ivalue[2]=(v1 >> 8) & 0xFF; 235 ivalue[3]=v1 & 0xFF; 236 237 ivalue[4]=(v2 >> 24) & 0xFF; 238 ivalue[5]=(v2 >> 16) & 0xFF; 239 ivalue[6]=(v2 >> 8) & 0xFF; 240 ivalue[7]=v2 & 0xFF; 241 242 return daap_add_formatted(parent,tag,8,ivalue); 243} 244 245/* 246 * daap_add_int 247 * 248 * Add an int block to a specific parent 249 */ 250DAAP_BLOCK *daap_add_int(DAAP_BLOCK *parent, char *tag, int value) { 251 char ivalue[4]; 252 253 ivalue[0]=(value >> 24) & 0xFF; 254 ivalue[1]=(value >> 16) & 0xFF; 255 ivalue[2]=(value >> 8) & 0xFF; 256 ivalue[3]=value & 0xFF; 257 258 return daap_add_formatted(parent,tag,4,ivalue); 259} 260 261/* 262 * daap_add_short 263 * 264 * Add an int block to a specific parent 265 */ 266DAAP_BLOCK *daap_add_short(DAAP_BLOCK *parent, char *tag, short int value) { 267 char ivalue[2]; 268 269 ivalue[0]=(value >> 8) & 0xFF; 270 ivalue[1]=value & 0xFF; 271 272 return daap_add_formatted(parent,tag,2,ivalue); 273} 274 275/* 276 * daap_add_char 277 * 278 * Add a single char 279 */ 280DAAP_BLOCK *daap_add_char(DAAP_BLOCK *parent, char *tag, char value) { 281 return daap_add_formatted(parent,tag,1,&value); 282} 283 284/* 285 * daap_add_data 286 * 287 * Add unstructured data to a specific parent 288 */ 289DAAP_BLOCK *daap_add_data(DAAP_BLOCK *parent, char *tag, 290 int len, void *value) { 291 void *pvalue; 292 293 if(len > 4) { 294 pvalue=(void*)malloc(len); 295 if(!pvalue) 296 return NULL; 297 298 memcpy(pvalue,value,len); 299 300 return daap_add_formatted(parent,tag,len,pvalue); 301 } 302 return daap_add_formatted(parent,tag,len,value); 303} 304 305/* 306 * daap_add_string 307 * 308 * Add a string element to a specific parent 309 */ 310DAAP_BLOCK *daap_add_string(DAAP_BLOCK *parent, char *tag, char *value) { 311 char *newvalue; 312 313 if(value) { 314 if(strlen(value) > 4) { 315 newvalue=strdup(value); 316 317 if(!newvalue) 318 return NULL; 319 320 return daap_add_formatted(parent,tag,strlen(newvalue),newvalue); 321 } 322 return daap_add_formatted(parent,tag,strlen(value),value); 323 } 324 return daap_add_formatted(parent,tag,0,""); 325} 326 327/* 328 * daap_add_empty 329 * 330 * add a tag whose only value is to act as an aggregator 331 */ 332DAAP_BLOCK *daap_add_empty(DAAP_BLOCK *parent, char *tag) { 333 return daap_add_formatted(parent,tag,0,NULL); 334} 335 336/* 337 * daap_serialmem 338 * 339 * Serialize a daap tree to a fd; 340 */ 341int daap_serialize(DAAP_BLOCK *root, int fd, GZIP_STREAM *gz) { 342 char size[4]; 343 344 while(root) { 345 if (gz == NULL) 346 r_write(fd,root->tag,4); 347 else 348 gzip_write(gz,root->tag,4); 349 350 size[0] = (root->reported_size >> 24) & 0xFF; 351 size[1] = (root->reported_size >> 16) & 0xFF; 352 size[2] = (root->reported_size >> 8 ) & 0xFF; 353 size[3] = (root->reported_size) & 0xFF; 354 355 if (gz == NULL) 356 r_write(fd,&size,4); 357 else 358 gzip_write(gz,&size,4); 359 360 if(root->size) { 361 if(root->free) { 362 if (gz == NULL) { 363 r_write(fd,root->value,root->size); 364 } 365 else { 366 gzip_write(gz,root->value,root->size); 367 } 368 } 369 else { 370 if (gz == NULL) { 371 r_write(fd,root->svalue,root->size); 372 } 373 else { 374 gzip_write(gz,root->svalue,root->size); 375 } 376 } 377 } 378 379 if(root->children) { 380 if(daap_serialize(root->children,fd,gz)) 381 return -1; 382 } 383 384 root=root->next; 385 } 386 387 return 0; 388} 389 390/* 391 * daap_remove 392 * 393 * remove a node from it's parent node and release it 394 */ 395void daap_remove(DAAP_BLOCK* node) 396{ 397 DAAP_BLOCK* parent = node->parent; 398 399 if(0 != parent) 400 { 401 DAAP_BLOCK** ptr = &parent->children; 402 403 while(*ptr && *ptr != node) 404 ptr = &(**ptr).next; 405 406 assert(0 != *ptr); 407 408 // remove us from the chain 409 *ptr = node->next; 410 411 // update sizes in parent chain 412 for(parent = node->parent ; parent ; parent = parent->parent) 413 parent->reported_size -= (8 + node->reported_size); 414 415 // clear parent and next pointers so daap_free doesn't get ambitious 416 node->parent = 0; 417 node->next = 0; 418 } 419 420 daap_free(node); 421} 422 423/* 424 * find a child block of the parent node 425 */ 426DAAP_BLOCK *daap_find(DAAP_BLOCK *parent, char* tag) 427{ 428 for(parent = parent->children ; parent ; parent = parent->next) 429 if(!strncmp(parent->tag, tag, 4)) 430 break; 431 432 return parent; 433} 434 435/* 436 * daap_free 437 * 438 * Free an entire daap formatted block 439 */ 440void daap_free(DAAP_BLOCK *root) { 441 DAAP_BLOCK *pnext; 442 443 while(root) { 444 DPRINTF(E_SPAM,L_DAAP,"Freeing %c%c%c%c\n",root->tag[0],root->tag[1], 445 root->tag[2],root->tag[3]); 446 447 if((root->size) && (root->free)) 448 free(root->value); /* otherwise, static value */ 449 450 daap_free(root->children); 451 452 pnext=root->next; 453 free(root); 454 root=pnext; 455 } 456 return; 457} 458 459// search a block's children and change an integer value 460int daap_set_int(DAAP_BLOCK* parent, char* tag, int value) 461{ 462 DAAP_BLOCK* child = daap_find(parent, tag); 463 464 if(0 == child || child->size != sizeof(int)) 465 return 0; 466 467 child->svalue[0]=(value >> 24) & 0xFF; 468 child->svalue[1]=(value >> 16) & 0xFF; 469 child->svalue[2]=(value >> 8) & 0xFF; 470 child->svalue[3]=value & 0xFF; 471 472 return 1; 473} 474 475