]> git.mxchange.org Git - simgear.git/blob - simgear/io/DAVMultiStatus.cxx
HTTP SVN fixes, cap max update-report depth.
[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 #include "DAVMultiStatus.hxx"
20
21 #include <iostream>
22 #include <cstring>
23 #include <cassert>
24 #include <algorithm>
25 #include <sstream>
26
27 #include <boost/foreach.hpp>
28
29 #include "simgear/debug/logstream.hxx"
30 #include "simgear/xml/xmlparse.h"
31 #include "simgear/misc/strutils.hxx"
32 #include "simgear/structure/exception.hxx"
33
34 using std::string;
35
36 using namespace simgear;
37
38 #define DAV_NS "DAV::"
39 #define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
40
41 const char* DAV_MULTISTATUS_TAG = DAV_NS "multistatus";
42 const char* DAV_RESPONSE_TAG = DAV_NS "response";
43 const char* DAV_PROPSTAT_TAG = DAV_NS "propstat";
44 const char* DAV_PROP_TAG = DAV_NS "prop";
45
46 const char* DAV_HREF_TAG = DAV_NS "href";
47 const char* DAV_RESOURCE_TYPE_TAG = DAV_NS "resourcetype";
48 const char* DAV_CONTENT_TYPE_TAG = DAV_NS "getcontenttype";
49 const char* DAV_CONTENT_LENGTH_TAG = DAV_NS "getcontentlength";
50 const char* DAV_VERSIONNAME_TAG = DAV_NS "version-name";
51 const char* DAV_COLLECTION_TAG = DAV_NS "collection";
52 const char* DAV_VCC_TAG = DAV_NS "version-controlled-configuration";
53
54 const char* SUBVERSION_MD5_CHECKSUM_TAG = SUBVERSION_DAV_NS ":md5-checksum";
55
56 DAVResource::DAVResource(const string& href) :
57   _type(Unknown),
58   _url(href),
59   _container(NULL)
60 {
61     assert(!href.empty());
62     if (strutils::ends_with(href, "/")) {
63         _url = href.substr(0, _url.size() - 1);
64     }
65 }
66
67 void DAVResource::setVersionName(const std::string& aVersion)
68 {
69   _versionName = aVersion;
70 }
71
72 void DAVResource::setVersionControlledConfiguration(const std::string& vcc)
73 {
74     _vcc = vcc;
75 }
76
77 void DAVResource::setMD5(const std::string& md5Hex)
78 {
79     _md5 = md5Hex;
80 }
81
82 std::string DAVResource::name() const
83 {
84     string::size_type index = _url.rfind('/');
85     if (index != string::npos) {
86         return _url.substr(index + 1);
87     }
88     
89     throw sg_exception("bad DAV resource HREF:" + _url);
90 }
91
92 ////////////////////////////////////////////////////////////////////////////
93
94 DAVCollection::DAVCollection(const string& href) :
95   DAVResource(href)
96 {
97   _type = DAVResource::Collection;
98 }
99
100 DAVCollection::~DAVCollection()
101 {
102   BOOST_FOREACH(DAVResource* c, _contents) {
103     delete c;
104   }
105 }
106
107 void DAVCollection::addChild(DAVResource *res)
108 {
109   assert(res);
110   if (res->container() == this) {
111     return;
112   }
113   
114   assert(res->container() == NULL);
115   assert(std::find(_contents.begin(), _contents.end(), res) == _contents.end());
116   assert(strutils::starts_with(res->url(), _url));
117   assert(childWithUrl(res->url()) == NULL);
118   
119   res->_container = this;
120   _contents.push_back(res);
121 }
122
123 void DAVCollection::removeChild(DAVResource* res)
124 {
125   assert(res);
126   assert(res->container() == this);
127   
128   res->_container = NULL;
129   DAVResourceList::iterator it = std::find(_contents.begin(), _contents.end(), res);
130   assert(it != _contents.end());
131   _contents.erase(it);
132 }
133
134 DAVCollection*
135 DAVCollection::createChildCollection(const std::string& name)
136 {
137     DAVCollection* child = new DAVCollection(urlForChildWithName(name));
138     addChild(child);
139     return child;
140 }
141
142 DAVResourceList DAVCollection::contents() const
143 {
144   return _contents;
145 }
146
147 DAVResource* DAVCollection::childWithUrl(const string& url) const
148 {
149   if (url.empty())
150     return NULL;
151   
152   BOOST_FOREACH(DAVResource* c, _contents) {
153     if (c->url() == url) {
154       return c;
155     }
156   }
157   
158   return NULL;
159 }
160
161 DAVResource* DAVCollection::childWithName(const string& name) const
162 {
163   return childWithUrl(urlForChildWithName(name));
164 }
165
166 std::string DAVCollection::urlForChildWithName(const std::string& name) const
167 {
168   return url() + "/" + name;
169 }
170
171 ///////////////////////////////////////////////////////////////////////////////
172
173 class DAVMultiStatus::DAVMultiStatusPrivate
174 {
175 public:
176   DAVMultiStatusPrivate() :
177   parserInited(false)
178   {
179     rootResource = NULL;
180   }
181   
182   void startElement (const char * name)
183   {
184     if (tagStack.empty()) {
185       if (strcmp(name, DAV_MULTISTATUS_TAG)) {
186         SG_LOG(SG_IO, SG_WARN, "root element is not " <<
187                DAV_MULTISTATUS_TAG << ", got:" << name);
188       } else {
189         
190       }
191     } else {
192       // not at the root element
193       if (tagStack.back() == DAV_MULTISTATUS_TAG) {
194         if (strcmp(name, DAV_RESPONSE_TAG)) {
195           SG_LOG(SG_IO, SG_WARN, "multistatus child is not response: saw:"
196                  << name);
197         }
198       }
199       
200       if (tagStack.back() == DAV_RESOURCE_TYPE_TAG) {
201         if (!strcmp(name, DAV_COLLECTION_TAG)) {
202           currentElementType = DAVResource::Collection;
203         } else {
204           currentElementType = DAVResource::Unknown;
205         }
206       }
207     }
208     
209     tagStack.push_back(name);
210     if (!strcmp(name, DAV_RESPONSE_TAG)) {
211       currentElementType = DAVResource::Unknown;
212       currentElementUrl.clear();
213       currentElementMD5.clear();
214       currentVersionName.clear();
215       currentVCC.clear();
216     }
217   }
218   
219   void endElement (const char * name)
220   {
221     assert(tagStack.back() == name);
222     tagStack.pop_back();
223     
224     if (!strcmp(name, DAV_RESPONSE_TAG)) {
225       // finish complete response
226       currentElementUrl = strutils::strip(currentElementUrl);
227       
228       DAVResource* res = NULL;
229       if (currentElementType == DAVResource::Collection) {
230         DAVCollection* col = new DAVCollection(currentElementUrl);
231         res = col;
232       } else {
233         res = new DAVResource(currentElementUrl);
234       }
235       
236       res->setVersionName(strutils::strip(currentVersionName));
237       res->setVersionControlledConfiguration(currentVCC);
238       if (rootResource &&
239           strutils::starts_with(currentElementUrl, rootResource->url()))
240       {
241         static_cast<DAVCollection*>(rootResource)->addChild(res);
242       }
243       
244       if (!rootResource) {
245         rootResource = res;
246       }
247     }
248   }
249   
250   void data (const char * s, int length)
251   {
252     if (tagStack.back() == DAV_HREF_TAG) {
253       if (tagN(1) == DAV_RESPONSE_TAG) {
254         currentElementUrl += string(s, length);
255       } else if (tagN(1) == DAV_VCC_TAG) {
256         currentVCC += string(s, length);
257       }
258     } else if (tagStack.back() == SUBVERSION_MD5_CHECKSUM_TAG) {
259       currentElementMD5 = string(s, length);
260     } else if (tagStack.back() == DAV_VERSIONNAME_TAG) {
261       currentVersionName = string(s, length);
262     } else if (tagStack.back() == DAV_CONTENT_LENGTH_TAG) {
263       std::istringstream is(string(s, length));
264       is >> currentElementLength;
265     }
266   }
267   
268   void pi (const char * target, const char * data) {}
269   
270   string tagN(const unsigned int n) const
271   {
272     size_t sz = tagStack.size();
273     if (n >= sz) {
274       return string();
275     }
276     
277     return tagStack[sz - (1 + n)];
278   }
279   
280   bool parserInited;
281   XML_Parser xmlParser;
282   DAVResource* rootResource;
283   
284   // in-flight data
285   string_list tagStack;
286   DAVResource::Type currentElementType;
287   string currentElementUrl,
288     currentVersionName,
289     currentVCC;
290   int currentElementLength;
291   string currentElementMD5;
292 };
293
294
295 ////////////////////////////////////////////////////////////////////////
296 // Static callback functions for Expat.
297 ////////////////////////////////////////////////////////////////////////
298
299 #define VISITOR static_cast<DAVMultiStatus::DAVMultiStatusPrivate *>(userData)
300
301 static void
302 start_element (void * userData, const char * name, const char ** atts)
303 {
304   VISITOR->startElement(name);
305 }
306
307 static void
308 end_element (void * userData, const char * name)
309 {
310   VISITOR->endElement(name);
311 }
312
313 static void
314 character_data (void * userData, const char * s, int len)
315 {
316   VISITOR->data(s, len);
317 }
318
319 static void
320 processing_instruction (void * userData,
321                         const char * target,
322                         const char * data)
323 {
324   VISITOR->pi(target, data);
325 }
326
327 #undef VISITOR
328
329 ///////////////////////////////////////////////////////////////////////////////
330
331 DAVMultiStatus::DAVMultiStatus() :
332 _d(new DAVMultiStatusPrivate)
333 {
334   
335 }
336
337 DAVMultiStatus::~DAVMultiStatus()
338 {
339   
340 }
341
342 void DAVMultiStatus::parseXML(const char* data, int size)
343 {
344   if (!_d->parserInited) {
345     _d->xmlParser = XML_ParserCreateNS(0, ':');
346     XML_SetUserData(_d->xmlParser, _d.get());
347     XML_SetElementHandler(_d->xmlParser, start_element, end_element);
348     XML_SetCharacterDataHandler(_d->xmlParser, character_data);
349     XML_SetProcessingInstructionHandler(_d->xmlParser, processing_instruction);
350     _d->parserInited = true;
351   }
352   
353   if (!XML_Parse(_d->xmlParser, data, size, false)) {
354     SG_LOG(SG_IO, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
355            << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
356            << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
357     
358     XML_ParserFree(_d->xmlParser);
359     _d->parserInited = false;
360   }
361 }
362
363 void DAVMultiStatus::finishParse()
364 {
365   if (_d->parserInited) {
366     XML_Parse(_d->xmlParser, NULL, 0, true);
367     XML_ParserFree(_d->xmlParser);
368   }
369   
370   _d->parserInited = false;
371 }
372
373 DAVResource* DAVMultiStatus::resource()
374 {
375   return _d->rootResource;
376 }
377
378