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