1 /*
2 * Copyright (C) 2002-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "wui/login_box.h"
21
22 #include "base/i18n.h"
23 #include "graphic/font_handler.h"
24 #include "network/crypto.h"
25 #include "network/internet_gaming.h"
26 #include "network/internet_gaming_protocol.h"
27 #include "ui_basic/button.h"
28 #include "ui_basic/messagebox.h"
29 #include "wlapplication_options.h"
30
LoginBox(Panel & parent)31 LoginBox::LoginBox(Panel& parent)
32 : Window(&parent, "login_box", 0, 0, 500, 280, _("Online Game Settings")) {
33 center_to_parent();
34
35 int32_t margin = 10;
36
37 ta_nickname = new UI::Textarea(this, margin, margin, 0, 0, _("Nickname:"));
38 ta_password = new UI::Textarea(this, margin, 70, 0, 0, _("Password:"));
39 eb_nickname = new UI::EditBox(this, 150, margin, 330, UI::PanelStyle::kWui);
40 eb_password = new UI::EditBox(this, 150, 70, 330, UI::PanelStyle::kWui);
41
42 cb_register = new UI::Checkbox(this, Vector2i(margin, 40), _("Log in to a registered account."),
43 "", get_inner_w() - 2 * margin);
44
45 register_account = new UI::MultilineTextarea(
46 this, margin, 105, 470, 140, UI::PanelStyle::kWui,
47 (boost::format(_("In order to use a registered "
48 "account, you need an account on the Widelands website. "
49 "Please log in at %s and set an online "
50 "gaming password on your profile page.")) %
51 "\n\nhttps://widelands.org/accounts/register/\n\n")
52 .str());
53
54 loginbtn =
55 new UI::Button(this, "login", UI::g_fh->fontset()->is_rtl() ?
56 (get_inner_w() / 2 - 200) / 2 :
57 (get_inner_w() / 2 - 200) / 2 + get_inner_w() / 2,
58 get_inner_h() - 20 - margin, 200, 20, UI::ButtonStyle::kWuiPrimary, _("Save"));
59
60 cancelbtn =
61 new UI::Button(this, "cancel", UI::g_fh->fontset()->is_rtl() ?
62 (get_inner_w() / 2 - 200) / 2 + get_inner_w() / 2 :
63 (get_inner_w() / 2 - 200) / 2,
64 loginbtn->get_y(), 200, 20, UI::ButtonStyle::kWuiSecondary, _("Cancel"));
65
66 loginbtn->sigclicked.connect([this]() { clicked_ok(); });
67 cancelbtn->sigclicked.connect([this]() { clicked_back(); });
68 eb_nickname->changed.connect([this]() { change_playername(); });
69 cb_register->clickedto.connect([this](bool) { clicked_register(); });
70
71 eb_nickname->set_text(get_config_string("nickname", _("nobody")));
72 cb_register->set_state(get_config_bool("registered", false));
73 eb_password->set_password(true);
74
75 if (registered()) {
76 eb_password->set_text(get_config_string("password_sha1", ""));
77 loginbtn->set_enabled(false);
78 } else {
79 eb_password->set_can_focus(false);
80 ta_password->set_style(g_gr->styles().font_style(UI::FontStyle::kDisabled));
81 }
82
83 eb_nickname->focus();
84
85 eb_nickname->cancel.connect([this]() { clicked_back(); });
86 eb_password->cancel.connect([this]() { clicked_back(); });
87 }
88
89 /// think function of the UI (main loop)
think()90 void LoginBox::think() {
91 verify_input();
92 }
93
94 /**
95 * called, if "login" is pressed.
96 */
clicked_ok()97 void LoginBox::clicked_ok() {
98 if (cb_register->get_state()) {
99 if (check_password()) {
100 set_config_string("nickname", eb_nickname->text());
101 set_config_bool("registered", true);
102 end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kOk);
103 }
104 } else {
105 set_config_string("nickname", eb_nickname->text());
106 set_config_bool("registered", false);
107 set_config_string("password_sha1", "");
108 end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kOk);
109 }
110 }
111
112 /// Called if "cancel" was pressed
clicked_back()113 void LoginBox::clicked_back() {
114 end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
115 }
116
117 /// Called when nickname was changed
change_playername()118 void LoginBox::change_playername() {
119 cb_register->set_state(false);
120 eb_password->set_can_focus(false);
121 eb_password->set_text("");
122 }
123
handle_key(bool down,SDL_Keysym code)124 bool LoginBox::handle_key(bool down, SDL_Keysym code) {
125 if (down) {
126 switch (code.sym) {
127 case SDLK_KP_ENTER:
128 case SDLK_RETURN:
129 clicked_ok();
130 return true;
131 case SDLK_ESCAPE:
132 clicked_back();
133 return true;
134 default:
135 break; // not handled
136 }
137 }
138 return UI::Panel::handle_key(down, code);
139 }
140
clicked_register()141 void LoginBox::clicked_register() {
142 if (cb_register->get_state()) {
143 ta_password->set_style(g_gr->styles().font_style(UI::FontStyle::kDisabled));
144 eb_password->set_can_focus(false);
145 eb_password->set_text("");
146 } else {
147 ta_password->set_style(g_gr->styles().font_style(UI::FontStyle::kLabel));
148 eb_password->set_can_focus(true);
149 eb_password->focus();
150 }
151 }
152
verify_input()153 void LoginBox::verify_input() {
154 // Check if all neccessary input fields are valid
155 loginbtn->set_enabled(true);
156 eb_nickname->set_tooltip("");
157 eb_password->set_tooltip("");
158 eb_nickname->set_warning(false);
159
160 if (eb_nickname->text().empty()) {
161 eb_nickname->set_warning(true);
162 eb_nickname->set_tooltip(_("Please enter a nickname!"));
163 loginbtn->set_enabled(false);
164 } else if (!InternetGaming::ref().valid_username(eb_nickname->text())) {
165 eb_nickname->set_warning(true);
166 eb_nickname->set_tooltip(_("Enter a valid nickname. This value may contain only "
167 "English letters, numbers, and @ . + - _ characters."));
168 loginbtn->set_enabled(false);
169 }
170
171 if (eb_password->text().empty() && cb_register->get_state()) {
172 eb_password->set_tooltip(_("Please enter your password!"));
173 loginbtn->set_enabled(false);
174 }
175
176 if (eb_password->has_focus() && eb_password->text() == get_config_string("password_sha1", "")) {
177 eb_password->set_text("");
178 }
179
180 if (cb_register->get_state() && eb_password->text() == get_config_string("password_sha1", "")) {
181 loginbtn->set_enabled(false);
182 }
183 }
184
185 /// Check password against metaserver
check_password()186 bool LoginBox::check_password() {
187 // Try to connect to the metaserver
188 const std::string& meta = get_config_string("metaserver", INTERNET_GAMING_METASERVER.c_str());
189 uint32_t port = get_config_natural("metaserverport", kInternetGamingPort);
190 std::string password = crypto::sha1(eb_password->text());
191
192 if (!InternetGaming::ref().check_password(get_nickname(), password, meta, port)) {
193 // something went wrong -> show the error message
194 // idealy it is about the wrong password
195 ChatMessage msg = InternetGaming::ref().get_messages().back();
196 UI::WLMessageBox wmb(this, _("Error!"), msg.msg, UI::WLMessageBox::MBoxType::kOk);
197 wmb.run<UI::Panel::Returncodes>();
198 eb_password->set_text("");
199 eb_password->focus();
200 return false;
201 }
202 // NOTE: The password is only stored (in memory and on disk) and transmitted (over the network to
203 // the metaserver) as cryptographic hash. This does NOT mean that the password is stored securely
204 // on the local disk. While the password should be secure while transmitted to the metaserver
205 // (no-one can use the transmitted data to log in as the user) this is not the case for local
206 // storage. The stored hash of the password makes it hard to look at the configuration file and
207 // figure out the plaintext password to, e.g., log in on the forum. However, the stored hash can
208 // be copied to another system and used to log in as the user on the metaserver. Further note:
209 // SHA-1 is considered broken and shouldn't be used anymore. But since the passwords on the
210 // server are protected by SHA-1 we have to use it here, too
211 set_config_string("password_sha1", password);
212 InternetGaming::ref().logout();
213 return true;
214 }
215