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