summaryrefslogtreecommitdiff
path: root/src/rexmpp_xml_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rexmpp_xml_parser.c')
-rw-r--r--src/rexmpp_xml_parser.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/rexmpp_xml_parser.c b/src/rexmpp_xml_parser.c
new file mode 100644
index 0000000..3f485d7
--- /dev/null
+++ b/src/rexmpp_xml_parser.c
@@ -0,0 +1,323 @@
+/**
+ @file rexmpp_xml_parser.c
+ @brief XML parsing for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include <string.h>
+
+#include "rexmpp.h"
+#include "rexmpp_xml.h"
+#include "rexmpp_xml_parser.h"
+#include "config.h"
+
+#if defined(USE_LIBXML2)
+
+void rexmpp_xml_sax_characters (rexmpp_xml_parser_ctx_t ctx,
+ const char *ch,
+ int len)
+{
+ ctx->handlers->text(ctx->user_data, ch, len);
+}
+
+void rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
+ const char *localname,
+ const char *prefix,
+ const char *URI,
+ int nb_namespaces,
+ const char **namespaces,
+ int nb_attributes,
+ int nb_defaulted,
+ const char **attributes)
+{
+ (void)prefix;
+ (void)nb_namespaces;
+ (void)namespaces;
+ (void)nb_defaulted;
+ rexmpp_xml_attr_t *attrs = NULL;
+ int i;
+ for (i = nb_attributes - 1; i >= 0; i--) {
+ size_t attr_len = attributes[i * 5 + 4] - attributes[i * 5 + 3];
+ char *attr_val = malloc(attr_len + 1);
+ if (attr_val != NULL) {
+ attr_val[attr_len] = '\0';
+ strncpy(attr_val, attributes[i * 5 + 3], attr_len);
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(attributes[i * 5], NULL, attr_val);
+ free(attr_val);
+ attr->next = attrs;
+ attrs = attr;
+ }
+ }
+
+ ctx->handlers->elem_start(ctx->user_data, localname, URI, attrs);
+}
+
+void rexmpp_xml_sax_elem_end (rexmpp_xml_parser_ctx_t ctx,
+ const char *localname,
+ const char *prefix,
+ const char *URI)
+{
+ (void)localname;
+ (void)prefix;
+ (void)URI;
+ ctx->handlers->elem_end(ctx->user_data);
+}
+
+xmlSAXHandler rexmpp_xml_parser_sax = {
+ .initialized = XML_SAX2_MAGIC,
+ .characters = (charactersSAXFunc)rexmpp_xml_sax_characters,
+ .startElementNs = (startElementNsSAX2Func)rexmpp_xml_sax_elem_start,
+ .endElementNs = (endElementNsSAX2Func)rexmpp_xml_sax_elem_end,
+};
+
+
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+
+/* rexmpp_xml_t *to = NULL; */
+/* if (from->type == XML_ELEMENT_NODE) { */
+/* to = malloc(sizeof(rexmpp_xml_t)); */
+
+/* /\* Type *\/ */
+/* to->type = REXMPP_XML_ELEMENT; */
+
+/* /\* Name and namespace *\/ */
+/* to->alt.elem.qname.name = strdup(from->name); */
+/* if (from->nsDef != NULL && from->nsDef->href != NULL) { */
+/* to->alt.elem.qname.namespace = strdup(from->nsDef->href); */
+/* } else { */
+/* to->alt.elem.qname.namespace = NULL; */
+/* } */
+
+/* /\* Attributes *\/ */
+/* to->alt.elem.attributes = NULL; */
+/* struct _xmlAttr *from_attr; */
+/* rexmpp_xml_attr_t **to_next_attr = &(to->alt.elem.attributes); */
+/* for (from_attr = from->properties; */
+/* from_attr != NULL; */
+/* from_attr = from_attr->next) */
+/* { */
+/* rexmpp_xml_attr_t *to_attr = */
+/* malloc(sizeof(rexmpp_xml_attr_t)); */
+/* to_attr->qname.name = strdup(from_attr->name); */
+/* to_attr->qname.namespace = NULL; */
+/* if (from_attr->ns != NULL && from_attr->ns->href != NULL) { */
+/* to_attr->qname.namespace = strdup(from_attr->ns->href); */
+/* to_attr->value = */
+/* xmlGetNsProp(from, to_attr->qname.name, to_attr->qname.namespace); */
+/* } else { */
+/* to_attr->value = xmlGetProp(from, to_attr->qname.name); */
+/* } */
+/* to_attr->next = NULL; */
+
+/* *to_next_attr = to_attr; */
+/* to_next_attr = &(to_attr->next); */
+/* } */
+
+/* /\* Children *\/ */
+/* to->alt.elem.children = NULL; */
+/* xmlNodePtr from_child; */
+/* rexmpp_xml_t **to_next_child = &(to->alt.elem.children); */
+/* for (from_child = from->children; */
+/* from_child != NULL; */
+/* from_child = from_child->next) */
+/* { */
+/* rexmpp_xml_t *next_child = rexmpp_xml_from_libxml2(from_child); */
+/* if (next_child != NULL) { */
+/* *to_next_child = next_child; */
+/* to_next_child = &(next_child->next); */
+/* } */
+/* } */
+
+/* /\* Next *\/ */
+/* to->next = NULL; */
+
+/* } else if (from->type == XML_TEXT_NODE) { */
+/* to = malloc(sizeof(rexmpp_xml_t)); */
+/* to->type = REXMPP_XML_TEXT; */
+/* to->alt.text = xmlNodeGetContent(from); */
+/* to->next = NULL; */
+/* } */
+/* return to; */
+/* } */
+
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+/* rexmpp_xml_t *to = rexmpp_xml_from_libxml2(from); */
+/* if (from->next != NULL) { */
+/* to->next = rexmpp_xml_from_libxml2_list(from->next); */
+/* } */
+/* return to; */
+/* } */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+
+/* if (from->type == REXMPP_XML_TEXT) { */
+/* xmlNodePtr to = xmlNewText(from->alt.text); */
+/* to->next = rexmpp_xml_to_libxml2(from->next); */
+/* return to; */
+/* } */
+
+/* /\* Name and namespace *\/ */
+/* xmlNodePtr to = xmlNewNode(NULL, from->alt.elem.qname.name); */
+/* if (from->alt.elem.qname.namespace != NULL) { */
+/* xmlNewNs(to, from->alt.elem.qname.namespace, NULL); */
+/* } */
+
+/* /\* Attributes *\/ */
+/* rexmpp_xml_attr_t *attr = from->alt.elem.attributes; */
+/* while (attr != NULL) { */
+/* /\* TODO: Would be nice to take namespaces into account, though */
+/* they are currently not used for attributes. *\/ */
+/* xmlNewProp(to, attr->qname.name, attr->value); */
+/* attr = attr->next; */
+/* } */
+
+/* /\* Children *\/ */
+/* rexmpp_xml_t *child = from->alt.elem.children; */
+/* while (child != NULL) { */
+/* xmlAddChild(to, rexmpp_xml_to_libxml2(child)); */
+/* child = child->next; */
+/* } */
+/* return to; */
+/* } */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from) { */
+/* xmlNodePtr to = rexmpp_xml_to_libxml2(from); */
+/* if (from->next != NULL) { */
+/* xmlAddNextSibling(to, rexmpp_xml_to_libxml2_list(from->next)); */
+/* } */
+/* return to; */
+/* } */
+
+#elif defined(USE_EXPAT)
+
+void XMLCALL
+rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
+ const char *el,
+ const char **attributes)
+{
+ char *buf = strdup(el);
+ char *name = NULL, *namespace = buf;
+ size_t i;
+ for (i = 0; i < strlen(namespace); i++) {
+ if (namespace[i] == '\xff') {
+ name = namespace + i + 1;
+ namespace[i] = '\0';
+ }
+ }
+ if (name == NULL) {
+ name = namespace;
+ namespace = NULL;
+ }
+ rexmpp_xml_attr_t *attrs = NULL;
+ for (i = 0; attributes[i] != NULL; i += 2) {
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(attributes[i], NULL, attributes[i + 1]);
+ attr->next = attrs;
+ attrs = attr;
+ }
+
+ ctx->handlers->elem_start(ctx->user_data, name, namespace, attrs);
+ free(buf);
+}
+
+void XMLCALL
+rexmpp_xml_sax_elem_end(rexmpp_xml_parser_ctx_t ctx,
+ const XML_Char *name)
+{
+ (void)name;
+ ctx->handlers->elem_end(ctx->user_data);
+}
+
+void XMLCALL
+rexmpp_xml_sax_characters (rexmpp_xml_parser_ctx_t ctx,
+ const XML_Char *ch,
+ int len)
+{
+ ctx->handlers->text(ctx->user_data, ch, len);
+}
+
+#endif
+
+
+
+rexmpp_xml_parser_ctx_t
+rexmpp_xml_parser_new (rexmpp_xml_parser_handlers_t handlers,
+ void *data)
+{
+ rexmpp_xml_parser_ctx_t ctx = malloc(sizeof(struct rexmpp_xml_parser_ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+#if defined(USE_LIBXML2)
+ xmlParserCtxtPtr p =
+ xmlCreatePushParserCtxt(&rexmpp_xml_parser_sax, ctx, "", 0, NULL);
+#elif defined(USE_EXPAT)
+ XML_Parser p = XML_ParserCreateNS("utf-8", '\xff');
+ XML_SetUserData(p, ctx);
+ XML_SetStartElementHandler(p, (XML_StartElementHandler)
+ rexmpp_xml_sax_elem_start);
+ XML_SetEndElementHandler(p, (XML_EndElementHandler)
+ rexmpp_xml_sax_elem_end);
+ XML_SetCharacterDataHandler(p, (XML_CharacterDataHandler)
+ rexmpp_xml_sax_characters);
+#endif
+ if (p == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->xml_parser = p;
+ ctx->handlers = handlers;
+ ctx->user_data = data;
+ return ctx;
+}
+
+void rexmpp_xml_parser_free (rexmpp_xml_parser_ctx_t ctx) {
+#if defined(USE_LIBXML2)
+ xmlFreeParserCtxt(ctx->xml_parser);
+#elif defined(USE_EXPAT)
+ XML_ParserFree(ctx->xml_parser);
+#endif
+ free(ctx);
+}
+
+rexmpp_xml_parser_ctx_t rexmpp_xml_parser_reset (rexmpp_xml_parser_ctx_t ctx) {
+#if defined(USE_LIBXML2)
+ xmlCtxtResetPush(ctx->xml_parser, "", 0, "", "utf-8");
+#elif defined(USE_EXPAT)
+ XML_ParserReset(ctx->xml_parser, "utf-8");
+ XML_SetUserData(ctx->xml_parser, ctx);
+ XML_SetStartElementHandler(ctx->xml_parser, (XML_StartElementHandler)
+ rexmpp_xml_sax_elem_start);
+ XML_SetEndElementHandler(ctx->xml_parser, (XML_EndElementHandler)
+ rexmpp_xml_sax_elem_end);
+ XML_SetCharacterDataHandler(ctx->xml_parser, (XML_CharacterDataHandler)
+ rexmpp_xml_sax_characters);
+#endif
+ return ctx;
+}
+
+void
+rexmpp_xml_parser_feed (rexmpp_xml_parser_ctx_t ctx,
+ const char *chunk,
+ size_t len,
+ int final)
+{
+#if defined(USE_LIBXML2)
+ xmlParseChunk(ctx->xml_parser, chunk, len, final);
+#elif defined(USE_EXPAT)
+ XML_Parse(ctx->xml_parser, chunk, len, final);
+#endif
+}