blob: b8f572303481df2536cdd7543e0f3f8de123f2bb [file] [log] [blame]
// Copyright 2015 The Weave Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <expat.h>
#include <map>
#include <memory>
#include <stack>
#include <string>
#include <base/macros.h>
namespace weave {
class XmlNode;
// A simple XML stream parser. As the XML data is being read from a data source
// (for example, a socket), XmppStreamParser::ParseData() should be called.
// This method parses the provided XML data chunk and if it finds complete
// XML elements, it will call internal OnOpenElement(), OnCloseElement() and
// OnCharData() member functions. These will track the element nesting level.
// When a top-level element starts, the parser will call Delegate::OnStreamStart
// method. Once this happens, every complete XML element (including its children
// if they are present) will trigger Delegate::OnStanze() callback.
// Finally, when top-level element is closed, Delegate::OnStreamEnd() is called.
// This class is specifically tailored to XMPP streams which look like this:
// B: <stream:stream to='' xmlns='jabber:client' version='1.0'>
// S: <presence><show/></presence>
// S: <message to='foo'><body/></message>
// S: <iq to='bar'><query/></iq>
// S: ...
// E: </stream:stream>
// Here, "B:" will trigger OnStreamStart(), "S:" will result in OnStanza() and
// "E:" will result in OnStreamEnd().
class XmppStreamParser final {
// Delegate interface that interested parties implement to receive
// notifications of stream opening/closing and on new stanzas arriving.
class Delegate {
virtual void OnStreamStart(
const std::string& node_name,
std::map<std::string, std::string> attributes) = 0;
virtual void OnStreamEnd(const std::string& node_name) = 0;
virtual void OnStanza(std::unique_ptr<XmlNode> stanza) = 0;
virtual ~Delegate() {}
explicit XmppStreamParser(Delegate* delegate);
// Parses additional XML data received from an input stream.
void ParseData(const std::string& data);
// Resets the parser to expect the top-level stream node again.
void Reset();
// Raw expat callbacks.
static void HandleElementStart(void* user_data,
const XML_Char* element,
const XML_Char** attr);
static void HandleElementEnd(void* user_data, const XML_Char* element);
static void HandleCharData(void* user_data, const char* content, int length);
// Reinterpreted callbacks from expat with some data pre-processed.
void OnOpenElement(const std::string& node_name,
std::map<std::string, std::string> attributes);
void OnCloseElement(const std::string& node_name);
void OnCharData(const std::string& text);
Delegate* delegate_;
XML_Parser parser_{nullptr};
bool started_{false};
std::stack<std::unique_ptr<XmlNode>> node_stack_;
} // namespace weave