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