Updated Finnish translation
[rhythmbox.git] / daapsharing / rb-daap-structure.c
blob0684998d9a3e7b9229514eb8a2e233f34422ddfb
1 /*
2 * Header for DAAP (iTunes Music Sharing) structures
4 * Copyright (C) 2004,2005 Charles Schmidt <cschmidt2@emich.edu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <glib.h>
23 #include <glib-object.h>
24 #include <gobject/gvaluecollector.h>
25 #include "rb-daap-structure.h"
26 #include "rb-debug.h"
28 #include <gst/gstutils.h>
30 #include <string.h>
31 #include <stdarg.h>
33 #define MAKE_CONTENT_CODE(ch0, ch1, ch2, ch3) \
34 (( (gint32)(gchar)(ch0) | ( (gint32)(gchar)(ch1) << 8 ) | \
35 ( (gint32)(gchar)(ch2) << 16 ) | \
36 ( (gint32)(gchar)(ch3) << 24 ) ))
38 static const RBDAAPContentCodeDefinition cc_defs[] = {
39 {RB_DAAP_CC_MDCL, MAKE_CONTENT_CODE('m','d','c','l'), "dmap.dictionary", "mdcl", RB_DAAP_TYPE_CONTAINER},
40 {RB_DAAP_CC_MSTT, MAKE_CONTENT_CODE('m','s','t','t'), "dmap.status", "mstt", RB_DAAP_TYPE_INT},
41 {RB_DAAP_CC_MIID, MAKE_CONTENT_CODE('m','i','i','d'), "dmap.itemid", "miid", RB_DAAP_TYPE_INT},
42 {RB_DAAP_CC_MINM, MAKE_CONTENT_CODE('m','i','n','m'), "dmap.itemname", "minm", RB_DAAP_TYPE_STRING},
43 {RB_DAAP_CC_MIKD, MAKE_CONTENT_CODE('m','i','k','d'), "dmap.itemkind", "mikd", RB_DAAP_TYPE_BYTE},
44 {RB_DAAP_CC_MPER, MAKE_CONTENT_CODE('m','p','e','r'), "dmap.persistentid", "mper", RB_DAAP_TYPE_INT64},
45 {RB_DAAP_CC_MCON, MAKE_CONTENT_CODE('m','c','o','n'), "dmap.container", "mcon", RB_DAAP_TYPE_CONTAINER},
46 {RB_DAAP_CC_MCTI, MAKE_CONTENT_CODE('m','c','t','i'), "dmap.containeritemid", "mcti", RB_DAAP_TYPE_INT},
47 {RB_DAAP_CC_MPCO, MAKE_CONTENT_CODE('m','p','c','o'), "dmap.parentcontainerid", "mpco", RB_DAAP_TYPE_INT},
48 {RB_DAAP_CC_MSTS, MAKE_CONTENT_CODE('m','s','t','s'), "dmap.statusstring", "msts", RB_DAAP_TYPE_STRING},
49 {RB_DAAP_CC_MIMC, MAKE_CONTENT_CODE('m','i','m','c'), "dmap.itemcount", "mimc", RB_DAAP_TYPE_INT},
50 {RB_DAAP_CC_MCTC, MAKE_CONTENT_CODE('m','c','t','c'), "dmap.containercount", "mctc", RB_DAAP_TYPE_INT},
51 {RB_DAAP_CC_MRCO, MAKE_CONTENT_CODE('m','r','c','o'), "dmap.returnedcount", "mrco", RB_DAAP_TYPE_INT},
52 {RB_DAAP_CC_MTCO, MAKE_CONTENT_CODE('m','t','c','o'), "dmap.specifiedtotalcount", "mtco", RB_DAAP_TYPE_INT},
53 {RB_DAAP_CC_MLCL, MAKE_CONTENT_CODE('m','l','c','l'), "dmap.listing", "mlcl", RB_DAAP_TYPE_CONTAINER},
54 {RB_DAAP_CC_MLIT, MAKE_CONTENT_CODE('m','l','i','t'), "dmap.listingitem", "mlit", RB_DAAP_TYPE_CONTAINER},
55 {RB_DAAP_CC_MBCL, MAKE_CONTENT_CODE('m','b','c','l'), "dmap.bag", "mbcl", RB_DAAP_TYPE_CONTAINER},
56 {RB_DAAP_CC_MSRV, MAKE_CONTENT_CODE('m','s','r','v'), "dmap.serverinforesponse", "msrv", RB_DAAP_TYPE_CONTAINER},
57 {RB_DAAP_CC_MSAU, MAKE_CONTENT_CODE('m','s','a','u'), "dmap.authenticationmethod", "msau", RB_DAAP_TYPE_BYTE},
58 {RB_DAAP_CC_MSLR, MAKE_CONTENT_CODE('m','s','l','r'), "dmap.loginrequired", "mslr", RB_DAAP_TYPE_BYTE},
59 {RB_DAAP_CC_MPRO, MAKE_CONTENT_CODE('m','p','r','o'), "dmap.protocolversion", "mpro", RB_DAAP_TYPE_VERSION},
60 {RB_DAAP_CC_APRO, MAKE_CONTENT_CODE('a','p','r','o'), "daap.protocolversion", "apro", RB_DAAP_TYPE_VERSION},
61 {RB_DAAP_CC_MSAL, MAKE_CONTENT_CODE('m','s','a','l'), "dmap.supportsautologout", "msal", RB_DAAP_TYPE_BYTE},
62 {RB_DAAP_CC_MSUP, MAKE_CONTENT_CODE('m','s','u','p'), "dmap.supportsupdate", "msup", RB_DAAP_TYPE_BYTE},
63 {RB_DAAP_CC_MSPI, MAKE_CONTENT_CODE('m','s','p','i'), "dmap.supportspersistenids", "mspi", RB_DAAP_TYPE_BYTE},
64 {RB_DAAP_CC_MSEX, MAKE_CONTENT_CODE('m','s','e','x'), "dmap.supportsextensions", "msex", RB_DAAP_TYPE_BYTE},
65 {RB_DAAP_CC_MSBR, MAKE_CONTENT_CODE('m','s','b','r'), "dmap.supportsbrowse", "msbr", RB_DAAP_TYPE_BYTE},
66 {RB_DAAP_CC_MSQY, MAKE_CONTENT_CODE('m','s','q','y'), "dmap.supportsquery", "msqy", RB_DAAP_TYPE_BYTE},
67 {RB_DAAP_CC_MSIX, MAKE_CONTENT_CODE('m','s','i','x'), "dmap.supportsindex", "msix", RB_DAAP_TYPE_BYTE},
68 {RB_DAAP_CC_MSRS, MAKE_CONTENT_CODE('m','s','r','s'), "dmap.supportsresolve", "msrs", RB_DAAP_TYPE_BYTE},
69 {RB_DAAP_CC_MSTM, MAKE_CONTENT_CODE('m','s','t','m'), "dmap.timeoutinterval", "mstm", RB_DAAP_TYPE_INT},
70 {RB_DAAP_CC_MSDC, MAKE_CONTENT_CODE('m','s','d','c'), "dmap.databasescount", "msdc", RB_DAAP_TYPE_INT},
71 {RB_DAAP_CC_MCCR, MAKE_CONTENT_CODE('m','c','c','r'), "dmap.contentcodesresponse", "mccr", RB_DAAP_TYPE_CONTAINER},
72 {RB_DAAP_CC_MCNM, MAKE_CONTENT_CODE('m','c','n','m'), "dmap.contentcodesnumber", "mcnm", RB_DAAP_TYPE_INT},
73 {RB_DAAP_CC_MCNA, MAKE_CONTENT_CODE('m','c','n','a'), "dmap.contentcodesname", "mcna", RB_DAAP_TYPE_STRING},
74 {RB_DAAP_CC_MCTY, MAKE_CONTENT_CODE('m','c','t','y'), "dmap.contentcodestype", "mcty", RB_DAAP_TYPE_SHORT},
75 {RB_DAAP_CC_MLOG, MAKE_CONTENT_CODE('m','l','o','g'), "dmap.loginresponse", "mlog", RB_DAAP_TYPE_CONTAINER},
76 {RB_DAAP_CC_MLID, MAKE_CONTENT_CODE('m','l','i','d'), "dmap.sessionid", "mlid", RB_DAAP_TYPE_INT},
77 {RB_DAAP_CC_MUPD, MAKE_CONTENT_CODE('m','u','p','d'), "dmap.updateresponse", "mupd", RB_DAAP_TYPE_CONTAINER},
78 {RB_DAAP_CC_MUSR, MAKE_CONTENT_CODE('m','u','s','r'), "dmap.serverrevision", "musr", RB_DAAP_TYPE_INT},
79 {RB_DAAP_CC_MUTY, MAKE_CONTENT_CODE('m','u','t','y'), "dmap.updatetype", "muty", RB_DAAP_TYPE_BYTE},
80 {RB_DAAP_CC_MUDL, MAKE_CONTENT_CODE('m','u','d','l'), "dmap.deletedidlisting", "mudl", RB_DAAP_TYPE_CONTAINER},
81 {RB_DAAP_CC_AVDB, MAKE_CONTENT_CODE('a','v','d','b'), "daap.serverdatabases", "avdb", RB_DAAP_TYPE_CONTAINER},
82 {RB_DAAP_CC_ABRO, MAKE_CONTENT_CODE('a','b','r','o'), "daap.databasebrowse", "abro", RB_DAAP_TYPE_CONTAINER},
83 {RB_DAAP_CC_ABAL, MAKE_CONTENT_CODE('a','b','a','l'), "daap.browsealbumlisting", "abal", RB_DAAP_TYPE_CONTAINER},
84 {RB_DAAP_CC_ABAR, MAKE_CONTENT_CODE('a','b','a','r'), "daap.browseartistlisting", "abar", RB_DAAP_TYPE_CONTAINER},
85 {RB_DAAP_CC_ABCP, MAKE_CONTENT_CODE('a','b','c','p'), "daap.browsecomposerlisting", "abcp", RB_DAAP_TYPE_CONTAINER},
86 {RB_DAAP_CC_ABGN, MAKE_CONTENT_CODE('a','b','g','n'), "daap.browsegenrelisting", "abgn", RB_DAAP_TYPE_CONTAINER},
87 {RB_DAAP_CC_ADBS, MAKE_CONTENT_CODE('a','d','b','s'), "daap.returndatabasesongs", "adbs", RB_DAAP_TYPE_CONTAINER},
88 {RB_DAAP_CC_ASAL, MAKE_CONTENT_CODE('a','s','a','l'), "daap.songalbum", "asal", RB_DAAP_TYPE_STRING},
89 {RB_DAAP_CC_ASAR, MAKE_CONTENT_CODE('a','s','a','r'), "daap.songartist", "asar", RB_DAAP_TYPE_STRING},
90 {RB_DAAP_CC_ASBT, MAKE_CONTENT_CODE('a','s','b','t'), "daap.songsbeatsperminute", "asbt", RB_DAAP_TYPE_SHORT},
91 {RB_DAAP_CC_ASBR, MAKE_CONTENT_CODE('a','s','b','r'), "daap.songbitrate", "asbr", RB_DAAP_TYPE_SHORT},
92 {RB_DAAP_CC_ASCM, MAKE_CONTENT_CODE('a','s','c','m'), "daap.songcomment", "ascm", RB_DAAP_TYPE_STRING},
93 {RB_DAAP_CC_ASCO, MAKE_CONTENT_CODE('a','s','c','o'), "daap.songcompliation", "asco", RB_DAAP_TYPE_BYTE},
94 {RB_DAAP_CC_ASDA, MAKE_CONTENT_CODE('a','s','d','a'), "daap.songdateadded", "asda", RB_DAAP_TYPE_DATE},
95 {RB_DAAP_CC_ASDM, MAKE_CONTENT_CODE('a','s','d','m'), "daap.songdatemodified", "asdm", RB_DAAP_TYPE_DATE},
96 {RB_DAAP_CC_ASDC, MAKE_CONTENT_CODE('a','s','d','c'), "daap.songdisccount", "asdc", RB_DAAP_TYPE_SHORT},
97 {RB_DAAP_CC_ASDN, MAKE_CONTENT_CODE('a','s','d','n'), "daap.songdiscnumber", "asdn", RB_DAAP_TYPE_SHORT},
98 {RB_DAAP_CC_ASDB, MAKE_CONTENT_CODE('a','s','d','b'), "daap.songdisabled", "asdb", RB_DAAP_TYPE_BYTE},
99 {RB_DAAP_CC_ASEQ, MAKE_CONTENT_CODE('a','s','e','q'), "daap.songeqpreset", "aseq", RB_DAAP_TYPE_STRING},
100 {RB_DAAP_CC_ASFM, MAKE_CONTENT_CODE('a','s','f','m'), "daap.songformat", "asfm", RB_DAAP_TYPE_STRING},
101 {RB_DAAP_CC_ASGN, MAKE_CONTENT_CODE('a','s','g','n'), "daap.songgenre", "asgn", RB_DAAP_TYPE_STRING},
102 {RB_DAAP_CC_ASDT, MAKE_CONTENT_CODE('a','s','d','t'), "daap.songdescription", "asdt", RB_DAAP_TYPE_STRING},
103 {RB_DAAP_CC_ASRV, MAKE_CONTENT_CODE('a','s','r','v'), "daap.songrelativevolume", "asrv", RB_DAAP_TYPE_SIGNED_INT},
104 {RB_DAAP_CC_ASSR, MAKE_CONTENT_CODE('a','s','s','r'), "daap.songsamplerate", "assr", RB_DAAP_TYPE_INT},
105 {RB_DAAP_CC_ASSZ, MAKE_CONTENT_CODE('a','s','s','z'), "daap.songsize", "assz", RB_DAAP_TYPE_INT},
106 {RB_DAAP_CC_ASST, MAKE_CONTENT_CODE('a','s','s','t'), "daap.songstarttime", "asst", RB_DAAP_TYPE_INT},
107 {RB_DAAP_CC_ASSP, MAKE_CONTENT_CODE('a','s','s','p'), "daap.songstoptime", "assp", RB_DAAP_TYPE_INT},
108 {RB_DAAP_CC_ASTM, MAKE_CONTENT_CODE('a','s','t','m'), "daap.songtime", "astm", RB_DAAP_TYPE_INT},
109 {RB_DAAP_CC_ASTC, MAKE_CONTENT_CODE('a','s','t','c'), "daap.songtrackcount", "astc", RB_DAAP_TYPE_SHORT},
110 {RB_DAAP_CC_ASTN, MAKE_CONTENT_CODE('a','s','t','n'), "daap.songtracknumber", "astn", RB_DAAP_TYPE_SHORT},
111 {RB_DAAP_CC_ASUR, MAKE_CONTENT_CODE('a','s','u','r'), "daap.songuserrating", "asur", RB_DAAP_TYPE_BYTE},
112 {RB_DAAP_CC_ASYR, MAKE_CONTENT_CODE('a','s','y','r'), "daap.songyear", "asyr", RB_DAAP_TYPE_SHORT},
113 {RB_DAAP_CC_ASDK, MAKE_CONTENT_CODE('a','s','d','k'), "daap.songdatakind", "asdk", RB_DAAP_TYPE_BYTE},
114 {RB_DAAP_CC_ASUL, MAKE_CONTENT_CODE('a','s','u','l'), "daap.songdataurl", "asul", RB_DAAP_TYPE_STRING},
115 {RB_DAAP_CC_APLY, MAKE_CONTENT_CODE('a','p','l','y'), "daap.databaseplaylists", "aply", RB_DAAP_TYPE_CONTAINER},
116 {RB_DAAP_CC_ABPL, MAKE_CONTENT_CODE('a','b','p','l'), "daap.baseplaylist", "abpl", RB_DAAP_TYPE_BYTE},
117 {RB_DAAP_CC_APSO, MAKE_CONTENT_CODE('a','p','s','o'), "daap.playlistsongs", "apso", RB_DAAP_TYPE_CONTAINER},
118 {RB_DAAP_CC_PRSV, MAKE_CONTENT_CODE('p','r','s','v'), "daap.resolve", "prsv", RB_DAAP_TYPE_CONTAINER},
119 {RB_DAAP_CC_ARIF, MAKE_CONTENT_CODE('a','r','i','f'), "daap.resolveinfo", "arif", RB_DAAP_TYPE_CONTAINER},
120 {RB_DAAP_CC_AESV, MAKE_CONTENT_CODE('a','e','S','V'), "com.applie.itunes.music-sharing-version", "aesv", RB_DAAP_TYPE_INT},
121 {RB_DAAP_CC_MSAS, MAKE_CONTENT_CODE('m','s','a','s'), "daap.authentication.schemes", "msas", RB_DAAP_TYPE_BYTE},
122 {RB_DAAP_CC_AGRP, MAKE_CONTENT_CODE('a','g','r','p'), "daap.songgrouping", "agrp", RB_DAAP_TYPE_STRING},
123 {RB_DAAP_CC_ASCP, MAKE_CONTENT_CODE('a','s','c','p'), "daap.songcomposer", "ascp", RB_DAAP_TYPE_STRING}
126 const gchar *
127 rb_daap_content_code_name (RBDAAPContentCode code)
129 return cc_defs[code-1].name;
132 RBDAAPType
133 rb_daap_content_code_rb_daap_type (RBDAAPContentCode code)
135 return cc_defs[code-1].type;
138 const gchar *
139 rb_daap_content_code_string (RBDAAPContentCode code)
141 return cc_defs[code-1].string;
144 static GType
145 rb_daap_content_code_gtype (RBDAAPContentCode code)
147 switch (rb_daap_content_code_rb_daap_type (code)) {
148 case RB_DAAP_TYPE_BYTE:
149 case RB_DAAP_TYPE_SIGNED_INT:
150 return G_TYPE_CHAR;
151 case RB_DAAP_TYPE_SHORT:
152 case RB_DAAP_TYPE_INT:
153 case RB_DAAP_TYPE_DATE:
154 return G_TYPE_INT;
155 case RB_DAAP_TYPE_INT64:
156 return G_TYPE_INT64;
157 case RB_DAAP_TYPE_VERSION:
158 return G_TYPE_DOUBLE;
159 case RB_DAAP_TYPE_STRING:
160 return G_TYPE_STRING;
161 case RB_DAAP_TYPE_CONTAINER:
162 default:
163 return G_TYPE_NONE;
167 GNode *
168 rb_daap_structure_add (GNode *parent,
169 RBDAAPContentCode cc,
170 ...)
172 RBDAAPType rb_daap_type;
173 GType gtype;
174 RBDAAPItem *item;
175 va_list list;
176 GNode *node;
177 gchar *error = NULL;
179 va_start (list, cc);
181 rb_daap_type = rb_daap_content_code_rb_daap_type (cc);
182 gtype = rb_daap_content_code_gtype (cc);
184 item = g_new0(RBDAAPItem, 1);
185 item->content_code = cc;
187 if (gtype != G_TYPE_NONE) {
188 g_value_init (&(item->content), gtype);
191 if (rb_daap_type != RB_DAAP_TYPE_STRING && rb_daap_type != RB_DAAP_TYPE_CONTAINER) {
192 G_VALUE_COLLECT (&(item->content), list, G_VALUE_NOCOPY_CONTENTS, &error);
193 if (error) {
194 g_warning (error);
195 g_free (error);
199 switch (rb_daap_type) {
200 case RB_DAAP_TYPE_BYTE:
201 case RB_DAAP_TYPE_SIGNED_INT:
202 item->size = 1;
203 break;
204 case RB_DAAP_TYPE_SHORT:
205 item->size = 2;
206 break;
207 case RB_DAAP_TYPE_DATE:
208 case RB_DAAP_TYPE_INT:
209 case RB_DAAP_TYPE_VERSION:
210 item->size = 4;
211 break;
212 case RB_DAAP_TYPE_INT64:
213 item->size = 8;
214 break;
215 case RB_DAAP_TYPE_STRING: {
216 gchar *s = va_arg (list, gchar *);
218 g_value_set_string (&(item->content), s);
220 /* we dont use G_VALUE_COLLECT for this because we also
221 * need the length */
222 item->size = strlen (s);
223 break;
225 case RB_DAAP_TYPE_CONTAINER:
226 default:
227 break;
230 node = g_node_new (item);
232 if (parent) {
233 g_node_append (parent, node);
235 while (parent) {
236 RBDAAPItem *parent_item = parent->data;
238 parent_item->size += (4 + 4 + item->size);
240 parent = parent->parent;
244 return node;
247 static gboolean
248 rb_daap_structure_node_serialize (GNode *node,
249 GByteArray *array)
251 RBDAAPItem *item = node->data;
252 RBDAAPType rb_daap_type;
253 guint32 size = GINT32_TO_BE (item->size);
255 g_byte_array_append (array, (const guint8 *)rb_daap_content_code_string (item->content_code), 4);
256 g_byte_array_append (array, (const guint8 *)&size, 4);
258 rb_daap_type = rb_daap_content_code_rb_daap_type (item->content_code);
260 switch (rb_daap_type) {
261 case RB_DAAP_TYPE_BYTE:
262 case RB_DAAP_TYPE_SIGNED_INT: {
263 gchar c = g_value_get_char (&(item->content));
265 g_byte_array_append (array, (const guint8 *)&c, 1);
267 break;
269 case RB_DAAP_TYPE_SHORT: {
270 gint32 i = g_value_get_int (&(item->content));
271 gint16 s = GINT16_TO_BE ((gint16) i);
273 g_byte_array_append (array, (const guint8 *)&s, 2);
275 break;
277 case RB_DAAP_TYPE_DATE:
278 case RB_DAAP_TYPE_INT: {
279 gint32 i = g_value_get_int (&(item->content));
280 gint32 s = GINT32_TO_BE (i);
282 g_byte_array_append (array, (const guint8 *)&s, 4);
284 break;
286 case RB_DAAP_TYPE_VERSION: {
287 gdouble v = g_value_get_double (&(item->content));
288 gint16 major;
289 gint8 minor;
290 gint8 patch = 0;
292 major = (gint16)v;
293 minor = (gint8)(v - ((gdouble)major));
295 major = GINT16_TO_BE (major);
297 g_byte_array_append (array, (const guint8 *)&major, 2);
298 g_byte_array_append (array, (const guint8 *)&minor, 1);
299 g_byte_array_append (array, (const guint8 *)&patch, 1);
301 break;
303 case RB_DAAP_TYPE_INT64: {
304 gint64 i = g_value_get_int64 (&(item->content));
305 gint64 s = GINT64_TO_BE (i);
307 g_byte_array_append (array, (const guint8 *)&s, 8);
309 break;
311 case RB_DAAP_TYPE_STRING: {
312 const gchar *s = g_value_get_string (&(item->content));
314 g_byte_array_append (array, (const guint8 *)s, strlen (s));
316 break;
318 case RB_DAAP_TYPE_CONTAINER:
319 default:
320 break;
323 return FALSE;
326 gchar *
327 rb_daap_structure_serialize (GNode *structure,
328 guint *length)
330 GByteArray *array;
331 gchar *data;
333 array = g_byte_array_new ();
335 if (structure) {
336 g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)rb_daap_structure_node_serialize, array);
339 data = (gchar *) array->data;
340 *length = array->len;
341 g_byte_array_free (array, FALSE);
343 return data;
346 static RBDAAPContentCode
347 rb_daap_buffer_read_content_code (const gchar *buf)
349 gint32 c = MAKE_CONTENT_CODE (buf[0], buf[1], buf[2], buf[3]);
350 guint i;
352 for (i = 0; i < G_N_ELEMENTS (cc_defs); i++) {
353 if (cc_defs[i].int_code == c) {
354 return cc_defs[i].code;
358 return RB_DAAP_CC_INVALID;
361 #define rb_daap_buffer_read_int8(b) GST_READ_UINT8 (b)
362 #define rb_daap_buffer_read_int16(b) (gint16) GST_READ_UINT16_BE (b)
363 #define rb_daap_buffer_read_int32(b) (gint32) GST_READ_UINT32_BE (b)
364 #define rb_daap_buffer_read_int64(b) (gint64) GST_READ_UINT64_BE (b)
366 static gchar *
367 rb_daap_buffer_read_string (const gchar *buf, gssize size)
369 if (g_utf8_validate (buf, size, NULL) == TRUE) {
370 return g_strndup (buf, size);
371 } else {
372 return g_strdup ("");
376 //#define PARSE_DEBUG
377 #define PARSE_DEBUG_FILE "daapbuffer"
379 #ifdef PARSE_DEBUG
380 #include <unistd.h>
381 #include <sys/stat.h>
382 #include <fcntl.h>
383 #endif
385 static void
386 rb_daap_structure_parse_container_buffer (GNode *parent,
387 const guchar *buf,
388 gint buf_length)
390 gint l = 0;
392 while (l < buf_length) {
393 RBDAAPContentCode cc;
394 gint codesize = 0;
395 RBDAAPItem *item = NULL;
396 GNode *node = NULL;
397 GType gtype;
399 #ifdef PARSE_DEBUG
400 g_print ("l is %d and buf_length is %d\n", l, buf_length);
401 #endif
403 /* we need at least 8 bytes, 4 of content_code and 4 of size */
404 if (buf_length - l < 8) {
405 #ifdef PARSE_DEBUG
406 g_print ("Malformed response recieved\n");
407 #endif
408 return;
411 cc = rb_daap_buffer_read_content_code ((const gchar*)&(buf[l]));
412 if (cc == RB_DAAP_CC_INVALID) {
413 #ifdef PARSE_DEBUG
414 g_print ("Invalid content_code recieved\n");
415 #endif
416 return;
418 l += 4;
420 codesize = rb_daap_buffer_read_int32(&(buf[l]));
421 /* CCCCSIZECONTENT
422 * if the buffer length (minus 8 for the content code & size)
423 * is smaller than the read codesize (ie, someone sent us
424 * a codesize that is larger than the remaining data)
425 * then get out before we start processing it
427 if (codesize > buf_length - l - 4 || codesize < 0) {
428 #ifdef PARSE_DEBUG
429 g_print ("Invalid codesize %d recieved in buf_length %d\n", codesize, buf_length);
430 #endif
431 return;
433 l += 4;
435 #ifdef PARSE_DEBUG
436 g_print ("content_code = %d, codesize is %d, l is %d\n", cc, codesize, l);
437 #endif
439 item = g_new0 (RBDAAPItem, 1);
440 item->content_code = cc;
441 node = g_node_new (item);
442 g_node_append (parent, node);
444 gtype = rb_daap_content_code_gtype (item->content_code);
446 if (gtype != G_TYPE_NONE) {
447 g_value_init (&(item->content), gtype);
450 #ifdef PARSE_DEBUG
452 guint i;
454 for (i = 2; i < g_node_depth (node); i++) {
455 g_print ("\t");
458 #endif
460 // FIXME USE THE G_TYPE CONVERTOR FUNCTION rb_daap_type_to_gtype
461 switch (rb_daap_content_code_rb_daap_type (item->content_code)) {
462 case RB_DAAP_TYPE_SIGNED_INT:
463 case RB_DAAP_TYPE_BYTE: {
464 gchar c = 0;
466 if (codesize == 1) {
467 c = (gchar) rb_daap_buffer_read_int8(&(buf[l]));
470 g_value_set_char (&(item->content), c);
471 #ifdef PARSE_DEBUG
472 g_print ("Code: %s, content (%d): \"%c\"\n", rb_daap_content_code_string (item->content_code), codesize, (gchar)c);
473 #endif
475 break;
477 case RB_DAAP_TYPE_SHORT: {
478 gint16 s = 0;
480 if (codesize == 2) {
481 s = rb_daap_buffer_read_int16(&(buf[l]));
484 g_value_set_int (&(item->content),(gint32)s);
485 #ifdef PARSE_DEBUG
486 g_print ("Code: %s, content (%d): %hi\n", rb_daap_content_code_string (item->content_code), codesize, s);
487 #endif
489 break;
491 case RB_DAAP_TYPE_DATE:
492 case RB_DAAP_TYPE_INT: {
493 gint32 i = 0;
495 if (codesize == 4) {
496 i = rb_daap_buffer_read_int32(&(buf[l]));
499 g_value_set_int (&(item->content), i);
500 #ifdef PARSE_DEBUG
501 g_print ("Code: %s, content (%d): %d\n", rb_daap_content_code_string (item->content_code), codesize, i);
502 #endif
503 break;
505 case RB_DAAP_TYPE_INT64: {
506 gint64 i = 0;
508 if (codesize == 8) {
509 i = rb_daap_buffer_read_int16(&(buf[l]));
512 g_value_set_int64 (&(item->content), i);
513 #ifdef PARSE_DEBUG
514 g_print ("Code: %s, content (%d): %"G_GINT64_FORMAT"\n", rb_daap_content_code_string (item->content_code), codesize, i);
515 #endif
517 break;
519 case RB_DAAP_TYPE_STRING: {
520 gchar *s = rb_daap_buffer_read_string ((const gchar*)&(buf[l]), codesize);
522 g_value_take_string (&(item->content), s);
523 #ifdef PARSE_DEBUG
524 g_print ("Code: %s, content (%d): \"%s\"\n", rb_daap_content_code_string (item->content_code), codesize, s);
525 #endif
527 break;
529 case RB_DAAP_TYPE_VERSION: {
530 gint16 major = 0;
531 gint16 minor = 0;
532 gint16 patch = 0;
533 gdouble v = 0;
535 if (codesize == 4) {
536 major = rb_daap_buffer_read_int16(&(buf[l]));
537 minor = rb_daap_buffer_read_int8(&(buf[l]) + 2);
538 patch = rb_daap_buffer_read_int8(&(buf[l]) + 3);
541 v = (gdouble)major;
542 v += (gdouble)(minor * 0.1);
543 v += (gdouble)(patch * 0.01);
545 g_value_set_double (&(item->content), v);
546 #ifdef PARSE_DEBUG
547 g_print ("Code: %s, content: %f\n", rb_daap_content_code_string (item->content_code), v);
548 #endif
550 break;
552 case RB_DAAP_TYPE_CONTAINER: {
553 #ifdef PARSE_DEBUG
554 g_print ("Code: %s, container\n", rb_daap_content_code_string (item->content_code));
555 #endif
556 rb_daap_structure_parse_container_buffer (node,&(buf[l]), codesize);
557 break;
561 l += codesize;
564 return;
567 GNode *
568 rb_daap_structure_parse (const gchar *buf,
569 gint buf_length)
571 GNode *root = NULL;
572 GNode *child = NULL;
574 #ifdef PARSE_DEBUG
576 int fd;
578 fd = open (PARSE_DEBUG_FILE, O_WRONLY | O_CREAT);
579 write (fd, (const void *)buf, (size_t)buf_length);
580 close (fd);
582 #endif
584 root = g_node_new (NULL);
586 rb_daap_structure_parse_container_buffer (root, (guchar *)buf, buf_length);
588 child = root->children;
589 if (child) {
590 g_node_unlink (child);
592 g_node_destroy (root);
594 return child;
597 struct NodeFinder {
598 RBDAAPContentCode code;
599 GNode *node;
602 static gboolean
603 gnode_find_node (GNode *node,
604 gpointer data)
606 struct NodeFinder *finder = (struct NodeFinder *)data;
607 RBDAAPItem *item = node->data;
609 if (item->content_code == finder->code) {
610 finder->node = node;
611 return TRUE;
614 return FALSE;
617 RBDAAPItem *
618 rb_daap_structure_find_item (GNode *structure,
619 RBDAAPContentCode code)
621 GNode *node = NULL;
623 node = rb_daap_structure_find_node (structure, code);
625 if (node) {
626 return node->data;
629 return NULL;
632 GNode *
633 rb_daap_structure_find_node (GNode *structure,
634 RBDAAPContentCode code)
636 struct NodeFinder *finder;
637 GNode *node = NULL;
639 finder = g_new0(struct NodeFinder,1);
640 finder->code = code;
642 g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_find_node, finder);
644 node = finder->node;
645 g_free (finder);
646 finder = NULL;
648 return node;
651 static void
652 rb_daap_item_free (RBDAAPItem *item)
654 if (rb_daap_content_code_rb_daap_type (item->content_code) != RB_DAAP_TYPE_CONTAINER) {
655 g_value_unset (&(item->content));
658 g_free (item);
661 static gboolean
662 gnode_free_rb_daap_item (GNode *node,
663 gpointer data)
665 rb_daap_item_free ((RBDAAPItem *)node->data);
667 return FALSE;
670 void
671 rb_daap_structure_destroy (GNode *structure)
673 if (structure) {
674 g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_free_rb_daap_item, NULL);
676 g_node_destroy (structure);
678 structure = NULL;
682 const RBDAAPContentCodeDefinition *
683 rb_daap_content_codes (guint *number)
685 *number = G_N_ELEMENTS (cc_defs);
687 return cc_defs;
690 gint32
691 rb_daap_content_code_string_as_int32 (const gchar *str)
693 union {
694 gint32 i;
695 gchar str[4];
696 } u;
698 strncpy (u.str, str, 4);
700 return g_htonl (u.i);
703 static gboolean
704 print_rb_daap_item (GNode *node,
705 gpointer data)
707 RBDAAPItem *item;
708 const gchar *name;
709 gchar *value;
710 gint i;
712 for (i = 1; i < g_node_depth (node); i++) {
713 g_print ("\t");
716 item = node->data;
718 name = rb_daap_content_code_name (item->content_code);
720 if (G_IS_VALUE (&(item->content))) {
721 value = g_strdup_value_contents (&(item->content));
722 } else {
723 value = g_strdup ("");
726 g_print ("%d, %s = %s (%d)\n", g_node_depth (node), name, value, item->size);
727 g_free (value);
729 return FALSE;
732 void
733 rb_daap_structure_print (GNode *structure)
735 if (structure) {
736 g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)print_rb_daap_item, NULL);