"Fossies" - the Fresh Open Source Software Archive

Member "gvm-libs-11.0.1/util/xmlutils.c" (12 May 2020, 54844 Bytes) of package /linux/misc/openvas/gvm-libs-11.0.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "xmlutils.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 11.0.0_vs_11.0.1.

    1 /* Copyright (C) 2009-2019 Greenbone Networks GmbH
    2  *
    3  * SPDX-License-Identifier: GPL-2.0-or-later
    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
    7  * as published by the Free Software Foundation; either version 2
    8  * of 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, write to the Free Software
   17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   18  */
   19 
   20 /**
   21  * @file
   22  * @brief Simple XML reader.
   23  *
   24  * This is a generic XML interface.  The key function is \ref read_entity.
   25  *
   26  * There are examples of using this interface in omp.c.
   27  */
   28 
   29 #include "xmlutils.h"
   30 
   31 #include <assert.h>      /* for assert */
   32 #include <errno.h>       /* for errno, EAGAIN, EINTR */
   33 #include <fcntl.h>       /* for fcntl, F_SETFL, O_NONBLOCK */
   34 #include <glib.h>        /* for g_free, GSList, g_markup_parse_context_free */
   35 #include <glib/gtypes.h> /* for GPOINTER_TO_INT, GINT_TO_POINTER, gsize */
   36 #include <libxml/parser.h>
   37 #include <libxml/tree.h>
   38 #include <string.h>      /* for strcmp, strerror, strlen */
   39 #include <time.h>        /* for time, time_t */
   40 #include <unistd.h>      /* for ssize_t */
   41 
   42 #undef G_LOG_DOMAIN
   43 /**
   44  * @brief GLib log domain.
   45  */
   46 #define G_LOG_DOMAIN "lib   xml"
   47 
   48 /**
   49  * @brief Size of the buffer for reading from the manager.
   50  */
   51 #define BUFFER_SIZE 1048576
   52 
   53 /**
   54  * @brief Create an entity.
   55  *
   56  * @param[in]  name  Name of the entity.  Copied, freed by free_entity.
   57  * @param[in]  text  Text of the entity.  Copied, freed by free_entity.
   58  *
   59  * @return A newly allocated entity.
   60  */
   61 entity_t
   62 make_entity (const char *name, const char *text)
   63 {
   64   entity_t entity;
   65   entity = g_malloc (sizeof (*entity));
   66   entity->name = g_strdup (name ? name : "");
   67   entity->text = g_strdup (text ? text : "");
   68   entity->entities = NULL;
   69   entity->attributes = NULL;
   70   return entity;
   71 }
   72 
   73 /**
   74  * @brief Return all the entities from an entities_t after the first.
   75  *
   76  * @param[in]  entities  The list of entities.
   77  *
   78  * @return All the entities that follow the first.
   79  */
   80 entities_t
   81 next_entities (entities_t entities)
   82 {
   83   if (entities)
   84     return (entities_t) entities->next;
   85   return NULL;
   86 }
   87 
   88 /**
   89  * @brief Return the first entity from an entities_t.
   90  *
   91  * @param[in]  entities  The list of entities.
   92  *
   93  * @return The first entity.
   94  */
   95 entity_t
   96 first_entity (entities_t entities)
   97 {
   98   if (entities)
   99     return (entity_t) entities->data;
  100   return NULL;
  101 }
  102 
  103 /**
  104  * @brief Add an XML entity to a tree of entities.
  105  *
  106  * @param[in]  entities  The tree of entities
  107  * @param[in]  name      Name of the entity.  Copied, copy is freed by
  108  *                       free_entity.
  109  * @param[in]  text      Text of the entity.  Copied, copy is freed by
  110  *                       free_entity.
  111  *
  112  * @return The new entity.
  113  */
  114 entity_t
  115 add_entity (entities_t *entities, const char *name, const char *text)
  116 {
  117   entity_t entity = make_entity (name, text);
  118   if (entities)
  119     *entities = g_slist_append (*entities, entity);
  120   return entity;
  121 }
  122 
  123 /**
  124  * @brief Free an entity, recursively.
  125  *
  126  * @param[in]  entity  The entity, can be NULL.
  127  */
  128 void
  129 free_entity (entity_t entity)
  130 {
  131   if (entity)
  132     {
  133       g_free (entity->name);
  134       g_free (entity->text);
  135       if (entity->attributes)
  136         g_hash_table_destroy (entity->attributes);
  137       if (entity->entities)
  138         {
  139           GSList *list = entity->entities;
  140           while (list)
  141             {
  142               free_entity (list->data);
  143               list = list->next;
  144             }
  145           g_slist_free (entity->entities);
  146         }
  147       g_free (entity);
  148     }
  149 }
  150 
  151 /**
  152  * @brief Get the text an entity.
  153  *
  154  * @param[in]  entity  Entity.
  155  *
  156  * @return Entity text, which is freed by free_entity.
  157  */
  158 char *
  159 entity_text (entity_t entity)
  160 {
  161   if (!entity)
  162     return NULL;
  163 
  164   return entity->text;
  165 }
  166 
  167 /**
  168  * @brief Get the name an entity.
  169  *
  170  * @param[in]  entity  Entity.
  171  *
  172  * @return Entity name, which is freed by free_entity.
  173  */
  174 char *
  175 entity_name (entity_t entity)
  176 {
  177   if (!entity)
  178     return NULL;
  179 
  180   return entity->name;
  181 }
  182 
  183 /**
  184  * @brief Compare a given name with the name of a given entity.
  185  *
  186  * @param[in]  entity  Entity.
  187  * @param[in]  name    Name.
  188  *
  189  * @return Zero if entity name matches name, otherwise a positive or negative
  190  *         number as from strcmp.
  191  */
  192 int
  193 compare_entity_with_name (gconstpointer entity, gconstpointer name)
  194 {
  195   return strcmp (entity_name ((entity_t) entity), (char *) name);
  196 }
  197 
  198 /**
  199  * @brief Get a child of an entity.
  200  *
  201  * @param[in]  entity  Entity.
  202  * @param[in]  name    Name of the child.
  203  *
  204  * @return Entity if found, else NULL.
  205  */
  206 entity_t
  207 entity_child (entity_t entity, const char *name)
  208 {
  209   if (!entity)
  210     return NULL;
  211 
  212   if (entity->entities)
  213     {
  214       entities_t match =
  215         g_slist_find_custom (entity->entities, name, compare_entity_with_name);
  216       return match ? (entity_t) match->data : NULL;
  217     }
  218   return NULL;
  219 }
  220 
  221 /**
  222  * @brief Get an attribute of an entity.
  223  *
  224  * @param[in]  entity  Entity.
  225  * @param[in]  name    Name of the attribute.
  226  *
  227  * @return Attribute if found, else NULL.
  228  */
  229 const char *
  230 entity_attribute (entity_t entity, const char *name)
  231 {
  232   if (!entity)
  233     return NULL;
  234 
  235   if (entity->attributes)
  236     return (const char *) g_hash_table_lookup (entity->attributes, name);
  237   return NULL;
  238 }
  239 
  240 /**
  241  * @brief Add attributes from an XML callback to an entity.
  242  *
  243  * @param[in]  entity  The entity.
  244  * @param[in]  names   List of attribute names.
  245  * @param[in]  values  List of attribute values.
  246  */
  247 void
  248 add_attributes (entity_t entity, const gchar **names, const gchar **values)
  249 {
  250   if (names && values && *names && *values)
  251     {
  252       if (entity->attributes == NULL)
  253         entity->attributes =
  254           g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  255       while (*names && *values)
  256         {
  257           if (*values)
  258             g_hash_table_insert (entity->attributes, g_strdup (*names),
  259                                  g_strdup (*values));
  260           names++;
  261           values++;
  262         }
  263     }
  264 }
  265 
  266 /**
  267  * @brief Handle the start of an OMP XML element.
  268  *
  269  * @param[in]  context           Parser context.
  270  * @param[in]  element_name      XML element name.
  271  * @param[in]  attribute_names   XML attribute name.
  272  * @param[in]  attribute_values  XML attribute values.
  273  * @param[in]  user_data         Dummy parameter.
  274  * @param[in]  error             Error parameter.
  275  */
  276 static void
  277 ignore_start_element (GMarkupParseContext *context, const gchar *element_name,
  278                       const gchar **attribute_names,
  279                       const gchar **attribute_values, gpointer user_data,
  280                       GError **error)
  281 {
  282   context_data_t *data = (context_data_t *) user_data;
  283 
  284   (void) context;
  285   (void) element_name;
  286   (void) attribute_names;
  287   (void) attribute_values;
  288   (void) error;
  289 
  290   data->current = GINT_TO_POINTER (GPOINTER_TO_INT (data->current) + 1);
  291 }
  292 
  293 /**
  294  * @brief Handle the start of an OMP XML element.
  295  *
  296  * @param[in]  context           Parser context.
  297  * @param[in]  element_name      XML element name.
  298  * @param[in]  attribute_names   XML attribute name.
  299  * @param[in]  attribute_values  XML attribute values.
  300  * @param[in]  user_data         Dummy parameter.
  301  * @param[in]  error             Error parameter.
  302  */
  303 static void
  304 handle_start_element (GMarkupParseContext *context, const gchar *element_name,
  305                       const gchar **attribute_names,
  306                       const gchar **attribute_values, gpointer user_data,
  307                       GError **error)
  308 {
  309   entity_t entity;
  310   context_data_t *data = (context_data_t *) user_data;
  311 
  312   (void) context;
  313   (void) error;
  314   if (data->current)
  315     {
  316       entity_t current = (entity_t) data->current->data;
  317       entity = add_entity (&current->entities, element_name, NULL);
  318     }
  319   else
  320     entity = add_entity (NULL, element_name, NULL);
  321 
  322   add_attributes (entity, attribute_names, attribute_values);
  323 
  324   /* "Push" the element. */
  325   if (data->first == NULL)
  326     data->current = data->first = g_slist_prepend (NULL, entity);
  327   else
  328     data->current = g_slist_prepend (data->current, entity);
  329 }
  330 
  331 /**
  332  * @brief Handle the start of an OMP XML element.
  333  *
  334  * @param[in]  context           Parser context.
  335  * @param[in]  element_name      XML element name.
  336  * @param[in]  attribute_names   XML attribute name.
  337  * @param[in]  attribute_values  XML attribute values.
  338  */
  339 void
  340 xml_handle_start_element (context_data_t *context, const gchar *element_name,
  341                           const gchar **attribute_names,
  342                           const gchar **attribute_values)
  343 {
  344   return handle_start_element (NULL, element_name, attribute_names,
  345                                attribute_values, context, NULL);
  346 }
  347 
  348 /**
  349  * @brief Handle the end of an XML element.
  350  *
  351  * @param[in]  context           Parser context.
  352  * @param[in]  element_name      XML element name.
  353  * @param[in]  user_data         Dummy parameter.
  354  * @param[in]  error             Error parameter.
  355  */
  356 static void
  357 ignore_end_element (GMarkupParseContext *context, const gchar *element_name,
  358                     gpointer user_data, GError **error)
  359 {
  360   context_data_t *data = (context_data_t *) user_data;
  361 
  362   (void) context;
  363   (void) element_name;
  364   (void) error;
  365 
  366   data->current = GINT_TO_POINTER (GPOINTER_TO_INT (data->current) - 1);
  367   if (data->current == NULL)
  368     data->done = TRUE;
  369 }
  370 
  371 /**
  372  * @brief Handle the end of an XML element.
  373  *
  374  * @param[in]  context           Parser context.
  375  * @param[in]  element_name      XML element name.
  376  * @param[in]  user_data         Dummy parameter.
  377  * @param[in]  error             Error parameter.
  378  */
  379 static void
  380 handle_end_element (GMarkupParseContext *context, const gchar *element_name,
  381                     gpointer user_data, GError **error)
  382 {
  383   context_data_t *data = (context_data_t *) user_data;
  384 
  385   (void) context;
  386   (void) error;
  387   (void) element_name;
  388   assert (data->current && data->first);
  389   if (data->current == data->first)
  390     {
  391       assert (strcmp (element_name,
  392                       /* The name of the very first entity. */
  393                       ((entity_t) (data->first->data))->name)
  394               == 0);
  395       data->done = TRUE;
  396       /* "Pop" the element. */
  397       data->current = g_slist_next (data->current);
  398     }
  399   else if (data->current)
  400     {
  401       GSList *front;
  402       /* "Pop" and free the element. */
  403       front = data->current;
  404       data->current = g_slist_next (data->current);
  405       g_slist_free_1 (front);
  406     }
  407 }
  408 
  409 /**
  410  * @brief Handle the end of an XML element.
  411  *
  412  * @param[in]  context           Parser context.
  413  * @param[in]  element_name      XML element name.
  414  */
  415 void
  416 xml_handle_end_element (context_data_t *context, const gchar *element_name)
  417 {
  418   handle_end_element (NULL, element_name, context, NULL);
  419 }
  420 
  421 /**
  422  * @brief Handle additional text of an XML element.
  423  *
  424  * @param[in]  context           Parser context.
  425  * @param[in]  text              The text.
  426  * @param[in]  text_len          Length of the text.
  427  * @param[in]  user_data         Dummy parameter.
  428  * @param[in]  error             Error parameter.
  429  */
  430 static void
  431 ignore_text (GMarkupParseContext *context, const gchar *text, gsize text_len,
  432              gpointer user_data, GError **error)
  433 {
  434   (void) context;
  435   (void) text;
  436   (void) text_len;
  437   (void) user_data;
  438   (void) error;
  439 }
  440 
  441 /**
  442  * @brief Handle additional text of an XML element.
  443  *
  444  * @param[in]  context           Parser context.
  445  * @param[in]  text              The text.
  446  * @param[in]  text_len          Length of the text.
  447  * @param[in]  user_data         Dummy parameter.
  448  * @param[in]  error             Error parameter.
  449  */
  450 static void
  451 handle_text (GMarkupParseContext *context, const gchar *text, gsize text_len,
  452              gpointer user_data, GError **error)
  453 {
  454   context_data_t *data = (context_data_t *) user_data;
  455 
  456   (void) context;
  457   (void) text_len;
  458   (void) error;
  459   entity_t current = (entity_t) data->current->data;
  460   if (current->text)
  461     {
  462       gchar *old = current->text;
  463       current->text = g_strconcat (current->text, text, NULL);
  464       g_free (old);
  465     }
  466   else
  467     current->text = g_strdup (text);
  468 }
  469 
  470 /**
  471  * @brief Handle additional text of an XML element.
  472  *
  473  * @param[in]  context           Parser context.
  474  * @param[in]  text              The text.
  475  * @param[in]  text_len          Length of the text.
  476  */
  477 void
  478 xml_handle_text (context_data_t *context, const gchar *text, gsize text_len)
  479 {
  480   handle_text (NULL, text, text_len, context, NULL);
  481 }
  482 
  483 /**
  484  * @brief Handle an OMP XML parsing error.
  485  *
  486  * @param[in]  context           Parser context.
  487  * @param[in]  error             The error.
  488  * @param[in]  user_data         Dummy parameter.
  489  */
  490 void
  491 handle_error (GMarkupParseContext *context, GError *error, gpointer user_data)
  492 {
  493   (void) context;
  494   (void) user_data;
  495   g_message ("   Error: %s\n", error->message);
  496 }
  497 
  498 /**
  499  * @brief Try read an XML entity tree from the manager.
  500  *
  501  * @param[in]   session        Pointer to GNUTLS session.
  502  * @param[in]   timeout        Server idle time before giving up, in seconds.  0
  503  * to wait forever.
  504  * @param[out]  entity         Pointer to an entity tree.
  505  * @param[out]  string_return  An optional return location for the text read
  506  *                             from the session.  If NULL then it simply
  507  *                             remains NULL.  If a pointer to NULL then it
  508  * points to a freshly allocated GString on successful return. Otherwise it
  509  * points to an existing GString onto which the text is appended.
  510  *
  511  * @return 0 success, -1 read error, -2 parse error, -3 end of file, -4 timeout.
  512  */
  513 int
  514 try_read_entity_and_string (gnutls_session_t *session, int timeout,
  515                             entity_t *entity, GString **string_return)
  516 {
  517   GMarkupParser xml_parser;
  518   GError *error = NULL;
  519   GMarkupParseContext *xml_context;
  520   GString *string;
  521   int socket;
  522   time_t last_time;
  523 
  524   // Buffer for reading from the manager.
  525   char *buffer;
  526 
  527   /* Record the start time. */
  528 
  529   if (time (&last_time) == -1)
  530     {
  531       g_warning ("   failed to get current time: %s\n", strerror (errno));
  532       return -1;
  533     }
  534 
  535   if (timeout > 0)
  536     {
  537       /* Turn off blocking. */
  538 
  539       socket = GPOINTER_TO_INT (gnutls_transport_get_ptr (*session));
  540       if (fcntl (socket, F_SETFL, O_NONBLOCK) == -1)
  541         return -1;
  542     }
  543   else
  544     /* Quiet compiler. */
  545     socket = 0;
  546 
  547   buffer = g_malloc0 (BUFFER_SIZE);
  548 
  549   /* Setup return arg. */
  550 
  551   if (string_return == NULL)
  552     string = NULL;
  553   else if (*string_return == NULL)
  554     string = g_string_new ("");
  555   else
  556     string = *string_return;
  557 
  558   /* Create the XML parser. */
  559 
  560   if (entity)
  561     {
  562       xml_parser.start_element = handle_start_element;
  563       xml_parser.end_element = handle_end_element;
  564       xml_parser.text = handle_text;
  565     }
  566   else
  567     {
  568       xml_parser.start_element = ignore_start_element;
  569       xml_parser.end_element = ignore_end_element;
  570       xml_parser.text = ignore_text;
  571     }
  572   xml_parser.passthrough = NULL;
  573   xml_parser.error = handle_error;
  574 
  575   context_data_t context_data;
  576   context_data.done = FALSE;
  577   context_data.first = NULL;
  578   context_data.current = NULL;
  579 
  580   /* Setup the XML context. */
  581 
  582   xml_context =
  583     g_markup_parse_context_new (&xml_parser, 0, &context_data, NULL);
  584 
  585   /* Read and parse, until encountering end of file or error. */
  586 
  587   while (1)
  588     {
  589       ssize_t count;
  590       while (1)
  591         {
  592           g_debug ("   asking for %i\n", BUFFER_SIZE);
  593           count = gnutls_record_recv (*session, buffer, BUFFER_SIZE);
  594           if (count < 0)
  595             {
  596               if (count == GNUTLS_E_INTERRUPTED)
  597                 /* Interrupted, try read again. */
  598                 continue;
  599               if ((timeout > 0) && (count == GNUTLS_E_AGAIN))
  600                 {
  601                   /* Server still busy, either timeout or try read again. */
  602                   if ((timeout - (time (NULL) - last_time)) <= 0)
  603                     {
  604                       g_warning ("   timeout\n");
  605                       if (fcntl (socket, F_SETFL, 0L) < 0)
  606                         g_warning ("%s :failed to set socket flag: %s",
  607                                    __FUNCTION__, strerror (errno));
  608                       g_markup_parse_context_free (xml_context);
  609                       g_free (buffer);
  610                       return -4;
  611                     }
  612                   continue;
  613                 }
  614               if (count == GNUTLS_E_REHANDSHAKE)
  615                 /* Try again. TODO Rehandshake. */
  616                 continue;
  617               if (context_data.first && context_data.first->data)
  618                 {
  619                   free_entity (context_data.first->data);
  620                   g_slist_free_1 (context_data.first);
  621                 }
  622               if (string && *string_return == NULL)
  623                 g_string_free (string, TRUE);
  624               if (timeout > 0)
  625                 {
  626                   if (fcntl (socket, F_SETFL, 0L) < 0)
  627                     g_warning ("%s :failed to set socket flag: %s",
  628                                __FUNCTION__, strerror (errno));
  629                 }
  630               g_markup_parse_context_free (xml_context);
  631               g_free (buffer);
  632               return -1;
  633             }
  634           if (count == 0)
  635             {
  636               /* End of file. */
  637               g_markup_parse_context_end_parse (xml_context, &error);
  638               if (error)
  639                 {
  640                   g_warning ("   End error: %s\n", error->message);
  641                   g_error_free (error);
  642                 }
  643               if (context_data.first && context_data.first->data)
  644                 {
  645                   free_entity (context_data.first->data);
  646                   g_slist_free_1 (context_data.first);
  647                 }
  648               if (string && *string_return == NULL)
  649                 g_string_free (string, TRUE);
  650               if (timeout > 0)
  651                 {
  652                   if (fcntl (socket, F_SETFL, 0L) < 0)
  653                     g_warning ("%s :failed to set socket flag: %s",
  654                                __FUNCTION__, strerror (errno));
  655                 }
  656               g_markup_parse_context_free (xml_context);
  657               g_free (buffer);
  658               return -3;
  659             }
  660           break;
  661         }
  662 
  663       g_debug ("<= %.*s\n", (int) count, buffer);
  664 
  665       if (string)
  666         g_string_append_len (string, buffer, count);
  667 
  668       g_markup_parse_context_parse (xml_context, buffer, count, &error);
  669       if (error)
  670         {
  671           g_error_free (error);
  672           if (context_data.first && context_data.first->data)
  673             {
  674               free_entity (context_data.first->data);
  675               g_slist_free_1 (context_data.first);
  676             }
  677           if (string && *string_return == NULL)
  678             g_string_free (string, TRUE);
  679           if (timeout > 0)
  680             {
  681               if (fcntl (socket, F_SETFL, 0L) < 0)
  682                 g_warning ("%s :failed to set socket flag: %s", __FUNCTION__,
  683                            strerror (errno));
  684             }
  685           g_markup_parse_context_free (xml_context);
  686           g_free (buffer);
  687           return -2;
  688         }
  689       if (context_data.done)
  690         {
  691           g_markup_parse_context_end_parse (xml_context, &error);
  692           if (error)
  693             {
  694               g_warning ("   End error: %s\n", error->message);
  695               g_error_free (error);
  696               if (context_data.first && context_data.first->data)
  697                 {
  698                   free_entity (context_data.first->data);
  699                   g_slist_free_1 (context_data.first);
  700                 }
  701               if (timeout > 0)
  702                 fcntl (socket, F_SETFL, 0L);
  703               g_markup_parse_context_free (xml_context);
  704               g_free (buffer);
  705               return -2;
  706             }
  707           if (entity)
  708             *entity = (entity_t) context_data.first->data;
  709           if (string)
  710             *string_return = string;
  711           if (timeout > 0)
  712             fcntl (socket, F_SETFL, 0L);
  713           g_markup_parse_context_free (xml_context);
  714           g_free (buffer);
  715           return 0;
  716         }
  717 
  718       if ((timeout > 0) && (time (&last_time) == -1))
  719         {
  720           g_warning ("   failed to get current time (1): %s\n",
  721                      strerror (errno));
  722           if (fcntl (socket, F_SETFL, 0L) < 0)
  723             g_warning ("%s :failed to set socket flag: %s", __FUNCTION__,
  724                        strerror (errno));
  725           g_markup_parse_context_free (xml_context);
  726           g_free (buffer);
  727           return -1;
  728         }
  729     }
  730 }
  731 
  732 /**
  733  * @brief Try read an XML entity tree from the socket.
  734  *
  735  * @param[in]   socket         Socket to read from.
  736  * @param[in]   timeout        Server idle time before giving up, in seconds.  0
  737  * to wait forever.
  738  * @param[out]  entity         Pointer to an entity tree.
  739  * @param[out]  string_return  An optional return location for the text read
  740  *                             from the session.  If NULL then it simply
  741  *                             remains NULL.  If a pointer to NULL then it
  742  * points to a freshly allocated GString on successful return. Otherwise it
  743  * points to an existing GString onto which the text is appended.
  744  *
  745  * @return 0 success, -1 read error, -2 parse error, -3 end of file, -4 timeout.
  746  */
  747 int
  748 try_read_entity_and_string_s (int socket, int timeout, entity_t *entity,
  749                               GString **string_return)
  750 {
  751   GMarkupParser xml_parser;
  752   GError *error = NULL;
  753   GMarkupParseContext *xml_context;
  754   GString *string;
  755   time_t last_time;
  756   /* Buffer for reading from the socket. */
  757   char *buffer;
  758 
  759   /* Record the start time. */
  760 
  761   if (time (&last_time) == -1)
  762     {
  763       g_warning ("   failed to get current time: %s\n", strerror (errno));
  764       return -1;
  765     }
  766 
  767   if (timeout > 0)
  768     {
  769       /* Turn off blocking. */
  770 
  771       if (fcntl (socket, F_SETFL, O_NONBLOCK) == -1)
  772         return -1;
  773     }
  774 
  775   buffer = g_malloc0 (BUFFER_SIZE);
  776 
  777   /* Setup return arg. */
  778 
  779   if (string_return == NULL)
  780     string = NULL;
  781   else if (*string_return == NULL)
  782     string = g_string_new ("");
  783   else
  784     string = *string_return;
  785 
  786   /* Create the XML parser. */
  787 
  788   if (entity)
  789     {
  790       xml_parser.start_element = handle_start_element;
  791       xml_parser.end_element = handle_end_element;
  792       xml_parser.text = handle_text;
  793     }
  794   else
  795     {
  796       xml_parser.start_element = ignore_start_element;
  797       xml_parser.end_element = ignore_end_element;
  798       xml_parser.text = ignore_text;
  799     }
  800   xml_parser.passthrough = NULL;
  801   xml_parser.error = handle_error;
  802 
  803   context_data_t context_data;
  804   context_data.done = FALSE;
  805   context_data.first = NULL;
  806   context_data.current = NULL;
  807 
  808   /* Setup the XML context. */
  809 
  810   xml_context =
  811     g_markup_parse_context_new (&xml_parser, 0, &context_data, NULL);
  812 
  813   /* Read and parse, until encountering end of file or error. */
  814 
  815   while (1)
  816     {
  817       int count;
  818       while (1)
  819         {
  820           g_debug ("   asking for %i\n", BUFFER_SIZE);
  821           count = read (socket, buffer, BUFFER_SIZE);
  822           if (count < 0)
  823             {
  824               if (errno == EINTR)
  825                 /* Interrupted, try read again. */
  826                 continue;
  827               if (timeout > 0)
  828                 {
  829                   if (errno == EAGAIN)
  830                     {
  831                       /* Server still busy, either timeout or try read again. */
  832                       if ((timeout - (time (NULL) - last_time)) <= 0)
  833                         {
  834                           g_warning ("   timeout\n");
  835                           if (fcntl (socket, F_SETFL, 0L) < 0)
  836                             g_warning ("%s :failed to set socket flag: %s",
  837                                        __FUNCTION__, strerror (errno));
  838                           g_markup_parse_context_free (xml_context);
  839                           g_free (buffer);
  840                           if (string && *string_return == NULL)
  841                             g_string_free (string, TRUE);
  842                           return -4;
  843                         }
  844                     }
  845                   continue;
  846                 }
  847               if (context_data.first && context_data.first->data)
  848                 {
  849                   free_entity (context_data.first->data);
  850                   g_slist_free_1 (context_data.first);
  851                 }
  852               if (string && *string_return == NULL)
  853                 g_string_free (string, TRUE);
  854               if (timeout > 0)
  855                 fcntl (socket, F_SETFL, 0L);
  856               g_markup_parse_context_free (xml_context);
  857               g_free (buffer);
  858               return -1;
  859             }
  860           if (count == 0)
  861             {
  862               /* End of file. */
  863               g_markup_parse_context_end_parse (xml_context, &error);
  864               if (error)
  865                 {
  866                   g_warning ("   End error: %s\n", error->message);
  867                   g_error_free (error);
  868                 }
  869               if (context_data.first && context_data.first->data)
  870                 {
  871                   free_entity (context_data.first->data);
  872                   g_slist_free_1 (context_data.first);
  873                 }
  874               if (string && *string_return == NULL)
  875                 g_string_free (string, TRUE);
  876               if (timeout > 0)
  877                 {
  878                   if (fcntl (socket, F_SETFL, 0L) < 0)
  879                     g_warning ("%s :failed to set socket flag: %s",
  880                                __FUNCTION__, strerror (errno));
  881                 }
  882               g_markup_parse_context_free (xml_context);
  883               g_free (buffer);
  884               return -3;
  885             }
  886           break;
  887         }
  888 
  889       g_debug ("<= %.*s\n", (int) count, buffer);
  890 
  891       if (string)
  892         g_string_append_len (string, buffer, count);
  893 
  894       g_markup_parse_context_parse (xml_context, buffer, count, &error);
  895       if (error)
  896         {
  897           g_error_free (error);
  898           // FIX there may be multiple entries in list
  899           if (context_data.first && context_data.first->data)
  900             {
  901               free_entity (context_data.first->data);
  902               g_slist_free_1 (context_data.first);
  903             }
  904           if (string && *string_return == NULL)
  905             g_string_free (string, TRUE);
  906           if (timeout > 0)
  907             {
  908               if (fcntl (socket, F_SETFL, 0L) < 0)
  909                 g_warning ("%s :failed to set socket flag: %s", __FUNCTION__,
  910                            strerror (errno));
  911             }
  912           g_markup_parse_context_free (xml_context);
  913           g_free (buffer);
  914           return -2;
  915         }
  916       if (context_data.done)
  917         {
  918           g_markup_parse_context_end_parse (xml_context, &error);
  919           if (error)
  920             {
  921               g_warning ("   End error: %s\n", error->message);
  922               g_error_free (error);
  923               if (context_data.first && context_data.first->data)
  924                 {
  925                   free_entity (context_data.first->data);
  926                   g_slist_free_1 (context_data.first);
  927                 }
  928               if (timeout > 0)
  929                 fcntl (socket, F_SETFL, 0L);
  930               g_markup_parse_context_free (xml_context);
  931               g_free (buffer);
  932               if (string && *string_return == NULL)
  933                 g_string_free (string, TRUE);
  934               return -2;
  935             }
  936           if (entity)
  937             *entity = (entity_t) context_data.first->data;
  938           if (string)
  939             *string_return = string;
  940           if (timeout > 0)
  941             fcntl (socket, F_SETFL, 0L);
  942           g_slist_free (context_data.first);
  943           g_markup_parse_context_free (xml_context);
  944           g_free (buffer);
  945           return 0;
  946         }
  947 
  948       if ((timeout > 0) && (time (&last_time) == -1))
  949         {
  950           g_warning ("   failed to get current time (1): %s\n",
  951                      strerror (errno));
  952           if (fcntl (socket, F_SETFL, 0L) < 0)
  953             g_warning ("%s :failed to set server socket flag: %s", __FUNCTION__,
  954                        strerror (errno));
  955           g_markup_parse_context_free (xml_context);
  956           g_free (buffer);
  957           if (string && *string_return == NULL)
  958             g_string_free (string, TRUE);
  959           return -1;
  960         }
  961     }
  962 }
  963 
  964 /**
  965  * @brief Try read an XML entity tree from the manager.
  966  *
  967  * @param[in]   session          Pointer to GNUTLS session.
  968  * @param[out]  entity           Pointer to an entity tree.
  969  * @param[out]  string_return    An optional return location for the text read
  970  *                               from the session.  If NULL then it simply
  971  *                               remains NULL.  If a pointer to NULL then it
  972  * points to a freshly allocated GString on successful return. Otherwise it
  973  * points to an existing GString onto which the text is appended.
  974  *
  975  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
  976  */
  977 int
  978 read_entity_and_string (gnutls_session_t *session, entity_t *entity,
  979                         GString **string_return)
  980 {
  981   return try_read_entity_and_string (session, 0, entity, string_return);
  982 }
  983 
  984 /**
  985  * @brief Try read an XML entity tree from the manager.
  986  *
  987  * @param[in]   connection       Connection.
  988  * @param[out]  entity           Pointer to an entity tree.
  989  * @param[out]  string_return    An optional return location for the text read
  990  *                               from the session.  If NULL then it simply
  991  *                               remains NULL.  If a pointer to NULL then it
  992  * points to a freshly allocated GString on successful return. Otherwise it
  993  * points to an existing GString onto which the text is appended.
  994  *
  995  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
  996  */
  997 int
  998 read_entity_and_string_c (gvm_connection_t *connection, entity_t *entity,
  999                           GString **string_return)
 1000 {
 1001   if (connection->tls)
 1002     return try_read_entity_and_string (&connection->session, 0, entity,
 1003                                        string_return);
 1004   return try_read_entity_and_string_s (connection->socket, 0, entity,
 1005                                        string_return);
 1006 }
 1007 
 1008 /**
 1009  * @brief Read an XML entity tree from the manager.
 1010  *
 1011  * @param[in]   session   Pointer to GNUTLS session.
 1012  * @param[out]  entity    Pointer to an entity tree.
 1013  * @param[out]  text      A pointer to a pointer, at which to store the
 1014  *                        address of a newly allocated string holding the
 1015  *                        text read from the session, if the text is required,
 1016  *                        else NULL.
 1017  *
 1018  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1019  */
 1020 int
 1021 read_entity_and_text (gnutls_session_t *session, entity_t *entity, char **text)
 1022 {
 1023   if (text)
 1024     {
 1025       GString *string = NULL;
 1026       int ret = read_entity_and_string (session, entity, &string);
 1027       if (ret)
 1028         {
 1029           if (string)
 1030             g_string_free (string, TRUE);
 1031           return ret;
 1032         }
 1033       *text = g_string_free (string, FALSE);
 1034       return 0;
 1035     }
 1036   return read_entity_and_string (session, entity, NULL);
 1037 }
 1038 
 1039 /**
 1040  * @brief Read an XML entity tree from the manager.
 1041  *
 1042  * @param[in]   connection  Connection.
 1043  * @param[out]  entity      Entity tree.
 1044  * @param[out]  text      A pointer to a pointer, at which to store the
 1045  *                        address of a newly allocated string holding the
 1046  *                        text read from the session, if the text is required,
 1047  *                        else NULL.
 1048  *
 1049  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1050  */
 1051 int
 1052 read_entity_and_text_c (gvm_connection_t *connection, entity_t *entity,
 1053                         char **text)
 1054 {
 1055   if (text)
 1056     {
 1057       GString *string = NULL;
 1058       int ret = read_entity_and_string_c (connection, entity, &string);
 1059       if (ret)
 1060         {
 1061           if (string)
 1062             g_string_free (string, TRUE);
 1063           return ret;
 1064         }
 1065       *text = g_string_free (string, FALSE);
 1066       return 0;
 1067     }
 1068   return read_entity_and_string_c (connection, entity, NULL);
 1069 }
 1070 
 1071 /**
 1072  * @brief Read entity and text. Free the entity immediately.
 1073  *
 1074  * @param[in]   session  Pointer to GNUTLS session to read from.
 1075  * @param[out]  string   Return location for the string.
 1076  *
 1077  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1078  */
 1079 int
 1080 read_string (gnutls_session_t *session, GString **string)
 1081 {
 1082   int ret = 0;
 1083   entity_t entity;
 1084 
 1085   if (!(ret = read_entity_and_string (session, &entity, string)))
 1086     free_entity (entity);
 1087 
 1088   return ret;
 1089 }
 1090 
 1091 /**
 1092  * @brief Read entity and text. Free the entity immediately.
 1093  *
 1094  * @param[in]   connection  Connection.
 1095  * @param[out]  string      Return location for the string.
 1096  *
 1097  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1098  */
 1099 int
 1100 read_string_c (gvm_connection_t *connection, GString **string)
 1101 {
 1102   return read_entity_and_string_c (connection, NULL, string);
 1103 }
 1104 
 1105 /**
 1106  * @brief Try read an XML entity tree from the manager.
 1107  *
 1108  * @param[in]   session   Pointer to GNUTLS session.
 1109  * @param[in]   timeout   Server idle time before giving up, in seconds.  0 to
 1110  *                        wait forever.
 1111  * @param[out]  entity    Pointer to an entity tree.
 1112  *
 1113  * @return 0 success, -1 read error, -2 parse error, -3 end of file, -4 timeout.
 1114  */
 1115 int
 1116 try_read_entity (gnutls_session_t *session, int timeout, entity_t *entity)
 1117 {
 1118   return try_read_entity_and_string (session, timeout, entity, NULL);
 1119 }
 1120 
 1121 /**
 1122  * @brief Try read an XML entity tree from the manager.
 1123  *
 1124  * @param[in]   connection  Connection.
 1125  * @param[in]   timeout     Server idle time before giving up, in seconds.  0 to
 1126  *                          wait forever.
 1127  * @param[out]  entity      Pointer to an entity tree.
 1128  *
 1129  * @return 0 success, -1 read error, -2 parse error, -3 end of file, -4 timeout.
 1130  */
 1131 int
 1132 try_read_entity_c (gvm_connection_t *connection, int timeout, entity_t *entity)
 1133 {
 1134   if (connection->tls)
 1135     return try_read_entity_and_string (&connection->session, 0, entity, NULL);
 1136   return try_read_entity_and_string_s (connection->socket, timeout, entity,
 1137                                        NULL);
 1138 }
 1139 
 1140 /**
 1141  * @brief Read an XML entity tree from the manager.
 1142  *
 1143  * @param[in]   session   Pointer to GNUTLS session.
 1144  * @param[out]  entity    Pointer to an entity tree.
 1145  *
 1146  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1147  */
 1148 int
 1149 read_entity (gnutls_session_t *session, entity_t *entity)
 1150 {
 1151   return try_read_entity (session, 0, entity);
 1152 }
 1153 
 1154 /**
 1155  * @brief Read an XML entity tree from the socket.
 1156  *
 1157  * @param[in]   socket    Socket to read from.
 1158  * @param[out]  entity    Pointer to an entity tree.
 1159  *
 1160  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1161  */
 1162 int
 1163 read_entity_s (int socket, entity_t *entity)
 1164 {
 1165   return try_read_entity_and_string_s (socket, 0, entity, NULL);
 1166 }
 1167 
 1168 /**
 1169  * @brief Read an XML entity tree from the manager.
 1170  *
 1171  * @param[in]   connection Connection.
 1172  * @param[out]  entity     Pointer to an entity tree.
 1173  *
 1174  * @return 0 success, -1 read error, -2 parse error, -3 end of file.
 1175  */
 1176 int
 1177 read_entity_c (gvm_connection_t *connection, entity_t *entity)
 1178 {
 1179   return try_read_entity_c (connection, 0, entity);
 1180 }
 1181 
 1182 /**
 1183  * @brief Read an XML entity tree from a string.
 1184  *
 1185  * @param[in]   string  Input string.
 1186  * @param[out]  entity  Pointer to an entity tree.
 1187  *
 1188  * @return 0 success, -1 read error, -2 parse error, -3 XML ended prematurely.
 1189  */
 1190 int
 1191 parse_entity (const char *string, entity_t *entity)
 1192 {
 1193   GMarkupParser xml_parser;
 1194   GError *error = NULL;
 1195   GMarkupParseContext *xml_context;
 1196   context_data_t context_data;
 1197 
 1198   /* Create the XML parser. */
 1199 
 1200   xml_parser.start_element = handle_start_element;
 1201   xml_parser.end_element = handle_end_element;
 1202   xml_parser.text = handle_text;
 1203   xml_parser.passthrough = NULL;
 1204   xml_parser.error = handle_error;
 1205 
 1206   context_data.done = FALSE;
 1207   context_data.first = NULL;
 1208   context_data.current = NULL;
 1209 
 1210   /* Setup the XML context. */
 1211 
 1212   xml_context =
 1213     g_markup_parse_context_new (&xml_parser, 0, &context_data, NULL);
 1214 
 1215   /* Parse the string. */
 1216 
 1217   g_markup_parse_context_parse (xml_context, string, strlen (string), &error);
 1218   if (error)
 1219     {
 1220       g_error_free (error);
 1221       if (context_data.first && context_data.first->data)
 1222         {
 1223           free_entity (context_data.first->data);
 1224           g_slist_free_1 (context_data.first);
 1225         }
 1226       return -2;
 1227     }
 1228   if (context_data.done)
 1229     {
 1230       g_markup_parse_context_end_parse (xml_context, &error);
 1231       if (error)
 1232         {
 1233           g_warning ("   End error: %s\n", error->message);
 1234           g_error_free (error);
 1235           if (context_data.first && context_data.first->data)
 1236             {
 1237               free_entity (context_data.first->data);
 1238               g_slist_free_1 (context_data.first);
 1239             }
 1240           return -2;
 1241         }
 1242       *entity = (entity_t) context_data.first->data;
 1243       g_slist_free_1 (context_data.first);
 1244       return 0;
 1245     }
 1246   if (context_data.first && context_data.first->data)
 1247     {
 1248       free_entity (context_data.first->data);
 1249       g_slist_free_1 (context_data.first);
 1250     }
 1251   return -3;
 1252 }
 1253 
 1254 /**
 1255  * @brief Print an XML entity for g_slist_foreach to a GString.
 1256  *
 1257  * @param[in]  entity  The entity, as a gpointer.
 1258  * @param[in]  string  The stream to which to print, as a gpointer.
 1259  */
 1260 static void
 1261 foreach_print_entity_to_string (gpointer entity, gpointer string)
 1262 {
 1263   print_entity_to_string ((entity_t) entity, (GString *) string);
 1264 }
 1265 
 1266 /**
 1267  * @brief Print an XML attribute for g_hash_table_foreach to a GString.
 1268  *
 1269  * @param[in]  name    The attribute name.
 1270  * @param[in]  value   The attribute value.
 1271  * @param[in]  string  The string to which to print.
 1272  */
 1273 static void
 1274 foreach_print_attribute_to_string (gpointer name, gpointer value,
 1275                                    gpointer string)
 1276 {
 1277   gchar *text_escaped;
 1278   text_escaped = g_markup_escape_text ((gchar *) value, -1);
 1279   g_string_append_printf ((GString *) string, " %s=\"%s\"", (char *) name,
 1280                           text_escaped);
 1281   g_free (text_escaped);
 1282 }
 1283 
 1284 /**
 1285  * @brief Print an XML entity tree to a GString, appending it if string is not
 1286  * @brief empty.
 1287  *
 1288  * @param[in]      entity  Entity tree to print to string.
 1289  * @param[in,out]  string  String to write to.
 1290  */
 1291 void
 1292 print_entity_to_string (entity_t entity, GString *string)
 1293 {
 1294   gchar *text_escaped = NULL;
 1295   g_string_append_printf (string, "<%s", entity->name);
 1296   if (entity->attributes && g_hash_table_size (entity->attributes))
 1297     g_hash_table_foreach (entity->attributes, foreach_print_attribute_to_string,
 1298                           string);
 1299   g_string_append_printf (string, ">");
 1300   text_escaped = g_markup_escape_text (entity->text, -1);
 1301   g_string_append_printf (string, "%s", text_escaped);
 1302   g_free (text_escaped);
 1303   g_slist_foreach (entity->entities, foreach_print_entity_to_string, string);
 1304   g_string_append_printf (string, "</%s>", entity->name);
 1305 }
 1306 
 1307 /**
 1308  * @brief Print an XML entity for g_slist_foreach.
 1309  *
 1310  * @param[in]  entity  The entity, as a gpointer.
 1311  * @param[in]  stream  The stream to which to print, as a gpointer.
 1312  */
 1313 static void
 1314 foreach_print_entity (gpointer entity, gpointer stream)
 1315 {
 1316   print_entity ((FILE *) stream, (entity_t) entity);
 1317 }
 1318 
 1319 /**
 1320  * @brief Print an XML attribute for g_hash_table_foreach.
 1321  *
 1322  * @param[in]  name    The attribute name.
 1323  * @param[in]  value   The attribute value.
 1324  * @param[in]  stream  The stream to which to print.
 1325  */
 1326 static void
 1327 foreach_print_attribute (gpointer name, gpointer value, gpointer stream)
 1328 {
 1329   fprintf ((FILE *) stream, " %s=\"%s\"", (char *) name, (char *) value);
 1330 }
 1331 
 1332 /**
 1333  * @brief Print an XML entity.
 1334  *
 1335  * @param[in]  entity  The entity.
 1336  * @param[in]  stream  The stream to which to print.
 1337  */
 1338 void
 1339 print_entity (FILE *stream, entity_t entity)
 1340 {
 1341   gchar *text_escaped = NULL;
 1342   fprintf (stream, "<%s", entity->name);
 1343   if (entity->attributes && g_hash_table_size (entity->attributes))
 1344     g_hash_table_foreach (entity->attributes, foreach_print_attribute, stream);
 1345   fprintf (stream, ">");
 1346   text_escaped = g_markup_escape_text (entity->text, -1);
 1347   fprintf (stream, "%s", text_escaped);
 1348   g_free (text_escaped);
 1349   g_slist_foreach (entity->entities, foreach_print_entity, stream);
 1350   fprintf (stream, "</%s>", entity->name);
 1351   fflush (stream);
 1352 }
 1353 
 1354 /* "Formatted" (indented) output of entity_t */
 1355 
 1356 /**
 1357  * @brief Print an XML attribute for g_hash_table_foreach to stdout.
 1358  *
 1359  * @param[in]  name    The attribute name.
 1360  * @param[in]  value   The attribute value.
 1361  * @param[in]  none    (ignored).
 1362  */
 1363 static void
 1364 foreach_print_attribute_format (gpointer name, gpointer value, gpointer none)
 1365 {
 1366   (void) none;
 1367   printf (" %s=\"%s\"", (char *) name, (char *) value);
 1368 }
 1369 
 1370 /**
 1371  * @brief Print an XML entity to stdout, recursively printing its children.
 1372  * @brief Does very basic indentation for pretty printing.
 1373  *
 1374  * This function is used as the (callback) GFunc in g_slist_foreach.
 1375  *
 1376  * @param[in]  entity  The entity.
 1377  * @param[in]  indent  Indentation level, indentation width is 2 spaces.
 1378  *                     Use GINT_TO_POINTER to convert a integer value when
 1379  *                     passing this parameter.
 1380  */
 1381 void
 1382 print_entity_format (entity_t entity, gpointer indent)
 1383 {
 1384   int i = 0;
 1385   int indentation = GPOINTER_TO_INT (indent);
 1386   gchar *text_escaped = NULL;
 1387 
 1388   for (i = 0; i < indentation; i++)
 1389     printf ("  ");
 1390 
 1391   printf ("<%s", entity->name);
 1392   if (entity->attributes && g_hash_table_size (entity->attributes))
 1393     g_hash_table_foreach (entity->attributes, foreach_print_attribute_format,
 1394                           indent);
 1395   printf (">");
 1396 
 1397   text_escaped = g_markup_escape_text (entity->text, -1);
 1398   printf ("%s", text_escaped);
 1399   g_free (text_escaped);
 1400 
 1401   if (entity->entities)
 1402     {
 1403       printf ("\n");
 1404       g_slist_foreach (entity->entities, (GFunc) print_entity_format,
 1405                        GINT_TO_POINTER (indentation + 1));
 1406       for (i = 0; i < indentation; i++)
 1407         printf ("  ");
 1408     }
 1409 
 1410   printf ("</%s>\n", entity->name);
 1411 }
 1412 
 1413 /**
 1414  * @brief Look for a key-value pair in a hash table.
 1415  *
 1416  * @param[in]  key          Key.
 1417  * @param[in]  value        Value.
 1418  * @param[in]  attributes2  The hash table.
 1419  *
 1420  * @return FALSE if found, TRUE otherwise.
 1421  */
 1422 gboolean
 1423 compare_find_attribute (gpointer key, gpointer value, gpointer attributes2)
 1424 {
 1425   gchar *value2 = g_hash_table_lookup (attributes2, key);
 1426   if (value2 && strcmp (value, value2) == 0)
 1427     return FALSE;
 1428   g_debug ("  compare failed attribute: %s\n", (char *) value);
 1429   return TRUE;
 1430 }
 1431 
 1432 /**
 1433  * @brief Compare two XML entity.
 1434  *
 1435  * @param[in]  entity1  First entity.
 1436  * @param[in]  entity2  First entity.
 1437  *
 1438  * @return 0 if equal, 1 otherwise.
 1439  */
 1440 int
 1441 compare_entities (entity_t entity1, entity_t entity2)
 1442 {
 1443   if (entity1 == NULL)
 1444     return entity2 == NULL ? 0 : 1;
 1445   if (entity2 == NULL)
 1446     return 1;
 1447 
 1448   if (strcmp (entity1->name, entity2->name))
 1449     {
 1450       g_debug ("  compare failed name: %s vs %s\n", entity1->name,
 1451                entity2->name);
 1452       return 1;
 1453     }
 1454   if (strcmp (entity1->text, entity2->text))
 1455     {
 1456       g_debug ("  compare failed text %s vs %s (%s)\n", entity1->text,
 1457                entity2->text, entity1->name);
 1458       return 1;
 1459     }
 1460 
 1461   if (entity1->attributes == NULL)
 1462     {
 1463       if (entity2->attributes)
 1464         return 1;
 1465     }
 1466   else
 1467     {
 1468       if (entity2->attributes == NULL)
 1469         return 1;
 1470       if (g_hash_table_find (entity1->attributes, compare_find_attribute,
 1471                              (gpointer) entity2->attributes))
 1472         {
 1473           g_debug ("  compare failed attributes\n");
 1474           return 1;
 1475         }
 1476     }
 1477 
 1478   // FIX entities can be in any order
 1479   GSList *list1 = entity1->entities;
 1480   GSList *list2 = entity2->entities;
 1481   while (list1 && list2)
 1482     {
 1483       if (compare_entities (list1->data, list2->data))
 1484         {
 1485           g_debug ("  compare failed subentity\n");
 1486           return 1;
 1487         }
 1488       list1 = g_slist_next (list1);
 1489       list2 = g_slist_next (list2);
 1490     }
 1491   if (list1 == list2)
 1492     return 0;
 1493   /* More entities in one of the two. */
 1494   g_debug ("  compare failed number of entities (%s)\n", entity1->name);
 1495   return 1;
 1496 }
 1497 
 1498 /**
 1499  * @brief Count the number of entities.
 1500  *
 1501  * @param[in]  entities  Entities.
 1502  *
 1503  * @return Number of entities.
 1504  */
 1505 int
 1506 xml_count_entities (entities_t entities)
 1507 {
 1508   int count = 0;
 1509   while (first_entity (entities))
 1510     {
 1511       entities = next_entities (entities);
 1512       count++;
 1513     }
 1514   return count;
 1515 }
 1516 
 1517 /**
 1518  * @brief Append formatted escaped XML to a string.
 1519  *
 1520  * @param[in]  xml     XML string.
 1521  * @param[in]  format  Format string.
 1522  * @param[in]  ...     Arguments for format string.
 1523  *
 1524  * @return Result of XSL transformation.
 1525  */
 1526 void
 1527 xml_string_append (GString *xml, const char *format, ...)
 1528 {
 1529   gchar *piece;
 1530   va_list args;
 1531 
 1532   va_start (args, format);
 1533   piece = g_markup_vprintf_escaped (format, args);
 1534   va_end (args);
 1535   g_string_append (xml, piece);
 1536   g_free (piece);
 1537 }
 1538 
 1539 /* XML file utilities */
 1540 
 1541 /**
 1542  * @brief Handle the opening tag of an element in an XML search.
 1543  *
 1544  * @param[in]   ctx               The parse context.
 1545  * @param[in]   element_name      The name of the element.
 1546  * @param[in]   attribute_names   NULL-terminated array of attribute names.
 1547  * @param[in]   attribute_values  NULL-terminated array of attribute values.
 1548  * @param[in]   data              The search data struct.
 1549  * @param[out]  error             Pointer to error output location.
 1550  */
 1551 static void
 1552 xml_search_handle_start_element (GMarkupParseContext *ctx,
 1553                                  const gchar *element_name,
 1554                                  const gchar **attribute_names,
 1555                                  const gchar **attribute_values, gpointer data,
 1556                                  GError **error)
 1557 {
 1558   (void) ctx;
 1559   (void) error;
 1560 
 1561   xml_search_data_t *search_data = ((xml_search_data_t *) data);
 1562 
 1563   if (strcmp (element_name, search_data->find_element) == 0
 1564       && search_data->found == 0)
 1565     {
 1566       g_debug ("%s: Found element <%s>", __FUNCTION__, element_name);
 1567 
 1568       if (search_data->find_attributes
 1569           && g_hash_table_size (search_data->find_attributes))
 1570         {
 1571           int index;
 1572           GHashTable *found_attributes;
 1573           found_attributes =
 1574             g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
 1575           index = 0;
 1576           while (attribute_names[index])
 1577             {
 1578               gchar *searched_value;
 1579               searched_value = g_hash_table_lookup (
 1580                 search_data->find_attributes, attribute_names[index]);
 1581               if (searched_value
 1582                   && strcmp (searched_value, attribute_values[index]) == 0)
 1583                 {
 1584                   g_debug ("%s: Found attribute %s=\"%s\"", __FUNCTION__,
 1585                            attribute_names[index], searched_value);
 1586                   g_hash_table_add (found_attributes, searched_value);
 1587                 }
 1588               index++;
 1589             }
 1590           g_debug ("%s: Found %d of %d attributes", __FUNCTION__,
 1591                    g_hash_table_size (found_attributes),
 1592                    g_hash_table_size (search_data->find_attributes));
 1593 
 1594           if (g_hash_table_size (found_attributes)
 1595               == g_hash_table_size (search_data->find_attributes))
 1596             {
 1597               search_data->found = 1;
 1598             }
 1599 
 1600           g_hash_table_destroy (found_attributes);
 1601         }
 1602       else
 1603         {
 1604           search_data->found = 1;
 1605         }
 1606     }
 1607 }
 1608 
 1609 #define XML_FILE_BUFFER_SIZE 1048576
 1610 int
 1611 /**
 1612  * @brief Tests if an XML file contains an element with given attributes.
 1613  *
 1614  * @param[in]   file_path         Path of the XML file.
 1615  * @param[in]   find_element      Name of the element to find.
 1616  * @param[in]   find_attributes   GHashTable of attributes to find.
 1617  *
 1618  * @return  1 if element was found, 0 if not.
 1619  */
 1620 find_element_in_xml_file (gchar *file_path, gchar *find_element,
 1621                           GHashTable *find_attributes)
 1622 {
 1623   gchar buffer[XML_FILE_BUFFER_SIZE];
 1624   FILE *file;
 1625   int read_len;
 1626   GMarkupParser xml_parser;
 1627   GMarkupParseContext *xml_context;
 1628   xml_search_data_t search_data;
 1629   GError *error = NULL;
 1630 
 1631   search_data.find_element = find_element;
 1632   search_data.find_attributes = find_attributes;
 1633   search_data.found = 0;
 1634 
 1635   /* Create the XML parser. */
 1636   xml_parser.start_element = xml_search_handle_start_element;
 1637   xml_parser.end_element = NULL;
 1638   xml_parser.text = NULL;
 1639   xml_parser.passthrough = NULL;
 1640   xml_parser.error = NULL;
 1641   xml_context = g_markup_parse_context_new (&xml_parser, 0, &search_data, NULL);
 1642 
 1643   file = fopen (file_path, "r");
 1644   if (file == NULL)
 1645     {
 1646       g_markup_parse_context_free (xml_context);
 1647       g_warning ("%s: Failed to open '%s':", __FUNCTION__, strerror (errno));
 1648       return 0;
 1649     }
 1650 
 1651   while ((read_len = fread (&buffer, sizeof (char), XML_FILE_BUFFER_SIZE, file))
 1652          && g_markup_parse_context_parse (xml_context, buffer, read_len, &error)
 1653          && error == NULL)
 1654     {
 1655     }
 1656   g_markup_parse_context_end_parse (xml_context, &error);
 1657 
 1658   fclose (file);
 1659 
 1660   g_markup_parse_context_free (xml_context);
 1661   return search_data.found;
 1662 }
 1663 #undef XML_FILE_BUFFER_SIZE
 1664 
 1665 
 1666 /* The new faster parser that uses libxml2. */
 1667 
 1668 /**
 1669  * @brief Read an XML element tree from a string.
 1670  *
 1671  * Caller must not free string until caller is finished using element.
 1672  *
 1673  * @param[in]   string   Input string.
 1674  * @param[out]  element  Location for parsed element tree, or NULL if not
 1675  *                       required.   If given, set to NULL on failure.
 1676  *                       Free with element_free.
 1677  *
 1678  * @return 0 success, -1 read error, -2 parse error, -3 XML ended prematurely,
 1679  *         -4 setup error.
 1680  */
 1681 int
 1682 parse_element (const gchar *string, element_t *element)
 1683 {
 1684   xmlDocPtr doc;
 1685 
 1686   LIBXML_TEST_VERSION
 1687 
 1688   if (element)
 1689     *element = NULL;
 1690 
 1691   if (xmlMemSetup (g_free, g_malloc, g_realloc, g_strdup))
 1692     return -4;
 1693 
 1694   doc = xmlReadMemory (string, strlen (string), "noname.xml", NULL, 0);
 1695   if (doc == NULL)
 1696     return -2;
 1697 
 1698   if (element)
 1699     *element = xmlDocGetRootElement (doc);
 1700 
 1701   return 0;
 1702 }
 1703 
 1704 /**
 1705  * @brief Free an entire element tree.
 1706  *
 1707  * Beware that this frees the entire tree that element is part of, including
 1708  * any ancestors.
 1709  *
 1710  * @param[in]  element  Element.
 1711  */
 1712 void
 1713 element_free (element_t element)
 1714 {
 1715   if (element)
 1716     {
 1717       assert (element->doc);
 1718       xmlFreeDoc (element->doc);
 1719     }
 1720 }
 1721 
 1722 /**
 1723  * @brief Get the name of an element.
 1724  *
 1725  * @param[in]  element  Element.
 1726  *
 1727  * @return Element name.
 1728  */
 1729 const gchar *
 1730 element_name (element_t element)
 1731 {
 1732   if (element
 1733       && (element->type == XML_ELEMENT_NODE))
 1734     return (const gchar *) element->name;
 1735 
 1736   return "";
 1737 }
 1738 
 1739 /**
 1740  * @brief Find child in an element.
 1741  *
 1742  * @param[in]  element  Element.
 1743  * @param[in]  name     Name of child.
 1744  *
 1745  * @return Child if found, else NULL.
 1746  */
 1747 static element_t
 1748 find_child (element_t element, const gchar *name)
 1749 {
 1750   for (xmlNode *node = element->children; node; node = node->next)
 1751     if (xmlStrcmp (node->name, (const xmlChar *) name) == 0)
 1752       return node;
 1753   return NULL;
 1754 }
 1755 
 1756 /**
 1757  * @brief Get a child of an element.
 1758  *
 1759  * @param[in]  element  Element.
 1760  * @param[in]  name    Name of the child.
 1761  *
 1762  * @return Element if found, else NULL.
 1763  */
 1764 element_t
 1765 element_child (element_t element, const gchar *name)
 1766 {
 1767   const gchar *stripped_name;
 1768 
 1769   if (!element)
 1770     return NULL;
 1771 
 1772   stripped_name = strchr (name, ':');
 1773   if (stripped_name)
 1774     {
 1775        element_t child;
 1776 
 1777        /* There was a namespace in the name.
 1778         *
 1779         * First try without the namespace, because libxml2 doesn't consider the
 1780         * namespace in the name when the namespace is defined. */
 1781 
 1782       stripped_name++;
 1783 
 1784       if (*stripped_name == '\0')
 1785         /* Don't search for child with empty stripped name, because we'll
 1786          * find text nodes.  But search with just the namespace for glib
 1787          * compatibility. */
 1788         return find_child (element, name);
 1789 
 1790       child = find_child (element, stripped_name);
 1791       if (child)
 1792        return child;
 1793 
 1794       /* Didn't find anything. */
 1795     }
 1796 
 1797   /* There was no namespace, or we didn't find anything without the namespace.
 1798    *
 1799    * Try with the full name. */
 1800 
 1801   return find_child (element, name);
 1802 }
 1803 
 1804 /**
 1805  * @brief Get text of an element.
 1806  *
 1807  * If element is not NULL then the return is guaranteed to be a string.
 1808  * So if the caller has NULL checked element then there is no need for
 1809  * the caller to NULL check the return.
 1810  *
 1811  * @param[in]  element  Element.
 1812  *
 1813  * @return NULL if element is NULL, else the text.  Caller must g_free.
 1814  */
 1815 gchar *
 1816 element_text (element_t element)
 1817 {
 1818   gchar *string;
 1819 
 1820   if (!element)
 1821     return NULL;
 1822 
 1823   string = (gchar *) xmlNodeListGetString (element->doc, element->xmlChildrenNode, 1);
 1824   if (string)
 1825     return string;
 1826   string = xmlMalloc (1);
 1827   string[0] = '\0';
 1828   return string;
 1829 }
 1830 
 1831 /**
 1832  * @brief Get an attribute of an element.
 1833  *
 1834  * @param[in]  element  Element.
 1835  * @param[in]  name     Name of the attribute.
 1836  *
 1837  * @return Attribute value if found, else NULL.  Caller must g_free.
 1838  */
 1839 gchar *
 1840 element_attribute (element_t element, const gchar *name)
 1841 {
 1842   const gchar *stripped_name;
 1843 
 1844   if (!element)
 1845     return NULL;
 1846 
 1847   stripped_name = strchr (name, ':');
 1848   if (stripped_name)
 1849     {
 1850        gchar *attribute;
 1851 
 1852        /* There was a namespace in the name.
 1853         *
 1854         * First try without the namespace, because libxml2 doesn't consider the
 1855         * namespace in the name when the namespace is defined. */
 1856 
 1857       stripped_name++;
 1858 
 1859       if (*stripped_name == '\0')
 1860         /* Don't search for child with empty stripped name, because we'll
 1861          * find text nodes.  But search with just the namespace for glib
 1862          * compatibility. */
 1863         return (gchar *) xmlGetProp (element, (const xmlChar *) name);
 1864 
 1865       attribute = (gchar *) xmlGetProp (element, (const xmlChar *) stripped_name);
 1866       if (attribute)
 1867        return attribute;
 1868 
 1869       /* Didn't find anything. */
 1870     }
 1871 
 1872   /* There was no namespace, or we didn't find anything without the namespace.
 1873    *
 1874    * Try with the full name. */
 1875 
 1876   return (gchar *) xmlGetProp (element, (const xmlChar *) name);
 1877 }
 1878 
 1879 /**
 1880  * @brief Get the first child of an element.
 1881  *
 1882  * @param[in]  element  Element.
 1883  *
 1884  * @return Child if there is one, else NULL.
 1885  */
 1886 element_t
 1887 element_first_child (element_t element)
 1888 {
 1889   if (element)
 1890     return element->children;
 1891   return NULL;
 1892 }
 1893 
 1894 /**
 1895  * @brief Get the next sibling of an element
 1896  *
 1897  * @param[in]  element  Element.
 1898  *
 1899  * @return Next sibling element if there is one, else NULL.
 1900  */
 1901 element_t
 1902 element_next (element_t element)
 1903 {
 1904   if (element)
 1905     return element->next;
 1906   return NULL;
 1907 }