diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 58596a1..49982d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,6 +77,8 @@ set(common_SRCS player.cpp utility.cpp test.cpp + sha1.cpp + base64.cpp ) # Client sources @@ -91,12 +93,11 @@ set(minetest_SRCS guiTextInputMenu.cpp guiInventoryMenu.cpp guiPauseMenu.cpp + guiPasswordChange.cpp client.cpp tile.cpp game.cpp main.cpp - sha1.cpp - base64.cpp ) # Server sources diff --git a/src/client.cpp b/src/client.cpp index c85d6e9..5869dc7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1602,6 +1602,43 @@ void Client::sendChatMessage(const std::wstring &message) Send(0, data, true); } +void Client::sendChangePassword(const std::wstring oldpassword, + const std::wstring newpassword) +{ + Player *player = m_env.getLocalPlayer(); + if(player == NULL) + return; + + std::string playername = player->getName(); + std::string oldpwd = translatePassword(playername, oldpassword); + std::string newpwd = translatePassword(playername, newpassword); + + std::ostringstream os(std::ios_base::binary); + u8 buf[2+PASSWORD_SIZE*2]; + /* + [0] u16 TOSERVER_PASSWORD + [2] u8[28] old password + [30] u8[28] new password + */ + + writeU16(buf, TOSERVER_PASSWORD); + for(u32 i=0;i data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + + void Client::sendDamage(u8 damage) { DSTACK(__FUNCTION_NAME); diff --git a/src/client.h b/src/client.h index 222f83a..f661838 100644 --- a/src/client.h +++ b/src/client.h @@ -255,6 +255,8 @@ public: void sendSignNodeText(v3s16 p, std::string text); void sendInventoryAction(InventoryAction *a); void sendChatMessage(const std::wstring &message); + void sendChangePassword(const std::wstring oldpassword, + const std::wstring newpassword); void sendDamage(u8 damage); // locks envlock diff --git a/src/clientserver.h b/src/clientserver.h index a64a11f..256aed3 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -285,6 +285,16 @@ enum ToServerCommand u16 command u8 amount */ + + TOSERVER_PASSWORD=0x36, + /* + Sent to change password. + + [0] u16 TOSERVER_PASSWORD + [2] u8[28] old password + [30] u8[28] new password + */ + }; inline SharedBuffer makePacket_TOCLIENT_TIME_OF_DAY(u16 time) diff --git a/src/game.cpp b/src/game.cpp index 99e08b7..e82e4cd 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "server.h" #include "guiPauseMenu.h" +#include "guiPasswordChange.h" #include "guiInventoryMenu.h" #include "guiTextInputMenu.h" #include "guiFurnaceMenu.h" @@ -912,6 +913,13 @@ void the_game( break; } + if(g_gamecallback->changepassword_requested) + { + (new GUIPasswordChange(guienv, guiroot, -1, + &g_menumgr, &client))->drop(); + g_gamecallback->changepassword_requested = false; + } + /* Process TextureSource's queue */ diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp new file mode 100644 index 0000000..98b11b4 --- /dev/null +++ b/src/guiPasswordChange.cpp @@ -0,0 +1,252 @@ +/* +Minetest-c55 +Copyright (C) 2010-11 celeron55, Perttu Ahola +Copyright (C) 2011 Ciaran Gultnieks + +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; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiPasswordChange.h" +#include "debug.h" +#include "serialization.h" +#include + +const int ID_oldPassword = 256; +const int ID_newPassword1 = 257; +const int ID_newPassword2 = 258; +const int ID_change = 259; +const int ID_message = 260; + +GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, s32 id, + IMenuManager *menumgr, + Client* client +): + GUIModalMenu(env, parent, id, menumgr), + m_client(client) +{ +} + +GUIPasswordChange::~GUIPasswordChange() +{ + removeChildren(); +} + +void GUIPasswordChange::removeChildren() +{ + { + gui::IGUIElement *e = getElementFromId(ID_oldPassword); + if(e != NULL) + e->remove(); + } + { + gui::IGUIElement *e = getElementFromId(ID_newPassword1); + if(e != NULL) + e->remove(); + } + { + gui::IGUIElement *e = getElementFromId(ID_newPassword2); + if(e != NULL) + e->remove(); + } + { + gui::IGUIElement *e = getElementFromId(ID_change); + if(e != NULL) + e->remove(); + } +} + +void GUIPasswordChange::regenerateGui(v2u32 screensize) +{ + /* + Remove stuff + */ + removeChildren(); + + /* + Calculate new sizes and positions + */ + core::rect rect( + screensize.X/2 - 580/2, + screensize.Y/2 - 300/2, + screensize.X/2 + 580/2, + screensize.Y/2 + 300/2 + ); + + DesiredRect = rect; + recalculateAbsolutePosition(false); + + v2s32 size = rect.getSize(); + v2s32 topleft_client(40, 0); + v2s32 size_client = size - v2s32(40, 0); + + /* + Add stuff + */ + s32 ypos = 50; + { + core::rect rect(0, 0, 110, 20); + rect += topleft_client + v2s32(35, ypos+6); + const wchar_t *text = L"Old Password"; + Environment->addStaticText(text, rect, false, true, this, -1); + } + { + core::rect rect(0, 0, 230, 30); + rect += topleft_client + v2s32(160, ypos); + gui::IGUIEditBox *e = + Environment->addEditBox(L"", rect, true, this, ID_oldPassword); + Environment->setFocus(e); + e->setPasswordBox(true); + } + ypos += 50; + { + core::rect rect(0, 0, 110, 20); + rect += topleft_client + v2s32(35, ypos+6); + const wchar_t *text = L"New Password"; + Environment->addStaticText(text, rect, false, true, this, -1); + } + { + core::rect rect(0, 0, 230, 30); + rect += topleft_client + v2s32(160, ypos); + gui::IGUIEditBox *e = + Environment->addEditBox(L"", rect, true, this, ID_newPassword1); + e->setPasswordBox(true); + } + ypos += 50; + { + core::rect rect(0, 0, 110, 20); + rect += topleft_client + v2s32(35, ypos+6); + const wchar_t *text = L"Confirm Password"; + Environment->addStaticText(text, rect, false, true, this, -1); + } + { + core::rect rect(0, 0, 230, 30); + rect += topleft_client + v2s32(160, ypos); + gui::IGUIEditBox *e = + Environment->addEditBox(L"", rect, true, this, ID_newPassword2); + e->setPasswordBox(true); + } + + ypos += 50; + { + core::rect rect(0, 0, 140, 30); + rect = rect + v2s32(size.X/2-140/2, ypos); + Environment->addButton(rect, this, ID_change, L"Change"); + } + + ypos += 50; + { + core::rect rect(0, 0, 300, 20); + rect += topleft_client + v2s32(35, ypos); + const wchar_t *text = L"Passwords do not match!"; + IGUIElement *e = + Environment->addStaticText(text, rect, false, true, this, ID_message); + e->setVisible(false); + } + +} + +void GUIPasswordChange::drawMenu() +{ + gui::IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; + video::IVideoDriver* driver = Environment->getVideoDriver(); + + video::SColor bgcolor(140,0,0,0); + driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect); + + gui::IGUIElement::draw(); +} + +bool GUIPasswordChange::acceptInput() +{ + std::wstring oldpass; + std::wstring newpass; + gui::IGUIElement *e; + e = getElementFromId(ID_oldPassword); + if(e != NULL) + oldpass = e->getText(); + e = getElementFromId(ID_newPassword1); + if(e != NULL) + newpass = e->getText(); + e = getElementFromId(ID_newPassword2); + if(e != NULL && newpass != e->getText()) + { + e = getElementFromId(ID_message); + if(e != NULL) + e->setVisible(true); + return false; + } + m_client->sendChangePassword(oldpass, newpass); + return true; +} + +bool GUIPasswordChange::OnEvent(const SEvent& event) +{ + if(event.EventType==EET_KEY_INPUT_EVENT) + { + if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown) + { + quitMenu(); + return true; + } + if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown) + { + if(acceptInput()) + quitMenu(); + return true; + } + } + if(event.EventType==EET_GUI_EVENT) + { + if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST + && isVisible()) + { + if(!canTakeFocus(event.GUIEvent.Element)) + { + dstream<<"GUIPasswordChange: Not allowing focus change." + <getID()) + { + case ID_change: + if(acceptInput()) + quitMenu(); + return true; + } + } + if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER) + { + switch(event.GUIEvent.Caller->getID()) + { + case ID_oldPassword: + case ID_newPassword1: + case ID_newPassword2: + if(acceptInput()) + quitMenu(); + return true; + } + } + } + + return Parent ? Parent->OnEvent(event) : false; +} + diff --git a/src/guiPasswordChange.h b/src/guiPasswordChange.h new file mode 100644 index 0000000..defac31 --- /dev/null +++ b/src/guiPasswordChange.h @@ -0,0 +1,57 @@ +/* +Minetest-c55 +Copyright (C) 2010-11 celeron55, Perttu Ahola +Copyright (C) 2011 Ciaran Gultnieks + +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; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef GUIPASSWORDCHANGE_HEADER +#define GUIPASSWORDCHANGE_HEADER + +#include "common_irrlicht.h" +#include "modalMenu.h" +#include "utility.h" +#include "client.h" +#include + +class GUIPasswordChange : public GUIModalMenu +{ +public: + GUIPasswordChange(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, s32 id, + IMenuManager *menumgr, + Client* client); + ~GUIPasswordChange(); + + void removeChildren(); + /* + Remove and re-add (or reposition) stuff + */ + void regenerateGui(v2u32 screensize); + + void drawMenu(); + + bool acceptInput(); + + bool OnEvent(const SEvent& event); + +private: + Client* m_client; + +}; + +#endif + diff --git a/src/guiPauseMenu.cpp b/src/guiPauseMenu.cpp index 2d42fdb..d32d1a1 100644 --- a/src/guiPauseMenu.cpp +++ b/src/guiPauseMenu.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "porting.h" #include "config.h" +#include "main.h" GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, @@ -64,6 +65,11 @@ void GUIPauseMenu::removeChildren() if(e != NULL) e->remove(); } + { + gui::IGUIElement *e = getElementFromId(261); + if(e != NULL) + e->remove(); + } } void GUIPauseMenu::regenerateGui(v2u32 screensize) @@ -91,21 +97,34 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize) /* Add stuff */ + const s32 btn_height = 30; + const s32 btn_gap = 20; + const s32 btn_num = 4; + s32 btn_y = size.Y/2-((btn_num*btn_height+(btn_num-1)*btn_gap))/2; { - core::rect rect(0, 0, 140, 30); - rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2-50); + core::rect rect(0, 0, 140, btn_height); + rect = rect + v2s32(size.X/2-140/2, btn_y); Environment->addButton(rect, this, 256, L"Continue"); } + btn_y += btn_height + btn_gap; { - core::rect rect(0, 0, 140, 30); - rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+0); + core::rect rect(0, 0, 140, btn_height); + rect = rect + v2s32(size.X/2-140/2, btn_y); + Environment->addButton(rect, this, 261, L"Change Password"); + } + btn_y += btn_height + btn_gap; + { + core::rect rect(0, 0, 140, btn_height); + rect = rect + v2s32(size.X/2-140/2, btn_y); Environment->addButton(rect, this, 260, L"Disconnect"); } + btn_y += btn_height + btn_gap; { - core::rect rect(0, 0, 140, 30); - rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+50); + core::rect rect(0, 0, 140, btn_height); + rect = rect + v2s32(size.X/2-140/2, btn_y); Environment->addButton(rect, this, 257, L"Exit to OS"); } + { core::rect rect(0, 0, 180, 240); rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2); @@ -172,6 +191,7 @@ void GUIPauseMenu::drawMenu() bool GUIPauseMenu::OnEvent(const SEvent& event) { + if(event.EventType==EET_KEY_INPUT_EVENT) { if(event.KeyInput.PressedDown) @@ -209,6 +229,10 @@ bool GUIPauseMenu::OnEvent(const SEvent& event) quitMenu(); // ALWAYS return immediately after quitMenu() return true; + case 261: + quitMenu(); + m_gamecallback->changePassword(); + return true; case 260: // disconnect m_gamecallback->disconnect(); quitMenu(); diff --git a/src/guiPauseMenu.h b/src/guiPauseMenu.h index 22cb65b..64e3c71 100644 --- a/src/guiPauseMenu.h +++ b/src/guiPauseMenu.h @@ -28,6 +28,7 @@ class IGameCallback public: virtual void exitToOS() = 0; virtual void disconnect() = 0; + virtual void changePassword() = 0; }; class GUIPauseMenu : public GUIModalMenu diff --git a/src/main.cpp b/src/main.cpp index c3b0757..2913d01 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -353,8 +353,6 @@ Making it more portable: #include "materials.h" #include "game.h" #include "keycode.h" -#include "sha1.h" -#include "base64.h" // This makes textures ITextureSource *g_texturesource = NULL; @@ -1468,24 +1466,7 @@ int main(int argc, char *argv[]) playername = wide_to_narrow(menudata.name); - // Get an sha-1 hash of the player's name combined with - // the password entered. That's what the server uses as - // their password. (Exception : if the password field is - // blank, we send a blank password - this is for backwards - // compatibility with password-less players). - if(menudata.password.length() > 0) - { - std::string slt=playername + wide_to_narrow(menudata.password); - SHA1 *sha1 = new SHA1(); - sha1->addBytes(slt.c_str(), slt.length()); - unsigned char *digest = sha1->getDigest(); - password = base64_encode(digest, 20); - free(digest); - } - else - { - password = ""; - } + password = translatePassword(playername, menudata.password); address = wide_to_narrow(menudata.address); int newport = stoi(wide_to_narrow(menudata.port)); diff --git a/src/main.h b/src/main.h index c8ac7cc..fcf150f 100644 --- a/src/main.h +++ b/src/main.h @@ -119,6 +119,7 @@ class MainGameCallback : public IGameCallback public: MainGameCallback(IrrlichtDevice *a_device): disconnect_requested(false), + changepassword_requested(false), device(a_device) { } @@ -133,7 +134,13 @@ public: disconnect_requested = true; } + virtual void changePassword() + { + changepassword_requested = true; + } + bool disconnect_requested; + bool changepassword_requested; IrrlichtDevice *device; }; diff --git a/src/server.cpp b/src/server.cpp index 84f9a28..5c03ea8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3004,6 +3004,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendPlayerHP(player); } + else if(command == TOSERVER_PASSWORD) + { + /* + [0] u16 TOSERVER_PASSWORD + [2] u8[28] old password + [30] u8[28] new password + */ + + if(datasize != 2+PASSWORD_SIZE*2) + return; + char password[PASSWORD_SIZE]; + for(u32 i=0; igetPassword(),password)) + { + // Wrong old password supplied!! + SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed."); + return; + } + for(u32 i=0; iupdatePassword(password); + SendChatMessage(peer_id, L"Password change successful"); + } else { derr_server<<"WARNING: Server::ProcessData(): Ignoring " diff --git a/src/utility.cpp b/src/utility.cpp index fc657b2..186881c 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" #include "gettime.h" +#include "sha1.h" +#include "base64.h" TimeTaker::TimeTaker(const char *name, u32 *result) { @@ -217,3 +219,24 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, return true; } +// Get an sha-1 hash of the player's name combined with +// the password entered. That's what the server uses as +// their password. (Exception : if the password field is +// blank, we send a blank password - this is for backwards +// compatibility with password-less players). +std::string translatePassword(std::string playername, std::wstring password) +{ + if(password.length() == 0) + return ""; + + std::string slt=playername + wide_to_narrow(password); + SHA1 *sha1 = new SHA1(); + sha1->addBytes(slt.c_str(), slt.length()); + unsigned char *digest = sha1->getDigest(); + std::string pwd = base64_encode(digest, 20); + free(digest); + return pwd; +} + + + diff --git a/src/utility.h b/src/utility.h index f32dc3a..c7513e9 100644 --- a/src/utility.h +++ b/src/utility.h @@ -2112,6 +2112,7 @@ protected: float m_accumulator; }; +std::string translatePassword(std::string playername, std::wstring password); #endif