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