LCOV - code coverage report
Current view: top level - src - parser_expat.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 89.0 % 172 153
Test Date: 2024-07-22 12:36:40 Functions: 86.7 % 15 13

            Line data    Source code
       1              : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
       2              : /* parser.c
       3              : ** strophe XMPP client library -- xml parser handlers and utility functions
       4              : **
       5              : ** Copyright (C) 2005-2009 Collecta, Inc.
       6              : **
       7              : **  This software is provided AS-IS with no warranty, either express
       8              : **  or implied.
       9              : **
      10              : **  This program is dual licensed under the MIT or GPLv3 licenses.
      11              : */
      12              : 
      13              : /** @file
      14              :  *  XML parser handlers.
      15              :  */
      16              : 
      17              : #include <stdio.h>
      18              : #include <stdlib.h>
      19              : #include <string.h>
      20              : 
      21              : #include <expat.h>
      22              : 
      23              : #include "strophe.h"
      24              : #include "common.h"
      25              : #include "parser.h"
      26              : 
      27              : /* Allocate inner text by this number bytes more. Expat splits string
      28              :  * "new\nline" into 3 strings: "new" "\n" "line". Expecting this pattern,
      29              :  * we can leave few bytes in the inner_text for "\n". It should reduce
      30              :  * number of re-allocations in 2 times for multi-line texts. */
      31              : #define INNER_TEXT_PADDING 2
      32              : 
      33              : struct _parser_t {
      34              :     xmpp_ctx_t *ctx;
      35              :     XML_Parser expat;
      36              :     parser_start_callback startcb;
      37              :     parser_end_callback endcb;
      38              :     parser_stanza_callback stanzacb;
      39              :     void *userdata;
      40              :     int depth;
      41              :     xmpp_stanza_t *stanza;
      42              :     char *inner_text;
      43              :     /* number of allocated bytes */
      44              :     int inner_text_size;
      45              :     /* excluding terminal '\0' */
      46              :     int inner_text_used;
      47              : };
      48              : 
      49              : /* Use the Unit Separator to delimit namespace and name in our XML */
      50              : const XML_Char namespace_sep = '\x1F';
      51              : 
      52              : /*
      53              :  * Cached strophe ctx. It is used for memory suite.
      54              :  * Note, expat doesn't support userdata in memory suite, therefore,
      55              :  * we can support only one strophe context. If user creates more than one
      56              :  * context, this module will fallback to default library allocator for all
      57              :  * contexts other than mem_ctx.
      58              :  */
      59              : static xmpp_ctx_t *mem_ctx = NULL;
      60              : 
      61          193 : static void *parser_mem_malloc(size_t size)
      62              : {
      63          193 :     if (mem_ctx != NULL)
      64          193 :         return strophe_alloc(mem_ctx, size);
      65              :     else
      66              :         return NULL;
      67              : }
      68              : 
      69            0 : static void *parser_mem_realloc(void *ptr, size_t size)
      70              : {
      71            0 :     if (mem_ctx != NULL)
      72            0 :         return strophe_realloc(mem_ctx, ptr, size);
      73              :     else
      74              :         return NULL;
      75              : }
      76              : 
      77         1178 : static void parser_mem_free(void *ptr)
      78              : {
      79         1178 :     if (mem_ctx != NULL)
      80         1178 :         strophe_free(mem_ctx, ptr);
      81         1178 : }
      82              : 
      83              : static const XML_Memory_Handling_Suite parser_mem_suite = {
      84              :     .malloc_fcn = &parser_mem_malloc,
      85              :     .realloc_fcn = &parser_mem_realloc,
      86              :     .free_fcn = &parser_mem_free,
      87              : };
      88              : 
      89              : /* return allocated string with the name from a delimited
      90              :  * namespace/name string */
      91           48 : static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname)
      92              : {
      93           48 :     char *result = NULL;
      94           48 :     const char *c;
      95           48 :     size_t len;
      96              : 
      97           48 :     c = strchr(nsname, namespace_sep);
      98           48 :     if (c == NULL)
      99           23 :         return strophe_strdup(ctx, nsname);
     100              : 
     101           25 :     c++;
     102           25 :     len = strlen(c);
     103           25 :     result = strophe_alloc(ctx, len + 1);
     104           25 :     if (result != NULL) {
     105           25 :         memcpy(result, c, len);
     106           25 :         result[len] = '\0';
     107              :     }
     108              : 
     109              :     return result;
     110              : }
     111              : 
     112              : /* return allocated string with the namespace from a delimited string */
     113           34 : static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname)
     114              : {
     115           34 :     char *result = NULL;
     116           34 :     const char *c;
     117              : 
     118           34 :     c = strchr(nsname, namespace_sep);
     119           34 :     if (c != NULL) {
     120           25 :         result = strophe_alloc(ctx, (c - nsname) + 1);
     121           25 :         if (result != NULL) {
     122           25 :             memcpy(result, nsname, (c - nsname));
     123           25 :             result[c - nsname] = '\0';
     124              :         }
     125              :     }
     126              : 
     127           34 :     return result;
     128              : }
     129              : 
     130           29 : static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
     131              : {
     132           29 :     char *attr;
     133           29 :     int i;
     134              : 
     135           29 :     if (!attrs)
     136              :         return;
     137              : 
     138           43 :     for (i = 0; attrs[i]; i += 2) {
     139              :         /* namespaced attributes aren't used in xmpp, discard namespace */
     140           14 :         attr = _xml_name(stanza->ctx, attrs[i]);
     141           14 :         xmpp_stanza_set_attribute(stanza, attr, attrs[i + 1]);
     142           14 :         strophe_free(stanza->ctx, attr);
     143              :     }
     144              : }
     145              : 
     146           50 : static void complete_inner_text(parser_t *parser)
     147              : {
     148           50 :     xmpp_stanza_t *stanza;
     149              : 
     150           50 :     if (parser->inner_text) {
     151              :         /* create and populate stanza */
     152            4 :         stanza = xmpp_stanza_new(parser->ctx);
     153              :         /* FIXME: disconnect on allocation error */
     154            4 :         if (stanza) {
     155            4 :             xmpp_stanza_set_text(stanza, parser->inner_text);
     156            4 :             xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
     157              :         }
     158            4 :         strophe_free(parser->ctx, parser->inner_text);
     159            4 :         parser->inner_text = NULL;
     160            4 :         parser->inner_text_size = 0;
     161            4 :         parser->inner_text_used = 0;
     162              :     }
     163           50 : }
     164              : 
     165              : static void
     166           34 : _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
     167              : {
     168           34 :     parser_t *parser = (parser_t *)userdata;
     169           34 :     xmpp_stanza_t *child;
     170           34 :     char *ns, *name;
     171              : 
     172           34 :     ns = _xml_namespace(parser->ctx, nsname);
     173           34 :     name = _xml_name(parser->ctx, nsname);
     174              : 
     175           34 :     if (parser->depth == 0) {
     176              :         /* notify the owner */
     177            5 :         if (parser->startcb)
     178            5 :             parser->startcb(name, (char **)attrs, parser->userdata);
     179              :     } else {
     180              :         /* build stanzas at depth 1 */
     181           29 :         if (!parser->stanza && parser->depth != 1) {
     182              :             /* something terrible happened */
     183              :             /* FIXME: shutdown disconnect */
     184            0 :             strophe_error(parser->ctx, "parser",
     185              :                           "oops, where did our stanza go?");
     186              :         } else {
     187           29 :             child = xmpp_stanza_new(parser->ctx);
     188           29 :             if (!child) {
     189              :                 /* FIXME: can't allocate, disconnect */
     190           29 :             }
     191           29 :             xmpp_stanza_set_name(child, name);
     192           29 :             _set_attributes(child, attrs);
     193           29 :             if (ns)
     194           25 :                 xmpp_stanza_set_ns(child, ns);
     195              : 
     196           29 :             if (parser->stanza != NULL) {
     197           23 :                 complete_inner_text(parser);
     198           23 :                 xmpp_stanza_add_child_ex(parser->stanza, child, 0);
     199              :             }
     200           29 :             parser->stanza = child;
     201              :         }
     202              :     }
     203              : 
     204           34 :     if (ns)
     205           25 :         strophe_free(parser->ctx, ns);
     206           34 :     if (name)
     207           34 :         strophe_free(parser->ctx, name);
     208              : 
     209           34 :     parser->depth++;
     210           34 : }
     211              : 
     212           31 : static void _end_element(void *userdata, const XML_Char *name)
     213              : {
     214           31 :     parser_t *parser = (parser_t *)userdata;
     215              : 
     216           31 :     parser->depth--;
     217              : 
     218           31 :     if (parser->depth == 0) {
     219              :         /* notify the owner */
     220            4 :         if (parser->endcb)
     221            4 :             parser->endcb((char *)name, parser->userdata);
     222              :     } else {
     223           27 :         complete_inner_text(parser);
     224           27 :         if (parser->stanza->parent) {
     225              :             /* we're finishing a child stanza, so set current to the parent */
     226           22 :             parser->stanza = parser->stanza->parent;
     227              :         } else {
     228            5 :             if (parser->stanzacb)
     229            5 :                 parser->stanzacb(parser->stanza, parser->userdata);
     230            5 :             xmpp_stanza_release(parser->stanza);
     231            5 :             parser->stanza = NULL;
     232              :         }
     233              :     }
     234           31 : }
     235              : 
     236            5 : static void _characters(void *userdata, const XML_Char *s, int len)
     237              : {
     238            5 :     parser_t *parser = (parser_t *)userdata;
     239            5 :     char *p;
     240              : 
     241            5 :     if (parser->depth < 2)
     242              :         return;
     243              : 
     244              :     /* Join all parts to a single resulting string. Stanza is created in
     245              :      * _start_element() and _end_element(). */
     246            5 :     if (parser->inner_text_used + len >= parser->inner_text_size) {
     247            5 :         parser->inner_text_size =
     248            5 :             parser->inner_text_used + len + 1 + INNER_TEXT_PADDING;
     249            5 :         p = strophe_realloc(parser->ctx, parser->inner_text,
     250              :                             parser->inner_text_size);
     251            5 :         if (p == NULL) {
     252            0 :             strophe_free(parser->ctx, parser->inner_text);
     253            0 :             parser->inner_text = NULL;
     254            0 :             parser->inner_text_used = 0;
     255            0 :             parser->inner_text_size = 0;
     256            0 :             return;
     257              :         }
     258            5 :         parser->inner_text = p;
     259            5 :         parser->inner_text[parser->inner_text_used] = '\0';
     260              :     }
     261            5 :     parser->inner_text_used += len;
     262            5 :     strncat(parser->inner_text, s, len);
     263              : }
     264              : 
     265           13 : parser_t *parser_new(xmpp_ctx_t *ctx,
     266              :                      parser_start_callback startcb,
     267              :                      parser_end_callback endcb,
     268              :                      parser_stanza_callback stanzacb,
     269              :                      void *userdata)
     270              : {
     271           13 :     parser_t *parser;
     272              : 
     273           13 :     parser = strophe_alloc(ctx, sizeof(parser_t));
     274           13 :     if (parser != NULL) {
     275           13 :         parser->ctx = ctx;
     276           13 :         parser->expat = NULL;
     277           13 :         parser->startcb = startcb;
     278           13 :         parser->endcb = endcb;
     279           13 :         parser->stanzacb = stanzacb;
     280           13 :         parser->userdata = userdata;
     281           13 :         parser->depth = 0;
     282           13 :         parser->stanza = NULL;
     283           13 :         parser->inner_text = NULL;
     284           13 :         parser->inner_text_size = 0;
     285           13 :         parser->inner_text_used = 0;
     286              : 
     287           13 :         parser_reset(parser);
     288              :     }
     289              : 
     290           13 :     return parser;
     291              : }
     292              : 
     293            0 : char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
     294              : {
     295            0 :     return _xml_name(ctx, nsname);
     296              : }
     297              : 
     298              : static void _free_parent_stanza(xmpp_stanza_t *stanza)
     299              : {
     300              :     xmpp_stanza_t *parent;
     301              : 
     302            2 :     for (parent = stanza; parent->parent != NULL; parent = parent->parent)
     303              :         ;
     304            1 :     xmpp_stanza_release(parent);
     305              : }
     306              : 
     307              : /* free a parser */
     308           13 : void parser_free(parser_t *parser)
     309              : {
     310           13 :     if (parser->expat)
     311           13 :         XML_ParserFree(parser->expat);
     312              : 
     313           13 :     if (parser->stanza) {
     314            1 :         _free_parent_stanza(parser->stanza);
     315            1 :         parser->stanza = NULL;
     316              :     }
     317              : 
     318           13 :     if (parser->inner_text) {
     319            1 :         strophe_free(parser->ctx, parser->inner_text);
     320            1 :         parser->inner_text = NULL;
     321              :     }
     322              : 
     323           13 :     strophe_free(parser->ctx, parser);
     324           13 : }
     325              : 
     326              : /* shuts down and restarts XML parser.  true on success */
     327           13 : int parser_reset(parser_t *parser)
     328              : {
     329           13 :     XML_Bool ret;
     330           13 :     const XML_Memory_Handling_Suite *mem = NULL;
     331              : 
     332           13 :     if (parser->expat) {
     333            0 :         ret = XML_ParserReset(parser->expat, NULL);
     334            0 :         if (ret != XML_TRUE) {
     335            0 :             XML_ParserFree(parser->expat);
     336            0 :             parser->expat = NULL;
     337              :         }
     338              :     } else {
     339           13 :         if (mem_ctx == NULL)
     340            2 :             mem_ctx = parser->ctx;
     341           13 :         if (parser->ctx == mem_ctx)
     342           13 :             mem = &parser_mem_suite;
     343           13 :         parser->expat = XML_ParserCreate_MM(NULL, mem, &namespace_sep);
     344              :     }
     345              : 
     346           13 :     if (parser->stanza) {
     347            0 :         _free_parent_stanza(parser->stanza);
     348            0 :         parser->stanza = NULL;
     349              :     }
     350              : 
     351           13 :     if (parser->inner_text) {
     352            0 :         strophe_free(parser->ctx, parser->inner_text);
     353            0 :         parser->inner_text = NULL;
     354              :     }
     355              : 
     356           13 :     if (!parser->expat)
     357              :         return 0;
     358              : 
     359           13 :     parser->depth = 0;
     360              : 
     361           13 :     XML_SetUserData(parser->expat, parser);
     362           13 :     XML_SetElementHandler(parser->expat, _start_element, _end_element);
     363           13 :     XML_SetCharacterDataHandler(parser->expat, _characters);
     364              : 
     365           13 :     return 1;
     366              : }
     367              : 
     368           15 : int parser_feed(parser_t *parser, char *chunk, int len)
     369              : {
     370           15 :     return XML_Parse(parser->expat, chunk, len, 0);
     371              : }
        

Generated by: LCOV version 2.0-1