From 3d8f868604e5787b73109964be346469e5073567 Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Sat, 9 May 2020 21:25:30 +0200
Subject: [PATCH] Follow redirects on well-known lookup

---
 lib/http/client.cpp | 62 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 11 deletions(-)

diff --git a/lib/http/client.cpp b/lib/http/client.cpp
index 9624e9282..ba2939e83 100644
--- a/lib/http/client.cpp
+++ b/lib/http/client.cpp
@@ -197,12 +197,19 @@ mtx::http::Client::get(const std::string &endpoint,
 void
 Client::set_server(const std::string &server)
 {
-        std::string server_name = server;
+        std::string_view server_name = server;
+        int port                     = 443;
         // Remove https prefix, if it exists
-        if (boost::algorithm::starts_with(server_name, "https://"))
-                boost::algorithm::erase_first(server_name, "https://");
+        if (server_name.substr(0, 8) == "https://") {
+                server_name.remove_prefix(8);
+                port = 443;
+        }
+        if (server_name.substr(0, 7) == "http://") {
+                server_name.remove_prefix(7);
+                port = 80;
+        }
         if (server_name.size() > 0 && server_name.back() == '/')
-                server_name.erase(server_name.end() - 1);
+                server_name.remove_suffix(1);
 
         // Check if the input also contains the port.
         std::vector<std::string> parts;
@@ -213,6 +220,7 @@ Client::set_server(const std::string &server)
                 port_   = std::stoi(parts.at(1));
         } else {
                 server_ = server_name;
+                port_   = port;
         }
 }
 
@@ -313,13 +321,45 @@ Client::login_sso_redirect(std::string redirectUrl)
 void
 Client::well_known(Callback<mtx::responses::WellKnown> callback)
 {
-        get<mtx::responses::WellKnown>(
-          "/matrix/client",
-          [callback](const mtx::responses::WellKnown &res, HeaderFields, RequestErr err) {
-                  callback(res, err);
-          },
-          false,
-          "/.well-known");
+        struct DoLookup
+        {
+                void operator()(const mtx::responses::WellKnown &res,
+                                HeaderFields headers,
+                                RequestErr err)
+                {
+                        if (err &&
+                            (err->status_code == boost::beast::http::status::found ||
+                             err->status_code == boost::beast::http::status::moved_permanently) &&
+                            numRedirects < 30 && headers &&
+                            headers->count(boost::beast::http::field::location)) {
+                                numRedirects++;
+                                auto server = headers.value()[boost::beast::http::field::location];
+                                size_t start_search = 0;
+                                if (server.starts_with("https://"))
+                                        start_search = 8;
+                                else if (server.starts_with("http://"))
+                                        start_search = 7;
+
+                                client->set_server(std::string(
+                                  server.substr(0, server.find_first_of('/', start_search))));
+
+                                client->get<mtx::responses::WellKnown>(
+                                  "/matrix/client", std::move(*this), false, "/.well-known");
+                                return;
+                        }
+
+                        callback(res, err);
+                }
+
+                Callback<mtx::responses::WellKnown> callback;
+                int numRedirects = 0;
+                Client *client   = nullptr;
+        } func;
+        func.numRedirects = 0;
+        func.callback     = std::move(callback);
+        func.client = this;
+
+        get<mtx::responses::WellKnown>("/matrix/client", std::move(func), false, "/.well-known");
 }
 
 void
-- 
GitLab