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:
parent
28126d61ee
commit
0d34ad46a6
|
@ -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 += '/';
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue