HTTPMemoryRequest.hxx
HTTPRequest.hxx
AbstractRepository.hxx
- DAVMultiStatus.hxx
- SVNRepository.hxx
- SVNDirectory.hxx
- SVNReportParser.hxx
HTTPRepository.hxx
)
HTTPMemoryRequest.cxx
HTTPRequest.cxx
AbstractRepository.cxx
- DAVMultiStatus.cxx
- SVNRepository.cxx
- SVNDirectory.cxx
- SVNReportParser.cxx
HTTPRepository.cxx
)
if(ENABLE_TESTS)
-add_executable(http_svn http_svn.cxx)
-target_link_libraries(http_svn ${TEST_LIBS})
-
add_executable(test_sock socktest.cxx)
target_link_libraries(test_sock ${TEST_LIBS})
+++ /dev/null
-// DAVMultiStatus.cxx -- parser for WebDAV MultiStatus XML data
-//
-// Copyright (C) 2012 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-# include <simgear_config.h>
-#endif
-
-#include "DAVMultiStatus.hxx"
-
-#include <iostream>
-#include <cstring>
-#include <cassert>
-#include <algorithm>
-#include <sstream>
-
-#include <boost/foreach.hpp>
-
-#include "simgear/debug/logstream.hxx"
-#include "simgear/misc/strutils.hxx"
-#include "simgear/structure/exception.hxx"
-
-#ifdef SYSTEM_EXPAT
-# include <expat.h>
-#else
-# include "sg_expat.h"
-#endif
-
-using std::string;
-
-using namespace simgear;
-
-#define DAV_NS "DAV::"
-#define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
-
-const char* DAV_MULTISTATUS_TAG = DAV_NS "multistatus";
-const char* DAV_RESPONSE_TAG = DAV_NS "response";
-const char* DAV_PROPSTAT_TAG = DAV_NS "propstat";
-const char* DAV_PROP_TAG = DAV_NS "prop";
-
-const char* DAV_HREF_TAG = DAV_NS "href";
-const char* DAV_RESOURCE_TYPE_TAG = DAV_NS "resourcetype";
-const char* DAV_CONTENT_TYPE_TAG = DAV_NS "getcontenttype";
-const char* DAV_CONTENT_LENGTH_TAG = DAV_NS "getcontentlength";
-const char* DAV_VERSIONNAME_TAG = DAV_NS "version-name";
-const char* DAV_COLLECTION_TAG = DAV_NS "collection";
-const char* DAV_VCC_TAG = DAV_NS "version-controlled-configuration";
-
-const char* SUBVERSION_MD5_CHECKSUM_TAG = SUBVERSION_DAV_NS ":md5-checksum";
-
-DAVResource::DAVResource(const string& href) :
- _type(Unknown),
- _url(href),
- _container(NULL)
-{
- assert(!href.empty());
- if (strutils::ends_with(href, "/")) {
- _url = href.substr(0, _url.size() - 1);
- }
-}
-
-void DAVResource::setVersionName(const std::string& aVersion)
-{
- _versionName = aVersion;
-}
-
-void DAVResource::setVersionControlledConfiguration(const std::string& vcc)
-{
- _vcc = vcc;
-}
-
-void DAVResource::setMD5(const std::string& md5Hex)
-{
- _md5 = md5Hex;
-}
-
-std::string DAVResource::name() const
-{
- string::size_type index = _url.rfind('/');
- if (index != string::npos) {
- return _url.substr(index + 1);
- }
-
- throw sg_exception("bad DAV resource HREF:" + _url);
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-DAVCollection::DAVCollection(const string& href) :
- DAVResource(href)
-{
- _type = DAVResource::Collection;
-}
-
-DAVCollection::~DAVCollection()
-{
- BOOST_FOREACH(DAVResource* c, _contents) {
- delete c;
- }
-}
-
-void DAVCollection::addChild(DAVResource *res)
-{
- assert(res);
- if (res->container() == this) {
- return;
- }
-
- assert(res->container() == NULL);
- assert(std::find(_contents.begin(), _contents.end(), res) == _contents.end());
- assert(strutils::starts_with(res->url(), _url));
- assert(childWithUrl(res->url()) == NULL);
-
- res->_container = this;
- _contents.push_back(res);
-}
-
-void DAVCollection::removeChild(DAVResource* res)
-{
- assert(res);
- assert(res->container() == this);
-
- res->_container = NULL;
- DAVResourceList::iterator it = std::find(_contents.begin(), _contents.end(), res);
- assert(it != _contents.end());
- _contents.erase(it);
-}
-
-DAVCollection*
-DAVCollection::createChildCollection(const std::string& name)
-{
- DAVCollection* child = new DAVCollection(urlForChildWithName(name));
- addChild(child);
- return child;
-}
-
-DAVResourceList DAVCollection::contents() const
-{
- return _contents;
-}
-
-DAVResource* DAVCollection::childWithUrl(const string& url) const
-{
- if (url.empty())
- return NULL;
-
- BOOST_FOREACH(DAVResource* c, _contents) {
- if (c->url() == url) {
- return c;
- }
- }
-
- return NULL;
-}
-
-DAVResource* DAVCollection::childWithName(const string& name) const
-{
- return childWithUrl(urlForChildWithName(name));
-}
-
-std::string DAVCollection::urlForChildWithName(const std::string& name) const
-{
- return url() + "/" + name;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class DAVMultiStatus::DAVMultiStatusPrivate
-{
-public:
- DAVMultiStatusPrivate() :
- parserInited(false),
- valid(false)
- {
- rootResource = NULL;
- }
-
- void startElement (const char * name)
- {
- if (tagStack.empty()) {
- if (strcmp(name, DAV_MULTISTATUS_TAG)) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "root element is not " <<
- DAV_MULTISTATUS_TAG << ", got:" << name);
- } else {
-
- }
- } else {
- // not at the root element
- if (tagStack.back() == DAV_MULTISTATUS_TAG) {
- if (strcmp(name, DAV_RESPONSE_TAG)) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "multistatus child is not response: saw:"
- << name);
- }
- }
-
- if (tagStack.back() == DAV_RESOURCE_TYPE_TAG) {
- if (!strcmp(name, DAV_COLLECTION_TAG)) {
- currentElementType = DAVResource::Collection;
- } else {
- currentElementType = DAVResource::Unknown;
- }
- }
- }
-
- tagStack.push_back(name);
- if (!strcmp(name, DAV_RESPONSE_TAG)) {
- currentElementType = DAVResource::Unknown;
- currentElementUrl.clear();
- currentElementMD5.clear();
- currentVersionName.clear();
- currentVCC.clear();
- }
- }
-
- void endElement (const char * name)
- {
- assert(tagStack.back() == name);
- tagStack.pop_back();
-
- if (!strcmp(name, DAV_RESPONSE_TAG)) {
- // finish complete response
- currentElementUrl = strutils::strip(currentElementUrl);
-
- DAVResource* res = NULL;
- if (currentElementType == DAVResource::Collection) {
- DAVCollection* col = new DAVCollection(currentElementUrl);
- res = col;
- } else {
- res = new DAVResource(currentElementUrl);
- }
-
- res->setVersionName(strutils::strip(currentVersionName));
- res->setVersionControlledConfiguration(currentVCC);
- if (rootResource &&
- strutils::starts_with(currentElementUrl, rootResource->url()))
- {
- static_cast<DAVCollection*>(rootResource)->addChild(res);
- }
-
- if (!rootResource) {
- rootResource = res;
- }
- }
- }
-
- void data (const char * s, int length)
- {
- if (tagStack.back() == DAV_HREF_TAG) {
- if (tagN(1) == DAV_RESPONSE_TAG) {
- currentElementUrl += string(s, length);
- } else if (tagN(1) == DAV_VCC_TAG) {
- currentVCC += string(s, length);
- }
- } else if (tagStack.back() == SUBVERSION_MD5_CHECKSUM_TAG) {
- currentElementMD5 = string(s, length);
- } else if (tagStack.back() == DAV_VERSIONNAME_TAG) {
- currentVersionName = string(s, length);
- } else if (tagStack.back() == DAV_CONTENT_LENGTH_TAG) {
- std::istringstream is(string(s, length));
- is >> currentElementLength;
- }
- }
-
- void pi (const char * target, const char * data) {}
-
- string tagN(const unsigned int n) const
- {
- size_t sz = tagStack.size();
- if (n >= sz) {
- return string();
- }
-
- return tagStack[sz - (1 + n)];
- }
-
- bool parserInited;
- bool valid;
- XML_Parser xmlParser;
- DAVResource* rootResource;
-
- // in-flight data
- string_list tagStack;
- DAVResource::Type currentElementType;
- string currentElementUrl,
- currentVersionName,
- currentVCC;
- int currentElementLength;
- string currentElementMD5;
-};
-
-
-////////////////////////////////////////////////////////////////////////
-// Static callback functions for Expat.
-////////////////////////////////////////////////////////////////////////
-
-#define VISITOR static_cast<DAVMultiStatus::DAVMultiStatusPrivate *>(userData)
-
-static void
-start_element (void * userData, const char * name, const char ** atts)
-{
- VISITOR->startElement(name);
-}
-
-static void
-end_element (void * userData, const char * name)
-{
- VISITOR->endElement(name);
-}
-
-static void
-character_data (void * userData, const char * s, int len)
-{
- VISITOR->data(s, len);
-}
-
-static void
-processing_instruction (void * userData,
- const char * target,
- const char * data)
-{
- VISITOR->pi(target, data);
-}
-
-#undef VISITOR
-
-///////////////////////////////////////////////////////////////////////////////
-
-DAVMultiStatus::DAVMultiStatus() :
-_d(new DAVMultiStatusPrivate)
-{
-
-}
-
-DAVMultiStatus::~DAVMultiStatus()
-{
-
-}
-
-void DAVMultiStatus::parseXML(const char* data, int size)
-{
- if (!_d->parserInited) {
- _d->xmlParser = XML_ParserCreateNS(0, ':');
- XML_SetUserData(_d->xmlParser, _d.get());
- XML_SetElementHandler(_d->xmlParser, start_element, end_element);
- XML_SetCharacterDataHandler(_d->xmlParser, character_data);
- XML_SetProcessingInstructionHandler(_d->xmlParser, processing_instruction);
- _d->parserInited = true;
- }
-
- if (!XML_Parse(_d->xmlParser, data, size, false)) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
- << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
- << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
-
- XML_ParserFree(_d->xmlParser);
- _d->parserInited = false;
- _d->valid = false;
- }
-}
-
-void DAVMultiStatus::finishParse()
-{
- if (_d->parserInited) {
- if (!XML_Parse(_d->xmlParser, NULL, 0, true)) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
- << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
- << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
- _d->valid = false;
- } else {
- _d->valid = true;
- }
- XML_ParserFree(_d->xmlParser);
- }
-
- _d->parserInited = false;
-}
-
-DAVResource* DAVMultiStatus::resource()
-{
- return _d->rootResource;
-}
-
-bool DAVMultiStatus::isValid() const
-{
- return _d->valid;
-}
-
-
+++ /dev/null
-// DAVMultiStatus.hxx -- parser for WebDAV MultiStatus XML data
-//
-// Copyright (C) 2012 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-#ifndef SG_IO_DAVMULTISTATUS_HXX
-#define SG_IO_DAVMULTISTATUS_HXX
-
-#include <string>
-#include <vector>
-#include <memory> // for auto_ptr
-
-namespace simgear
-{
-
-class DAVCollection;
-
-class DAVResource
-{
-public:
- DAVResource(const std::string& url);
- virtual ~DAVResource() { }
-
- typedef enum {
- Unknown = 0,
- Collection = 1
- } Type;
-
- const Type type() const
- { return _type; }
-
- const std::string& url() const
- { return _url; }
-
- std::string name() const;
-
- /**
- * SVN servers use this field to expose the head revision
- * of the resource, which is useful
- */
- const std::string& versionName() const
- { return _versionName; }
-
- void setVersionName(const std::string& aVersion);
-
- DAVCollection* container() const
- { return _container; }
-
- virtual bool isCollection() const
- { return false; }
-
- void setVersionControlledConfiguration(const std::string& vcc);
- const std::string& versionControlledConfiguration() const
- { return _vcc; }
-
- void setMD5(const std::string& md5Hex);
- const std::string& md5() const
- { return _md5; }
-protected:
- friend class DAVCollection;
-
- Type _type;
- std::string _url;
- std::string _versionName;
- std::string _vcc;
- std::string _md5;
- DAVCollection* _container;
-};
-
-typedef std::vector<DAVResource*> DAVResourceList;
-
-class DAVCollection : public DAVResource
-{
-public:
- DAVCollection(const std::string& url);
- virtual ~DAVCollection();
-
- DAVResourceList contents() const;
-
- void addChild(DAVResource* res);
- void removeChild(DAVResource* res);
-
- DAVCollection* createChildCollection(const std::string& name);
-
- /**
- * find the collection member with the specified URL, or return NULL
- * if no such member of this collection exists.
- */
- DAVResource* childWithUrl(const std::string& url) const;
-
- /**
- * find the collection member with the specified name, or return NULL
- */
- DAVResource* childWithName(const std::string& name) const;
-
- /**
- * wrapper around URL manipulation
- */
- std::string urlForChildWithName(const std::string& name) const;
-
- virtual bool isCollection() const
- { return true; }
-private:
- DAVResourceList _contents;
-};
-
-class DAVMultiStatus
-{
-public:
- DAVMultiStatus();
- ~DAVMultiStatus();
-
- // incremental XML parsing
- void parseXML(const char* data, int size);
-
- void finishParse();
-
- bool isValid() const;
-
- DAVResource* resource();
-
- class DAVMultiStatusPrivate;
-private:
- std::auto_ptr<DAVMultiStatusPrivate> _d;
-};
-
-} // of namespace simgear
-
-#endif // of SG_IO_DAVMULTISTATUS_HXX
+++ /dev/null
-
-#include "SVNDirectory.hxx"
-
-#include <cassert>
-#include <fstream>
-#include <iostream>
-#include <boost/foreach.hpp>
-
-#include <simgear/debug/logstream.hxx>
-#include <simgear/misc/strutils.hxx>
-#include <simgear/misc/sg_dir.hxx>
-#include <simgear/io/HTTPClient.hxx>
-#include <simgear/io/DAVMultiStatus.hxx>
-#include <simgear/io/SVNRepository.hxx>
-#include <simgear/io/sg_file.hxx>
-#include <simgear/io/SVNReportParser.hxx>
-#include <simgear/package/md5.h>
-#include <simgear/structure/exception.hxx>
-
-using std::string;
-using std::cout;
-using std::endl;
-using namespace simgear;
-
-typedef std::vector<HTTP::Request_ptr> RequestVector;
-typedef std::map<std::string, DAVResource*> DAVResourceMap;
-
-
-const char* DAV_CACHE_NAME = ".terrasync_cache";
-const char* CACHE_VERSION_4_TOKEN = "terrasync-cache-4";
-
-// important: with the Google servers, setting this higher than '1' causes
-// server internal errors (500, the connection is closed). In other words we
-// can only specify update report items one level deep at most and no more.
-// (the root and its direct children, not NOT grand-children)
-const unsigned int MAX_UPDATE_REPORT_DEPTH = 1;
-
-enum LineState
-{
- LINESTATE_HREF = 0,
- LINESTATE_VERSIONNAME
-};
-
-SVNDirectory::SVNDirectory(SVNRepository *r, const SGPath& path) :
- localPath(path),
- dav(NULL),
- repo(r),
- _doingUpdateReport(false),
- _parent(NULL)
-{
- if (path.exists()) {
- parseCache();
- }
-
- // don't create dir here, repo might not exist at all
-}
-
-SVNDirectory::SVNDirectory(SVNDirectory* pr, DAVCollection* col) :
- dav(col),
- repo(pr->repository()),
- _doingUpdateReport(false),
- _parent(pr)
-{
- assert(col->container());
- assert(!col->url().empty());
- assert(_parent);
-
- localPath = pr->fsDir().file(col->name());
- if (!localPath.exists()) {
- Dir d(localPath);
- d.create(0755);
- writeCache();
- } else {
- parseCache();
- }
-}
-
-SVNDirectory::~SVNDirectory()
-{
- // recursive delete our child directories
- BOOST_FOREACH(SVNDirectory* d, _children) {
- delete d;
- }
-}
-
-void SVNDirectory::parseCache()
-{
- SGPath p(localPath);
- p.append(DAV_CACHE_NAME);
- if (!p.exists()) {
- return;
- }
-
- char href[1024];
- char versionName[128];
- LineState lineState = LINESTATE_HREF;
- std::ifstream file(p.c_str());
- if (!file.is_open()) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "unable to open cache file for reading:" << p);
- return;
- }
- bool doneSelf = false;
-
- file.getline(href, 1024);
- if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "invalid cache file [missing header token]:" << p << " '" << href << "'");
- return;
- }
-
- std::string vccUrl;
- file.getline(href, 1024);
- vccUrl = href;
-
- while (!file.eof()) {
- if (lineState == LINESTATE_HREF) {
- file.getline(href, 1024);
- lineState = LINESTATE_VERSIONNAME;
- } else {
- assert(lineState == LINESTATE_VERSIONNAME);
- file.getline(versionName, 1024);
- lineState = LINESTATE_HREF;
- char* hrefPtr = href;
-
- if (!doneSelf) {
- if (!dav) {
- dav = new DAVCollection(hrefPtr);
- dav->setVersionName(versionName);
- } else {
- assert(string(hrefPtr) == dav->url());
- }
-
- if (!vccUrl.empty()) {
- dav->setVersionControlledConfiguration(vccUrl);
- }
-
- _cachedRevision = versionName;
- doneSelf = true;
- } else {
- DAVResource* child = parseChildDirectory(hrefPtr)->collection();
- string s = strutils::strip(versionName);
- if (!s.empty()) {
- child->setVersionName(versionName);
- }
- } // of done self test
- } // of line-state switching
- } // of file get-line loop
-}
-
-void SVNDirectory::writeCache()
-{
- SGPath p(localPath);
- if (!p.exists()) {
- Dir d(localPath);
- d.create(0755);
- }
-
- p.append(string(DAV_CACHE_NAME) + ".new");
-
- std::ofstream file(p.c_str(), std::ios::trunc);
-// first, cache file version header
- file << CACHE_VERSION_4_TOKEN << '\n';
-
-// second, the repository VCC url
- file << dav->versionControlledConfiguration() << '\n';
-
-// third, our own URL, and version
- file << dav->url() << '\n' << _cachedRevision << '\n';
-
- BOOST_FOREACH(DAVResource* child, dav->contents()) {
- if (child->isCollection()) {
- file << child->name() << '\n' << child->versionName() << "\n";
- }
- } // of child iteration
-
- file.close();
-
-// approximately atomic delete + rename operation
- SGPath cacheName(localPath);
- cacheName.append(DAV_CACHE_NAME);
- p.rename(cacheName);
-}
-
-void SVNDirectory::setBaseUrl(const string& url)
-{
- if (_parent) {
- SG_LOG(SG_TERRASYNC, SG_ALERT, "setting base URL on non-root directory " << url);
- return;
- }
-
- if (dav && (url == dav->url())) {
- return;
- }
-
- dav = new DAVCollection(url);
-}
-
-std::string SVNDirectory::url() const
-{
- if (!_parent) {
- return repo->baseUrl();
- }
-
- return _parent->url() + "/" + name();
-}
-
-std::string SVNDirectory::name() const
-{
- return dav->name();
-}
-
-DAVResource*
-SVNDirectory::addChildFile(const std::string& fileName)
-{
- DAVResource* child = NULL;
- child = new DAVResource(dav->urlForChildWithName(fileName));
- dav->addChild(child);
-
- writeCache();
- return child;
-}
-
-SVNDirectory*
-SVNDirectory::addChildDirectory(const std::string& dirName)
-{
- if (dav->childWithName(dirName)) {
- // existing child, let's remove it
- deleteChildByName(dirName);
- }
-
- DAVCollection* childCol = dav->createChildCollection(dirName);
- SVNDirectory* child = new SVNDirectory(this, childCol);
- childCol->setVersionName(child->cachedRevision());
- _children.push_back(child);
- writeCache();
- return child;
-}
-
-SVNDirectory*
-SVNDirectory::parseChildDirectory(const std::string& dirName)
-{
- assert(!dav->childWithName(dirName));
- DAVCollection* childCol = dav->createChildCollection(dirName);
- SVNDirectory* child = new SVNDirectory(this, childCol);
- childCol->setVersionName(child->cachedRevision());
- _children.push_back(child);
- return child;
-}
-
-void SVNDirectory::deleteChildByName(const std::string& nm)
-{
- DAVResource* child = dav->childWithName(nm);
- if (!child) {
- return;
- }
-
- SGPath path = fsDir().file(nm);
-
- if (child->isCollection()) {
- Dir d(path);
- bool ok = d.remove(true);
- if (!ok) {
- SG_LOG(SG_TERRASYNC, SG_ALERT, "SVNDirectory::deleteChildByName: failed to remove dir:"
- << nm << " at path:\n\t" << path);
- }
-
- DirectoryList::iterator it = findChildDir(nm);
- if (it != _children.end()) {
- SVNDirectory* c = *it;
- delete c;
- _children.erase(it);
- }
- } else {
- bool ok = path.remove();
- if (!ok) {
- SG_LOG(SG_TERRASYNC, SG_ALERT, "SVNDirectory::deleteChildByName: failed to remove path:" << nm
- << " at path:\n\t" << path);
- }
- }
-
- dav->removeChild(child);
- delete child;
-
- writeCache();
-}
-
-bool SVNDirectory::isDoingSync() const
-{
- if (_doingUpdateReport) {
- return true;
- }
-
- BOOST_FOREACH(SVNDirectory* child, _children) {
- if (child->isDoingSync()) {
- return true;
- } // of children
- }
-
- return false;
-}
-
-void SVNDirectory::beginUpdateReport()
-{
- _doingUpdateReport = true;
- _cachedRevision.clear();
- writeCache();
-}
-
-void SVNDirectory::updateReportComplete()
-{
- _cachedRevision = dav->versionName();
- _doingUpdateReport = false;
- writeCache();
-
- SVNDirectory* pr = parent();
- if (pr) {
- pr->writeCache();
- }
-}
-
-SVNRepository* SVNDirectory::repository() const
-{
- return repo;
-}
-
-void SVNDirectory::mergeUpdateReportDetails(unsigned int depth,
- string_list& items)
-{
- // normal, easy case: we are fully in-sync at a revision
- if (!_cachedRevision.empty()) {
- std::ostringstream os;
- os << "<S:entry rev=\"" << _cachedRevision << "\" depth=\"infinity\">"
- << repoPath() << "</S:entry>";
- items.push_back(os.str());
- return;
- }
-
- Dir d(localPath);
- if (depth >= MAX_UPDATE_REPORT_DEPTH) {
- d.removeChildren();
- return;
- }
-
- PathList cs = d.children(Dir::NO_DOT_OR_DOTDOT | Dir::INCLUDE_HIDDEN | Dir::TYPE_DIR);
- BOOST_FOREACH(SGPath path, cs) {
- SVNDirectory* c = child(path.file());
- if (!c) {
- // ignore this child, if it's an incomplete download,
- // it will be over-written on the update anyway
- //std::cerr << "unknown SVN child" << path << std::endl;
- } else {
- // recurse down into children
- c->mergeUpdateReportDetails(depth+1, items);
- }
- } // of child dir iteration
-}
-
-std::string SVNDirectory::repoPath() const
-{
- if (!_parent) {
- return "/";
- }
-
- // find the length of the repository base URL, then
- // trim that off our repo URL - job done!
- size_t baseUrlLen = repo->baseUrl().size();
- return dav->url().substr(baseUrlLen + 1);
-}
-
-SVNDirectory* SVNDirectory::parent() const
-{
- return _parent;
-}
-
-SVNDirectory* SVNDirectory::child(const std::string& dirName) const
-{
- BOOST_FOREACH(SVNDirectory* d, _children) {
- if (d->name() == dirName) {
- return d;
- }
- }
-
- return NULL;
-}
-
-DirectoryList::iterator
-SVNDirectory::findChildDir(const std::string& dirName)
-{
- DirectoryList::iterator it;
- for (it=_children.begin(); it != _children.end(); ++it) {
- if ((*it)->name() == dirName) {
- return it;
- }
- }
- return it;
-}
-
-simgear::Dir SVNDirectory::fsDir() const
-{
- return Dir(localPath);
-}
+++ /dev/null
-// DAVCollectionMirror.hxx - mirror a DAV collection to the local filesystem
-//
-// Copyright (C) 2013 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-#ifndef SG_IO_DAVCOLLECTIONMIRROR_HXX
-#define SG_IO_DAVCOLLECTIONMIRROR_HXX
-
-#include <string>
-#include <vector>
-#include <memory>
-
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/misc/strutils.hxx>
-#include <simgear/io/DAVMultiStatus.hxx>
-
-namespace simgear {
-
-class Dir;
-namespace HTTP { class Request; }
-
-// forward decls
-class DAVMirror;
-class SVNRepository;
-class SVNDirectory;
-
-typedef std::vector<SVNDirectory*> DirectoryList;
-
-class SVNDirectory
-{
-public:
- // init from local
- SVNDirectory(SVNRepository *repo, const SGPath& path);
- ~SVNDirectory();
-
- void setBaseUrl(const std::string& url);
-
- // init from a collection
- SVNDirectory(SVNDirectory* pr, DAVCollection* col);
-
- void beginUpdateReport();
- void updateReportComplete();
-
- bool isDoingSync() const;
-
- std::string url() const;
-
- std::string name() const;
-
- DAVResource* addChildFile(const std::string& fileName);
- SVNDirectory* addChildDirectory(const std::string& dirName);
-
- // void updateChild(DAVResource* child);
- void deleteChildByName(const std::string& name);
-
- SGPath fsPath() const
- { return localPath; }
-
- simgear::Dir fsDir() const;
-
- std::string repoPath() const;
-
- SVNRepository* repository() const;
- DAVCollection* collection() const
- { return dav; }
-
- std::string cachedRevision() const
- { return _cachedRevision; }
-
- void mergeUpdateReportDetails(unsigned int depth, string_list& items);
-
- SVNDirectory* parent() const;
- SVNDirectory* child(const std::string& dirName) const;
-private:
-
- void parseCache();
- void writeCache();
-
- DirectoryList::iterator findChildDir(const std::string& dirName);
- SVNDirectory* parseChildDirectory(const std::string& dirName);
-
- SGPath localPath;
- DAVCollection* dav;
- SVNRepository* repo;
-
- std::string _cachedRevision;
- bool _doingUpdateReport;
-
- SVNDirectory* _parent;
- DirectoryList _children;
-};
-
-} // of namespace simgear
-
-#endif // of SG_IO_DAVCOLLECTIONMIRROR_HXX
+++ /dev/null
-// SVNReportParser -- parser for SVN report XML data
-//
-// Copyright (C) 2012 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-# include <simgear_config.h>
-#endif
-
-#include "SVNReportParser.hxx"
-
-#include <iostream>
-#include <cstring>
-#include <cassert>
-#include <algorithm>
-#include <sstream>
-#include <fstream>
-
-#include <boost/foreach.hpp>
-
-#include "simgear/misc/sg_path.hxx"
-#include "simgear/misc/sg_dir.hxx"
-#include "simgear/debug/logstream.hxx"
-#include "simgear/xml/easyxml.hxx"
-#include "simgear/misc/strutils.hxx"
-#include "simgear/package/md5.h"
-
-#ifdef SYSTEM_EXPAT
-# include <expat.h>
-#else
-# include "sg_expat.h"
-#endif
-
-#include "SVNDirectory.hxx"
-#include "SVNRepository.hxx"
-#include "DAVMultiStatus.hxx"
-
-using std::cout;
-using std::cerr;
-using std::endl;
-using std::string;
-
-using namespace simgear;
-
-#define DAV_NS "DAV::"
-#define SVN_NS "svn::"
-#define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
-
-namespace {
-
- #define MAX_ENCODED_INT_LEN 10
-
- static size_t
- decode_size(unsigned char* &p,
- const unsigned char *end)
- {
- if (p + MAX_ENCODED_INT_LEN < end)
- end = p + MAX_ENCODED_INT_LEN;
- /* Decode bytes until we're done. */
- size_t result = 0;
-
- while (p < end) {
- result = (result << 7) | (*p & 0x7f);
- if (((*p++ >> 7) & 0x1) == 0) {
- break;
- }
- }
-
- return result;
- }
-
- static bool
- try_decode_size(unsigned char* &p,
- const unsigned char *end)
- {
- if (p + MAX_ENCODED_INT_LEN < end)
- end = p + MAX_ENCODED_INT_LEN;
-
- while (p < end) {
- if (((*p++ >> 7) & 0x1) == 0) {
- return true;
- }
- }
-
- return false;
- }
-
-// const char* SVN_UPDATE_REPORT_TAG = SVN_NS "update-report";
- // const char* SVN_TARGET_REVISION_TAG = SVN_NS "target-revision";
- const char* SVN_OPEN_DIRECTORY_TAG = SVN_NS "open-directory";
- const char* SVN_OPEN_FILE_TAG = SVN_NS "open-file";
- const char* SVN_ADD_DIRECTORY_TAG = SVN_NS "add-directory";
- const char* SVN_ADD_FILE_TAG = SVN_NS "add-file";
- const char* SVN_TXDELTA_TAG = SVN_NS "txdelta";
- const char* SVN_SET_PROP_TAG = SVN_NS "set-prop";
- const char* SVN_PROP_TAG = SVN_NS "prop";
- const char* SVN_DELETE_ENTRY_TAG = SVN_NS "delete-entry";
-
- const char* SVN_DAV_MD5_CHECKSUM = SUBVERSION_DAV_NS ":md5-checksum";
-
- const char* DAV_HREF_TAG = DAV_NS "href";
- const char* DAV_CHECKED_IN_TAG = DAV_NS "checked-in";
-
-
- const int svn_txdelta_source = 0;
- const int svn_txdelta_target = 1;
- const int svn_txdelta_new = 2;
-
- const size_t DELTA_HEADER_SIZE = 4;
-
- /**
- * helper struct to decode and store the SVN delta header
- * values
- */
- struct SVNDeltaWindow
- {
- public:
-
- static bool isWindowComplete(unsigned char* buffer, size_t bytes)
- {
- unsigned char* p = buffer;
- unsigned char* pEnd = p + bytes;
- // if we can't decode five sizes, certainly incomplete
- for (int i=0; i<5; i++) {
- if (!try_decode_size(p, pEnd)) {
- return false;
- }
- }
-
- p = buffer;
- // ignore these three
- decode_size(p, pEnd);
- decode_size(p, pEnd);
- decode_size(p, pEnd);
- size_t instructionLen = decode_size(p, pEnd);
- size_t newLength = decode_size(p, pEnd);
- size_t headerLength = p - buffer;
-
- return (bytes >= (instructionLen + newLength + headerLength));
- }
-
- SVNDeltaWindow(unsigned char* p) :
- headerLength(0),
- _ptr(p)
- {
- sourceViewOffset = decode_size(p, p+20);
- sourceViewLength = decode_size(p, p+20);
- targetViewLength = decode_size(p, p+20);
- instructionLength = decode_size(p, p+20);
- newLength = decode_size(p, p+20);
-
- headerLength = p - _ptr;
- _ptr = p;
- }
-
- bool apply(std::vector<unsigned char>& output, std::istream& source)
- {
- unsigned char* pEnd = _ptr + instructionLength;
- unsigned char* newData = pEnd;
-
- while (_ptr < pEnd) {
- int op = ((*_ptr >> 6) & 0x3);
- if (op >= 3) {
- SG_LOG(SG_IO, SG_INFO, "SVNDeltaWindow: bad opcode:" << op);
- return false;
- }
-
- int length = *_ptr++ & 0x3f;
- int offset = 0;
-
- if (length == 0) {
- length = decode_size(_ptr, pEnd);
- }
-
- if (length == 0) {
- SG_LOG(SG_IO, SG_INFO, "SVNDeltaWindow: malformed stream, 0 length" << op);
- return false;
- }
-
- // if op != new, decode another size value
- if (op != svn_txdelta_new) {
- offset = decode_size(_ptr, pEnd);
- }
-
- if (op == svn_txdelta_target) {
- // this is inefficent, but ranges can overlap.
- while (length > 0) {
- output.push_back(output[offset++]);
- --length;
- }
- } else if (op == svn_txdelta_new) {
- output.insert(output.end(), newData, newData + length);
- newData += length;
- } else if (op == svn_txdelta_source) {
- source.seekg(offset);
- char* sourceBuf = (char*) malloc(length);
- assert(sourceBuf);
- source.read(sourceBuf, length);
- output.insert(output.end(), sourceBuf, sourceBuf + length);
- free(sourceBuf);
- } else {
- SG_LOG(SG_IO, SG_WARN, "bad opcode logic");
- return false;
- }
- } // of instruction loop
-
- return true;
- }
-
- size_t size() const
- {
- return headerLength + instructionLength + newLength;
- }
-
- unsigned int sourceViewOffset;
- size_t sourceViewLength,
- targetViewLength;
- size_t headerLength,
- instructionLength,
- newLength;
-
-private:
- unsigned char* _ptr;
- };
-
-
-} // of anonymous namespace
-
-class SVNReportParser::SVNReportParserPrivate
-{
-public:
- SVNReportParserPrivate(SVNRepository* repo) :
- tree(repo),
- status(AbstractRepository::REPO_NO_ERROR),
- parserInited(false),
- currentPath(repo->fsBase())
- {
- inFile = false;
- currentDir = repo->rootDir();
- }
-
- ~SVNReportParserPrivate()
- {
- }
-
- void startElement (const char * name, const char** attributes)
- {
- if (status != AbstractRepository::REPO_NO_ERROR) {
- return;
- }
-
- ExpatAtts attrs(attributes);
- tagStack.push_back(name);
- if (!strcmp(name, SVN_TXDELTA_TAG)) {
- txDeltaData.clear();
- } else if (!strcmp(name, SVN_ADD_FILE_TAG)) {
- string fileName(attrs.getValue("name"));
- SGPath filePath(currentDir->fsDir().file(fileName));
- currentPath = filePath;
- inFile = true;
- } else if (!strcmp(name, SVN_OPEN_FILE_TAG)) {
- string fileName(attrs.getValue("name"));
- SGPath filePath(Dir(currentPath).file(fileName));
- currentPath = filePath;
-
- if (!filePath.exists()) {
- fail(AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
- return;
- }
-
- inFile = true;
- } else if (!strcmp(name, SVN_ADD_DIRECTORY_TAG)) {
- string dirName(attrs.getValue("name"));
- Dir d(currentDir->fsDir().file(dirName));
- if (d.exists()) {
- // policy decision : if we're doing an add, wipe the existing
- d.remove(true);
- }
-
- currentDir = currentDir->addChildDirectory(dirName);
- currentPath = currentDir->fsPath();
- currentDir->beginUpdateReport();
- //cout << "addDir:" << currentPath << endl;
- } else if (!strcmp(name, SVN_SET_PROP_TAG)) {
- setPropName = attrs.getValue("name");
- setPropValue.clear();
- } else if (!strcmp(name, SVN_DAV_MD5_CHECKSUM)) {
- md5Sum.clear();
- } else if (!strcmp(name, SVN_OPEN_DIRECTORY_TAG)) {
- string dirName;
- if (attrs.getValue("name")) {
- dirName = string(attrs.getValue("name"));
- }
- openDirectory(dirName);
- } else if (!strcmp(name, SVN_DELETE_ENTRY_TAG)) {
- string entryName(attrs.getValue("name"));
- deleteEntry(entryName);
- } else if (!strcmp(name, DAV_CHECKED_IN_TAG) ||
- !strcmp(name, DAV_HREF_TAG) ||
- !strcmp(name, SVN_PROP_TAG)) {
- // don't warn on these ones
- } else {
- //SG_LOG(SG_IO, SG_WARN, "SVNReportParser: unhandled tag:" << name);
- }
- } // of startElement
-
- void openDirectory(const std::string& dirName)
- {
- if (dirName.empty()) {
- // root directory, we shall assume
- currentDir = tree->rootDir();
- } else {
- assert(currentDir);
- currentDir = currentDir->child(dirName);
- }
-
- assert(currentDir);
- currentPath = currentDir->fsPath();
- currentDir->beginUpdateReport();
- }
-
- void deleteEntry(const std::string& entryName)
- {
- currentDir->deleteChildByName(entryName);
- }
-
- bool decodeTextDelta(const SGPath& outputPath)
- {
- std::vector<unsigned char> output, decoded;
- strutils::decodeBase64(txDeltaData, decoded);
- size_t bytesToDecode = decoded.size();
-
- unsigned char* p = decoded.data();
- if (memcmp(p, "SVN\0", DELTA_HEADER_SIZE) != 0) {
- return false; // bad header
- }
-
- bytesToDecode -= DELTA_HEADER_SIZE;
- p += DELTA_HEADER_SIZE;
- std::ifstream source;
- source.open(outputPath.c_str(), std::ios::in | std::ios::binary);
-
- while (bytesToDecode > 0) {
- if (!SVNDeltaWindow::isWindowComplete(p, bytesToDecode)) {
- SG_LOG(SG_IO, SG_WARN, "SVN txdelta broken window");
- return false;
- }
-
- SVNDeltaWindow window(p);
- assert(bytesToDecode >= window.size());
- window.apply(output, source);
- bytesToDecode -= window.size();
- p += window.size();
- }
-
- source.close();
-
- std::ofstream f;
- f.open(outputPath.c_str(),
- std::ios::out | std::ios::trunc | std::ios::binary);
- f.write((char*) output.data(), output.size());
-
- // compute MD5 while we have the file in memory
- memset(&md5Context, 0, sizeof(SG_MD5_CTX));
- SG_MD5Init(&md5Context);
- SG_MD5Update(&md5Context, (unsigned char*) output.data(), output.size());
- unsigned char digest[MD5_DIGEST_LENGTH];
- SG_MD5Final(digest, &md5Context);
- decodedFileMd5 = strutils::encodeHex(digest, MD5_DIGEST_LENGTH);
-
- return true;
- }
-
- void endElement (const char * name)
- {
- if (status != SVNRepository::REPO_NO_ERROR) {
- return;
- }
-
- assert(tagStack.back() == name);
- tagStack.pop_back();
-
- if (!strcmp(name, SVN_TXDELTA_TAG)) {
- if (!decodeTextDelta(currentPath)) {
- fail(SVNRepository::SVN_ERROR_TXDELTA);
- }
- } else if (!strcmp(name, SVN_ADD_FILE_TAG)) {
- finishFile(currentPath);
- } else if (!strcmp(name, SVN_OPEN_FILE_TAG)) {
- finishFile(currentPath);
- } else if (!strcmp(name, SVN_ADD_DIRECTORY_TAG)) {
- // pop directory
- currentPath = currentPath.dir();
- currentDir->updateReportComplete();
- currentDir = currentDir->parent();
- } else if (!strcmp(name, SVN_SET_PROP_TAG)) {
- if (setPropName == "svn:entry:committed-rev") {
- revision = strutils::to_int(setPropValue);
- currentVersionName = setPropValue;
- if (!inFile) {
- // for directories we have the resource already
- // for adding files, we might not; we set the version name
- // above when ending the add/open-file element
- currentDir->collection()->setVersionName(currentVersionName);
- }
- }
- } else if (!strcmp(name, SVN_DAV_MD5_CHECKSUM)) {
- // validate against (presumably) just written file
- if (decodedFileMd5 != md5Sum) {
- fail(SVNRepository::REPO_ERROR_CHECKSUM);
- }
- } else if (!strcmp(name, SVN_OPEN_DIRECTORY_TAG)) {
- currentDir->updateReportComplete();
- if (currentDir->parent()) {
- // pop the collection stack
- currentDir = currentDir->parent();
- }
-
- currentPath = currentDir->fsPath();
- } else {
- // std::cout << "element:" << name;
- }
- }
-
- void finishFile(const SGPath& path)
- {
- currentPath = path.dir();
- inFile = false;
- }
-
- void data (const char * s, int length)
- {
- if (status != SVNRepository::REPO_NO_ERROR) {
- return;
- }
-
- if (tagStack.back() == SVN_SET_PROP_TAG) {
- setPropValue.append(s, length);
- } else if (tagStack.back() == SVN_TXDELTA_TAG) {
- txDeltaData.append(s, length);
- } else if (tagStack.back() == SVN_DAV_MD5_CHECKSUM) {
- md5Sum.append(s, length);
- }
- }
-
- void pi (const char * target, const char * data) {}
-
- string tagN(const unsigned int n) const
- {
- size_t sz = tagStack.size();
- if (n >= sz) {
- return string();
- }
-
- return tagStack[sz - (1 + n)];
- }
-
- void fail(SVNRepository::ResultCode err)
- {
- status = err;
- }
-
- SVNRepository* tree;
- DAVCollection* rootCollection;
- SVNDirectory* currentDir;
- SVNRepository::ResultCode status;
-
- bool parserInited;
- XML_Parser xmlParser;
-
-// in-flight data
- string_list tagStack;
- string currentVersionName;
- string txDeltaData;
- SGPath currentPath;
- bool inFile;
-
- unsigned int revision;
- SG_MD5_CTX md5Context;
- string md5Sum, decodedFileMd5;
- std::string setPropName, setPropValue;
-};
-
-
-////////////////////////////////////////////////////////////////////////
-// Static callback functions for Expat.
-////////////////////////////////////////////////////////////////////////
-
-#define VISITOR static_cast<SVNReportParser::SVNReportParserPrivate *>(userData)
-
-static void
-start_element (void * userData, const char * name, const char ** atts)
-{
- VISITOR->startElement(name, atts);
-}
-
-static void
-end_element (void * userData, const char * name)
-{
- VISITOR->endElement(name);
-}
-
-static void
-character_data (void * userData, const char * s, int len)
-{
- VISITOR->data(s, len);
-}
-
-static void
-processing_instruction (void * userData,
- const char * target,
- const char * data)
-{
- VISITOR->pi(target, data);
-}
-
-#undef VISITOR
-
-///////////////////////////////////////////////////////////////////////////////
-
-SVNReportParser::SVNReportParser(SVNRepository* repo) :
- _d(new SVNReportParserPrivate(repo))
-{
-
-}
-
-SVNReportParser::~SVNReportParser()
-{
-}
-
-SVNRepository::ResultCode
-SVNReportParser::innerParseXML(const char* data, int size)
-{
- if (_d->status != SVNRepository::REPO_NO_ERROR) {
- return _d->status;
- }
-
- bool isEnd = (data == NULL);
- if (!XML_Parse(_d->xmlParser, data, size, isEnd)) {
- SG_LOG(SG_IO, SG_INFO, "SVN parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
- << " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
- << " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
-
- XML_ParserFree(_d->xmlParser);
- _d->parserInited = false;
- return SVNRepository::SVN_ERROR_XML;
- } else if (isEnd) {
- XML_ParserFree(_d->xmlParser);
- _d->parserInited = false;
- }
-
- return _d->status;
-}
-
-SVNRepository::ResultCode
-SVNReportParser::parseXML(const char* data, int size)
-{
- if (_d->status != SVNRepository::REPO_NO_ERROR) {
- return _d->status;
- }
-
- if (!_d->parserInited) {
- _d->xmlParser = XML_ParserCreateNS(0, ':');
- XML_SetUserData(_d->xmlParser, _d.get());
- XML_SetElementHandler(_d->xmlParser, start_element, end_element);
- XML_SetCharacterDataHandler(_d->xmlParser, character_data);
- XML_SetProcessingInstructionHandler(_d->xmlParser, processing_instruction);
- _d->parserInited = true;
- }
-
- return innerParseXML(data, size);
-}
-
-SVNRepository::ResultCode SVNReportParser::finishParse()
-{
- if (_d->status != SVNRepository::REPO_NO_ERROR) {
- return _d->status;
- }
-
- return innerParseXML(NULL, 0);
-}
-
-std::string SVNReportParser::etagFromRevision(unsigned int revision)
-{
- // etags look like W/"7//", hopefully this is stable
- // across different servers and similar
- std::ostringstream os;
- os << "W/\"" << revision << "//";
- return os.str();
-}
-
-
+++ /dev/null
-// SVNReportParser -- parser for SVN report XML data
-//
-// Copyright (C) 2012 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-#ifndef SG_IO_SVNREPORTPARSER_HXX
-#define SG_IO_SVNREPORTPARSER_HXX
-
-#include <string>
-#include <memory> // for auto_ptr
-
-#include "SVNRepository.hxx"
-
-class SGPath;
-
-namespace simgear
-{
-
-class SVNRepository;
-
-class SVNReportParser
-{
-public:
- SVNReportParser(SVNRepository* repo);
- ~SVNReportParser();
-
- // incremental XML parsing
- SVNRepository::ResultCode parseXML(const char* data, int size);
-
- SVNRepository::ResultCode finishParse();
-
- static std::string etagFromRevision(unsigned int revision);
-
- class SVNReportParserPrivate;
-private:
- SVNRepository::ResultCode innerParseXML(const char* data, int size);
-
- std::auto_ptr<SVNReportParserPrivate> _d;
-};
-
-} // of namespace simgear
-
-#endif // of SG_IO_SVNREPORTPARSER_HXX
+++ /dev/null
-// DAVMirrorTree -- mirror a DAV tree to the local file-system
-//
-// Copyright (C) 2012 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#include "SVNRepository.hxx"
-
-#include <iostream>
-#include <cstring>
-#include <cassert>
-#include <algorithm>
-#include <sstream>
-#include <map>
-#include <set>
-#include <fstream>
-#include <memory>
-
-#include <boost/foreach.hpp>
-
-#include "simgear/debug/logstream.hxx"
-#include "simgear/misc/strutils.hxx"
-#include <simgear/misc/sg_dir.hxx>
-#include <simgear/io/HTTPClient.hxx>
-#include <simgear/io/DAVMultiStatus.hxx>
-#include <simgear/io/SVNDirectory.hxx>
-#include <simgear/io/sg_file.hxx>
-#include <simgear/io/SVNReportParser.hxx>
-
-using std::cout;
-using std::cerr;
-using std::endl;
-using std::string;
-
-namespace simgear
-{
-
-typedef std::vector<HTTP::Request_ptr> RequestVector;
-
-class SVNRepoPrivate
-{
-public:
- SVNRepoPrivate(SVNRepository* parent) :
- p(parent),
- isUpdating(false),
- status(SVNRepository::REPO_NO_ERROR)
- { ; }
-
- SVNRepository* p; // link back to outer
- SVNDirectory* rootCollection;
- HTTP::Client* http;
- std::string baseUrl;
- std::string vccUrl;
- std::string targetRevision;
- bool isUpdating;
- SVNRepository::ResultCode status;
-
- void svnUpdateDone()
- {
- isUpdating = false;
- }
-
- void updateFailed(HTTP::Request* req, SVNRepository::ResultCode err)
- {
- SG_LOG(SG_TERRASYNC, SG_WARN, "SVN: failed to update from:" << req->url()
- << "\n(repository:" << p->baseUrl() << ")");
- isUpdating = false;
- status = err;
- }
-
- void propFindComplete(HTTP::Request* req, DAVCollection* col);
- void propFindFailed(HTTP::Request* req, SVNRepository::ResultCode err);
-};
-
-
-namespace { // anonmouse
-
- string makeAbsoluteUrl(const string& url, const string& base)
- {
- if (strutils::starts_with(url, "http://"))
- return url; // already absolute
-
- assert(strutils::starts_with(base, "http://"));
- int schemeEnd = base.find("://");
- int hostEnd = base.find('/', schemeEnd + 3);
- if (hostEnd < 0) {
- return url;
- }
-
- return base.substr(0, hostEnd) + url;
- }
-
- // keep the responses small by only requesting the properties we actually
- // care about; the ETag, length and MD5-sum
- const char* PROPFIND_REQUEST_BODY =
- "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
- "<D:propfind xmlns:D=\"DAV:\">"
- "<D:prop xmlns:R=\"http://subversion.tigris.org/xmlns/dav/\">"
- "<D:resourcetype/>"
- "<D:version-name/>"
- "<D:version-controlled-configuration/>"
- "</D:prop>"
- "</D:propfind>";
-
- class PropFindRequest : public HTTP::Request
- {
- public:
- PropFindRequest(SVNRepoPrivate* repo) :
- Request(repo->baseUrl, "PROPFIND"),
- _repo(repo)
- {
- assert(repo);
- requestHeader("Depth") = "0";
- setBodyData( PROPFIND_REQUEST_BODY,
- "application/xml; charset=\"utf-8\"" );
- }
-
- protected:
- virtual void responseHeadersComplete()
- {
- if (responseCode() == 207) {
- // fine
- } else if (responseCode() == 404) {
- _repo->propFindFailed(this, SVNRepository::REPO_ERROR_NOT_FOUND);
- } else {
- SG_LOG(SG_TERRASYNC, SG_WARN, "request for:" << url() <<
- " return code " << responseCode());
- _repo->propFindFailed(this, SVNRepository::REPO_ERROR_SOCKET);
- _repo = NULL;
- }
-
- Request::responseHeadersComplete();
- }
-
- virtual void onDone()
- {
- if (responseCode() == 207) {
- _davStatus.finishParse();
- if (_davStatus.isValid()) {
- _repo->propFindComplete(this, (DAVCollection*) _davStatus.resource());
- } else {
- _repo->propFindFailed(this, SVNRepository::REPO_ERROR_SOCKET);
- }
- }
- }
-
- virtual void gotBodyData(const char* s, int n)
- {
- if (responseCode() != 207) {
- return;
- }
- _davStatus.parseXML(s, n);
- }
-
- virtual void onFail()
- {
- HTTP::Request::onFail();
- if (_repo) {
- _repo->propFindFailed(this, SVNRepository::REPO_ERROR_SOCKET);
- _repo = NULL;
- }
- }
-
- private:
- SVNRepoPrivate* _repo;
- DAVMultiStatus _davStatus;
- };
-
-class UpdateReportRequest:
- public HTTP::Request
-{
-public:
- UpdateReportRequest(SVNRepoPrivate* repo,
- const std::string& aVersionName,
- bool startEmpty) :
- HTTP::Request("", "REPORT"),
- _parser(repo->p),
- _repo(repo),
- _failed(false)
- {
- setUrl(repo->vccUrl);
- std::string request =
- "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
- "<S:update-report send-all=\"true\" xmlns:S=\"svn:\">\n"
- "<S:src-path>" + repo->baseUrl + "</S:src-path>\n"
- "<S:depth>unknown</S:depth>\n"
- "<S:entry rev=\"" + aVersionName + "\" depth=\"infinity\" start-empty=\"true\"/>\n";
-
- if( !startEmpty )
- {
- string_list entries;
- _repo->rootCollection->mergeUpdateReportDetails(0, entries);
- BOOST_FOREACH(string e, entries)
- {
- request += e + "\n";
- }
- }
-
- request += "</S:update-report>";
-
- setBodyData(request, "application/xml; charset=\"utf-8\"");
- }
-
-protected:
- virtual void onDone()
- {
- if (_failed) {
- return;
- }
-
- if (responseCode() == 200) {
- SVNRepository::ResultCode err = _parser.finishParse();
- if (err) {
- _repo->updateFailed(this, err);
- _failed = true;
- } else {
- _repo->svnUpdateDone();
- }
- } else if (responseCode() == 404) {
- _repo->updateFailed(this, SVNRepository::REPO_ERROR_NOT_FOUND);
- _failed = true;
- } else {
- SG_LOG(SG_TERRASYNC, SG_WARN, "SVN: request for:" << url() <<
- " got HTTP status " << responseCode());
- _repo->updateFailed(this, SVNRepository::REPO_ERROR_HTTP);
- _failed = true;
- }
- }
-
- virtual void gotBodyData(const char* s, int n)
- {
- if (_failed) {
- return;
- }
-
- if (responseCode() != 200) {
- return;
- }
-
- SVNRepository::ResultCode err = _parser.parseXML(s, n);
- if (err) {
- _failed = true;
- SG_LOG(SG_IO, SG_WARN, this << ": SVN: request for:" << url() << " failed:" << err);
- _repo->updateFailed(this, err);
- _repo = NULL;
- }
- }
-
- virtual void onFail()
- {
- HTTP::Request::onFail();
- if (_repo) {
- _repo->updateFailed(this, SVNRepository::REPO_ERROR_SOCKET);
- _repo = NULL;
- }
- }
-private:
- SVNReportParser _parser;
- SVNRepoPrivate* _repo;
- bool _failed;
-};
-
-} // anonymous
-
-SVNRepository::SVNRepository(const SGPath& base, HTTP::Client *cl) :
- _d(new SVNRepoPrivate(this))
-{
- _d->http = cl;
- _d->rootCollection = new SVNDirectory(this, base);
- _d->baseUrl = _d->rootCollection->url();
-}
-
-SVNRepository::~SVNRepository()
-{
- delete _d->rootCollection;
-}
-
-void SVNRepository::setBaseUrl(const std::string &url)
-{
- _d->baseUrl = url;
- _d->rootCollection->setBaseUrl(url);
-}
-
-std::string SVNRepository::baseUrl() const
-{
- return _d->baseUrl;
-}
-
-HTTP::Client* SVNRepository::http() const
-{
- return _d->http;
-}
-
-SGPath SVNRepository::fsBase() const
-{
- return _d->rootCollection->fsPath();
-}
-
-bool SVNRepository::isBare() const
-{
- if (!fsBase().exists() || Dir(fsBase()).isEmpty()) {
- return true;
- }
-
- if (_d->vccUrl.empty()) {
- return true;
- }
-
- return false;
-}
-
-void SVNRepository::update()
-{
- _d->status = REPO_NO_ERROR;
- if (_d->targetRevision.empty() || _d->vccUrl.empty()) {
- _d->isUpdating = true;
- PropFindRequest* pfr = new PropFindRequest(_d.get());
- http()->makeRequest(pfr);
- return;
- }
-
- if (_d->targetRevision == rootDir()->cachedRevision()) {
- SG_LOG(SG_TERRASYNC, SG_DEBUG, baseUrl() << " in sync at version " << _d->targetRevision);
- _d->isUpdating = false;
- return;
- }
-
- _d->isUpdating = true;
- UpdateReportRequest* urr = new UpdateReportRequest(_d.get(),
- _d->targetRevision, isBare());
- http()->makeRequest(urr);
-}
-
-bool SVNRepository::isDoingSync() const
-{
- if (_d->status != REPO_NO_ERROR) {
- return false;
- }
-
- return _d->isUpdating || _d->rootCollection->isDoingSync();
-}
-
-SVNDirectory* SVNRepository::rootDir() const
-{
- return _d->rootCollection;
-}
-
-SVNRepository::ResultCode
-SVNRepository::failure() const
-{
- return _d->status;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-void SVNRepoPrivate::propFindComplete(HTTP::Request* req, DAVCollection* c)
-{
- targetRevision = c->versionName();
- vccUrl = makeAbsoluteUrl(c->versionControlledConfiguration(), baseUrl);
- rootCollection->collection()->setVersionControlledConfiguration(vccUrl);
- p->update();
-}
-
-void SVNRepoPrivate::propFindFailed(HTTP::Request *req, SVNRepository::ResultCode err)
-{
- if (err != SVNRepository::REPO_ERROR_NOT_FOUND) {
- SG_LOG(SG_TERRASYNC, SG_WARN, "PropFind failed for:" << req->url());
- }
-
- isUpdating = false;
- status = err;
-}
-
-} // of namespace simgear
+++ /dev/null
-// DAVMirrorTree.hxx - mirror a DAV tree to the local file system
-//
-// Copyright (C) 2012 James Turner <zakalawe@mac.com>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-#ifndef SG_IO_SVN_REPOSITORY_HXX
-#define SG_IO_SVN_REPOSITORY_HXX
-
-#include <simgear/io/AbstractRepository.hxx>
-#include <memory>
-
-namespace simgear {
-
-class SVNDirectory;
-class SVNRepoPrivate;
-
-class SVNRepository : public AbstractRepository
-{
-public:
-
- SVNRepository(const SGPath& root, HTTP::Client* cl);
- virtual ~SVNRepository();
-
- SVNDirectory* rootDir() const;
- virtual SGPath fsBase() const;
-
- virtual void setBaseUrl(const std::string& url);
- virtual std::string baseUrl() const;
-
- virtual HTTP::Client* http() const;
-
- virtual void update();
-
- virtual bool isDoingSync() const;
-
- virtual ResultCode failure() const;
-private:
- bool isBare() const;
-
- std::auto_ptr<SVNRepoPrivate> _d;
-};
-
-} // of namespace simgear
-
-#endif // of SG_IO_SVN_REPOSITORY_HXX
+++ /dev/null
-#include <cstdio>
-#include <cstring>
-#include <signal.h>
-
-#include <iostream>
-#include <boost/foreach.hpp>
-
-
-#include <simgear/io/sg_file.hxx>
-#include <simgear/io/HTTPClient.hxx>
-#include <simgear/io/HTTPRequest.hxx>
-#include <simgear/io/sg_netChannel.hxx>
-#include <simgear/io/DAVMultiStatus.hxx>
-#include <simgear/io/SVNRepository.hxx>
-#include <simgear/debug/logstream.hxx>
-
-#include <simgear/misc/strutils.hxx>
-#include <simgear/timing/timestamp.hxx>
-
-using namespace simgear;
-using std::cout;
-using std::endl;
-using std::cerr;
-using std::string;
-
-HTTP::Client* httpClient;
-
-int main(int argc, char* argv[])
-{
- sglog().setLogLevels( SG_ALL, SG_INFO );
- HTTP::Client cl;
- httpClient = &cl;
-
-
- SGPath p("/Users/jmt/Desktop/traffic");
- SVNRepository airports(p, &cl);
- // airports.setBaseUrl("http://svn.goneabitbursar.com/testproject1");
-// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Models");
-
- airports.setBaseUrl("http://fgfs.goneabitbursar.com/fgfsai/trunk/AI/Traffic");
-
-// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Airports");
- airports.update();
-
- while (airports.isDoingSync()) {
- cl.update(100);
- }
-
- cout << "all done!" << endl;
- return EXIT_SUCCESS;
-}
\ No newline at end of file