2003-01-08 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / dbus / dbus-marshal.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-marshal.c  Marshalling routines
3  *
4  * Copyright (C) 2002  CodeFactory AB
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-marshal.h"
25 #include "dbus-internals.h"
26
27 #include <string.h>
28
29 /* from ORBit */
30 static void
31 swap_bytes (unsigned char *data,
32             unsigned int   len)
33 {
34   unsigned char *p1 = data;
35   unsigned char *p2 = data + len - 1;
36
37   while (p1 < p2)
38     {
39       unsigned char tmp = *p1;
40       *p1 = *p2;
41       *p2 = tmp;
42
43       --p2;
44       ++p1;
45     }
46 }
47
48 /**
49  * @defgroup DBusMarshal marshaling and unmarshaling
50  * @ingroup  DBusInternals
51  * @brief functions to marshal/unmarshal data from the wire
52  *
53  * Types and functions related to converting primitive data types from
54  * wire format to native machine format, and vice versa.
55  *
56  * @{
57  */
58
59 /**
60  * Unpacks a 32 bit unsigned integer from a data pointer
61  *
62  * @param byte_order The byte order to use
63  * @param data the data pointer
64  * @returns the integer
65  */
66 dbus_uint32_t
67 _dbus_unpack_uint32 (int                  byte_order,
68                      const unsigned char *data)
69 {
70   _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
71   
72   if (byte_order == DBUS_LITTLE_ENDIAN)
73     return DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)data);
74   else
75     return DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)data);
76 }             
77
78 /**
79  * Unpacks a 32 bit signed integer from a data pointer
80  *
81  * @param byte_order The byte order to use
82  * @param data the data pointer
83  * @returns the integer
84  */
85 dbus_int32_t
86 _dbus_unpack_int32 (int                  byte_order,
87                     const unsigned char *data)
88 {
89   _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
90   
91   if (byte_order == DBUS_LITTLE_ENDIAN)
92     return DBUS_INT32_FROM_LE (*(dbus_int32_t*)data);
93   else
94     return DBUS_INT32_FROM_BE (*(dbus_int32_t*)data);
95 }
96
97 /**
98  * Packs a 32 bit unsigned integer into a data pointer.
99  *
100  * @param value the value
101  * @param byte_order the byte order to use
102  * @param data the data pointer
103  */
104 void
105 _dbus_pack_uint32 (dbus_uint32_t   value,
106                    int             byte_order,
107                    unsigned char  *data)
108 {
109   _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
110   
111   if ((byte_order) == DBUS_LITTLE_ENDIAN)                  
112     *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_LE (value);       
113   else
114     *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_BE (value);
115 }
116
117 /**
118  * Packs a 32 bit signed integer into a data pointer.
119  *
120  * @param value the value
121  * @param byte_order the byte order to use
122  * @param data the data pointer
123  */
124 void
125 _dbus_pack_int32 (dbus_int32_t   value,
126                   int            byte_order,
127                   unsigned char *data)
128 {
129   _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
130   
131   if ((byte_order) == DBUS_LITTLE_ENDIAN)                  
132     *((dbus_int32_t*)(data)) = DBUS_INT32_TO_LE (value);       
133   else
134     *((dbus_int32_t*)(data)) = DBUS_INT32_TO_BE (value);
135 }
136
137 /**
138  * Marshals a double value.
139  *
140  * @param str the string to append the marshalled value to
141  * @param byte_order the byte order to use
142  * @param value the value
143  * @returns #TRUE on success
144  */
145 dbus_bool_t
146 _dbus_marshal_double (DBusString *str,
147                       int         byte_order,
148                       double      value)
149 {
150   _dbus_assert (sizeof (double) == 8);
151   
152   if (!_dbus_string_set_length (str,
153                                 _DBUS_ALIGN_VALUE (_dbus_string_get_length (str),
154                                                    sizeof (double))))
155     return FALSE;
156   
157   if (byte_order != DBUS_COMPILER_BYTE_ORDER)
158     swap_bytes ((unsigned char *)&value, sizeof (double));
159
160   return _dbus_string_append_len (str, (const char *)&value, sizeof (double));
161 }
162
163 /**
164  * Marshals a 32 bit signed integer value.
165  *
166  * @param str the string to append the marshalled value to
167  * @param byte_order the byte order to use
168  * @param value the value
169  * @returns #TRUE on success
170  */
171 dbus_bool_t
172 _dbus_marshal_int32  (DBusString   *str,
173                       int           byte_order,
174                       dbus_int32_t  value)
175 {
176   if (!_dbus_string_set_length (str,
177                                 _DBUS_ALIGN_VALUE (_dbus_string_get_length (str),
178                                                    sizeof (dbus_int32_t))))
179     return FALSE;
180   
181   if (byte_order != DBUS_COMPILER_BYTE_ORDER)
182     swap_bytes ((unsigned char *)&value, sizeof (dbus_int32_t));
183
184   return _dbus_string_append_len (str, (const char *)&value, sizeof (dbus_int32_t));
185 }
186
187 /**
188  * Marshals a 32 bit unsigned integer value.
189  *
190  * @param str the string to append the marshalled value to
191  * @param byte_order the byte order to use
192  * @param value the value
193  * @returns #TRUE on success
194  */
195 dbus_bool_t
196 _dbus_marshal_uint32 (DBusString    *str,
197                       int            byte_order,
198                       dbus_uint32_t  value)
199 {
200   if (!_dbus_string_set_length (str,
201                                 _DBUS_ALIGN_VALUE (_dbus_string_get_length (str),
202                                                    sizeof (dbus_uint32_t))))
203     return FALSE;
204
205   if (byte_order != DBUS_COMPILER_BYTE_ORDER)
206     swap_bytes ((unsigned char *)&value, sizeof (dbus_uint32_t));
207
208   return _dbus_string_append_len (str, (const char *)&value, sizeof (dbus_uint32_t));
209 }
210
211 /**
212  * Marshals a UTF-8 string
213  *
214  * @param str the string to append the marshalled value to
215  * @param byte_order the byte order to use
216  * @param value the string
217  * @returns #TRUE on success
218  */
219 dbus_bool_t
220 _dbus_marshal_string (DBusString    *str,
221                       int            byte_order,
222                       const char    *value)
223 {
224   int len, old_string_len;
225
226   old_string_len = _dbus_string_get_length (str);
227   
228   len = strlen (value);
229
230   if (!_dbus_marshal_uint32 (str, byte_order, len))
231     {
232       /* Restore the previous length */
233       _dbus_string_set_length (str, old_string_len);
234
235       return FALSE;
236     }
237
238   return _dbus_string_append_len (str, value, len + 1);
239 }
240
241 /**
242  * Marshals a byte array
243  *
244  * @param str the string to append the marshalled value to
245  * @param byte_order the byte order to use
246  * @param value the byte array
247  * @param len the length of the byte array
248  * @returns #TRUE on success
249  */
250 dbus_bool_t
251 _dbus_marshal_byte_array (DBusString          *str,
252                           int                  byte_order,
253                           const unsigned char *value,
254                           int                  len)
255 {
256   int old_string_len;
257
258   old_string_len = _dbus_string_get_length (str);
259   
260   if (!_dbus_marshal_uint32 (str, byte_order, len))
261     {
262       /* Restore the previous length */
263       _dbus_string_set_length (str, old_string_len);
264
265       return FALSE;
266     }
267
268   return _dbus_string_append_len (str, value, len);
269 }
270
271 /**
272  * Demarshals a double.
273  *
274  * @param str the string containing the data
275  * @param byte_order the byte order
276  * @param pos the position in the string
277  * @param new_pos the new position of the string
278  * @returns the demarshaled double.
279  */
280 double
281 _dbus_demarshal_double (DBusString  *str,
282                         int          byte_order,
283                         int          pos,
284                         int         *new_pos)
285 {
286   double retval;
287   const char *buffer;
288
289   pos = _DBUS_ALIGN_VALUE (pos, sizeof (double));
290
291   _dbus_string_get_const_data_len (str, &buffer, pos, sizeof (double));
292
293   retval = *(double *)buffer;
294   
295   if (byte_order != DBUS_COMPILER_BYTE_ORDER)
296     swap_bytes ((unsigned char *)&retval, sizeof (double));
297
298   if (new_pos)
299     *new_pos = pos + sizeof (double);
300   
301   return retval;  
302 }
303
304 /**
305  * Demarshals a 32 bit signed integer.
306  *
307  * @param str the string containing the data
308  * @param byte_order the byte order
309  * @param pos the position in the string
310  * @param new_pos the new position of the string
311  * @returns the demarshaled integer.
312  */
313 dbus_int32_t
314 _dbus_demarshal_int32  (DBusString *str,
315                         int         byte_order,
316                         int         pos,
317                         int        *new_pos)
318 {
319   const char *buffer;
320
321   pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_int32_t));
322   
323   _dbus_string_get_const_data_len (str, &buffer, pos, sizeof (dbus_int32_t));
324
325   if (new_pos)
326     *new_pos = pos + sizeof (dbus_int32_t);
327
328   return _dbus_unpack_int32 (byte_order, buffer);
329 }
330
331 /**
332  * Demarshals a 32 bit unsigned integer.
333  *
334  * @param str the string containing the data
335  * @param byte_order the byte order
336  * @param pos the position in the string
337  * @param new_pos the new position of the string
338  * @returns the demarshaled integer.
339  */
340 dbus_uint32_t
341 _dbus_demarshal_uint32  (DBusString *str,
342                          int         byte_order,
343                          int         pos,
344                          int        *new_pos)
345 {
346   const char *buffer;
347
348   pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_uint32_t));
349   
350   _dbus_string_get_const_data_len (str, &buffer, pos, sizeof (dbus_uint32_t));
351
352   if (new_pos)
353     *new_pos = pos + sizeof (dbus_uint32_t);
354
355   return _dbus_unpack_uint32 (byte_order, buffer);
356 }
357
358 /**
359  * Demarshals an UTF-8 string.
360  *
361  * @todo Should we check the string to make sure
362  * that it's  valid UTF-8, and maybe "fix" the string
363  * if it's broken?
364  *
365  * @todo Should probably demarshal to a DBusString,
366  * having memcpy() in here is Evil(tm).
367  *
368  * @param str the string containing the data
369  * @param byte_order the byte order
370  * @param pos the position in the string
371  * @param new_pos the new position of the string
372  * @returns the demarshaled string.
373  */
374 char *
375 _dbus_demarshal_string (DBusString *str,
376                         int         byte_order,
377                         int         pos,
378                         int        *new_pos)
379 {
380   int len;
381   char *retval;
382   const char *data;
383
384   len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos);
385
386   retval = dbus_malloc (len + 1);
387
388   if (!retval)
389     return NULL;
390
391   _dbus_string_get_const_data_len (str, &data, pos, len);
392
393   if (!data)
394     return NULL;
395
396   memcpy (retval, data, len + 1);
397
398   if (new_pos)
399     *new_pos = pos + len + 1;
400   
401   return retval;
402 }
403
404 unsigned char *
405 _dbus_demarshal_byte_array (DBusString *str,
406                             int         byte_order,
407                             int         pos,
408                             int        *new_pos,
409                             int        *array_len)
410 {
411   int len;
412   unsigned char *retval;
413   const char *data;
414
415   len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos);
416
417   retval = dbus_malloc (len);
418
419   if (!retval)
420     return NULL;
421
422   _dbus_string_get_const_data_len (str, &data, pos, len);
423
424   if (!data)
425     return NULL;
426
427   memcpy (retval, data, len);
428
429   if (new_pos)
430     *new_pos = pos + len;
431
432   if (array_len)
433     *array_len = len;
434
435   return retval;
436 }
437
438 /** 
439  * Returns the position right after the end position 
440  * end position of a field
441  *
442  * @todo warns on invalid type in a message, but
443  * probably the whole message needs to be dumped,
444  * or we might even drop the connection due
445  * to bad protocol. Needs better error handling.
446  * Possible security issue.
447  *
448  * @param str a string
449  * @param byte_order the byte order to use
450  * @param pos the pos where the field starts
451  * @param end_pos pointer where the position right
452  * after the end position will follow
453  * @returns TRUE if more data exists after the field
454  */
455 dbus_bool_t
456 _dbus_marshal_get_field_end_pos (DBusString *str,
457                                  int         byte_order,
458                                  int         pos,
459                                  int        *end_pos)
460 {
461   const char *data;
462
463   if (pos >= _dbus_string_get_length (str))
464     return FALSE;
465
466   _dbus_string_get_const_data_len (str, &data, pos, 1);
467   
468   switch (*data)
469     {
470     case DBUS_TYPE_INVALID:
471       return FALSE;
472       break;
473
474     case DBUS_TYPE_INT32:
475       *end_pos = _DBUS_ALIGN_VALUE (pos + 1, sizeof (dbus_int32_t)) + sizeof (dbus_int32_t);
476
477       break;
478
479     case DBUS_TYPE_UINT32:
480       *end_pos = _DBUS_ALIGN_VALUE (pos + 1, sizeof (dbus_uint32_t)) + sizeof (dbus_uint32_t);
481
482       break;
483
484     case DBUS_TYPE_DOUBLE:
485       *end_pos = _DBUS_ALIGN_VALUE (pos + 1, sizeof (double)) + sizeof (double);
486
487       break;
488
489     case DBUS_TYPE_STRING:
490       {
491         int len, new_pos;
492
493         /* Demarshal the length */
494         len = _dbus_demarshal_uint32 (str, byte_order, pos + 1, &new_pos);
495
496         *end_pos = new_pos + len + 1;
497
498         break;
499       }
500
501     case DBUS_TYPE_BYTE_ARRAY:
502       {
503         int len, new_pos;
504
505         /* Demarshal the length */
506         len = _dbus_demarshal_uint32 (str, byte_order, pos + 1, &new_pos);
507         
508         *end_pos = new_pos + len;
509
510         break;
511       }
512       
513     default:
514       _dbus_warn ("Unknown message field type %d\n", *data);
515       return FALSE;
516     }
517
518   if (*end_pos >= _dbus_string_get_length (str))
519     return FALSE;
520   
521   return TRUE;
522 }
523
524 /**
525  * If in verbose mode, print a block of binary data.
526  *
527  * @todo right now it prints even if not in verbose mode
528  * 
529  * @param data the data
530  * @param len the length of the data
531  */
532 void
533 _dbus_verbose_bytes (const unsigned char *data,
534                      int                  len)
535 {
536   int i;
537   const unsigned char *aligned;
538
539   /* Print blanks on first row if appropriate */
540   aligned = _DBUS_ALIGN_ADDRESS (data, 4);
541   if (aligned > data)
542     aligned -= 4;
543   _dbus_assert (aligned <= data);
544
545   if (aligned != data)
546     {
547       _dbus_verbose ("%5d\t%p: ", - (data - aligned), aligned); 
548       while (aligned != data)
549         {
550           _dbus_verbose ("    ");
551           ++aligned;
552         }
553     }
554
555   /* now print the bytes */
556   i = 0;
557   while (i < len)
558     {
559       if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
560         {
561           _dbus_verbose ("%5d\t%p: ",
562                    i, &data[i]);
563         }
564       
565       if (data[i] >= 32 &&
566           data[i] <= 126)
567         _dbus_verbose (" '%c' ", data[i]);
568       else
569         _dbus_verbose ("0x%s%x ",
570                  data[i] <= 0xf ? "0" : "", data[i]);
571
572       ++i;
573
574       if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
575         {
576           if (i > 3)
577             _dbus_verbose ("big: %d little: %d",
578                            _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]),
579                            _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4]));
580           
581           _dbus_verbose ("\n");
582         }
583     }
584
585   _dbus_verbose ("\n");
586 }
587
588 /**
589  * Dump the given part of the string to verbose log.
590  *
591  * @param str the string
592  * @param start the start of range to dump
593  * @param len length of range
594  */
595 void
596 _dbus_verbose_bytes_of_string (const DBusString    *str,
597                                int                  start,
598                                int                  len)
599 {
600   const char *d;
601
602   _dbus_string_get_const_data_len (str, &d, start, len);
603
604   _dbus_verbose_bytes (d, len);
605 }
606
607 /** @} */
608
609 #ifdef DBUS_BUILD_TESTS
610 #include "dbus-test.h"
611 #include <stdio.h>
612
613 dbus_bool_t
614 _dbus_marshal_test (void)
615 {
616   DBusString str;
617   char *tmp1, *tmp2;
618   int pos = 0;
619   
620   if (!_dbus_string_init (&str, _DBUS_INT_MAX))
621     _dbus_assert_not_reached ("failed to init string");
622
623
624   /* Marshal doubles */
625   if (!_dbus_marshal_double (&str, DBUS_BIG_ENDIAN, 3.14))
626     _dbus_assert_not_reached ("could not marshal double value");
627   _dbus_assert (_dbus_demarshal_double (&str, DBUS_BIG_ENDIAN, pos, &pos) == 3.14);
628
629   if (!_dbus_marshal_double (&str, DBUS_LITTLE_ENDIAN, 3.14))
630     _dbus_assert_not_reached ("could not marshal double value");
631   _dbus_assert (_dbus_demarshal_double (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == 3.14);
632   
633   /* Marshal signed integers */
634   if (!_dbus_marshal_int32 (&str, DBUS_BIG_ENDIAN, -12345678))
635     _dbus_assert_not_reached ("could not marshal signed integer value");
636   _dbus_assert (_dbus_demarshal_int32 (&str, DBUS_BIG_ENDIAN, pos, &pos) == -12345678);
637
638   if (!_dbus_marshal_int32 (&str, DBUS_LITTLE_ENDIAN, -12345678))
639     _dbus_assert_not_reached ("could not marshal signed integer value");
640   _dbus_assert (_dbus_demarshal_int32 (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == -12345678);
641   
642   /* Marshal unsigned integers */
643   if (!_dbus_marshal_uint32 (&str, DBUS_BIG_ENDIAN, 0x12345678))
644     _dbus_assert_not_reached ("could not marshal signed integer value");
645   _dbus_assert (_dbus_demarshal_uint32 (&str, DBUS_BIG_ENDIAN, pos, &pos) == 0x12345678);
646   
647   if (!_dbus_marshal_uint32 (&str, DBUS_LITTLE_ENDIAN, 0x12345678))
648     _dbus_assert_not_reached ("could not marshal signed integer value");
649   _dbus_assert (_dbus_demarshal_uint32 (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == 0x12345678);
650
651   /* Marshal strings */
652   tmp1 = "This is the dbus test string";
653   if (!_dbus_marshal_string (&str, DBUS_BIG_ENDIAN, tmp1))
654     _dbus_assert_not_reached ("could not marshal string");
655   tmp2 = _dbus_demarshal_string (&str, DBUS_BIG_ENDIAN, pos, &pos);
656   _dbus_assert (strcmp (tmp1, tmp2) == 0);
657   dbus_free (tmp2);
658
659   tmp1 = "This is the dbus test string";
660   if (!_dbus_marshal_string (&str, DBUS_LITTLE_ENDIAN, tmp1))
661     _dbus_assert_not_reached ("could not marshal string");
662   tmp2 = _dbus_demarshal_string (&str, DBUS_LITTLE_ENDIAN, pos, &pos);
663   _dbus_assert (strcmp (tmp1, tmp2) == 0);
664   dbus_free (tmp2);
665
666   _dbus_string_free (&str);
667   
668   return TRUE;
669 }
670
671 #endif /* DBUS_BUILD_TESTS */