LCOV - code coverage report
Current view: top level - src - parser_expat.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 153 172 89.0 %
Date: 2023-08-10 00:00:00 Functions: 13 15 86.7 %

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

Generated by: LCOV version 1.14