Xerces 3 revisions.
[shibboleth/cpp-xmltooling.git] / xmltooling / exceptions.cpp
1 /*\r
2  *  Copyright 2001-2007 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * exceptions.cpp\r
19  * \r
20  * Exception classes\r
21  */\r
22  \r
23 #include "internal.h"\r
24 #include "exceptions.h"\r
25 #include "XMLToolingConfig.h"\r
26 #include "util/URLEncoder.h"\r
27 #include "util/XMLConstants.h"\r
28 #include "util/XMLHelper.h"\r
29 \r
30 #include <stdarg.h>\r
31 #include <memory>\r
32 #include <sstream>\r
33 #include <xercesc/util/XMLUniDefs.hpp>\r
34 \r
35 using namespace xmltooling;\r
36 using namespace xercesc;\r
37 using namespace std;\r
38 using xmlconstants::XMLTOOLING_NS;\r
39 \r
40 params::params(int count,...)\r
41 {\r
42     va_list args;\r
43     va_start(args,count);\r
44     while (count--)\r
45         v.push_back(va_arg(args,char*));\r
46     va_end(args);\r
47 }\r
48 \r
49 namedparams::namedparams(int count,...)\r
50 {\r
51     count*=2;\r
52     va_list args;\r
53     va_start(args,count);\r
54     while (count--)\r
55         v.push_back(va_arg(args,char*));\r
56     va_end(args);\r
57 }\r
58 \r
59 XMLToolingException::ExceptionFactoryMap XMLToolingException::m_factoryMap;\r
60 \r
61 XMLToolingException* XMLToolingException::getInstance(const char* exceptionClass)\r
62 {\r
63     if (exceptionClass) {\r
64         ExceptionFactoryMap::const_iterator i=m_factoryMap.find(exceptionClass);\r
65         if (i!=m_factoryMap.end())\r
66             return (i->second)();\r
67     }\r
68     return new XMLToolingException();\r
69 }\r
70 \r
71 XMLToolingException::XMLToolingException(const char* msg, const params& p)\r
72 {\r
73     if (msg)\r
74         m_msg=msg;\r
75     addProperties(p);\r
76 }\r
77 \r
78 XMLToolingException::XMLToolingException(const char* msg, const namedparams& p)\r
79 {\r
80     if (msg)\r
81         m_msg=msg;\r
82     addProperties(p);\r
83 }\r
84 \r
85 XMLToolingException::XMLToolingException(const std::string& msg, const params& p) : m_msg(msg)\r
86 {\r
87     addProperties(p);\r
88 }\r
89 \r
90 XMLToolingException::XMLToolingException(const std::string& msg, const namedparams& p) : m_msg(msg)\r
91 {\r
92     addProperties(p);\r
93 }\r
94 \r
95 void XMLToolingException::setMessage(const char* msg)\r
96 {\r
97     if (msg)\r
98         m_msg=msg;\r
99     else\r
100         m_msg.erase();\r
101     m_processedmsg.erase();\r
102 }\r
103 \r
104 inline const char* get_digit_character()\r
105 {\r
106     static const char  s_characters[19] = \r
107     {\r
108             '9'\r
109         ,   '8'\r
110         ,   '7'\r
111         ,   '6'\r
112         ,   '5'\r
113         ,   '4'\r
114         ,   '3'\r
115         ,   '2'\r
116         ,   '1'\r
117         ,   '0'\r
118         ,   '1'\r
119         ,   '2'\r
120         ,   '3'\r
121         ,   '4'\r
122         ,   '5'\r
123         ,   '6'\r
124         ,   '7'\r
125         ,   '8'\r
126         ,   '9'\r
127     };\r
128     static const char  *s_mid  =   s_characters + 9;\r
129 \r
130     return s_mid;\r
131 }\r
132 \r
133 inline const char* unsigned_integer_to_string(char* buf, size_t cchBuf, size_t i)\r
134 {\r
135     char* psz=buf + cchBuf - 1;     // Set psz to last char\r
136     *psz = 0;                       // Set terminating null\r
137 \r
138     do {\r
139         size_t lsd = i % 10;  // Get least significant\r
140                                     // digit\r
141 \r
142         i /= 10;                    // Prepare for next most\r
143                                     // significant digit\r
144 \r
145         --psz;                      // Move back\r
146 \r
147         *psz = get_digit_character()[lsd]; // Place the digit\r
148 \r
149     } while(i!=0 && psz>buf);\r
150 \r
151     return psz;\r
152 }\r
153 \r
154 void XMLToolingException::addProperties(const params& p)\r
155 {\r
156     m_processedmsg.erase();\r
157     map<string,string>::size_type i=m_params.size()+1;\r
158     char buf[20];\r
159     const vector<const char*>& v=p.get();\r
160     for (vector<const char*>::const_iterator ci=v.begin(); ci!=v.end(); ci++) {\r
161         m_params[unsigned_integer_to_string(buf,sizeof(buf),i++)] = *ci;\r
162     }\r
163 }\r
164         \r
165 void XMLToolingException::addProperties(const namedparams& p)\r
166 {\r
167     m_processedmsg.erase();\r
168     const vector<const char*>& v=p.get();\r
169     for (vector<const char*>::const_iterator ci=v.begin(); ci!=v.end(); ci++) {\r
170         m_params.erase(*ci);\r
171         m_params[*ci] = *(ci+1);\r
172         ci++;   // advance past name to value, then loop will advance it again\r
173     }\r
174 }\r
175 \r
176 const char* XMLToolingException::getProperty(unsigned int index) const\r
177 {\r
178     char buf[20];\r
179     map<string,string>::const_iterator i=m_params.find(unsigned_integer_to_string(buf,sizeof(buf),index));\r
180     return (i==m_params.end()) ? NULL : i->second.c_str();\r
181 }\r
182 \r
183 const char* XMLToolingException::getProperty(const char* name) const\r
184 {\r
185     map<string,string>::const_iterator i=m_params.find(name);\r
186     return (i==m_params.end()) ? NULL : i->second.c_str();\r
187 }\r
188 \r
189 const char* XMLToolingException::getMessage() const\r
190 {\r
191     if (!m_processedmsg.empty())\r
192         return m_processedmsg.c_str();\r
193     else if (m_params.empty())\r
194         return m_msg.c_str();\r
195 \r
196     static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_";\r
197 \r
198     // Replace any parameters in the message.\r
199     string::size_type i=0,start=0;\r
200     while (start!=string::npos && start<m_msg.length() && (i=m_msg.find("$",start))!=string::npos) {\r
201         if (i>start)\r
202             m_processedmsg += m_msg.substr(start,i-start);  // append everything in between\r
203         start=i+1;                                  // move start to the beginning of the token name\r
204         i=m_msg.find_first_not_of(legal,start);     // find token delimiter\r
205         if (i==start) {                             // append a non legal character\r
206            m_processedmsg+=m_msg[start++];\r
207            continue;\r
208         }\r
209         \r
210         // search for token in map\r
211         map<string,string>::const_iterator param=m_params.find(m_msg.substr(start,(i==string::npos) ? i : i-start));\r
212         if (param!=m_params.end()) {\r
213             m_processedmsg+=param->second;\r
214             start=i;\r
215         }\r
216     }\r
217     if (start!=string::npos && start<m_msg.length())\r
218         m_processedmsg += m_msg.substr(start,i);    // append rest of string\r
219     return m_processedmsg.c_str();\r
220 }\r
221 \r
222 void xml_encode(string& s, const char* pre, const char* start, const char* post)\r
223 {\r
224     s += pre;\r
225     size_t pos;\r
226     while (start && *start) {\r
227         pos = strcspn(start, "\"<>&");\r
228         if (pos > 0) {\r
229             s.append(start, pos);\r
230             start += pos;\r
231         }\r
232         else {\r
233             switch (*start) {\r
234                 case '\'':  s += "&apos;";     break;\r
235                 case '<':   s += "&lt;";       break;\r
236                 case '>':   s += "&gt;";       break;\r
237                 case '&':   s += "&amp;";      break;\r
238                 default:    s += *start;\r
239             }\r
240             start++;\r
241         }\r
242     }\r
243     s += post;\r
244 }\r
245 \r
246 string XMLToolingException::toString() const\r
247 {\r
248     string xml=string("<exception xmlns='http://www.opensaml.org/xmltooling' type='") + getClassName() + "'>";\r
249     const char* msg=getMessage();\r
250     if (msg)\r
251         xml_encode(xml, "<message>", msg, "</message>");\r
252     for (map<string,string>::const_iterator i=m_params.begin(); i!=m_params.end(); i++) {\r
253         xml_encode(xml, "<param name='", i->first.c_str(), "'");\r
254         xml_encode(xml, ">", i->second.c_str(), "</param>");\r
255     }\r
256     xml+="</exception>";\r
257     return xml;\r
258 }\r
259 \r
260 string XMLToolingException::toQueryString() const\r
261 {\r
262     string q;\r
263     const URLEncoder* enc = XMLToolingConfig::getConfig().getURLEncoder();\r
264     for (map<string,string>::const_iterator i=m_params.begin(); i!=m_params.end(); i++) {\r
265         if (!q.empty())\r
266             q += '&';\r
267         q = q + i->first + '=' + enc->encode(i->second.c_str());\r
268     }\r
269     return q;\r
270 }\r
271 \r
272 XMLToolingException* XMLToolingException::fromStream(std::istream& in)\r
273 {\r
274     static const XMLCh exception[] =    UNICODE_LITERAL_9(e,x,c,e,p,t,i,o,n);\r
275     static const XMLCh message[] =      UNICODE_LITERAL_7(m,e,s,s,a,g,e);\r
276     static const XMLCh name[] =         UNICODE_LITERAL_4(n,a,m,e);\r
277     static const XMLCh param[] =        UNICODE_LITERAL_5(p,a,r,a,m);\r
278     static const XMLCh type[] =         UNICODE_LITERAL_4(t,y,p,e);\r
279 \r
280     DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);\r
281     \r
282     // Check root element.\r
283     const DOMElement* root=doc->getDocumentElement();\r
284     if (!XMLHelper::isNodeNamed(root,XMLTOOLING_NS,exception)) {\r
285         doc->release();\r
286         throw XMLToolingException("Invalid root element on serialized exception.");\r
287     }\r
288     \r
289     auto_ptr_char classname(root->getAttributeNS(NULL,type));\r
290     auto_ptr<XMLToolingException> excep(XMLToolingException::getInstance(classname.get()));\r
291     \r
292     DOMElement* child=XMLHelper::getFirstChildElement(root,XMLTOOLING_NS,message);\r
293     if (child && child->hasChildNodes()) {\r
294         auto_ptr_char m(child->getFirstChild()->getNodeValue());\r
295         excep->setMessage(m.get());\r
296     }\r
297     \r
298     child=XMLHelper::getFirstChildElement(root,XMLTOOLING_NS,param);\r
299     while (child && child->hasChildNodes()) {\r
300         auto_ptr_char n(child->getAttributeNS(NULL,name));\r
301         char* v=toUTF8(child->getFirstChild()->getNodeValue());\r
302         if (n.get() && v)\r
303             excep->addProperty(n.get(), v);\r
304         delete[] v;\r
305         child=XMLHelper::getNextSiblingElement(child,XMLTOOLING_NS,param);\r
306     }\r
307 \r
308     doc->release();\r
309     return excep.release();\r
310 }\r
311         \r
312 XMLToolingException* XMLToolingException::fromString(const char* s)\r
313 {\r
314     istringstream in(s);\r
315     return fromStream(in);\r
316 }\r