PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/viewer_components/login/lllogin.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 434 lines | 205 code | 51 blank | 178 comment | 26 complexity | 059177884f22fb777df626707c51615a MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lllogin.cpp
  3. *
  4. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  5. * Second Life Viewer Source Code
  6. * Copyright (C) 2010, Linden Research, Inc.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation;
  11. * version 2.1 of the License only.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  23. * $/LicenseInfo$
  24. */
  25. #include <boost/coroutine/coroutine.hpp>
  26. #include "linden_common.h"
  27. #include "llsd.h"
  28. #include "llsdutil.h"
  29. /*==========================================================================*|
  30. #ifdef LL_WINDOWS
  31. // non-virtual destructor warning, boost::statechart does this intentionally.
  32. #pragma warning (disable : 4265)
  33. #endif
  34. |*==========================================================================*/
  35. #include "lllogin.h"
  36. #include <boost/bind.hpp>
  37. #include "llcoros.h"
  38. #include "llevents.h"
  39. #include "lleventfilter.h"
  40. #include "lleventcoro.h"
  41. //*********************
  42. // LLLogin
  43. // *NOTE:Mani - Is this Impl needed now that the state machine runs the show?
  44. class LLLogin::Impl
  45. {
  46. public:
  47. Impl():
  48. mPump("login", true) // Create the module's event pump with a tweaked (unique) name.
  49. {
  50. mValidAuthResponse["status"] = LLSD();
  51. mValidAuthResponse["errorcode"] = LLSD();
  52. mValidAuthResponse["error"] = LLSD();
  53. mValidAuthResponse["transfer_rate"] = LLSD();
  54. }
  55. void connect(const std::string& uri, const LLSD& credentials);
  56. void disconnect();
  57. LLEventPump& getEventPump() { return mPump; }
  58. private:
  59. LLSD getProgressEventLLSD(const std::string& state, const std::string& change,
  60. const LLSD& data = LLSD())
  61. {
  62. LLSD status_data;
  63. status_data["state"] = state;
  64. status_data["change"] = change;
  65. status_data["progress"] = 0.0f;
  66. if(mAuthResponse.has("transfer_rate"))
  67. {
  68. status_data["transfer_rate"] = mAuthResponse["transfer_rate"];
  69. }
  70. if(data.isDefined())
  71. {
  72. status_data["data"] = data;
  73. }
  74. return status_data;
  75. }
  76. void sendProgressEvent(const std::string& state, const std::string& change,
  77. const LLSD& data = LLSD())
  78. {
  79. LLSD status_data = getProgressEventLLSD(state, change, data);
  80. mPump.post(status_data);
  81. }
  82. LLSD validateResponse(const std::string& pumpName, const LLSD& response)
  83. {
  84. // Validate the response. If we don't recognize it, things
  85. // could get ugly.
  86. std::string mismatch(llsd_matches(mValidAuthResponse, response));
  87. if (! mismatch.empty())
  88. {
  89. LL_ERRS("LLLogin") << "Received unrecognized event (" << mismatch << ") on "
  90. << pumpName << "pump: " << response
  91. << LL_ENDL;
  92. return LLSD();
  93. }
  94. return response;
  95. }
  96. // In a coroutine's top-level function args, do NOT NOT NOT accept
  97. // references (const or otherwise) to anything but the self argument! Pass
  98. // by value only!
  99. void login_(LLCoros::self& self, std::string uri, LLSD credentials);
  100. LLEventStream mPump;
  101. LLSD mAuthResponse, mValidAuthResponse;
  102. };
  103. void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params)
  104. {
  105. LL_DEBUGS("LLLogin") << " connect with uri '" << uri << "', login_params " << login_params << LL_ENDL;
  106. // Launch a coroutine with our login_() method. Run the coroutine until
  107. // its first wait; at that point, return here.
  108. std::string coroname =
  109. LLCoros::instance().launch("LLLogin::Impl::login_",
  110. boost::bind(&Impl::login_, this, _1, uri, login_params));
  111. LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL;
  112. }
  113. void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_params)
  114. {
  115. try
  116. {
  117. LLSD printable_params = login_params;
  118. //if(printable_params.has("params")
  119. // && printable_params["params"].has("passwd"))
  120. //{
  121. // printable_params["params"]["passwd"] = "*******";
  122. //}
  123. LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName(self)
  124. << " with uri '" << uri << "', parameters " << printable_params << LL_ENDL;
  125. // Arriving in SRVRequest state
  126. LLEventStream replyPump("SRVreply", true);
  127. // Should be an array of one or more uri strings.
  128. LLSD rewrittenURIs;
  129. {
  130. LLEventTimeout filter(replyPump);
  131. sendProgressEvent("offline", "srvrequest");
  132. // Request SRV record.
  133. LL_DEBUGS("LLLogin") << "Requesting SRV record from " << uri << LL_ENDL;
  134. // *NOTE:Mani - Completely arbitrary default timeout value for SRV request.
  135. F32 seconds_to_timeout = 5.0f;
  136. if(login_params.has("cfg_srv_timeout"))
  137. {
  138. seconds_to_timeout = login_params["cfg_srv_timeout"].asReal();
  139. }
  140. // If the SRV request times out (e.g. EXT-3934), simulate response: an
  141. // array containing our original URI.
  142. LLSD fakeResponse(LLSD::emptyArray());
  143. fakeResponse.append(uri);
  144. filter.eventAfter(seconds_to_timeout, fakeResponse);
  145. std::string srv_pump_name = "LLAres";
  146. if(login_params.has("cfg_srv_pump"))
  147. {
  148. srv_pump_name = login_params["cfg_srv_pump"].asString();
  149. }
  150. // Make request
  151. LLSD request;
  152. request["op"] = "rewriteURI";
  153. request["uri"] = uri;
  154. request["reply"] = replyPump.getName();
  155. rewrittenURIs = postAndWait(self, request, srv_pump_name, filter);
  156. // EXP-772: If rewrittenURIs fail, try original URI as a fallback.
  157. rewrittenURIs.append(uri);
  158. } // we no longer need the filter
  159. LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction"));
  160. // EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used
  161. // to share them -- but the EXT-3934 fix made it possible for an abandoned
  162. // SRV response to arrive just as we were expecting the XMLRPC response.
  163. LLEventStream loginReplyPump("loginreply", true);
  164. // Loop through the rewrittenURIs, counting attempts along the way.
  165. // Because of possible redirect responses, we may make more than one
  166. // attempt per rewrittenURIs entry.
  167. LLSD::Integer attempts = 0;
  168. for (LLSD::array_const_iterator urit(rewrittenURIs.beginArray()),
  169. urend(rewrittenURIs.endArray());
  170. urit != urend; ++urit)
  171. {
  172. LLSD request(login_params);
  173. request["reply"] = loginReplyPump.getName();
  174. request["uri"] = *urit;
  175. std::string status;
  176. // Loop back to here if login attempt redirects to a different
  177. // request["uri"]
  178. for (;;)
  179. {
  180. ++attempts;
  181. LLSD progress_data;
  182. progress_data["attempt"] = attempts;
  183. progress_data["request"] = request;
  184. if(progress_data["request"].has("params")
  185. && progress_data["request"]["params"].has("passwd"))
  186. {
  187. progress_data["request"]["params"]["passwd"] = "*******";
  188. }
  189. sendProgressEvent("offline", "authenticating", progress_data);
  190. // We expect zero or more "Downloading" status events, followed by
  191. // exactly one event with some other status. Use postAndWait() the
  192. // first time, because -- at least in unit-test land -- it's
  193. // possible for the reply to arrive before the post() call
  194. // returns. Subsequent responses, of course, must be awaited
  195. // without posting again.
  196. for (mAuthResponse = validateResponse(loginReplyPump.getName(),
  197. postAndWait(self, request, xmlrpcPump, loginReplyPump, "reply"));
  198. mAuthResponse["status"].asString() == "Downloading";
  199. mAuthResponse = validateResponse(loginReplyPump.getName(),
  200. waitForEventOn(self, loginReplyPump)))
  201. {
  202. // Still Downloading -- send progress update.
  203. sendProgressEvent("offline", "downloading");
  204. }
  205. LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL;
  206. status = mAuthResponse["status"].asString();
  207. // Okay, we've received our final status event for this
  208. // request. Unless we got a redirect response, break the retry
  209. // loop for the current rewrittenURIs entry.
  210. if (!(status == "Complete" &&
  211. mAuthResponse["responses"]["login"].asString() == "indeterminate"))
  212. {
  213. break;
  214. }
  215. sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]);
  216. // Here the login service at the current URI is redirecting us
  217. // to some other URI ("indeterminate" -- why not "redirect"?).
  218. // The response should contain another uri to try, with its
  219. // own auth method.
  220. request["uri"] = mAuthResponse["responses"]["next_url"].asString();
  221. request["method"] = mAuthResponse["responses"]["next_method"].asString();
  222. } // loop back to try the redirected URI
  223. // Here we're done with redirects for the current rewrittenURIs
  224. // entry.
  225. if (status == "Complete")
  226. {
  227. // StatusComplete does not imply auth success. Check the
  228. // actual outcome of the request. We've already handled the
  229. // "indeterminate" case in the loop above.
  230. if (mAuthResponse["responses"]["login"].asString() == "true")
  231. {
  232. sendProgressEvent("online", "connect", mAuthResponse["responses"]);
  233. }
  234. else
  235. {
  236. sendProgressEvent("offline", "fail.login", mAuthResponse["responses"]);
  237. }
  238. return; // Done!
  239. }
  240. // If we don't recognize status at all, trouble
  241. if (! (status == "CURLError"
  242. || status == "XMLRPCError"
  243. || status == "OtherError"))
  244. {
  245. LL_ERRS("LLLogin") << "Unexpected status from " << xmlrpcPump.getName() << " pump: "
  246. << mAuthResponse << LL_ENDL;
  247. return;
  248. }
  249. // Here status IS one of the errors tested above.
  250. } // Retry if there are any more rewrittenURIs.
  251. // Here we got through all the rewrittenURIs without succeeding. Tell
  252. // caller this didn't work out so well. Of course, the only failure data
  253. // we can reasonably show are from the last of the rewrittenURIs.
  254. // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an
  255. // llsd with no "responses" node. To make the output from an incomplete login symmetrical
  256. // to success, add a data/message and data/reason fields.
  257. LLSD error_response;
  258. error_response["reason"] = mAuthResponse["status"];
  259. error_response["errorcode"] = mAuthResponse["errorcode"];
  260. error_response["message"] = mAuthResponse["error"];
  261. if(mAuthResponse.has("certificate"))
  262. {
  263. error_response["certificate"] = mAuthResponse["certificate"];
  264. }
  265. sendProgressEvent("offline", "fail.login", error_response);
  266. }
  267. catch (...) {
  268. llerrs << "login exception caught" << llendl;
  269. }
  270. }
  271. void LLLogin::Impl::disconnect()
  272. {
  273. sendProgressEvent("offline", "disconnect");
  274. }
  275. //*********************
  276. // LLLogin
  277. LLLogin::LLLogin() :
  278. mImpl(new LLLogin::Impl())
  279. {
  280. }
  281. LLLogin::~LLLogin()
  282. {
  283. }
  284. void LLLogin::connect(const std::string& uri, const LLSD& credentials)
  285. {
  286. mImpl->connect(uri, credentials);
  287. }
  288. void LLLogin::disconnect()
  289. {
  290. mImpl->disconnect();
  291. }
  292. LLEventPump& LLLogin::getEventPump()
  293. {
  294. return mImpl->getEventPump();
  295. }
  296. // The following is the list of important functions that happen in the
  297. // current login process that we want to move to this login module.
  298. // The list associates to event with the original idle_startup() 'STATE'.
  299. // Rewrite URIs
  300. // State_LOGIN_AUTH_INIT
  301. // Given a vector of login uris (usually just one), perform a dns lookup for the
  302. // SRV record from each URI. I think this is used to distribute login requests to
  303. // a single URI to multiple hosts.
  304. // This is currently a synchronous action. (See LLSRV::rewriteURI() implementation)
  305. // On dns lookup error the output uris == the input uris.
  306. //
  307. // Input: A vector of login uris
  308. // Output: A vector of login uris
  309. //
  310. // Code:
  311. // std::vector<std::string> uris;
  312. // LLViewerLogin::getInstance()->getLoginURIs(uris);
  313. // std::vector<std::string>::const_iterator iter, end;
  314. // for (iter = uris.begin(), end = uris.end(); iter != end; ++iter)
  315. // {
  316. // std::vector<std::string> rewritten;
  317. // rewritten = LLSRV::rewriteURI(*iter);
  318. // sAuthUris.insert(sAuthUris.end(),
  319. // rewritten.begin(), rewritten.end());
  320. // }
  321. // sAuthUriNum = 0;
  322. // Authenticate
  323. // STATE_LOGIN_AUTHENTICATE
  324. // Connect to the login server, presumably login.cgi, requesting the login
  325. // and a slew of related initial connection information.
  326. // This is an asynch action. The final response, whether success or error
  327. // is handled by STATE_LOGIN_PROCESS_REPONSE.
  328. // There is no immediate error or output from this call.
  329. //
  330. // Input:
  331. // URI
  332. // Credentials (first, last, password)
  333. // Start location
  334. // Bool Flags:
  335. // skip optional update
  336. // accept terms of service
  337. // accept critical message
  338. // Last exec event. (crash state of previous session)
  339. // requested optional data (inventory skel, initial outfit, etc.)
  340. // local mac address
  341. // viewer serial no. (md5 checksum?)
  342. //sAuthUriNum = llclamp(sAuthUriNum, 0, (S32)sAuthUris.size()-1);
  343. //LLUserAuth::getInstance()->authenticate(
  344. // sAuthUris[sAuthUriNum],
  345. // auth_method,
  346. // firstname,
  347. // lastname,
  348. // password, // web_login_key,
  349. // start.str(),
  350. // gSkipOptionalUpdate,
  351. // gAcceptTOS,
  352. // gAcceptCriticalMessage,
  353. // gLastExecEvent,
  354. // requested_options,
  355. // hashed_mac_string,
  356. // LLAppViewer::instance()->getSerialNumber());
  357. //
  358. // Download the Response
  359. // STATE_LOGIN_NO_REPONSE_YET and STATE_LOGIN_DOWNLOADING
  360. // I had assumed that this was default behavior of the message system. However...
  361. // During login, the message system is checked only by these two states in idle_startup().
  362. // I guess this avoids the overhead of checking network messages for those login states
  363. // that don't need to do so, but geez!
  364. // There are two states to do this one function just to update the login
  365. // status text from 'Logging In...' to 'Downloading...'
  366. //
  367. //
  368. // Handle Login Response
  369. // STATE_LOGIN_PROCESS_RESPONSE
  370. //
  371. // This state handle the result of the request to login. There is a metric ton of
  372. // code in this case. This state will transition to:
  373. // STATE_WORLD_INIT, on success.
  374. // STATE_AUTHENTICATE, on failure.
  375. // STATE_UPDATE_CHECK, to handle user during login interaction like TOS display.
  376. //
  377. // Much of the code in this case belongs on the viewer side of the fence and not in login.
  378. // Login should probably return with a couple of events, success and failure.
  379. // Failure conditions can be specified in the events data pacet to allow the viewer
  380. // to re-engauge login as is appropriate. (Or should there be multiple failure messages?)
  381. // Success is returned with the data requested from the login. According to OGP specs
  382. // there may be intermediate steps before reaching this result in future login
  383. // implementations.