Home | History | Annotate | Download | only in notification
      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:
     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