1// Copyright 2015 The Weave Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef LIBWEAVE_SRC_NOTIFICATION_XMPP_STREAM_PARSER_H_
6#define LIBWEAVE_SRC_NOTIFICATION_XMPP_STREAM_PARSER_H_
7
8#include <expat.h>
9
10#include <map>
11#include <memory>
12#include <stack>
13#include <string>
14
15#include <base/macros.h>
16
17namespace weave {
18
19class XmlNode;
20
21// A simple XML stream parser. As the XML data is being read from a data source
22// (for example, a socket), XmppStreamParser::ParseData() should be called.
23// This method parses the provided XML data chunk and if it finds complete
24// XML elements, it will call internal OnOpenElement(), OnCloseElement() and
25// OnCharData() member functions. These will track the element nesting level.
26// When a top-level element starts, the parser will call Delegate::OnStreamStart
27// method. Once this happens, every complete XML element (including its children
28// if they are present) will trigger Delegate::OnStanze() callback.
29// Finally, when top-level element is closed, Delegate::OnStreamEnd() is called.
30// This class is specifically tailored to XMPP streams which look like this:
31// B:  <stream:stream to='example.com' xmlns='jabber:client' version='1.0'>
32// S:    <presence><show/></presence>
33// S:    <message to='foo'><body/></message>
34// S:    <iq to='bar'><query/></iq>
35// S:    ...
36// E:  </stream:stream>
37// Here, "B:" will trigger OnStreamStart(), "S:" will result in OnStanza() and
38// "E:" will result in OnStreamEnd().
39class XmppStreamParser final {
40 public:
41  // Delegate interface that interested parties implement to receive
42  // notifications of stream opening/closing and on new stanzas arriving.
43  class Delegate {
44   public:
45    virtual void OnStreamStart(
46        const std::string& node_name,
47        std::map<std::string, std::string> attributes) = 0;
48    virtual void OnStreamEnd(const std::string& node_name) = 0;
49    virtual void OnStanza(std::unique_ptr<XmlNode> stanza) = 0;
50
51   protected:
52    virtual ~Delegate() {}
53  };
54
55  explicit XmppStreamParser(Delegate* delegate);
56  ~XmppStreamParser();
57
58  // Parses additional XML data received from an input stream.
59  void ParseData(const std::string& data);
60
61  // Resets the parser to expect the top-level stream node again.
62  void Reset();
63
64 private:
65  // Raw expat callbacks.
66  static void HandleElementStart(void* user_data,
67                                 const XML_Char* element,
68                                 const XML_Char** attr);
69  static void HandleElementEnd(void* user_data, const XML_Char* element);
70  static void HandleCharData(void* user_data, const char* content, int length);
71
72  // Reinterpreted callbacks from expat with some data pre-processed.
73  void OnOpenElement(const std::string& node_name,
74                     std::map<std::string, std::string> attributes);
75  void OnCloseElement(const std::string& node_name);
76  void OnCharData(const std::string& text);
77
78  Delegate* delegate_;
79  XML_Parser parser_{nullptr};
80  bool started_{false};
81  std::stack<std::unique_ptr<XmlNode>> node_stack_;
82
83  DISALLOW_COPY_AND_ASSIGN(XmppStreamParser);
84};
85
86}  // namespace weave
87
88#endif  // LIBWEAVE_SRC_NOTIFICATION_XMPP_STREAM_PARSER_H_
89