summaryrefslogtreecommitdiffstats
blob: 32d4f53b70650175df093fadc5c861524ab8f904 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * Copyright (c) 2010-2019 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swiften/Parser/ExpatParser.h>

#include <cassert>
#include <limits>
#include <memory>
#include <string>

#include <boost/algorithm/string.hpp>

#include <expat.h>

#include <Swiften/Base/String.h>
#include <Swiften/Parser/XMLParserClient.h>

#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"

namespace {
struct XmlInfo {
    std::string prefix;
    std::string uri;
    std::string name;
};

XmlInfo splitExpatInfo(const std::string& s, char sep) {
    // name
    // uri|name
    // uri|name|prefix
    std::vector<std::string> v;
    boost::split(v, s, [sep](char c) {return c == sep; });
    switch (v.size()) {
    case 1:
        return{ "", "", std::move(v[0]) };
    case 2:
        return{ "", std::move(v[0]), std::move(v[1]) };
    case 3:
        return{ std::move(v[2]), std::move(v[0]), std::move(v[1]) };
    default:
        return{ "", "", "" };
    }
}
}


namespace Swift {

static const char NAMESPACE_SEPARATOR = '\x01';

struct ExpatParser::Private {
    XML_Parser parser_;
};

static void handleStartElement(void* parser, const XML_Char* name, const XML_Char** attributes) {
    auto elemInfo = splitExpatInfo(name, NAMESPACE_SEPARATOR);

    AttributeMap attributeValues;
    const XML_Char** currentAttribute = attributes;
    while (*currentAttribute) {
        auto attribInfo = splitExpatInfo(*currentAttribute, NAMESPACE_SEPARATOR);
        attributeValues.addAttribute(attribInfo.name, attribInfo.uri, attribInfo.prefix, std::string(*(currentAttribute+1)));
        currentAttribute += 2;
    }

    auto* client = static_cast<XMLParser*>(parser)->getClient();
    client->handleStartElementPrefix(elemInfo.prefix, elemInfo.uri, elemInfo.name, attributeValues);
    client->handleStartElement(elemInfo.name, elemInfo.uri, attributeValues);
}

static void handleEndElement(void* parser, const XML_Char* name) {
    auto elemInfo = splitExpatInfo(name, NAMESPACE_SEPARATOR);
    static_cast<XMLParser*>(parser)->getClient()->handleEndElement(elemInfo.name, elemInfo.uri);
}

static void handleCharacterData(void* parser, const XML_Char* data, int len) {
    assert(len >= 0);
    static_cast<XMLParser*>(parser)->getClient()->handleCharacterData(std::string(data, static_cast<size_t>(len)));
}

static void handleXMLDeclaration(void*, const XML_Char*, const XML_Char*, int) {
}

static void handleNamespaceDeclaration(void* parser, const XML_Char* prefix, const XML_Char* uri) {
    static_cast<XMLParser*>(parser)->getClient()->handleNamespaceDeclaration(std::string(prefix ? prefix : ""), std::string(uri ? uri : ""));
}

static void handleEntityDeclaration(void* parser, const XML_Char*, int, const XML_Char*, int, const XML_Char*, const XML_Char*, const XML_Char*, const XML_Char*) {
    static_cast<ExpatParser*>(parser)->stopParser();
}

static void handleComment(void* parser, const XML_Char* /*data*/) {
    if (!static_cast<ExpatParser*>(parser)->allowsComments()) {
        static_cast<ExpatParser*>(parser)->stopParser();
    }
}

static void handleProcessingInstruction(void* parser, const XML_Char* /*target*/, const XML_Char* /*data*/) {
    static_cast<ExpatParser*>(parser)->stopParser();
}

static void handleDoctypeDeclaration(void* parser, const XML_Char* /*doctypeName*/, const XML_Char* /*sysid*/, const XML_Char* /*pubid*/, int /*has_internal_subset*/) {
    static_cast<ExpatParser*>(parser)->stopParser();
}

ExpatParser::ExpatParser(XMLParserClient* client, bool allowComments) : XMLParser(client, allowComments), p(new Private()) {
    p->parser_ = XML_ParserCreateNS("UTF-8", NAMESPACE_SEPARATOR);
    XML_SetReturnNSTriplet(p->parser_, true);
    XML_SetUserData(p->parser_, this);
    XML_SetElementHandler(p->parser_, handleStartElement, handleEndElement);
    XML_SetCharacterDataHandler(p->parser_, handleCharacterData);
    XML_SetXmlDeclHandler(p->parser_, handleXMLDeclaration);
    XML_SetEntityDeclHandler(p->parser_, handleEntityDeclaration);
    XML_SetNamespaceDeclHandler(p->parser_, handleNamespaceDeclaration, nullptr);
    XML_SetCommentHandler(p->parser_, handleComment);
    XML_SetProcessingInstructionHandler(p->parser_, handleProcessingInstruction);
    XML_SetDoctypeDeclHandler(p->parser_, handleDoctypeDeclaration, nullptr);
}

ExpatParser::~ExpatParser() {
    XML_ParserFree(p->parser_);
}

bool ExpatParser::parse(const std::string& data, bool finalData) {
    if (data.size() > std::numeric_limits<int>::max()) {
        return false;
    }
    bool success = XML_Parse(p->parser_, data.c_str(), static_cast<int>(data.size()), finalData) == XML_STATUS_OK;
    /*if (!success) {
        std::cout << "ERROR: " << XML_ErrorString(XML_GetErrorCode(p->parser_)) << " while parsing " << data << std::endl;
    }*/
    return success;
}

void ExpatParser::stopParser() {
    XML_StopParser(p->parser_, static_cast<XML_Bool>(0));
}

}