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 
17 namespace weave {
18 
19 class 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().
39 class 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:
~Delegate()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