summaryrefslogtreecommitdiffstats
path: root/src/RegisterPage.cpp
blob: 93e2cf1bb33a7562456defd6b86a0ac481b1a5b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include <set>

#include <mtx/responses/common.hpp>
#include <mtx/responses/register.hpp>
#include <mtx/responses/version.hpp>
#include <mtx/responses/well-known.hpp>
#include <mtxclient/http/client.hpp>

#include "Logging.h"
#include "LoginPage.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "RegisterPage.h"
#include "ui/UIA.h"

RegisterPage::RegisterPage(QObject *parent)
  : QObject(parent)
{
    connect(this, &RegisterPage::registerOk, this, [] { MainWindow::instance()->showChatPage(); });
}

void
RegisterPage::setError(const QString &err)
{
    registrationError_ = err;
    emit errorChanged();
    registering_ = false;
    emit registeringChanged();
}
void
RegisterPage::setHsError(const QString &err)
{
    hsError_ = err;
    emit hsErrorChanged();
    lookingUpHs_ = false;
    emit lookingUpHsChanged();
}

QString
RegisterPage::initialDeviceName() const
{
    return QString::fromStdString(LoginPage::initialDeviceName_());
}

void
RegisterPage::setServer(const QString &server)
{
    if (server == lastServer)
        return;

    lastServer = server;

    http::client()->set_server(server.toStdString());
    http::client()->verify_certificates(!UserSettings::instance()->disableCertificateValidation());

    hsError_.clear();
    emit hsErrorChanged();
    supported_   = false;
    lookingUpHs_ = true;
    emit lookingUpHsChanged();

    http::client()->well_known(
      [this, prevServer = server](const mtx::responses::WellKnown &res, mtx::http::RequestErr err) {
          // server changed in between
          if (lastServer != prevServer)
              return;

          if (err) {
              if (err->status_code == 404) {
                  nhlog::net()->info("Autodiscovery: No .well-known.");
                  // Check that the homeserver can be reached
                  versionsCheck();
                  return;
              }

              if (!err->parse_error.empty()) {
                  setHsError(tr("Autodiscovery failed. Received malformed response."));
                  nhlog::net()->error("Autodiscovery failed. Received malformed response. {}",
                                      err->parse_error);
                  emit hsErrorChanged();
                  return;
              }

              setHsError(tr("Autodiscovery failed. Unknown error when requesting .well-known."));
              nhlog::net()->error("Autodiscovery failed. Unknown error when "
                                  "requesting .well-known. {}",
                                  *err);
              return;
          }

          nhlog::net()->info("Autodiscovery: Discovered '" + res.homeserver.base_url + "'");
          http::client()->set_server(res.homeserver.base_url);
          emit hsErrorChanged();
          // Check that the homeserver can be reached
          versionsCheck();
      });
}

void
RegisterPage::versionsCheck()
{
    // Make a request to /_matrix/client/versions to check the address
    // given is a Matrix homeserver.
    http::client()->versions([this](const mtx::responses::Versions &versions,
                                    mtx::http::RequestErr err) {
        if (err) {
            if (err->status_code == 404) {
                setHsError(
                  tr("The required endpoints were not found. Possibly not a Matrix server."));
                emit hsErrorChanged();
                return;
            }

            if (!err->parse_error.empty()) {
                setHsError(
                  tr("Received malformed response. Make sure the homeserver domain is valid."));
                emit hsErrorChanged();
                return;
            }

            setHsError(tr("An unknown error occured. Make sure the homeserver domain is valid."));
            emit hsErrorChanged();
            return;
        }

        if (std::find_if(
              versions.versions.cbegin(), versions.versions.cend(), [](const std::string &v) {
                  static const std::set<std::string_view, std::less<>> supported{
                    "v1.1",
                    "v1.2",
                    "v1.3",
                    "v1.4",
                    "v1.5",
                    "v1.6",
                    "v1.7",
                    "v1.8",
                    "v1.9",
                    "v1.10",
                  };
                  return supported.count(v) != 0;
              }) == versions.versions.cend()) {
            emit setHsError(
              tr("The selected server does not support a version of the Matrix protocol that "
                 "this client understands (%1 to %2). You can't register.")
                .arg(u"v1.1", u"v1.10"));
            emit hsErrorChanged();
            return;
        }

        http::client()->registration([this](const mtx::responses::Register &,
                                            mtx::http::RequestErr e) {
            nhlog::net()->debug("Registration check: {}", e);

            if (!e) {
                setHsError(tr("Server does not support querying registration flows!"));
                emit hsErrorChanged();
                return;
            }
            if (e->status_code != 401) {
                setHsError(tr("Server does not support registration."));
                emit hsErrorChanged();
                return;
            }

            for (const auto &f : e->matrix_error.unauthorized.flows)
                nhlog::ui()->debug("Registration flows for server: {}", fmt::join(f.stages, ", "));

            supported_   = true;
            lookingUpHs_ = false;
            emit lookingUpHsChanged();
        });
    });
}

void
RegisterPage::checkUsername(const QString &name)
{
    usernameAvailable_ = usernameUnavailable_ = false;
    usernameError_.clear();
    lookingUpUsername_ = true;
    emit lookingUpUsernameChanged();

    http::client()->register_username_available(
      name.toStdString(),
      [this](const mtx::responses::Available &available, mtx::http::RequestErr e) {
          if (e) {
              if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_INVALID_USERNAME) {
                  usernameError_ = tr("Invalid username.");
              } else if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_USER_IN_USE) {
                  usernameError_ = tr("Name already in use.");
              } else if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_EXCLUSIVE) {
                  usernameError_ = tr("Part of the reserved namespace.");
              } else {
              }

              usernameAvailable_   = false;
              usernameUnavailable_ = true;
          } else {
              usernameAvailable_   = available.available;
              usernameUnavailable_ = !available.available;
          }
          lookingUpUsername_ = false;
          emit lookingUpUsernameChanged();
      });
}

void
RegisterPage::startRegistration(const QString &username,
                                const QString &password,
                                const QString &devicename)
{
    // These inputs should still be alright, but check just in case
    if (!username.isEmpty() && !password.isEmpty() && usernameAvailable_ && supported_) {
        registrationError_.clear();
        emit errorChanged();
        registering_ = true;
        emit registeringChanged();

        connect(UIA::instance(), &UIA::error, this, [this](const QString &msg) {
            setError(msg);
            disconnect(UIA::instance(), &UIA::error, this, nullptr);
        });
        http::client()->registration(
          username.toStdString(),
          password.toStdString(),
          ::UIA::instance()->genericHandler(QStringLiteral("Registration")),
          [this](const mtx::responses::Register &res, mtx::http::RequestErr err) {
              registering_ = false;
              emit registeringChanged();

              if (!err) {
                  http::client()->set_user(res.user_id);
                  http::client()->set_access_token(res.access_token);
                  emit registerOk();
                  disconnect(UIA::instance(), &UIA::error, this, nullptr);
                  return;
              }

              // The server requires registration flows.
              if (err->status_code == 401 && err->matrix_error.unauthorized.flows.empty()) {
                  nhlog::net()->warn("failed to retrieve registration flows: {}", *err);
                  setError(QString::fromStdString(err->matrix_error.error));
                  disconnect(UIA::instance(), &UIA::error, this, nullptr);
                  return;
              }

              nhlog::net()->error("failed to register: {}", *