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