diff options
author | Maxim Nikolenko <maximnproductengine@lindenlab.com> | 2024-08-06 18:00:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-06 18:00:32 +0300 |
commit | 0bf3b61457e7c77e91fb03d91b4caf0d702f99fa (patch) | |
tree | 8f43417c7f5605c3cee9a4d5c9fe255abbd63b4a /indra/newview | |
parent | cf29b701b19644062a1428a64023c3cf9829e2de (diff) | |
parent | 628cd605080546c400d3343bf0834bebf693bbcf (diff) |
Merge branch 'release/luau-scripting' into lua-ui-visibility
Diffstat (limited to 'indra/newview')
-rw-r--r-- | indra/newview/llappviewer.cpp | 20 | ||||
-rw-r--r-- | indra/newview/llpanellogin.cpp | 57 | ||||
-rw-r--r-- | indra/newview/llpanellogin.h | 2 | ||||
-rw-r--r-- | indra/newview/llpanelloginlistener.cpp | 196 | ||||
-rw-r--r-- | indra/newview/llpanelloginlistener.h | 2 | ||||
-rw-r--r-- | indra/newview/llsecapi.h | 2 | ||||
-rw-r--r-- | indra/newview/scripts/lua/require/login.lua | 40 | ||||
-rw-r--r-- | indra/newview/scripts/lua/test_login.lua | 13 |
8 files changed, 285 insertions, 47 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ef12fe0bd3..ff29f64aeb 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -113,6 +113,7 @@ #include "llgltfmateriallist.h" // Linden library includes +#include "fsyspath.h" #include "llavatarnamecache.h" #include "lldiriterator.h" #include "llexperiencecache.h" @@ -1216,8 +1217,23 @@ bool LLAppViewer::init() "--luafile", "LuaScript", [](const LLSD& script) { - // no completion callback: we don't need to know - LLLUAmanager::runScriptFile(script); + LLSD paths(gSavedSettings.getLLSD("LuaCommandPath")); + LL_DEBUGS("Lua") << "LuaCommandPath = " << paths << LL_ENDL; + for (const auto& path : llsd::inArray(paths)) + { + // if script path is already absolute, operator/() preserves it + auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); + auto absscript{ (abspath / script.asString()) }; + std::error_code ec; + if (std::filesystem::exists(absscript, ec)) + { + // no completion callback: we don't need to know + LLLUAmanager::runScriptFile(absscript.u8string()); + return; // from lambda + } + } + LL_WARNS("Lua") << "--luafile " << std::quoted(script.asString()) + << " not found on " << paths << LL_ENDL; }); processComposeSwitch( "LuaAutorunPath", "LuaAutorunPath", diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 86f56f0949..a6dbcedc4b 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -81,15 +81,16 @@ BOOL LLPanelLogin::sCredentialSet = FALSE; LLPointer<LLCredential> load_user_credentials(std::string &user_key) { - if (gSecAPIHandler->hasCredentialMap("login_list", LLGridManager::getInstance()->getGrid())) + std::string grid{ LLGridManager::instance().getGrid() }; + if (gSecAPIHandler->hasCredentialMap("login_list", grid)) { // user_key should be of "name Resident" format - return gSecAPIHandler->loadFromCredentialMap("login_list", LLGridManager::getInstance()->getGrid(), user_key); + return gSecAPIHandler->loadFromCredentialMap("login_list", grid, user_key); } else { // legacy (or legacy^2, since it also tries to load from settings) - return gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + return gSecAPIHandler->loadCredential(grid); } } @@ -223,7 +224,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, sendChildToBack(getChildView("forgot_password_text")); sendChildToBack(getChildView("sign_up_text")); - std::string current_grid = LLGridManager::getInstance()->getGrid(); + LLGridManager& gridmgr{ LLGridManager::instance() }; + std::string current_grid = gridmgr.getGrid(); if (!mFirstLoginThisInstall) { LLComboBox* favorites_combo = getChild<LLComboBox>("start_location_combo"); @@ -237,7 +239,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, // Load all of the grids, sorted, and then add a bar and the current grid at the top server_choice_combo->removeall(); - std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); + std::map<std::string, std::string> known_grids = gridmgr.getKnownGrids(); for (std::map<std::string, std::string>::iterator grid_choice = known_grids.begin(); grid_choice != known_grids.end(); grid_choice++) @@ -251,7 +253,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, server_choice_combo->sortByName(); LL_DEBUGS("AppInit") << "adding current " << current_grid << LL_ENDL; - server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), + server_choice_combo->add(gridmgr.getGridLabel(), current_grid, ADD_TOP); server_choice_combo->selectFirstItem(); @@ -760,6 +762,7 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) LL_DEBUGS("AppInit")<<new_start_slurl.asString()<<LL_ENDL; LLComboBox* location_combo = sInstance->getChild<LLComboBox>("start_location_combo"); + LLGridManager& gridmgr{ LLGridManager::instance() }; /* * Determine whether or not the new_start_slurl modifies the grid. * @@ -774,17 +777,17 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) { case LLSLURL::LOCATION: { - std::string slurl_grid = LLGridManager::getInstance()->getGrid(new_start_slurl.getGrid()); + std::string slurl_grid = gridmgr.getGrid(new_start_slurl.getGrid()); if ( ! slurl_grid.empty() ) // is that a valid grid? { - if ( slurl_grid != LLGridManager::getInstance()->getGrid() ) // new grid? + if ( slurl_grid != gridmgr.getGrid() ) // new grid? { // the slurl changes the grid, so update everything to match - LLGridManager::getInstance()->setGridChoice(slurl_grid); + gridmgr.setGridChoice(slurl_grid); // update the grid selector to match the slurl LLComboBox* server_combo = sInstance->getChild<LLComboBox>("server_combo"); - std::string server_label(LLGridManager::getInstance()->getGridLabel(slurl_grid)); + std::string server_label(gridmgr.getGridLabel(slurl_grid)); server_combo->setSimple(server_label); updateServer(); // to change the links and splash screen @@ -831,8 +834,7 @@ void LLPanelLogin::autologinToLocation(const LLSLURL& slurl) if ( LLPanelLogin::sInstance != NULL ) { - void* unused_parameter = 0; - LLPanelLogin::sInstance->onClickConnect(unused_parameter); + LLPanelLogin::sInstance->onClickConnect(false); } } @@ -872,7 +874,8 @@ void LLPanelLogin::loadLoginPage() { if (!sInstance) return; - LLURI login_page = LLURI(LLGridManager::getInstance()->getLoginPage()); + LLGridManager& gridmgr{ LLGridManager::instance() }; + LLURI login_page = LLURI(gridmgr.getLoginPage()); LLSD params(login_page.queryMap()); LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL; @@ -899,7 +902,7 @@ void LLPanelLogin::loadLoginPage() params["channel"] = LLVersionInfo::instance().getChannel(); // Grid - params["grid"] = LLGridManager::getInstance()->getGridId(); + params["grid"] = gridmgr.getGridId(); // add OS info params["os"] = LLOSInfo::instance().getOSStringSimple(); @@ -916,7 +919,7 @@ void LLPanelLogin::loadLoginPage() login_page.path(), params)); - gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); + gViewerWindow->setMenuBackgroundColor(false, !gridmgr.isInProductionGrid()); LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); if (web_browser->getCurrentNavUrl() != login_uri.asString()) @@ -949,9 +952,10 @@ void LLPanelLogin::onClickConnect(bool commit_fields) // the grid definitions may come from a user-supplied grids.xml, so they may not be good LL_DEBUGS("AppInit")<<"grid "<<combo_val.asString()<<LL_ENDL; + LLGridManager& gridmgr{ LLGridManager::instance() }; try { - LLGridManager::getInstance()->setGridChoice(combo_val.asString()); + gridmgr.setGridChoice(combo_val.asString()); } catch (LLInvalidGridName ex) { @@ -984,7 +988,7 @@ void LLPanelLogin::onClickConnect(bool commit_fields) std::string identifier_type; cred->identifierType(identifier_type); LLSD allowed_credential_types; - LLGridManager::getInstance()->getLoginIdentifierTypes(allowed_credential_types); + gridmgr.getLoginIdentifierTypes(allowed_credential_types); // check the typed in credential type against the credential types expected by the server. for(LLSD::array_iterator i = allowed_credential_types.beginArray(); @@ -1133,6 +1137,7 @@ void LLPanelLogin::updateServer() { if (sInstance) { + LLGridManager& gridmgr{ LLGridManager::instance() }; try { // if they've selected another grid, we should load the credentials @@ -1152,7 +1157,7 @@ void LLPanelLogin::updateServer() // populate dropbox and setFields // Note: following call is related to initializeLoginInfo() - LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(gridmgr.getGrid()); sInstance->populateUserList(credential); // restore creds @@ -1165,12 +1170,12 @@ void LLPanelLogin::updateServer() { // populate dropbox and setFields // Note: following call is related to initializeLoginInfo() - LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(gridmgr.getGrid()); sInstance->populateUserList(credential); } // update the login panel links - bool system_grid = LLGridManager::getInstance()->isSystemGrid(); + bool system_grid = gridmgr.isSystemGrid(); // Want to vanish not only create_new_account_btn, but also the // title text over it, so turn on/off the whole layout_panel element. @@ -1219,11 +1224,12 @@ void LLPanelLogin::populateUserList(LLPointer<LLCredential> credential) getChild<LLUICtrl>("password_edit")->setValue(std::string()); mUsernameLength = 0; mPasswordLength = 0; + std::string grid{ LLGridManager::instance().getGrid() }; - if (gSecAPIHandler->hasCredentialMap("login_list", LLGridManager::getInstance()->getGrid())) + if (gSecAPIHandler->hasCredentialMap("login_list", grid)) { LLSecAPIHandler::credential_map_t credencials; - gSecAPIHandler->loadCredentialMap("login_list", LLGridManager::getInstance()->getGrid(), credencials); + gSecAPIHandler->loadCredentialMap("login_list", grid, credencials); LLSecAPIHandler::credential_map_t::iterator cr_iter = credencials.begin(); LLSecAPIHandler::credential_map_t::iterator cr_end = credencials.end(); @@ -1278,7 +1284,8 @@ void LLPanelLogin::onSelectServer() LLComboBox* server_combo = getChild<LLComboBox>("server_combo"); LLSD server_combo_val = server_combo->getSelectedValue(); LL_INFOS("AppInit") << "grid "<<server_combo_val.asString()<< LL_ENDL; - LLGridManager::getInstance()->setGridChoice(server_combo_val.asString()); + auto& gridmgr{ LLGridManager::instance() }; + gridmgr.setGridChoice(server_combo_val.asString()); addFavoritesToStartLocation(); /* @@ -1308,7 +1315,7 @@ void LLPanelLogin::onSelectServer() LLSLURL slurl(location); // generata a slurl from the location combo contents if (location.empty() || (slurl.getType() == LLSLURL::LOCATION - && slurl.getGrid() != LLGridManager::getInstance()->getGrid()) + && slurl.getGrid() != gridmgr.getGrid()) ) { // the grid specified by the location is not this one, so clear the combo @@ -1338,7 +1345,7 @@ bool LLPanelLogin::getShowFavorites() } // static -std::string LLPanelLogin::getUserName(LLPointer<LLCredential> &cred) +std::string LLPanelLogin::getUserName(const LLPointer<LLCredential> &cred) { if (cred.isNull()) { diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 7f4c2487bf..a7019ea2a3 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -88,7 +88,7 @@ public: static bool getShowFavorites(); // extract name from cred in a format apropriate for username field - static std::string getUserName(LLPointer<LLCredential> &cred); + static std::string getUserName(const LLPointer<LLCredential> &cred); private: friend class LLPanelLoginListener; diff --git a/indra/newview/llpanelloginlistener.cpp b/indra/newview/llpanelloginlistener.cpp index fb6f034b7f..aeaf2d1d00 100644 --- a/indra/newview/llpanelloginlistener.cpp +++ b/indra/newview/llpanelloginlistener.cpp @@ -32,9 +32,19 @@ #include "llpanelloginlistener.h" // STL headers // std headers +#include <iomanip> +#include <memory> // external library headers // other Linden headers +#include "llcombobox.h" #include "llpanellogin.h" +#include "llsdutil.h" +#include "llsecapi.h" +#include "llslurl.h" +#include "llstartup.h" +#include "lluictrl.h" +#include "llviewernetwork.h" +#include "stringize.h" LLPanelLoginListener::LLPanelLoginListener(LLPanelLogin* instance): LLEventAPI("LLPanelLogin", "Access to LLPanelLogin methods"), @@ -43,9 +53,195 @@ LLPanelLoginListener::LLPanelLoginListener(LLPanelLogin* instance): add("onClickConnect", "Pretend user clicked the \"Log In\" button", &LLPanelLoginListener::onClickConnect); + add("login", + "Login to Second Life with saved credentials.\n" + "Pass [\"username\"] to select credentials previously saved with that username.\n" + "Pass [\"slurl\"] to specify target location.\n" + "Pass [\"grid\"] to select one of the valid grids in grids.xml.", + &LLPanelLoginListener::login, + llsd::map("reply", LLSD::String())); + add("savedLogins", + "Return \"logins\" as a list of {} saved login names.\n" + "Pass [\"grid\"] to select one of the valid grids in grids.xml.", + &LLPanelLoginListener::savedLogins, + llsd::map("reply", LLSD::String())); } void LLPanelLoginListener::onClickConnect(const LLSD&) const { mPanel->onClickConnect(false); } + +namespace +{ + + std::string gridkey(const std::string& grid) + { + if (grid.find('.') != std::string::npos) + { + return grid; + } + else + { + return stringize("util.", grid, ".lindenlab.com"); + } + } + +} // anonymous namespace + +void LLPanelLoginListener::login(const LLSD& args) const +{ + // Although we require a "reply" key, don't instantiate a Response object: + // if things go well, response to our invoker will come later, once the + // viewer gets the response from the login server. + std::string username(args["username"]), slurlstr(args["slurl"]), grid(args["grid"]); + LL_DEBUGS("AppInit") << "Login with saved credentials"; + if (! username.empty()) + { + LL_CONT << " for user " << std::quoted(username); + } + if (! slurlstr.empty()) + { + LL_CONT << " to location " << std::quoted(slurlstr); + } + if (! grid.empty()) + { + LL_CONT << " on grid " << std::quoted(grid); + } + LL_CONT << LL_ENDL; + + // change grid first, allowing slurl to override + auto& gridmgr{ LLGridManager::instance() }; + if (! grid.empty()) + { + grid = gridkey(grid); + // setGridChoice() can throw LLInvalidGridName -- but if so, let it + // propagate, trusting that LLEventAPI will catch it and send an + // appropriate reply. + auto server_combo = mPanel->getChild<LLComboBox>("server_combo"); + server_combo->setSimple(gridmgr.getGridLabel(grid)); + LLPanelLogin::updateServer(); + } + if (! slurlstr.empty()) + { + LLSLURL slurl(slurlstr); + if (! slurl.isSpatial()) + { + // don't bother with LLTHROW() because we expect LLEventAPI to + // catch and report back to invoker + throw DispatchError(stringize("Invalid start SLURL ", std::quoted(slurlstr))); + } + // Bypass LLStartUp::setStartSLURL() because, after validating as + // above, it bounces right back to LLPanelLogin::onUpdateStartSLURL(). + // It also sets the "NextLoginLocation" user setting, but as with grid, + // we don't yet know whether that's desirable for scripted login. + LLPanelLogin::onUpdateStartSLURL(slurl); + } + if (! username.empty()) + { + // Transform (e.g.) "Nat Linden" to the internal form expected by + // loadFromCredentialMap(), e.g. "nat_linden" + LLStringUtil::toLower(username); + LLStringUtil::replaceChar(username, ' ', '_'); + LLStringUtil::replaceChar(username, '.', '_'); + + // call gridmgr.getGrid() because our caller didn't necessarily pass + // ["grid"] -- or it might have been overridden by the SLURL + auto cred = gSecAPIHandler->loadFromCredentialMap("login_list", + gridmgr.getGrid(), + username); + LLPanelLogin::setFields(cred); + } + + if (mPanel->getChild<LLUICtrl>("username_combo")->getValue().asString().empty() || + mPanel->getChild<LLUICtrl>("password_edit") ->getValue().asString().empty()) + { + // as above, let LLEventAPI report back to invoker + throw DispatchError(stringize("Unrecognized username ", std::quoted(username))); + } + + // Here we're about to trigger actual login, which is all we can do in + // this method. All subsequent responses must come via the "login" + // LLEventPump. + LLEventPumps::instance().obtain("login").listen( + "Lua login", + [args] + (const LLBoundListener& conn, const LLSD& update) + { + LLSD response{ llsd::map("ok", false) }; + if (update["change"] == "connect") + { + // success! + response["ok"] = true; + } + else if (update["change"] == "disconnect") + { + // Does this ever actually happen? + // LLLoginInstance::handleDisconnect() says it's a placeholder. + response["error"] = "login disconnect"; + } + else if (update["change"] == "fail.login") + { + // why? + // LLLoginInstance::handleLoginFailure() has special code to + // handle "tos", "update" and "mfa_challenge". Do not respond + // automatically because these things are SUPPOSED to engage + // the user. + // Copy specific response fields because there's an enormous + // chunk of stuff that comes back on success. + LLSD data{ update["data"] }; + const char* fields[] = { + "reason", + "error_code" + }; + for (auto field : fields) + { + response[field] = data[field]; + } + response["error"] = update["message"]; + } + else + { + // Ignore all other "change" values: LLLogin sends multiple update + // events. Only a few of them (above) indicate completion. + return; + } + // For all the completion cases, disconnect from the "login" + // LLEventPump. + conn.disconnect(); + // and at last, send response to the original invoker + sendReply(response, args); + }); + + // Turn the Login crank. + mPanel->onClickConnect(false); +} + +LLSD LLPanelLoginListener::savedLogins(const LLSD& args) const +{ + LL_INFOS() << "called with " << args << LL_ENDL; + std::string grid = (args.has("grid")? gridkey(args["grid"].asString()) + : LLGridManager::instance().getGrid()); + if (! gSecAPIHandler->hasCredentialMap("login_list", grid)) + { + LL_INFOS() << "no creds for " << grid << LL_ENDL; + return llsd::map("error", + stringize("No saved logins for grid ", std::quoted(grid))); + } + LLSecAPIHandler::credential_map_t creds; + gSecAPIHandler->loadCredentialMap("login_list", grid, creds); + auto result = llsd::map( + "logins", + llsd::toArray( + creds, + [](const auto& pair) + { + return llsd::map( + "name", + LLPanelLogin::getUserName(pair.second), + "key", + pair.first); + })); + LL_INFOS() << "returning creds " << result << LL_ENDL; + return result; +} diff --git a/indra/newview/llpanelloginlistener.h b/indra/newview/llpanelloginlistener.h index b552ccd5b0..f2f4d70ff9 100644 --- a/indra/newview/llpanelloginlistener.h +++ b/indra/newview/llpanelloginlistener.h @@ -40,6 +40,8 @@ public: private: void onClickConnect(const LLSD&) const; + void login(const LLSD&) const; + LLSD savedLogins(const LLSD&) const; LLPanelLogin* mPanel; }; diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 5cc78d09dc..367bfe1469 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -311,7 +311,7 @@ public: mIdentifier = identifier; mAuthenticator = authenticator; } - virtual LLSD getIdentifier() { return mIdentifier; } + virtual LLSD getIdentifier() const { return mIdentifier; } virtual void identifierType(std::string& idType); virtual LLSD getAuthenticator() { return mAuthenticator; } virtual void authenticatorType(std::string& authType); diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua index 0d8591cace..50fc9e3793 100644 --- a/indra/newview/scripts/lua/require/login.lua +++ b/indra/newview/scripts/lua/require/login.lua @@ -1,19 +1,33 @@ -local UI = require 'UI' local leap = require 'leap' +local startup = require 'startup' +local mapargs = require 'mapargs' -local function login(username, password) - if username and password then - local userpath = '//username_combo/Combo Text Entry' - local passpath = '//password_edit' - -- first clear anything presently in those text fields - for _, path in pairs({userpath, passpath}) do - UI.click(path) - UI.keypress{keysym='Backsp', path=path} - end - UI.type{path=userpath, text=username} - UI.type{path=passpath, text=password} +local login = {} + +local function ensure_login_state(op) + -- no point trying to login until the viewer is ready + startup.wait('STATE_LOGIN_WAIT') + -- Once we've actually started login, LLPanelLogin is destroyed, and so is + -- its "LLPanelLogin" listener. At that point, + -- leap.request("LLPanelLogin", ...) will hang indefinitely because no one + -- is listening on that LLEventPump any more. Intercept that case and + -- produce a sensible error. + local state = startup.state() + if startup.before('STATE_LOGIN_WAIT', state) then + error(`Can't engage login operation {op} once we've reached state {state}`, 2) end - leap.send('LLPanelLogin', {op='onClickConnect'}) +end + +function login.login(...) + ensure_login_state('login') + local args = mapargs('username,grid,slurl', ...) + args.op = 'login' + return leap.request('LLPanelLogin', args) +end + +function login.savedLogins(grid) + ensure_login_state('savedLogins') + return leap.request('LLPanelLogin', {op='savedLogins'})['logins'] end return login diff --git a/indra/newview/scripts/lua/test_login.lua b/indra/newview/scripts/lua/test_login.lua index a8c31807bc..54d3635a41 100644 --- a/indra/newview/scripts/lua/test_login.lua +++ b/indra/newview/scripts/lua/test_login.lua @@ -1,7 +1,10 @@ -startup = require 'startup' +inspect = require 'inspect' login = require 'login' -startup.wait('STATE_LOGIN_WAIT') -login() --- Fill in valid credentials as they would be entered on the login screen --- login('My Username', 'password') +local grid = 'agni' +print(inspect(login.savedLogins(grid))) + +print(inspect(login.login{ + username='Your Username', + grid=grid + })) |