Don't store the user's password in plaintext in the prefs file

The game now supports @ or = in usernames for the purpose of saving them to a file.

Though I don't think these are allowed currently, it's probably better to
support it from the start rather than having things break if they later
become allowed.
This commit is contained in:
Celtic Minstrel 2017-04-19 17:28:51 -04:00
parent 640e28f731
commit 9365538b3b
29 changed files with 401 additions and 212 deletions

View file

@ -738,6 +738,7 @@
<ClCompile Include="..\..\src\config_cache.cpp" />
<ClCompile Include="..\..\src\controller_base.cpp" />
<ClCompile Include="..\..\src\countdown_clock.cpp" />
<ClCompile Include="..\..\src\preferences\credentials.cpp" />
<ClCompile Include="..\..\src\cursor.cpp" />
<ClCompile Include="..\..\src\desktop\clipboard.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Desktop\</ObjectFileName>
@ -3439,6 +3440,7 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\preferences\credentials.hpp" />
<ClInclude Include="..\..\src\about.hpp" />
<ClInclude Include="..\..\src\actions\advancement.hpp" />
<ClInclude Include="..\..\src\actions\attack.hpp" />

View file

@ -1536,6 +1536,9 @@
<ClCompile Include="..\..\src\preferences\lobby.cpp">
<Filter>Preferences</Filter>
</ClCompile>
<ClCompile Include="..\..\src\preferences\credentials.cpp">
<Filter>Preferences</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\addon\client.hpp">
@ -2983,6 +2986,9 @@
<ClInclude Include="..\..\src\preferences\lobby.hpp">
<Filter>Preferences</Filter>
</ClInclude>
<ClInclude Include="..\..\src\preferences\credentials.hpp">
<Filter>Preferences</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\tests\test_sdl_utils.hpp">

View file

@ -28,6 +28,7 @@ hotkey/hotkey_item.cpp
hotkey/hotkey_manager.cpp
image.cpp
image_modifications.cpp
preferences/credentials.cpp
preferences/general.cpp
joystick.cpp
key.cpp

View file

@ -20,6 +20,7 @@
#include "log.hpp"
#include "map_command_handler.hpp"
#include "chat_command_handler.hpp"
#include "preferences/credentials.hpp"
#include "preferences/general.hpp"
#include "preferences/game.hpp"

View file

@ -24,6 +24,7 @@
#include "mp_ui_alerts.hpp"
#include "serialization/string_utils.hpp"
#include "color.hpp"
#include "preferences/credentials.hpp"
#include <SDL_timer.h>

View file

@ -73,6 +73,7 @@ std::string get_dir(const std::string &dir);
// The location of various important files:
std::string get_prefs_file();
std::string get_credentials_file();
std::string get_default_prefs_file();
std::string get_save_index_file();
std::string get_saves_dir();

View file

@ -34,6 +34,11 @@ std::string get_prefs_file()
return get_user_config_dir() + "/preferences";
}
std::string get_credentials_file()
{
return get_user_config_dir() + "/credentials";
}
std::string get_default_prefs_file()
{
#ifdef HAS_RELATIVE_DEFPREF

View file

@ -17,6 +17,7 @@
#include "mp_game_settings.hpp"
#include "settings.hpp"
#include "tod_manager.hpp"
#include "preferences/credentials.hpp"
#include <cassert>
#include <sstream>

View file

@ -15,6 +15,7 @@
#include "ai/configuration.hpp"
#include "config_assign.hpp"
#include "preferences/credentials.hpp"
#include "formula/string_utils.hpp"
#include "game_initialization/mp_game_utils.hpp"
#include "game_initialization/playcampaign.hpp"

View file

@ -15,6 +15,7 @@
#include "game_initialization/create_engine.hpp"
#include "config_assign.hpp"
#include "preferences/credentials.hpp"
#include "filesystem.hpp"
#include "formula/string_utils.hpp"
#include "game_config_manager.hpp"

View file

@ -15,6 +15,7 @@
#include "game_initialization/lobby_data.hpp"
#include "config.hpp"
#include "preferences/credentials.hpp"
#include "preferences/game.hpp"
#include "gui/dialogs/campaign_difficulty.hpp"
#include "filesystem.hpp"

View file

@ -16,6 +16,7 @@
#include "addon/manager.hpp" // for installed_addons
#include "config_assign.hpp"
#include "preferences/credentials.hpp"
#include "formula/string_utils.hpp"
#include "game_config_manager.hpp"
#include "game_initialization/mp_game_utils.hpp"
@ -191,7 +192,7 @@ static std::unique_ptr<wesnothd_connection> open_connection(CVideo& video, const
if(!*error) break;
do {
std::string password = preferences::password();
std::string password = preferences::password(host, login);
bool fall_through = (*error)["force_confirmation"].to_bool() ?
(gui2::show_message(video, _("Confirm"), (*error)["message"], gui2::dialogs::message::ok_cancel_buttons) == gui2::window::CANCEL) :
@ -289,7 +290,7 @@ static std::unique_ptr<wesnothd_connection> open_connection(CVideo& video, const
error_message = (*error)["message"].str();
}
gui2::dialogs::mp_login dlg(error_message, !((*error)["password_request"].empty()));
gui2::dialogs::mp_login dlg(host, error_message, !((*error)["password_request"].empty()));
dlg.show(video);
switch(dlg.get_retval()) {

View file

@ -15,6 +15,7 @@
#include "game_launcher.hpp"
#include "game_errors.hpp"
#include "preferences/credentials.hpp"
#include "commandline_options.hpp" // for commandline_options
#include "config.hpp" // for config, etc
#include "config_assign.hpp"
@ -239,14 +240,14 @@ game_launcher::game_launcher(const commandline_options& cmdline_opts, const char
else
multiplayer_server_ = "";
}
}
if (cmdline_opts_.username) {
preferences::disable_preferences_save();
preferences::set_login(*cmdline_opts_.username);
}
if (cmdline_opts_.password) {
preferences::disable_preferences_save();
preferences::set_password(*cmdline_opts_.password);
if (cmdline_opts_.username) {
preferences::disable_preferences_save();
preferences::set_login(*cmdline_opts_.username);
if (cmdline_opts_.password) {
preferences::disable_preferences_save();
preferences::set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
}
}
}
if (cmdline_opts_.test)
{

View file

@ -22,7 +22,7 @@
#include "formula/string_utils.hpp"
#include "game_board.hpp"
#include "game_display.hpp"
#include "preferences/game.hpp"
#include "preferences/credentials.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/dialogs/helper.hpp"
#include "gui/widgets/label.hpp"

View file

@ -21,7 +21,7 @@
#include "game_config.hpp"
#include "game_config_manager.hpp"
#include "game_initialization/mp_game_utils.hpp"
#include "preferences/game.hpp"
#include "preferences/credentials.hpp"
#include "gettext.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/dialogs/helper.hpp"

View file

@ -16,8 +16,9 @@
#include "gui/dialogs/multiplayer/mp_login.hpp"
#include "preferences/game.hpp"
#include "preferences/credentials.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/auxiliary/field.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/password_box.hpp"
#include "gui/widgets/settings.hpp"
@ -64,24 +65,32 @@ namespace dialogs
REGISTER_DIALOG(mp_login)
mp_login::mp_login(const std::string& label, const bool focus_password)
mp_login::mp_login(const std::string& host, const std::string& label, const bool focus_password)
: host_(host), focus_password_(focus_password)
{
register_label("login_label", false, label);
register_text("user_name", true,
username_ = register_text("user_name", true,
&preferences::login,
&preferences::set_login,
!focus_password);
register_text("password", true,
&preferences::password,
nullptr /* The password box returns '*' as value. */,
focus_password);
register_bool("remember_password", false,
&preferences::remember_password,
&preferences::set_remember_password);
}
void mp_login::load_password(window& win) const
{
text_box& pwd = find_widget<text_box>(&win, "password", false);
pwd.set_value(preferences::password(host_, username_->get_widget_value(win)));
}
void mp_login::save_password(window& win) const
{
password_box& pwd = find_widget<password_box>(&win, "password", false);
preferences::set_password(host_, username_->get_widget_value(win), pwd.get_real_value());
}
void mp_login::pre_show(window& win)
{
if(button* btn = find_widget<button>(&win, "password_reminder", false, false)) {
@ -93,12 +102,20 @@ void mp_login::pre_show(window& win)
btn->set_retval(2);
}
text_box& login = find_widget<text_box>(&win, "user_name", false);
login.connect_signal<event::RECEIVE_KEYBOARD_FOCUS>(std::bind(&mp_login::load_password, this, std::ref(win)));
load_password(win);
if(focus_password_) {
win.keyboard_capture(find_widget<text_box>(&win, "password", false, true));
}
}
void mp_login::post_show(window& win)
{
void mp_login::post_show(window& win) {
if(get_retval() == window::OK) {
preferences::set_password(find_widget<password_box>(&win, "password", false).get_real_value());
save_password(win);
}
}

View file

@ -18,13 +18,15 @@
namespace gui2
{
class field_text;
namespace dialogs
{
class mp_login : public modal_dialog
{
public:
mp_login(const std::string& label, const bool focus_password);
mp_login(const std::string& host, const std::string& label, const bool focus_password);
private:
/** Inherited from modal_dialog, implemented by REGISTER_DIALOG. */
@ -35,6 +37,13 @@ private:
/** Inherited from modal_dialog. */
virtual void post_show(window& window) override;
void load_password(window& win) const;
void save_password(window& win) const;
const std::string host_;
field_text* username_;
bool focus_password_;
};
} // namespace dialogs

View file

@ -16,7 +16,7 @@
#include "gui/dialogs/multiplayer/mp_method_selection.hpp"
#include "preferences/game.hpp"
#include "preferences/credentials.hpp"
#include "gui/auxiliary/find_widget.hpp"
#ifdef GUI2_EXPERIMENTAL_LISTBOX
#include "gui/widgets/list.hpp"

View file

@ -18,6 +18,7 @@
#include "gui/dialogs/preferences_dialog.hpp"
#include "config_assign.hpp"
#include "preferences/credentials.hpp"
#include "gettext.hpp"
#include "filesystem.hpp"
#include "formatter.hpp"

View file

@ -34,6 +34,7 @@
#include "gettext.hpp"
#include "wesnothd_connection.hpp"
#include "config_assign.hpp"
#include "preferences/credentials.hpp"
#include "preferences/game.hpp"
#include "preferences/lobby.hpp"
#include "log.hpp"

View file

@ -30,6 +30,7 @@
#include "chat_command_handler.hpp"
#include "color.hpp"
#include "config_assign.hpp"
#include "preferences/credentials.hpp"
#include "display_chat_manager.hpp"
#include "font/standard_colors.hpp"
#include "formatter.hpp"

View file

@ -26,6 +26,7 @@
#include "actions/vision.hpp"
#include "ai/manager.hpp"
#include "ai/testing.hpp"
#include "preferences/credentials.hpp"
#include "display_chat_manager.hpp"
#include "formula/string_utils.hpp"
#include "game_events/manager.hpp"

View file

@ -0,0 +1,287 @@
/*
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "credentials.hpp"
#include "preferences/general.hpp"
#include "serialization/unicode.hpp"
#include "filesystem.hpp"
#include "log.hpp"
#include "serialization/string_utils.hpp"
#include <algorithm>
#include <sstream>
#ifdef _WIN32
#include <boost/range/iterator_range.hpp>
#include <windows.h>
#endif
static lg::log_domain log_config("config");
#define ERR_CFG LOG_STREAM(err , log_config)
struct login_info
{
std::string username, server, key;
login_info(const std::string& username, const std::string& server, const std::string& key)
: username(username), server(server), key(key)
{}
};
static std::vector<login_info> credentials;
// Separate password entries with formfeed
static const char CREDENTIAL_SEPARATOR = '\f';
static std::string encrypt(const std::string& text, const std::string& key);
static std::string decrypt(const std::string& text, const std::string& key);
static std::string build_key(const std::string& server, const std::string& login);
static std::string escape(const std::string& text);
static std::string unescape(const std::string& text);
static std::string get_system_username()
{
std::string res;
#ifdef _WIN32
wchar_t buffer[300];
DWORD size = 300;
if(GetUserNameW(buffer, &size)) {
//size includes a terminating null character.
assert(size > 0);
res = unicode_cast<utf8::string>(boost::iterator_range<wchar_t*>(buffer, buffer + size - 1));
}
#else
if(char* const login = getenv("USER")) {
res = login;
}
#endif
return res;
}
static void clear_credentials()
{
// Zero them before clearing.
// Probably overly paranoid, but doesn't hurt?
for(auto& cred : credentials) {
std::fill(cred.username.begin(), cred.username.end(), '\0');
std::fill(cred.server.begin(), cred.server.end(), '\0');
std::fill(cred.key.begin(), cred.key.end(), '\0');
}
credentials.clear();
}
static const std::string EMPTY_LOGIN = "@@";
namespace preferences
{
std::string login()
{
std::string name = preferences::get("login", EMPTY_LOGIN);
if(name == EMPTY_LOGIN) {
name = get_system_username();
} else if(name.size() > 2 && name[0] == '@' && name[name.size() - 1] == '@') {
name = name.substr(1, name.size() - 2);
} else {
ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)" << std::endl;
}
if(name.empty()) {
return "player";
}
return name;
}
void set_login(const std::string& login)
{
preferences::set("login", '@' + login + '@');
}
bool remember_password()
{
return preferences::get("remember_password", false);
}
void set_remember_password(bool remember)
{
preferences::set("remember_password", remember);
if(remember) {
load_credentials();
} else {
clear_credentials();
}
}
std::string password(const std::string& server, const std::string& login)
{
if(!remember_password()) {
if(!credentials.empty() && credentials[0].username == login && credentials[0].server == server) {
return decrypt(credentials[0].key, build_key(server, login));
} else {
return "";
}
}
auto cred = std::find_if(credentials.begin(), credentials.end(), [&](const login_info& cred) {
return cred.server == server && cred.username == login;
});
if(cred == credentials.end()) {
return "";
}
return decrypt(unescape(cred->key), build_key(server, login));
}
void set_password(const std::string& server, const std::string& login, const std::string& key)
{
if(!remember_password()) {
clear_credentials();
credentials.emplace_back(login, server, encrypt(key, build_key(server, login)));
return;
}
auto cred = std::find_if(credentials.begin(), credentials.end(), [&](const login_info& cred) {
return cred.server == server && cred.username == login;
});
if(cred == credentials.end()) {
// This is equivalent to emplace_back, but also returns the iterator to the new element
cred = credentials.emplace(credentials.end(), login, server, "");
}
cred->key = escape(encrypt(key, build_key(server, login)));
}
void load_credentials()
{
if(!remember_password()) {
return;
}
clear_credentials();
std::string cred_file = filesystem::get_credentials_file();
if(!filesystem::file_exists(cred_file)) {
return;
}
filesystem::scoped_istream stream = filesystem::istream_file(cred_file, false);
// Credentials file is a binary blob, so use streambuf iterator
std::string data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
data = decrypt(data, build_key("global", get_system_username()));
if(data.empty() || data[0] != CREDENTIAL_SEPARATOR) {
ERR_CFG << "Invalid data in credentials file\n";
std::fill(data.begin(), data.end(), '\0');
return;
}
for(const std::string elem : utils::split(data, CREDENTIAL_SEPARATOR, utils::REMOVE_EMPTY)) {
size_t at = elem.find_last_of('@');
size_t eq = elem.find_first_of('=', at + 1);
if(at != std::string::npos && eq != std::string::npos) {
credentials.emplace_back(elem.substr(0, at), elem.substr(at + 1, eq - at - 1), elem.substr(eq + 1));
}
}
std::fill(data.begin(), data.end(), '\0');
}
void save_credentials()
{
if(!remember_password()) {
filesystem::delete_file(filesystem::get_credentials_file());
return;
}
std::ostringstream credentials_data;
for(const auto& cred : credentials) {
credentials_data.put(CREDENTIAL_SEPARATOR);
std::copy(cred.username.begin(), cred.username.end(), std::ostreambuf_iterator<char>(credentials_data));
credentials_data.put('@');
std::copy(cred.server.begin(), cred.server.end(), std::ostreambuf_iterator<char>(credentials_data));
credentials_data.put('=');
std::copy(cred.key.begin(), cred.key.end(), std::ostreambuf_iterator<char>(credentials_data));
}
credentials_data.put(CREDENTIAL_SEPARATOR);
try {
filesystem::scoped_ostream credentials_file = filesystem::ostream_file(filesystem::get_credentials_file());
std::string encrypted = encrypt(credentials_data.str(), build_key("global", get_system_username()));
credentials_file->write(encrypted.c_str(), encrypted.size());
} catch(filesystem::io_exception&) {
ERR_CFG << "error writing to credentials file '" << filesystem::get_credentials_file() << "'" << std::endl;
}
size_t n = credentials_data.tellp();
credentials_data.seekp(0, std::ios::beg);
std::fill_n(std::ostreambuf_iterator<char>(credentials_data), n, '\0');
}
}
// TODO: Key-stretching (bcrypt was recommended)
std::string build_key(const std::string& server, const std::string& login)
{
std::ostringstream out;
out << login << get_system_username() << server;
return out.str();
}
// FIXME: XOR encryption is a really terrible choice - swap it out for something better!
// TODO: Maybe use cryptopp or something for AES encryption?
static std::string xor_crypt(std::string text, const std::string& key)
{
const size_t m = key.size();
for(size_t i = 0; i < text.size(); i++) {
text[i] ^= key[i % m];
}
return text;
}
std::string encrypt(const std::string& text, const std::string& key)
{
return xor_crypt(text, key);
}
std::string decrypt(const std::string& text, const std::string& key)
{
return xor_crypt(text, key);
}
std::string unescape(const std::string& text)
{
std::string unescaped;
unescaped.reserve(text.size());
bool escaping = false;
for(char c : text) {
if(escaping) {
if(c == '\xa') {
unescaped.push_back('\xc');
} else if(c == '.') {
unescaped.push_back('@');
} else {
unescaped.push_back(c);
}
escaping = false;
} else if(c == '\x1') {
escaping = true;
} else {
unescaped.push_back(c);
}
}
assert(!escaping);
return unescaped;
}
std::string escape(const std::string& text)
{
std::string escaped;
escaped.reserve(text.size());
for(char c : text) {
if(c == '\x1') {
escaped += "\x1\x1";
} else if(c == '\xc') {
escaped += "\x1\xa";
} else if(c == '@') {
escaped += "\x1.";
} else {
escaped.push_back(c);
}
}
return escaped;
}

View file

@ -0,0 +1,30 @@
/*
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include <string>
namespace preferences {
std::string login();
void set_login(const std::string& login);
std::string password(const std::string& server, const std::string& login);
void set_password(const std::string& server, const std::string& login, const std::string& key);
bool remember_password();
void set_remember_password(bool remember);
void load_credentials();
void save_credentials();
}

View file

@ -28,20 +28,6 @@
#include "wml_exception.hpp"
#include <cassert>
#ifdef _WIN32
#include <boost/range/iterator_range.hpp>
#ifdef INADDR_ANY
#undef INADDR_ANY
#endif
#ifdef INADDR_BROADCAST
#undef INADDR_BROADCAST
#endif
#ifdef INADDR_NONE
#undef INADDR_NONE
#endif
#include <windows.h> //GetUserName
#endif
static lg::log_domain log_config("config");
#define ERR_CFG LOG_STREAM(err , log_config)
@ -72,28 +58,6 @@ bool options_initialized = false;
bool authenticated = false;
const char WRAP_CHAR = '@';
const std::string EMPTY_WRAPPED_STRING = "@@";
std::string wrap_credentials_field_value(const std::string& value)
{
return WRAP_CHAR + value + WRAP_CHAR;
}
std::string parse_wrapped_credentials_field(const std::string& raw)
{
if(raw.empty() || raw == EMPTY_WRAPPED_STRING) {
// empty (wrapped or not)
return raw;
} else if(raw.length() < 2 || raw[0] != WRAP_CHAR || raw[raw.length() - 1] != WRAP_CHAR ) {
// malformed/not wrapped (shouldn't happen)
ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)" << std::endl;
return raw;
}
return raw.substr(1, raw.length() - 2);
}
void initialize_modifications(bool mp = true)
{
if (mp) {
@ -453,117 +417,6 @@ void set_campaign_server(const std::string& host)
preferences::set("campaign_server", host);
}
bool wrap_password()
{
const bool have_old_password_format =
(!preferences::have_setting("password_is_wrapped")) && preferences::have_setting("password");
return have_old_password_format ? false : preferences::get("password_is_wrapped", true);
}
void set_wrap_password(bool wrap)
{
preferences::set("password_is_wrapped", wrap);
}
bool wrap_login()
{
const bool have_old_login_format =
(!preferences::have_setting("login_is_wrapped")) && preferences::have_setting("login");
return have_old_login_format ? false : preferences::get("login_is_wrapped", true);
}
void set_wrap_login(bool wrap)
{
preferences::set("login_is_wrapped", wrap);
}
static std::string get_system_username()
{
std::string res;
#ifdef _WIN32
wchar_t buffer[300];
DWORD size = 300;
if(GetUserNameW(buffer,&size)) {
//size includes a terminating null character.
assert(size > 0);
res = unicode_cast<utf8::string>(boost::iterator_range<wchar_t*>(buffer, buffer + size - 1));
}
#else
if(char* const login = getenv("USER")) {
res = login;
}
#endif
return res;
}
std::string login()
{
const std::string res = preferences::get("login");
if(res.empty() || res == EMPTY_WRAPPED_STRING) {
const std::string& login = get_system_username();
if(!login.empty()) {
return login;
}
if(res.empty()) {
return _("player");
}
}
if(!wrap_login()) {
return res;
} else {
return parse_wrapped_credentials_field(res);
}
}
void set_login(const std::string& username)
{
set_wrap_login(true);
preferences::set("login", wrap_credentials_field_value(username));
}
namespace prv {
std::string password;
}
std::string password()
{
if(remember_password()) {
const std::string saved_pass = preferences::get("password");
if(!wrap_password()) {
return saved_pass;
} else {
return parse_wrapped_credentials_field(saved_pass);
}
} else {
return prv::password;
}
}
void set_password(const std::string& password)
{
prv::password = password;
if(remember_password()) {
set_wrap_password(true);
preferences::set("password", wrap_credentials_field_value(password));
}
}
bool remember_password()
{
return preferences::get("remember_password", false);
}
void set_remember_password(bool remember)
{
preferences::set("remember_password", remember);
if(!remember) {
preferences::set("password", "");
}
}
bool turn_dialog()
{
return preferences::get("turn_dialog", false);

View file

@ -83,45 +83,6 @@ class acquaintance;
std::string campaign_server();
void set_campaign_server(const std::string& host);
/**
* Returns whether the MP username is stored wrapped in markers.
*
* New usernames are stored in a specific format to force string interpretation
* (due to bug #16571).
*/
bool wrap_login();
void set_wrap_login(bool wrap);
std::string login();
void set_login(const std::string& username);
// If password remembering is turned off use
// prv::password instead. This way we will not
// have to worry about whether to remember the
// password or not anywhere else in the code.
//
// It is put into a separate namespace to make clear
// it is "private" and not supposed to be edit outside
// of the preferences functions.
namespace prv {
extern std::string password;
}
/**
* Returns whether the password is stored wrapped in markers.
*
* New passwords are stored in a specific format to force string interpretation
* (due to bug #16571).
*/
bool wrap_password();
void set_wrap_password(bool wrap);
std::string password();
void set_password(const std::string& password);
bool remember_password();
void set_remember_password(bool remember);
bool turn_dialog();
void set_turn_dialog(bool ison);

View file

@ -25,6 +25,7 @@
#include "hotkey/hotkey_item.hpp"
#include "lexical_cast.hpp"
#include "log.hpp"
#include "credentials.hpp"
#include "preferences/general.hpp"
#include "sound.hpp"
#include "video.hpp" // non_interactive()
@ -104,6 +105,7 @@ base_manager::base_manager()
<< e.what()
<< std::endl;
}
preferences::load_credentials();
}
base_manager::~base_manager()
@ -162,6 +164,8 @@ void write_preferences()
ERR_FS << "error writing to preferences file '" << filesystem::get_prefs_file() << "'" << std::endl;
}
preferences::save_credentials();
#ifndef _WIN32
if(!prefs_file_existed) {

View file

@ -20,6 +20,7 @@
#include "server/game.hpp"
#include "server/player_network.hpp"
#include "serialization/string_utils.hpp"
#include "preferences/credentials.hpp"
#include <sstream>
#include <iomanip>

View file

@ -946,7 +946,7 @@ struct dialog_tester<mp_login>
{
mp_login* create()
{
return new mp_login("label", true);
return new mp_login("wesnoth.org", "label", true);
}
};