Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of libdom.
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org>
  6.  */
  7.  
  8. #include <stdbool.h>
  9. #include <string.h>
  10. #include <assert.h>
  11.  
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14.  
  15. #include <dom/dom.h>
  16.  
  17. #include "xmlparser.h"
  18. #include "utils.h"
  19.  
  20. #include <expat.h>
  21.  
  22. /**
  23.  * expat XML parser object
  24.  */
  25. struct dom_xml_parser {
  26.         dom_msg msg;                    /**< Informational message function */
  27.         void *mctx;                     /**< Pointer to client data */
  28.         XML_Parser parser;              /**< expat parser context */
  29.         struct dom_document *doc;       /**< DOM Document we're building */
  30.         struct dom_node *current;       /**< DOM node we're currently building */
  31.         bool is_cdata;                  /**< If the character data is cdata or text */
  32. };
  33.  
  34. /* Binding functions */
  35.  
  36. static void
  37. expat_xmlparser_start_element_handler(void *_parser,
  38.                                       const XML_Char *name,
  39.                                       const XML_Char **atts)
  40. {
  41.         dom_xml_parser *parser = _parser;
  42.         dom_exception err;
  43.         dom_element *elem, *ins_elem;
  44.         dom_string *tag_name;
  45.         dom_string *namespace = NULL;
  46.         const XML_Char *ns_sep = strchr(name, '\n');
  47.  
  48.         if (ns_sep != NULL) {
  49.                 err = dom_string_create_interned((const uint8_t *)name,
  50.                                                  ns_sep - name,
  51.                                                  &namespace);
  52.                 if (err != DOM_NO_ERR) {
  53.                         parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  54.                                     "No memory for namespace name");
  55.                         return;
  56.                 }
  57.                 name = ns_sep + 1;
  58.         }
  59.  
  60.         err = dom_string_create_interned((const uint8_t *)name,
  61.                                          strlen(name),
  62.                                          &tag_name);
  63.         if (err != DOM_NO_ERR) {
  64.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  65.                             "No memory for tag name");
  66.                 if (namespace != NULL)
  67.                         dom_string_unref(namespace);
  68.                 return;
  69.         }
  70.  
  71.         if (namespace == NULL)
  72.                 err = dom_document_create_element(parser->doc,
  73.                                                   tag_name, &elem);
  74.         else
  75.                 err = dom_document_create_element_ns(parser->doc, namespace,
  76.                                                      tag_name, &elem);
  77.         if (err != DOM_NO_ERR) {
  78.                 if (namespace != NULL)
  79.                         dom_string_unref(namespace);
  80.                 dom_string_unref(tag_name);
  81.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  82.                             "Failed to create element '%s'", name);
  83.                 return;
  84.         }
  85.  
  86.         dom_string_unref(tag_name);
  87.         if (namespace != NULL)
  88.                 dom_string_unref(namespace);
  89.  
  90.         /* Add attributes to the element */
  91.         while (*atts) {
  92.                 dom_string *key, *value;
  93.                 ns_sep = strchr(*atts, '\n');
  94.                 if (ns_sep != NULL) {
  95.                         err = dom_string_create_interned((const uint8_t *)(*atts),
  96.                                                          ns_sep - (*atts),
  97.                                                          &namespace);
  98.                         if (err != DOM_NO_ERR) {
  99.                                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  100.                                             "No memory for attr namespace");
  101.                                 dom_node_unref(elem);
  102.                                 return;
  103.                         }
  104.                 } else
  105.                         namespace = NULL;
  106.                 if (ns_sep == NULL)
  107.                         err = dom_string_create_interned((const uint8_t *)(*atts),
  108.                                                          strlen(*atts), &key);
  109.                 else
  110.                         err = dom_string_create_interned((const uint8_t *)(ns_sep + 1),
  111.                                                          strlen(ns_sep + 1),
  112.                                                          &key);
  113.                 if (err != DOM_NO_ERR) {
  114.                         parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  115.                                     "No memory for attribute name");
  116.                         if (namespace != NULL)
  117.                                 dom_string_unref(namespace);
  118.                         dom_node_unref(elem);
  119.                         return;
  120.                 }
  121.                 atts++;
  122.                 err = dom_string_create((const uint8_t *)(*atts),
  123.                                         strlen(*atts), &value);
  124.                 if (err != DOM_NO_ERR) {
  125.                         dom_node_unref(elem);
  126.                         if (namespace != NULL)
  127.                                 dom_string_unref(namespace);
  128.                         dom_string_unref(key);
  129.                         parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  130.                                     "No memory for attribute value");
  131.                         return;
  132.                 }
  133.                 atts++;
  134.  
  135.                 if (namespace == NULL)
  136.                         err = dom_element_set_attribute(elem, key, value);
  137.                 else
  138.                         err = dom_element_set_attribute_ns(elem, namespace,
  139.                                                            key, value);
  140.                 if (namespace != NULL)
  141.                         dom_string_unref(namespace);
  142.                 dom_string_unref(key);
  143.                 dom_string_unref(value);
  144.                 if (err != DOM_NO_ERR) {
  145.                         dom_node_unref(elem);
  146.                         parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  147.                                     "No memory for setting attribute");
  148.                         return;
  149.                 }
  150.         }
  151.  
  152.         err = dom_node_append_child(parser->current, (struct dom_node *) elem,
  153.                                     (struct dom_node **) (void *) &ins_elem);
  154.         if (err != DOM_NO_ERR) {
  155.                 dom_node_unref(elem);
  156.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  157.                             "No memory for appending child node");
  158.                 return;
  159.         }
  160.  
  161.         dom_node_unref(ins_elem);
  162.  
  163.         dom_node_unref(parser->current);
  164.         parser->current = (struct dom_node *)elem; /* Steal initial ref */
  165. }
  166.  
  167. static void
  168. expat_xmlparser_end_element_handler(void *_parser,
  169.                                     const XML_Char *name)
  170. {
  171.         dom_xml_parser *parser = _parser;
  172.         dom_exception err;
  173.         dom_node *parent;
  174.  
  175.         UNUSED(name);
  176.  
  177.         err = dom_node_get_parent_node(parser->current, &parent);
  178.  
  179.         if (err != DOM_NO_ERR) {
  180.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  181.                             "Unable to find a parent while closing element.");
  182.                 return;
  183.         }
  184.  
  185.         dom_node_unref(parser->current);
  186.         parser->current = parent;  /* Takes the ref given by get_parent_node */
  187. }
  188.  
  189. static void
  190. expat_xmlparser_start_cdata_handler(void *_parser)
  191. {
  192.         dom_xml_parser *parser = _parser;
  193.  
  194.         parser->is_cdata = true;
  195. }
  196.  
  197. static void
  198. expat_xmlparser_end_cdata_handler(void *_parser)
  199. {
  200.         dom_xml_parser *parser = _parser;
  201.  
  202.         parser->is_cdata = false;
  203. }
  204.  
  205. static void
  206. expat_xmlparser_cdata_handler(void *_parser,
  207.                               const XML_Char *s,
  208.                               int len)
  209. {
  210.         dom_xml_parser *parser = _parser;
  211.         dom_string *data;
  212.         dom_exception err;
  213.         struct dom_node *cdata, *ins_cdata, *lastchild = NULL;
  214.         dom_node_type ntype = 0;
  215.  
  216.         err = dom_string_create((const uint8_t *)s, len, &data);
  217.         if (err != DOM_NO_ERR) {
  218.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  219.                             "No memory for cdata section contents");
  220.                 return;
  221.         }
  222.  
  223.         err = dom_node_get_last_child(parser->current, &lastchild);
  224.  
  225.         if (err == DOM_NO_ERR && lastchild != NULL) {
  226.                 err = dom_node_get_node_type(lastchild, &ntype);
  227.         }
  228.  
  229.         if (err != DOM_NO_ERR) {
  230.                 dom_string_unref(data);
  231.                 if (lastchild != NULL)
  232.                         dom_node_unref(lastchild);
  233.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  234.                             "No memory for cdata section");
  235.                 return;
  236.         }
  237.  
  238.         if (ntype == DOM_TEXT_NODE && parser->is_cdata == false) {
  239.                 /* We can append this text instead */
  240.                 err = dom_characterdata_append_data(
  241.                         (dom_characterdata *)lastchild, data);
  242.                 dom_string_unref(data);
  243.                 if (lastchild != NULL)
  244.                         dom_node_unref(lastchild);
  245.                 if (err != DOM_NO_ERR) {
  246.                         parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  247.                                     "No memory for cdata section");
  248.                 }
  249.                 return;
  250.         }
  251.  
  252.         if (lastchild != NULL)
  253.                 dom_node_unref(lastchild);
  254.  
  255.         /* We can't append directly, so make a new node */
  256.         err = parser->is_cdata ?
  257.                 dom_document_create_cdata_section(parser->doc, data,
  258.                                 (dom_cdata_section **) (void *) &cdata) :
  259.                 dom_document_create_text_node(parser->doc, data,
  260.                                               (dom_text **) (void *) &cdata);
  261.         if (err != DOM_NO_ERR) {
  262.                 dom_string_unref(data);
  263.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  264.                             "No memory for cdata section");
  265.                 return;
  266.         }
  267.  
  268.         /* No longer need data */
  269.         dom_string_unref(data);
  270.  
  271.         /* Append cdata section to parent */
  272.         err = dom_node_append_child(parser->current, cdata, &ins_cdata);
  273.         if (err != DOM_NO_ERR) {
  274.                 dom_node_unref((struct dom_node *) cdata);
  275.                 parser->msg(DOM_MSG_ERROR, parser->mctx,
  276.                                 "Failed attaching cdata section");
  277.                 return;
  278.         }
  279.  
  280.         /* We're not interested in the inserted cdata section */
  281.         if (ins_cdata != NULL)
  282.                 dom_node_unref(ins_cdata);
  283.  
  284.         /* No longer interested in cdata section */
  285.         dom_node_unref(cdata);
  286. }
  287.  
  288. static int
  289. expat_xmlparser_external_entity_ref_handler(XML_Parser parser,
  290.                                             const XML_Char *context,
  291.                                             const XML_Char *base,
  292.                                             const XML_Char *system_id,
  293.                                             const XML_Char *public_id)
  294. {
  295.         FILE *fh;
  296.         XML_Parser subparser;
  297.         unsigned char data[1024];
  298.         size_t len;
  299.         enum XML_Status status;
  300.  
  301.         UNUSED(base);
  302.         UNUSED(public_id);
  303.  
  304.         if (system_id == NULL)
  305.                 return XML_STATUS_OK;
  306.  
  307.         fh = fopen(system_id, "r");
  308.  
  309.         if (fh == NULL)
  310.                 return XML_STATUS_OK;
  311.  
  312.         subparser = XML_ExternalEntityParserCreate(parser,
  313.                                                    context,
  314.                                                    NULL);
  315.  
  316.         if (subparser == NULL) {
  317.                 fclose(fh);
  318.                 return XML_STATUS_OK;
  319.         }
  320.  
  321.         /* Parse the file bit by bit */
  322.         while ((len = fread(data, 1, 1024, fh)) > 0) {
  323.                 status = XML_Parse(subparser, (const char *)data, len, 0);
  324.                 if (status != XML_STATUS_OK) {
  325.                         XML_ParserFree(subparser);
  326.                         fclose(fh);
  327.                         return XML_STATUS_OK;
  328.                 }
  329.         }
  330.         XML_Parse(subparser, "", 0, 1);
  331.         XML_ParserFree(subparser);
  332.         fclose(fh);
  333.         return XML_STATUS_OK;
  334. }
  335.  
  336. static void
  337. expat_xmlparser_comment_handler(void *_parser,
  338.                                 const XML_Char *_comment)
  339. {
  340.         dom_xml_parser *parser = _parser;
  341.         struct dom_comment *comment, *ins_comment = NULL;
  342.         dom_string *data;
  343.         dom_exception err;
  344.  
  345.         /* Create DOM string data for comment */
  346.         err = dom_string_create((const uint8_t *)_comment,
  347.                         strlen((const char *) _comment), &data);
  348.         if (err != DOM_NO_ERR) {
  349.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  350.                                 "No memory for comment data");
  351.                 return;
  352.         }
  353.  
  354.         /* Create comment */
  355.         err = dom_document_create_comment(parser->doc, data, &comment);
  356.         if (err != DOM_NO_ERR) {
  357.                 dom_string_unref(data);
  358.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  359.                                         "No memory for comment node");
  360.                 return;
  361.         }
  362.  
  363.         /* No longer need data */
  364.         dom_string_unref(data);
  365.  
  366.         /* Append comment to parent */
  367.         err = dom_node_append_child(parser->current, (struct dom_node *) comment,
  368.                         (struct dom_node **) (void *) &ins_comment);
  369.         if (err != DOM_NO_ERR) {
  370.                 dom_node_unref((struct dom_node *) comment);
  371.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  372.                                 "Failed attaching comment node");
  373.                 return;
  374.         }
  375.  
  376.         /* We're not interested in the inserted comment */
  377.         if (ins_comment != NULL)
  378.                 dom_node_unref((struct dom_node *) ins_comment);
  379.  
  380.         /* No longer interested in comment */
  381.         dom_node_unref((struct dom_node *) comment);
  382.  
  383. }
  384.  
  385. static void
  386. expat_xmlparser_start_doctype_decl_handler(void *_parser,
  387.                                            const XML_Char *doctype_name,
  388.                                            const XML_Char *system_id,
  389.                                            const XML_Char *public_id,
  390.                                            int has_internal_subset)
  391. {
  392.         dom_xml_parser *parser = _parser;
  393.         struct dom_document_type *doctype, *ins_doctype = NULL;
  394.         dom_exception err;
  395.  
  396.         UNUSED(has_internal_subset);
  397.  
  398.         err = dom_implementation_create_document_type(
  399.                 doctype_name, system_id ? system_id : "",
  400.                 public_id ? public_id : "",
  401.                 &doctype);
  402.  
  403.         if (err != DOM_NO_ERR) {
  404.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  405.                                 "Failed to create document type");
  406.                 return;
  407.         }
  408.  
  409.         /* Add doctype to document */
  410.         err = dom_node_append_child(parser->doc, (struct dom_node *) doctype,
  411.                         (struct dom_node **) (void *) &ins_doctype);
  412.         if (err != DOM_NO_ERR) {
  413.                 dom_node_unref((struct dom_node *) doctype);
  414.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  415.                                         "Failed attaching doctype");
  416.                 return;
  417.         }
  418.  
  419.         /* Not interested in inserted node */
  420.         if (ins_doctype != NULL)
  421.                 dom_node_unref((struct dom_node *) ins_doctype);
  422.  
  423.         /* No longer interested in doctype */
  424.         dom_node_unref((struct dom_node *) doctype);
  425. }
  426.  
  427. static void
  428. expat_xmlparser_unknown_data_handler(void *_parser,
  429.                                      const XML_Char *s,
  430.                                      int len)
  431. {
  432.         UNUSED(_parser);
  433.         UNUSED(s);
  434.         UNUSED(len);
  435. }
  436. /**
  437.  * Create an XML parser instance
  438.  *
  439.  * \param enc      Source charset, or NULL
  440.  * \param int_enc  Desired charset of document buffer (UTF-8 or UTF-16)
  441.  * \param msg      Informational message function
  442.  * \param mctx     Pointer to client-specific private data
  443.  * \return Pointer to instance, or NULL on memory exhaustion
  444.  *
  445.  * int_enc is ignored due to it being made of bees.
  446.  */
  447. dom_xml_parser *
  448. dom_xml_parser_create(const char *enc, const char *int_enc,
  449.                       dom_msg msg, void *mctx, dom_document **document)
  450. {
  451.         dom_xml_parser *parser;
  452.         dom_exception err;
  453.  
  454.         UNUSED(int_enc);
  455.  
  456.         parser = calloc(sizeof(*parser), 1);
  457.         if (parser == NULL) {
  458.                 msg(DOM_MSG_CRITICAL, mctx, "No memory for parser");
  459.                 return NULL;
  460.         }
  461.  
  462.         parser->msg = msg;
  463.         parser->mctx = mctx;
  464.  
  465.         parser->parser = XML_ParserCreateNS(enc, '\n');
  466.  
  467.         if (parser->parser == NULL) {
  468.                 free(parser);
  469.                 msg(DOM_MSG_CRITICAL, mctx, "No memory for parser");
  470.                 return NULL;
  471.         }
  472.  
  473.         parser->doc = NULL;
  474.  
  475.         err = dom_implementation_create_document(
  476.                 DOM_IMPLEMENTATION_XML,
  477.                 /* namespace */ NULL,
  478.                 /* qname */ NULL,
  479.                 /* doctype */ NULL,
  480.                 NULL,
  481.                 NULL,
  482.                 document);
  483.  
  484.         if (err != DOM_NO_ERR) {
  485.                 parser->msg(DOM_MSG_CRITICAL, parser->mctx,
  486.                             "Failed creating document");
  487.                 XML_ParserFree(parser->parser);
  488.                 free(parser);
  489.                 return NULL;
  490.         }
  491.  
  492.         parser->doc = (dom_document *) dom_node_ref(*document);
  493.  
  494.         XML_SetUserData(parser->parser, parser);
  495.  
  496.         XML_SetElementHandler(parser->parser,
  497.                               expat_xmlparser_start_element_handler,
  498.                               expat_xmlparser_end_element_handler);
  499.  
  500.         XML_SetCdataSectionHandler(parser->parser,
  501.                                    expat_xmlparser_start_cdata_handler,
  502.                                    expat_xmlparser_end_cdata_handler);
  503.  
  504.         XML_SetCharacterDataHandler(parser->parser,
  505.                                     expat_xmlparser_cdata_handler);
  506.  
  507.         XML_SetParamEntityParsing(parser->parser,
  508.                                   XML_PARAM_ENTITY_PARSING_ALWAYS);
  509.  
  510.         XML_SetExternalEntityRefHandler(parser->parser,
  511.                                         expat_xmlparser_external_entity_ref_handler);
  512.  
  513.         XML_SetCommentHandler(parser->parser,
  514.                               expat_xmlparser_comment_handler);
  515.  
  516.         XML_SetStartDoctypeDeclHandler(parser->parser,
  517.                                        expat_xmlparser_start_doctype_decl_handler);
  518.  
  519.         XML_SetDefaultHandlerExpand(parser->parser,
  520.                               expat_xmlparser_unknown_data_handler);
  521.  
  522.         parser->current = dom_node_ref(parser->doc);
  523.  
  524.         parser->is_cdata = false;
  525.  
  526.         return parser;
  527. }
  528.  
  529. /**
  530.  * Destroy an XML parser instance
  531.  *
  532.  * \param parser  The parser instance to destroy
  533.  */
  534. void
  535. dom_xml_parser_destroy(dom_xml_parser *parser)
  536. {
  537.         XML_ParserFree(parser->parser);
  538.         if (parser->current != NULL)
  539.                 dom_node_unref(parser->current);
  540.         dom_node_unref(parser->doc);
  541.         free(parser);
  542. }
  543.  
  544. /**
  545.  * Parse a chunk of data
  546.  *
  547.  * \param parser  The XML parser instance to use for parsing
  548.  * \param data    Pointer to data chunk
  549.  * \param len     Byte length of data chunk
  550.  * \return DOM_XML_OK on success, DOM_XML_EXTERNAL_ERR | <expat error> on failure
  551.  */
  552. dom_xml_error
  553. dom_xml_parser_parse_chunk(dom_xml_parser *parser, uint8_t *data, size_t len)
  554. {
  555.         enum XML_Status status;
  556.  
  557.         status = XML_Parse(parser->parser, (const char *)data, len, 0);
  558.         if (status != XML_STATUS_OK) {
  559.                 parser->msg(DOM_MSG_ERROR, parser->mctx,
  560.                             "XML_Parse failed: %d", status);
  561.                 return DOM_XML_EXTERNAL_ERR | status;
  562.         }
  563.  
  564.         return DOM_XML_OK;
  565. }
  566.  
  567. /**
  568.  * Notify parser that datastream is empty
  569.  *
  570.  * \param parser  The XML parser instance to notify
  571.  * \return DOM_XML_OK on success, DOM_XML_EXTERNAL_ERR | <expat error> on failure
  572.  *
  573.  * This will force any remaining data through the parser
  574.  */
  575. dom_xml_error
  576. dom_xml_parser_completed(dom_xml_parser *parser)
  577. {
  578.         enum XML_Status status;
  579.  
  580.         status = XML_Parse(parser->parser, "", 0, 1);
  581.         if (status != XML_STATUS_OK) {
  582.                 parser->msg(DOM_MSG_ERROR, parser->mctx,
  583.                             "XML_Parse failed: %d", status);
  584.                 return DOM_XML_EXTERNAL_ERR | status;
  585.         }
  586.  
  587.         return DOM_XML_OK;
  588.  
  589. }
  590.