1 /* xml++.cc
2  * libxml++ and this file are copyright (C) 2000 by Ari Johnson
3  * (C) 2002-2004 by the libxml dev team and
4  * are covered by the GNU Lesser General Public License, which should be
5  * included with libxml++ as the file COPYING.
6  */
7 
8 #include "libxml++/exceptions/wrapped_exception.h"
9 #include "libxml++/validators/validator.h"
10 
11 #include <libxml/parser.h>
12 
13 #include <cstdarg> //For va_list.
14 #include <memory> //For unique_ptr.
15 
16 namespace xmlpp {
17 
Validator()18 Validator::Validator()
19 : valid_(nullptr), exception_(nullptr)
20 {
21 }
22 
~Validator()23 Validator::~Validator()
24 {
25   release_underlying();
26 }
27 
initialize_valid()28 void Validator::initialize_valid()
29 {
30   // valid_ is used only by DtdValidator.
31   //TODO: When we can break ABI, move valid_ to DtdValidator.
32   if (valid_)
33   {
34     //Tell the validation context about the callbacks:
35     valid_->error = &callback_validity_error;
36     valid_->warning = &callback_validity_warning;
37 
38     //Allow the callback_validity_*() methods to retrieve the C++ instance:
39     valid_->userData = this;
40   }
41 
42   //Clear these temporary buffers too:
43   validate_error_.erase();
44   validate_warning_.erase();
45 }
46 
release_underlying()47 void Validator::release_underlying()
48 {
49   if(valid_)
50   {
51     valid_->userData = nullptr; //Not really necessary.
52 
53     xmlFreeValidCtxt(valid_);
54     valid_ = nullptr;
55   }
56 }
57 
on_validity_error(const Glib::ustring & message)58 void Validator::on_validity_error(const Glib::ustring& message)
59 {
60   //Throw an exception later when the whole message has been received:
61   validate_error_ += message;
62 }
63 
on_validity_warning(const Glib::ustring & message)64 void Validator::on_validity_warning(const Glib::ustring& message)
65 {
66   //Throw an exception later when the whole message has been received:
67   validate_warning_ += message;
68 }
69 
check_for_validity_messages()70 void Validator::check_for_validity_messages()
71 {
72   Glib::ustring msg(exception_ ? exception_->what() : "");
73   bool validity_msg = false;
74 
75   if (!validate_error_.empty())
76   {
77     validity_msg = true;
78     msg += "\nValidity error:\n" + validate_error_;
79     validate_error_.erase();
80   }
81 
82   if (!validate_warning_.empty())
83   {
84     validity_msg = true;
85     msg += "\nValidity warning:\n" + validate_warning_;
86     validate_warning_.erase();
87   }
88 
89   if (validity_msg)
90   {
91     delete exception_;
92     exception_ = new validity_error(msg);
93   }
94 }
95 
callback_validity_error(void * valid_,const char * msg,...)96 void Validator::callback_validity_error(void* valid_, const char* msg, ...)
97 {
98   auto validator = static_cast<Validator*>(valid_);
99 
100   if(validator)
101   {
102     //Convert the ... to a string:
103     va_list arg;
104     char buff[1024]; //TODO: Larger/Shared
105 
106     va_start(arg, msg);
107     vsnprintf(buff, sizeof(buff)/sizeof(buff[0]), msg, arg);
108     va_end(arg);
109 
110     try
111     {
112       validator->on_validity_error(Glib::ustring(buff));
113     }
114     catch (...)
115     {
116       validator->handle_exception();
117     }
118   }
119 }
120 
callback_validity_warning(void * valid_,const char * msg,...)121 void Validator::callback_validity_warning(void* valid_, const char* msg, ...)
122 {
123   auto validator = static_cast<Validator*>(valid_);
124 
125   if(validator)
126   {
127     //Convert the ... to a string:
128     va_list arg;
129     char buff[1024]; //TODO: Larger/Shared
130 
131     va_start(arg, msg);
132     vsnprintf(buff, sizeof(buff)/sizeof(buff[0]), msg, arg);
133     va_end(arg);
134 
135     try
136     {
137       validator->on_validity_warning(Glib::ustring(buff));
138     }
139     catch (...)
140     {
141       validator->handle_exception();
142     }
143   }
144 }
145 
handleException(const exception & e)146 void Validator::handleException(const exception& e)
147 {
148   delete exception_;
149   exception_ = e.Clone();
150 
151   // Don't delete the DTD validation context or schema validation context
152   // while validating. It would cause accesses to deallocated memory in libxml2
153   // functions after the return from Validator::callback_validity_...().
154   // Parser::handleException() calls xmlStopParser(), but there is no
155   // xmlStopValidator() or similar function to call here.
156   // We don't throw the exception here, since it would have to pass through
157   // C functions. That's not guaranteed to work. It might work, but it depends
158   // on the C compiler and the options used when building libxml2.
159 
160   //release_underlying();
161 }
162 
handle_exception()163 void Validator::handle_exception()
164 {
165   delete exception_;
166   exception_ = nullptr;
167 
168   try
169   {
170     throw; // Rethrow current exception
171   }
172   catch (const exception& e)
173   {
174     exception_ = e.Clone();
175   }
176 #ifdef LIBXMLXX_HAVE_EXCEPTION_PTR
177   catch (...)
178   {
179     exception_ = new wrapped_exception(std::current_exception());
180   }
181 #else
182   catch (const std::exception& e)
183   {
184     exception_ = new exception(e.what());
185   }
186   catch (...)
187   {
188     exception_ = new exception("An exception was thrown that is not derived from std::exception or xmlpp::exception.\n"
189       "It could not be caught and rethrown because this platform does not support std::exception_ptr.");
190   }
191 #endif
192 
193   // Don't delete the DTD validation context or schema validation context
194   // while validating. It would cause accesses to deallocated memory in libxml2
195   // functions after the return from Validator::callback_validity_...().
196   // Parser::handle_exception() calls xmlStopParser(), but there is no
197   // xmlStopValidator() or similar function to call here.
198   // We don't throw the exception here, since it would have to pass through
199   // C functions. That's not guaranteed to work. It might work, but it depends
200   // on the C compiler and the options used when building libxml2.
201 
202   //release_underlying();
203 }
204 
check_for_exception()205 void Validator::check_for_exception()
206 {
207   check_for_validity_messages();
208 
209   if(exception_)
210   {
211     std::unique_ptr<exception> tmp(exception_);
212     exception_ = nullptr;
213     tmp->Raise();
214   }
215 }
216 
217 } // namespace xmlpp
218