>From cdeddefc56797e46ec087a980a72f5670e6df882 Mon Sep 17 00:00:00 2001
From: Salvatore Iovene <salvatore.iovene@linux.intel.com>
Date: Mon, 23 May 2011 12:04:51 +0300
Subject: [PATCH 3/3] WebDavSource.cpp: hijack error 404 to 401 when appropriate.

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
---
 src/backends/webdav/NeonCXX.cpp      |   12 ++++++++++-
 src/backends/webdav/WebDAVSource.cpp |   36 ++++++++++++++++++++++++++++-----
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/src/backends/webdav/NeonCXX.cpp b/src/backends/webdav/NeonCXX.cpp
index 5752a8c..0f4cf06 100644
--- a/src/backends/webdav/NeonCXX.cpp
+++ b/src/backends/webdav/NeonCXX.cpp
@@ -140,7 +140,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 += '/';
diff --git a/src/backends/webdav/WebDAVSource.cpp b/src/backends/webdav/WebDAVSource.cpp
index fe2b40f..cb26fee 100644
--- a/src/backends/webdav/WebDAVSource.cpp
+++ b/src/backends/webdav/WebDAVSource.cpp
@@ -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();
-- 
1.7.2.2

