From 9e11339ee6be8161c676fe16c34edb5bc089a9bf Mon Sep 17 00:00:00 2001 From: McCabe Maxsted Date: Tue, 22 Mar 2011 01:59:52 -0700 Subject: Support username logins on Linden grids. Seems to cover most edge cases with our current grid manager, but really exposes the weaknesses of not saving login info with the grid, usability-wise. (Note: autologin and the command line need to be tested) --- linden/indra/llui/lllineeditor.cpp | 20 +- linden/indra/llui/lllineeditor.h | 1 + linden/indra/newview/hippogridmanager.cpp | 7 + linden/indra/newview/hippogridmanager.h | 1 + linden/indra/newview/llloginhandler.cpp | 20 +- linden/indra/newview/llpanellogin.cpp | 206 +++++++++++++++++++-- linden/indra/newview/llpanellogin.h | 20 +- linden/indra/newview/llstartup.cpp | 19 +- .../skins/default/xui/en-us/notifications.xml | 19 ++ .../skins/default/xui/en-us/panel_login.xml | 22 ++- 10 files changed, 315 insertions(+), 20 deletions(-) diff --git a/linden/indra/llui/lllineeditor.cpp b/linden/indra/llui/lllineeditor.cpp index 76b8927..a21ad5d 100644 --- a/linden/indra/llui/lllineeditor.cpp +++ b/linden/indra/llui/lllineeditor.cpp @@ -2696,7 +2696,6 @@ BOOL LLLineEditor::prevalidatePrintableNotPipe(const LLWString &str) return rv; } - // static BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str) { @@ -2721,6 +2720,25 @@ BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str) } // static +BOOL LLLineEditor::prevalidatePrintableSpace(const LLWString &str) +{ + BOOL rv = TRUE; + S32 len = str.length(); + if(len == 0) return rv; + while(len--) + { + if( !(LLStringOps::isAlnum((char)str[len]) || + LLStringOps::isPunct((char)str[len]) || + ' ' == str[len]) ) + { + rv = FALSE; + break; + } + } + return rv; +} + +// static BOOL LLLineEditor::prevalidateASCII(const LLWString &str) { BOOL rv = TRUE; diff --git a/linden/indra/llui/lllineeditor.h b/linden/indra/llui/lllineeditor.h index d217859..43ce869 100644 --- a/linden/indra/llui/lllineeditor.h +++ b/linden/indra/llui/lllineeditor.h @@ -253,6 +253,7 @@ public: static BOOL prevalidateAlphaNumSpace(const LLWString &str ); static BOOL prevalidatePrintableNotPipe(const LLWString &str); static BOOL prevalidatePrintableNoSpace(const LLWString &str); + static BOOL prevalidatePrintableSpace(const LLWString &str); static BOOL prevalidateASCII(const LLWString &str); static BOOL postvalidateFloat(const std::string &str); diff --git a/linden/indra/newview/hippogridmanager.cpp b/linden/indra/newview/hippogridmanager.cpp index d56214c..a15f676 100644 --- a/linden/indra/newview/hippogridmanager.cpp +++ b/linden/indra/newview/hippogridmanager.cpp @@ -165,6 +165,13 @@ const std::string& HippoGridInfo::getRealCurrencySymbol() const return mRealCurrencySymbol; } +bool HippoGridInfo::isUsernameCompat() const +{ + // currently only SecondLife grids support username-style logins + // but Aurora is working on implementing it -- MC + return (mPlatform == HippoGridInfo::PLATFORM_SECONDLIFE); +} + // ******************************************************************** diff --git a/linden/indra/newview/hippogridmanager.h b/linden/indra/newview/hippogridmanager.h index 8429dba..1b6fbc5 100644 --- a/linden/indra/newview/hippogridmanager.h +++ b/linden/indra/newview/hippogridmanager.h @@ -55,6 +55,7 @@ public: const std::string& getVoiceConnector() const { return mVoiceConnector; } std::string getSearchUrl(SearchType ty, bool is_web) const; bool isRenderCompat() const; + bool isUsernameCompat() const; int getMaxAgentGroups() const { return mMaxAgentGroups; } const std::string& getCurrencySymbol() const; diff --git a/linden/indra/newview/llloginhandler.cpp b/linden/indra/newview/llloginhandler.cpp index 30b05ef..c35ba04 100644 --- a/linden/indra/newview/llloginhandler.cpp +++ b/linden/indra/newview/llloginhandler.cpp @@ -35,6 +35,7 @@ #include "llloginhandler.h" // viewer includes +#include "hippogridmanager.h" #include "llpanellogin.h" // save_password_to_disk() #include "llstartup.h" // getStartupState() #include "llurlsimstring.h" @@ -141,10 +142,27 @@ bool LLLoginHandler::handle(const LLSD& tokens, if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) //on splash page { + // if we ever support saving names based on grid, we'll have to support saving usernames too -- MC + LLPanelLogin::loadLoginForm(); + if (!mFirstName.empty() || !mLastName.empty()) { // Fill in the name, and maybe the password - LLPanelLogin::setFields(mFirstName, mLastName, password); + if (gHippoGridManager && gHippoGridManager->getCurrentGrid()->isUsernameCompat()) + { + if (mLastName == "resident" || mLastName == "Resident") + { + LLPanelLogin::setFields(mFirstName, password); + } + else + { + LLPanelLogin::setFields(mFirstName+"."+ mLastName, password); + } + } + else + { + LLPanelLogin::setFields(mFirstName, mLastName, password); + } } if (mWebLoginKey.isNull()) diff --git a/linden/indra/newview/llpanellogin.cpp b/linden/indra/newview/llpanellogin.cpp index 0b91091..ba5b963 100644 --- a/linden/indra/newview/llpanellogin.cpp +++ b/linden/indra/newview/llpanellogin.cpp @@ -81,6 +81,8 @@ #include "llglheaders.h" +#include + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -208,6 +210,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, #if !USE_VIEWER_AUTH childSetPrevalidate("first_name_edit", LLLineEditor::prevalidatePrintableNoSpace); childSetPrevalidate("last_name_edit", LLLineEditor::prevalidatePrintableNoSpace); + childSetPrevalidate("username_edit", LLLineEditor::prevalidatePrintableSpace); childSetCommitCallback("password_edit", mungePassword); childSetKeystrokeCallback("password_edit", onPassKey, this); @@ -316,6 +319,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, refreshLocation( false ); #endif + loadLoginForm(); + loadNewsBar(); LLFirstUse::useLoginScreen(); @@ -551,6 +556,26 @@ void LLPanelLogin::show(const LLRect &rect, LLPanelLogin::addServer(LLViewerLogin::getInstance()->getGridLabel()); } + +// static +void LLPanelLogin::setFields(const std::string& username, const std::string& password) +{ + if (!sInstance) + { + llwarns << "Attempted fillFields with no login view shown" << llendl; + return; + } + + if (!gHippoGridManager->getCurrentGrid()->isUsernameCompat()) + { + llwarns << "Trying to set a username for an incompatible grid!" << llendl; + return; + } + + sInstance->childSetText("username_edit", username); + setPassword(password); +} + // static void LLPanelLogin::setFields(const std::string& firstname, const std::string& lastname, @@ -564,7 +589,13 @@ void LLPanelLogin::setFields(const std::string& firstname, sInstance->childSetText("first_name_edit", firstname); sInstance->childSetText("last_name_edit", lastname); + setPassword(password); +} + +// static +void LLPanelLogin::setPassword(const std::string& password) +{ // Max "actual" password length is 16 characters. // Hex digests are always 32 characters. if (password.length() == 32) @@ -663,11 +694,25 @@ void LLPanelLogin::getFields(std::string *firstname, return; } - *firstname = sInstance->childGetText("first_name_edit"); - LLStringUtil::trim(*firstname); + // SL grids use a generic one-line text entry field for logins + std::string username = sInstance->childGetText("username_edit"); + if (!username.empty() && gHippoGridManager->getConnectedGrid()->isUsernameCompat()) + { + // no need to trim here, spaces are removed + if (!convertUsernameToLegacy(username, *firstname, *lastname)) + { + llerrs << "Invalid username accepted! Cannot proceed!" << llendl; + return; + } + } + else + { + *firstname = sInstance->childGetText("first_name_edit"); + LLStringUtil::trim(*firstname); - *lastname = sInstance->childGetText("last_name_edit"); - LLStringUtil::trim(*lastname); + *lastname = sInstance->childGetText("last_name_edit"); + LLStringUtil::trim(*lastname); + } *password = sInstance->mMungedPassword; } @@ -802,6 +847,54 @@ void LLPanelLogin::refreshLoginPage() } +// static +void LLPanelLogin::loadLoginForm() +{ + if (!sInstance) return; + + // toggle between username/first+last login based on grid -- MC + LLTextBox* firstnamet = sInstance->getChild("first_name_text"); + LLTextBox* lastnamet = sInstance->getChild("last_name_text"); + LLTextBox* usernamet = sInstance->getChild("username_text"); + + LLLineEditor* firstnamel = sInstance->getChild("first_name_edit"); + LLLineEditor* lastnamel = sInstance->getChild("last_name_edit"); + LLLineEditor* usernamel = sInstance->getChild("username_edit"); + + firstnamet->setVisible(!gHippoGridManager->getCurrentGrid()->isUsernameCompat()); + lastnamet->setVisible(!gHippoGridManager->getCurrentGrid()->isUsernameCompat()); + usernamet->setVisible(gHippoGridManager->getCurrentGrid()->isUsernameCompat()); + + firstnamel->setVisible(!gHippoGridManager->getCurrentGrid()->isUsernameCompat()); + lastnamel->setVisible(!gHippoGridManager->getCurrentGrid()->isUsernameCompat()); + usernamel->setVisible(gHippoGridManager->getCurrentGrid()->isUsernameCompat()); + + // these should really REALLY be stored in the grid info -- MC + std::string firstnames = gSavedSettings.getString("FirstName"); + std::string lastnames = gSavedSettings.getString("LastName"); + if (!firstnames.empty() && !lastnames.empty()) + { + if (gHippoGridManager->getCurrentGrid()->isUsernameCompat()) + { + if (lastnames == "resident" || lastnames == "Resident") + { + usernamel->setText(firstnames); + } + else + { + usernamel->setText(firstnames+"."+lastnames); + } + } + else + { + firstnamel->setText(firstnames); + lastnamel->setText(lastnames); + } + } +} + + +// static void LLPanelLogin::loadLoginPage() { if (!sInstance) return; @@ -972,6 +1065,63 @@ void LLPanelLogin::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent ev //--------------------------------------------------------------------------- // static +bool LLPanelLogin::convertUsernameToLegacy(std::string& username, std::string& firstname, std::string& lastname) +{ + if (!username.empty()) + { + // trim beginning and end + LLStringUtil::trim(username); + + // minimum length for an SL grid + if (username.length() < 5) + { + return false; + } + } + else + { + return false; + } + + std::vector names; + boost::algorithm::split(names, username, boost::is_any_of(" .")); + + // maybe they typed in a few too many spaces? + if (names.size() > 2) + { + std::vector::iterator vIt = names.begin(); + while (vIt != names.end()) + { + if ((*vIt).empty()) + { + vIt = names.erase(vIt); + } + else + { + ++vIt; + } + } + } + + if (names.size() == 1) // username + { + firstname = names[0]; + lastname = "Resident"; + return true; + } + else if (names.size() == 2) // first.last or first+" "+last + { + firstname = names[0]; + lastname = names[1]; + return true; + } + else + { + return false; + } +} + +// static void LLPanelLogin::onClickConnect(void *) { if (sInstance && sInstance->mCallback) @@ -983,17 +1133,48 @@ void LLPanelLogin::onClickConnect(void *) // JC - Make sure the fields all get committed. sInstance->setFocus(FALSE); - std::string first = sInstance->childGetText("first_name_edit"); - std::string last = sInstance->childGetText("last_name_edit"); - if (!first.empty() && !last.empty()) + // Note: valid logins are username or Username or First.Last or First Last -- MC + if (gHippoGridManager->getCurrentGrid()->isUsernameCompat()) { - // has both first and last name typed - sInstance->mCallback(0, sInstance->mCallbackData); + std::string username = sInstance->childGetText("username_edit"); + if (!username.empty()) + { + // todo: make this two functions, one for validating the other for converting + std::string temp1; + std::string temp2; + if (convertUsernameToLegacy(username, temp1, temp2)) + { + // has username typed, make sure we're just using that + sInstance->childSetText("first_name_edit", LLStringUtil::null); + sInstance->childSetText("last_name_edit", LLStringUtil::null); + sInstance->mCallback(0, sInstance->mCallbackData); + } + else + { + LLNotifications::instance().add("InvalidLogInSecondLife", LLSD(), LLSD(), + LLPanelLogin::newAccountAlertCallback); + } + } + else + { + LLNotifications::instance().add("MustHaveAccountToLogIn", LLSD(), LLSD(), + LLPanelLogin::newAccountAlertCallback); + } } else { - LLNotifications::instance().add("MustHaveAccountToLogIn", LLSD(), LLSD(), - LLPanelLogin::newAccountAlertCallback); + std::string first = sInstance->childGetText("first_name_edit"); + std::string last = sInstance->childGetText("last_name_edit"); + if (!first.empty() && !last.empty()) + { + // has both first and last name typed + sInstance->mCallback(0, sInstance->mCallbackData); + } + else + { + LLNotifications::instance().add("MustHaveAccountToLogIn", LLSD(), LLSD(), + LLPanelLogin::newAccountAlertCallback); + } } } } @@ -1123,6 +1304,9 @@ void LLPanelLogin::updateGridCombo(std::string grid_nick) llinfos << "current grid set to " << grid_nick << llendl; + // switch between username/first+last name based on grid + loadLoginForm(); + // grid changed so show new splash screen (possibly) loadLoginPage(); diff --git a/linden/indra/newview/llpanellogin.h b/linden/indra/newview/llpanellogin.h index 5830b52..947aea6 100644 --- a/linden/indra/newview/llpanellogin.h +++ b/linden/indra/newview/llpanellogin.h @@ -59,9 +59,9 @@ public: void (*callback)(S32 option, void* user_data), void* callback_data); - // Remember password checkbox is set via gSavedSettings "RememberPassword" - static void setFields(const std::string& firstname, const std::string& lastname, - const std::string& password); + // Sets the login screen's name and password editors. Remember password checkbox is set via gSavedSettings "RememberPassword" + static void setFields(const std::string& firstname, const std::string& lastname, const std::string& password); + static void setFields(const std::string& username, const std::string& password); static void addServer(const std::string& server); static void refreshLocation( bool force_visible ); @@ -78,6 +78,7 @@ public: void setSiteIsAlive( bool alive ); + static void loadLoginForm(); static void loadLoginPage(); static void refreshLoginPage(); static void giveFocus(); @@ -101,8 +102,19 @@ private: static void onPassKey(LLLineEditor* caller, void* user_data); static void onSelectServer(LLUICtrl*, void*); static void onServerComboLostFocus(LLFocusableElement*, void*); + + // converts the following login name formats into valid firstname lastname combos: + // username + // username.Resident + // first.last + // first+" "+last + // " "+first+" "+last+" " + // returns true if name conversion successful + static bool convertUsernameToLegacy(std::string& username, std::string& firstname, std::string& lastname); + + // set the password for the login screen + static void setPassword(const std::string& password); -private: LLPointer mLogoImage; void (*mCallback)(S32 option, void *userdata); diff --git a/linden/indra/newview/llstartup.cpp b/linden/indra/newview/llstartup.cpp index c9cdc8f..e21efa3 100644 --- a/linden/indra/newview/llstartup.cpp +++ b/linden/indra/newview/llstartup.cpp @@ -813,8 +813,23 @@ bool idle_startup() // Show the login dialog login_show(); // connect dialog is already shown, so fill in the names - LLPanelLogin::setFields( firstname, lastname, password); - + // icky how usernames get bolted on here as a kind of hack -- MC + if (gHippoGridManager && gHippoGridManager->getCurrentGrid()->isUsernameCompat()) + { + if (lastname == "resident" || lastname == "Resident") + { + LLPanelLogin::setFields(firstname, password); + } + else + { + LLPanelLogin::setFields(firstname+"."+lastname, password); + } + } + else + { + LLPanelLogin::setFields(firstname, lastname, password); + } + LLPanelLogin::giveFocus(); gSavedSettings.setBOOL("FirstRunThisInstall", FALSE); diff --git a/linden/indra/newview/skins/default/xui/en-us/notifications.xml b/linden/indra/newview/skins/default/xui/en-us/notifications.xml index 27f5529..eb8860d 100644 --- a/linden/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/linden/indra/newview/skins/default/xui/en-us/notifications.xml @@ -7197,6 +7197,25 @@ IM history could not be found for [NAME]. +Oops! The login name you entered wasn't formatted correctly! + +[SECOND_LIFE] accepts the following login formats: + + - username + - username.Resident + - firstname.lastname + +If you don't have an account, would you like to create one now? + + + + diff --git a/linden/indra/newview/skins/default/xui/en-us/panel_login.xml b/linden/indra/newview/skins/default/xui/en-us/panel_login.xml index 9499239..68991b6 100644 --- a/linden/indra/newview/skins/default/xui/en-us/panel_login.xml +++ b/linden/indra/newview/skins/default/xui/en-us/panel_login.xml @@ -70,10 +70,30 @@ allow_translate="false" /> + + + + Login name: + + + +