diff options
author | Patrick Ohly <patrick.ohly@intel.com> | 2011-10-10 13:36:04 +0200 |
---|---|---|
committer | Patrick Ohly <patrick.ohly@intel.com> | 2011-10-17 13:17:41 +0200 |
commit | 6fa5cc30c3a4f51c1151599ac5caaf35a4467222 (patch) | |
tree | 1d70896af1f016dc409b0e0c5d74ba22c6a710d3 | |
parent | bf6bab8f036fe3ad5909ca459f4f7f192d17b90d (diff) |
WebDAV: initial support for listing databases
Based on the previous code, which stopped when finding the first
collection. Now that code is an utility function which reports all
resources to a callback until told to stop by that callback.
-rw-r--r-- | src/backends/webdav/WebDAVSource.cpp | 196 | ||||
-rw-r--r-- | src/backends/webdav/WebDAVSource.h | 11 |
2 files changed, 147 insertions, 60 deletions
diff --git a/src/backends/webdav/WebDAVSource.cpp b/src/backends/webdav/WebDAVSource.cpp index 4d4de7e4..8d6d15e2 100644 --- a/src/backends/webdav/WebDAVSource.cpp +++ b/src/backends/webdav/WebDAVSource.cpp @@ -179,6 +179,11 @@ WebDAVSource::WebDAVSource(const SyncSourceParams ¶ms, this, m_operations.m_backupData, _1, _2, _3); m_operations.m_restoreData = boost::bind(&WebDAVSource::restoreData, this, m_operations.m_restoreData, _1, _2, _3); + + // ignore the "Request ends, status 207 class 2xx, error line:" printed by neon + LogRedirect::addIgnoreError(", error line:"); + // ignore error messages in returned data + LogRedirect::addIgnoreError("Read block ("); } void WebDAVSource::replaceHTMLEntities(std::string &item) @@ -300,6 +305,15 @@ void WebDAVSource::open() // Nothing to do here, expensive initialization is in contactServer(). } +static bool setFirstURL(Neon::URI &result, + const std::string &name, + const Neon::URI &uri) +{ + result = uri; + // stop + return false; +} + void WebDAVSource::contactServer() { if (!m_calendar.empty() && @@ -307,6 +321,9 @@ void WebDAVSource::contactServer() // we have done this work before, no need to repeat it } + SE_LOG_DEBUG(NULL, NULL, "using libneon %s with %s", + ne_version_string(), Neon::features().c_str()); + // Can we skip auto-detection because a full resource URL is set? std::string database = getDatabaseID(); if (!database.empty() && @@ -323,6 +340,59 @@ void WebDAVSource::contactServer() return; } + // Create session and find first collection (the default). + m_calendar = Neon::URI(); + findCollections(boost::bind(setFirstURL, + boost::ref(m_calendar), + _1, _2)); + if (m_calendar.empty()) { + throwError("no database found"); + } + SE_LOG_DEBUG(NULL, NULL, "picked final path %s", m_calendar.m_path.c_str()); + + // Check some server capabilities. Purely informational at this + // point, doesn't have to succeed either (Google 401 throttling + // workaround not active here, so it may really fail!). +#ifdef HAVE_LIBNEON_OPTIONS + if (LoggerBase::instance().getLevel() >= Logger::DEV) { + try { + SE_LOG_DEBUG(NULL, NULL, "read capabilities of %s", m_calendar.toURL().c_str()); + m_session->startOperation("OPTIONS", Timespec()); + int caps = m_session->options(m_calendar.m_path); + static const Flag descr[] = { + { NE_CAP_DAV_CLASS1, "Class 1 WebDAV (RFC 2518)" }, + { NE_CAP_DAV_CLASS2, "Class 2 WebDAV (RFC 2518)" }, + { NE_CAP_DAV_CLASS3, "Class 3 WebDAV (RFC 4918)" }, + { NE_CAP_MODDAV_EXEC, "mod_dav 'executable' property" }, + { NE_CAP_DAV_ACL, "WebDAV ACL (RFC 3744)" }, + { NE_CAP_VER_CONTROL, "DeltaV version-control" }, + { NE_CAP_CO_IN_PLACE, "DeltaV checkout-in-place" }, + { NE_CAP_VER_HISTORY, "DeltaV version-history" }, + { NE_CAP_WORKSPACE, "DeltaV workspace" }, + { NE_CAP_UPDATE, "DeltaV update" }, + { NE_CAP_LABEL, "DeltaV label" }, + { NE_CAP_WORK_RESOURCE, "DeltaV working-resouce" }, + { NE_CAP_MERGE, "DeltaV merge" }, + { NE_CAP_BASELINE, "DeltaV baseline" }, + { NE_CAP_ACTIVITY, "DeltaV activity" }, + { NE_CAP_VC_COLLECTION, "DeltaV version-controlled-collection" }, + { 0, NULL } + }; + SE_LOG_DEBUG(NULL, NULL, "%s WebDAV capabilities: %s", + m_session->getURL().c_str(), + Flags2String(caps, descr).c_str()); + } catch (...) { + Exception::handle(); + } + } +#endif // HAVE_LIBNEON_OPTIONS +} + +bool WebDAVSource::findCollections(const boost::function<bool (const std::string &, + const Neon::URI &)> &storeResult) +{ + bool res = true; // completed + bool found = false; int timeoutSeconds = m_settings->timeoutSeconds(); int retrySeconds = m_settings->retrySeconds(); SE_LOG_DEBUG(this, NULL, "timout %ds, retry %ds => %s", @@ -330,14 +400,6 @@ void WebDAVSource::contactServer() (timeoutSeconds <= 0 || retrySeconds <= 0) ? "resending disabled" : "resending allowed"); - // ignore the "Request ends, status 207 class 2xx, error line:" printed by neon - LogRedirect::addIgnoreError(", error line:"); - // ignore error messages in returned data - LogRedirect::addIgnoreError("Read block ("); - - SE_LOG_DEBUG(NULL, NULL, "using libneon %s with %s", - ne_version_string(), Neon::features().c_str()); - std::string username, password; m_contextSettings->getCredentials("", username, password); @@ -588,7 +650,12 @@ void WebDAVSource::contactServer() ex.getLocation().c_str()); if (candidates.empty()) { // nothing left to try, bail out with this error - throw; + // unless we found something else already + if (found) { + break; + } else { + throw; + } } } else { candidates.push_front(next.m_path); @@ -629,7 +696,8 @@ void WebDAVSource::contactServer() } if (success) { - if (m_davProps.find(path) == m_davProps.end()) { + Props_t::iterator pathProps = m_davProps.find(path); + if (pathProps == m_davProps.end()) { // No reply for requested path? Happens with Yahoo Calendar server, // which returns information about "/dav" when asked about "/". // Move to that path. @@ -640,10 +708,27 @@ void WebDAVSource::contactServer() path = newpath; } } - StringMap &props = m_davProps[path]; + StringMap &props = pathProps->second; if (typeMatches(props)) { - // found it - break; + StringMap::const_iterator it; + + // TODO: filter out CalDAV collections which do + // not contain the right components + // (urn:ietf:params:xml:ns:caldav:supported-calendar-component-set) + + // found something + found = true; + it = props.find("DAV::displayname"); + Neon::URI uri = m_session->getURI(); + uri.m_path = path; + res = storeResult(it == props.end() ? + std::string("<no name>") : + it->second, + uri); + if (!res) { + // done + break; + } } // find next path: @@ -691,7 +776,7 @@ void WebDAVSource::contactServer() // new candidates are: // - untested // - not already a candidate - // - a resource + // - a resource, but not the CalDAV schedule-inbox/outbox // - not shared ("global-addressbook" in Apple Calendar Server), // because these are unlikely to be the right "default" collection // @@ -703,6 +788,7 @@ void WebDAVSource::contactServer() if (tried.find(sub) == tried.end() && std::find(candidates.begin(), candidates.end(), sub) == candidates.end() && subType.find("<DAV:collection></DAV:collection>") != subType.npos && + subType.find("<urn:ietf:params:xml:ns:caldavschedule-") == subType.npos && subType.find("<http://calendarserver.org/ns/shared") == subType.npos) { // insert before other candidates (depth-first search) candidates.push_front(sub); @@ -719,8 +805,8 @@ void WebDAVSource::contactServer() if (next.empty()) { // use next candidate - if (candidates.empty()) { - throwError(StringPrintf("no collection found in %s", path.c_str())); + if (candidates.empty() ) { + break; } next = candidates.front(); candidates.pop_front(); @@ -734,47 +820,7 @@ void WebDAVSource::contactServer() path = next; } - // Pick final path. - m_calendar = m_session->getURI(); - m_calendar.m_path = path; - SE_LOG_DEBUG(NULL, NULL, "picked final path %s", m_calendar.m_path.c_str()); - - // Check some server capabilities. Purely informational at this - // point, doesn't have to succeed either (Google 401 throttling - // workaround not active here, so it may really fail!). -#ifdef HAVE_LIBNEON_OPTIONS - if (LoggerBase::instance().getLevel() >= Logger::DEV) { - try { - SE_LOG_DEBUG(NULL, NULL, "read capabilities of %s", m_calendar.toURL().c_str()); - m_session->startOperation("OPTIONS", Timespec()); - int caps = m_session->options(path); - static const Flag descr[] = { - { NE_CAP_DAV_CLASS1, "Class 1 WebDAV (RFC 2518)" }, - { NE_CAP_DAV_CLASS2, "Class 2 WebDAV (RFC 2518)" }, - { NE_CAP_DAV_CLASS3, "Class 3 WebDAV (RFC 4918)" }, - { NE_CAP_MODDAV_EXEC, "mod_dav 'executable' property" }, - { NE_CAP_DAV_ACL, "WebDAV ACL (RFC 3744)" }, - { NE_CAP_VER_CONTROL, "DeltaV version-control" }, - { NE_CAP_CO_IN_PLACE, "DeltaV checkout-in-place" }, - { NE_CAP_VER_HISTORY, "DeltaV version-history" }, - { NE_CAP_WORKSPACE, "DeltaV workspace" }, - { NE_CAP_UPDATE, "DeltaV update" }, - { NE_CAP_LABEL, "DeltaV label" }, - { NE_CAP_WORK_RESOURCE, "DeltaV working-resouce" }, - { NE_CAP_MERGE, "DeltaV merge" }, - { NE_CAP_BASELINE, "DeltaV baseline" }, - { NE_CAP_ACTIVITY, "DeltaV activity" }, - { NE_CAP_VC_COLLECTION, "DeltaV version-controlled-collection" }, - { 0, NULL } - }; - SE_LOG_DEBUG(NULL, NULL, "%s WebDAV capabilities: %s", - m_session->getURL().c_str(), - Flags2String(caps, descr).c_str()); - } catch (...) { - Exception::handle(); - } - } -#endif // HAVE_LIBNEON_OPTIONS + return res; } std::string WebDAVSource::extractHREF(const std::string &propval) @@ -829,13 +875,43 @@ void WebDAVSource::close() m_session.reset(); } +static bool storeCollection(SyncSource::Databases &result, + const std::string &name, + const Neon::URI &uri) +{ + std::string url = uri.toURL(); + + // avoid duplicates + BOOST_FOREACH(const SyncSource::Database &entry, result) { + if (entry.m_uri == url) { + // already found before + return true; + } + } + + result.push_back(SyncSource::Database(name, url)); + return true; +} + WebDAVSource::Databases WebDAVSource::getDatabases() { Databases result; - // TODO: scan for right collections - result.push_back(Database("select database via relative URI", - "<path>")); + // do a scan if at least username is set + std::string username, password; + m_contextSettings->getCredentials("", username, password); + + if (!username.empty()) { + findCollections(boost::bind(storeCollection, + boost::ref(result), + _1, _2)); + if (!result.empty()) { + result.front().m_isDefault = true; + } + } else { + result.push_back(Database("select database via absolute URL, set username/password to scan, set syncURL to base URL if server does not support auto-discovery", + "<path>")); + } return result; } diff --git a/src/backends/webdav/WebDAVSource.h b/src/backends/webdav/WebDAVSource.h index 6d97a796..0701b39e 100644 --- a/src/backends/webdav/WebDAVSource.h +++ b/src/backends/webdav/WebDAVSource.h @@ -54,6 +54,17 @@ class WebDAVSource : public TrackingSyncSource, private boost::noncopyable */ void contactServer(); + /** + * Scan server based on username/password/syncURL. Callback is + * passed name and URL of each collection (in this order), may + * return false to stop scanning gracefully or throw errors to + * abort. + * + * @return true if scanning completed, false if callback requested stop + */ + bool findCollections(const boost::function<bool (const std::string &, + const Neon::URI &)> &callback); + /** store resource URL permanently after successful sync */ void storeServerInfos(); |