1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-marshal-recursive.c  Marshalling routines for recursive types
3  *
4  * Copyright (C) 2004, 2005 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23 
24 #include <config.h>
25 #include "dbus-marshal-recursive.h"
26 #include "dbus-marshal-basic.h"
27 #include "dbus-signature.h"
28 #include "dbus-internals.h"
29 
30 /**
31  * @addtogroup DBusMarshal
32  * @{
33  */
34 
35 static dbus_bool_t _dbus_type_reader_greater_than              (const DBusTypeReader  *lhs,
36                                                                 const DBusTypeReader  *rhs);
37 
38 static void       _dbus_type_writer_set_enabled           (DBusTypeWriter        *writer,
39                                                            dbus_bool_t            enabled);
40 static dbus_bool_t _dbus_type_writer_write_reader_partial (DBusTypeWriter        *writer,
41                                                            DBusTypeReader        *reader,
42                                                            const DBusTypeReader  *start_after,
43                                                            int                    start_after_new_pos,
44                                                            int                    start_after_new_len,
45                                                            DBusList             **fixups);
46 
47 /** turn this on to get deluged in TypeReader verbose spam */
48 #define RECURSIVE_MARSHAL_READ_TRACE  0
49 
50 /** turn this on to get deluged in TypeWriter verbose spam */
51 #define RECURSIVE_MARSHAL_WRITE_TRACE 0
52 
53 static void
free_fixups(DBusList ** fixups)54 free_fixups (DBusList **fixups)
55 {
56   DBusList *link;
57 
58   link = _dbus_list_get_first_link (fixups);
59   while (link != NULL)
60     {
61       DBusList *next;
62 
63       next = _dbus_list_get_next_link (fixups, link);
64 
65       dbus_free (link->data);
66       _dbus_list_free_link (link);
67 
68       link = next;
69     }
70 
71   *fixups = NULL;
72 }
73 
74 static void
apply_and_free_fixups(DBusList ** fixups,DBusTypeReader * reader)75 apply_and_free_fixups (DBusList      **fixups,
76                        DBusTypeReader *reader)
77 {
78   DBusList *link;
79 
80 #if RECURSIVE_MARSHAL_WRITE_TRACE
81   if (*fixups)
82     _dbus_verbose (" %d FIXUPS to apply\n",
83                    _dbus_list_get_length (fixups));
84 #endif
85 
86   link = _dbus_list_get_first_link (fixups);
87   while (link != NULL)
88     {
89       DBusList *next;
90 
91       next = _dbus_list_get_next_link (fixups, link);
92 
93       if (reader)
94         {
95           DBusArrayLenFixup *f;
96 
97           f = link->data;
98 
99 #if RECURSIVE_MARSHAL_WRITE_TRACE
100           _dbus_verbose (" applying FIXUP to reader %p at pos %d new_len = %d old len %d\n",
101                          reader, f->len_pos_in_reader, f->new_len,
102                          _dbus_marshal_read_uint32 (reader->value_str,
103                                                     f->len_pos_in_reader,
104                                                     reader->byte_order, NULL));
105 #endif
106 
107           _dbus_marshal_set_uint32 ((DBusString*) reader->value_str,
108                                     f->len_pos_in_reader,
109                                     f->new_len,
110                                     reader->byte_order);
111         }
112 
113       dbus_free (link->data);
114       _dbus_list_free_link (link);
115 
116       link = next;
117     }
118 
119   *fixups = NULL;
120 }
121 
122 /**
123  * Virtual table for a type reader.
124  */
125 struct DBusTypeReaderClass
126 {
127   const char *name;       /**< name for debugging */
128   int         id;         /**< index in all_reader_classes */
129   dbus_bool_t types_only; /**< only iterates over types, not values */
130   void        (* recurse)          (DBusTypeReader        *sub,
131                                     DBusTypeReader        *parent); /**< recurse with this reader as sub */
132   dbus_bool_t (* check_finished)   (const DBusTypeReader  *reader); /**< check whether reader is at the end */
133   void        (* next)             (DBusTypeReader        *reader,
134                                     int                    current_type); /**< go to the next value */
135 };
136 
137 static int
element_type_get_alignment(const DBusString * str,int pos)138 element_type_get_alignment (const DBusString *str,
139                             int               pos)
140 {
141   return _dbus_type_get_alignment (_dbus_first_type_in_signature (str, pos));
142 }
143 
144 static void
reader_init(DBusTypeReader * reader,int byte_order,const DBusString * type_str,int type_pos,const DBusString * value_str,int value_pos)145 reader_init (DBusTypeReader    *reader,
146              int                byte_order,
147              const DBusString  *type_str,
148              int                type_pos,
149              const DBusString  *value_str,
150              int                value_pos)
151 {
152   _DBUS_ZERO (*reader);
153   reader->byte_order = byte_order;
154   reader->finished = FALSE;
155   reader->type_str = type_str;
156   reader->type_pos = type_pos;
157   reader->value_str = value_str;
158   reader->value_pos = value_pos;
159 }
160 
161 static void
base_reader_recurse(DBusTypeReader * sub,DBusTypeReader * parent)162 base_reader_recurse (DBusTypeReader *sub,
163                      DBusTypeReader *parent)
164 {
165   /* point subreader at the same place as parent */
166   reader_init (sub,
167                parent->byte_order,
168                parent->type_str,
169                parent->type_pos,
170                parent->value_str,
171                parent->value_pos);
172 }
173 
174 static void
struct_or_dict_entry_types_only_reader_recurse(DBusTypeReader * sub,DBusTypeReader * parent)175 struct_or_dict_entry_types_only_reader_recurse (DBusTypeReader *sub,
176                                                 DBusTypeReader *parent)
177 {
178   base_reader_recurse (sub, parent);
179 
180   _dbus_assert (_dbus_string_get_byte (sub->type_str,
181                                        sub->type_pos) == DBUS_STRUCT_BEGIN_CHAR ||
182                 _dbus_string_get_byte (sub->type_str,
183                                        sub->type_pos) == DBUS_DICT_ENTRY_BEGIN_CHAR);
184 
185   sub->type_pos += 1;
186 }
187 
188 static void
struct_or_dict_entry_reader_recurse(DBusTypeReader * sub,DBusTypeReader * parent)189 struct_or_dict_entry_reader_recurse (DBusTypeReader *sub,
190                                      DBusTypeReader *parent)
191 {
192   struct_or_dict_entry_types_only_reader_recurse (sub, parent);
193 
194   /* struct and dict entry have 8 byte alignment */
195   sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
196 }
197 
198 static void
array_types_only_reader_recurse(DBusTypeReader * sub,DBusTypeReader * parent)199 array_types_only_reader_recurse (DBusTypeReader *sub,
200                                  DBusTypeReader *parent)
201 {
202   base_reader_recurse (sub, parent);
203 
204   /* point type_pos at the array element type */
205   sub->type_pos += 1;
206 
207   /* Init with values likely to crash things if misused */
208   sub->u.array.start_pos = _DBUS_INT_MAX;
209   sub->array_len_offset = 7;
210 }
211 
212 /** compute position of array length given array_len_offset, which is
213     the offset back from start_pos to end of the len */
214 #define ARRAY_READER_LEN_POS(reader) \
215   ((reader)->u.array.start_pos - ((int)(reader)->array_len_offset) - 4)
216 
217 static int
array_reader_get_array_len(const DBusTypeReader * reader)218 array_reader_get_array_len (const DBusTypeReader *reader)
219 {
220   dbus_uint32_t array_len;
221   int len_pos;
222 
223   len_pos = ARRAY_READER_LEN_POS (reader);
224 
225   _dbus_assert (_DBUS_ALIGN_VALUE (len_pos, 4) == (unsigned) len_pos);
226   array_len = _dbus_unpack_uint32 (reader->byte_order,
227                                    _dbus_string_get_const_udata_len (reader->value_str, len_pos, 4));
228 
229 #if RECURSIVE_MARSHAL_READ_TRACE
230   _dbus_verbose ("   reader %p len_pos %d array len %u len_offset %d\n",
231                  reader, len_pos, array_len, reader->array_len_offset);
232 #endif
233 
234   _dbus_assert (reader->u.array.start_pos - len_pos - 4 < 8);
235 
236   return array_len;
237 }
238 
239 static void
array_reader_recurse(DBusTypeReader * sub,DBusTypeReader * parent)240 array_reader_recurse (DBusTypeReader *sub,
241                       DBusTypeReader *parent)
242 {
243   int alignment;
244   int len_pos;
245 
246   array_types_only_reader_recurse (sub, parent);
247 
248   sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
249 
250   len_pos = sub->value_pos;
251 
252   sub->value_pos += 4; /* for the length */
253 
254   alignment = element_type_get_alignment (sub->type_str,
255                                           sub->type_pos);
256 
257   sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
258 
259   sub->u.array.start_pos = sub->value_pos;
260   _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */
261   sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4);
262 
263 #if RECURSIVE_MARSHAL_READ_TRACE
264   _dbus_verbose ("    type reader %p array start = %d len_offset = %d array len = %d array element type = %s\n",
265                  sub,
266                  sub->u.array.start_pos,
267                  sub->array_len_offset,
268                  array_reader_get_array_len (sub),
269                  _dbus_type_to_string (_dbus_first_type_in_signature (sub->type_str,
270                                                                 sub->type_pos)));
271 #endif
272 }
273 
274 static void
variant_reader_recurse(DBusTypeReader * sub,DBusTypeReader * parent)275 variant_reader_recurse (DBusTypeReader *sub,
276                         DBusTypeReader *parent)
277 {
278   int sig_len;
279   int contained_alignment;
280 
281   base_reader_recurse (sub, parent);
282 
283   /* Variant is 1 byte sig length (without nul), signature with nul,
284    * padding to 8-boundary, then values
285    */
286 
287   sig_len = _dbus_string_get_byte (sub->value_str, sub->value_pos);
288 
289   sub->type_str = sub->value_str;
290   sub->type_pos = sub->value_pos + 1;
291 
292   sub->value_pos = sub->type_pos + sig_len + 1;
293 
294   contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (sub->type_str,
295                                                                            sub->type_pos));
296 
297   sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
298 
299 #if RECURSIVE_MARSHAL_READ_TRACE
300   _dbus_verbose ("    type reader %p variant containing '%s'\n",
301                  sub,
302                  _dbus_string_get_const_data_len (sub->type_str,
303                                                   sub->type_pos, 0));
304 #endif
305 }
306 
307 static dbus_bool_t
array_reader_check_finished(const DBusTypeReader * reader)308 array_reader_check_finished (const DBusTypeReader *reader)
309 {
310   int end_pos;
311 
312   /* return the array element type if elements remain, and
313    * TYPE_INVALID otherwise
314    */
315 
316   end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
317 
318   _dbus_assert (reader->value_pos <= end_pos);
319   _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
320 
321   return reader->value_pos == end_pos;
322 }
323 
324 static void
skip_one_complete_type(const DBusString * type_str,int * type_pos)325 skip_one_complete_type (const DBusString *type_str,
326                         int              *type_pos)
327 {
328   _dbus_type_signature_next (_dbus_string_get_const_data (type_str),
329 			     type_pos);
330 }
331 
332 /**
333  * Skips to the next "complete" type inside a type signature.
334  * The signature is read starting at type_pos, and the next
335  * type position is stored in the same variable.
336  *
337  * @param type_str a type signature (must be valid)
338  * @param type_pos an integer position in the type signature (in and out)
339  */
340 void
_dbus_type_signature_next(const char * type_str,int * type_pos)341 _dbus_type_signature_next (const char       *type_str,
342 			   int              *type_pos)
343 {
344   const unsigned char *p;
345   const unsigned char *start;
346 
347   _dbus_assert (type_str != NULL);
348   _dbus_assert (type_pos != NULL);
349 
350   start = (const unsigned char *)type_str;
351   p = start + *type_pos;
352 
353   _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
354   _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
355 
356   while (*p == DBUS_TYPE_ARRAY)
357     ++p;
358 
359   _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
360   _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
361 
362   if (*p == DBUS_STRUCT_BEGIN_CHAR)
363     {
364       int depth;
365 
366       depth = 1;
367 
368       while (TRUE)
369         {
370           _dbus_assert (*p != DBUS_TYPE_INVALID);
371 
372           ++p;
373 
374           _dbus_assert (*p != DBUS_TYPE_INVALID);
375 
376           if (*p == DBUS_STRUCT_BEGIN_CHAR)
377             depth += 1;
378           else if (*p == DBUS_STRUCT_END_CHAR)
379             {
380               depth -= 1;
381               if (depth == 0)
382                 {
383                   ++p;
384                   break;
385                 }
386             }
387         }
388     }
389   else if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
390     {
391       int depth;
392 
393       depth = 1;
394 
395       while (TRUE)
396         {
397           _dbus_assert (*p != DBUS_TYPE_INVALID);
398 
399           ++p;
400 
401           _dbus_assert (*p != DBUS_TYPE_INVALID);
402 
403           if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
404             depth += 1;
405           else if (*p == DBUS_DICT_ENTRY_END_CHAR)
406             {
407               depth -= 1;
408               if (depth == 0)
409                 {
410                   ++p;
411                   break;
412                 }
413             }
414         }
415     }
416   else
417     {
418       ++p;
419     }
420 
421   *type_pos = (int) (p - start);
422 }
423 
424 static int
find_len_of_complete_type(const DBusString * type_str,int type_pos)425 find_len_of_complete_type (const DBusString *type_str,
426                            int               type_pos)
427 {
428   int end;
429 
430   end = type_pos;
431 
432   skip_one_complete_type (type_str, &end);
433 
434   return end - type_pos;
435 }
436 
437 static void
base_reader_next(DBusTypeReader * reader,int current_type)438 base_reader_next (DBusTypeReader *reader,
439                   int             current_type)
440 {
441   switch (current_type)
442     {
443     case DBUS_TYPE_DICT_ENTRY:
444     case DBUS_TYPE_STRUCT:
445     case DBUS_TYPE_VARIANT:
446       /* Scan forward over the entire container contents */
447       {
448         DBusTypeReader sub;
449 
450         if (reader->klass->types_only && current_type == DBUS_TYPE_VARIANT)
451           ;
452         else
453           {
454             /* Recurse into the struct or variant */
455             _dbus_type_reader_recurse (reader, &sub);
456 
457             /* Skip everything in this subreader */
458             while (_dbus_type_reader_next (&sub))
459               {
460                 /* nothing */;
461               }
462           }
463         if (!reader->klass->types_only)
464           reader->value_pos = sub.value_pos;
465 
466         /* Now we are at the end of this container; for variants, the
467          * subreader's type_pos is totally inapplicable (it's in the
468          * value string) but we know that we increment by one past the
469          * DBUS_TYPE_VARIANT
470          */
471         if (current_type == DBUS_TYPE_VARIANT)
472           reader->type_pos += 1;
473         else
474           reader->type_pos = sub.type_pos;
475       }
476       break;
477 
478     case DBUS_TYPE_ARRAY:
479       {
480         if (!reader->klass->types_only)
481           _dbus_marshal_skip_array (reader->value_str,
482                                     _dbus_first_type_in_signature (reader->type_str,
483                                                                    reader->type_pos + 1),
484                                     reader->byte_order,
485                                     &reader->value_pos);
486 
487         skip_one_complete_type (reader->type_str, &reader->type_pos);
488       }
489       break;
490 
491     default:
492       if (!reader->klass->types_only)
493         _dbus_marshal_skip_basic (reader->value_str,
494                                   current_type, reader->byte_order,
495                                   &reader->value_pos);
496 
497       reader->type_pos += 1;
498       break;
499     }
500 }
501 
502 static void
struct_reader_next(DBusTypeReader * reader,int current_type)503 struct_reader_next (DBusTypeReader *reader,
504                     int             current_type)
505 {
506   int t;
507 
508   base_reader_next (reader, current_type);
509 
510   /* for STRUCT containers we return FALSE at the end of the struct,
511    * for INVALID we return FALSE at the end of the signature.
512    * In both cases we arrange for get_current_type() to return INVALID
513    * which is defined to happen iff we're at the end (no more next())
514    */
515   t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
516   if (t == DBUS_STRUCT_END_CHAR)
517     {
518       reader->type_pos += 1;
519       reader->finished = TRUE;
520     }
521 }
522 
523 static void
dict_entry_reader_next(DBusTypeReader * reader,int current_type)524 dict_entry_reader_next (DBusTypeReader *reader,
525                         int             current_type)
526 {
527   int t;
528 
529   base_reader_next (reader, current_type);
530 
531   /* for STRUCT containers we return FALSE at the end of the struct,
532    * for INVALID we return FALSE at the end of the signature.
533    * In both cases we arrange for get_current_type() to return INVALID
534    * which is defined to happen iff we're at the end (no more next())
535    */
536   t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
537   if (t == DBUS_DICT_ENTRY_END_CHAR)
538     {
539       reader->type_pos += 1;
540       reader->finished = TRUE;
541     }
542 }
543 
544 static void
array_types_only_reader_next(DBusTypeReader * reader,int current_type)545 array_types_only_reader_next (DBusTypeReader *reader,
546                               int             current_type)
547 {
548   /* We have one "element" to be iterated over
549    * in each array, which is its element type.
550    * So the finished flag indicates whether we've
551    * iterated over it yet or not.
552    */
553   reader->finished = TRUE;
554 }
555 
556 static void
array_reader_next(DBusTypeReader * reader,int current_type)557 array_reader_next (DBusTypeReader *reader,
558                    int             current_type)
559 {
560   /* Skip one array element */
561   int end_pos;
562 
563   end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
564 
565 #if RECURSIVE_MARSHAL_READ_TRACE
566   _dbus_verbose ("  reader %p array next START start_pos = %d end_pos = %d value_pos = %d current_type = %s\n",
567                  reader,
568                  reader->u.array.start_pos,
569                  end_pos, reader->value_pos,
570                  _dbus_type_to_string (current_type));
571 #endif
572 
573   _dbus_assert (reader->value_pos < end_pos);
574   _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
575 
576   switch (_dbus_first_type_in_signature (reader->type_str,
577                                          reader->type_pos))
578     {
579     case DBUS_TYPE_DICT_ENTRY:
580     case DBUS_TYPE_STRUCT:
581     case DBUS_TYPE_VARIANT:
582       {
583         DBusTypeReader sub;
584 
585         /* Recurse into the struct or variant */
586         _dbus_type_reader_recurse (reader, &sub);
587 
588         /* Skip everything in this element */
589         while (_dbus_type_reader_next (&sub))
590           {
591             /* nothing */;
592           }
593 
594         /* Now we are at the end of this element */
595         reader->value_pos = sub.value_pos;
596       }
597       break;
598 
599     case DBUS_TYPE_ARRAY:
600       {
601         _dbus_marshal_skip_array (reader->value_str,
602                                   _dbus_first_type_in_signature (reader->type_str,
603                                                            reader->type_pos + 1),
604                                   reader->byte_order,
605                                   &reader->value_pos);
606       }
607       break;
608 
609     default:
610       {
611         _dbus_marshal_skip_basic (reader->value_str,
612                                   current_type, reader->byte_order,
613                                   &reader->value_pos);
614       }
615       break;
616     }
617 
618 #if RECURSIVE_MARSHAL_READ_TRACE
619   _dbus_verbose ("  reader %p array next END start_pos = %d end_pos = %d value_pos = %d current_type = %s\n",
620                  reader,
621                  reader->u.array.start_pos,
622                  end_pos, reader->value_pos,
623                  _dbus_type_to_string (current_type));
624 #endif
625 
626   _dbus_assert (reader->value_pos <= end_pos);
627 
628   if (reader->value_pos == end_pos)
629     {
630       skip_one_complete_type (reader->type_str,
631                               &reader->type_pos);
632     }
633 }
634 
635 static const DBusTypeReaderClass body_reader_class = {
636   "body", 0,
637   FALSE,
638   NULL, /* body is always toplevel, so doesn't get recursed into */
639   NULL,
640   base_reader_next
641 };
642 
643 static const DBusTypeReaderClass body_types_only_reader_class = {
644   "body types", 1,
645   TRUE,
646   NULL, /* body is always toplevel, so doesn't get recursed into */
647   NULL,
648   base_reader_next
649 };
650 
651 static const DBusTypeReaderClass struct_reader_class = {
652   "struct", 2,
653   FALSE,
654   struct_or_dict_entry_reader_recurse,
655   NULL,
656   struct_reader_next
657 };
658 
659 static const DBusTypeReaderClass struct_types_only_reader_class = {
660   "struct types", 3,
661   TRUE,
662   struct_or_dict_entry_types_only_reader_recurse,
663   NULL,
664   struct_reader_next
665 };
666 
667 static const DBusTypeReaderClass dict_entry_reader_class = {
668   "dict_entry", 4,
669   FALSE,
670   struct_or_dict_entry_reader_recurse,
671   NULL,
672   dict_entry_reader_next
673 };
674 
675 static const DBusTypeReaderClass dict_entry_types_only_reader_class = {
676   "dict_entry types", 5,
677   TRUE,
678   struct_or_dict_entry_types_only_reader_recurse,
679   NULL,
680   dict_entry_reader_next
681 };
682 
683 static const DBusTypeReaderClass array_reader_class = {
684   "array", 6,
685   FALSE,
686   array_reader_recurse,
687   array_reader_check_finished,
688   array_reader_next
689 };
690 
691 static const DBusTypeReaderClass array_types_only_reader_class = {
692   "array types", 7,
693   TRUE,
694   array_types_only_reader_recurse,
695   NULL,
696   array_types_only_reader_next
697 };
698 
699 static const DBusTypeReaderClass variant_reader_class = {
700   "variant", 8,
701   FALSE,
702   variant_reader_recurse,
703   NULL,
704   base_reader_next
705 };
706 
707 #ifndef DBUS_DISABLE_ASSERT
708 static const DBusTypeReaderClass * const
709 all_reader_classes[] = {
710   &body_reader_class,
711   &body_types_only_reader_class,
712   &struct_reader_class,
713   &struct_types_only_reader_class,
714   &dict_entry_reader_class,
715   &dict_entry_types_only_reader_class,
716   &array_reader_class,
717   &array_types_only_reader_class,
718   &variant_reader_class
719 };
720 #endif
721 
722 /**
723  * Initializes a type reader.
724  *
725  * @param reader the reader
726  * @param byte_order the byte order of the block to read
727  * @param type_str the signature of the block to read
728  * @param type_pos location of signature
729  * @param value_str the string containing values block
730  * @param value_pos start of values block
731  */
732 void
_dbus_type_reader_init(DBusTypeReader * reader,int byte_order,const DBusString * type_str,int type_pos,const DBusString * value_str,int value_pos)733 _dbus_type_reader_init (DBusTypeReader    *reader,
734                         int                byte_order,
735                         const DBusString  *type_str,
736                         int                type_pos,
737                         const DBusString  *value_str,
738                         int                value_pos)
739 {
740   reader_init (reader, byte_order, type_str, type_pos,
741                value_str, value_pos);
742 
743   reader->klass = &body_reader_class;
744 
745 #if RECURSIVE_MARSHAL_READ_TRACE
746   _dbus_verbose ("  type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n",
747                  reader, reader->type_pos, reader->value_pos,
748                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
749 #endif
750 }
751 
752 /**
753  * Like _dbus_type_reader_init() but the iteration is over the
754  * signature, not over values.
755  *
756  * @param reader the reader
757  * @param type_str the signature string
758  * @param type_pos location in the signature string
759  */
760 void
_dbus_type_reader_init_types_only(DBusTypeReader * reader,const DBusString * type_str,int type_pos)761 _dbus_type_reader_init_types_only (DBusTypeReader    *reader,
762                                    const DBusString  *type_str,
763                                    int                type_pos)
764 {
765   reader_init (reader, DBUS_COMPILER_BYTE_ORDER /* irrelevant */,
766                type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */);
767 
768   reader->klass = &body_types_only_reader_class;
769 
770 #if RECURSIVE_MARSHAL_READ_TRACE
771   _dbus_verbose ("  type reader %p init types only type_pos = %d remaining sig '%s'\n",
772                  reader, reader->type_pos,
773                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
774 #endif
775 }
776 
777 /**
778  * Gets the type of the value the reader is currently pointing to;
779  * or for a types-only reader gets the type it's currently pointing to.
780  * If the reader is at the end of a block or end of a container such
781  * as an array, returns #DBUS_TYPE_INVALID.
782  *
783  * @param reader the reader
784  */
785 int
_dbus_type_reader_get_current_type(const DBusTypeReader * reader)786 _dbus_type_reader_get_current_type (const DBusTypeReader *reader)
787 {
788   int t;
789 
790   if (reader->finished ||
791       (reader->klass->check_finished &&
792        (* reader->klass->check_finished) (reader)))
793     t = DBUS_TYPE_INVALID;
794   else
795     t = _dbus_first_type_in_signature (reader->type_str,
796                                        reader->type_pos);
797 
798   _dbus_assert (t != DBUS_STRUCT_END_CHAR);
799   _dbus_assert (t != DBUS_STRUCT_BEGIN_CHAR);
800   _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR);
801   _dbus_assert (t != DBUS_DICT_ENTRY_BEGIN_CHAR);
802 
803 #if 0
804   _dbus_verbose ("  type reader %p current type_pos = %d type = %s\n",
805                  reader, reader->type_pos,
806                  _dbus_type_to_string (t));
807 #endif
808 
809   return t;
810 }
811 
812 /**
813  * Gets the type of an element of the array the reader is currently
814  * pointing to. It's an error to call this if
815  * _dbus_type_reader_get_current_type() doesn't return #DBUS_TYPE_ARRAY
816  * for this reader.
817  *
818  * @param reader the reader
819  */
820 int
_dbus_type_reader_get_element_type(const DBusTypeReader * reader)821 _dbus_type_reader_get_element_type (const DBusTypeReader  *reader)
822 {
823   int element_type;
824 
825   _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_ARRAY);
826 
827   element_type = _dbus_first_type_in_signature (reader->type_str,
828                                           reader->type_pos + 1);
829 
830   return element_type;
831 }
832 
833 /**
834  * Gets the current position in the value block
835  * @param reader the reader
836  */
837 int
_dbus_type_reader_get_value_pos(const DBusTypeReader * reader)838 _dbus_type_reader_get_value_pos (const DBusTypeReader  *reader)
839 {
840   return reader->value_pos;
841 }
842 
843 /**
844  * Get the address of the marshaled value in the data being read.  The
845  * address may not be aligned; you have to align it to the type of the
846  * value you want to read. Most of the demarshal routines do this for
847  * you.
848  *
849  * @param reader the reader
850  * @param value_location the address of the marshaled value
851  */
852 void
_dbus_type_reader_read_raw(const DBusTypeReader * reader,const unsigned char ** value_location)853 _dbus_type_reader_read_raw (const DBusTypeReader  *reader,
854                             const unsigned char  **value_location)
855 {
856   _dbus_assert (!reader->klass->types_only);
857 
858   *value_location = _dbus_string_get_const_udata_len (reader->value_str,
859                                                       reader->value_pos,
860                                                       0);
861 }
862 
863 /**
864  * Reads a basic-typed value, as with _dbus_marshal_read_basic().
865  *
866  * @param reader the reader
867  * @param value the address of the value
868  */
869 void
_dbus_type_reader_read_basic(const DBusTypeReader * reader,void * value)870 _dbus_type_reader_read_basic (const DBusTypeReader    *reader,
871                               void                    *value)
872 {
873   int t;
874 
875   _dbus_assert (!reader->klass->types_only);
876 
877   t = _dbus_type_reader_get_current_type (reader);
878 
879   _dbus_marshal_read_basic (reader->value_str,
880                             reader->value_pos,
881                             t, value,
882                             reader->byte_order,
883                             NULL);
884 
885 
886 #if RECURSIVE_MARSHAL_READ_TRACE
887   _dbus_verbose ("  type reader %p read basic type_pos = %d value_pos = %d remaining sig '%s'\n",
888                  reader, reader->type_pos, reader->value_pos,
889                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
890 #endif
891 }
892 
893 /**
894  * Returns the number of bytes in the array.
895  *
896  * @param reader the reader to read from
897  * @returns the number of bytes in the array
898  */
899 int
_dbus_type_reader_get_array_length(const DBusTypeReader * reader)900 _dbus_type_reader_get_array_length (const DBusTypeReader  *reader)
901 {
902   _dbus_assert (!reader->klass->types_only);
903   _dbus_assert (reader->klass == &array_reader_class);
904 
905   return array_reader_get_array_len (reader);
906 }
907 
908 /**
909  * Reads a block of fixed-length basic values, from the current point
910  * in an array to the end of the array.  Does not work for arrays of
911  * string or container types.
912  *
913  * This function returns the array in-place; it does not make a copy,
914  * and it does not swap the bytes.
915  *
916  * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back
917  * and the "value" argument should be a "const double**" and so on.
918  *
919  * @param reader the reader to read from
920  * @param value place to return the array values
921  * @param n_elements place to return number of array elements
922  */
923 void
_dbus_type_reader_read_fixed_multi(const DBusTypeReader * reader,void * value,int * n_elements)924 _dbus_type_reader_read_fixed_multi (const DBusTypeReader  *reader,
925                                     void                  *value,
926                                     int                   *n_elements)
927 {
928   int element_type;
929   int end_pos;
930   int remaining_len;
931   int alignment;
932   int total_len;
933 
934   _dbus_assert (!reader->klass->types_only);
935   _dbus_assert (reader->klass == &array_reader_class);
936 
937   element_type = _dbus_first_type_in_signature (reader->type_str,
938                                                 reader->type_pos);
939 
940   _dbus_assert (element_type != DBUS_TYPE_INVALID); /* why we don't use get_current_type() */
941   _dbus_assert (dbus_type_is_fixed (element_type));
942 
943   alignment = _dbus_type_get_alignment (element_type);
944 
945   _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
946 
947   total_len = array_reader_get_array_len (reader);
948   end_pos = reader->u.array.start_pos + total_len;
949   remaining_len = end_pos - reader->value_pos;
950 
951 #if RECURSIVE_MARSHAL_READ_TRACE
952   _dbus_verbose ("end_pos %d total_len %d remaining_len %d value_pos %d\n",
953                  end_pos, total_len, remaining_len, reader->value_pos);
954 #endif
955 
956   _dbus_assert (remaining_len <= total_len);
957 
958   if (remaining_len == 0)
959     *(const DBusBasicValue**) value = NULL;
960   else
961     *(const DBusBasicValue**) value =
962       (void*) _dbus_string_get_const_data_len (reader->value_str,
963                                                reader->value_pos,
964                                                remaining_len);
965 
966   *n_elements = remaining_len / alignment;
967   _dbus_assert ((remaining_len % alignment) == 0);
968 
969 #if RECURSIVE_MARSHAL_READ_TRACE
970   _dbus_verbose ("  type reader %p read fixed array type_pos = %d value_pos = %d remaining sig '%s'\n",
971                  reader, reader->type_pos, reader->value_pos,
972                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
973 #endif
974 }
975 
976 /**
977  * Initialize a new reader pointing to the first type and
978  * corresponding value that's a child of the current container. It's
979  * an error to call this if the current type is a non-container.
980  *
981  * Note that DBusTypeReader traverses values, not types. So if you
982  * have an empty array of array of int, you can't recurse into it. You
983  * can only recurse into each element.
984  *
985  * @param reader the reader
986  * @param sub a reader to init pointing to the first child
987  */
988 void
_dbus_type_reader_recurse(DBusTypeReader * reader,DBusTypeReader * sub)989 _dbus_type_reader_recurse (DBusTypeReader *reader,
990                            DBusTypeReader *sub)
991 {
992   int t;
993   const DBusTypeReaderClass *klass = NULL;
994 
995   t = _dbus_first_type_in_signature (reader->type_str, reader->type_pos);
996 
997   switch (t)
998     {
999     case DBUS_TYPE_STRUCT:
1000       if (reader->klass->types_only)
1001         klass = &struct_types_only_reader_class;
1002       else
1003         klass = &struct_reader_class;
1004       break;
1005     case DBUS_TYPE_DICT_ENTRY:
1006       if (reader->klass->types_only)
1007         klass = &dict_entry_types_only_reader_class;
1008       else
1009         klass = &dict_entry_reader_class;
1010       break;
1011     case DBUS_TYPE_ARRAY:
1012       if (reader->klass->types_only)
1013         klass = &array_types_only_reader_class;
1014       else
1015         klass = &array_reader_class;
1016       break;
1017     case DBUS_TYPE_VARIANT:
1018       if (reader->klass->types_only)
1019         _dbus_assert_not_reached ("can't recurse into variant typecode");
1020       else
1021         klass = &variant_reader_class;
1022       break;
1023     default:
1024       _dbus_verbose ("recursing into type %s\n", _dbus_type_to_string (t));
1025 #ifndef DBUS_DISABLE_CHECKS
1026       if (t == DBUS_TYPE_INVALID)
1027         _dbus_warn_check_failed ("You can't recurse into an empty array or off the end of a message body");
1028 #endif /* DBUS_DISABLE_CHECKS */
1029 
1030       _dbus_assert_not_reached ("don't yet handle recursing into this type");
1031     }
1032 
1033   _dbus_assert (klass != NULL);
1034   _dbus_assert (klass == all_reader_classes[klass->id]);
1035 
1036   (* klass->recurse) (sub, reader);
1037   sub->klass = klass;
1038 
1039 #if RECURSIVE_MARSHAL_READ_TRACE
1040   _dbus_verbose ("  type reader %p RECURSED type_pos = %d value_pos = %d remaining sig '%s'\n",
1041                  sub, sub->type_pos, sub->value_pos,
1042                  _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0));
1043 #endif
1044 }
1045 
1046 /**
1047  * Skip to the next value on this "level". e.g. the next field in a
1048  * struct, the next value in an array. Returns FALSE at the end of the
1049  * current container.
1050  *
1051  * @param reader the reader
1052  * @returns FALSE if nothing more to read at or below this level
1053  */
1054 dbus_bool_t
_dbus_type_reader_next(DBusTypeReader * reader)1055 _dbus_type_reader_next (DBusTypeReader *reader)
1056 {
1057   int t;
1058 
1059   t = _dbus_type_reader_get_current_type (reader);
1060 
1061 #if RECURSIVE_MARSHAL_READ_TRACE
1062   _dbus_verbose ("  type reader %p START next() { type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n",
1063                  reader, reader->type_pos, reader->value_pos,
1064                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
1065                  _dbus_type_to_string (t));
1066 #endif
1067 
1068   if (t == DBUS_TYPE_INVALID)
1069     return FALSE;
1070 
1071   (* reader->klass->next) (reader, t);
1072 
1073 #if RECURSIVE_MARSHAL_READ_TRACE
1074   _dbus_verbose ("  type reader %p END next() type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n",
1075                  reader, reader->type_pos, reader->value_pos,
1076                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
1077                  _dbus_type_to_string (_dbus_type_reader_get_current_type (reader)));
1078 #endif
1079 
1080   return _dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID;
1081 }
1082 
1083 /**
1084  * Check whether there's another value on this "level". e.g. the next
1085  * field in a struct, the next value in an array. Returns FALSE at the
1086  * end of the current container.
1087  *
1088  * You probably don't want to use this; it makes for an awkward for/while
1089  * loop. A nicer one is "while ((current_type = get_current_type()) != INVALID)"
1090  *
1091  * @param reader the reader
1092  * @returns FALSE if nothing more to read at or below this level
1093  */
1094 dbus_bool_t
_dbus_type_reader_has_next(const DBusTypeReader * reader)1095 _dbus_type_reader_has_next (const DBusTypeReader *reader)
1096 {
1097   /* Not efficient but works for now. */
1098   DBusTypeReader copy;
1099 
1100   copy = *reader;
1101   return _dbus_type_reader_next (&copy);
1102 }
1103 
1104 /**
1105  * Gets the string and range of said string containing the signature
1106  * of the current value. Essentially a more complete version of
1107  * _dbus_type_reader_get_current_type() (returns the full type
1108  * rather than only the outside of the onion).
1109  *
1110  * Note though that the first byte in a struct signature is
1111  * #DBUS_STRUCT_BEGIN_CHAR while the current type will be
1112  * #DBUS_TYPE_STRUCT so it isn't true that the first byte of the
1113  * signature is always the same as the current type. Another
1114  * difference is that this function will still return a signature when
1115  * inside an empty array; say you recurse into empty array of int32,
1116  * the signature is "i" but the current type will always be
1117  * #DBUS_TYPE_INVALID since there are no elements to be currently
1118  * pointing to.
1119  *
1120  * @param reader the reader
1121  * @param str_p place to return the string with the type in it
1122  * @param start_p place to return start of the type
1123  * @param len_p place to return the length of the type
1124  */
1125 void
_dbus_type_reader_get_signature(const DBusTypeReader * reader,const DBusString ** str_p,int * start_p,int * len_p)1126 _dbus_type_reader_get_signature (const DBusTypeReader  *reader,
1127                                  const DBusString     **str_p,
1128                                  int                   *start_p,
1129                                  int                   *len_p)
1130 {
1131   *str_p = reader->type_str;
1132   *start_p = reader->type_pos;
1133   *len_p = find_len_of_complete_type (reader->type_str, reader->type_pos);
1134 }
1135 
1136 typedef struct
1137 {
1138   DBusString replacement; /**< Marshaled value including alignment padding */
1139   int padding;            /**< How much of the replacement block is padding */
1140 } ReplacementBlock;
1141 
1142 static dbus_bool_t
replacement_block_init(ReplacementBlock * block,DBusTypeReader * reader)1143 replacement_block_init (ReplacementBlock *block,
1144                         DBusTypeReader   *reader)
1145 {
1146   if (!_dbus_string_init (&block->replacement))
1147     return FALSE;
1148 
1149   /* % 8 is the padding to have the same align properties in
1150    * our replacement string as we do at the position being replaced
1151    */
1152   block->padding = reader->value_pos % 8;
1153 
1154   if (!_dbus_string_lengthen (&block->replacement, block->padding))
1155     goto oom;
1156 
1157   return TRUE;
1158 
1159  oom:
1160   _dbus_string_free (&block->replacement);
1161   return FALSE;
1162 }
1163 
1164 static dbus_bool_t
replacement_block_replace(ReplacementBlock * block,DBusTypeReader * reader,const DBusTypeReader * realign_root)1165 replacement_block_replace (ReplacementBlock     *block,
1166                            DBusTypeReader       *reader,
1167                            const DBusTypeReader *realign_root)
1168 {
1169   DBusTypeWriter writer;
1170   DBusTypeReader realign_reader;
1171   DBusList *fixups;
1172   int orig_len;
1173 
1174   _dbus_assert (realign_root != NULL);
1175 
1176   orig_len = _dbus_string_get_length (&block->replacement);
1177 
1178   realign_reader = *realign_root;
1179 
1180 #if RECURSIVE_MARSHAL_WRITE_TRACE
1181   _dbus_verbose ("INITIALIZING replacement block writer %p at value_pos %d\n",
1182                  &writer, _dbus_string_get_length (&block->replacement));
1183 #endif
1184   _dbus_type_writer_init_values_only (&writer,
1185                                       realign_reader.byte_order,
1186                                       realign_reader.type_str,
1187                                       realign_reader.type_pos,
1188                                       &block->replacement,
1189                                       _dbus_string_get_length (&block->replacement));
1190 
1191   _dbus_assert (realign_reader.value_pos <= reader->value_pos);
1192 
1193 #if RECURSIVE_MARSHAL_WRITE_TRACE
1194   _dbus_verbose ("COPYING from reader at value_pos %d to writer %p starting after value_pos %d\n",
1195                  realign_reader.value_pos, &writer, reader->value_pos);
1196 #endif
1197   fixups = NULL;
1198   if (!_dbus_type_writer_write_reader_partial (&writer,
1199                                                &realign_reader,
1200                                                reader,
1201                                                block->padding,
1202                                                _dbus_string_get_length (&block->replacement) - block->padding,
1203                                                &fixups))
1204     goto oom;
1205 
1206 #if RECURSIVE_MARSHAL_WRITE_TRACE
1207   _dbus_verbose ("REPLACEMENT at padding %d len %d\n", block->padding,
1208                  _dbus_string_get_length (&block->replacement) - block->padding);
1209   _dbus_verbose_bytes_of_string (&block->replacement, block->padding,
1210                                  _dbus_string_get_length (&block->replacement) - block->padding);
1211   _dbus_verbose ("TO BE REPLACED at value_pos = %d (align pad %d) len %d realign_reader.value_pos %d\n",
1212                  reader->value_pos, reader->value_pos % 8,
1213                  realign_reader.value_pos - reader->value_pos,
1214                  realign_reader.value_pos);
1215   _dbus_verbose_bytes_of_string (reader->value_str,
1216                                  reader->value_pos,
1217                                  realign_reader.value_pos - reader->value_pos);
1218 #endif
1219 
1220   /* Move the replacement into position
1221    * (realign_reader should now be at the end of the block to be replaced)
1222    */
1223   if (!_dbus_string_replace_len (&block->replacement, block->padding,
1224                                  _dbus_string_get_length (&block->replacement) - block->padding,
1225                                  (DBusString*) reader->value_str,
1226                                  reader->value_pos,
1227                                  realign_reader.value_pos - reader->value_pos))
1228     goto oom;
1229 
1230   /* Process our fixups now that we can't have an OOM error */
1231   apply_and_free_fixups (&fixups, reader);
1232 
1233   return TRUE;
1234 
1235  oom:
1236   _dbus_string_set_length (&block->replacement, orig_len);
1237   free_fixups (&fixups);
1238   return FALSE;
1239 }
1240 
1241 static void
replacement_block_free(ReplacementBlock * block)1242 replacement_block_free (ReplacementBlock *block)
1243 {
1244   _dbus_string_free (&block->replacement);
1245 }
1246 
1247 /* In the variable-length case, we have to fix alignment after we insert.
1248  * The strategy is as follows:
1249  *
1250  *  - pad a new string to have the same alignment as the
1251  *    start of the current basic value
1252  *  - write the new basic value
1253  *  - copy from the original reader to the new string,
1254  *    which will fix the alignment of types following
1255  *    the new value
1256  *    - this copy has to start at realign_root,
1257  *      but not really write anything until it
1258  *      passes the value being set
1259  *    - as an optimization, we can stop copying
1260  *      when the source and dest values are both
1261  *      on an 8-boundary, since we know all following
1262  *      padding and alignment will be identical
1263  *  - copy the new string back to the original
1264  *    string, replacing the relevant part of the
1265  *    original string
1266  *  - now any arrays in the original string that
1267  *    contained the replaced string may have the
1268  *    wrong length; so we have to fix that
1269  */
1270 static dbus_bool_t
reader_set_basic_variable_length(DBusTypeReader * reader,int current_type,const void * value,const DBusTypeReader * realign_root)1271 reader_set_basic_variable_length (DBusTypeReader       *reader,
1272                                   int                   current_type,
1273                                   const void           *value,
1274                                   const DBusTypeReader *realign_root)
1275 {
1276   dbus_bool_t retval;
1277   ReplacementBlock block;
1278   DBusTypeWriter writer;
1279 
1280   _dbus_assert (realign_root != NULL);
1281 
1282   retval = FALSE;
1283 
1284   if (!replacement_block_init (&block, reader))
1285     return FALSE;
1286 
1287   /* Write the new basic value */
1288 #if RECURSIVE_MARSHAL_WRITE_TRACE
1289   _dbus_verbose ("INITIALIZING writer %p to write basic value at value_pos %d of replacement string\n",
1290                  &writer, _dbus_string_get_length (&block.replacement));
1291 #endif
1292   _dbus_type_writer_init_values_only (&writer,
1293                                       reader->byte_order,
1294                                       reader->type_str,
1295                                       reader->type_pos,
1296                                       &block.replacement,
1297                                       _dbus_string_get_length (&block.replacement));
1298 #if RECURSIVE_MARSHAL_WRITE_TRACE
1299   _dbus_verbose ("WRITING basic value to writer %p (replacement string)\n", &writer);
1300 #endif
1301   if (!_dbus_type_writer_write_basic (&writer, current_type, value))
1302     goto out;
1303 
1304   if (!replacement_block_replace (&block,
1305                                   reader,
1306                                   realign_root))
1307     goto out;
1308 
1309   retval = TRUE;
1310 
1311  out:
1312   replacement_block_free (&block);
1313   return retval;
1314 }
1315 
1316 static void
reader_set_basic_fixed_length(DBusTypeReader * reader,int current_type,const void * value)1317 reader_set_basic_fixed_length (DBusTypeReader *reader,
1318                                int             current_type,
1319                                const void     *value)
1320 {
1321   _dbus_marshal_set_basic ((DBusString*) reader->value_str,
1322                            reader->value_pos,
1323                            current_type,
1324                            value,
1325                            reader->byte_order,
1326                            NULL, NULL);
1327 }
1328 
1329 /**
1330  * Sets a new value for the basic type value pointed to by the reader,
1331  * leaving the reader valid to continue reading. Any other readers
1332  * will be invalidated if you set a variable-length type such as a
1333  * string.
1334  *
1335  * The provided realign_root is the reader to start from when
1336  * realigning the data that follows the newly-set value. The reader
1337  * parameter must point to a value below the realign_root parameter.
1338  * If the type being set is fixed-length, then realign_root may be
1339  * #NULL. Only values reachable from realign_root will be realigned,
1340  * so if your string contains other values you will need to deal with
1341  * those somehow yourself. It is OK if realign_root is the same
1342  * reader as the reader parameter, though if you aren't setting the
1343  * root it may not be such a good idea.
1344  *
1345  * @todo DBusTypeReader currently takes "const" versions of the type
1346  * and value strings, and this function modifies those strings by
1347  * casting away the const, which is of course bad if we want to get
1348  * picky. (To be truly clean you'd have an object which contained the
1349  * type and value strings and set_basic would be a method on that
1350  * object... this would also make DBusTypeReader the same thing as
1351  * DBusTypeMark. But since DBusMessage is effectively that object for
1352  * D-Bus it doesn't seem worth creating some random object.)
1353  *
1354  * @todo optimize this by only rewriting until the old and new values
1355  * are at the same alignment. Frequently this should result in only
1356  * replacing the value that's immediately at hand.
1357  *
1358  * @param reader reader indicating where to set a new value
1359  * @param value address of the value to set
1360  * @param realign_root realign from here
1361  * @returns #FALSE if not enough memory
1362  */
1363 dbus_bool_t
_dbus_type_reader_set_basic(DBusTypeReader * reader,const void * value,const DBusTypeReader * realign_root)1364 _dbus_type_reader_set_basic (DBusTypeReader       *reader,
1365                              const void           *value,
1366                              const DBusTypeReader *realign_root)
1367 {
1368   int current_type;
1369 
1370   _dbus_assert (!reader->klass->types_only);
1371   _dbus_assert (reader->value_str == realign_root->value_str);
1372   _dbus_assert (reader->value_pos >= realign_root->value_pos);
1373 
1374   current_type = _dbus_type_reader_get_current_type (reader);
1375 
1376 #if RECURSIVE_MARSHAL_WRITE_TRACE
1377   _dbus_verbose ("  SET BASIC type reader %p type_pos = %d value_pos = %d remaining sig '%s' realign_root = %p with value_pos %d current_type = %s\n",
1378                  reader, reader->type_pos, reader->value_pos,
1379                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
1380                  realign_root,
1381                  realign_root ? realign_root->value_pos : -1,
1382                  _dbus_type_to_string (current_type));
1383   _dbus_verbose_bytes_of_string (realign_root->value_str, realign_root->value_pos,
1384                                  _dbus_string_get_length (realign_root->value_str) -
1385                                  realign_root->value_pos);
1386 #endif
1387 
1388   _dbus_assert (dbus_type_is_basic (current_type));
1389 
1390   if (dbus_type_is_fixed (current_type))
1391     {
1392       reader_set_basic_fixed_length (reader, current_type, value);
1393       return TRUE;
1394     }
1395   else
1396     {
1397       _dbus_assert (realign_root != NULL);
1398       return reader_set_basic_variable_length (reader, current_type,
1399                                                value, realign_root);
1400     }
1401 }
1402 
1403 /**
1404  * Recursively deletes any value pointed to by the reader, leaving the
1405  * reader valid to continue reading. Any other readers will be
1406  * invalidated.
1407  *
1408  * The provided realign_root is the reader to start from when
1409  * realigning the data that follows the newly-set value.
1410  * See _dbus_type_reader_set_basic() for more details on the
1411  * realign_root paramter.
1412  *
1413  * @todo for now this does not delete the typecodes associated with
1414  * the value, so this function should only be used for array elements.
1415  *
1416  * @param reader reader indicating where to delete a value
1417  * @param realign_root realign from here
1418  * @returns #FALSE if not enough memory
1419  */
1420 dbus_bool_t
_dbus_type_reader_delete(DBusTypeReader * reader,const DBusTypeReader * realign_root)1421 _dbus_type_reader_delete (DBusTypeReader        *reader,
1422                           const DBusTypeReader  *realign_root)
1423 {
1424   dbus_bool_t retval;
1425   ReplacementBlock block;
1426 
1427   _dbus_assert (realign_root != NULL);
1428   _dbus_assert (reader->klass == &array_reader_class);
1429 
1430   retval = FALSE;
1431 
1432   if (!replacement_block_init (&block, reader))
1433     return FALSE;
1434 
1435   if (!replacement_block_replace (&block,
1436                                   reader,
1437                                   realign_root))
1438     goto out;
1439 
1440   retval = TRUE;
1441 
1442  out:
1443   replacement_block_free (&block);
1444   return retval;
1445 }
1446 
1447 /*
1448  * Compares two readers, which must be iterating over the same value data.
1449  * Returns #TRUE if the first parameter is further along than the second parameter.
1450  *
1451  * @param lhs left-hand-side (first) parameter
1452  * @param rhs left-hand-side (first) parameter
1453  * @returns whether lhs is greater than rhs
1454  */
1455 static dbus_bool_t
_dbus_type_reader_greater_than(const DBusTypeReader * lhs,const DBusTypeReader * rhs)1456 _dbus_type_reader_greater_than (const DBusTypeReader  *lhs,
1457                                 const DBusTypeReader  *rhs)
1458 {
1459   _dbus_assert (lhs->value_str == rhs->value_str);
1460 
1461   return lhs->value_pos > rhs->value_pos;
1462 }
1463 
1464 /*
1465  *
1466  *
1467  *         DBusTypeWriter
1468  *
1469  *
1470  *
1471  */
1472 
1473 /**
1474  * Initialize a write iterator, which is used to write out values in
1475  * serialized D-Bus format.
1476  *
1477  * The type_pos passed in is expected to be inside an already-valid,
1478  * though potentially empty, type signature. This means that the byte
1479  * after type_pos must be either #DBUS_TYPE_INVALID (aka nul) or some
1480  * other valid type. #DBusTypeWriter won't enforce that the signature
1481  * is already valid (you can append the nul byte at the end if you
1482  * like), but just be aware that you need the nul byte eventually and
1483  * #DBusTypeWriter isn't going to write it for you.
1484  *
1485  * @param writer the writer to init
1486  * @param byte_order the byte order to marshal into
1487  * @param type_str the string to write typecodes into
1488  * @param type_pos where to insert typecodes
1489  * @param value_str the string to write values into
1490  * @param value_pos where to insert values
1491  *
1492  */
1493 void
_dbus_type_writer_init(DBusTypeWriter * writer,int byte_order,DBusString * type_str,int type_pos,DBusString * value_str,int value_pos)1494 _dbus_type_writer_init (DBusTypeWriter *writer,
1495                         int             byte_order,
1496                         DBusString     *type_str,
1497                         int             type_pos,
1498                         DBusString     *value_str,
1499                         int             value_pos)
1500 {
1501   writer->byte_order = byte_order;
1502   writer->type_str = type_str;
1503   writer->type_pos = type_pos;
1504   writer->value_str = value_str;
1505   writer->value_pos = value_pos;
1506   writer->container_type = DBUS_TYPE_INVALID;
1507   writer->type_pos_is_expectation = FALSE;
1508   writer->enabled = TRUE;
1509 
1510 #if RECURSIVE_MARSHAL_WRITE_TRACE
1511   _dbus_verbose ("writer %p init remaining sig '%s'\n", writer,
1512                  writer->type_str ?
1513                  _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
1514                  "unknown");
1515 #endif
1516 }
1517 
1518 /**
1519  * Initialize a write iterator, with the signature to be provided
1520  * later.
1521  *
1522  * @param writer the writer to init
1523  * @param byte_order the byte order to marshal into
1524  * @param value_str the string to write values into
1525  * @param value_pos where to insert values
1526  *
1527  */
1528 void
_dbus_type_writer_init_types_delayed(DBusTypeWriter * writer,int byte_order,DBusString * value_str,int value_pos)1529 _dbus_type_writer_init_types_delayed (DBusTypeWriter *writer,
1530                                       int             byte_order,
1531                                       DBusString     *value_str,
1532                                       int             value_pos)
1533 {
1534   _dbus_type_writer_init (writer, byte_order,
1535                           NULL, 0, value_str, value_pos);
1536 }
1537 
1538 /**
1539  * Adds type string to the writer, if it had none.
1540  *
1541  * @param writer the writer to init
1542  * @param type_str type string to add
1543  * @param type_pos type position
1544  *
1545  */
1546 void
_dbus_type_writer_add_types(DBusTypeWriter * writer,DBusString * type_str,int type_pos)1547 _dbus_type_writer_add_types (DBusTypeWriter *writer,
1548                              DBusString     *type_str,
1549                              int             type_pos)
1550 {
1551   if (writer->type_str == NULL) /* keeps us from using this as setter */
1552     {
1553       writer->type_str = type_str;
1554       writer->type_pos = type_pos;
1555     }
1556 }
1557 
1558 /**
1559  * Removes type string from the writer.
1560  *
1561  * @param writer the writer to remove from
1562  */
1563 void
_dbus_type_writer_remove_types(DBusTypeWriter * writer)1564 _dbus_type_writer_remove_types (DBusTypeWriter *writer)
1565 {
1566   writer->type_str = NULL;
1567   writer->type_pos = -1;
1568 }
1569 
1570 /**
1571  * Like _dbus_type_writer_init(), except the type string
1572  * passed in should correspond to an existing signature that
1573  * matches what you're going to write out. The writer will
1574  * check what you write vs. this existing signature.
1575  *
1576  * @param writer the writer to init
1577  * @param byte_order the byte order to marshal into
1578  * @param type_str the string with signature
1579  * @param type_pos start of signature
1580  * @param value_str the string to write values into
1581  * @param value_pos where to insert values
1582  *
1583  */
1584 void
_dbus_type_writer_init_values_only(DBusTypeWriter * writer,int byte_order,const DBusString * type_str,int type_pos,DBusString * value_str,int value_pos)1585 _dbus_type_writer_init_values_only (DBusTypeWriter   *writer,
1586                                     int               byte_order,
1587                                     const DBusString *type_str,
1588                                     int               type_pos,
1589                                     DBusString       *value_str,
1590                                     int               value_pos)
1591 {
1592   _dbus_type_writer_init (writer, byte_order,
1593                           (DBusString*)type_str, type_pos,
1594                           value_str, value_pos);
1595 
1596   writer->type_pos_is_expectation = TRUE;
1597 }
1598 
1599 static dbus_bool_t
_dbus_type_writer_write_basic_no_typecode(DBusTypeWriter * writer,int type,const void * value)1600 _dbus_type_writer_write_basic_no_typecode (DBusTypeWriter *writer,
1601                                            int             type,
1602                                            const void     *value)
1603 {
1604   if (writer->enabled)
1605     return _dbus_marshal_write_basic (writer->value_str,
1606                                       writer->value_pos,
1607                                       type,
1608                                       value,
1609                                       writer->byte_order,
1610                                       &writer->value_pos);
1611   else
1612     return TRUE;
1613 }
1614 
1615 /* If our parent is an array, things are a little bit complicated.
1616  *
1617  * The parent must have a complete element type, such as
1618  * "i" or "aai" or "(ii)" or "a(ii)". There can't be
1619  * unclosed parens, or an "a" with no following type.
1620  *
1621  * To recurse, the only allowed operation is to recurse into the
1622  * first type in the element type. So for "i" you can't recurse, for
1623  * "ai" you can recurse into the array, for "(ii)" you can recurse
1624  * into the struct.
1625  *
1626  * If you recurse into the array for "ai", then you must specify
1627  * "i" for the element type of the array you recurse into.
1628  *
1629  * While inside an array at any level, we need to avoid writing to
1630  * type_str, since the type only appears once for the whole array,
1631  * it does not appear for each array element.
1632  *
1633  * While inside an array type_pos points to the expected next
1634  * typecode, rather than the next place we could write a typecode.
1635  */
1636 static void
writer_recurse_init_and_check(DBusTypeWriter * writer,int container_type,DBusTypeWriter * sub)1637 writer_recurse_init_and_check (DBusTypeWriter *writer,
1638                                int             container_type,
1639                                DBusTypeWriter *sub)
1640 {
1641   _dbus_type_writer_init (sub,
1642                           writer->byte_order,
1643                           writer->type_str,
1644                           writer->type_pos,
1645                           writer->value_str,
1646                           writer->value_pos);
1647 
1648   sub->container_type = container_type;
1649 
1650   if (writer->type_pos_is_expectation ||
1651       (sub->container_type == DBUS_TYPE_ARRAY || sub->container_type == DBUS_TYPE_VARIANT))
1652     sub->type_pos_is_expectation = TRUE;
1653   else
1654     sub->type_pos_is_expectation = FALSE;
1655 
1656   sub->enabled = writer->enabled;
1657 
1658 #ifndef DBUS_DISABLE_CHECKS
1659   if (writer->type_pos_is_expectation && writer->type_str)
1660     {
1661       int expected;
1662 
1663       expected = _dbus_first_type_in_signature (writer->type_str, writer->type_pos);
1664 
1665       if (expected != sub->container_type)
1666         {
1667           if (expected != DBUS_TYPE_INVALID)
1668             _dbus_warn_check_failed ("Writing an element of type %s, but the expected type here is %s\n"
1669                                      "The overall signature expected here was '%s' and we are on byte %d of that signature.",
1670                                      _dbus_type_to_string (sub->container_type),
1671                                      _dbus_type_to_string (expected),
1672                                      _dbus_string_get_const_data (writer->type_str), writer->type_pos);
1673           else
1674             _dbus_warn_check_failed ("Writing an element of type %s, but no value is expected here\n"
1675                                      "The overall signature expected here was '%s' and we are on byte %d of that signature.",
1676                                      _dbus_type_to_string (sub->container_type),
1677                                      _dbus_string_get_const_data (writer->type_str), writer->type_pos);
1678 
1679           _dbus_assert_not_reached ("bad array element or variant content written");
1680         }
1681     }
1682 #endif /* DBUS_DISABLE_CHECKS */
1683 
1684 #if RECURSIVE_MARSHAL_WRITE_TRACE
1685   _dbus_verbose ("  type writer %p recurse parent %s type_pos = %d value_pos = %d is_expectation = %d remaining sig '%s' enabled = %d\n",
1686                  writer,
1687                  _dbus_type_to_string (writer->container_type),
1688                  writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
1689                  writer->type_str ?
1690                  _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
1691                  "unknown",
1692                  writer->enabled);
1693   _dbus_verbose ("  type writer %p recurse sub %s   type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n",
1694                  sub,
1695                  _dbus_type_to_string (sub->container_type),
1696                  sub->type_pos, sub->value_pos,
1697                  sub->type_pos_is_expectation,
1698                  sub->enabled);
1699 #endif
1700 }
1701 
1702 static dbus_bool_t
write_or_verify_typecode(DBusTypeWriter * writer,int typecode)1703 write_or_verify_typecode (DBusTypeWriter *writer,
1704                           int             typecode)
1705 {
1706   /* A subwriter inside an array or variant will have type_pos
1707    * pointing to the expected typecode; a writer not inside an array
1708    * or variant has type_pos pointing to the next place to insert a
1709    * typecode.
1710    */
1711 #if RECURSIVE_MARSHAL_WRITE_TRACE
1712   _dbus_verbose ("  type writer %p write_or_verify start type_pos = %d remaining sig '%s' enabled = %d\n",
1713                  writer, writer->type_pos,
1714                  writer->type_str ?
1715                  _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
1716                  "unknown",
1717                  writer->enabled);
1718 #endif
1719 
1720   if (writer->type_str == NULL)
1721     return TRUE;
1722 
1723   if (writer->type_pos_is_expectation)
1724     {
1725 #ifndef DBUS_DISABLE_CHECKS
1726       {
1727         int expected;
1728 
1729         expected = _dbus_string_get_byte (writer->type_str, writer->type_pos);
1730 
1731         if (expected != typecode)
1732           {
1733             if (expected != DBUS_TYPE_INVALID)
1734               _dbus_warn_check_failed ("Array or variant type requires that type %s be written, but %s was written.\n"
1735                                        "The overall signature expected here was '%s' and we are on byte %d of that signature.",
1736                                        _dbus_type_to_string (expected), _dbus_type_to_string (typecode),
1737                                        _dbus_string_get_const_data (writer->type_str), writer->type_pos);
1738             else
1739               _dbus_warn_check_failed ("Array or variant type wasn't expecting any more values to be written into it, but a value %s was written.\n"
1740                                        "The overall signature expected here was '%s' and we are on byte %d of that signature.",
1741                                        _dbus_type_to_string (typecode),
1742                                        _dbus_string_get_const_data (writer->type_str), writer->type_pos);
1743             _dbus_assert_not_reached ("bad type inserted somewhere inside an array or variant");
1744           }
1745       }
1746 #endif /* DBUS_DISABLE_CHECKS */
1747 
1748       /* if immediately inside an array we'd always be appending an element,
1749        * so the expected type doesn't change; if inside a struct or something
1750        * below an array, we need to move through said struct or something.
1751        */
1752       if (writer->container_type != DBUS_TYPE_ARRAY)
1753         writer->type_pos += 1;
1754     }
1755   else
1756     {
1757       if (!_dbus_string_insert_byte (writer->type_str,
1758                                      writer->type_pos,
1759                                      typecode))
1760         return FALSE;
1761 
1762       writer->type_pos += 1;
1763     }
1764 
1765 #if RECURSIVE_MARSHAL_WRITE_TRACE
1766   _dbus_verbose ("  type writer %p write_or_verify end type_pos = %d remaining sig '%s'\n",
1767                  writer, writer->type_pos,
1768                  _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0));
1769 #endif
1770 
1771   return TRUE;
1772 }
1773 
1774 static dbus_bool_t
writer_recurse_struct_or_dict_entry(DBusTypeWriter * writer,int begin_char,const DBusString * contained_type,int contained_type_start,int contained_type_len,DBusTypeWriter * sub)1775 writer_recurse_struct_or_dict_entry (DBusTypeWriter   *writer,
1776                                      int               begin_char,
1777                                      const DBusString *contained_type,
1778                                      int               contained_type_start,
1779                                      int               contained_type_len,
1780                                      DBusTypeWriter   *sub)
1781 {
1782   /* FIXME right now contained_type is ignored; we could probably
1783    * almost trivially fix the code so if it's present we
1784    * write it out and then set type_pos_is_expectation
1785    */
1786 
1787   /* Ensure that we'll be able to add alignment padding and the typecode */
1788   if (writer->enabled)
1789     {
1790       if (!_dbus_string_alloc_space (sub->value_str, 8))
1791         return FALSE;
1792     }
1793 
1794   if (!write_or_verify_typecode (sub, begin_char))
1795     _dbus_assert_not_reached ("failed to insert struct typecode after prealloc");
1796 
1797   if (writer->enabled)
1798     {
1799       if (!_dbus_string_insert_bytes (sub->value_str,
1800                                       sub->value_pos,
1801                                       _DBUS_ALIGN_VALUE (sub->value_pos, 8) - sub->value_pos,
1802                                       '\0'))
1803         _dbus_assert_not_reached ("should not have failed to insert alignment padding for struct");
1804       sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
1805     }
1806 
1807   return TRUE;
1808 }
1809 
1810 
1811 static dbus_bool_t
writer_recurse_array(DBusTypeWriter * writer,const DBusString * contained_type,int contained_type_start,int contained_type_len,DBusTypeWriter * sub,dbus_bool_t is_array_append)1812 writer_recurse_array (DBusTypeWriter   *writer,
1813                       const DBusString *contained_type,
1814                       int               contained_type_start,
1815                       int               contained_type_len,
1816                       DBusTypeWriter   *sub,
1817                       dbus_bool_t       is_array_append)
1818 {
1819   dbus_uint32_t value = 0;
1820   int alignment;
1821   int aligned;
1822 
1823 #ifndef DBUS_DISABLE_CHECKS
1824   if (writer->container_type == DBUS_TYPE_ARRAY &&
1825       writer->type_str)
1826     {
1827       if (!_dbus_string_equal_substring (contained_type,
1828                                          contained_type_start,
1829                                          contained_type_len,
1830                                          writer->type_str,
1831                                          writer->u.array.element_type_pos + 1))
1832         {
1833           _dbus_warn_check_failed ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array",
1834                                    _dbus_string_get_const_data_len (contained_type,
1835                                                                     contained_type_start,
1836                                                                     contained_type_len));
1837           _dbus_assert_not_reached ("incompatible type for child array");
1838         }
1839     }
1840 #endif /* DBUS_DISABLE_CHECKS */
1841 
1842   if (writer->enabled && !is_array_append)
1843     {
1844       /* 3 pad + 4 bytes for the array length, and 4 bytes possible padding
1845        * before array values
1846        */
1847       if (!_dbus_string_alloc_space (sub->value_str, 3 + 4 + 4))
1848         return FALSE;
1849     }
1850 
1851   if (writer->type_str != NULL)
1852     {
1853       sub->type_pos += 1; /* move to point to the element type, since type_pos
1854                            * should be the expected type for further writes
1855                            */
1856       sub->u.array.element_type_pos = sub->type_pos;
1857     }
1858 
1859   if (!writer->type_pos_is_expectation)
1860     {
1861       /* sub is a toplevel/outermost array so we need to write the type data */
1862 
1863       /* alloc space for array typecode, element signature */
1864       if (!_dbus_string_alloc_space (writer->type_str, 1 + contained_type_len))
1865         return FALSE;
1866 
1867       if (!_dbus_string_insert_byte (writer->type_str,
1868                                      writer->type_pos,
1869                                      DBUS_TYPE_ARRAY))
1870         _dbus_assert_not_reached ("failed to insert array typecode after prealloc");
1871 
1872       if (!_dbus_string_copy_len (contained_type,
1873                                   contained_type_start, contained_type_len,
1874                                   sub->type_str,
1875                                   sub->u.array.element_type_pos))
1876         _dbus_assert_not_reached ("should not have failed to insert array element typecodes");
1877     }
1878 
1879   if (writer->type_str != NULL)
1880     {
1881       /* If the parent is an array, we hold type_pos pointing at the array element type;
1882        * otherwise advance it to reflect the array value we just recursed into
1883        */
1884       if (writer->container_type != DBUS_TYPE_ARRAY)
1885         writer->type_pos += 1 + contained_type_len;
1886       else
1887         _dbus_assert (writer->type_pos_is_expectation); /* because it's an array */
1888     }
1889 
1890   if (writer->enabled)
1891     {
1892       /* Write (or jump over, if is_array_append) the length */
1893       sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
1894 
1895       if (is_array_append)
1896         {
1897           sub->value_pos += 4;
1898         }
1899       else
1900         {
1901           if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32,
1902                                                           &value))
1903             _dbus_assert_not_reached ("should not have failed to insert array len");
1904         }
1905 
1906       _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4);
1907 
1908       /* Write alignment padding for array elements
1909        * Note that we write the padding *even for empty arrays*
1910        * to avoid wonky special cases
1911        */
1912       alignment = element_type_get_alignment (contained_type, contained_type_start);
1913 
1914       aligned = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
1915       if (aligned != sub->value_pos)
1916         {
1917           if (!is_array_append)
1918             {
1919               if (!_dbus_string_insert_bytes (sub->value_str,
1920                                               sub->value_pos,
1921                                               aligned - sub->value_pos,
1922                                               '\0'))
1923                 _dbus_assert_not_reached ("should not have failed to insert alignment padding");
1924             }
1925 
1926           sub->value_pos = aligned;
1927         }
1928 
1929       sub->u.array.start_pos = sub->value_pos;
1930 
1931       if (is_array_append)
1932         {
1933           dbus_uint32_t len;
1934 
1935           _dbus_assert (_DBUS_ALIGN_VALUE (sub->u.array.len_pos, 4) ==
1936                         (unsigned) sub->u.array.len_pos);
1937           len = _dbus_unpack_uint32 (sub->byte_order,
1938                                      _dbus_string_get_const_udata_len (sub->value_str,
1939                                                                       sub->u.array.len_pos,
1940                                                                       4));
1941 
1942           sub->value_pos += len;
1943         }
1944     }
1945   else
1946     {
1947       /* not enabled, so we won't write the len_pos; set it to -1 to so indicate */
1948       sub->u.array.len_pos = -1;
1949       sub->u.array.start_pos = sub->value_pos;
1950     }
1951 
1952   _dbus_assert (sub->u.array.len_pos < sub->u.array.start_pos);
1953   _dbus_assert (is_array_append || sub->u.array.start_pos == sub->value_pos);
1954 
1955 #if RECURSIVE_MARSHAL_WRITE_TRACE
1956       _dbus_verbose ("  type writer %p recurse array done remaining sig '%s' array start_pos = %d len_pos = %d value_pos = %d\n", sub,
1957                      sub->type_str ?
1958                      _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0) :
1959                      "unknown",
1960                      sub->u.array.start_pos, sub->u.array.len_pos, sub->value_pos);
1961 #endif
1962 
1963   return TRUE;
1964 }
1965 
1966 /* Variant value will normally have:
1967  *   1 byte signature length not including nul
1968  *   signature typecodes (nul terminated)
1969  *   padding to alignment of contained type
1970  *   body according to signature
1971  *
1972  * The signature string can only have a single type
1973  * in it but that type may be complex/recursive.
1974  *
1975  * So a typical variant type with the integer 3 will have these
1976  * octets:
1977  *   0x1 'i' '\0' [1 byte padding to alignment boundary] 0x0 0x0 0x0 0x3
1978  *
1979  * The main world of hurt for writing out a variant is that the type
1980  * string is the same string as the value string. Which means
1981  * inserting to the type string will move the value_pos; and it means
1982  * that inserting to the type string could break type alignment.
1983  */
1984 static dbus_bool_t
writer_recurse_variant(DBusTypeWriter * writer,const DBusString * contained_type,int contained_type_start,int contained_type_len,DBusTypeWriter * sub)1985 writer_recurse_variant (DBusTypeWriter   *writer,
1986                         const DBusString *contained_type,
1987                         int               contained_type_start,
1988                         int               contained_type_len,
1989                         DBusTypeWriter   *sub)
1990 {
1991   int contained_alignment;
1992 
1993   if (writer->enabled)
1994     {
1995       /* Allocate space for the worst case, which is 1 byte sig
1996        * length, nul byte at end of sig, and 7 bytes padding to
1997        * 8-boundary.
1998        */
1999       if (!_dbus_string_alloc_space (sub->value_str, contained_type_len + 9))
2000         return FALSE;
2001     }
2002 
2003   /* write VARIANT typecode to the parent's type string */
2004   if (!write_or_verify_typecode (writer, DBUS_TYPE_VARIANT))
2005     return FALSE;
2006 
2007   /* If not enabled, mark that we have no type_str anymore ... */
2008 
2009   if (!writer->enabled)
2010     {
2011       sub->type_str = NULL;
2012       sub->type_pos = -1;
2013 
2014       return TRUE;
2015     }
2016 
2017   /* If we're enabled then continue ... */
2018 
2019   if (!_dbus_string_insert_byte (sub->value_str,
2020                                  sub->value_pos,
2021                                  contained_type_len))
2022     _dbus_assert_not_reached ("should not have failed to insert variant type sig len");
2023 
2024   sub->value_pos += 1;
2025 
2026   /* Here we switch over to the expected type sig we're about to write */
2027   sub->type_str = sub->value_str;
2028   sub->type_pos = sub->value_pos;
2029 
2030   if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len,
2031                               sub->value_str, sub->value_pos))
2032     _dbus_assert_not_reached ("should not have failed to insert variant type sig");
2033 
2034   sub->value_pos += contained_type_len;
2035 
2036   if (!_dbus_string_insert_byte (sub->value_str,
2037                                  sub->value_pos,
2038                                  DBUS_TYPE_INVALID))
2039     _dbus_assert_not_reached ("should not have failed to insert variant type nul termination");
2040 
2041   sub->value_pos += 1;
2042 
2043   contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (contained_type, contained_type_start));
2044 
2045   if (!_dbus_string_insert_bytes (sub->value_str,
2046                                   sub->value_pos,
2047                                   _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment) - sub->value_pos,
2048                                   '\0'))
2049     _dbus_assert_not_reached ("should not have failed to insert alignment padding for variant body");
2050   sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
2051 
2052   return TRUE;
2053 }
2054 
2055 static dbus_bool_t
_dbus_type_writer_recurse_contained_len(DBusTypeWriter * writer,int container_type,const DBusString * contained_type,int contained_type_start,int contained_type_len,DBusTypeWriter * sub,dbus_bool_t is_array_append)2056 _dbus_type_writer_recurse_contained_len (DBusTypeWriter   *writer,
2057                                          int               container_type,
2058                                          const DBusString *contained_type,
2059                                          int               contained_type_start,
2060                                          int               contained_type_len,
2061                                          DBusTypeWriter   *sub,
2062                                          dbus_bool_t       is_array_append)
2063 {
2064   writer_recurse_init_and_check (writer, container_type, sub);
2065 
2066   switch (container_type)
2067     {
2068     case DBUS_TYPE_STRUCT:
2069       return writer_recurse_struct_or_dict_entry (writer,
2070                                                   DBUS_STRUCT_BEGIN_CHAR,
2071                                                   contained_type,
2072                                                   contained_type_start, contained_type_len,
2073                                                   sub);
2074       break;
2075     case DBUS_TYPE_DICT_ENTRY:
2076       return writer_recurse_struct_or_dict_entry (writer,
2077                                                   DBUS_DICT_ENTRY_BEGIN_CHAR,
2078                                                   contained_type,
2079                                                   contained_type_start, contained_type_len,
2080                                                   sub);
2081       break;
2082     case DBUS_TYPE_ARRAY:
2083       return writer_recurse_array (writer,
2084                                    contained_type, contained_type_start, contained_type_len,
2085                                    sub, is_array_append);
2086       break;
2087     case DBUS_TYPE_VARIANT:
2088       return writer_recurse_variant (writer,
2089                                      contained_type, contained_type_start, contained_type_len,
2090                                      sub);
2091       break;
2092     default:
2093       _dbus_assert_not_reached ("tried to recurse into type that doesn't support that");
2094       return FALSE;
2095       break;
2096     }
2097 }
2098 
2099 /**
2100  * Opens a new container and writes out the initial information for that container.
2101  *
2102  * @param writer the writer
2103  * @param container_type the type of the container to open
2104  * @param contained_type the array element type or variant content type
2105  * @param contained_type_start position to look for the type
2106  * @param sub the new sub-writer to write container contents
2107  * @returns #FALSE if no memory
2108  */
2109 dbus_bool_t
_dbus_type_writer_recurse(DBusTypeWriter * writer,int container_type,const DBusString * contained_type,int contained_type_start,DBusTypeWriter * sub)2110 _dbus_type_writer_recurse (DBusTypeWriter   *writer,
2111                            int               container_type,
2112                            const DBusString *contained_type,
2113                            int               contained_type_start,
2114                            DBusTypeWriter   *sub)
2115 {
2116   int contained_type_len;
2117 
2118   if (contained_type)
2119     contained_type_len = find_len_of_complete_type (contained_type, contained_type_start);
2120   else
2121     contained_type_len = 0;
2122 
2123   return _dbus_type_writer_recurse_contained_len (writer, container_type,
2124                                                   contained_type,
2125                                                   contained_type_start,
2126                                                   contained_type_len,
2127                                                   sub,
2128                                                   FALSE);
2129 }
2130 
2131 /**
2132  * Append to an existing array. Essentially, the writer will read an
2133  * existing length at the write location; jump over that length; and
2134  * write new fields. On unrecurse(), the existing length will be
2135  * updated.
2136  *
2137  * @param writer the writer
2138  * @param contained_type element type
2139  * @param contained_type_start position of element type
2140  * @param sub the subwriter to init
2141  * @returns #FALSE if no memory
2142  */
2143 dbus_bool_t
_dbus_type_writer_append_array(DBusTypeWriter * writer,const DBusString * contained_type,int contained_type_start,DBusTypeWriter * sub)2144 _dbus_type_writer_append_array (DBusTypeWriter   *writer,
2145                                 const DBusString *contained_type,
2146                                 int               contained_type_start,
2147                                 DBusTypeWriter   *sub)
2148 {
2149   int contained_type_len;
2150 
2151   if (contained_type)
2152     contained_type_len = find_len_of_complete_type (contained_type, contained_type_start);
2153   else
2154     contained_type_len = 0;
2155 
2156   return _dbus_type_writer_recurse_contained_len (writer, DBUS_TYPE_ARRAY,
2157                                                   contained_type,
2158                                                   contained_type_start,
2159                                                   contained_type_len,
2160                                                   sub,
2161                                                   TRUE);
2162 }
2163 
2164 static int
writer_get_array_len(DBusTypeWriter * writer)2165 writer_get_array_len (DBusTypeWriter *writer)
2166 {
2167   _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
2168   return writer->value_pos - writer->u.array.start_pos;
2169 }
2170 
2171 /**
2172  * Closes a container created by _dbus_type_writer_recurse()
2173  * and writes any additional information to the values block.
2174  *
2175  * @param writer the writer
2176  * @param sub the sub-writer created by _dbus_type_writer_recurse()
2177  * @returns #FALSE if no memory
2178  */
2179 dbus_bool_t
_dbus_type_writer_unrecurse(DBusTypeWriter * writer,DBusTypeWriter * sub)2180 _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
2181                              DBusTypeWriter *sub)
2182 {
2183   /* type_pos_is_expectation never gets unset once set, or we'd get all hosed */
2184   _dbus_assert (!writer->type_pos_is_expectation ||
2185                 (writer->type_pos_is_expectation && sub->type_pos_is_expectation));
2186 
2187 #if RECURSIVE_MARSHAL_WRITE_TRACE
2188   _dbus_verbose ("  type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
2189                  writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
2190                  _dbus_type_to_string (writer->container_type));
2191   _dbus_verbose ("  type writer %p unrecurse sub type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
2192                  sub, sub->type_pos, sub->value_pos,
2193                  sub->type_pos_is_expectation,
2194                  _dbus_type_to_string (sub->container_type));
2195 #endif
2196 
2197   if (sub->container_type == DBUS_TYPE_STRUCT)
2198     {
2199       if (!write_or_verify_typecode (sub, DBUS_STRUCT_END_CHAR))
2200         return FALSE;
2201     }
2202   else if (sub->container_type == DBUS_TYPE_DICT_ENTRY)
2203     {
2204       if (!write_or_verify_typecode (sub, DBUS_DICT_ENTRY_END_CHAR))
2205         return FALSE;
2206     }
2207   else if (sub->container_type == DBUS_TYPE_ARRAY)
2208     {
2209       if (sub->u.array.len_pos >= 0) /* len_pos == -1 if we weren't enabled when we passed it */
2210         {
2211           dbus_uint32_t len;
2212 
2213           /* Set the array length */
2214           len = writer_get_array_len (sub);
2215           _dbus_marshal_set_uint32 (sub->value_str,
2216                                     sub->u.array.len_pos,
2217                                     len,
2218                                     sub->byte_order);
2219 #if RECURSIVE_MARSHAL_WRITE_TRACE
2220           _dbus_verbose ("    filled in sub array len to %u at len_pos %d\n",
2221                          len, sub->u.array.len_pos);
2222 #endif
2223         }
2224 #if RECURSIVE_MARSHAL_WRITE_TRACE
2225       else
2226         {
2227           _dbus_verbose ("    not filling in sub array len because we were disabled when we passed the len\n");
2228         }
2229 #endif
2230     }
2231 
2232   /* Now get type_pos right for the parent writer. Here are the cases:
2233    *
2234    * Cases !writer->type_pos_is_expectation:
2235    *   (in these cases we want to update to the new insertion point)
2236    *
2237    * - if we recursed into a STRUCT then we didn't know in advance
2238    *   what the types in the struct would be; so we have to fill in
2239    *   that information now.
2240    *       writer->type_pos = sub->type_pos
2241    *
2242    * - if we recursed into anything else, we knew the full array
2243    *   type, or knew the single typecode marking VARIANT, so
2244    *   writer->type_pos is already correct.
2245    *       writer->type_pos should remain as-is
2246    *
2247    * - note that the parent is never an ARRAY or VARIANT, if it were
2248    *   then type_pos_is_expectation would be TRUE. The parent
2249    *   is thus known to be a toplevel or STRUCT.
2250    *
2251    * Cases where writer->type_pos_is_expectation:
2252    *   (in these cases we want to update to next expected type to write)
2253    *
2254    * - we recursed from STRUCT into STRUCT and we didn't increment
2255    *   type_pos in the parent just to stay consistent with the
2256    *   !writer->type_pos_is_expectation case (though we could
2257    *   special-case this in recurse_struct instead if we wanted)
2258    *       writer->type_pos = sub->type_pos
2259    *
2260    * - we recursed from STRUCT into ARRAY or VARIANT and type_pos
2261    *   for parent should have been incremented already
2262    *       writer->type_pos should remain as-is
2263    *
2264    * - we recursed from ARRAY into a sub-element, so type_pos in the
2265    *   parent is the element type and should remain the element type
2266    *   for the benefit of the next child element
2267    *       writer->type_pos should remain as-is
2268    *
2269    * - we recursed from VARIANT into its value, so type_pos in the
2270    *   parent makes no difference since there's only one value
2271    *   and we just finished writing it and won't use type_pos again
2272    *       writer->type_pos should remain as-is
2273    *
2274    *
2275    * For all these, DICT_ENTRY is the same as STRUCT
2276    */
2277   if (writer->type_str != NULL)
2278     {
2279       if ((sub->container_type == DBUS_TYPE_STRUCT ||
2280            sub->container_type == DBUS_TYPE_DICT_ENTRY) &&
2281           (writer->container_type == DBUS_TYPE_STRUCT ||
2282            writer->container_type == DBUS_TYPE_DICT_ENTRY ||
2283            writer->container_type == DBUS_TYPE_INVALID))
2284         {
2285           /* Advance the parent to the next struct field */
2286           writer->type_pos = sub->type_pos;
2287         }
2288     }
2289 
2290   writer->value_pos = sub->value_pos;
2291 
2292 #if RECURSIVE_MARSHAL_WRITE_TRACE
2293   _dbus_verbose ("  type writer %p unrecursed type_pos = %d value_pos = %d remaining sig '%s'\n",
2294                  writer, writer->type_pos, writer->value_pos,
2295                  writer->type_str ?
2296                  _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
2297                  "unknown");
2298 #endif
2299 
2300   return TRUE;
2301 }
2302 
2303 /**
2304  * Writes out a basic type.
2305  *
2306  * @param writer the writer
2307  * @param type the type to write
2308  * @param value the address of the value to write
2309  * @returns #FALSE if no memory
2310  */
2311 dbus_bool_t
_dbus_type_writer_write_basic(DBusTypeWriter * writer,int type,const void * value)2312 _dbus_type_writer_write_basic (DBusTypeWriter *writer,
2313                                int             type,
2314                                const void     *value)
2315 {
2316   dbus_bool_t retval;
2317 
2318   /* First ensure that our type realloc will succeed */
2319   if (!writer->type_pos_is_expectation && writer->type_str != NULL)
2320     {
2321       if (!_dbus_string_alloc_space (writer->type_str, 1))
2322         return FALSE;
2323     }
2324 
2325   retval = FALSE;
2326 
2327   if (!_dbus_type_writer_write_basic_no_typecode (writer, type, value))
2328     goto out;
2329 
2330   if (!write_or_verify_typecode (writer, type))
2331     _dbus_assert_not_reached ("failed to write typecode after prealloc");
2332 
2333   retval = TRUE;
2334 
2335  out:
2336 #if RECURSIVE_MARSHAL_WRITE_TRACE
2337   _dbus_verbose ("  type writer %p basic type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n",
2338                  writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
2339                  writer->enabled);
2340 #endif
2341 
2342   return retval;
2343 }
2344 
2345 /**
2346  * Writes a block of fixed-length basic values, i.e. those that are
2347  * both dbus_type_is_fixed() and _dbus_type_is_basic(). The block
2348  * must be written inside an array.
2349  *
2350  * The value parameter should be the address of said array of values,
2351  * so e.g. if it's an array of double, pass in "const double**"
2352  *
2353  * @param writer the writer
2354  * @param element_type type of stuff in the array
2355  * @param value address of the array
2356  * @param n_elements number of elements in the array
2357  * @returns #FALSE if no memory
2358  */
2359 dbus_bool_t
_dbus_type_writer_write_fixed_multi(DBusTypeWriter * writer,int element_type,const void * value,int n_elements)2360 _dbus_type_writer_write_fixed_multi (DBusTypeWriter        *writer,
2361                                      int                    element_type,
2362                                      const void            *value,
2363                                      int                    n_elements)
2364 {
2365   _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
2366   _dbus_assert (dbus_type_is_fixed (element_type));
2367   _dbus_assert (writer->type_pos_is_expectation);
2368   _dbus_assert (n_elements >= 0);
2369 
2370 #if RECURSIVE_MARSHAL_WRITE_TRACE
2371   _dbus_verbose ("  type writer %p entering fixed multi type_pos = %d value_pos = %d n_elements %d\n",
2372                  writer, writer->type_pos, writer->value_pos, n_elements);
2373 #endif
2374 
2375   if (!write_or_verify_typecode (writer, element_type))
2376     _dbus_assert_not_reached ("OOM should not happen if only verifying typecode");
2377 
2378   if (writer->enabled)
2379     {
2380       if (!_dbus_marshal_write_fixed_multi (writer->value_str,
2381                                             writer->value_pos,
2382                                             element_type,
2383                                             value,
2384                                             n_elements,
2385                                             writer->byte_order,
2386                                             &writer->value_pos))
2387         return FALSE;
2388     }
2389 
2390 #if RECURSIVE_MARSHAL_WRITE_TRACE
2391   _dbus_verbose ("  type writer %p fixed multi written new type_pos = %d new value_pos = %d n_elements %d\n",
2392                  writer, writer->type_pos, writer->value_pos, n_elements);
2393 #endif
2394 
2395   return TRUE;
2396 }
2397 
2398 static void
enable_if_after(DBusTypeWriter * writer,DBusTypeReader * reader,const DBusTypeReader * start_after)2399 enable_if_after (DBusTypeWriter       *writer,
2400                  DBusTypeReader       *reader,
2401                  const DBusTypeReader *start_after)
2402 {
2403   if (start_after)
2404     {
2405       if (!writer->enabled && _dbus_type_reader_greater_than (reader, start_after))
2406         {
2407           _dbus_type_writer_set_enabled (writer, TRUE);
2408 #if RECURSIVE_MARSHAL_WRITE_TRACE
2409           _dbus_verbose ("ENABLING writer %p at %d because reader at value_pos %d is after reader at value_pos %d\n",
2410                          writer, writer->value_pos, reader->value_pos, start_after->value_pos);
2411 #endif
2412         }
2413 
2414       _dbus_assert ((!writer->enabled && !_dbus_type_reader_greater_than (reader, start_after)) ||
2415                     (writer->enabled && _dbus_type_reader_greater_than (reader, start_after)));
2416     }
2417 }
2418 
2419 static dbus_bool_t
append_fixup(DBusList ** fixups,const DBusArrayLenFixup * fixup)2420 append_fixup (DBusList               **fixups,
2421               const DBusArrayLenFixup *fixup)
2422 {
2423   DBusArrayLenFixup *f;
2424 
2425   f = dbus_new (DBusArrayLenFixup, 1);
2426   if (f == NULL)
2427     return FALSE;
2428 
2429   *f = *fixup;
2430 
2431   if (!_dbus_list_append (fixups, f))
2432     {
2433       dbus_free (f);
2434       return FALSE;
2435     }
2436 
2437   _dbus_assert (f->len_pos_in_reader == fixup->len_pos_in_reader);
2438   _dbus_assert (f->new_len == fixup->new_len);
2439 
2440   return TRUE;
2441 }
2442 
2443 /* This loop is trivial if you ignore all the start_after nonsense,
2444  * so if you're trying to figure it out, start by ignoring that
2445  */
2446 static dbus_bool_t
writer_write_reader_helper(DBusTypeWriter * writer,DBusTypeReader * reader,const DBusTypeReader * start_after,int start_after_new_pos,int start_after_new_len,DBusList ** fixups,dbus_bool_t inside_start_after)2447 writer_write_reader_helper (DBusTypeWriter       *writer,
2448                             DBusTypeReader       *reader,
2449                             const DBusTypeReader *start_after,
2450                             int                   start_after_new_pos,
2451                             int                   start_after_new_len,
2452                             DBusList            **fixups,
2453                             dbus_bool_t           inside_start_after)
2454 {
2455   int current_type;
2456 
2457   while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
2458     {
2459       if (dbus_type_is_container (current_type))
2460         {
2461           DBusTypeReader subreader;
2462           DBusTypeWriter subwriter;
2463           const DBusString *sig_str;
2464           int sig_start;
2465           int sig_len;
2466           dbus_bool_t enabled_at_recurse;
2467           dbus_bool_t past_start_after;
2468           int reader_array_len_pos;
2469           int reader_array_start_pos;
2470           dbus_bool_t this_is_start_after;
2471 
2472           /* type_pos is checked since e.g. in a struct the struct
2473            * and its first field have the same value_pos.
2474            * type_str will differ in reader/start_after for variants
2475            * where type_str is inside the value_str
2476            */
2477           if (!inside_start_after && start_after &&
2478               reader->value_pos == start_after->value_pos &&
2479               reader->type_str == start_after->type_str &&
2480               reader->type_pos == start_after->type_pos)
2481             this_is_start_after = TRUE;
2482           else
2483             this_is_start_after = FALSE;
2484 
2485           _dbus_type_reader_recurse (reader, &subreader);
2486 
2487           if (current_type == DBUS_TYPE_ARRAY)
2488             {
2489               reader_array_len_pos = ARRAY_READER_LEN_POS (&subreader);
2490               reader_array_start_pos = subreader.u.array.start_pos;
2491             }
2492           else
2493             {
2494               /* quiet gcc */
2495               reader_array_len_pos = -1;
2496               reader_array_start_pos = -1;
2497             }
2498 
2499           _dbus_type_reader_get_signature (&subreader, &sig_str,
2500                                            &sig_start, &sig_len);
2501 
2502 #if RECURSIVE_MARSHAL_WRITE_TRACE
2503           _dbus_verbose ("about to recurse into %s reader at %d subreader at %d writer at %d start_after reader at %d write target len %d inside_start_after = %d this_is_start_after = %d\n",
2504                          _dbus_type_to_string (current_type),
2505                          reader->value_pos,
2506                          subreader.value_pos,
2507                          writer->value_pos,
2508                          start_after ? start_after->value_pos : -1,
2509                          _dbus_string_get_length (writer->value_str),
2510                          inside_start_after, this_is_start_after);
2511 #endif
2512 
2513           if (!inside_start_after && !this_is_start_after)
2514             enable_if_after (writer, &subreader, start_after);
2515           enabled_at_recurse = writer->enabled;
2516           if (!_dbus_type_writer_recurse_contained_len (writer, current_type,
2517                                                         sig_str, sig_start, sig_len,
2518                                                         &subwriter, FALSE))
2519             goto oom;
2520 
2521 #if RECURSIVE_MARSHAL_WRITE_TRACE
2522           _dbus_verbose ("recursed into subwriter at %d write target len %d\n",
2523                          subwriter.value_pos,
2524                          _dbus_string_get_length (subwriter.value_str));
2525 #endif
2526 
2527           if (!writer_write_reader_helper (&subwriter, &subreader, start_after,
2528                                            start_after_new_pos, start_after_new_len,
2529                                            fixups,
2530                                            inside_start_after ||
2531                                            this_is_start_after))
2532             goto oom;
2533 
2534 #if RECURSIVE_MARSHAL_WRITE_TRACE
2535           _dbus_verbose ("about to unrecurse from %s subreader at %d writer at %d subwriter at %d  write target len %d\n",
2536                          _dbus_type_to_string (current_type),
2537                          subreader.value_pos,
2538                          writer->value_pos,
2539                          subwriter.value_pos,
2540                          _dbus_string_get_length (writer->value_str));
2541 #endif
2542 
2543           if (!inside_start_after && !this_is_start_after)
2544             enable_if_after (writer, &subreader, start_after);
2545           past_start_after = writer->enabled;
2546           if (!_dbus_type_writer_unrecurse (writer, &subwriter))
2547             goto oom;
2548 
2549           /* If we weren't enabled when we recursed, we didn't
2550            * write an array len; if we passed start_after
2551            * somewhere inside the array, then we need to generate
2552            * a fixup.
2553            */
2554           if (start_after != NULL &&
2555               !enabled_at_recurse && past_start_after &&
2556               current_type == DBUS_TYPE_ARRAY &&
2557               fixups != NULL)
2558             {
2559               DBusArrayLenFixup fixup;
2560               int bytes_written_after_start_after;
2561               int bytes_before_start_after;
2562               int old_len;
2563 
2564               /* this subwriter access is moderately unkosher since we
2565                * already unrecursed, but it works as long as unrecurse
2566                * doesn't break us on purpose
2567                */
2568               bytes_written_after_start_after = writer_get_array_len (&subwriter);
2569 
2570               bytes_before_start_after =
2571                 start_after->value_pos - reader_array_start_pos;
2572 
2573               fixup.len_pos_in_reader = reader_array_len_pos;
2574               fixup.new_len =
2575                 bytes_before_start_after +
2576                 start_after_new_len +
2577                 bytes_written_after_start_after;
2578 
2579               _dbus_assert (_DBUS_ALIGN_VALUE (fixup.len_pos_in_reader, 4) ==
2580                             (unsigned) fixup.len_pos_in_reader);
2581 
2582               old_len = _dbus_unpack_uint32 (reader->byte_order,
2583                                              _dbus_string_get_const_udata_len (reader->value_str,
2584                                                                               fixup.len_pos_in_reader, 4));
2585 
2586               if (old_len != fixup.new_len && !append_fixup (fixups, &fixup))
2587                 goto oom;
2588 
2589 #if RECURSIVE_MARSHAL_WRITE_TRACE
2590               _dbus_verbose ("Generated fixup len_pos_in_reader = %d new_len = %d reader_array_start_pos = %d start_after->value_pos = %d bytes_before_start_after = %d start_after_new_len = %d bytes_written_after_start_after = %d\n",
2591                              fixup.len_pos_in_reader,
2592                              fixup.new_len,
2593                              reader_array_start_pos,
2594                              start_after->value_pos,
2595                              bytes_before_start_after,
2596                              start_after_new_len,
2597                              bytes_written_after_start_after);
2598 #endif
2599             }
2600         }
2601       else
2602         {
2603           DBusBasicValue val;
2604 
2605           _dbus_assert (dbus_type_is_basic (current_type));
2606 
2607 #if RECURSIVE_MARSHAL_WRITE_TRACE
2608           _dbus_verbose ("Reading basic value %s at %d\n",
2609                          _dbus_type_to_string (current_type),
2610                          reader->value_pos);
2611 #endif
2612 
2613           _dbus_type_reader_read_basic (reader, &val);
2614 
2615 #if RECURSIVE_MARSHAL_WRITE_TRACE
2616           _dbus_verbose ("Writing basic value %s at %d write target len %d inside_start_after = %d\n",
2617                          _dbus_type_to_string (current_type),
2618                          writer->value_pos,
2619                          _dbus_string_get_length (writer->value_str),
2620                          inside_start_after);
2621 #endif
2622           if (!inside_start_after)
2623             enable_if_after (writer, reader, start_after);
2624           if (!_dbus_type_writer_write_basic (writer, current_type, &val))
2625             goto oom;
2626 #if RECURSIVE_MARSHAL_WRITE_TRACE
2627           _dbus_verbose ("Wrote basic value %s, new value_pos %d write target len %d\n",
2628                          _dbus_type_to_string (current_type),
2629                          writer->value_pos,
2630                          _dbus_string_get_length (writer->value_str));
2631 #endif
2632         }
2633 
2634       _dbus_type_reader_next (reader);
2635     }
2636 
2637   return TRUE;
2638 
2639  oom:
2640   if (fixups)
2641     apply_and_free_fixups (fixups, NULL); /* NULL for reader to apply to */
2642 
2643   return FALSE;
2644 }
2645 
2646 /*
2647  * Iterate through all values in the given reader, writing a copy of
2648  * each value to the writer.  The reader will be moved forward to its
2649  * end position.
2650  *
2651  * If a reader start_after is provided, it should be a reader for the
2652  * same data as the reader to be written. Only values occurring after
2653  * the value pointed to by start_after will be written to the writer.
2654  *
2655  * If start_after is provided, then the copy of the reader will be
2656  * partial. This means that array lengths will not have been copied.
2657  * The assumption is that you wrote a new version of the value at
2658  * start_after to the writer. You have to pass in the start position
2659  * and length of the new value. (If you are deleting the value
2660  * at start_after, pass in 0 for the length.)
2661  *
2662  * If the fixups parameter is non-#NULL, then any array length that
2663  * was read but not written due to start_after will be provided
2664  * as a #DBusArrayLenFixup. The fixup contains the position of the
2665  * array length in the source data, and the correct array length
2666  * assuming you combine the source data before start_after with
2667  * the written data at start_after and beyond.
2668  *
2669  * @param writer the writer to copy to
2670  * @param reader the reader to copy from
2671  * @param start_after #NULL or a reader showing where to start
2672  * @param start_after_new_pos the position of start_after equivalent in the target data
2673  * @param start_after_new_len the length of start_after equivalent in the target data
2674  * @param fixups list to append #DBusArrayLenFixup if the write was partial
2675  * @returns #FALSE if no memory
2676  */
2677 static dbus_bool_t
_dbus_type_writer_write_reader_partial(DBusTypeWriter * writer,DBusTypeReader * reader,const DBusTypeReader * start_after,int start_after_new_pos,int start_after_new_len,DBusList ** fixups)2678 _dbus_type_writer_write_reader_partial (DBusTypeWriter       *writer,
2679                                         DBusTypeReader       *reader,
2680                                         const DBusTypeReader *start_after,
2681                                         int                   start_after_new_pos,
2682                                         int                   start_after_new_len,
2683                                         DBusList            **fixups)
2684 {
2685   DBusTypeWriter orig;
2686   int orig_type_len;
2687   int orig_value_len;
2688   int new_bytes;
2689   int orig_enabled;
2690 
2691   orig = *writer;
2692   orig_type_len = _dbus_string_get_length (writer->type_str);
2693   orig_value_len = _dbus_string_get_length (writer->value_str);
2694   orig_enabled = writer->enabled;
2695 
2696   if (start_after)
2697     _dbus_type_writer_set_enabled (writer, FALSE);
2698 
2699   if (!writer_write_reader_helper (writer, reader, start_after,
2700                                    start_after_new_pos,
2701                                    start_after_new_len,
2702                                    fixups, FALSE))
2703     goto oom;
2704 
2705   _dbus_type_writer_set_enabled (writer, orig_enabled);
2706   return TRUE;
2707 
2708  oom:
2709   if (!writer->type_pos_is_expectation)
2710     {
2711       new_bytes = _dbus_string_get_length (writer->type_str) - orig_type_len;
2712       _dbus_string_delete (writer->type_str, orig.type_pos, new_bytes);
2713     }
2714   new_bytes = _dbus_string_get_length (writer->value_str) - orig_value_len;
2715   _dbus_string_delete (writer->value_str, orig.value_pos, new_bytes);
2716 
2717   *writer = orig;
2718 
2719   return FALSE;
2720 }
2721 
2722 /**
2723  * Iterate through all values in the given reader, writing a copy of
2724  * each value to the writer.  The reader will be moved forward to its
2725  * end position.
2726  *
2727  * @param writer the writer to copy to
2728  * @param reader the reader to copy from
2729  * @returns #FALSE if no memory
2730  */
2731 dbus_bool_t
_dbus_type_writer_write_reader(DBusTypeWriter * writer,DBusTypeReader * reader)2732 _dbus_type_writer_write_reader (DBusTypeWriter       *writer,
2733                                 DBusTypeReader       *reader)
2734 {
2735   return _dbus_type_writer_write_reader_partial (writer, reader, NULL, 0, 0, NULL);
2736 }
2737 
2738 /*
2739  * If disabled, a writer can still be iterated forward and recursed/unrecursed
2740  * but won't write any values. Types will still be written unless the
2741  * writer is a "values only" writer, because the writer needs access to
2742  * a valid signature to be able to iterate.
2743  *
2744  * @param writer the type writer
2745  * @param enabled #TRUE if values should be written
2746  */
2747 static void
_dbus_type_writer_set_enabled(DBusTypeWriter * writer,dbus_bool_t enabled)2748 _dbus_type_writer_set_enabled (DBusTypeWriter   *writer,
2749                                dbus_bool_t       enabled)
2750 {
2751   writer->enabled = enabled != FALSE;
2752 }
2753 
2754 /** @} */ /* end of DBusMarshal group */
2755 
2756 /* tests in dbus-marshal-recursive-util.c */
2757