WebDavSource.cpp: hijack error 404 to 401 when appropriate (BMC #17862).

If we get a 404 error while contacting the server, it might mean
that the username was wrong, so the server gave us a not found
error. It's better to let the user know that, because we don't
have a clear heuristic to determin whether this might have been
a true 404 error.

The convertion of 404 errors to 401 should happen only if the URL
we're trying to open is one in which it was us who injected the
username into the URL. This was achieved by removing the username
injection from the context creation code, and moving it into the
loop that does the autodiscovery, adding it path by path as it
was necessary.
Notice: this required NeonCXX to be aware of the "%u" semantic,
something I'm not completely comfortable with.

See also: https://bugs.meego.com/show_bug.cgi?id=17862
This commit is contained in:
Salvatore Iovene 2011-05-23 12:04:51 +03:00
parent 28126d61ee
commit 0d34ad46a6
2 changed files with 41 additions and 7 deletions

View file

@ -143,7 +143,17 @@ std::string URI::normalizePath(const std::string &path, bool collection)
string_split_iterator it =
boost::make_split_iterator(path, boost::first_finder("/", boost::is_iequal()));
while (!it.eof()) {
res += escape(unescape(std::string(it->begin(), it->end())));
std::string split(it->begin(), it->end());
// Let's have an exception here for "%u", since we use that to replace the
// actual username into the path. It's safe to ignore "%u" because it
// couldn't be in a valid URI anyway.
// TODO: we should find a neat way to remove the awareness of "%u" from
// NeonCXX.
std::string normalizedSplit = split;
if (split != "%u") {
normalizedSplit = escape(unescape(split));
}
res += normalizedSplit;
++it;
if (!it.eof()) {
res += '/';

View file

@ -8,6 +8,7 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/find.hpp>
#include <boost/scoped_ptr.hpp>
#include <syncevo/LogRedirect.h>
@ -45,12 +46,14 @@ public:
{
if (m_context) {
vector<string> urls = m_context->getSyncURL();
string urlWithUsername;
if (!urls.empty()) {
m_url = urls.front();
m_url = urlWithUsername = urls.front();
std::string username = m_context->getSyncUsername();
boost::replace_all(m_url, "%u", Neon::URI::escape(username));
boost::replace_all(urlWithUsername, "%u", Neon::URI::escape(username));
}
Neon::URI uri = Neon::URI::parse(m_url);
Neon::URI uri = Neon::URI::parse(urlWithUsername);
typedef boost::split_iterator<string::iterator> string_split_iterator;
for (string_split_iterator arg =
boost::make_split_iterator(uri.m_query, boost::first_finder("&", boost::is_iequal()));
@ -77,13 +80,13 @@ public:
} else {
SE_THROW(StringPrintf("unknown SyncEvolution flag %s in URL %s",
std::string(flag->begin(), flag->end()).c_str(),
m_url.c_str()));
urlWithUsername.c_str()));
}
}
} else if (arg->end() != arg->begin()) {
SE_THROW(StringPrintf("unknown parameter %s in URL %s",
std::string(arg->begin(), arg->end()).c_str(),
m_url.c_str()));
urlWithUsername.c_str()));
}
}
boost::shared_ptr<FilterConfigNode> node = m_context->getNode(WebDAVCredentialsOkay);
@ -449,8 +452,18 @@ void WebDAVSource::contactServer()
Timespec finalDeadline = createDeadline(); // no resending if left empty
while (true) {
bool usernameInserted = false;
std::string next;
// Replace %u with the username, if the %u is found. Also, keep track
// of this event happening, because if we later on get a 404 error,
// we will convert it to 401 only if the path contains the username
// and it was indeed us who put the username there (not the server).
if (boost::find_first(path, "%u")) {
boost::replace_all(path, "%u", Neon::URI::escape(username));
usernameInserted = true;
}
// must normalize so that we can compare against results from server
path = Neon::URI::normalizePath(path, true);
SE_LOG_DEBUG(NULL, NULL, "testing %s", path.c_str());
@ -573,6 +586,17 @@ void WebDAVSource::contactServer()
} else {
candidates.push_front(next.m_path);
}
} catch (const TransportStatusException &ex) {
SE_LOG_DEBUG(NULL, NULL, "TransportStatusException: %s", ex.what());
if (ex.syncMLStatus() == 404 && boost::find_first(path, username) && usernameInserted) {
// We're actually looking at an authentication error: the path to the calendar has
// not been found, so the username was wrong. Let's hijack the error message and
// code of the exception by throwing a new one.
string descr = StringPrintf("Path not found: %s. Is the username '%s' correct?",
path.c_str(), username.c_str());
int code = 401;
SE_THROW_EXCEPTION_STATUS(TransportStatusException, descr, SyncMLStatus(code));
}
} catch (const Exception &ex) {
if (candidates.empty()) {
// nothing left to try, bail out with this error
@ -678,7 +702,7 @@ void WebDAVSource::contactServer()
if (next.empty()) {
// use next candidate
if (candidates.empty()) {
throwError(StringPrintf("no collection found in %s", m_settings->getURL().c_str()));
throwError(StringPrintf("no collection found in %s", path.c_str()));
}
next = candidates.front();
candidates.pop_front();