summaryrefslogtreecommitdiff
path: root/indra/newview/lllogininstance.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lllogininstance.cpp')
-rw-r--r--indra/newview/lllogininstance.cpp495
1 files changed, 495 insertions, 0 deletions
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
new file mode 100644
index 0000000000..7b2f5984a7
--- /dev/null
+++ b/indra/newview/lllogininstance.cpp
@@ -0,0 +1,495 @@
+/**
+ * @file lllogininstance.cpp
+ * @brief Viewer's host for a login connection.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lllogininstance.h"
+
+// llcommon
+#include "llevents.h"
+#include "llmd5.h"
+#include "stringize.h"
+
+// llmessage (!)
+#include "llfiltersd2xmlrpc.h" // for xml_escape_string()
+
+// login
+#include "lllogin.h"
+
+// newview
+#include "llviewernetwork.h"
+#include "llviewercontrol.h"
+#include "llslurl.h"
+#include "llstartup.h"
+#include "llfloaterreg.h"
+#include "llnotifications.h"
+#include "llwindow.h"
+#if LL_LINUX || LL_SOLARIS
+#include "lltrans.h"
+#endif
+#include "llsecapi.h"
+#include "llstartup.h"
+#include "llmachineid.h"
+
+static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback";
+static const char * const TOS_LISTENER_NAME = "lllogininstance_tos";
+
+std::string construct_start_string();
+
+LLLoginInstance::LLLoginInstance() :
+ mLoginModule(new LLLogin()),
+ mNotifications(NULL),
+ mLoginState("offline"),
+ mUserInteraction(true),
+ mSkipOptionalUpdate(false),
+ mAttemptComplete(false),
+ mTransferRate(0.0f),
+ mDispatcher("LLLoginInstance", "change")
+{
+ mLoginModule->getEventPump().listen("lllogininstance",
+ boost::bind(&LLLoginInstance::handleLoginEvent, this, _1));
+ mDispatcher.add("fail.login", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1));
+ mDispatcher.add("connect", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1));
+ mDispatcher.add("disconnect", boost::bind(&LLLoginInstance::handleDisconnect, this, _1));
+ mDispatcher.add("indeterminate", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1));
+}
+
+LLLoginInstance::~LLLoginInstance()
+{
+}
+
+void LLLoginInstance::connect(LLPointer<LLCredential> credentials)
+{
+ std::vector<std::string> uris;
+ LLGridManager::getInstance()->getLoginURIs(uris);
+ connect(uris.front(), credentials);
+}
+
+void LLLoginInstance::connect(const std::string& uri, LLPointer<LLCredential> credentials)
+{
+ mAttemptComplete = false; // Reset attempt complete at this point!
+ constructAuthParams(credentials);
+ mLoginModule->connect(uri, mRequestData);
+}
+
+void LLLoginInstance::reconnect()
+{
+ // Sort of like connect, only using the pre-existing
+ // request params.
+ std::vector<std::string> uris;
+ LLGridManager::getInstance()->getLoginURIs(uris);
+ mLoginModule->connect(uris.front(), mRequestData);
+}
+
+void LLLoginInstance::disconnect()
+{
+ mAttemptComplete = false; // Reset attempt complete at this point!
+ mRequestData.clear();
+ mLoginModule->disconnect();
+}
+
+LLSD LLLoginInstance::getResponse()
+{
+ return mResponseData;
+}
+
+void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credential)
+{
+ // Set up auth request options.
+//#define LL_MINIMIAL_REQUESTED_OPTIONS
+ LLSD requested_options;
+ // *Note: this is where gUserAuth used to be created.
+ requested_options.append("inventory-root");
+ requested_options.append("inventory-skeleton");
+ //requested_options.append("inventory-meat");
+ //requested_options.append("inventory-skel-targets");
+#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS)
+ if(FALSE == gSavedSettings.getBOOL("NoInventoryLibrary"))
+ {
+ requested_options.append("inventory-lib-root");
+ requested_options.append("inventory-lib-owner");
+ requested_options.append("inventory-skel-lib");
+ // requested_options.append("inventory-meat-lib");
+ }
+
+ requested_options.append("initial-outfit");
+ requested_options.append("gestures");
+ requested_options.append("event_categories");
+ requested_options.append("event_notifications");
+ requested_options.append("classified_categories");
+ requested_options.append("adult_compliant");
+ //requested_options.append("inventory-targets");
+ requested_options.append("buddy-list");
+ requested_options.append("newuser-config");
+ requested_options.append("ui-config");
+#endif
+ requested_options.append("map-server-url");
+ requested_options.append("voice-config");
+ requested_options.append("tutorial_setting");
+ requested_options.append("login-flags");
+ requested_options.append("global-textures");
+ if(gSavedSettings.getBOOL("ConnectAsGod"))
+ {
+ gSavedSettings.setBOOL("UseDebugMenus", TRUE);
+ requested_options.append("god-connect");
+ }
+
+ // (re)initialize the request params with creds.
+ LLSD request_params = user_credential->getLoginParams();
+
+ char hashed_unique_id_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ LLMD5 hashed_unique_id;
+ unsigned char unique_id[MAC_ADDRESS_BYTES];
+ if(LLUUID::getNodeID(unique_id) == 0) {
+ if(LLMachineID::getUniqueID(unique_id, sizeof(unique_id)) == 0) {
+ llerrs << "Failed to get an id; cannot uniquely identify this machine." << llendl;
+ }
+ }
+ hashed_unique_id.update(unique_id, MAC_ADDRESS_BYTES);
+ hashed_unique_id.finalize();
+ hashed_unique_id.hex_digest(hashed_unique_id_string);
+
+ request_params["start"] = construct_start_string();
+ request_params["skipoptional"] = mSkipOptionalUpdate;
+ request_params["agree_to_tos"] = false; // Always false here. Set true in
+ request_params["read_critical"] = false; // handleTOSResponse
+ request_params["last_exec_event"] = mLastExecEvent;
+ request_params["mac"] = hashed_unique_id_string;
+ request_params["version"] = gCurrentVersion; // Includes channel name
+ request_params["channel"] = gSavedSettings.getString("VersionChannelName");
+ request_params["id0"] = mSerialNumber;
+
+ mRequestData.clear();
+ mRequestData["method"] = "login_to_simulator";
+ mRequestData["params"] = request_params;
+ mRequestData["options"] = requested_options;
+
+ mRequestData["cfg_srv_timeout"] = gSavedSettings.getF32("LoginSRVTimeout");
+ mRequestData["cfg_srv_pump"] = gSavedSettings.getString("LoginSRVPump");
+}
+
+bool LLLoginInstance::handleLoginEvent(const LLSD& event)
+{
+ LL_DEBUGS("LLLogin") << "LoginListener called!: \n" << event << LL_ENDL;
+
+ if(!(event.has("state") && event.has("change") && event.has("progress")))
+ {
+ llerrs << "Unknown message from LLLogin: " << event << llendl;
+ }
+
+ mLoginState = event["state"].asString();
+ mResponseData = event["data"];
+
+ if(event.has("transfer_rate"))
+ {
+ mTransferRate = event["transfer_rate"].asReal();
+ }
+
+
+
+ // Call the method registered in constructor, if any, for more specific
+ // handling
+ LLEventDispatcher::Callable method(mDispatcher.get(event["change"]));
+ if (! method.empty())
+ {
+ method(event);
+ }
+ return false;
+}
+
+void LLLoginInstance::handleLoginFailure(const LLSD& event)
+{
+ // Login has failed.
+ // Figure out why and respond...
+ LLSD response = event["data"];
+ std::string reason_response = response["reason"].asString();
+ std::string message_response = response["message"].asString();
+ if(mUserInteraction)
+ {
+ // For the cases of critical message or TOS agreement,
+ // start the TOS dialog. The dialog response will be handled
+ // by the LLLoginInstance::handleTOSResponse() callback.
+ // The callback intiates the login attempt next step, either
+ // to reconnect or to end the attempt in failure.
+ if(reason_response == "tos")
+ {
+ LLSD data(LLSD::emptyMap());
+ data["message"] = message_response;
+ data["reply_pump"] = TOS_REPLY_PUMP;
+ LLFloaterReg::showInstance("message_tos", data);
+ LLEventPumps::instance().obtain(TOS_REPLY_PUMP)
+ .listen(TOS_LISTENER_NAME,
+ boost::bind(&LLLoginInstance::handleTOSResponse,
+ this, _1, "agree_to_tos"));
+ }
+ else if(reason_response == "critical")
+ {
+ LLSD data(LLSD::emptyMap());
+ data["message"] = message_response;
+ data["reply_pump"] = TOS_REPLY_PUMP;
+ if(response.has("error_code"))
+ {
+ data["error_code"] = response["error_code"];
+ }
+ if(response.has("certificate"))
+ {
+ data["certificate"] = response["certificate"];
+ }
+
+ LLFloaterReg::showInstance("message_critical", data);
+ LLEventPumps::instance().obtain(TOS_REPLY_PUMP)
+ .listen(TOS_LISTENER_NAME,
+ boost::bind(&LLLoginInstance::handleTOSResponse,
+ this, _1, "read_critical"));
+ }
+ else if(reason_response == "update" || gSavedSettings.getBOOL("ForceMandatoryUpdate"))
+ {
+ gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE);
+ updateApp(true, message_response);
+ }
+ else if(reason_response == "optional")
+ {
+ updateApp(false, message_response);
+ }
+ else
+ {
+ attemptComplete();
+ }
+ }
+ else // no user interaction
+ {
+ attemptComplete();
+ }
+}
+
+void LLLoginInstance::handleLoginSuccess(const LLSD& event)
+{
+ if(gSavedSettings.getBOOL("ForceMandatoryUpdate"))
+ {
+ LLSD response = event["data"];
+ std::string message_response = response["message"].asString();
+
+ // Testing update...
+ gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE);
+
+ // Don't confuse startup by leaving login "online".
+ mLoginModule->disconnect();
+ updateApp(true, message_response);
+ }
+ else
+ {
+ attemptComplete();
+ }
+}
+
+void LLLoginInstance::handleDisconnect(const LLSD& event)
+{
+ // placeholder
+}
+
+void LLLoginInstance::handleIndeterminate(const LLSD& event)
+{
+ // The indeterminate response means that the server
+ // gave the viewer a new url and params to try.
+ // The login module handles the retry, but it gives us the
+ // server response so that we may show
+ // the user some status.
+ LLSD message = event.get("data").get("message");
+ if(message.isDefined())
+ {
+ LLSD progress_update;
+ progress_update["desc"] = message;
+ LLEventPumps::getInstance()->obtain("LLProgressView").post(progress_update);
+ }
+}
+
+bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
+{
+ if(accepted)
+ {
+ // Set the request data to true and retry login.
+ mRequestData["params"][key] = true;
+ reconnect();
+ }
+ else
+ {
+ attemptComplete();
+ }
+
+ LLEventPumps::instance().obtain(TOS_REPLY_PUMP).stopListening(TOS_LISTENER_NAME);
+ return true;
+}
+
+
+void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg)
+{
+ // store off config state, as we might quit soon
+ gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE);
+ LLUIColorTable::instance().saveUserSettings();
+
+ std::ostringstream message;
+ std::string msg;
+ if (!auth_msg.empty())
+ {
+ msg = "(" + auth_msg + ") \n";
+ }
+
+ LLSD args;
+ args["MESSAGE"] = msg;
+
+ LLSD payload;
+ payload["mandatory"] = mandatory;
+
+/*
+ We're constructing one of the following 9 strings here:
+ "DownloadWindowsMandatory"
+ "DownloadWindowsReleaseForDownload"
+ "DownloadWindows"
+ "DownloadMacMandatory"
+ "DownloadMacReleaseForDownload"
+ "DownloadMac"
+ "DownloadLinuxMandatory"
+ "DownloadLinuxReleaseForDownload"
+ "DownloadLinux"
+
+ I've called them out explicitly in this comment so that they can be grepped for.
+ */
+ std::string notification_name = "Download";
+
+#if LL_WINDOWS
+ notification_name += "Windows";
+#elif LL_DARWIN
+ notification_name += "Mac";
+#else
+ notification_name += "Linux";
+#endif
+
+ if (mandatory)
+ {
+ notification_name += "Mandatory";
+ }
+ else
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ notification_name += "ReleaseForDownload";
+#endif
+ }
+
+ if(mNotifications)
+ {
+ mNotifications->add(notification_name, args, payload,
+ boost::bind(&LLLoginInstance::updateDialogCallback, this, _1, _2));
+ }
+}
+
+bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotification::getSelectedOption(notification, response);
+ std::string update_exe_path;
+ bool mandatory = notification["payload"]["mandatory"].asBoolean();
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+ if (option == 2)
+ {
+ // This condition attempts to skip the
+ // update if using a dev build.
+ // The relog probably won't work if the
+ // update is mandatory. :)
+
+ // *REMOVE:Mani - Saving for reference...
+ //LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT );
+ mSkipOptionalUpdate = true;
+ reconnect();
+ return false;
+ }
+#endif
+
+ if (option == 1)
+ {
+ // ...user doesn't want to do it
+ if (mandatory)
+ {
+ // Mandatory update, user chose to not to update...
+ // The login attemp is complete, startup should
+ // quit when detecting this.
+ attemptComplete();
+
+ // *REMOVE:Mani - Saving for reference...
+ //LLAppViewer::instance()->forceQuit();
+ // // Bump them back to the login screen.
+ // //reset_login();
+ }
+ else
+ {
+ // Optional update, user chose to skip
+ mSkipOptionalUpdate = true;
+ reconnect();
+ }
+ return false;
+ }
+
+ if(mUpdaterLauncher)
+ {
+ mUpdaterLauncher();
+ }
+
+ attemptComplete();
+
+ return false;
+}
+
+std::string construct_start_string()
+{
+ std::string start;
+ LLSLURL start_slurl = LLStartUp::getStartSLURL();
+ switch(start_slurl.getType())
+ {
+ case LLSLURL::LOCATION:
+ {
+ // a startup URL was specified
+ LLVector3 position = start_slurl.getPosition();
+ std::string unescaped_start =
+ STRINGIZE( "uri:"
+ << start_slurl.getRegion() << "&"
+ << position[VX] << "&"
+ << position[VY] << "&"
+ << position[VZ]);
+ start = xml_escape_string(unescaped_start);
+ break;
+ }
+ case LLSLURL::HOME_LOCATION:
+ {
+ start = "home";
+ break;
+ }
+ default:
+ {
+ start = "last";
+ }
+ }
+ return start;
+}
+