somewhat clearing the multiplayer login dialog mess:

- added the possibility to provide a password in the mp method selection dialog

- merged all dialogs that displayed erros or allowed entering the
  username or password into one dialog.

- password is now saved in preferences (todo: add option to turn this off)
This commit is contained in:
Thomas Baumhauer 2009-01-13 20:46:19 +00:00
parent 51fa967ce8
commit 61fc432f9b
8 changed files with 282 additions and 146 deletions

View file

@ -55,6 +55,59 @@
[/row]
[row]
grow_factor = 1
[column]
grow_factor = 1
horizontal_grow = "true"
[grid]
[row]
grow_factor = 1
[column]
grow_factor = 0
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
definition = "default"
label = _ "Login:"
[/label]
[/column]
[column]
grow_factor = 1
border = "all"
border_size = 5
horizontal_alignment = "right"
[text_box]
id = "user_name"
definition = "default"
history = "mp_user_name_history"
label = ""
[/text_box]
[/column]
[/row]
[/grid]
[/column]
[/row]
[row]
grow_factor = 1
@ -88,7 +141,7 @@
border = "all"
border_size = 5
horizontal_grow = "true"
horizontal_alignment = "right"
[text_box]
id = "password"
@ -111,8 +164,8 @@
grow_factor = 0
[column]
grow_factor = 1
horizontal_grow = "true"
grow_factor = 0
horizontal_alignment = "right"
[grid]
@ -133,20 +186,6 @@
[/column]
[column]
border = "all"
border_size = 5
horizontal_alignment = "right"
[button]
definition = "default"
id = "change_username"
label = _ "Change username"
[/button]
[/column]
[column]
border = "all"
border_size = 5

View file

@ -68,7 +68,7 @@
border = "all"
border_size = 5
horizontal_grow = "true"
horizontal_alignment = "right"
[text_box]
id = "user_name"
@ -88,6 +88,59 @@
[/row]
[row]
grow_factor = 1
[column]
grow_factor = 1
horizontal_grow = "true"
[grid]
[row]
grow_factor = 1
[column]
grow_factor = 0
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
definition = "default"
label = _ "Password:"
[/label]
[/column]
[column]
grow_factor = 1
border = "all"
border_size = 5
horizontal_alignment = "right"
[text_box]
id = "password"
definition = "default"
label = ""
[/text_box]
[/column]
[/row]
[/grid]
[/column]
[/row]
[row]
grow_factor = 1
vertical_grow = "true"

View file

@ -337,11 +337,21 @@ std::string login()
return res;
}
std::string password()
{
return preferences::get("password");
}
void set_login(const std::string& username)
{
preferences::set("login", username);
}
void set_password(const std::string& password)
{
preferences::set("password", password);
}
bool turn_dialog()
{
return utils::string_bool(preferences::get("turn_dialog"), false);

View file

@ -73,6 +73,9 @@ namespace preferences {
std::string login();
void set_login(const std::string& username);
std::string password();
void set_password(const std::string& password);
bool turn_dialog();
void set_turn_dialog(bool ison);

View file

@ -193,6 +193,7 @@ void tmp_connect::show_server_list(twindow& window)
* This shows the dialog to log in to the MP server
*
* @start_table = container
* user_name (text_box) the login user name
* password (text_box) the password
* [password_reminder] (button) Request a password reminder
* [change_username] (button) Use a different username
@ -201,7 +202,7 @@ void tmp_connect::show_server_list(twindow& window)
* @end_table
*/
tmp_login::tmp_login(const t_string& label) : password_(), label_(label) { }
tmp_login::tmp_login(const t_string& label) : label_(label) { }
twindow* tmp_login::build_window(CVideo& video)
{
@ -210,10 +211,16 @@ twindow* tmp_login::build_window(CVideo& video)
void tmp_login::pre_show(CVideo& /*video*/, twindow& window)
{
ttext_box* username =
dynamic_cast<ttext_box*>(window.find_widget("user_name", false));
VALIDATE(username, missing_widget("user_name"));
username->set_value(preferences::login());
window.keyboard_capture(username);
ttext_box* password =
dynamic_cast<ttext_box*>(window.find_widget("password", false));
VALIDATE(password, missing_widget("password"));
window.keyboard_capture(password);
password->set_value(preferences::password());
tbutton *password_reminder =
dynamic_cast<tbutton*>(window.find_widget("password_reminder", false));
@ -231,10 +238,17 @@ void tmp_login::pre_show(CVideo& /*video*/, twindow& window)
void tmp_login::post_show(twindow& window)
{
ttext_box* username =
dynamic_cast<ttext_box*>(window.find_widget("user_name", false));
assert(username);
preferences::set_login(username->get_value());
ttext_box* password =
dynamic_cast<ttext_box*>(window.find_widget("password", false));
VALIDATE(password, missing_widget("password"));
password_ = password->get_value();
assert(password);
preferences::set_password(password->get_value());
}
} // namespace gui2

View file

@ -50,8 +50,6 @@ class tmp_login : public tdialog
public:
tmp_login(const t_string& label);
const std::string& password() const { return password_; }
private:
/** Inherited from tdialog. */
twindow* build_window(CVideo& video);
@ -62,7 +60,6 @@ private:
/** Inherited from tdialog. */
void post_show(twindow& window);
std::string password_;
t_string label_;
};

View file

@ -53,6 +53,11 @@ void tmp_method_selection::pre_show(CVideo& /*video*/, twindow& window)
user_widget->set_maximum_length(mp::max_login_size);
window.keyboard_capture(user_widget);
ttext_box* password_widget = dynamic_cast<ttext_box*>(window.find_widget("password", false));
VALIDATE(password_widget, missing_widget("password"));
password_widget->set_value(preferences::password());
tlistbox* list = dynamic_cast<tlistbox*>(window.find_widget("method_list", false));
VALIDATE(list, missing_widget("method_list"));
@ -67,13 +72,19 @@ void tmp_method_selection::post_show(twindow& window)
ttext_box* user_widget = dynamic_cast<ttext_box*>(window.find_widget("user_name", false));
assert(user_widget);
ttext_box* password_widget = dynamic_cast<ttext_box*>(window.find_widget("password", false));
assert(password_widget);
tlistbox* list = dynamic_cast<tlistbox*>(window.find_widget("method_list", false));
assert(list);
choice_ = list->get_selected_row();
user_widget->save_to_history();
user_name_= user_widget->get_value();
preferences::set_login(user_name_);
preferences::set_password(password_widget->get_value());
}
}

View file

@ -176,134 +176,17 @@ static server_type open_connection(game_display& disp, const std::string& origin
//if we got a direction to login
if(data.child("mustlogin")) {
bool first_time = true;
config* error = NULL;
do {
std::string login = preferences::login();
std::string password = "";
for(;;) {
std::string password_reminder = "";
if(!first_time) {
//Somewhat hacky implementation, including a goto of death
/** @todo A fancy textbox that displays characters as dots or asterisks would nice. */
if(!((*error)["password_request"].empty())) {
gui2::tmp_login dlg((*error)["message"]);
dlg.show(disp.video());
password = dlg.password();
switch(dlg.get_retval()) {
//Log in with password
case gui2::twindow::OK:
break;
//Request a password reminder
case 1:
password_reminder = "yes";
break;
//Choose a different username
case 2:
password = "";
goto new_username;
break;
default: return ABORT_SERVER;
}
} else {
new_username:
const int res = gui::show_dialog(disp, NULL, "",
(*error)["message"], gui::OK_CANCEL,
NULL, NULL, _("Login: "), &login, mp::max_login_size);
if(res != 0 || login.empty()) {
return ABORT_SERVER;
}
preferences::set_login(login);
}
}
first_time = false;
std::string login = preferences::login();
config response ;
config &sp = response.add_child("login") ;
sp["username"] = login ;
sp["password_reminder"] = password_reminder;
// If password is not empty start hashing
std::string result;
if(!(password.empty())) {
// Check if we have everything we need
if((*error)["salt"].empty() || (*error)["hash_seed"].empty()) {
return ABORT_SERVER;
}
std::string itoa64("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
std::string salt = (*error)["salt"];
int hash_seed;
try {
hash_seed = lexical_cast_default<int>((*error)["hash_seed"]);
} catch (bad_lexical_cast) {
std::cerr << "Bad lexical cast reading hash_seed\n";
return ABORT_SERVER;
}
// Start the MD5 hashing
salt.append(password);
MD5 md5_worker;
md5_worker.update((unsigned char *)salt.c_str(),salt.length());
md5_worker.finalize();
unsigned char * output = (unsigned char *) malloc (sizeof(unsigned char) * 16);
output = md5_worker.raw_digest();
std::string temp_hash;
do {
temp_hash = std::string((char *) output, (char *) output + 16);
temp_hash.append(password);
md5_worker.~MD5();
MD5 md5_worker;
md5_worker.update((unsigned char *)temp_hash.c_str(),temp_hash.length());
md5_worker.finalize();
output = md5_worker.raw_digest();
} while (--hash_seed);
temp_hash = std::string((char *) output, (char *) output + 16);
// Now encode the resulting mix
std::string encoded_hash;
unsigned int i = 0, value;
do {
value = output[i++];
encoded_hash.append(itoa64.substr(value & 0x3f,1));
if(i < 16)
value |= (int)output[i] << 8;
encoded_hash.append(itoa64.substr((value >> 6) & 0x3f,1));
if(i++ >= 16)
break;
if(i < 16)
value |= (int)output[i] << 16;
encoded_hash.append(itoa64.substr((value >> 12) & 0x3f,1));
if(i++ >= 16)
break;
encoded_hash.append(itoa64.substr((value >> 18) & 0x3f,1));
} while (i < 16);
free (output);
// Now mix the resulting hash with the random seed
result = encoded_hash + (*error)["random_salt"];
MD5 md5_worker2;
md5_worker2.update((unsigned char *)result.c_str(), result.size());
md5_worker2.finalize();
result = std::string(md5_worker2.hex_digest());
}
sp["password"] = result;
// Login and enable selective pings -- saves server bandwidth
// If ping_timeout has a non-zero value, do not enable
// selective pings as this will cause clients to falsely
@ -318,13 +201,139 @@ static server_type open_connection(game_display& disp, const std::string& origin
}
network::send_data(response, 0, true);
// Get response for our login request...
network::connection data_res = network::receive_data(data, 0, 3000);
if(!data_res) {
throw network::error(_("Connection timed out"));
}
error = data.child("error");
} while(error != NULL);
config* error = data.child("error");
// ... and get us out of here if the server did not complain
if(!error) break;
do {
std::string password = preferences::password();
// If the server asks for a password, provide one if we can,
// otherwise go directly to the username/password dialog
if(!((*error)["password_request"].empty()) && !(password.empty())) {
// start the hashing
std::string result;
// Check if we have everything we need
if((*error)["salt"].empty() || (*error)["hash_seed"].empty()) {
return ABORT_SERVER;
}
std::string itoa64("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
std::string salt = (*error)["salt"];
int hash_seed;
try {
hash_seed = lexical_cast_default<int>((*error)["hash_seed"]);
} catch (bad_lexical_cast) {
std::cerr << "Bad lexical cast reading hash_seed\n";
return ABORT_SERVER;
}
// Start the MD5 hashing
salt.append(password);
MD5 md5_worker;
md5_worker.update((unsigned char *)salt.c_str(),salt.length());
md5_worker.finalize();
unsigned char * output = (unsigned char *) malloc (sizeof(unsigned char) * 16);
output = md5_worker.raw_digest();
std::string temp_hash;
do {
temp_hash = std::string((char *) output, (char *) output + 16);
temp_hash.append(password);
md5_worker.~MD5();
MD5 md5_worker;
md5_worker.update((unsigned char *)temp_hash.c_str(),temp_hash.length());
md5_worker.finalize();
output = md5_worker.raw_digest();
} while (--hash_seed);
temp_hash = std::string((char *) output, (char *) output + 16);
// Now encode the resulting mix
std::string encoded_hash;
unsigned int i = 0, value;
do {
value = output[i++];
encoded_hash.append(itoa64.substr(value & 0x3f,1));
if(i < 16)
value |= (int)output[i] << 8;
encoded_hash.append(itoa64.substr((value >> 6) & 0x3f,1));
if(i++ >= 16)
break;
if(i < 16)
value |= (int)output[i] << 16;
encoded_hash.append(itoa64.substr((value >> 12) & 0x3f,1));
if(i++ >= 16)
break;
encoded_hash.append(itoa64.substr((value >> 18) & 0x3f,1));
} while (i < 16);
free (output);
// Now mix the resulting hash with the random seed
result = encoded_hash + (*error)["random_salt"];
MD5 md5_worker2;
md5_worker2.update((unsigned char *)result.c_str(), result.size());
md5_worker2.finalize();
result = std::string(md5_worker2.hex_digest());
sp["password"] = result;
// Once again send our request...
network::send_data(response, 0, true);
network::connection data_res = network::receive_data(data, 0, 3000);
if(!data_res) {
throw network::error(_("Connection timed out"));
}
error = data.child("error");
// ... and get us out of here if the server is happy now
if(!error) break;
}
// Providing a password either was not attempted because we did not
// have any or failed:
// Now show a dialog that displays the error and allows to
// enter a new user name and/or password
/** @todo A fancy textbox that displays characters as dots or asterisks would nice. */
gui2::tmp_login dlg((*error)["message"]);
dlg.show(disp.video());
switch(dlg.get_retval()) {
//Log in with password
case gui2::twindow::OK:
break;
//Request a password reminder
case 1:
password_reminder = "yes";
break;
// Cancel
default: return ABORT_SERVER;
}
// If we have got a new username we have to start all over again
} while(login == preferences::login());
// Somewhat hacky...
// If we broke out of the do-while loop above error
// is still going to be NULL
if(!error) break;
} // end login loop
}
} while(!(data.child("join_lobby") || data.child("join_game")));