]> git.mxchange.org Git - simgear.git/blob - simgear/io/DAVMultiStatus.cxx
Check for DAV status parse failures.
[simgear.git] / simgear / io / DAVMultiStatus.cxx
1 // DAVMultiStatus.cxx -- parser for WebDAV MultiStatus XML data
2 //
3 // Copyright (C) 2012  James Turner <zakalawe@mac.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #ifdef HAVE_CONFIG_H
20 #  include <simgear_config.h>
21 #endif
22      
23 #include "DAVMultiStatus.hxx"
24
25 #include <iostream>
26 #include <cstring>
27 #include <cassert>
28 #include <algorithm>
29 #include <sstream>
30
31 #include <boost/foreach.hpp>
32
33 #include "simgear/debug/logstream.hxx"
34 #include "simgear/misc/strutils.hxx"
35 #include "simgear/structure/exception.hxx"
36
37 #ifdef SYSTEM_EXPAT
38 #  include <expat.h>
39 #else
40 #  include "sg_expat.h"     
41 #endif
42
43 using std::string;
44
45 using namespace simgear;
46
47 #define DAV_NS "DAV::"
48 #define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
49
50 const char* DAV_MULTISTATUS_TAG = DAV_NS "multistatus";
51 const char* DAV_RESPONSE_TAG = DAV_NS "response";
52 const char* DAV_PROPSTAT_TAG = DAV_NS "propstat";
53 const char* DAV_PROP_TAG = DAV_NS "prop";
54
55 const char* DAV_HREF_TAG = DAV_NS "href";
56 const char* DAV_RESOURCE_TYPE_TAG = DAV_NS "resourcetype";
57 const char* DAV_CONTENT_TYPE_TAG = DAV_NS "getcontenttype";
58 const char* DAV_CONTENT_LENGTH_TAG = DAV_NS "getcontentlength";
59 const char* DAV_VERSIONNAME_TAG = DAV_NS "version-name";
60 const char* DAV_COLLECTION_TAG = DAV_NS "collection";
61 const char* DAV_VCC_TAG = DAV_NS "version-controlled-configuration";
62
63 const char* SUBVERSION_MD5_CHECKSUM_TAG = SUBVERSION_DAV_NS ":md5-checksum";
64
65 DAVResource::DAVResource(const string& href) :
66   _type(Unknown),
67   _url(href),
68   _container(NULL)
69 {
70     assert(!href.empty());
71     if (strutils::ends_with(href, "/")) {
72         _url = href.substr(0, _url.size() - 1);
73     }
74 }
75
76 void DAVResource::setVersionName(const std::string& aVersion)
77 {
78   _versionName = aVersion;
79 }
80
81 void DAVResource::setVersionControlledConfiguration(const std::string& vcc)
82 {
83     _vcc = vcc;
84 }
85
86 void DAVResource::setMD5(const std::string& md5Hex)
87 {
88     _md5 = md5Hex;
89 }
90
91 std::string DAVResource::name() const
92 {
93     string::size_type index = _url.rfind('/');
94     if (index != string::npos) {
95         return _url.substr(index + 1);
96     }
97     
98     throw sg_exception("bad DAV resource HREF:" + _url);
99 }
100
101 ////////////////////////////////////////////////////////////////////////////
102
103 DAVCollection::DAVCollection(const string& href) :
104   DAVResource(href)
105 {
106   _type = DAVResource::Collection;
107 }
108
109 DAVCollection::~DAVCollection()
110 {
111   BOOST_FOREACH(DAVResource* c, _contents) {
112     delete c;
113   }
114 }
115
116 void DAVCollection::addChild(DAVResource *res)
117 {
118   assert(res);
119   if (res->container() == this) {
120     return;
121   }
122   
123   assert(res->container() == NULL);
124   assert(std::find(_contents.begin(), _contents.end(), res) == _contents.end());
125   assert(strutils::starts_with(res->url(), _url));
126   assert(childWithUrl(res->url()) == NULL);
127   
128   res->_container = this;
129   _contents.push_back(res);
130 }
131
132 void DAVCollection::removeChild(DAVResource* res)
133 {
134   assert(res);
135   assert(res->container() == this);
136   
137   res->_container = NULL;
138   DAVResourceList::iterator it = std::find(_contents.begin(), _contents.end(), res);
139   assert(it != _contents.end());
140   _contents.erase(it);
141 }
142
143 DAVCollection*
144 DAVCollection::createChildCollection(const std::string& name)
145 {
146     DAVCollection* child = new DAVCollection(urlForChildWithName(name));
147     addChild(child);
148     return child;
149 }
150
151 DAVResourceList DAVCollection::contents() const
152 {
153   return _contents;
154 }
155
156 DAVResource* DAVCollection::childWithUrl(const string& url) const
157 {
158   if (url.empty())
159     return NULL;
160   
161   BOOST_FOREACH(DAVResource* c, _contents) {
162     if (c->url() == url) {
163       return c;
164     }
165   }
166   
167   return NULL;
168 }
169
170 DAVResource* DAVCollection::childWithName(const string& name) const
171 {
172   return childWithUrl(urlForChildWithName(name));
173 }
174
175 std::string DAVCollection::urlForChildWithName(const std::string& name) const
176 {
177   return url() + "/" + name;
178 }
179
180 ///////////////////////////////////////////////////////////////////////////////
181
182 class DAVMultiStatus::DAVMultiStatusPrivate
183 {
184 public:
185   DAVMultiStatusPrivate() :
186       parserInited(false),
187       valid(false)
188   {
189     rootResource = NULL;
190   }
191   
192   void startElement (const char * name)
193   {
194     if (tagStack.empty()) {
195       if (strcmp(name, DAV_MULTISTATUS_TAG)) {
196         SG_LOG(SG_IO, SG_WARN, "root element is not " <<
197                DAV_MULTISTATUS_TAG << ", got:" << name);
198       } else {
199         
200       }
201     } else {
202       // not at the root element
203       if (tagStack.back() == DAV_MULTISTATUS_TAG) {
204         if (strcmp(name, DAV_RESPONSE_TAG)) {
205           SG_LOG(SG_IO, SG_WARN, "multistatus child is not response: saw:"
206                  << name);
207         }
208       }
209       
210       if (tagStack.back() == DAV_RESOURCE_TYPE_TAG) {
211         if (!strcmp(name, DAV_COLLECTION_TAG)) {
212           currentElementType = DAVResource::Collection;
213         } else {
214           currentElementType = DAVResource::Unknown;
215         }
216       }
217     }
218     
219     tagStack.push_back(name);
220     if (!strcmp(name, DAV_RESPONSE_TAG)) {
221       currentElementType = DAVResource::Unknown;
222       currentElementUrl.clear();
223       currentElementMD5.clear();
224       currentVersionName.clear();
225       currentVCC.clear();
226     }
227   }
228   
229   void endElement (const char * name)
230   {
231     assert(tagStack.back() == name);
232     tagStack.pop_back();
233     
234     if (!strcmp(name, DAV_RESPONSE_TAG)) {
235       // finish complete response
236       currentElementUrl = strutils::strip(currentElementUrl);
237       
238       DAVResource* res = NULL;
239       if (currentElementType == DAVResource::Collection) {
240         DAVCollection* col = new DAVCollection(currentElementUrl);
241         res = col;
242       } else {
243         res = new DAVResource(currentElementUrl);
244       }
245       
246       res->setVersionName(strutils::strip(currentVersionName));
247       res->setVersionControlledConfiguration(currentVCC);
248       if (rootResource &&
249           strutils::starts_with(currentElementUrl, rootResource->url()))
250       {
251         static_cast<DAVCollection*>(rootResource)->addChild(res);
252       }
253       
254       if (!rootResource) {
255         rootResource = res;
256       }
257     }
258   }
259   
260   void data (const char * s, int length)
261   {
262     if (tagStack.back() == DAV_HREF_TAG) {
263       if (tagN(1) == DAV_RESPONSE_TAG) {
264         currentElementUrl += string(s, length);
265       } else if (tagN(1) == DAV_VCC_TAG) {
266         currentVCC += string(s, length);
267       }
268     } else if (tagStack.back() == SUBVERSION_MD5_CHECKSUM_TAG) {
269       currentElementMD5 = string(s, length);
270     } else if (tagStack.back() == DAV_VERSIONNAME_TAG) {
271       currentVersionName = string(s, length);
272     } else if (tagStack.back() == DAV_CONTENT_LENGTH_TAG) {
273       std::istringstream is(string(s, length));
274       is >> currentElementLength;
275     }
276   }
277   
278   void pi (const char * target, const char * data) {}
279   
280   string tagN(const unsigned int n) const
281   {
282     size_t sz = tagStack.size();
283     if (n >= sz) {
284       return string();
285     }
286     
287     return tagStack[sz - (1 + n)];
288   }
289   
290   bool parserInited;
291   bool valid;
292   XML_Parser xmlParser;
293   DAVResource* rootResource;
294   
295   // in-flight data
296   string_list tagStack;
297   DAVResource::Type currentElementType;
298   string currentElementUrl,
299     currentVersionName,
300     currentVCC;
301   int currentElementLength;
302   string currentElementMD5;
303 };
304
305
306 ////////////////////////////////////////////////////////////////////////
307 // Static callback functions for Expat.
308 ////////////////////////////////////////////////////////////////////////
309
310 #define VISITOR static_cast<DAVMultiStatus::DAVMultiStatusPrivate *>(userData)
311
312 static void
313 start_element (void * userData, const char * name, const char ** atts)
314 {
315   VISITOR->startElement(name);
316 }
317
318 static void
319 end_element (void * userData, const char * name)
320 {
321   VISITOR->endElement(name);
322 }
323
324 static void
325 character_data (void * userData, const char * s, int len)
326 {
327   VISITOR->data(s, len);
328 }
329
330 static void
331 processing_instruction (void * userData,
332                         const char * target,
333                         const char * data)
334 {
335   VISITOR->pi(target, data);
336 }
337
338 #undef VISITOR
339
340 ///////////////////////////////////////////////////////////////////////////////
341
342 DAVMultiStatus::DAVMultiStatus() :
343 _d(new DAVMultiStatusPrivate)
344 {
345   
346 }
347
348 DAVMultiStatus::~DAVMultiStatus()
349 {
350   
351 }
352
353 void DAVMultiStatus::parseXML(const char* data, int size)
354 {
355   if (!_d->parserInited) {
356     _d->xmlParser = XML_ParserCreateNS(0, ':');
357     XML_SetUserData(_d->xmlParser, _d.get());
358     XML_SetElementHandler(_d->xmlParser, start_element, end_element);
359     XML_SetCharacterDataHandler(_d->xmlParser, character_data);
360     XML_SetProcessingInstructionHandler(_d->xmlParser, processing_instruction);
361     _d->parserInited = true;
362   }
363   
364   if (!XML_Parse(_d->xmlParser, data, size, false)) {
365     SG_LOG(SG_IO, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
366            << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
367            << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
368     
369     XML_ParserFree(_d->xmlParser);
370     _d->parserInited = false;
371     _d->valid = false;
372   }
373 }
374
375 void DAVMultiStatus::finishParse()
376 {
377   if (_d->parserInited) {
378     if (!XML_Parse(_d->xmlParser, NULL, 0, true)) {
379         SG_LOG(SG_IO, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
380                << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
381                << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
382         _d->valid = false;
383     } else {
384         _d->valid = true;
385     }
386     XML_ParserFree(_d->xmlParser);
387   }
388   
389   _d->parserInited = false;
390 }
391
392 DAVResource* DAVMultiStatus::resource()
393 {
394   return _d->rootResource;
395 }
396
397 bool DAVMultiStatus::isValid() const
398 {
399     return _d->valid;
400 }
401
402