1 // DAVMultiStatus.cxx -- parser for WebDAV MultiStatus XML data
3 // Copyright (C) 2012 James Turner <zakalawe@mac.com>
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.
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.
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.
20 # include <simgear_config.h>
23 #include "DAVMultiStatus.hxx"
31 #include <boost/foreach.hpp>
33 #include "simgear/debug/logstream.hxx"
34 #include "simgear/misc/strutils.hxx"
35 #include "simgear/structure/exception.hxx"
40 # include "sg_expat.h"
45 using namespace simgear;
47 #define DAV_NS "DAV::"
48 #define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
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";
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";
63 const char* SUBVERSION_MD5_CHECKSUM_TAG = SUBVERSION_DAV_NS ":md5-checksum";
65 DAVResource::DAVResource(const string& href) :
70 assert(!href.empty());
71 if (strutils::ends_with(href, "/")) {
72 _url = href.substr(0, _url.size() - 1);
76 void DAVResource::setVersionName(const std::string& aVersion)
78 _versionName = aVersion;
81 void DAVResource::setVersionControlledConfiguration(const std::string& vcc)
86 void DAVResource::setMD5(const std::string& md5Hex)
91 std::string DAVResource::name() const
93 string::size_type index = _url.rfind('/');
94 if (index != string::npos) {
95 return _url.substr(index + 1);
98 throw sg_exception("bad DAV resource HREF:" + _url);
101 ////////////////////////////////////////////////////////////////////////////
103 DAVCollection::DAVCollection(const string& href) :
106 _type = DAVResource::Collection;
109 DAVCollection::~DAVCollection()
111 BOOST_FOREACH(DAVResource* c, _contents) {
116 void DAVCollection::addChild(DAVResource *res)
119 if (res->container() == this) {
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);
128 res->_container = this;
129 _contents.push_back(res);
132 void DAVCollection::removeChild(DAVResource* res)
135 assert(res->container() == this);
137 res->_container = NULL;
138 DAVResourceList::iterator it = std::find(_contents.begin(), _contents.end(), res);
139 assert(it != _contents.end());
144 DAVCollection::createChildCollection(const std::string& name)
146 DAVCollection* child = new DAVCollection(urlForChildWithName(name));
151 DAVResourceList DAVCollection::contents() const
156 DAVResource* DAVCollection::childWithUrl(const string& url) const
161 BOOST_FOREACH(DAVResource* c, _contents) {
162 if (c->url() == url) {
170 DAVResource* DAVCollection::childWithName(const string& name) const
172 return childWithUrl(urlForChildWithName(name));
175 std::string DAVCollection::urlForChildWithName(const std::string& name) const
177 return url() + "/" + name;
180 ///////////////////////////////////////////////////////////////////////////////
182 class DAVMultiStatus::DAVMultiStatusPrivate
185 DAVMultiStatusPrivate() :
192 void startElement (const char * name)
194 if (tagStack.empty()) {
195 if (strcmp(name, DAV_MULTISTATUS_TAG)) {
196 SG_LOG(SG_TERRASYNC, SG_WARN, "root element is not " <<
197 DAV_MULTISTATUS_TAG << ", got:" << name);
202 // not at the root element
203 if (tagStack.back() == DAV_MULTISTATUS_TAG) {
204 if (strcmp(name, DAV_RESPONSE_TAG)) {
205 SG_LOG(SG_TERRASYNC, SG_WARN, "multistatus child is not response: saw:"
210 if (tagStack.back() == DAV_RESOURCE_TYPE_TAG) {
211 if (!strcmp(name, DAV_COLLECTION_TAG)) {
212 currentElementType = DAVResource::Collection;
214 currentElementType = DAVResource::Unknown;
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();
229 void endElement (const char * name)
231 assert(tagStack.back() == name);
234 if (!strcmp(name, DAV_RESPONSE_TAG)) {
235 // finish complete response
236 currentElementUrl = strutils::strip(currentElementUrl);
238 DAVResource* res = NULL;
239 if (currentElementType == DAVResource::Collection) {
240 DAVCollection* col = new DAVCollection(currentElementUrl);
243 res = new DAVResource(currentElementUrl);
246 res->setVersionName(strutils::strip(currentVersionName));
247 res->setVersionControlledConfiguration(currentVCC);
249 strutils::starts_with(currentElementUrl, rootResource->url()))
251 static_cast<DAVCollection*>(rootResource)->addChild(res);
260 void data (const char * s, int length)
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);
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;
278 void pi (const char * target, const char * data) {}
280 string tagN(const unsigned int n) const
282 size_t sz = tagStack.size();
287 return tagStack[sz - (1 + n)];
292 XML_Parser xmlParser;
293 DAVResource* rootResource;
296 string_list tagStack;
297 DAVResource::Type currentElementType;
298 string currentElementUrl,
301 int currentElementLength;
302 string currentElementMD5;
306 ////////////////////////////////////////////////////////////////////////
307 // Static callback functions for Expat.
308 ////////////////////////////////////////////////////////////////////////
310 #define VISITOR static_cast<DAVMultiStatus::DAVMultiStatusPrivate *>(userData)
313 start_element (void * userData, const char * name, const char ** atts)
315 VISITOR->startElement(name);
319 end_element (void * userData, const char * name)
321 VISITOR->endElement(name);
325 character_data (void * userData, const char * s, int len)
327 VISITOR->data(s, len);
331 processing_instruction (void * userData,
335 VISITOR->pi(target, data);
340 ///////////////////////////////////////////////////////////////////////////////
342 DAVMultiStatus::DAVMultiStatus() :
343 _d(new DAVMultiStatusPrivate)
348 DAVMultiStatus::~DAVMultiStatus()
353 void DAVMultiStatus::parseXML(const char* data, int size)
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;
364 if (!XML_Parse(_d->xmlParser, data, size, false)) {
365 SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
366 << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
367 << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
369 XML_ParserFree(_d->xmlParser);
370 _d->parserInited = false;
375 void DAVMultiStatus::finishParse()
377 if (_d->parserInited) {
378 if (!XML_Parse(_d->xmlParser, NULL, 0, true)) {
379 SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
380 << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
381 << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
386 XML_ParserFree(_d->xmlParser);
389 _d->parserInited = false;
392 DAVResource* DAVMultiStatus::resource()
394 return _d->rootResource;
397 bool DAVMultiStatus::isValid() const