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
|
#include <xmpp/xmpp_parser.hpp>
#include <xmpp/xmpp_stanza.hpp>
/**
* Expat handlers. Called by the Expat library, never by ourself.
* They just forward the call to the XmppParser corresponding methods.
*/
static void start_element_handler(void* user_data, const XML_Char* name, const XML_Char** atts)
{
static_cast<XmppParser*>(user_data)->start_element(name, atts);
}
static void end_element_handler(void* user_data, const XML_Char* name)
{
static_cast<XmppParser*>(user_data)->end_element(name);
}
static void character_data_handler(void *user_data, const XML_Char *s, int len)
{
static_cast<XmppParser*>(user_data)->char_data(s, len);
}
/**
* XmppParser class
*/
XmppParser::XmppParser():
level(0),
current_node(nullptr)
{
// Create the expat parser
this->parser = XML_ParserCreateNS("UTF-8", ':');
XML_SetUserData(this->parser, static_cast<void*>(this));
// Install Expat handlers
XML_SetElementHandler(this->parser, &start_element_handler, &end_element_handler);
XML_SetCharacterDataHandler(this->parser, &character_data_handler);
}
XmppParser::~XmppParser()
{
if (this->current_node)
delete this->current_node;
XML_ParserFree(this->parser);
}
void XmppParser::feed(const char* data, const int len, const bool is_final)
{
XML_Parse(this->parser, data, len, is_final);
}
void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute)
{
level++;
XmlNode* new_node = new XmlNode(name, this->current_node);
if (this->current_node)
this->current_node->add_child(new_node);
this->current_node = new_node;
for (size_t i = 0; attribute[i]; i += 2)
this->current_node->set_attribute(attribute[i], attribute[i+1]);
if (this->level == 1)
this->stream_open_event(*this->current_node);
}
void XmppParser::end_element(const XML_Char* name)
{
(void)name;
level--;
this->current_node->close();
if (level == 1)
{
this->stanza_event(*this->current_node);
}
if (level == 0)
{
this->stream_close_event(*this->current_node);
delete this->current_node;
this->current_node = nullptr;
}
else
this->current_node = this->current_node->get_parent();
if (level == 1)
this->current_node->delete_all_children();
}
void XmppParser::char_data(const XML_Char* data, int len)
{
if (this->current_node->has_children())
this->current_node->get_last_child()->add_to_tail(std::string(data, len));
else
this->current_node->add_to_inner(std::string(data, len));
}
void XmppParser::stanza_event(const Stanza& stanza) const
{
for (const auto& callback: this->stanza_callbacks)
callback(stanza);
}
void XmppParser::stream_open_event(const XmlNode& node) const
{
for (const auto& callback: this->stream_open_callbacks)
callback(node);
}
void XmppParser::stream_close_event(const XmlNode& node) const
{
for (const auto& callback: this->stream_close_callbacks)
callback(node);
}
void XmppParser::add_stanza_callback(std::function<void(const Stanza&)>&& callback)
{
this->stanza_callbacks.emplace_back(std::move(callback));
}
void XmppParser::add_stream_open_callback(std::function<void(const XmlNode&)>&& callback)
{
this->stream_open_callbacks.emplace_back(std::move(callback));
}
void XmppParser::add_stream_close_callback(std::function<void(const XmlNode&)>&& callback)
{
this->stream_close_callbacks.emplace_back(std::move(callback));
}
|