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