Merge remote-tracking branch 'll/d476' into fs-vs2017-d476

master
Nicky Dasmijn 2020-07-20 11:53:09 +02:00
commit c7804dbf7e
8 changed files with 275 additions and 240 deletions

View File

@ -838,13 +838,37 @@
<key>archive</key>
<map>
<key>hash</key>
<string>350866eec6be17ffc265904b91dcfe6b</string>
<string>e145f8ea99a21712434e0e868d1885dc</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60900/572290/dullahan-1.7.0.202005311125_81.3.10_gb223419_chromium-81.0.4044.138-darwin64-543086.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62333/588183/dullahan-1.7.0.202006240858_81.3.10_gb223419_chromium-81.0.4044.138-darwin64-544091.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>fdbbbfc377e28cba664f2b1c54ea6086</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62331/588162/dullahan-1.7.0.202006241556_81.3.10_gb223419_chromium-81.0.4044.138-windows-544091.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>d85a32d905b199534e8feafa34b28e39</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62332/588168/dullahan-1.7.0.202006241556_81.3.10_gb223419_chromium-81.0.4044.138-windows64-544091.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
<key>linux64</key>
<map>
<key>archive</key>
@ -857,33 +881,9 @@
<key>name</key>
<string>linux64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>579253178199688a84e9c4f11d6dc3a5</string>
<key>url</key>
<string>http://3p.firestormviewer.org/dullahan-1.7.0.202007182328_81.3.10_gb223419_chromium-81.0.4044.138-windows-202002136.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>8a00268cfd5fce2477420fe2ff2a56e5</string>
<key>url</key>
<string>http://3p.firestormviewer.org/dullahan-1.7.0.202007182328_81.3.10_gb223419_chromium-81.0.4044.138-windows64-202002125.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>1.7.0.202005311828_81.3.10_gb223419_chromium-81.0.4044.138</string>
<string>1.7.0.202006240858_81.3.10_gb223419_chromium-81.0.4044.138</string>
</map>
<key>elfio</key>
<map>

View File

@ -67,7 +67,10 @@ if (WINDOWS)
# http://www.cmake.org/pipermail/cmake/2009-September/032143.html
string(REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
# Without PreferredToolArchitecture=x64, as of 2020-06-26 the 32-bit
# compiler on our TeamCity build hosts has started running out of virtual
# memory for the precompiled header file.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /p:PreferredToolArchitecture=x64")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zo"

View File

@ -232,6 +232,11 @@ public:
};
/// thrown by checkStop()
// It may sound ironic that Stop is derived from LLContinueError, but the
// point is that LLContinueError is the category of exception that should
// not immediately crash the viewer. Stop and its subclasses are to notify
// coroutines that the viewer intends to shut down. The expected response
// is to terminate the coroutine, rather than abort the viewer.
struct Stop: public LLContinueError
{
Stop(const std::string& what): LLContinueError(what) {}

View File

@ -40,6 +40,8 @@
# include <syslog.h>
# include <unistd.h>
# include <sys/stat.h>
#else
# include <io.h>
#endif // !LL_WINDOWS
#include <vector>
#include "string.h"
@ -241,14 +243,11 @@ namespace {
static bool checkANSI(void)
{
#if LL_LINUX || LL_DARWIN
// Check whether it's okay to use ANSI; if stderr is
// a tty then we assume yes. Can be turned off with
// the LL_NO_ANSI_COLOR env var.
return (0 != isatty(2)) &&
(NULL == getenv("LL_NO_ANSI_COLOR"));
#endif // LL_LINUX
return FALSE; // works in a cygwin shell... ;)
}
};

View File

@ -116,12 +116,25 @@ void llcoro::suspend()
void llcoro::suspendUntilTimeout(float seconds)
{
LLCoros::checkStop();
// The fact that we accept non-integer seconds means we should probably
// use granularity finer than one second. However, given the overhead of
// the rest of our processing, it seems silly to use granularity finer
// than a millisecond.
LLCoros::TempStatus st(STRINGIZE("waiting for " << seconds << "s"));
boost::this_fiber::sleep_for(std::chrono::milliseconds(long(seconds * 1000)));
// We used to call boost::this_fiber::sleep_for(). But some coroutines
// (e.g. LLExperienceCache::idleCoro()) sit in a suspendUntilTimeout()
// loop, in which case a sleep_for() call risks sleeping through shutdown.
// So instead, listen for "LLApp" state-changing events -- which
// fortunately is handled for us by suspendUntilEventOnWithTimeout().
// Wait for an event on a bogus LLEventPump on which nobody ever posts
// events. Don't make it static because that would force instantiation of
// the LLEventPumps LLSingleton registry at static initialization time.
// DO allow tweaking the name for uniqueness, this definitely gets
// re-entered on multiple coroutines!
// We could use an LLUUID if it were important to actively prohibit anyone
// from ever posting on this LLEventPump.
LLEventStream bogus("xyzzy", true);
// Timeout is the NORMAL case for this call!
static LLSD timedout;
// Deliver, but ignore, timedout when (as usual) we did not receive any
// "LLApp" event. The point is that suspendUntilEventOnWithTimeout() will
// itself throw Stopping when "LLApp" starts broadcasting shutdown events.
suspendUntilEventOnWithTimeout(bogus, seconds, timedout);
}
namespace
@ -276,6 +289,10 @@ LLSD llcoro::postAndSuspendWithTimeout(const LLSD& event,
{
LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName()
<< " for " << timeout << "s"));
// The fact that we accept non-integer seconds means we should probably
// use granularity finer than one second. However, given the overhead of
// the rest of our processing, it seems silly to use granularity finer
// than a millisecond.
status = future.wait_for(std::chrono::milliseconds(long(timeout * 1000)));
}
// if the future is NOT yet ready, return timeoutResult instead

View File

@ -41,8 +41,8 @@
//=========================================================================
namespace
{
LLTrace::BlockTimerStatHandle FTM_BLEND_WATERVALUES("Blending Water Environment");
LLTrace::BlockTimerStatHandle FTM_UPDATE_WATERVALUES("Update Water Environment");
LLTrace::BlockTimerStatHandle FTM_BLEND_WATERVALUES("Blending Water Environment Day");
LLTrace::BlockTimerStatHandle FTM_UPDATE_WATERVALUES("Update Water Environment Day");
template<typename T>
inline T get_wrapping_distance(T begin, T end)

View File

@ -622,68 +622,76 @@ void LLAppViewerWin32::disableWinErrorReporting()
}
const S32 MAX_CONSOLE_LINES = 500;
// Only defined in newer SDKs than we currently use
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4
#endif
static bool create_console()
namespace {
void set_stream(const char* desc, FILE* fp, DWORD handle_id, const char* name, const char* mode="w");
bool create_console()
{
int h_con_handle;
intptr_t l_std_handle;
// allocate a console for this app
const bool isConsoleAllocated = AllocConsole();
CONSOLE_SCREEN_BUFFER_INFO coninfo;
FILE *fp;
if (isConsoleAllocated)
{
// set the screen buffer to be big enough to let us scroll text
CONSOLE_SCREEN_BUFFER_INFO coninfo;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
// allocate a console for this app
const bool isConsoleAllocated = AllocConsole();
// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
// redirect unbuffered STDOUT to the console
l_std_handle = reinterpret_cast<decltype(l_std_handle)>(GetStdHandle(STD_OUTPUT_HANDLE));
h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
if (h_con_handle == -1)
{
LL_WARNS() << "create_console() failed to open stdout handle" << LL_ENDL;
}
else
{
fp = _fdopen( h_con_handle, "w" );
*stdout = *fp;
setvbuf( stdout, NULL, _IONBF, 0 );
}
// redirect unbuffered STDIN to the console
l_std_handle = reinterpret_cast<decltype(l_std_handle)>(GetStdHandle(STD_INPUT_HANDLE));
h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
if (h_con_handle == -1)
{
LL_WARNS() << "create_console() failed to open stdin handle" << LL_ENDL;
}
else
{
fp = _fdopen( h_con_handle, "r" );
*stdin = *fp;
setvbuf( stdin, NULL, _IONBF, 0 );
}
// redirect unbuffered STDERR to the console
l_std_handle = reinterpret_cast<decltype(l_std_handle)>(GetStdHandle(STD_ERROR_HANDLE));
h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
if (h_con_handle == -1)
{
LL_WARNS() << "create_console() failed to open stderr handle" << LL_ENDL;
}
else
{
fp = _fdopen( h_con_handle, "w" );
*stderr = *fp;
setvbuf( stderr, NULL, _IONBF, 0 );
}
// redirect unbuffered STDOUT to the console
set_stream("stdout", stdout, STD_OUTPUT_HANDLE, "CONOUT$");
// redirect unbuffered STDERR to the console
set_stream("stderr", stderr, STD_ERROR_HANDLE, "CONOUT$");
// redirect unbuffered STDIN to the console
// Don't bother: our console is solely for log output. We never read stdin.
// set_stream("stdin", stdin, STD_INPUT_HANDLE, "CONIN$", "r");
}
return isConsoleAllocated;
}
void set_stream(const char* desc, FILE* fp, DWORD handle_id, const char* name, const char* mode)
{
// SL-13528: This code used to be based on
// http://dslweb.nwnexus.com/~ast/dload/guicon.htm
// (referenced in https://stackoverflow.com/a/191880).
// But one of the comments on that StackOverflow answer points out that
// assigning to *stdout or *stderr "probably doesn't even work with the
// Universal CRT that was introduced in 2015," suggesting freopen_s()
// instead. Code below is based on https://stackoverflow.com/a/55875595.
auto std_handle = GetStdHandle(handle_id);
if (std_handle == INVALID_HANDLE_VALUE)
{
LL_WARNS() << "create_console() failed to get " << desc << " handle" << LL_ENDL;
}
else
{
if (mode == std::string("w"))
{
// Enable color processing on Windows 10 console windows.
DWORD dwMode = 0;
GetConsoleMode(std_handle, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(std_handle, dwMode);
}
// Redirect the passed fp to the console.
FILE* ignore;
if (freopen_s(&ignore, name, mode, fp) == 0)
{
// use unbuffered I/O
setvbuf( fp, NULL, _IONBF, 0 );
}
}
}
} // anonymous namespace
LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line) :
mCmdLine(cmd_line),
mIsConsoleAllocated(false)

View File

@ -148,170 +148,173 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
}
try
{
LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::getName()
<< " with uri '" << uri << "', parameters " << printable_params << LL_ENDL;
LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::getName()
<< " with uri '" << uri << "', parameters " << printable_params << LL_ENDL;
LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction"));
// EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used
// to share them -- but the EXT-3934 fix made it possible for an abandoned
// SRV response to arrive just as we were expecting the XMLRPC response.
LLEventStream loginReplyPump("loginreply", true);
LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction"));
// EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used
// to share them -- but the EXT-3934 fix made it possible for an abandoned
// SRV response to arrive just as we were expecting the XMLRPC response.
LLEventStream loginReplyPump("loginreply", true);
LLSD::Integer attempts = 0;
LLSD::Integer attempts = 0;
LLSD request(login_params);
request["reply"] = loginReplyPump.getName();
request["uri"] = uri;
std::string status;
LLSD request(login_params);
request["reply"] = loginReplyPump.getName();
request["uri"] = uri;
std::string status;
// Loop back to here if login attempt redirects to a different
// request["uri"]
for (;;)
{
++attempts;
LLSD progress_data;
progress_data["attempt"] = attempts;
progress_data["request"] = request;
if (progress_data["request"].has("params")
&& progress_data["request"]["params"].has("passwd"))
// Loop back to here if login attempt redirects to a different
// request["uri"]
for (;;)
{
progress_data["request"]["params"]["passwd"] = "*******";
}
sendProgressEvent("offline", "authenticating", progress_data);
++attempts;
LLSD progress_data;
progress_data["attempt"] = attempts;
progress_data["request"] = request;
if (progress_data["request"].has("params")
&& progress_data["request"]["params"].has("passwd"))
{
progress_data["request"]["params"]["passwd"] = "*******";
}
sendProgressEvent("offline", "authenticating", progress_data);
// We expect zero or more "Downloading" status events, followed by
// exactly one event with some other status. Use postAndSuspend() the
// first time, because -- at least in unit-test land -- it's
// possible for the reply to arrive before the post() call
// returns. Subsequent responses, of course, must be awaited
// without posting again.
for (mAuthResponse = validateResponse(loginReplyPump.getName(),
llcoro::postAndSuspend(request, xmlrpcPump, loginReplyPump, "reply"));
mAuthResponse["status"].asString() == "Downloading";
mAuthResponse = validateResponse(loginReplyPump.getName(),
llcoro::suspendUntilEventOn(loginReplyPump)))
// We expect zero or more "Downloading" status events, followed by
// exactly one event with some other status. Use postAndSuspend() the
// first time, because -- at least in unit-test land -- it's
// possible for the reply to arrive before the post() call
// returns. Subsequent responses, of course, must be awaited
// without posting again.
for (mAuthResponse = validateResponse(loginReplyPump.getName(),
llcoro::postAndSuspend(request, xmlrpcPump, loginReplyPump, "reply"));
mAuthResponse["status"].asString() == "Downloading";
mAuthResponse = validateResponse(loginReplyPump.getName(),
llcoro::suspendUntilEventOn(loginReplyPump)))
{
// Still Downloading -- send progress update.
sendProgressEvent("offline", "downloading");
}
LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL;
status = mAuthResponse["status"].asString();
// Okay, we've received our final status event for this
// request. Unless we got a redirect response, break the retry
// loop for the current rewrittenURIs entry.
if (!(status == "Complete" &&
mAuthResponse["responses"]["login"].asString() == "indeterminate"))
{
break;
}
sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]);
// Here the login service at the current URI is redirecting us
// to some other URI ("indeterminate" -- why not "redirect"?).
// The response should contain another uri to try, with its
// own auth method.
request["uri"] = mAuthResponse["responses"]["next_url"].asString();
request["method"] = mAuthResponse["responses"]["next_method"].asString();
} // loop back to try the redirected URI
// Here we're done with redirects.
if (status == "Complete")
{
// Still Downloading -- send progress update.
sendProgressEvent("offline", "downloading");
// StatusComplete does not imply auth success. Check the
// actual outcome of the request. We've already handled the
// "indeterminate" case in the loop above.
if (mAuthResponse["responses"]["login"].asString() == "true")
{
sendProgressEvent("online", "connect", mAuthResponse["responses"]);
}
else
{
// Synchronize here with the updater. We synchronize here rather
// than in the fail.login handler, which actually examines the
// response from login.cgi, because here we are definitely in a
// coroutine and can definitely use suspendUntilBlah(). Whoever's
// listening for fail.login might not be.
// If the reason for login failure is that we must install a
// required update, we definitely want to pass control to the
// updater to manage that for us. We'll handle any other login
// failure ourselves, as usual. We figure that no matter where you
// are in the world, or what kind of network you're on, we can
// reasonably expect the Viewer Version Manager to respond more or
// less as quickly as login.cgi. This synchronization is only
// intended to smooth out minor races between the two services.
// But what if the updater crashes? Use a timeout so that
// eventually we'll tire of waiting for it and carry on as usual.
// Given the above, it can be a fairly short timeout, at least
// from a human point of view.
// Since sSyncPoint is an LLEventMailDrop, we DEFINITELY want to
// consume the posted event.
// <FS:ND> Disable updater (n.b. this might not beed needed anymore)
// LLCoros::OverrideConsuming oc(true);
// // Timeout should produce the isUndefined() object passed here.
// LL_DEBUGS("LLLogin") << "Login failure, waiting for sync from updater" << LL_ENDL;
// LLSD updater = llcoro::suspendUntilEventOnWithTimeout(sSyncPoint, 10, LLSD());
// if (updater.isUndefined())
// {
// LL_WARNS("LLLogin") << "Failed to hear from updater, proceeding with fail.login"
// << LL_ENDL;
// }
// else
// {
// LL_DEBUGS("LLLogin") << "Got responses from updater and login.cgi" << LL_ENDL;
// }
// // Let the fail.login handler deal with empty updater response.
// LLSD responses(mAuthResponse["responses"]);
// responses["updater"] = updater;
// sendProgressEvent("offline", "fail.login", responses);
sendProgressEvent("offline", "fail.login", mAuthResponse["responses"]);
// </FS:ND>
}
return; // Done!
}
LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL;
status = mAuthResponse["status"].asString();
// Okay, we've received our final status event for this
// request. Unless we got a redirect response, break the retry
// loop for the current rewrittenURIs entry.
if (!(status == "Complete" &&
mAuthResponse["responses"]["login"].asString() == "indeterminate"))
/*==========================================================================*|
// Sometimes we end with "Started" here. Slightly slow server? Seems
// to be ok to just skip it. Otherwise we'd error out and crash in the
// if below.
if( status == "Started")
{
break;
LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL;
continue;
}
|*==========================================================================*/
// If we don't recognize status at all, trouble
if (! (status == "CURLError"
|| status == "XMLRPCError"
|| status == "OtherError"))
{
LL_ERRS("LLLogin") << "Unexpected status from " << xmlrpcPump.getName() << " pump: "
<< mAuthResponse << LL_ENDL;
return;
}
sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]);
// Here status IS one of the errors tested above.
// Tell caller this didn't work out so well.
// Here the login service at the current URI is redirecting us
// to some other URI ("indeterminate" -- why not "redirect"?).
// The response should contain another uri to try, with its
// own auth method.
request["uri"] = mAuthResponse["responses"]["next_url"].asString();
request["method"] = mAuthResponse["responses"]["next_method"].asString();
} // loop back to try the redirected URI
// Here we're done with redirects.
if (status == "Complete")
{
// StatusComplete does not imply auth success. Check the
// actual outcome of the request. We've already handled the
// "indeterminate" case in the loop above.
if (mAuthResponse["responses"]["login"].asString() == "true")
// *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an
// llsd with no "responses" node. To make the output from an incomplete login symmetrical
// to success, add a data/message and data/reason fields.
LLSD error_response(LLSDMap
("reason", mAuthResponse["status"])
("errorcode", mAuthResponse["errorcode"])
("message", mAuthResponse["error"]));
if(mAuthResponse.has("certificate"))
{
sendProgressEvent("online", "connect", mAuthResponse["responses"]);
error_response["certificate"] = mAuthResponse["certificate"];
}
else
{
// Synchronize here with the updater. We synchronize here rather
// than in the fail.login handler, which actually examines the
// response from login.cgi, because here we are definitely in a
// coroutine and can definitely use suspendUntilBlah(). Whoever's
// listening for fail.login might not be.
// If the reason for login failure is that we must install a
// required update, we definitely want to pass control to the
// updater to manage that for us. We'll handle any other login
// failure ourselves, as usual. We figure that no matter where you
// are in the world, or what kind of network you're on, we can
// reasonably expect the Viewer Version Manager to respond more or
// less as quickly as login.cgi. This synchronization is only
// intended to smooth out minor races between the two services.
// But what if the updater crashes? Use a timeout so that
// eventually we'll tire of waiting for it and carry on as usual.
// Given the above, it can be a fairly short timeout, at least
// from a human point of view.
// Since sSyncPoint is an LLEventMailDrop, we DEFINITELY want to
// consume the posted event.
// <FS:Ansariel> Disable updater
//LLCoros::OverrideConsuming oc(true);
//// Timeout should produce the isUndefined() object passed here.
//LL_DEBUGS("LLLogin") << "Login failure, waiting for sync from updater" << LL_ENDL;
//LLSD updater = llcoro::suspendUntilEventOnWithTimeout(sSyncPoint, 10, LLSD());
//if (updater.isUndefined())
//{
// LL_WARNS("LLLogin") << "Failed to hear from updater, proceeding with fail.login"
// << LL_ENDL;
//}
//else
//{
// LL_DEBUGS("LLLogin") << "Got responses from updater and login.cgi" << LL_ENDL;
//}
//// Let the fail.login handler deal with empty updater response.
//LLSD responses(mAuthResponse["responses"]);
//responses["updater"] = updater;
//sendProgressEvent("offline", "fail.login", responses);
sendProgressEvent("offline", "fail.login", mAuthResponse["responses"]);
// </FS:Ansariel>
}
return; // Done!
}
// /* Sometimes we end with "Started" here. Slightly slow server?
// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below.
// */
// if( status == "Started")
// {
// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL;
// continue;
// }
// If we don't recognize status at all, trouble
if (! (status == "CURLError"
|| status == "XMLRPCError"
|| status == "OtherError"))
{
LL_ERRS("LLLogin") << "Unexpected status from " << xmlrpcPump.getName() << " pump: "
<< mAuthResponse << LL_ENDL;
return;
}
// Here status IS one of the errors tested above.
// Tell caller this didn't work out so well.
// *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an
// llsd with no "responses" node. To make the output from an incomplete login symmetrical
// to success, add a data/message and data/reason fields.
LLSD error_response(LLSDMap
("reason", mAuthResponse["status"])
("errorcode", mAuthResponse["errorcode"])
("message", mAuthResponse["error"]));
if(mAuthResponse.has("certificate"))
{
error_response["certificate"] = mAuthResponse["certificate"];
}
sendProgressEvent("offline", "fail.login", error_response);
sendProgressEvent("offline", "fail.login", error_response);
}
catch (...) {
CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName()
<< "('" << uri << "', " << printable_params << ")"));
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName()
<< "('" << uri << "', " << printable_params << ")"));
throw;
}
}