1 /* By Bryan Henderson July 2006.
2 
3    Contributed to the public domain.
4 */
5 
6 #include "xmlrpc_config.h"
7 
8 #include <stddef.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <string.h>
12 
13 #include "bool.h"
14 #include "c_util.h"
15 #include "mallocvar.h"
16 #include "stdargx.h"
17 
18 #include "xmlrpc-c/base.h"
19 #include "xmlrpc-c/base_int.h"
20 #include "xmlrpc-c/string_int.h"
21 
22 
23 /* THE DECOMPOSITION TREE
24 
25    We execute xmlrpc_decompose_value() in two steps:
26 
27    1) Create a "decomposition tree" that tells how Caller wants the XML-RPC
28       value decomposed.
29 
30    2) Using that tree, decompose the value.  I.e. store stuff in the variables
31       in which Caller wants it stored.
32 
33    The decomposition tree is composed of information from the format
34    string and the variable arguments that the format string describes.
35    Nothing in the tree is derived from the actual XML-RPC value being
36    decomposed, and the tree may in fact be invalid for the particular
37    XML-RPC value it's meant for.
38 
39    If the XML-RPC value is a simple value such as an integer, the
40    decomposition tree is trivial -- it's a single node that says
41    "store the value of an integer via pointer P".
42 
43    Where it gets interesting is where the XML-RPC value to be decomposed
44    is a complex value (array or struct).  Then, the root node of the tree
45    says, e.g., "decompose a 5-item array according to the following
46    5 decomposition trees" and it points to 5 additional nodes.  Each of
47    those nodes is the root of another decomposition tree (which can also
48    be called a branch in this context).  Each of those branches tells
49    how to decompose one of the items of the array.
50 
51    Roots, interior nodes, and leaves are all essentially the same data
52    type.
53 */
54 
55 struct integerDecomp {
56     xmlrpc_int32 * valueP;
57 };
58 
59 struct boolDecomp {
60     xmlrpc_bool * valueP;
61 };
62 
63 struct doubleDecomp {
64     double * valueP;
65 };
66 
67 struct datetimeTDecomp {
68     time_t * valueP;
69 };
70 
71 struct datetime8Decomp {
72     const char ** valueP;
73 };
74 
75 struct stringDecomp {
76     const char ** valueP;
77     size_t * sizeP;
78         /* NULL means don't store a size */
79 };
80 
81 struct wideStringDecomp {
82 #if HAVE_UNICODE_WCHAR
83     const wchar_t ** valueP;
84 #endif
85     size_t * sizeP;
86         /* NULL means don't store a size */
87 };
88 
89 struct bitStringDecomp {
90     const unsigned char ** valueP;
91     size_t * sizeP;
92 };
93 
94 struct cptrDecomp {
95     void ** valueP;
96 };
97 
98 struct i8Decomp {
99     xmlrpc_int64 * valueP;
100 };
101 
102 struct valueDecomp {
103     xmlrpc_value ** valueP;
104 };
105 
106 struct arrayValDecomp {
107     xmlrpc_value ** valueP;
108 };
109 
110 struct structValDecomp {
111     xmlrpc_value ** valueP;
112 };
113 
114 struct arrayDecomp {
115     unsigned int itemCnt;
116     bool ignoreExcess;
117         /* If there are more that 'itemCnt' items in the array, just
118            extract the first 'itemCnt' and ignore the rest, rather than
119            fail the decomposition.
120         */
121     struct decompTreeNode * itemArray[16];
122         /* Only first 'itemCnt' elements of this array are defined */
123 };
124 
125 struct mbrDecomp {
126     const char * key;
127         /* The key for the member whose value client wants to extract */
128     struct decompTreeNode * decompTreeP;
129         /* Instructions on how to decompose (extract) member's value */
130 };
131 
132 struct structDecomp {
133     unsigned int mbrCnt;
134     struct mbrDecomp mbrArray[16];
135 };
136 
137 
138 struct decompTreeNode {
139     char formatSpecChar;
140         /* e.g. 'i', 'b', '8', 'A'.  '(' means array; '{' means struct */
141     union {
142     /*------------------------------------------------------------------------
143       'formatSpecChar' selects among these members.
144     -------------------------------------------------------------------------*/
145         struct integerDecomp    Tinteger;
146         struct boolDecomp       Tbool;
147         struct doubleDecomp     Tdouble;
148         struct datetimeTDecomp  TdatetimeT;
149         struct datetime8Decomp  Tdatetime8;
150         struct stringDecomp     Tstring;
151         struct wideStringDecomp TwideString;
152         struct bitStringDecomp  TbitString;
153         struct cptrDecomp       Tcptr;
154         struct i8Decomp         Ti8;
155         struct valueDecomp      Tvalue;
156         struct arrayValDecomp   TarrayVal;
157         struct structValDecomp  TstructVal;
158         struct arrayDecomp      Tarray;
159         struct structDecomp     Tstruct;
160     } store;
161 
162 };
163 
164 
165 
166 /* prototype for recursive calls */
167 static void
168 releaseDecomposition(const struct decompTreeNode * const decompRootP,
169                      bool                          const oldstyleMemMgmt);
170 
171 
172 static void
releaseDecompArray(struct arrayDecomp const arrayDecomp,bool const oldstyleMemMgmt)173 releaseDecompArray(struct arrayDecomp const arrayDecomp,
174                    bool               const oldstyleMemMgmt) {
175 
176     unsigned int i;
177     for (i = 0; i < arrayDecomp.itemCnt; ++i) {
178         releaseDecomposition(arrayDecomp.itemArray[i], oldstyleMemMgmt);
179     }
180 }
181 
182 
183 static void
releaseDecompStruct(struct structDecomp const structDecomp,bool const oldstyleMemMgmt)184 releaseDecompStruct(struct structDecomp const structDecomp,
185                     bool                const oldstyleMemMgmt) {
186 
187     unsigned int i;
188     for (i = 0; i < structDecomp.mbrCnt; ++i) {
189         releaseDecomposition(structDecomp.mbrArray[i].decompTreeP,
190                              oldstyleMemMgmt);
191     }
192 }
193 
194 
195 
196 static void
releaseDecomposition(const struct decompTreeNode * const decompRootP,bool const oldstyleMemMgmt)197 releaseDecomposition(const struct decompTreeNode * const decompRootP,
198                      bool                          const oldstyleMemMgmt) {
199 /*----------------------------------------------------------------------------
200    Assuming that Caller has decomposed something according to 'decompRootP',
201    release whatever resources the decomposed information occupies.
202 
203    E.g. if it's  an XML-RPC string, Caller would have allocated memory
204    for the C string that represents the decomposed value of XML-RPC string,
205    and we release that memory.
206 -----------------------------------------------------------------------------*/
207     switch (decompRootP->formatSpecChar) {
208     case 'i':
209     case 'b':
210     case 'd':
211     case 'n':
212     case 'I':
213     case 't':
214     case 'p':
215         /* Nothing was allocated; nothing to release */
216         break;
217     case '8':
218         xmlrpc_strfree(*decompRootP->store.Tdatetime8.valueP);
219         break;
220     case 's':
221         xmlrpc_strfree(*decompRootP->store.Tstring.valueP);
222         break;
223     case 'w':
224         free((void*)*decompRootP->store.TwideString.valueP);
225         break;
226     case '6':
227         free((void*)*decompRootP->store.TbitString.valueP);
228         break;
229     case 'V':
230         xmlrpc_DECREF(*decompRootP->store.Tvalue.valueP);
231         break;
232     case 'A':
233         xmlrpc_DECREF(*decompRootP->store.TarrayVal.valueP);
234         break;
235     case 'S':
236         xmlrpc_DECREF(*decompRootP->store.TstructVal.valueP);
237         break;
238     case '(':
239         releaseDecompArray(decompRootP->store.Tarray, oldstyleMemMgmt);
240         break;
241     case '{':
242         releaseDecompStruct(decompRootP->store.Tstruct, oldstyleMemMgmt);
243         break;
244     }
245 }
246 
247 
248 
249 /* Prototype for recursive invocation: */
250 
251 static void
252 decomposeValueWithTree(xmlrpc_env *                  const envP,
253                        xmlrpc_value *                const valueP,
254                        bool                          const oldstyleMemMgmt,
255                        const struct decompTreeNode * const decompRootP);
256 
257 
258 
259 static void
validateArraySize(xmlrpc_env * const envP,const xmlrpc_value * const arrayP,struct arrayDecomp const arrayDecomp)260 validateArraySize(xmlrpc_env *         const envP,
261                   const xmlrpc_value * const arrayP,
262                   struct arrayDecomp   const arrayDecomp) {
263 
264     unsigned int size;
265 
266     size = xmlrpc_array_size(envP, arrayP);
267     if (!envP->fault_occurred) {
268         if (arrayDecomp.itemCnt > size)
269             xmlrpc_env_set_fault_formatted(
270                 envP, XMLRPC_INDEX_ERROR,
271                 "Format string requests %u items from array, but array "
272                 "has only %u items.", arrayDecomp.itemCnt, size);
273         else if (arrayDecomp.itemCnt < size && !arrayDecomp.ignoreExcess)
274             xmlrpc_env_set_fault_formatted(
275                 envP, XMLRPC_INDEX_ERROR,
276                 "Format string requests exactly %u items from array, "
277                 "but array has %u items.  (A '*' at the end would avoid "
278                 "this failure)", arrayDecomp.itemCnt, size);
279     }
280 }
281 
282 
283 
284 static void
parsearray(xmlrpc_env * const envP,const xmlrpc_value * const arrayP,struct arrayDecomp const arrayDecomp,bool const oldstyleMemMgmt)285 parsearray(xmlrpc_env *         const envP,
286            const xmlrpc_value * const arrayP,
287            struct arrayDecomp   const arrayDecomp,
288            bool                 const oldstyleMemMgmt) {
289 
290     validateArraySize(envP, arrayP, arrayDecomp);
291 
292     if (!envP->fault_occurred) {
293         unsigned int doneCnt;
294 
295         doneCnt = 0;
296         while(doneCnt < arrayDecomp.itemCnt && !envP->fault_occurred) {
297             xmlrpc_value * itemP;
298 
299             xmlrpc_array_read_item(envP, arrayP, doneCnt, &itemP);
300 
301             if (!envP->fault_occurred) {
302                 XMLRPC_ASSERT(doneCnt < ARRAY_SIZE(arrayDecomp.itemArray));
303                 decomposeValueWithTree(envP, itemP, oldstyleMemMgmt,
304                                        arrayDecomp.itemArray[doneCnt]);
305 
306                 if (!envP->fault_occurred)
307                     ++doneCnt;
308 
309                 xmlrpc_DECREF(itemP);
310             }
311         }
312         if (envP->fault_occurred) {
313             /* Release the items we completed before we failed. */
314             unsigned int i;
315             for (i = 0; i < doneCnt; ++i)
316                 releaseDecomposition(arrayDecomp.itemArray[i],
317                                      oldstyleMemMgmt);
318         }
319     }
320 }
321 
322 
323 
324 static void
parsestruct(xmlrpc_env * const envP,xmlrpc_value * const structP,struct structDecomp const structDecomp,bool const oldstyleMemMgmt)325 parsestruct(xmlrpc_env *        const envP,
326             xmlrpc_value *      const structP,
327             struct structDecomp const structDecomp,
328             bool                const oldstyleMemMgmt) {
329 
330     unsigned int doneCount;
331 
332     doneCount = 0;  /* No members done yet */
333 
334     while (doneCount < structDecomp.mbrCnt && !envP->fault_occurred) {
335         const char * const key = structDecomp.mbrArray[doneCount].key;
336 
337         xmlrpc_value * valueP;
338 
339         xmlrpc_struct_read_value(envP, structP, key, &valueP);
340 
341         if (!envP->fault_occurred) {
342             decomposeValueWithTree(
343                 envP, valueP, oldstyleMemMgmt,
344                 structDecomp.mbrArray[doneCount].decompTreeP);
345 
346             if (!envP->fault_occurred)
347                 ++doneCount;
348 
349             xmlrpc_DECREF(valueP);
350         }
351     }
352 
353     if (envP->fault_occurred) {
354         unsigned int i;
355         for (i = 0; i < doneCount; ++i)
356             releaseDecomposition(structDecomp.mbrArray[i].decompTreeP,
357                                  oldstyleMemMgmt);
358     }
359 }
360 
361 
362 
363 static void
readString(xmlrpc_env * const envP,const xmlrpc_value * const valueP,const char ** const stringValueP,bool const oldstyleMemMgmt)364 readString(xmlrpc_env *         const envP,
365            const xmlrpc_value * const valueP,
366            const char **        const stringValueP,
367            bool                 const oldstyleMemMgmt) {
368 
369     if (oldstyleMemMgmt) {
370         xmlrpc_read_string_old(envP, valueP, stringValueP);
371     } else
372         xmlrpc_read_string(envP, valueP, stringValueP);
373 }
374 
375 
376 
377 static void
readStringLp(xmlrpc_env * const envP,const xmlrpc_value * const valueP,size_t * const lengthP,const char ** const stringValueP,bool const oldstyleMemMgmt)378 readStringLp(xmlrpc_env *         const envP,
379              const xmlrpc_value * const valueP,
380              size_t *             const lengthP,
381              const char **        const stringValueP,
382              bool                 const oldstyleMemMgmt) {
383 
384     if (oldstyleMemMgmt) {
385         xmlrpc_read_string_lp_old(envP, valueP, lengthP, stringValueP);
386     } else
387         xmlrpc_read_string_lp(envP, valueP, lengthP, stringValueP);
388 }
389 
390 
391 
392 #if HAVE_UNICODE_WCHAR
393 static void
readStringW(xmlrpc_env * const envP,xmlrpc_value * const valueP,const wchar_t ** const stringValueP,bool const oldstyleMemMgmt)394 readStringW(xmlrpc_env *     const envP,
395             xmlrpc_value *   const valueP,
396             const wchar_t ** const stringValueP,
397             bool             const oldstyleMemMgmt) {
398 
399     if (oldstyleMemMgmt) {
400         xmlrpc_read_string_w_old(envP, valueP, stringValueP);
401     } else
402         xmlrpc_read_string_w(envP, valueP, stringValueP);
403 }
404 
405 
406 
407 static void
readStringWLp(xmlrpc_env * const envP,xmlrpc_value * const valueP,size_t * const lengthP,const wchar_t ** const stringValueP,bool const oldstyleMemMgmt)408 readStringWLp(xmlrpc_env *     const envP,
409               xmlrpc_value *   const valueP,
410               size_t *         const lengthP,
411               const wchar_t ** const stringValueP,
412               bool             const oldstyleMemMgmt) {
413 
414     if (oldstyleMemMgmt) {
415         xmlrpc_read_string_w_lp_old(envP, valueP, lengthP, stringValueP);
416     } else
417         xmlrpc_read_string_w_lp(envP, valueP, lengthP, stringValueP);
418 }
419 #endif
420 
421 
422 static void
readDatetime8Str(xmlrpc_env * const envP,const xmlrpc_value * const valueP,const char ** const stringValueP,bool const oldstyleMemMgmt)423 readDatetime8Str(xmlrpc_env *         const envP,
424                  const xmlrpc_value * const valueP,
425                  const char **        const stringValueP,
426                  bool                 const oldstyleMemMgmt) {
427 
428     if (oldstyleMemMgmt)
429         xmlrpc_read_datetime_str_old(envP, valueP, stringValueP);
430     else
431         xmlrpc_read_datetime_str(envP, valueP, stringValueP);
432 }
433 
434 
435 
436 static void
readBase64(xmlrpc_env * const envP,const xmlrpc_value * const valueP,size_t * const lengthP,const unsigned char ** const byteStringValueP,bool const oldstyleMemMgmt)437 readBase64(xmlrpc_env *           const envP,
438            const xmlrpc_value *   const valueP,
439            size_t *               const lengthP,
440            const unsigned char ** const byteStringValueP,
441            bool                   const oldstyleMemMgmt) {
442 
443     if (oldstyleMemMgmt)
444         xmlrpc_read_base64_old(envP, valueP, lengthP, byteStringValueP);
445     else
446         xmlrpc_read_base64(envP, valueP, lengthP, byteStringValueP);
447 }
448 
449 
450 static void
decomposeValueWithTree(xmlrpc_env * const envP,xmlrpc_value * const valueP,bool const oldstyleMemMgmt,const struct decompTreeNode * const decompRootP)451 decomposeValueWithTree(xmlrpc_env *                  const envP,
452                        xmlrpc_value *                const valueP,
453                        bool                          const oldstyleMemMgmt,
454                        const struct decompTreeNode * const decompRootP) {
455 /*----------------------------------------------------------------------------
456    Decompose XML-RPC value *valueP, given the decomposition tree
457    *decompRootP.  The decomposition tree tells what structure *valueP
458    is expected to have and where to put the various components of it
459    (e.g. it says "it's an array of 3 integers.  Put their values at
460    locations x, y, and z")
461 -----------------------------------------------------------------------------*/
462     switch (decompRootP->formatSpecChar) {
463     case '-':
464         /* There's nothing to validate or return */
465         break;
466     case 'i':
467         xmlrpc_read_int(envP, valueP, decompRootP->store.Tinteger.valueP);
468         break;
469 
470     case 'b':
471         xmlrpc_read_bool(envP, valueP, decompRootP->store.Tbool.valueP);
472         break;
473 
474     case 'd':
475         xmlrpc_read_double(envP, valueP, decompRootP->store.Tdouble.valueP);
476         break;
477 
478     case 't':
479         xmlrpc_read_datetime_sec(envP, valueP,
480                                  decompRootP->store.TdatetimeT.valueP);
481         break;
482 
483     case '8':
484         readDatetime8Str(envP, valueP, decompRootP->store.Tdatetime8.valueP,
485                          oldstyleMemMgmt);
486         break;
487 
488     case 's':
489         if (decompRootP->store.Tstring.sizeP)
490             readStringLp(envP, valueP,
491                          decompRootP->store.Tstring.sizeP,
492                          decompRootP->store.Tstring.valueP,
493                          oldstyleMemMgmt);
494         else
495             readString(envP, valueP, decompRootP->store.Tstring.valueP,
496                        oldstyleMemMgmt);
497         break;
498 
499     case 'w':
500 #if HAVE_UNICODE_WCHAR
501         if (decompRootP->store.Tstring.sizeP)
502             readStringWLp(envP, valueP,
503                           decompRootP->store.TwideString.sizeP,
504                           decompRootP->store.TwideString.valueP,
505                           oldstyleMemMgmt);
506         else
507             readStringW(envP, valueP, decompRootP->store.TwideString.valueP,
508                         oldstyleMemMgmt);
509 #else
510         XMLRPC_ASSERT(false);
511 #endif /* HAVE_UNICODE_WCHAR */
512         break;
513 
514     case '6':
515         readBase64(envP, valueP,
516                    decompRootP->store.TbitString.sizeP,
517                    decompRootP->store.TbitString.valueP,
518                    oldstyleMemMgmt);
519         break;
520 
521     case 'n':
522         xmlrpc_read_nil(envP, valueP);
523         break;
524 
525     case 'I':
526         xmlrpc_read_i8(envP, valueP, decompRootP->store.Ti8.valueP);
527         break;
528 
529     case 'p':
530         xmlrpc_read_cptr(envP, valueP, decompRootP->store.Tcptr.valueP);
531         break;
532 
533     case 'V':
534         *decompRootP->store.Tvalue.valueP = valueP;
535         if (!oldstyleMemMgmt)
536             xmlrpc_INCREF(valueP);
537         break;
538 
539     case 'A':
540         if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_ARRAY)
541             xmlrpc_env_set_fault_formatted(
542                 envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type "
543                 "%s, but the 'A' specifier requires type ARRAY",
544                 xmlrpc_type_name(xmlrpc_value_type(valueP)));
545         else {
546             *decompRootP->store.TarrayVal.valueP = valueP;
547             if (!oldstyleMemMgmt)
548                 xmlrpc_INCREF(valueP);
549         }
550         break;
551 
552     case 'S':
553         if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_STRUCT)
554             xmlrpc_env_set_fault_formatted(
555                 envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type "
556                 "%s, but the 'S' specifier requires type STRUCT.",
557                 xmlrpc_type_name(xmlrpc_value_type(valueP)));
558         else {
559             *decompRootP->store.TstructVal.valueP = valueP;
560             if (!oldstyleMemMgmt)
561                 xmlrpc_INCREF(valueP);
562         }
563         break;
564 
565     case '(':
566         if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_ARRAY)
567             xmlrpc_env_set_fault_formatted(
568                 envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type "
569                 "%s, but the '(...)' specifier requires type ARRAY",
570                 xmlrpc_type_name(xmlrpc_value_type(valueP)));
571         else
572             parsearray(envP, valueP, decompRootP->store.Tarray,
573                        oldstyleMemMgmt);
574         break;
575 
576     case '{':
577         if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_STRUCT)
578             xmlrpc_env_set_fault_formatted(
579                 envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type "
580                 "%s, but the '{...}' specifier requires type STRUCT",
581                 xmlrpc_type_name(xmlrpc_value_type(valueP)));
582         else
583             parsestruct(envP, valueP, decompRootP->store.Tstruct,
584                         oldstyleMemMgmt);
585         break;
586 
587     default:
588         /* Every format character that is allowed in a decomposition tree
589            node is handled above.
590         */
591         XMLRPC_ASSERT(false);
592     }
593 }
594 
595 
596 /* Forward declaration for recursive calls */
597 
598 static void
599 createDecompTreeNext(xmlrpc_env *             const envP,
600                      const char **            const formatP,
601                      va_listx *               const argsP,
602                      struct decompTreeNode ** const decompNodePP);
603 
604 
605 
606 static void
buildWideStringNode(xmlrpc_env * const envP ATTR_UNUSED,const char ** const formatP,va_listx * const argsP,struct decompTreeNode * const decompNodeP)607 buildWideStringNode(xmlrpc_env *            const envP ATTR_UNUSED,
608                     const char **           const formatP,
609                     va_listx *              const argsP,
610                     struct decompTreeNode * const decompNodeP) {
611 
612 #if HAVE_UNICODE_WCHAR
613     decompNodeP->store.TwideString.valueP =
614         (const wchar_t**) va_arg(argsP->v, wchar_t**);
615     if (**formatP == '#') {
616         decompNodeP->store.TwideString.sizeP =
617             (size_t*) va_arg(argsP->v, size_t**);
618         (*formatP)++;
619     } else
620         decompNodeP->store.TwideString.sizeP = NULL;
621 #else
622     xmlrpc_faultf(envP,
623                   "This XML-RPC For C/C++ library was built without Unicode "
624                   "wide character capability.  'w' isn't available.");
625 #endif /* HAVE_UNICODE_WCHAR */
626 }
627 
628 
629 
630 static void
destroyDecompTree(struct decompTreeNode * const decompRootP)631 destroyDecompTree(struct decompTreeNode * const decompRootP) {
632 
633     switch (decompRootP->formatSpecChar) {
634     case '(': {
635         unsigned int i;
636         for (i = 0; i < decompRootP->store.Tarray.itemCnt; ++i)
637             destroyDecompTree(decompRootP->store.Tarray.itemArray[i]);
638     } break;
639     case '{': {
640         unsigned int i;
641         for (i = 0; i < decompRootP->store.Tstruct.mbrCnt; ++i)
642             destroyDecompTree(
643                 decompRootP->store.Tstruct.mbrArray[i].decompTreeP);
644     } break;
645     }
646 
647     free(decompRootP);
648 }
649 
650 
651 
652 static void
processArraySpecTail(xmlrpc_env * const envP,const char ** const formatP,bool * const hasTrailingAsteriskP,char const delim)653 processArraySpecTail(xmlrpc_env *  const envP,
654                      const char ** const formatP,
655                      bool *        const hasTrailingAsteriskP,
656                      char          const delim) {
657 
658     if (**formatP == '*') {
659         *hasTrailingAsteriskP = true;
660 
661         ++*formatP;
662 
663         if (!**formatP)
664             xmlrpc_faultf(envP, "missing closing delimiter ('%c')", delim);
665         else if (**formatP != delim)
666             xmlrpc_faultf(envP, "character following '*' in array "
667                           "specification should be the closing delimiter "
668                           "'%c', but is '%c'", delim, **formatP);
669     } else {
670         *hasTrailingAsteriskP = false;
671 
672         if (!**formatP)
673             xmlrpc_faultf(envP, "missing closing delimiter ('%c')", delim);
674     }
675     if (!envP->fault_occurred)
676         XMLRPC_ASSERT(**formatP == delim);
677 }
678 
679 
680 
681 static void
buildArrayDecompBranch(xmlrpc_env * const envP,const char ** const formatP,char const delim,va_listx * const argsP,struct decompTreeNode * const decompNodeP)682 buildArrayDecompBranch(xmlrpc_env *            const envP,
683                        const char **           const formatP,
684                        char                    const delim,
685                        va_listx *              const argsP,
686                        struct decompTreeNode * const decompNodeP) {
687 /*----------------------------------------------------------------------------
688    Fill in the decomposition tree node *decompNodeP to cover an array
689    whose items are described by *formatP.  To wit, they are the values
690    described by successive format specifiers in *formatP up to but not
691    including the next 'delim' character.
692 
693    Plus, the last character before the delimiter might be a '*', which
694    means "ignore any additional items in the array."
695 
696    We create a node (and whole branch if required) to describe each array
697    item.
698 
699    The pointers to where those items are to be stored are given by
700    'argsP'.
701 
702    We advance *formatP to the delimiter character, and advance 'argsP'
703    past whatever arguments we use.
704 -----------------------------------------------------------------------------*/
705     unsigned int itemCnt;
706         /* Number of array items in the branch so far */
707 
708     itemCnt = 0;  /* Branch is empty so far */
709 
710     while (**formatP && **formatP != delim && **formatP != '*' &&
711            !envP->fault_occurred) {
712         if (itemCnt >= ARRAY_SIZE(decompNodeP->store.Tarray.itemArray))
713             xmlrpc_faultf(envP, "Too many array items in format string.  "
714                           "The most items you can have for an array in "
715                           "a format string is %u.", (unsigned)
716                           ARRAY_SIZE(decompNodeP->store.Tarray.itemArray));
717         else {
718             struct decompTreeNode * itemNodeP;
719 
720             createDecompTreeNext(envP, formatP, argsP, &itemNodeP);
721 
722             if (!envP->fault_occurred)
723                 decompNodeP->store.Tarray.itemArray[itemCnt++] = itemNodeP;
724         }
725     }
726     if (!envP->fault_occurred) {
727         decompNodeP->store.Tarray.itemCnt = itemCnt;
728         processArraySpecTail(envP, formatP,
729                              &decompNodeP->store.Tarray.ignoreExcess,
730                              delim);
731     }
732     if (envP->fault_occurred) {
733         unsigned int i;
734         for (i = 0; i < itemCnt; ++i)
735             destroyDecompTree(decompNodeP->store.Tarray.itemArray[i]);
736     }
737 }
738 
739 
740 
741 static void
doStructValue(xmlrpc_env * const envP,const char ** const formatP,va_listx * const argsP,struct mbrDecomp * const mbrP)742 doStructValue(xmlrpc_env *       const envP,
743               const char **      const formatP,
744               va_listx *         const argsP,
745               struct mbrDecomp * const mbrP) {
746 
747     struct decompTreeNode * valueNodeP;
748 
749     mbrP->key = (const char*) va_arg(argsP->v, char*);
750 
751     createDecompTreeNext(envP, formatP, argsP,  &valueNodeP);
752 
753     if (!envP->fault_occurred)
754         mbrP->decompTreeP = valueNodeP;
755 }
756 
757 
758 
759 static void
skipAsterisk(xmlrpc_env * const envP,const char ** const formatP,char const delim)760 skipAsterisk(xmlrpc_env *  const envP,
761              const char ** const formatP,
762              char          const delim) {
763 
764     if (**formatP == '*') {
765         ++*formatP;
766 
767         if (!**formatP)
768             xmlrpc_faultf(envP, "missing closing delimiter ('%c')", delim);
769         else if (**formatP != delim)
770             xmlrpc_faultf(envP, "junk after '*' in the specifier of an "
771                           "array.  First character='%c'", **formatP);
772     } else
773         /* Conceptually, one can make it an error to leave some struct
774            members behind, but we have never had code that knows how to
775            recognize that case.
776         */
777         xmlrpc_faultf(envP,
778                       "You must put a trailing '*' in the specifiers for "
779                       "struct members to signify it's OK if there are "
780                       "additional members you didn't get.");
781 }
782 
783 
784 
785 static void
skipColon(xmlrpc_env * const envP,const char ** const formatP,char const delim)786 skipColon(xmlrpc_env * const envP,
787           const char ** const formatP,
788           char          const delim) {
789 
790     if (**formatP == '\0')
791         xmlrpc_faultf(envP, "format string ends in the middle of a struct "
792                       "member specifier");
793     else if (**formatP == delim)
794         xmlrpc_faultf(envP, "member list ends in the middle of a member");
795     else if (**formatP != ':')
796         xmlrpc_faultf(envP, "In a struct specifier, '%c' found "
797                       "where a colon (':') separating key and "
798                       "value was expected.", **formatP);
799 }
800 
801 
802 
803 static void
skipComma(xmlrpc_env * const envP,const char ** const formatP,char const delim)804 skipComma(xmlrpc_env *  const envP,
805           const char ** const formatP,
806           char          const delim) {
807 
808     if (**formatP && **formatP != delim) {
809         if (**formatP == ',')
810             ++*formatP;  /* skip over comma */
811         else
812             xmlrpc_faultf(envP, "'%c' where we expected a ',' "
813                           "to separate struct members", **formatP);
814     }
815 }
816 
817 
818 
819 static void
buildStructDecompBranch(xmlrpc_env * const envP,const char ** const formatP,char const delim,va_listx * const argsP,struct decompTreeNode * const decompNodeP)820 buildStructDecompBranch(xmlrpc_env *            const envP,
821                         const char **           const formatP,
822                         char                    const delim,
823                         va_listx *              const argsP,
824                         struct decompTreeNode * const decompNodeP) {
825 /*----------------------------------------------------------------------------
826    Fill in the decomposition tree node *decompNodeP to cover a struct
827    whose members are described by *formatP.  To wit, they are the values
828    described by successive format specifiers in *formatP up to but not
829    including the next 'delim' character.
830 
831    We create a node (and whole branch if required) to describe each
832    struct member value.
833 
834    The pointers to where those values are to be stored are given by
835    'argsP'.
836 
837    The names of the members to be extracted are also given by 'argsP'.
838 
839    We advance *formatP to the delimiter character, and advance 'argsP'
840    past whatever arguments we use.
841 -----------------------------------------------------------------------------*/
842     unsigned int memberCnt;
843         /* Number of struct members in the branch so far */
844 
845     memberCnt = 0;  /* Branch is empty so far */
846 
847     while (**formatP && **formatP != delim && **formatP != '*' &&
848            !envP->fault_occurred) {
849         if (memberCnt >= ARRAY_SIZE(decompNodeP->store.Tstruct.mbrArray))
850             xmlrpc_faultf(envP,
851                           "Too many structure members in format string.  "
852                           "The most members you can specify in "
853                           "a format string is %u.", (unsigned)
854                           ARRAY_SIZE(decompNodeP->store.Tstruct.mbrArray));
855         else {
856             struct mbrDecomp * const mbrP =
857                 &decompNodeP->store.Tstruct.mbrArray[memberCnt];
858 
859             if (**formatP != 's')
860                 xmlrpc_faultf(envP, "In a struct specifier, the specifier "
861                               "for the key is '%c', but it must be 's'.",
862                               **formatP);
863             else {
864                 ++*formatP;
865 
866                 skipColon(envP, formatP, delim);
867 
868                 if (!envP->fault_occurred) {
869                     ++*formatP;
870 
871                     doStructValue(envP, formatP, argsP, mbrP);
872 
873                     if (!envP->fault_occurred)
874                         ++memberCnt;
875 
876                     skipComma(envP, formatP, delim);
877                 }
878             }
879         }
880     }
881     decompNodeP->store.Tstruct.mbrCnt = memberCnt;
882 
883     if (!envP->fault_occurred) {
884         skipAsterisk(envP, formatP, delim);
885         if (!envP->fault_occurred)
886             XMLRPC_ASSERT(**formatP == delim);
887     }
888 
889     if (envP->fault_occurred) {
890         unsigned int i;
891         for (i = 0; i < memberCnt; ++i)
892             destroyDecompTree(
893                 decompNodeP->store.Tstruct.mbrArray[i].decompTreeP);
894     }
895 }
896 
897 
898 
899 static void
createDecompTreeNext(xmlrpc_env * const envP,const char ** const formatP,va_listx * const argsP,struct decompTreeNode ** const decompNodePP)900 createDecompTreeNext(xmlrpc_env *             const envP,
901                      const char **            const formatP,
902                      va_listx *               const argsP,
903                      struct decompTreeNode ** const decompNodePP) {
904 /*----------------------------------------------------------------------------
905    Create a branch of a decomposition tree that applies to the first
906    value described by '*formatP', and advance *formatP past the description
907    of that first value.  E.g.:
908 
909      - If *formatP is "isb", we create a branch consisting of one
910        node -- for an integer.  We advance *formatP by one character, so
911        it points to the "s".
912 
913      - If *formatP is "(isb)s", we create a branch that represents the
914        array (isb) and advance *formatP past the closing parenthesis to
915        point to the final "s".  We return as *decompNodePP a pointer to
916        a node for the array, and that array in turn points to nodes for
917        each of the 3 array items:  one for an integer, one for a string,
918        and one for a boolean.
919 
920    The locations at which the components of that value are to be
921    stored (which is the main contents of the branch we create) are
922    given by 'argsP'.
923 
924    Return as *decompNodeP a pointer to the root node of the branch we
925    generate.
926 -----------------------------------------------------------------------------*/
927     struct decompTreeNode * decompNodeP;
928 
929     MALLOCVAR(decompNodeP);
930 
931     if (decompNodeP == NULL)
932         xmlrpc_faultf(envP, "Could not allocate space for a decomposition "
933                       "tree node");
934     else {
935         decompNodeP->formatSpecChar = *(*formatP)++;
936 
937         switch (decompNodeP->formatSpecChar) {
938         case '-':
939             /* There's nothing to store */
940             break;
941         case 'i':
942             decompNodeP->store.Tinteger.valueP =
943                 (xmlrpc_int32*) va_arg(argsP->v, xmlrpc_int32*);
944             break;
945 
946         case 'b':
947             decompNodeP->store.Tbool.valueP =
948                 (xmlrpc_bool*) va_arg(argsP->v, xmlrpc_bool*);
949             break;
950 
951         case 'd':
952             decompNodeP->store.Tdouble.valueP =
953                 (double*) va_arg(argsP->v, double*);
954             break;
955 
956         case 't':
957             decompNodeP->store.TdatetimeT.valueP =
958                 va_arg(argsP->v, time_t*);
959             break;
960 
961         case '8':
962             decompNodeP->store.Tdatetime8.valueP =
963                 (const char**) va_arg(argsP->v, char**);
964             break;
965 
966         case 's':
967             decompNodeP->store.Tstring.valueP =
968                 (const char**) va_arg(argsP->v, char**);
969             if (**formatP == '#') {
970                 decompNodeP->store.Tstring.sizeP =
971                     (size_t*) va_arg(argsP->v, size_t**);
972                 ++*formatP;
973             } else
974                 decompNodeP->store.Tstring.sizeP = NULL;
975             break;
976 
977         case 'w':
978             buildWideStringNode(envP, formatP, argsP, decompNodeP);
979             break;
980 
981         case '6':
982             decompNodeP->store.TbitString.valueP =
983                 (const unsigned char**) va_arg(argsP->v, unsigned char**);
984             decompNodeP->store.TbitString.sizeP =
985                 (size_t*) va_arg(argsP->v, size_t**);
986             break;
987 
988         case 'n':
989             /* There's no value to store */
990             break;
991 
992         case 'I':
993             decompNodeP->store.Ti8.valueP =
994                 (xmlrpc_int64 *) va_arg(argsP->v, xmlrpc_int64 *);
995             break;
996 
997         case 'p':
998             decompNodeP->store.Tcptr.valueP =
999                 (void**) va_arg(argsP->v, void**);
1000             break;
1001 
1002         case 'V':
1003             decompNodeP->store.Tvalue.valueP =
1004                 (xmlrpc_value**) va_arg(argsP->v, xmlrpc_value**);
1005             break;
1006 
1007         case 'A':
1008             decompNodeP->store.TarrayVal.valueP =
1009                 (xmlrpc_value**) va_arg(argsP->v, xmlrpc_value**);
1010             break;
1011 
1012         case 'S':
1013             decompNodeP->store.TstructVal.valueP =
1014                 (xmlrpc_value**) va_arg(argsP->v, xmlrpc_value**);
1015             break;
1016 
1017         case '(':
1018             buildArrayDecompBranch(envP, formatP, ')', argsP, decompNodeP);
1019             ++(*formatP);  /* skip past closing ')' */
1020             break;
1021 
1022         case '{':
1023             buildStructDecompBranch(envP, formatP, '}', argsP, decompNodeP);
1024             ++(*formatP);  /* skip past closing '}' */
1025             break;
1026 
1027         default:
1028             xmlrpc_faultf(envP, "Invalid format character '%c'",
1029                           decompNodeP->formatSpecChar);
1030         }
1031         if (envP->fault_occurred)
1032             free(decompNodeP);
1033         else
1034             *decompNodePP = decompNodeP;
1035     }
1036 }
1037 
1038 
1039 
1040 static void
createDecompTree(xmlrpc_env * const envP,const char * const format,va_listx const args,struct decompTreeNode ** const decompRootPP)1041 createDecompTree(xmlrpc_env *             const envP,
1042                  const char *             const format,
1043                  va_listx                 const args,
1044                  struct decompTreeNode ** const decompRootPP) {
1045 
1046     const char * formatCursor;
1047     struct decompTreeNode * decompRootP;
1048     va_listx currentArgs;
1049 
1050     currentArgs = args;
1051     formatCursor = &format[0];
1052     createDecompTreeNext(envP, &formatCursor, &currentArgs, &decompRootP);
1053     if (!envP->fault_occurred) {
1054         if (*formatCursor != '\0')
1055             xmlrpc_faultf(envP, "format string '%s' has garbage at the end: "
1056                           "'%s'.  It should be a specifier of a single value "
1057                           "(but that might be a complex value, such as an "
1058                           "array)", format, formatCursor);
1059 
1060         if (envP->fault_occurred)
1061             destroyDecompTree(decompRootP);
1062     }
1063     *decompRootPP = decompRootP;
1064 }
1065 
1066 
1067 
1068 static void
decomposeValue(xmlrpc_env * const envP,xmlrpc_value * const valueP,bool const oldstyleMemMgmt,const char * const format,va_listx const args)1069 decomposeValue(xmlrpc_env *   const envP,
1070                xmlrpc_value * const valueP,
1071                bool           const oldstyleMemMgmt,
1072                const char *   const format,
1073                va_listx       const args) {
1074 
1075     struct decompTreeNode * decompRootP;
1076 
1077     XMLRPC_ASSERT_ENV_OK(envP);
1078     XMLRPC_ASSERT_VALUE_OK(valueP);
1079     XMLRPC_ASSERT(format != NULL);
1080 
1081     createDecompTree(envP, format, args, &decompRootP);
1082 
1083     if (!envP->fault_occurred) {
1084         decomposeValueWithTree(envP, valueP, oldstyleMemMgmt, decompRootP);
1085 
1086         destroyDecompTree(decompRootP);
1087     }
1088 }
1089 
1090 
1091 
1092 void
xmlrpc_decompose_value_va(xmlrpc_env * const envP,xmlrpc_value * const valueP,const char * const format,va_list const args)1093 xmlrpc_decompose_value_va(xmlrpc_env *   const envP,
1094                           xmlrpc_value * const valueP,
1095                           const char *   const format,
1096                           va_list        const args) {
1097 
1098     bool const oldstyleMemMgtFalse = false;
1099     va_listx argsx;
1100 
1101     init_va_listx(&argsx, args);
1102 
1103     decomposeValue(envP, valueP, oldstyleMemMgtFalse, format, argsx);
1104 }
1105 
1106 
1107 
1108 void
xmlrpc_decompose_value(xmlrpc_env * const envP,xmlrpc_value * const value,const char * const format,...)1109 xmlrpc_decompose_value(xmlrpc_env *   const envP,
1110                        xmlrpc_value * const value,
1111                        const char *   const format,
1112                        ...) {
1113 
1114     va_list args;
1115 
1116     va_start(args, format);
1117     xmlrpc_decompose_value_va(envP, value, format, args);
1118     va_end(args);
1119 }
1120 
1121 
1122 
1123 void
xmlrpc_parse_value_va(xmlrpc_env * const envP,xmlrpc_value * const valueP,const char * const format,va_list const args)1124 xmlrpc_parse_value_va(xmlrpc_env *   const envP,
1125                       xmlrpc_value * const valueP,
1126                       const char *   const format,
1127                       va_list        const args) {
1128 
1129     bool const oldstyleMemMgmtTrue = true;
1130     va_listx argsx;
1131 
1132     init_va_listx(&argsx, args);
1133 
1134     decomposeValue(envP, valueP, oldstyleMemMgmtTrue, format, argsx);
1135 }
1136 
1137 
1138 
1139 void
xmlrpc_parse_value(xmlrpc_env * const envP,xmlrpc_value * const value,const char * const format,...)1140 xmlrpc_parse_value(xmlrpc_env *   const envP,
1141                    xmlrpc_value * const value,
1142                    const char *   const format,
1143                    ...) {
1144 
1145     va_list args;
1146 
1147     va_start(args, format);
1148     xmlrpc_parse_value_va(envP, value, format, args);
1149     va_end(args);
1150 }
1151