upgrade obexd to 0.47
[profile/ivi/obexd.git] / gobex / gobex-header.c
1 /*
2  *
3  *  OBEX library with GLib integration
4  *
5  *  Copyright (C) 2011  Intel Corporation. All rights reserved.
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 version 2 as
9  *  published by the Free Software Foundation.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27
28 #include "gobex-header.h"
29 #include "gobex-debug.h"
30
31 /* Header types */
32 #define G_OBEX_HDR_ENC_UNICODE  (0 << 6)
33 #define G_OBEX_HDR_ENC_BYTES    (1 << 6)
34 #define G_OBEX_HDR_ENC_UINT8    (2 << 6)
35 #define G_OBEX_HDR_ENC_UINT32   (3 << 6)
36
37 #define G_OBEX_HDR_ENC(id)      ((id) & 0xc0)
38
39 struct _GObexHeader {
40         guint8 id;
41         gboolean extdata;
42         gsize vlen;                     /* Length of value */
43         gsize hlen;                     /* Length of full encoded header */
44         union {
45                 char *string;           /* UTF-8 converted from UTF-16 */
46                 guint8 *data;           /* Own buffer */
47                 const guint8 *extdata;  /* Reference to external buffer */
48                 guint8 u8;
49                 guint32 u32;
50         } v;
51 };
52
53 static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
54         glong utf16_len;
55         int i;
56
57         if (*utf8 == '\0') {
58                 *utf16 = NULL;
59                 return 0;
60         }
61
62         *utf16 = g_utf8_to_utf16(utf8, -1, NULL, &utf16_len, NULL);
63         if (*utf16 == NULL)
64                 return -1;
65
66         /* g_utf8_to_utf16 produces host-byteorder UTF-16,
67          * but OBEX requires network byteorder (big endian) */
68         for (i = 0; i < utf16_len; i++)
69                 (*utf16)[i] = g_htons((*utf16)[i]);
70
71         utf16_len = (utf16_len + 1) << 1;
72
73         return utf16_len;
74 }
75
76 static guint8 *put_bytes(guint8 *to, const void *from, gsize count)
77 {
78         memcpy(to, from, count);
79         return (to + count);
80 }
81
82 static const guint8 *get_bytes(void *to, const guint8 *from, gsize count)
83 {
84         memcpy(to, from, count);
85         return (from + count);
86 }
87
88 gssize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
89 {
90         guint8 *ptr = buf;
91         guint16 u16;
92         guint32 u32;
93         gunichar2 *utf16;
94         glong utf16_len;
95
96         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
97                                                 G_OBEX_HDR_ENC(header->id));
98
99         if (buf_len < header->hlen)
100                 return -1;
101
102         ptr = put_bytes(ptr, &header->id, sizeof(header->id));
103
104         switch (G_OBEX_HDR_ENC(header->id)) {
105         case G_OBEX_HDR_ENC_UNICODE:
106                 utf16_len = utf8_to_utf16(&utf16, header->v.string);
107                 if (utf16_len < 0 || (guint16) utf16_len > buf_len)
108                         return -1;
109                 g_assert_cmpuint(utf16_len + 3, ==, header->hlen);
110                 u16 = g_htons(utf16_len + 3);
111                 ptr = put_bytes(ptr, &u16, sizeof(u16));
112                 put_bytes(ptr, utf16, utf16_len);
113                 g_free(utf16);
114                 break;
115         case G_OBEX_HDR_ENC_BYTES:
116                 u16 = g_htons(header->hlen);
117                 ptr = put_bytes(ptr, &u16, sizeof(u16));
118                 if (header->extdata)
119                         put_bytes(ptr, header->v.extdata, header->vlen);
120                 else
121                         put_bytes(ptr, header->v.data, header->vlen);
122                 break;
123         case G_OBEX_HDR_ENC_UINT8:
124                 *ptr = header->v.u8;
125                 break;
126         case G_OBEX_HDR_ENC_UINT32:
127                 u32 = g_htonl(header->v.u32);
128                 put_bytes(ptr, &u32, sizeof(u32));
129                 break;
130         default:
131                 g_assert_not_reached();
132         }
133
134         return header->hlen;
135 }
136
137 GObexHeader *g_obex_header_decode(const void *data, gsize len,
138                                 GObexDataPolicy data_policy, gsize *parsed,
139                                 GError **err)
140 {
141         GObexHeader *header;
142         const guint8 *ptr = data;
143         guint16 hdr_len;
144         gsize str_len;
145         GError *conv_err = NULL;
146
147         if (len < 2) {
148                 g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
149                                                 "Too short header in packet");
150                 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
151                 return NULL;
152         }
153
154         header = g_new0(GObexHeader, 1);
155
156         ptr = get_bytes(&header->id, ptr, sizeof(header->id));
157
158         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
159                                                 G_OBEX_HDR_ENC(header->id));
160
161         switch (G_OBEX_HDR_ENC(header->id)) {
162         case G_OBEX_HDR_ENC_UNICODE:
163                 if (len < 3) {
164                         g_set_error(err, G_OBEX_ERROR,
165                                 G_OBEX_ERROR_PARSE_ERROR,
166                                 "Not enough data for unicode header (0x%02x)",
167                                 header->id);
168                         goto failed;
169                 }
170                 ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len));
171                 hdr_len = g_ntohs(hdr_len);
172
173                 if (hdr_len == 3) {
174                         header->v.string = g_strdup("");
175                         header->vlen = 0;
176                         header->hlen = hdr_len;
177                         *parsed = hdr_len;
178                         break;
179                 }
180
181                 if (hdr_len > len || hdr_len < 5) {
182                         g_set_error(err, G_OBEX_ERROR,
183                                 G_OBEX_ERROR_PARSE_ERROR,
184                                 "Invalid unicode header (0x%02x) length (%u)",
185                                 header->id, hdr_len);
186                         goto failed;
187                 }
188
189                 header->v.string = g_convert((const char *) ptr, hdr_len - 5,
190                                                 "UTF-8", "UTF-16BE",
191                                                 NULL, &str_len, &conv_err);
192                 if (header->v.string == NULL) {
193                         g_set_error(err, G_OBEX_ERROR,
194                                         G_OBEX_ERROR_PARSE_ERROR,
195                                         "Unicode conversion failed: %s",
196                                         conv_err->message);
197                         g_error_free(conv_err);
198                         goto failed;
199                 }
200
201                 header->vlen = (gsize) str_len;
202                 header->hlen = hdr_len;
203
204                 *parsed = hdr_len;
205
206                 break;
207         case G_OBEX_HDR_ENC_BYTES:
208                 if (len < 3) {
209                         g_set_error(err, G_OBEX_ERROR,
210                                         G_OBEX_ERROR_PARSE_ERROR,
211                                         "Too short byte array header");
212                         goto failed;
213                 }
214                 ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len));
215                 hdr_len = g_ntohs(hdr_len);
216                 if (hdr_len > len) {
217                         g_set_error(err, G_OBEX_ERROR,
218                                         G_OBEX_ERROR_PARSE_ERROR,
219                                         "Too long byte array header");
220                         goto failed;
221                 }
222
223                 if (hdr_len < 3) {
224                         g_set_error(err, G_OBEX_ERROR,
225                                         G_OBEX_ERROR_PARSE_ERROR,
226                                         "Too small byte array length");
227                         goto failed;
228                 }
229
230                 header->vlen = hdr_len - 3;
231                 header->hlen = hdr_len;
232
233                 switch (data_policy) {
234                 case G_OBEX_DATA_COPY:
235                         header->v.data = g_memdup(ptr, header->vlen);
236                         break;
237                 case G_OBEX_DATA_REF:
238                         header->extdata = TRUE;
239                         header->v.extdata = ptr;
240                         break;
241                 default:
242                         g_set_error(err, G_OBEX_ERROR,
243                                         G_OBEX_ERROR_INVALID_ARGS,
244                                         "Invalid data policy");
245                         goto failed;
246                 }
247
248                 *parsed = hdr_len;
249
250                 break;
251         case G_OBEX_HDR_ENC_UINT8:
252                 header->vlen = 1;
253                 header->hlen = 2;
254                 header->v.u8 = *ptr;
255                 *parsed = 2;
256                 break;
257         case G_OBEX_HDR_ENC_UINT32:
258                 if (len < 5) {
259                         g_set_error(err, G_OBEX_ERROR,
260                                         G_OBEX_ERROR_PARSE_ERROR,
261                                         "Too short uint32 header");
262                         goto failed;
263                 }
264                 header->vlen = 4;
265                 header->hlen = 5;
266                 get_bytes(&header->v.u32, ptr, sizeof(header->v.u32));
267                 header->v.u32 = g_ntohl(header->v.u32);
268                 *parsed = 5;
269                 break;
270         default:
271                 g_assert_not_reached();
272         }
273
274         return header;
275
276 failed:
277         if (*err)
278                 g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
279         g_obex_header_free(header);
280         return NULL;
281 }
282
283 void g_obex_header_free(GObexHeader *header)
284 {
285         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
286                                                 G_OBEX_HDR_ENC(header->id));
287
288         switch (G_OBEX_HDR_ENC(header->id)) {
289         case G_OBEX_HDR_ENC_UNICODE:
290                 g_free(header->v.string);
291                 break;
292         case G_OBEX_HDR_ENC_BYTES:
293                 if (!header->extdata)
294                         g_free(header->v.data);
295                 break;
296         case G_OBEX_HDR_ENC_UINT8:
297         case G_OBEX_HDR_ENC_UINT32:
298                 break;
299         default:
300                 g_assert_not_reached();
301         }
302
303         g_free(header);
304 }
305
306 gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str)
307 {
308         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
309                                                 G_OBEX_HDR_ENC(header->id));
310
311         if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UNICODE)
312                 return FALSE;
313
314         *str = header->v.string;
315
316         g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", *str);
317
318         return TRUE;
319 }
320
321 gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val,
322                                                                 gsize *len)
323 {
324         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
325                                                 G_OBEX_HDR_ENC(header->id));
326
327         if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_BYTES)
328                 return FALSE;
329
330         *len = header->vlen;
331
332         if (header->extdata)
333                 *val = header->v.extdata;
334         else
335                 *val = header->v.data;
336
337         return TRUE;
338 }
339
340 gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val)
341 {
342         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
343                                                 G_OBEX_HDR_ENC(header->id));
344
345         if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT8)
346                 return FALSE;
347
348         *val = header->v.u8;
349
350         g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val);
351
352         return TRUE;
353 }
354
355 gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val)
356 {
357         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
358                                                 G_OBEX_HDR_ENC(header->id));
359
360         if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT32)
361                 return FALSE;
362
363         *val = header->v.u32;
364
365         g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val);
366
367         return TRUE;
368 }
369
370 GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
371 {
372         GObexHeader *header;
373         gsize len;
374
375         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
376
377         if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE)
378                 return NULL;
379
380         header = g_new0(GObexHeader, 1);
381
382         header->id = id;
383
384         len = g_utf8_strlen(str, -1);
385
386         header->vlen = len;
387         header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2);
388         header->v.string = g_strdup(str);
389
390         g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string);
391
392         return header;
393 }
394
395 GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len)
396 {
397         GObexHeader *header;
398
399         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
400
401         if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_BYTES)
402                 return NULL;
403
404         header = g_new0(GObexHeader, 1);
405
406         header->id = id;
407         header->vlen = len;
408         header->hlen = len + 3;
409         header->v.data = g_memdup(data, len);
410
411         return header;
412 }
413
414 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val)
415 {
416         GObexHeader *header;
417
418         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
419
420         if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT8)
421                 return NULL;
422
423         header = g_new0(GObexHeader, 1);
424
425         header->id = id;
426         header->vlen = 1;
427         header->hlen = 2;
428         header->v.u8 = val;
429
430         g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u8);
431
432         return header;
433 }
434
435 GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val)
436 {
437         GObexHeader *header;
438
439         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
440
441         if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT32)
442                 return NULL;
443
444         header = g_new0(GObexHeader, 1);
445
446         header->id = id;
447         header->vlen = 4;
448         header->hlen = 5;
449         header->v.u32 = val;
450
451         g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u32);
452
453         return header;
454 }
455
456 guint8 g_obex_header_get_id(GObexHeader *header)
457 {
458         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x id 0x%02x",
459                                 G_OBEX_HDR_ENC(header->id), header->id);
460
461         return header->id;
462 }
463
464 guint16 g_obex_header_get_length(GObexHeader *header)
465 {
466         g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x length %zu",
467                                 G_OBEX_HDR_ENC(header->id), header->hlen);
468
469         return header->hlen;
470 }
471
472 GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args,
473                                                         gsize *total_len)
474 {
475         unsigned int id = first_hdr_id;
476         GSList *l = NULL;
477
478         g_obex_debug(G_OBEX_DEBUG_HEADER, "");
479
480         *total_len = 0;
481
482         while (id != G_OBEX_HDR_INVALID) {
483                 GObexHeader *hdr;
484                 const char *str;
485                 const void *bytes;
486                 unsigned int val;
487                 gsize len;
488
489                 switch (G_OBEX_HDR_ENC(id)) {
490                 case G_OBEX_HDR_ENC_UNICODE:
491                         str = va_arg(args, const char *);
492                         hdr = g_obex_header_new_unicode(id, str);
493                         break;
494                 case G_OBEX_HDR_ENC_BYTES:
495                         bytes = va_arg(args, void *);
496                         len = va_arg(args, gsize);
497                         hdr = g_obex_header_new_bytes(id, bytes, len);
498                         break;
499                 case G_OBEX_HDR_ENC_UINT8:
500                         val = va_arg(args, unsigned int);
501                         hdr = g_obex_header_new_uint8(id, val);
502                         break;
503                 case G_OBEX_HDR_ENC_UINT32:
504                         val = va_arg(args, unsigned int);
505                         hdr = g_obex_header_new_uint32(id, val);
506                         break;
507                 default:
508                         g_assert_not_reached();
509                 }
510
511                 l = g_slist_append(l, hdr);
512                 *total_len += hdr->hlen;
513                 id = va_arg(args, int);
514         }
515
516         return l;
517 }