1 /********************************************************************
2  * sixtp-dom-parsers.c                                              *
3  * Copyright 2001 Gnumatic, Inc.                                    *
4  *                                                                  *
5  * This program is free software; you can redistribute it and/or    *
6  * modify it under the terms of the GNU General Public License as   *
7  * published by the Free Software Foundation; either version 2 of   *
8  * the License, or (at your option) any later version.              *
9  *                                                                  *
10  * This program is distributed in the hope that it will be useful,  *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
13  * GNU General Public License for more details.                     *
14  *                                                                  *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact:                        *
17  *                                                                  *
18  * Free Software Foundation           Voice:  +1-617-542-5942       *
19  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
20  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
21  *                                                                  *
22  ********************************************************************/
23 #include <glib.h>
24 
25 extern "C"
26 {
27 #include <config.h>
28 
29 #include <string.h>
30 
31 #include <gnc-engine.h>
32 }
33 
34 #include "gnc-xml-helper.h"
35 #include "sixtp-utils.h"
36 #include "sixtp-dom-parsers.h"
37 #include <kvp-frame.hpp>
38 
39 static QofLogModule log_module = GNC_MOD_IO;
40 
41 GncGUID*
dom_tree_to_guid(xmlNodePtr node)42 dom_tree_to_guid (xmlNodePtr node)
43 {
44     if (!node->properties)
45     {
46         return NULL;
47     }
48 
49     if (strcmp ((char*) node->properties->name, "type") != 0)
50     {
51         PERR ("Unknown attribute for id tag: %s",
52               node->properties->name ?
53               (char*) node->properties->name : "(null)");
54         return NULL;
55     }
56 
57     {
58         char* type;
59 
60         type = (char*)xmlNodeGetContent (node->properties->xmlAttrPropertyValue);
61 
62         /* handle new and guid the same for the moment */
63         if ((g_strcmp0 ("guid", type) == 0) || (g_strcmp0 ("new", type) == 0))
64         {
65             auto gid = guid_new ();
66             char* guid_str;
67 
68             guid_str = (char*)xmlNodeGetContent (node->xmlChildrenNode);
69             string_to_guid (guid_str, gid);
70             xmlFree (guid_str);
71             xmlFree (type);
72             return gid;
73         }
74         else
75         {
76             PERR ("Unknown type %s for attribute type for tag %s",
77                   type ? type : "(null)",
78                   node->properties->name ?
79                   (char*) node->properties->name : "(null)");
80             xmlFree (type);
81             return NULL;
82         }
83     }
84 }
85 
86 static KvpValue*
dom_tree_to_integer_kvp_value(xmlNodePtr node)87 dom_tree_to_integer_kvp_value (xmlNodePtr node)
88 {
89     gchar* text;
90     gint64 daint;
91     KvpValue* ret = NULL;
92 
93     text = dom_tree_to_text (node);
94 
95     if (string_to_gint64 (text, &daint))
96     {
97         ret = new KvpValue {daint};
98     }
99     g_free (text);
100 
101     return ret;
102 }
103 
104 gboolean
dom_tree_to_integer(xmlNodePtr node,gint64 * daint)105 dom_tree_to_integer (xmlNodePtr node, gint64* daint)
106 {
107     gchar* text;
108     gboolean ret;
109 
110     text = dom_tree_to_text (node);
111 
112     ret = string_to_gint64 (text, daint);
113 
114     g_free (text);
115 
116     return ret;
117 }
118 
119 gboolean
dom_tree_to_guint16(xmlNodePtr node,guint16 * i)120 dom_tree_to_guint16 (xmlNodePtr node, guint16* i)
121 {
122     gboolean ret;
123     guint j = 0;
124 
125     ret = dom_tree_to_guint (node, &j);
126     *i = (guint16) j;
127     return ret;
128 }
129 
130 gboolean
dom_tree_to_guint(xmlNodePtr node,guint * i)131 dom_tree_to_guint (xmlNodePtr node, guint* i)
132 {
133     gchar* text, *endptr;
134     gboolean ret;
135 
136     text = dom_tree_to_text (node);
137     /* In spite of the strange string_to_gint64 function, I'm just
138        going to use strtoul here until someone shows me the error of
139        my ways. -CAS */
140     *i = (guint) strtoul (text, &endptr, 0);
141     ret = (endptr != text);
142     g_free (text);
143     return ret;
144 }
145 
146 gboolean
dom_tree_to_boolean(xmlNodePtr node,gboolean * b)147 dom_tree_to_boolean (xmlNodePtr node, gboolean* b)
148 {
149     gchar* text;
150 
151     text = dom_tree_to_text (node);
152     if (g_ascii_strncasecmp (text, "true", 4) == 0)
153     {
154         *b = TRUE;
155         g_free (text);
156         return TRUE;
157     }
158     else if (g_ascii_strncasecmp (text, "false", 5) == 0)
159     {
160         *b = FALSE;
161         g_free (text);
162         return TRUE;
163     }
164     else
165     {
166         *b = FALSE;
167         g_free (text);
168         return FALSE;
169     }
170 }
171 
172 static KvpValue*
dom_tree_to_double_kvp_value(xmlNodePtr node)173 dom_tree_to_double_kvp_value (xmlNodePtr node)
174 {
175     gchar* text;
176     double dadoub;
177     KvpValue* ret = NULL;
178 
179     text = dom_tree_to_text (node);
180 
181     if (string_to_double (text, &dadoub))
182     {
183         ret = new KvpValue {dadoub};
184     }
185 
186     g_free (text);
187 
188     return ret;
189 }
190 
191 static KvpValue*
dom_tree_to_numeric_kvp_value(xmlNodePtr node)192 dom_tree_to_numeric_kvp_value (xmlNodePtr node)
193 {
194     gnc_numeric* danum;
195     KvpValue* ret = NULL;
196 
197     danum = dom_tree_to_gnc_numeric (node);
198 
199     if (danum)
200     {
201         ret = new KvpValue {*danum};
202     }
203 
204     g_free (danum);
205 
206     return ret;
207 }
208 
209 static KvpValue*
dom_tree_to_string_kvp_value(xmlNodePtr node)210 dom_tree_to_string_kvp_value (xmlNodePtr node)
211 {
212     const gchar* datext;
213     KvpValue* ret = NULL;
214 
215     datext = dom_tree_to_text (node);
216     if (datext)
217     {
218         ret = new KvpValue {datext};
219     }
220 
221     return ret;
222 }
223 
224 static KvpValue*
dom_tree_to_guid_kvp_value(xmlNodePtr node)225 dom_tree_to_guid_kvp_value (xmlNodePtr node)
226 {
227     GncGUID* daguid;
228     KvpValue* ret = NULL;
229 
230     daguid = dom_tree_to_guid (node);
231     if (daguid)
232     {
233         ret = new KvpValue {daguid};
234     }
235 
236     return ret;
237 }
238 
239 static KvpValue*
dom_tree_to_time64_kvp_value(xmlNodePtr node)240 dom_tree_to_time64_kvp_value (xmlNodePtr node)
241 {
242     Time64 t{dom_tree_to_time64 (node)};
243     return new KvpValue {t};
244 }
245 
246 static KvpValue*
dom_tree_to_gdate_kvp_value(xmlNodePtr node)247 dom_tree_to_gdate_kvp_value (xmlNodePtr node)
248 {
249     GDate* date;
250     KvpValue* ret = NULL;
251 
252     date = dom_tree_to_gdate (node);
253 
254     if (date)
255     {
256         ret = new KvpValue {*date};
257     }
258 
259     g_free (date);
260 
261     return ret;
262 }
263 
264 gboolean
string_to_binary(const gchar * str,void ** v,guint64 * data_len)265 string_to_binary (const gchar* str,  void** v, guint64* data_len)
266 {
267     guint64 str_len;
268     guchar* data;
269     unsigned int i, j;
270 
271     g_return_val_if_fail (v != NULL, FALSE);
272     g_return_val_if_fail (data_len != NULL, FALSE);
273 
274     str_len = strlen (str);
275 
276     /* Since no whitespace is allowed and hex encoding is 2 text chars
277        per binary char, the result must be half the input size and the
278        input size must be even. */
279     if ((str_len % 2) != 0)
280         return (FALSE);
281     *data_len = str_len / 2;
282     data = g_new0 (guchar, *data_len);
283 
284     for (j = 0, i = 0; i < str_len; i += 2, j++)
285     {
286         gchar tmpstr[3];
287         long int converted;
288 
289         tmpstr[0] = str[i];
290         tmpstr[1] = str[i + 1];
291         tmpstr[2] = '\0';
292 
293         converted = strtol (tmpstr, NULL, 16);
294 
295         data[j] = (unsigned char)converted;
296     }
297 
298     *v = data;
299 
300     return (TRUE);
301 }
302 
303 static KvpValue* dom_tree_to_kvp_value (xmlNodePtr node);
304 //needed for test access as well as internal use.
305 KvpFrame* dom_tree_to_kvp_frame (xmlNodePtr node);
306 
307 static KvpValue*
dom_tree_to_list_kvp_value(xmlNodePtr node)308 dom_tree_to_list_kvp_value (xmlNodePtr node)
309 {
310     GList* list = NULL;
311     xmlNodePtr mark;
312     KvpValue* ret = NULL;
313 
314     for (mark = node->xmlChildrenNode; mark; mark = mark->next)
315     {
316         KvpValue* new_val;
317 
318         if (g_strcmp0 ((char*)mark->name, "text") == 0)
319             continue;
320 
321         new_val = dom_tree_to_kvp_value (mark);
322         if (new_val)
323         {
324             list = g_list_prepend (list, (gpointer)new_val);
325         }
326     }
327 
328     list = g_list_reverse (list);
329 
330     ret = new KvpValue {list};
331 
332     return ret;
333 }
334 
335 static KvpValue*
dom_tree_to_frame_kvp_value(xmlNodePtr node)336 dom_tree_to_frame_kvp_value (xmlNodePtr node)
337 {
338     KvpFrame* frame;
339     KvpValue* ret = NULL;
340 
341     frame = dom_tree_to_kvp_frame (node);
342 
343     if (frame)
344     {
345         ret = new KvpValue {frame};
346     }
347 
348     return ret;
349 }
350 
351 
352 struct kvp_val_converter
353 {
354     const gchar* tag;
355     KvpValue* (*converter) (xmlNodePtr node);
356 };
357 /* Note: The type attribute must remain 'timespec' to maintain compatibility.
358  */
359 
360 struct kvp_val_converter val_converters[] =
361 {
362     { "integer", dom_tree_to_integer_kvp_value },
363     { "double", dom_tree_to_double_kvp_value },
364     { "numeric", dom_tree_to_numeric_kvp_value },
365     { "string", dom_tree_to_string_kvp_value },
366     { "guid", dom_tree_to_guid_kvp_value },
367     { "timespec", dom_tree_to_time64_kvp_value },
368     { "gdate", dom_tree_to_gdate_kvp_value },
369     { "list", dom_tree_to_list_kvp_value },
370     { "frame", dom_tree_to_frame_kvp_value },
371     { 0, 0 },
372 };
373 
374 static KvpValue*
dom_tree_to_kvp_value(xmlNodePtr node)375 dom_tree_to_kvp_value (xmlNodePtr node)
376 {
377     xmlChar* xml_type;
378     gchar* type;
379     struct kvp_val_converter* mark;
380     KvpValue* ret = NULL;
381 
382     xml_type = xmlGetProp (node, BAD_CAST "type");
383     if (xml_type)
384     {
385         type = g_strdup ((char*) xml_type);
386         xmlFree (xml_type);
387     }
388     else
389         type = NULL;
390 
391     for (mark = val_converters; mark->tag; mark++)
392     {
393         if (g_strcmp0 (type, mark->tag) == 0)
394         {
395             ret = (mark->converter) (node);
396         }
397     }
398 
399     if (!mark->tag)
400     {
401         /* FIXME: deal with unknown type tag here */
402     }
403 
404     g_free (type);
405 
406     return ret;
407 }
408 
409 static gboolean
dom_tree_to_kvp_frame_given(xmlNodePtr node,KvpFrame * frame)410 dom_tree_to_kvp_frame_given (xmlNodePtr node, KvpFrame* frame)
411 {
412     xmlNodePtr mark;
413 
414     g_return_val_if_fail (node, FALSE);
415     g_return_val_if_fail (frame, FALSE);
416 
417     for (mark = node->xmlChildrenNode; mark; mark = mark->next)
418     {
419         if (g_strcmp0 ((char*)mark->name, "slot") == 0)
420         {
421             xmlNodePtr mark2;
422             gchar* key = NULL;
423             KvpValue* val = NULL;
424 
425             for (mark2 = mark->xmlChildrenNode; mark2; mark2 = mark2->next)
426             {
427                 if (g_strcmp0 ((char*)mark2->name, "slot:key") == 0)
428                 {
429                     key = dom_tree_to_text (mark2);
430                 }
431                 else if (g_strcmp0 ((char*)mark2->name, "slot:value") == 0)
432                 {
433                     val = dom_tree_to_kvp_value (mark2);
434                 }
435                 else
436                 {
437                     /* FIXME: should put some error here.
438                      *        But ignore text type! */
439                 }
440             }
441 
442             if (key)
443             {
444                 if (val)
445                 {
446                     //We're deleting the old KvpValue returned by replace_nc().
447                     delete frame->set ({key}, val);
448                 }
449                 else
450                 {
451                     /* FIXME: should put some error here */
452                 }
453                 g_free (key);
454             }
455         }
456     }
457 
458     return TRUE;
459 }
460 
461 
462 KvpFrame*
dom_tree_to_kvp_frame(xmlNodePtr node)463 dom_tree_to_kvp_frame (xmlNodePtr node)
464 {
465     g_return_val_if_fail (node, NULL);
466 
467     auto ret = new KvpFrame;
468 
469     if (dom_tree_to_kvp_frame_given (node, ret))
470         return ret;
471 
472     delete ret;
473     return NULL;
474 }
475 
476 gboolean
dom_tree_create_instance_slots(xmlNodePtr node,QofInstance * inst)477 dom_tree_create_instance_slots (xmlNodePtr node, QofInstance* inst)
478 {
479     KvpFrame* frame = qof_instance_get_slots (inst);
480     return dom_tree_to_kvp_frame_given (node, frame);
481 }
482 
483 gchar*
dom_tree_to_text(xmlNodePtr tree)484 dom_tree_to_text (xmlNodePtr tree)
485 {
486     /* Expect *only* text and comment sibling nodes in the given tree --
487        which actually may only be a "list".  i.e. if you're trying to
488        extract bar from <foo>bar</foo>, pass in <foo>->xmlChildrenNode
489        to this function.  This expectation is different from the rest of
490        the dom_tree_to_* converters...
491 
492        Ignores comment nodes and collapse text nodes into one string.
493        Returns NULL if expectations are unsatisfied.
494     */
495     gchar* result;
496     gchar* temp;
497 
498     g_return_val_if_fail (tree, NULL);
499 
500     /* no nodes means it's an empty string text */
501     if (!tree->xmlChildrenNode)
502     {
503         DEBUG ("No children");
504         return g_strdup ("");
505     }
506 
507     temp = (char*)xmlNodeListGetString (NULL, tree->xmlChildrenNode, TRUE);
508     if (!temp)
509     {
510         DEBUG ("Null string");
511         return NULL;
512     }
513 
514     DEBUG ("node string [%s]", (temp == NULL ? "(null)" : temp));
515     result = g_strdup (temp);
516     xmlFree (temp);
517     return result;
518 }
519 
520 gnc_numeric*
dom_tree_to_gnc_numeric(xmlNodePtr node)521 dom_tree_to_gnc_numeric (xmlNodePtr node)
522 {
523     gchar* content = dom_tree_to_text (node);
524     if (!content)
525         return NULL;
526 
527     gnc_numeric *ret = g_new (gnc_numeric, 1);
528 
529     if (!string_to_gnc_numeric (content, ret))
530 	*ret = gnc_numeric_zero ();
531     g_free (content);
532     return ret;
533 }
534 
535 
536 time64
dom_tree_to_time64(xmlNodePtr node)537 dom_tree_to_time64 (xmlNodePtr node)
538 {
539     /* Turn something like this
540 
541        <date-posted>
542          <ts:date>Mon, 05 Jun 2000 23:16:19 -0500</ts:date>
543        </date-posted>
544 
545        into a time64, returning INT64_MAX that we're using to flag an erroneous
546        date if there's a problem. Only one ts:date element is permitted for any
547        date attribute.
548     */
549 
550     time64 ret {INT64_MAX};
551     gboolean seen = FALSE;
552     xmlNodePtr n;
553 
554     for (n = node->xmlChildrenNode; n; n = n->next)
555     {
556         switch (n->type)
557         {
558         case XML_COMMENT_NODE:
559         case XML_TEXT_NODE:
560             break;
561         case XML_ELEMENT_NODE:
562             if (g_strcmp0 ("ts:date", (char*)n->name) == 0)
563             {
564                 if (seen)
565                 {
566                     return INT64_MAX;
567                 }
568                 else
569                 {
570                     gchar* content = dom_tree_to_text (n);
571                     if (!content)
572                     {
573                         return INT64_MAX;
574                     }
575 
576                     ret = gnc_iso8601_to_time64_gmt (content);
577                     g_free (content);
578                     seen = TRUE;
579                 }
580             }
581             break;
582         default:
583             PERR ("unexpected sub-node.");
584             return INT64_MAX;
585             break;
586         }
587     }
588 
589     if (!seen)
590     {
591         PERR ("no ts:date node found.");
592         return INT64_MAX;
593     }
594 
595     return ret;
596 }
597 
598 GDate*
dom_tree_to_gdate(xmlNodePtr node)599 dom_tree_to_gdate (xmlNodePtr node)
600 {
601     /* Turn something like this
602 
603        <sx:startdate>
604            <gdate>2001-04-03</gdate>
605        </sx:startdate>
606 
607        into a GDate.  If the xml is invalid, returns NULL. */
608 
609     GDate* ret;
610     gboolean seen_date = FALSE;
611     xmlNodePtr n;
612 
613     /* creates an invalid date */
614     ret = g_date_new ();
615 
616     for (n = node->xmlChildrenNode; n; n = n->next)
617     {
618         switch (n->type)
619         {
620         case XML_COMMENT_NODE:
621         case XML_TEXT_NODE:
622             break;
623         case XML_ELEMENT_NODE:
624             if (g_strcmp0 ("gdate", (char*)n->name) == 0)
625             {
626                 if (seen_date)
627                 {
628                     goto failure;
629                 }
630                 else
631                 {
632                     gchar* content = dom_tree_to_text (n);
633                     gint year, month, day;
634                     if (!content)
635                     {
636                         goto failure;
637                     }
638 
639                     if (sscanf (content, "%d-%d-%d", &year, &month, &day) != 3)
640                     {
641                         g_free (content);
642                         goto failure;
643                     }
644                     g_free (content);
645                     seen_date = TRUE;
646                     g_date_set_dmy (ret, day, static_cast<GDateMonth> (month),
647                                     year);
648                     if (!g_date_valid (ret))
649                     {
650                         PWARN ("invalid date");
651                         goto failure;
652                     }
653                 }
654             }
655             break;
656         default:
657             PERR ("unexpected sub-node.");
658             goto failure;
659         }
660     }
661 
662     if (!seen_date)
663     {
664         PWARN ("no gdate node found.");
665         goto failure;
666     }
667 
668     return ret;
669 failure:
670     g_date_free (ret);
671     return NULL;
672 }
673 
674 
675 gnc_commodity*
dom_tree_to_commodity_ref_no_engine(xmlNodePtr node,QofBook * book)676 dom_tree_to_commodity_ref_no_engine (xmlNodePtr node, QofBook* book)
677 {
678     /* Turn something like this
679 
680        <currency>
681          <cmdty:space>NASDAQ</cmdty:space>
682          <cmdty:id>LNUX</cmdty:space>
683        </currency>
684 
685        into a gnc_commodity*, returning NULL on failure.  Both sub-nodes
686        are required, though for now, order is irrelevant. */
687 
688     gnc_commodity* c = NULL;
689     gchar* space_str = NULL;
690     gchar* id_str = NULL;
691     xmlNodePtr n;
692 
693     if (!node) return NULL;
694     if (!node->xmlChildrenNode) return NULL;
695 
696     for (n = node->xmlChildrenNode; n; n = n->next)
697     {
698         switch (n->type)
699         {
700         case XML_COMMENT_NODE:
701         case XML_TEXT_NODE:
702             break;
703         case XML_ELEMENT_NODE:
704             if (g_strcmp0 ("cmdty:space", (char*)n->name) == 0)
705             {
706                 if (space_str)
707                 {
708                     return NULL;
709                 }
710                 else
711                 {
712                     gchar* content = dom_tree_to_text (n);
713                     if (!content) return NULL;
714                     space_str = content;
715                 }
716             }
717             else if (g_strcmp0 ("cmdty:id", (char*)n->name) == 0)
718             {
719                 if (id_str)
720                 {
721                     return NULL;
722                 }
723                 else
724                 {
725                     gchar* content = dom_tree_to_text (n);
726                     if (!content) return NULL;
727                     id_str = content;
728                 }
729             }
730             break;
731         default:
732             PERR ("unexpected sub-node.");
733             return NULL;
734             break;
735         }
736     }
737     if (! (space_str && id_str))
738     {
739         c = NULL;
740     }
741     else
742     {
743         g_strstrip (space_str);
744         g_strstrip (id_str);
745         c = gnc_commodity_new (book, NULL, space_str, id_str, NULL, 0);
746     }
747 
748     g_free (space_str);
749     g_free (id_str);
750 
751     return c;
752 }
753 
754 gnc_commodity*
dom_tree_to_commodity_ref(xmlNodePtr node,QofBook * book)755 dom_tree_to_commodity_ref (xmlNodePtr node, QofBook* book)
756 {
757     gnc_commodity* daref;
758     gnc_commodity* ret;
759     gnc_commodity_table* table;
760 
761     daref = dom_tree_to_commodity_ref_no_engine (node, book);
762 
763     table = gnc_commodity_table_get_table (book);
764 
765     g_return_val_if_fail (table != NULL, NULL);
766 
767     ret =  gnc_commodity_table_lookup (table,
768                                        gnc_commodity_get_namespace (daref),
769                                        gnc_commodity_get_mnemonic (daref));
770 
771     gnc_commodity_destroy (daref);
772 
773     g_return_val_if_fail (ret != NULL, NULL);
774 
775     return ret;
776 }
777 
778 /***********************************************************************/
779 /* generic parser */
780 
781 static inline void
dom_tree_handlers_reset(struct dom_tree_handler * handlers)782 dom_tree_handlers_reset (struct dom_tree_handler* handlers)
783 {
784     for (; handlers->tag != NULL; handlers++)
785     {
786         handlers->gotten = 0;
787     }
788 }
789 
790 static inline gboolean
dom_tree_handlers_all_gotten_p(struct dom_tree_handler * handlers)791 dom_tree_handlers_all_gotten_p (struct dom_tree_handler* handlers)
792 {
793     gboolean ret = TRUE;
794     for (; handlers->tag != NULL; handlers++)
795     {
796         if (handlers->required && ! handlers->gotten)
797         {
798             PERR ("Not defined and it should be: %s",
799                   handlers->tag ? handlers->tag : "(null)");
800             ret = FALSE;
801         }
802     }
803     return ret;
804 }
805 
806 
807 static inline gboolean
gnc_xml_set_data(const gchar * tag,xmlNodePtr node,gpointer item,struct dom_tree_handler * handlers)808 gnc_xml_set_data (const gchar* tag, xmlNodePtr node, gpointer item,
809                   struct dom_tree_handler* handlers)
810 {
811     for (; handlers->tag != NULL; handlers++)
812     {
813         if (g_strcmp0 (tag, handlers->tag) == 0)
814         {
815             (handlers->handler) (node, item);
816             handlers->gotten = TRUE;
817             break;
818         }
819     }
820 
821     if (!handlers->tag)
822     {
823         PERR ("Unhandled tag: %s",
824               tag ? tag : "(null)");
825         return FALSE;
826     }
827 
828     return TRUE;
829 }
830 
831 gboolean
dom_tree_generic_parse(xmlNodePtr node,struct dom_tree_handler * handlers,gpointer data)832 dom_tree_generic_parse (xmlNodePtr node, struct dom_tree_handler* handlers,
833                         gpointer data)
834 {
835     xmlNodePtr achild;
836     gboolean successful = TRUE;
837 
838     dom_tree_handlers_reset (handlers);
839 
840     for (achild = node->xmlChildrenNode; achild; achild = achild->next)
841     {
842         /* ignore stray text nodes */
843         if (g_strcmp0 ((char*)achild->name, "text") == 0)
844             continue;
845 
846         if (!gnc_xml_set_data ((char*)achild->name, achild, data, handlers))
847         {
848             PERR ("gnc_xml_set_data failed");
849             successful = FALSE;
850             continue;
851         }
852     }
853 
854     if (!dom_tree_handlers_all_gotten_p (handlers))
855     {
856         PERR ("didn't find all of the expected tags in the input");
857         successful = FALSE;
858     }
859 
860     return successful;
861 }
862 
863 gboolean
dom_tree_valid_time64(time64 val,const xmlChar * name)864 dom_tree_valid_time64 (time64 val, const xmlChar * name)
865 {
866     if (val != INT64_MAX)
867         return TRUE;
868     g_warning ("Invalid timestamp in data file. Look for a '%s' entry "
869             "with a year outside of the valid range: 1400..10000", name);
870     return FALSE;
871 }
872