// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab ft=cpp #ifndef CEPH_RGW_XML_H #define CEPH_RGW_XML_H #include #include #include #include #include #include class XMLObj; class RGWXMLParser; class XMLObjIter { public: typedef map::iterator map_iter_t; typedef map::iterator const_map_iter_t; XMLObjIter(); ~XMLObjIter(); void set(const XMLObjIter::const_map_iter_t &_cur, const XMLObjIter::const_map_iter_t &_end); XMLObj *get_next(); bool get_name(std::string& name) const; private: map_iter_t cur; map_iter_t end; }; /** * Represents a block of XML. * Give the class an XML blob, and it will parse the blob into * an attr_name->value map. * It shouldn't be the start point for any parsing. Look at RGWXMLParser for that. */ class XMLObj { private: XMLObj *parent; std::string obj_type; protected: std::string data; std::multimap children; std::map attr_map; // invoked at the beginning of the XML tag, and populate any attributes bool xml_start(XMLObj *parent, const char *el, const char **attr); // callback invoked at the end of the XML tag // if objects are created while parsing, this should be overwritten in the drived class virtual bool xml_end(const char *el); // callback invoked for storing the data of the XML tag // if data manipulation is needed this could be overwritten in the drived class virtual void xml_handle_data(const char *s, int len); // get the parent object XMLObj *get_parent(); // add a child XML object void add_child(const std::string& el, XMLObj *obj); public: XMLObj() : parent(nullptr) {} virtual ~XMLObj(); // get the data (as string) const std::string& get_data() const; // get the type of the object (as string) const std::string& get_obj_type() const; bool get_attr(const std::string& name, std::string& attr) const; // return a list of sub-tags matching the name XMLObjIter find(const std::string& name); // return the first sub-tag XMLObjIter find_first(); // return the first sub-tags matching the name XMLObj *find_first(const std::string& name); friend ostream& operator<<(ostream &out, const XMLObj &obj); friend RGWXMLParser; }; struct XML_ParserStruct; // an XML parser is an XML object without a parent (root of the tree) // the parser could be used in 2 ways: // // (1) lazy object creation/intrusive API: usually used within the RGWXMLDecode namespace (as RGWXMLDecode::XMLParser) // the parser will parse the input and store info, but will not generate the target object. The object can be allocated outside // of the parser (stack or heap), and require to implement the decode_xml() API for the values to be populated. // note that the decode_xml() calls may throw exceptions if parsing fails // // (2) object creation while parsing: a new class needs to be derived from RGWXMLParser and implement alloc_obj() // API that should create a set of classes derived from XMLObj implementing xml_end() to create the actual target objects // // There could be a mix-and-match of the 2 types, control over that is in the alloc_obj() call // deciding for which tags objects are allocate during parsing and for which tags object allocation is external class RGWXMLParser : public XMLObj { private: XML_ParserStruct *p; char *buf; int buf_len; XMLObj *cur_obj; std::vector objs; std::list allocated_objs; std::list unallocated_objs; bool success; bool init_called; // calls xml_start() on each parsed object // passed as static callback to actual parser, passes itself as user_data static void call_xml_start(void* user_data, const char *el, const char **attr); // calls xml_end() on each parsed object // passed as static callback to actual parser, passes itself as user_data static void call_xml_end(void* user_data, const char *el); // calls xml_handle_data() on each parsed object // passed as static callback to actual parser, passes itself as user_data static void call_xml_handle_data(void* user_data, const char *s, int len); protected: // if objects are created while parsing, this should be implemented in the derived class // and be a factory for creating the classes derived from XMLObj // note that not all sub-tags has to be constructed here, any such tag which is not // constructed will be lazily created when decode_xml() is invoked on it // // note that in case of different tags sharing the same name at different levels // this method should not be used virtual XMLObj *alloc_obj(const char *el) { return nullptr; } public: RGWXMLParser(); ~RGWXMLParser() override; // initialize the parser, must be called before parsing bool init(); // parse the XML buffer (can be invoked multiple times for incremental parsing) // receives the buffer to parse, its length, and boolean indication (0,1) // whether this is the final chunk of the buffer bool parse(const char *buf, int len, int done); // get the XML blob being parsed const char *get_xml() const { return buf; } }; namespace RGWXMLDecoder { struct err : std::runtime_error { using runtime_error::runtime_error; }; typedef RGWXMLParser XMLParser; template bool decode_xml(const char *name, T& val, XMLObj* obj, bool mandatory = false); template bool decode_xml(const char *name, std::vector& v, XMLObj* obj, bool mandatory = false); template bool decode_xml(const char *name, C& container, void (*cb)(C&, XMLObj *obj), XMLObj *obj, bool mandatory = false); template void decode_xml(const char *name, T& val, T& default_val, XMLObj* obj); } static inline ostream& operator<<(ostream &out, RGWXMLDecoder::err& err) { return out << err.what(); } template void decode_xml_obj(T& val, XMLObj *obj) { val.decode_xml(obj); } static inline void decode_xml_obj(string& val, XMLObj *obj) { val = obj->get_data(); } void decode_xml_obj(unsigned long long& val, XMLObj *obj); void decode_xml_obj(long long& val, XMLObj *obj); void decode_xml_obj(unsigned long& val, XMLObj *obj); void decode_xml_obj(long& val, XMLObj *obj); void decode_xml_obj(unsigned& val, XMLObj *obj); void decode_xml_obj(int& val, XMLObj *obj); void decode_xml_obj(bool& val, XMLObj *obj); void decode_xml_obj(bufferlist& val, XMLObj *obj); class utime_t; void decode_xml_obj(utime_t& val, XMLObj *obj); template void decode_xml_obj(std::optional& val, XMLObj *obj) { val.emplace(); decode_xml_obj(*val, obj); } template void do_decode_xml_obj(list& l, const string& name, XMLObj *obj) { l.clear(); XMLObjIter iter = obj->find(name); XMLObj *o; while ((o = iter.get_next())) { T val; decode_xml_obj(val, o); l.push_back(val); } } template bool RGWXMLDecoder::decode_xml(const char *name, T& val, XMLObj *obj, bool mandatory) { XMLObjIter iter = obj->find(name); XMLObj *o = iter.get_next(); if (!o) { if (mandatory) { string s = "missing mandatory field " + string(name); throw err(s); } val = T(); return false; } try { decode_xml_obj(val, o); } catch (const err& e) { string s = string(name) + ": "; s.append(e.what()); throw err(s); } return true; } template bool RGWXMLDecoder::decode_xml(const char *name, std::vector& v, XMLObj *obj, bool mandatory) { XMLObjIter iter = obj->find(name); XMLObj *o = iter.get_next(); v.clear(); if (!o) { if (mandatory) { string s = "missing mandatory field " + string(name); throw err(s); } return false; } do { T val; try { decode_xml_obj(val, o); } catch (const err& e) { string s = string(name) + ": "; s.append(e.what()); throw err(s); } v.push_back(val); } while ((o = iter.get_next())); return true; } template bool RGWXMLDecoder::decode_xml(const char *name, C& container, void (*cb)(C&, XMLObj *), XMLObj *obj, bool mandatory) { container.clear(); XMLObjIter iter = obj->find(name); XMLObj *o = iter.get_next(); if (!o) { if (mandatory) { string s = "missing mandatory field " + string(name); throw err(s); } return false; } try { decode_xml_obj(container, cb, o); } catch (const err& e) { string s = string(name) + ": "; s.append(e.what()); throw err(s); } return true; } template void RGWXMLDecoder::decode_xml(const char *name, T& val, T& default_val, XMLObj *obj) { XMLObjIter iter = obj->find(name); XMLObj *o = iter.get_next(); if (!o) { val = default_val; return; } try { decode_xml_obj(val, o); } catch (const err& e) { val = default_val; string s = string(name) + ": "; s.append(e.what()); throw err(s); } } template static void encode_xml(const char *name, const T& val, ceph::Formatter *f) { f->open_object_section(name); val.dump_xml(f); f->close_section(); } template static void encode_xml(const char *name, const char *ns, const T& val, ceph::Formatter *f) { f->open_object_section_in_ns(name, ns); val.dump_xml(f); f->close_section(); } void encode_xml(const char *name, const string& val, ceph::Formatter *f); void encode_xml(const char *name, const char *val, ceph::Formatter *f); void encode_xml(const char *name, bool val, ceph::Formatter *f); void encode_xml(const char *name, int val, ceph::Formatter *f); void encode_xml(const char *name, unsigned val, ceph::Formatter *f); void encode_xml(const char *name, long val, ceph::Formatter *f); void encode_xml(const char *name, unsigned long val, ceph::Formatter *f); void encode_xml(const char *name, long long val, ceph::Formatter *f); void encode_xml(const char *name, const utime_t& val, ceph::Formatter *f); void encode_xml(const char *name, const bufferlist& bl, ceph::Formatter *f); void encode_xml(const char *name, long long unsigned val, ceph::Formatter *f); template static void do_encode_xml(const char *name, const std::list& l, const char *entry_name, ceph::Formatter *f) { f->open_array_section(name); for (typename std::list::const_iterator iter = l.begin(); iter != l.end(); ++iter) { encode_xml(entry_name, *iter, f); } f->close_section(); } template static void encode_xml(const char *name, const std::vector& l, ceph::Formatter *f) { for (typename std::vector::const_iterator iter = l.begin(); iter != l.end(); ++iter) { encode_xml(name, *iter, f); } } template static void encode_xml(const char *name, const std::optional& o, ceph::Formatter *f) { if (!o) { return; } encode_xml(name, *o, f); } #endif