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