Implement all aisv3 HTTP requests. Added PATCH and COPY methods.
This commit is contained in:
@@ -803,6 +803,23 @@ void CurlEasyRequest::setPut(U32 size, bool keepalive)
|
||||
setopt(CURLOPT_INFILESIZE, size);
|
||||
}
|
||||
|
||||
void CurlEasyRequest::setPatch(U32 size, bool keepalive)
|
||||
{
|
||||
DoutCurl("PATCH size is " << size << " bytes.");
|
||||
mContentLength = size;
|
||||
|
||||
// The server never replies with 100-continue, so suppress the "Expect: 100-continue" header that libcurl adds by default.
|
||||
addHeader("Expect:");
|
||||
if (size > 0 && keepalive)
|
||||
{
|
||||
addHeader("Connection: keep-alive");
|
||||
addHeader("Keep-alive: 300");
|
||||
}
|
||||
setopt(CURLOPT_UPLOAD, 1);
|
||||
setopt(CURLOPT_INFILESIZE, size);\
|
||||
setopt(CURLOPT_CUSTOMREQUEST, "PATCH");
|
||||
}
|
||||
|
||||
void CurlEasyRequest::setPost(AIPostFieldPtr const& postdata, U32 size, bool keepalive)
|
||||
{
|
||||
llassert_always(postdata->data());
|
||||
|
||||
@@ -222,6 +222,7 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
void setPost_raw(U32 size, char const* data, bool keepalive);
|
||||
public:
|
||||
void setPut(U32 size, bool keepalive = true);
|
||||
void setPatch(U32 size, bool keepalive = true);
|
||||
void setPost(U32 size, bool keepalive = true) { setPost_raw(size, NULL, keepalive); }
|
||||
void setPost(AIPostFieldPtr const& postdata, U32 size, bool keepalive = true);
|
||||
void setPost(char const* data, U32 size, bool keepalive = true) { setPost(new AIPostField(data), size, keepalive); }
|
||||
|
||||
@@ -908,6 +908,7 @@ AIHTTPTimeoutPolicy const* AIHTTPTimeoutPolicy::getTimeoutPolicyByName(std::stri
|
||||
P(assetReportHandler);
|
||||
P(authHandler);
|
||||
P2(baseCapabilitiesComplete, transfer_18s_connect_5s);
|
||||
P2(baseCapabilitiesCompleteTracker, transfer_18s_connect_5s);
|
||||
P(blockingLLSDPost);
|
||||
P(blockingLLSDGet);
|
||||
P(blockingRawGet);
|
||||
@@ -924,7 +925,6 @@ P(fetchScriptLimitsRegionInfoResponder);
|
||||
P(fetchScriptLimitsRegionSummaryResponder);
|
||||
P(fnPtrResponder);
|
||||
P(floaterPermsResponder);
|
||||
P2(gamingDataReceived, transfer_22s_connect_10s);
|
||||
P2(groupProposalBallotResponder, transfer_300s);
|
||||
P(homeLocationResponder);
|
||||
P2(HTTPGetResponder, reply_15s);
|
||||
@@ -954,7 +954,7 @@ P(requestAgentUpdateAppearance);
|
||||
P2(incrementCofVersionResponder_timeouts, transfer_30s_connect_10s);
|
||||
P(responderIgnore);
|
||||
P(setDisplayNameResponder);
|
||||
P2(simulatorFeaturesReceived, transfer_22s_connect_10s);
|
||||
P2(baseFeaturesReceived, transfer_22s_connect_10s);
|
||||
P2(startGroupVoteResponder, transfer_300s);
|
||||
P(translationReceiver);
|
||||
P(uploadModelPremissionsResponder);
|
||||
|
||||
@@ -727,6 +727,11 @@ void LLHTTPClient::putRaw(const std::string& url, const U8* data, S32 size, Resp
|
||||
request(url, HTTP_PUT, new RawInjector(data, size), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), no_keep_alive, no_does_authentication, no_allow_compressed_reply);
|
||||
}
|
||||
|
||||
void LLHTTPClient::patch(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug), EKeepAlive keepalive, AIStateMachine* parent, AIStateMachine::state_type new_parent_state)
|
||||
{
|
||||
request(url, HTTP_PATCH, new LLSDInjector(body), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, no_does_authentication, allow_compressed_reply, parent, new_parent_state);
|
||||
}
|
||||
|
||||
void LLHTTPClient::post(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug), EKeepAlive keepalive, AIStateMachine* parent, AIStateMachine::state_type new_parent_state)
|
||||
{
|
||||
request(url, HTTP_POST, new LLSDInjector(body), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, no_does_authentication, allow_compressed_reply, parent, new_parent_state);
|
||||
@@ -780,3 +785,10 @@ void LLHTTPClient::move(std::string const& url, std::string const& destination,
|
||||
headers.addHeader("Destination", destination);
|
||||
request(url, HTTP_MOVE, NULL, responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug));
|
||||
}
|
||||
|
||||
// static
|
||||
void LLHTTPClient::copy(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug))
|
||||
{
|
||||
headers.addHeader("Destination", destination);
|
||||
request(url, HTTP_COPY, NULL, responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug));
|
||||
}
|
||||
|
||||
@@ -121,6 +121,8 @@ public:
|
||||
HTTP_POST,
|
||||
HTTP_DELETE,
|
||||
HTTP_MOVE, // Caller will need to set 'Destination' header
|
||||
HTTP_PATCH,
|
||||
HTTP_COPY,
|
||||
REQUEST_ACTION_COUNT
|
||||
};
|
||||
|
||||
@@ -499,6 +501,10 @@ public:
|
||||
static void getHeaderOnly(std::string const& url, ResponderHeadersOnly* responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off))
|
||||
{ AIHTTPHeaders headers; getHeaderOnly(url, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug)); }
|
||||
|
||||
static void patch(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0);
|
||||
static void patch(std::string const& url, LLSD const& body, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0)
|
||||
{ AIHTTPHeaders headers; patch(url, body, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, parent, new_parent_state); }
|
||||
|
||||
static void post(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0);
|
||||
static void post(std::string const& url, LLSD const& body, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0)
|
||||
{ AIHTTPHeaders headers; post(url, body, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, parent, new_parent_state); }
|
||||
@@ -550,6 +556,12 @@ public:
|
||||
|
||||
//@}
|
||||
|
||||
static void copy(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off));
|
||||
static void copy(std::string const& url, std::string const& destination, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off))
|
||||
{
|
||||
AIHTTPHeaders headers; copy(url, destination, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Blocking HTTP GET that returns an LLSD map of status and body.
|
||||
*
|
||||
|
||||
@@ -97,7 +97,7 @@ void LLURLRequest::initialize_impl(void)
|
||||
useProxy(false);
|
||||
}
|
||||
|
||||
if (mAction == LLHTTPClient::HTTP_PUT || mAction == LLHTTPClient::HTTP_POST)
|
||||
if (mAction == LLHTTPClient::HTTP_PUT || mAction == LLHTTPClient::HTTP_POST || mAction == LLHTTPClient::HTTP_PATCH)
|
||||
{
|
||||
// If the Content-Type header was passed in we defer to the caller's wisdom,
|
||||
// but if they did not specify a Content-Type, then ask the injector.
|
||||
@@ -237,7 +237,16 @@ bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w)
|
||||
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
|
||||
rv = true;
|
||||
break;
|
||||
|
||||
|
||||
case LLHTTPClient::HTTP_PATCH:
|
||||
|
||||
curlEasyRequest_w->setPatch(mBodySize, mKeepAlive);
|
||||
|
||||
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
|
||||
rv = true;
|
||||
break;
|
||||
|
||||
case LLHTTPClient::HTTP_POST:
|
||||
|
||||
// Set the handle for an http post
|
||||
@@ -254,6 +263,12 @@ bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w)
|
||||
rv = true;
|
||||
break;
|
||||
|
||||
case LLHTTPClient::HTTP_COPY:
|
||||
// Set the handle for an http copy
|
||||
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "COPY");
|
||||
rv = true;
|
||||
break;
|
||||
|
||||
case LLHTTPClient::HTTP_MOVE:
|
||||
// Set the handle for an http post
|
||||
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
|
||||
|
||||
@@ -109,6 +109,7 @@ set(viewer_SOURCE_FILES
|
||||
lggdicdownload.cpp
|
||||
lgghunspell_wrapper.cpp
|
||||
llaccountingcostmanager.cpp
|
||||
llaisapi.cpp
|
||||
llagent.cpp
|
||||
llagentaccess.cpp
|
||||
llagentcamera.cpp
|
||||
@@ -639,6 +640,7 @@ set(viewer_HEADER_FILES
|
||||
lggdicdownload.h
|
||||
lgghunspell_wrapper.h
|
||||
llaccountingcostmanager.h
|
||||
llaisapi.h
|
||||
llagent.h
|
||||
llagentaccess.h
|
||||
llagentcamera.h
|
||||
|
||||
877
indra/newview/llaisapi.cpp
Normal file
877
indra/newview/llaisapi.cpp
Normal file
@@ -0,0 +1,877 @@
|
||||
/**
|
||||
* @file llaisapi.cpp
|
||||
* @brief classes and functions for interfacing with the v3+ ais inventory service.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2013, 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 "llaisapi.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llcallbacklist.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llsdutil.h"
|
||||
#include "llviewerregion.h"
|
||||
#include "llinventoryobserver.h"
|
||||
#include "llviewercontrol.h"
|
||||
|
||||
///----------------------------------------------------------------------------
|
||||
/// Classes for AISv3 support.
|
||||
///----------------------------------------------------------------------------
|
||||
|
||||
// AISCommand - base class for retry-able HTTP requests using the AISv3 cap.
|
||||
AISCommand::AISCommand(LLPointer<LLInventoryCallback> callback):
|
||||
mCommandFunc(NULL),
|
||||
mCallback(callback)
|
||||
{
|
||||
mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10);
|
||||
}
|
||||
|
||||
bool AISCommand::run_command()
|
||||
{
|
||||
if (NULL == mCommandFunc)
|
||||
{
|
||||
// This may happen if a command failed to initiate itself.
|
||||
LL_WARNS("Inventory") << "AIS command attempted with null command function" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCommandFunc();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void AISCommand::setCommandFunc(command_func_type command_func)
|
||||
{
|
||||
mCommandFunc = command_func;
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool AISCommand::getResponseUUID(const LLSD& content, LLUUID& id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
void AISCommand::httpSuccess()
|
||||
{
|
||||
// Command func holds a reference to self, need to release it
|
||||
// after a success or final failure.
|
||||
setCommandFunc(no_op);
|
||||
|
||||
const LLSD& content = getContent();
|
||||
if (!content.isMap())
|
||||
{
|
||||
failureResult(400, "Malformed response contents", content);
|
||||
return;
|
||||
}
|
||||
mRetryPolicy->onSuccess();
|
||||
|
||||
gInventory.onAISUpdateReceived("AISCommand", content);
|
||||
|
||||
if (mCallback)
|
||||
{
|
||||
LLUUID id; // will default to null if parse fails.
|
||||
getResponseUUID(content,id);
|
||||
mCallback->fire(id);
|
||||
}
|
||||
}
|
||||
|
||||
/*virtual*/
|
||||
void AISCommand::httpFailure()
|
||||
{
|
||||
LL_WARNS("Inventory") << dumpResponse() << LL_ENDL;
|
||||
S32 status = getStatus();
|
||||
const AIHTTPReceivedHeaders& headers = getResponseHeaders();
|
||||
mRetryPolicy->onFailure(status, headers);
|
||||
F32 seconds_to_wait;
|
||||
if (mRetryPolicy->shouldRetry(seconds_to_wait))
|
||||
{
|
||||
doAfterInterval(boost::bind(&AISCommand::run_command,this),seconds_to_wait);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Command func holds a reference to self, need to release it
|
||||
// after a success or final failure.
|
||||
// *TODO: Notify user? This seems bad.
|
||||
setCommandFunc(no_op);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
bool AISCommand::isAPIAvailable()
|
||||
{
|
||||
if (gAgent.getRegion())
|
||||
{
|
||||
return gAgent.getRegion()->isCapabilityAvailable("InventoryAPIv3");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//static
|
||||
bool AISCommand::getInvCap(std::string& cap)
|
||||
{
|
||||
if (gAgent.getRegion())
|
||||
{
|
||||
cap = gAgent.getRegion()->getCapability("InventoryAPIv3");
|
||||
}
|
||||
if (!cap.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//static
|
||||
bool AISCommand::getLibCap(std::string& cap)
|
||||
{
|
||||
if (gAgent.getRegion())
|
||||
{
|
||||
cap = gAgent.getRegion()->getCapability("LibraryAPIv3");
|
||||
}
|
||||
if (!cap.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//static
|
||||
void AISCommand::getCapabilityNames(LLSD& capabilityNames)
|
||||
{
|
||||
capabilityNames.append("InventoryAPIv3");
|
||||
capabilityNames.append("LibraryAPIv3");
|
||||
}
|
||||
|
||||
RemoveItemCommand::RemoveItemCommand(const LLUUID& item_id,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
std::string url = cap + std::string("/item/") + item_id.asString();
|
||||
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off));
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
RemoveCategoryCommand::RemoveCategoryCommand(const LLUUID& item_id,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
std::string url = cap + std::string("/category/") + item_id.asString();
|
||||
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off));
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
PurgeDescendentsCommand::PurgeDescendentsCommand(const LLUUID& item_id,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
std::string url = cap + std::string("/category/") + item_id.asString() + "/children";
|
||||
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off));
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
UpdateItemCommand::UpdateItemCommand(const LLUUID& item_id,
|
||||
const LLSD& updates,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
mUpdates(updates),
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
std::string url = cap + std::string("/item/") + item_id.asString();
|
||||
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
|
||||
LL_DEBUGS("Inventory") << "request: " << ll_pretty_print_sd(mUpdates) << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
AIHTTPHeaders headers;
|
||||
headers.addHeader("Content-Type", "application/llsd+xml");
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off), keep_alive, (AIStateMachine*)NULL, 0);
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
UpdateCategoryCommand::UpdateCategoryCommand(const LLUUID& cat_id,
|
||||
const LLSD& updates,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
mUpdates(updates),
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
std::string url = cap + std::string("/category/") + cat_id.asString();
|
||||
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
AIHTTPHeaders headers;
|
||||
headers.addHeader("Content-Type", "application/llsd+xml");
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off), keep_alive, (AIStateMachine*)NULL, 0);
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
CreateInventoryCommand::CreateInventoryCommand(const LLUUID& parent_id,
|
||||
const LLSD& new_inventory,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
mNewInventory(new_inventory),
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LLUUID tid;
|
||||
tid.generate();
|
||||
std::string url = cap + std::string("/category/") + parent_id.asString() + "?tid=" + tid.asString();
|
||||
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
AIHTTPHeaders headers;
|
||||
headers.addHeader("Content-Type", "application/llsd+xml");
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::post, url, mNewInventory, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off), keep_alive, (AIStateMachine*)NULL, 0);
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback):
|
||||
mContents(contents),
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getInvCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LLUUID tid;
|
||||
tid.generate();
|
||||
std::string url = cap + std::string("/category/") + folder_id.asString() + "/links?tid=" + tid.asString();
|
||||
LL_INFOS() << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
AIHTTPHeaders headers;
|
||||
headers.addHeader("Content-Type", "application/llsd+xml");
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::put, url, mContents, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug_off));
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
CopyLibraryCategoryCommand::CopyLibraryCategoryCommand(const LLUUID& source_id,
|
||||
const LLUUID& dest_id,
|
||||
LLPointer<LLInventoryCallback> callback):
|
||||
AISCommand(callback)
|
||||
{
|
||||
std::string cap;
|
||||
if (!getLibCap(cap))
|
||||
{
|
||||
LL_WARNS() << "No cap found" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LL_DEBUGS("Inventory") << "Copying library category: " << source_id << " => " << dest_id << LL_ENDL;
|
||||
LLUUID tid;
|
||||
tid.generate();
|
||||
std::string url = cap + std::string("/category/") + source_id.asString() + "?tid=" + tid.asString();
|
||||
LL_INFOS() << url << LL_ENDL;
|
||||
LLHTTPClient::ResponderPtr responder = this;
|
||||
command_func_type cmd = boost::bind(&LLHTTPClient::copy, url, dest_id.asString(), responder);
|
||||
setCommandFunc(cmd);
|
||||
}
|
||||
|
||||
bool CopyLibraryCategoryCommand::getResponseUUID(const LLSD& content, LLUUID& id)
|
||||
{
|
||||
if (content.has("category_id"))
|
||||
{
|
||||
id = content["category_id"];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AISUpdate::AISUpdate(const LLSD& update)
|
||||
{
|
||||
parseUpdate(update);
|
||||
}
|
||||
|
||||
void AISUpdate::clearParseResults()
|
||||
{
|
||||
mCatDescendentDeltas.clear();
|
||||
mCatDescendentsKnown.clear();
|
||||
mCatVersionsUpdated.clear();
|
||||
mItemsCreated.clear();
|
||||
mItemsUpdated.clear();
|
||||
mCategoriesCreated.clear();
|
||||
mCategoriesUpdated.clear();
|
||||
mObjectsDeletedIds.clear();
|
||||
mItemIds.clear();
|
||||
mCategoryIds.clear();
|
||||
}
|
||||
|
||||
void AISUpdate::parseUpdate(const LLSD& update)
|
||||
{
|
||||
clearParseResults();
|
||||
parseMeta(update);
|
||||
parseContent(update);
|
||||
}
|
||||
|
||||
void AISUpdate::parseMeta(const LLSD& update)
|
||||
{
|
||||
// parse _categories_removed -> mObjectsDeletedIds
|
||||
uuid_list_t cat_ids;
|
||||
parseUUIDArray(update,"_categories_removed",cat_ids);
|
||||
for (uuid_list_t::const_iterator it = cat_ids.begin();
|
||||
it != cat_ids.end(); ++it)
|
||||
{
|
||||
LLViewerInventoryCategory *cat = gInventory.getCategory(*it);
|
||||
if(cat)
|
||||
{
|
||||
mCatDescendentDeltas[cat->getParentUUID()]--;
|
||||
mObjectsDeletedIds.insert(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Inventory") << "removed category not found " << *it << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// parse _categories_items_removed -> mObjectsDeletedIds
|
||||
uuid_list_t item_ids;
|
||||
parseUUIDArray(update,"_category_items_removed",item_ids);
|
||||
parseUUIDArray(update,"_removed_items",item_ids);
|
||||
for (uuid_list_t::const_iterator it = item_ids.begin();
|
||||
it != item_ids.end(); ++it)
|
||||
{
|
||||
LLViewerInventoryItem *item = gInventory.getItem(*it);
|
||||
if(item)
|
||||
{
|
||||
mCatDescendentDeltas[item->getParentUUID()]--;
|
||||
mObjectsDeletedIds.insert(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Inventory") << "removed item not found " << *it << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// parse _broken_links_removed -> mObjectsDeletedIds
|
||||
uuid_list_t broken_link_ids;
|
||||
parseUUIDArray(update,"_broken_links_removed",broken_link_ids);
|
||||
for (uuid_list_t::const_iterator it = broken_link_ids.begin();
|
||||
it != broken_link_ids.end(); ++it)
|
||||
{
|
||||
LLViewerInventoryItem *item = gInventory.getItem(*it);
|
||||
if(item)
|
||||
{
|
||||
mCatDescendentDeltas[item->getParentUUID()]--;
|
||||
mObjectsDeletedIds.insert(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Inventory") << "broken link not found " << *it << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// parse _created_items
|
||||
parseUUIDArray(update,"_created_items",mItemIds);
|
||||
|
||||
// parse _created_categories
|
||||
parseUUIDArray(update,"_created_categories",mCategoryIds);
|
||||
|
||||
// Parse updated category versions.
|
||||
const std::string& ucv = "_updated_category_versions";
|
||||
if (update.has(ucv))
|
||||
{
|
||||
for(LLSD::map_const_iterator it = update[ucv].beginMap(),
|
||||
end = update[ucv].endMap();
|
||||
it != end; ++it)
|
||||
{
|
||||
const LLUUID id((*it).first);
|
||||
S32 version = (*it).second.asInteger();
|
||||
mCatVersionsUpdated[id] = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseContent(const LLSD& update)
|
||||
{
|
||||
if (update.has("linked_id"))
|
||||
{
|
||||
parseLink(update);
|
||||
}
|
||||
else if (update.has("item_id"))
|
||||
{
|
||||
parseItem(update);
|
||||
}
|
||||
|
||||
if (update.has("category_id"))
|
||||
{
|
||||
parseCategory(update);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (update.has("_embedded"))
|
||||
{
|
||||
parseEmbedded(update["_embedded"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseItem(const LLSD& item_map)
|
||||
{
|
||||
LLUUID item_id = item_map["item_id"].asUUID();
|
||||
LLPointer<LLViewerInventoryItem> new_item(new LLViewerInventoryItem);
|
||||
LLViewerInventoryItem *curr_item = gInventory.getItem(item_id);
|
||||
if (curr_item)
|
||||
{
|
||||
// Default to current values where not provided.
|
||||
new_item->copyViewerItem(curr_item);
|
||||
}
|
||||
BOOL rv = new_item->unpackMessage(item_map);
|
||||
if (rv)
|
||||
{
|
||||
if (curr_item)
|
||||
{
|
||||
mItemsUpdated[item_id] = new_item;
|
||||
// This statement is here to cause a new entry with 0
|
||||
// delta to be created if it does not already exist;
|
||||
// otherwise has no effect.
|
||||
mCatDescendentDeltas[new_item->getParentUUID()];
|
||||
}
|
||||
else
|
||||
{
|
||||
mItemsCreated[item_id] = new_item;
|
||||
mCatDescendentDeltas[new_item->getParentUUID()]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// *TODO: Wow, harsh. Should we just complain and get out?
|
||||
LL_ERRS() << "unpack failed" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseLink(const LLSD& link_map)
|
||||
{
|
||||
LLUUID item_id = link_map["item_id"].asUUID();
|
||||
LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem);
|
||||
LLViewerInventoryItem *curr_link = gInventory.getItem(item_id);
|
||||
if (curr_link)
|
||||
{
|
||||
// Default to current values where not provided.
|
||||
new_link->copyViewerItem(curr_link);
|
||||
}
|
||||
BOOL rv = new_link->unpackMessage(link_map);
|
||||
if (rv)
|
||||
{
|
||||
const LLUUID& parent_id = new_link->getParentUUID();
|
||||
if (curr_link)
|
||||
{
|
||||
mItemsUpdated[item_id] = new_link;
|
||||
// This statement is here to cause a new entry with 0
|
||||
// delta to be created if it does not already exist;
|
||||
// otherwise has no effect.
|
||||
mCatDescendentDeltas[parent_id];
|
||||
}
|
||||
else
|
||||
{
|
||||
LLPermissions default_perms;
|
||||
default_perms.init(gAgent.getID(),gAgent.getID(),LLUUID::null,LLUUID::null);
|
||||
default_perms.initMasks(PERM_NONE,PERM_NONE,PERM_NONE,PERM_NONE,PERM_NONE);
|
||||
new_link->setPermissions(default_perms);
|
||||
LLSaleInfo default_sale_info;
|
||||
new_link->setSaleInfo(default_sale_info);
|
||||
//LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL;
|
||||
mItemsCreated[item_id] = new_link;
|
||||
mCatDescendentDeltas[parent_id]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// *TODO: Wow, harsh. Should we just complain and get out?
|
||||
LL_ERRS() << "unpack failed" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AISUpdate::parseCategory(const LLSD& category_map)
|
||||
{
|
||||
LLUUID category_id = category_map["category_id"].asUUID();
|
||||
|
||||
// Check descendent count first, as it may be needed
|
||||
// to populate newly created categories
|
||||
if (category_map.has("_embedded"))
|
||||
{
|
||||
parseDescendentCount(category_id, category_map["_embedded"]);
|
||||
}
|
||||
|
||||
LLPointer<LLViewerInventoryCategory> new_cat(new LLViewerInventoryCategory(category_id));
|
||||
LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id);
|
||||
if (curr_cat)
|
||||
{
|
||||
// Default to current values where not provided.
|
||||
new_cat->copyViewerCategory(curr_cat);
|
||||
}
|
||||
BOOL rv = new_cat->unpackMessage(category_map);
|
||||
// *NOTE: unpackMessage does not unpack version or descendent count.
|
||||
//if (category_map.has("version"))
|
||||
//{
|
||||
// mCatVersionsUpdated[category_id] = category_map["version"].asInteger();
|
||||
//}
|
||||
if (rv)
|
||||
{
|
||||
if (curr_cat)
|
||||
{
|
||||
mCategoriesUpdated[category_id] = new_cat;
|
||||
// This statement is here to cause a new entry with 0
|
||||
// delta to be created if it does not already exist;
|
||||
// otherwise has no effect.
|
||||
mCatDescendentDeltas[new_cat->getParentUUID()];
|
||||
// Capture update for the category itself as well.
|
||||
mCatDescendentDeltas[category_id];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set version/descendents for newly created categories.
|
||||
if (category_map.has("version"))
|
||||
{
|
||||
S32 version = category_map["version"].asInteger();
|
||||
LL_DEBUGS("Inventory") << "Setting version to " << version
|
||||
<< " for new category " << category_id << LL_ENDL;
|
||||
new_cat->setVersion(version);
|
||||
}
|
||||
uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id);
|
||||
if (mCatDescendentsKnown.end() != lookup_it)
|
||||
{
|
||||
S32 descendent_count = lookup_it->second;
|
||||
LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count
|
||||
<< " for new category " << category_id << LL_ENDL;
|
||||
new_cat->setDescendentCount(descendent_count);
|
||||
}
|
||||
mCategoriesCreated[category_id] = new_cat;
|
||||
mCatDescendentDeltas[new_cat->getParentUUID()]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// *TODO: Wow, harsh. Should we just complain and get out?
|
||||
LL_ERRS() << "unpack failed" << LL_ENDL;
|
||||
}
|
||||
|
||||
// Check for more embedded content.
|
||||
if (category_map.has("_embedded"))
|
||||
{
|
||||
parseEmbedded(category_map["_embedded"]);
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded)
|
||||
{
|
||||
// We can only determine true descendent count if this contains all descendent types.
|
||||
if (embedded.has("categories") &&
|
||||
embedded.has("links") &&
|
||||
embedded.has("items"))
|
||||
{
|
||||
mCatDescendentsKnown[category_id] = embedded["categories"].size();
|
||||
mCatDescendentsKnown[category_id] += embedded["links"].size();
|
||||
mCatDescendentsKnown[category_id] += embedded["items"].size();
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseEmbedded(const LLSD& embedded)
|
||||
{
|
||||
if (embedded.has("links")) // _embedded in a category
|
||||
{
|
||||
parseEmbeddedLinks(embedded["links"]);
|
||||
}
|
||||
if (embedded.has("items")) // _embedded in a category
|
||||
{
|
||||
parseEmbeddedItems(embedded["items"]);
|
||||
}
|
||||
if (embedded.has("item")) // _embedded in a link
|
||||
{
|
||||
parseEmbeddedItem(embedded["item"]);
|
||||
}
|
||||
if (embedded.has("categories")) // _embedded in a category
|
||||
{
|
||||
parseEmbeddedCategories(embedded["categories"]);
|
||||
}
|
||||
if (embedded.has("category")) // _embedded in a link
|
||||
{
|
||||
parseEmbeddedCategory(embedded["category"]);
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids)
|
||||
{
|
||||
if (content.has(name))
|
||||
{
|
||||
for(LLSD::array_const_iterator it = content[name].beginArray(),
|
||||
end = content[name].endArray();
|
||||
it != end; ++it)
|
||||
{
|
||||
ids.insert((*it).asUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseEmbeddedLinks(const LLSD& links)
|
||||
{
|
||||
for(LLSD::map_const_iterator linkit = links.beginMap(),
|
||||
linkend = links.endMap();
|
||||
linkit != linkend; ++linkit)
|
||||
{
|
||||
const LLUUID link_id((*linkit).first);
|
||||
const LLSD& link_map = (*linkit).second;
|
||||
if (mItemIds.end() == mItemIds.find(link_id))
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseLink(link_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseEmbeddedItem(const LLSD& item)
|
||||
{
|
||||
// a single item (_embedded in a link)
|
||||
if (item.has("item_id"))
|
||||
{
|
||||
if (mItemIds.end() != mItemIds.find(item["item_id"].asUUID()))
|
||||
{
|
||||
parseItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseEmbeddedItems(const LLSD& items)
|
||||
{
|
||||
// a map of items (_embedded in a category)
|
||||
for(LLSD::map_const_iterator itemit = items.beginMap(),
|
||||
itemend = items.endMap();
|
||||
itemit != itemend; ++itemit)
|
||||
{
|
||||
const LLUUID item_id((*itemit).first);
|
||||
const LLSD& item_map = (*itemit).second;
|
||||
if (mItemIds.end() == mItemIds.find(item_id))
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "Ignoring item not in items list " << item_id << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseItem(item_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseEmbeddedCategory(const LLSD& category)
|
||||
{
|
||||
// a single category (_embedded in a link)
|
||||
if (category.has("category_id"))
|
||||
{
|
||||
if (mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID()))
|
||||
{
|
||||
parseCategory(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::parseEmbeddedCategories(const LLSD& categories)
|
||||
{
|
||||
// a map of categories (_embedded in a category)
|
||||
for(LLSD::map_const_iterator categoryit = categories.beginMap(),
|
||||
categoryend = categories.endMap();
|
||||
categoryit != categoryend; ++categoryit)
|
||||
{
|
||||
const LLUUID category_id((*categoryit).first);
|
||||
const LLSD& category_map = (*categoryit).second;
|
||||
if (mCategoryIds.end() == mCategoryIds.find(category_id))
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "Ignoring category not in categories list " << category_id << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseCategory(category_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AISUpdate::doUpdate()
|
||||
{
|
||||
// Do version/descendent accounting.
|
||||
for (std::map<LLUUID,S32>::const_iterator catit = mCatDescendentDeltas.begin();
|
||||
catit != mCatDescendentDeltas.end(); ++catit)
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "descendent accounting for " << catit->first << LL_ENDL;
|
||||
|
||||
const LLUUID cat_id(catit->first);
|
||||
// Don't account for update if we just created this category.
|
||||
if (mCategoriesCreated.find(cat_id) != mCategoriesCreated.end())
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "Skipping version increment for new category " << cat_id << LL_ENDL;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't account for update unless AIS told us it updated that category.
|
||||
if (mCatVersionsUpdated.find(cat_id) == mCatVersionsUpdated.end())
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "Skipping version increment for non-updated category " << cat_id << LL_ENDL;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have a known descendent count, set that now.
|
||||
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
|
||||
if (cat)
|
||||
{
|
||||
S32 descendent_delta = catit->second;
|
||||
S32 old_count = cat->getDescendentCount();
|
||||
LL_DEBUGS("Inventory") << "Updating descendent count for "
|
||||
<< cat->getName() << " " << cat_id
|
||||
<< " with delta " << descendent_delta << " from "
|
||||
<< old_count << " to " << (old_count+descendent_delta) << LL_ENDL;
|
||||
LLInventoryModel::LLCategoryUpdate up(cat_id, descendent_delta);
|
||||
gInventory.accountForUpdate(up);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("Inventory") << "Skipping version accounting for unknown category " << cat_id << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// CREATE CATEGORIES
|
||||
for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin();
|
||||
create_it != mCategoriesCreated.end(); ++create_it)
|
||||
{
|
||||
LLUUID category_id(create_it->first);
|
||||
LLPointer<LLViewerInventoryCategory> new_category = create_it->second;
|
||||
|
||||
gInventory.updateCategory(new_category, LLInventoryObserver::CREATE);
|
||||
LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL;
|
||||
}
|
||||
|
||||
// UPDATE CATEGORIES
|
||||
for (deferred_category_map_t::const_iterator update_it = mCategoriesUpdated.begin();
|
||||
update_it != mCategoriesUpdated.end(); ++update_it)
|
||||
{
|
||||
LLUUID category_id(update_it->first);
|
||||
LLPointer<LLViewerInventoryCategory> new_category = update_it->second;
|
||||
// Since this is a copy of the category *before* the accounting update, above,
|
||||
// we need to transfer back the updated version/descendent count.
|
||||
LLViewerInventoryCategory* curr_cat = gInventory.getCategory(new_category->getUUID());
|
||||
if (!curr_cat)
|
||||
{
|
||||
LL_WARNS("Inventory") << "Failed to update unknown category " << new_category->getUUID() << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_category->setVersion(curr_cat->getVersion());
|
||||
new_category->setDescendentCount(curr_cat->getDescendentCount());
|
||||
gInventory.updateCategory(new_category);
|
||||
LL_DEBUGS("Inventory") << "updated category " << new_category->getName() << " " << category_id << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// CREATE ITEMS
|
||||
for (deferred_item_map_t::const_iterator create_it = mItemsCreated.begin();
|
||||
create_it != mItemsCreated.end(); ++create_it)
|
||||
{
|
||||
LLUUID item_id(create_it->first);
|
||||
LLPointer<LLViewerInventoryItem> new_item = create_it->second;
|
||||
|
||||
// FIXME risky function since it calls updateServer() in some
|
||||
// cases. Maybe break out the update/create cases, in which
|
||||
// case this is create.
|
||||
LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL;
|
||||
gInventory.updateItem(new_item, LLInventoryObserver::CREATE);
|
||||
}
|
||||
|
||||
// UPDATE ITEMS
|
||||
for (deferred_item_map_t::const_iterator update_it = mItemsUpdated.begin();
|
||||
update_it != mItemsUpdated.end(); ++update_it)
|
||||
{
|
||||
LLUUID item_id(update_it->first);
|
||||
LLPointer<LLViewerInventoryItem> new_item = update_it->second;
|
||||
// FIXME risky function since it calls updateServer() in some
|
||||
// cases. Maybe break out the update/create cases, in which
|
||||
// case this is update.
|
||||
LL_DEBUGS("Inventory") << "updated item " << item_id << LL_ENDL;
|
||||
//LL_DEBUGS("Inventory") << ll_pretty_print_sd(new_item->asLLSD()) << LL_ENDL;
|
||||
gInventory.updateItem(new_item);
|
||||
}
|
||||
|
||||
// DELETE OBJECTS
|
||||
for (uuid_list_t::const_iterator del_it = mObjectsDeletedIds.begin();
|
||||
del_it != mObjectsDeletedIds.end(); ++del_it)
|
||||
{
|
||||
LL_INFOS("Inventory") << "deleted item " << *del_it << LL_ENDL;
|
||||
gInventory.onObjectDeletedFromServer(*del_it, false, false, false);
|
||||
}
|
||||
|
||||
// TODO - how can we use this version info? Need to be sure all
|
||||
// changes are going through AIS first, or at least through
|
||||
// something with a reliable responder.
|
||||
for (uuid_int_map_t::iterator ucv_it = mCatVersionsUpdated.begin();
|
||||
ucv_it != mCatVersionsUpdated.end(); ++ucv_it)
|
||||
{
|
||||
const LLUUID id = ucv_it->first;
|
||||
S32 version = ucv_it->second;
|
||||
LLViewerInventoryCategory *cat = gInventory.getCategory(id);
|
||||
LL_DEBUGS("Inventory") << "cat version update " << cat->getName() << " to version " << cat->getVersion() << LL_ENDL;
|
||||
if (cat->getVersion() != version)
|
||||
{
|
||||
LL_WARNS() << "Possible version mismatch for category " << cat->getName()
|
||||
<< ", viewer version " << cat->getVersion()
|
||||
<< " server version " << version << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
gInventory.notifyObservers();
|
||||
}
|
||||
|
||||
194
indra/newview/llaisapi.h
Normal file
194
indra/newview/llaisapi.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @file llaisapi.h
|
||||
* @brief classes and functions for interfacing with the v3+ ais inventory service.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2013, 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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLAISAPI_H
|
||||
#define LL_LLAISAPI_H
|
||||
|
||||
#include "lluuid.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "llcurl.h"
|
||||
#include "llhttpclient.h"
|
||||
#include "llhttpretrypolicy.h"
|
||||
#include "llviewerinventory.h"
|
||||
|
||||
class AISCommand: public LLHTTPClient::ResponderWithResult
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void()> command_func_type;
|
||||
|
||||
AISCommand(LLPointer<LLInventoryCallback> callback);
|
||||
|
||||
virtual ~AISCommand() {}
|
||||
|
||||
bool run_command();
|
||||
|
||||
void setCommandFunc(command_func_type command_func);
|
||||
|
||||
// Need to do command-specific parsing to get an id here, for
|
||||
// LLInventoryCallback::fire(). May or may not need to bother,
|
||||
// since most LLInventoryCallbacks do their work in the
|
||||
// destructor.
|
||||
|
||||
/* virtual */ void httpSuccess();
|
||||
/* virtual */ void httpFailure();
|
||||
|
||||
static bool isAPIAvailable();
|
||||
static bool getInvCap(std::string& cap);
|
||||
static bool getLibCap(std::string& cap);
|
||||
static void getCapabilityNames(LLSD& capabilityNames);
|
||||
|
||||
protected:
|
||||
virtual bool getResponseUUID(const LLSD& content, LLUUID& id);
|
||||
|
||||
private:
|
||||
command_func_type mCommandFunc;
|
||||
LLPointer<LLHTTPRetryPolicy> mRetryPolicy;
|
||||
LLPointer<LLInventoryCallback> mCallback;
|
||||
};
|
||||
|
||||
class RemoveItemCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
RemoveItemCommand(const LLUUID& item_id,
|
||||
LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "RemoveItemCommand"; }
|
||||
};
|
||||
|
||||
class RemoveCategoryCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
RemoveCategoryCommand(const LLUUID& item_id,
|
||||
LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "RemoveCategoryCommand"; }
|
||||
};
|
||||
|
||||
class PurgeDescendentsCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
PurgeDescendentsCommand(const LLUUID& item_id,
|
||||
LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "PurgeDescendentsCommand"; }
|
||||
|
||||
};
|
||||
|
||||
class UpdateItemCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
UpdateItemCommand(const LLUUID& item_id,
|
||||
const LLSD& updates,
|
||||
LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "UpdateItemCommand"; }
|
||||
|
||||
private:
|
||||
LLSD mUpdates;
|
||||
};
|
||||
|
||||
class UpdateCategoryCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
UpdateCategoryCommand(const LLUUID& cat_id,
|
||||
const LLSD& updates,
|
||||
LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "UpdateCategoryCommand"; }
|
||||
|
||||
private:
|
||||
LLSD mUpdates;
|
||||
};
|
||||
|
||||
class SlamFolderCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "SlamFolderCommand"; }
|
||||
|
||||
private:
|
||||
LLSD mContents;
|
||||
};
|
||||
|
||||
class CopyLibraryCategoryCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
CopyLibraryCategoryCommand(const LLUUID& source_id, const LLUUID& dest_id, LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "CopyLibraryCategoryCommand"; }
|
||||
|
||||
protected:
|
||||
/* virtual */ bool getResponseUUID(const LLSD& content, LLUUID& id);
|
||||
};
|
||||
|
||||
class CreateInventoryCommand: public AISCommand
|
||||
{
|
||||
public:
|
||||
CreateInventoryCommand(const LLUUID& parent_id, const LLSD& new_inventory, LLPointer<LLInventoryCallback> callback);
|
||||
/* virtual */ const char* getName() const { return "CreateInventoryCommand"; }
|
||||
|
||||
private:
|
||||
LLSD mNewInventory;
|
||||
};
|
||||
|
||||
class AISUpdate
|
||||
{
|
||||
public:
|
||||
AISUpdate(const LLSD& update);
|
||||
void parseUpdate(const LLSD& update);
|
||||
void parseMeta(const LLSD& update);
|
||||
void parseContent(const LLSD& update);
|
||||
void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
|
||||
void parseLink(const LLSD& link_map);
|
||||
void parseItem(const LLSD& link_map);
|
||||
void parseCategory(const LLSD& link_map);
|
||||
void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded);
|
||||
void parseEmbedded(const LLSD& embedded);
|
||||
void parseEmbeddedLinks(const LLSD& links);
|
||||
void parseEmbeddedItems(const LLSD& items);
|
||||
void parseEmbeddedCategories(const LLSD& categories);
|
||||
void parseEmbeddedItem(const LLSD& item);
|
||||
void parseEmbeddedCategory(const LLSD& category);
|
||||
void doUpdate();
|
||||
private:
|
||||
void clearParseResults();
|
||||
|
||||
typedef std::map<LLUUID,S32> uuid_int_map_t;
|
||||
uuid_int_map_t mCatDescendentDeltas;
|
||||
uuid_int_map_t mCatDescendentsKnown;
|
||||
uuid_int_map_t mCatVersionsUpdated;
|
||||
|
||||
typedef std::map<LLUUID,LLPointer<LLViewerInventoryItem> > deferred_item_map_t;
|
||||
deferred_item_map_t mItemsCreated;
|
||||
deferred_item_map_t mItemsUpdated;
|
||||
typedef std::map<LLUUID,LLPointer<LLViewerInventoryCategory> > deferred_category_map_t;
|
||||
deferred_category_map_t mCategoriesCreated;
|
||||
deferred_category_map_t mCategoriesUpdated;
|
||||
|
||||
// These keep track of uuid's mentioned in meta values.
|
||||
// Useful for filtering out which content we are interested in.
|
||||
uuid_list_t mObjectsDeletedIds;
|
||||
uuid_list_t mItemIds;
|
||||
uuid_list_t mCategoryIds;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "llwearablelist.h"
|
||||
#include "llsdutil.h"
|
||||
#include "llhttpretrypolicy.h"
|
||||
#include "llaisapi.h"
|
||||
#include "llinventorypanel.h"
|
||||
#include "llfloatercustomize.h"
|
||||
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
|
||||
@@ -2791,6 +2792,25 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
|
||||
LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName()
|
||||
<< " )" << LL_ENDL;
|
||||
|
||||
// If we are copying from library, attempt to use AIS to copy the category.
|
||||
bool ais_ran=false;
|
||||
if (copy && AISCommand::isAPIAvailable())
|
||||
{
|
||||
LLUUID parent_id;
|
||||
parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
|
||||
if (parent_id.isNull())
|
||||
{
|
||||
parent_id = gInventory.getRootFolderID();
|
||||
}
|
||||
|
||||
LLPointer<LLInventoryCallback> copy_cb = new LLWearCategoryAfterCopy(append);
|
||||
LLPointer<LLInventoryCallback> track_cb = new LLTrackPhaseWrapper(
|
||||
std::string("wear_inventory_category_callback"), copy_cb);
|
||||
boost::intrusive_ptr <AISCommand> cmd_ptr = new CopyLibraryCategoryCommand(category->getUUID(), parent_id, track_cb);
|
||||
ais_ran=cmd_ptr->run_command();
|
||||
}
|
||||
|
||||
if (!ais_ran)
|
||||
{
|
||||
selfStartPhase("wear_inventory_category_fetch");
|
||||
callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
|
||||
|
||||
@@ -5951,7 +5951,7 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
|
||||
{
|
||||
disabled_items.push_back(std::string("Wearable Add"));
|
||||
LLViewerWearable* wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID());
|
||||
if (cof_pending || (wearable && wearable != gAgentWearables.getTopWearable(mWearableType)))
|
||||
if ((wearable && wearable != gAgentWearables.getTopWearable(mWearableType)))
|
||||
disabled_items.push_back(std::string("Wearable And Object Wear"));
|
||||
}
|
||||
// [RLVa:KB] - Checked: 2010-06-09 (RLVa-1.2.0g) | Modified: RLVa-1.2.0g
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
#include "llinventorymodel.h"
|
||||
|
||||
#include "llaisapi.h"
|
||||
#include "llagent.h"
|
||||
#include "llagentwearables.h"
|
||||
#include "llappearancemgr.h"
|
||||
@@ -1292,6 +1293,19 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat,
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update)
|
||||
{
|
||||
LLTimer timer;
|
||||
if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
|
||||
{
|
||||
dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
|
||||
}
|
||||
|
||||
AISUpdate ais_update(update); // parse update llsd into stuff to do.
|
||||
ais_update.doUpdate(); // execute the updates in the appropriate order.
|
||||
LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL;
|
||||
}
|
||||
|
||||
// Does not appear to be used currently.
|
||||
void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version)
|
||||
{
|
||||
|
||||
@@ -367,6 +367,10 @@ public:
|
||||
// Delete
|
||||
//--------------------------------------------------------------------
|
||||
public:
|
||||
|
||||
// Update model after an AISv3 update received for any operation.
|
||||
void onAISUpdateReceived(const std::string& context, const LLSD& update);
|
||||
|
||||
// Update model after an item is confirmed as removed from
|
||||
// server. Works for categories or items.
|
||||
void onObjectDeletedFromServer(const LLUUID& item_id,
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "llsdserialize.h"
|
||||
#include "message.h"
|
||||
|
||||
#include "llaisapi.h"
|
||||
#include "llagent.h"
|
||||
#include "llagentcamera.h"
|
||||
#include "llagentwearables.h"
|
||||
@@ -1224,6 +1225,17 @@ void link_inventory_array(const LLUUID& category,
|
||||
<< " UUID:" << category << " ] " << LL_ENDL;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ais_ran = false;
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
LLSD new_inventory = LLSD::emptyMap();
|
||||
new_inventory["links"] = links;
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new CreateInventoryCommand(category, new_inventory, cb);
|
||||
ais_ran = cmd_ptr->run_command();
|
||||
}
|
||||
|
||||
if (!ais_ran)
|
||||
{
|
||||
LLMessageSystem* msg = gMessageSystem;
|
||||
for (LLSD::array_iterator iter = links.beginArray(); iter != links.endArray(); ++iter )
|
||||
@@ -1280,6 +1292,25 @@ void update_inventory_item(
|
||||
LLPointer<LLInventoryCallback> cb)
|
||||
{
|
||||
const LLUUID& item_id = update_item->getUUID();
|
||||
bool ais_ran = false;
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
LLSD updates = update_item->asLLSD();
|
||||
// Replace asset_id and/or shadow_id with transaction_id (hash_id)
|
||||
if (updates.has("asset_id"))
|
||||
{
|
||||
updates.erase("asset_id");
|
||||
updates["hash_id"] = update_item->getTransactionID();
|
||||
}
|
||||
if (updates.has("shadow_id"))
|
||||
{
|
||||
updates.erase("shadow_id");
|
||||
updates["hash_id"] = update_item->getTransactionID();
|
||||
}
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new UpdateItemCommand(item_id, updates, cb);
|
||||
ais_ran = cmd_ptr->run_command();
|
||||
}
|
||||
if (!ais_ran)
|
||||
{
|
||||
LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id);
|
||||
LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (update_item ? update_item->getName() : "(NOT FOUND)") << LL_ENDL;
|
||||
@@ -1331,6 +1362,13 @@ void update_inventory_item(
|
||||
}
|
||||
// [/SL:KB]
|
||||
|
||||
bool ais_ran = false;
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new UpdateItemCommand(item_id, updates, cb);
|
||||
ais_ran = cmd_ptr->run_command();
|
||||
}
|
||||
if (!ais_ran)
|
||||
{
|
||||
// LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id);
|
||||
// LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL;
|
||||
@@ -1383,6 +1421,14 @@ void update_inventory_category(
|
||||
|
||||
LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj);
|
||||
new_cat->fromLLSD(updates);
|
||||
// FIXME - restore this once the back-end work has been done.
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
LLSD new_llsd = new_cat->asLLSD();
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new UpdateCategoryCommand(cat_id, new_llsd, cb);
|
||||
cmd_ptr->run_command();
|
||||
}
|
||||
else // no cap
|
||||
{
|
||||
LLMessageSystem* msg = gMessageSystem;
|
||||
msg->newMessageFast(_PREHASH_UpdateInventoryFolder);
|
||||
@@ -1442,6 +1488,17 @@ void remove_inventory_item(
|
||||
{
|
||||
const LLUUID item_id(obj->getUUID());
|
||||
LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << obj->getName() << LL_ENDL;
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new RemoveItemCommand(item_id, cb);
|
||||
cmd_ptr->run_command();
|
||||
|
||||
if (immediate_delete)
|
||||
{
|
||||
gInventory.onObjectDeletedFromServer(item_id);
|
||||
}
|
||||
}
|
||||
else // no cap
|
||||
{
|
||||
LLMessageSystem* msg = gMessageSystem;
|
||||
msg->newMessageFast(_PREHASH_RemoveInventoryItem);
|
||||
@@ -1507,6 +1564,12 @@ void remove_inventory_category(
|
||||
LLNotificationsUtil::add("CannotRemoveProtectedCategories");
|
||||
return;
|
||||
}
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new RemoveCategoryCommand(cat_id, cb);
|
||||
cmd_ptr->run_command();
|
||||
}
|
||||
else // no cap
|
||||
{
|
||||
// RemoveInventoryFolder does not remove children, so must
|
||||
// clear descendents first.
|
||||
@@ -1604,6 +1667,12 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new PurgeDescendentsCommand(id, cb);
|
||||
cmd_ptr->run_command();
|
||||
}
|
||||
else // no cap
|
||||
{
|
||||
// Fast purge
|
||||
LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL;
|
||||
@@ -1814,6 +1883,14 @@ void slam_inventory_folder(const LLUUID& folder_id,
|
||||
const LLSD& contents,
|
||||
LLPointer<LLInventoryCallback> cb)
|
||||
{
|
||||
if (AISCommand::isAPIAvailable())
|
||||
{
|
||||
LL_DEBUGS(LOG_INV) << "using AISv3 to slam folder, id " << folder_id
|
||||
<< " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL;
|
||||
boost::intrusive_ptr<AISCommand> cmd_ptr = new SlamFolderCommand(folder_id, contents, cb);
|
||||
cmd_ptr->run_command();
|
||||
}
|
||||
else // no cap
|
||||
{
|
||||
// [RLVa:KB] - Checked: 2014-11-02 (RLVa-1.4.11)
|
||||
LL_DEBUGS(LOG_INV) << "using item-by-item calls to slam folder, id " << folder_id
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
// linden libraries
|
||||
#include "indra_constants.h"
|
||||
#include "llaisapi.h"
|
||||
#include "llavatarnamecache.h" // name lookup cap url
|
||||
//#include "llfloaterreg.h"
|
||||
#include "llmath.h"
|
||||
@@ -89,8 +90,8 @@
|
||||
|
||||
class AIHTTPTimeoutPolicy;
|
||||
extern AIHTTPTimeoutPolicy baseCapabilitiesComplete_timeout;
|
||||
extern AIHTTPTimeoutPolicy gamingDataReceived_timeout;
|
||||
extern AIHTTPTimeoutPolicy simulatorFeaturesReceived_timeout;
|
||||
extern AIHTTPTimeoutPolicy baseCapabilitiesCompleteTracker_timeout;
|
||||
extern AIHTTPTimeoutPolicy baseFeaturesReceived_timeout;
|
||||
|
||||
const F32 WATER_TEXTURE_SCALE = 8.f; // Number of times to repeat the water texture across a region
|
||||
const S16 MAX_MAP_DIST = 10;
|
||||
@@ -103,6 +104,8 @@ const S32 MAX_CAP_REQUEST_ATTEMPTS = 30;
|
||||
|
||||
typedef std::map<std::string, std::string> CapabilityMap;
|
||||
|
||||
static void log_capabilities(const CapabilityMap &capmap);
|
||||
|
||||
class LLViewerRegionImpl {
|
||||
public:
|
||||
LLViewerRegionImpl(LLViewerRegion * region, LLHost const & host)
|
||||
@@ -158,6 +161,7 @@ public:
|
||||
LLUUID mCacheID;
|
||||
|
||||
CapabilityMap mCapabilities;
|
||||
CapabilityMap mSecondCapabilitiesTracker;
|
||||
|
||||
LLEventPoll* mEventPoll;
|
||||
|
||||
@@ -219,15 +223,25 @@ class BaseCapabilitiesComplete : public LLHTTPClient::ResponderWithResult
|
||||
{
|
||||
LOG_CLASS(BaseCapabilitiesComplete);
|
||||
public:
|
||||
BaseCapabilitiesComplete(U64 region_handle, S32 id)
|
||||
BaseCapabilitiesComplete(U64 region_handle, S32 id)
|
||||
: mRegionHandle(region_handle), mID(id)
|
||||
{ }
|
||||
{ }
|
||||
virtual ~BaseCapabilitiesComplete()
|
||||
{ }
|
||||
|
||||
static boost::intrusive_ptr<BaseCapabilitiesComplete> build( U64 region_handle, S32 id )
|
||||
{
|
||||
return boost::intrusive_ptr<BaseCapabilitiesComplete>(
|
||||
new BaseCapabilitiesComplete(region_handle, id) );
|
||||
}
|
||||
|
||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return baseCapabilitiesComplete_timeout; }
|
||||
/*virtual*/ char const* getName(void) const { return "BaseCapabilitiesComplete"; }
|
||||
|
||||
private:
|
||||
void httpFailure(void)
|
||||
{
|
||||
LL_WARNS("AppInit", "Capabilities") << mStatus << ": " << mReason << LL_ENDL;
|
||||
LL_WARNS("AppInit", "Capabilities") << dumpResponse() << LL_ENDL;
|
||||
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
|
||||
if (regionp)
|
||||
{
|
||||
@@ -246,15 +260,23 @@ public:
|
||||
if( mID != regionp->getHttpResponderID() ) // region is no longer referring to this responder
|
||||
{
|
||||
LL_WARNS("AppInit", "Capabilities") << "Received results for a stale http responder!" << LL_ENDL;
|
||||
regionp->failedSeedCapability();
|
||||
return ;
|
||||
}
|
||||
|
||||
const LLSD& content = getContent();
|
||||
if (!content.isMap())
|
||||
{
|
||||
failureResult(400, "Malformed response contents", content);
|
||||
return;
|
||||
}
|
||||
LLSD::map_const_iterator iter;
|
||||
for(iter = mContent.beginMap(); iter != mContent.endMap(); ++iter)
|
||||
for(iter = content.beginMap(); iter != content.endMap(); ++iter)
|
||||
{
|
||||
regionp->setCapability(iter->first, iter->second);
|
||||
LL_DEBUGS("AppInit", "Capabilities") << "got capability for "
|
||||
<< iter->first << LL_ENDL;
|
||||
|
||||
LL_DEBUGS("AppInit", "Capabilities")
|
||||
<< "Capability '" << iter->first << "' is '" << iter->second << "'" << LL_ENDL;
|
||||
|
||||
/* HACK we're waiting for the ServerReleaseNotes */
|
||||
if (iter->first == "ServerReleaseNotes" && regionp->getReleaseNotesRequested())
|
||||
@@ -271,20 +293,103 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return baseCapabilitiesComplete_timeout; }
|
||||
/*virtual*/ char const* getName(void) const { return "BaseCapabilitiesComplete"; }
|
||||
|
||||
static boost::intrusive_ptr<BaseCapabilitiesComplete> build( U64 region_handle, S32 id )
|
||||
{
|
||||
return boost::intrusive_ptr<BaseCapabilitiesComplete>(
|
||||
new BaseCapabilitiesComplete(region_handle, id) );
|
||||
}
|
||||
|
||||
private:
|
||||
U64 mRegionHandle;
|
||||
S32 mID;
|
||||
};
|
||||
|
||||
class BaseCapabilitiesCompleteTracker : public LLHTTPClient::ResponderWithResult
|
||||
{
|
||||
LOG_CLASS(BaseCapabilitiesCompleteTracker);
|
||||
public:
|
||||
BaseCapabilitiesCompleteTracker( U64 region_handle)
|
||||
: mRegionHandle(region_handle)
|
||||
{ }
|
||||
|
||||
virtual ~BaseCapabilitiesCompleteTracker()
|
||||
{ }
|
||||
|
||||
static boost::intrusive_ptr<BaseCapabilitiesCompleteTracker> build( U64 region_handle )
|
||||
{
|
||||
return boost::intrusive_ptr<BaseCapabilitiesCompleteTracker>(
|
||||
new BaseCapabilitiesCompleteTracker(region_handle));
|
||||
}
|
||||
|
||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return baseCapabilitiesCompleteTracker_timeout; }
|
||||
/*virtual*/ char const* getName(void) const { return "BaseCapabilitiesCompleteTracker"; }
|
||||
|
||||
private:
|
||||
/* virtual */ void httpFailure()
|
||||
{
|
||||
LL_WARNS() << dumpResponse() << LL_ENDL;
|
||||
}
|
||||
|
||||
/* virtual */ void httpSuccess()
|
||||
{
|
||||
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
|
||||
if( !regionp )
|
||||
{
|
||||
LL_WARNS("AppInit", "Capabilities") << "Received results for region that no longer exists!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
|
||||
const LLSD& content = getContent();
|
||||
if (!content.isMap())
|
||||
{
|
||||
failureResult(400, "Malformed response contents", content);
|
||||
return;
|
||||
}
|
||||
LLSD::map_const_iterator iter;
|
||||
for(iter = content.beginMap(); iter != content.endMap(); ++iter)
|
||||
{
|
||||
regionp->setCapabilityDebug(iter->first, iter->second);
|
||||
//LL_INFOS()<<"BaseCapabilitiesCompleteTracker New Caps "<<iter->first<<" "<< iter->second<<LL_ENDL;
|
||||
}
|
||||
|
||||
if ( regionp->getRegionImpl()->mCapabilities.size() != regionp->getRegionImpl()->mSecondCapabilitiesTracker.size() )
|
||||
{
|
||||
LL_WARNS("AppInit", "Capabilities")
|
||||
<< "Sim sent duplicate base caps that differ in size from what we initially received - most likely content. "
|
||||
<< "mCapabilities == " << regionp->getRegionImpl()->mCapabilities.size()
|
||||
<< " mSecondCapabilitiesTracker == " << regionp->getRegionImpl()->mSecondCapabilitiesTracker.size()
|
||||
<< LL_ENDL;
|
||||
//#ifdef DEBUG_CAPS_GRANTS
|
||||
LL_WARNS("AppInit", "Capabilities")
|
||||
<< "Initial Base capabilities: " << LL_ENDL;
|
||||
|
||||
log_capabilities(regionp->getRegionImpl()->mCapabilities);
|
||||
|
||||
LL_WARNS("AppInit", "Capabilities")
|
||||
<< "Latest base capabilities: " << LL_ENDL;
|
||||
|
||||
log_capabilities(regionp->getRegionImpl()->mSecondCapabilitiesTracker);
|
||||
|
||||
//#endif
|
||||
|
||||
if (regionp->getRegionImpl()->mSecondCapabilitiesTracker.size() > regionp->getRegionImpl()->mCapabilities.size() )
|
||||
{
|
||||
// *HACK Since we were granted more base capabilities in this grant request than the initial, replace
|
||||
// the old with the new. This shouldn't happen i.e. we should always get the same capabilities from a
|
||||
// sim. The simulator fix from SH-3895 should prevent it from happening, at least in the case of the
|
||||
// inventory api capability grants.
|
||||
|
||||
// Need to clear a std::map before copying into it because old keys take precedence.
|
||||
regionp->getRegionImplNC()->mCapabilities.clear();
|
||||
regionp->getRegionImplNC()->mCapabilities = regionp->getRegionImpl()->mSecondCapabilitiesTracker;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("CrossingCaps") << "Sim sent multiple base cap grants with matching sizes." << LL_ENDL;
|
||||
}
|
||||
regionp->getRegionImplNC()->mSecondCapabilitiesTracker.clear();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
U64 mRegionHandle;
|
||||
};
|
||||
|
||||
|
||||
LLViewerRegion::LLViewerRegion(const U64 &handle,
|
||||
const LLHost &host,
|
||||
@@ -1786,6 +1891,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
|
||||
capabilityNames.append("FetchInventoryDescendents2");
|
||||
capabilityNames.append("IncrementCOFVersion");
|
||||
capabilityNames.append("GamingData"); //Used by certain grids.
|
||||
AISCommand::getCapabilityNames(capabilityNames);
|
||||
capabilityNames.append("GetDisplayNames");
|
||||
capabilityNames.append("GetMesh");
|
||||
capabilityNames.append("GetMesh2"); // Used on SecondLife(tm) sim versions 280647 and higher (13.09.17).
|
||||
@@ -1851,8 +1957,16 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
|
||||
void LLViewerRegion::setSeedCapability(const std::string& url)
|
||||
{
|
||||
if (getCapability("Seed") == url)
|
||||
{
|
||||
// LL_WARNS() << "Ignoring duplicate seed capability" << LL_ENDL;
|
||||
{
|
||||
setCapabilityDebug("Seed", url);
|
||||
LL_DEBUGS("CrossingCaps") << "Received duplicate seed capability, posting to seed " <<
|
||||
url << LL_ENDL;
|
||||
|
||||
//Instead of just returning we build up a second set of seed caps and compare them
|
||||
//to the "original" seed cap received and determine why there is problem!
|
||||
LLSD capabilityNames = LLSD::emptyArray();
|
||||
mImpl->buildCapabilityNames( capabilityNames );
|
||||
LLHTTPClient::post( url, capabilityNames, BaseCapabilitiesCompleteTracker::build(getHandle() ));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1913,19 +2027,19 @@ void LLViewerRegion::failedSeedCapability()
|
||||
}
|
||||
}
|
||||
|
||||
class SimulatorFeaturesReceived : public LLHTTPClient::ResponderWithResult
|
||||
class BaseFeaturesReceived : public LLHTTPClient::ResponderWithResult
|
||||
{
|
||||
LOG_CLASS(SimulatorFeaturesReceived);
|
||||
LOG_CLASS(BaseFeaturesReceived);
|
||||
public:
|
||||
SimulatorFeaturesReceived(const std::string& retry_url, U64 region_handle,
|
||||
BaseFeaturesReceived(const std::string& retry_url, U64 region_handle, const char* classname, boost::function<void(LLViewerRegion*, const LLSD&)> fn,
|
||||
S32 attempt = 0, S32 max_attempts = MAX_CAP_REQUEST_ATTEMPTS)
|
||||
: mRetryURL(retry_url), mRegionHandle(region_handle), mAttempt(attempt), mMaxAttempts(max_attempts)
|
||||
: mRetryURL(retry_url), mRegionHandle(region_handle), mAttempt(attempt), mMaxAttempts(max_attempts), mClassName(classname), mFunction(fn)
|
||||
{ }
|
||||
|
||||
|
||||
void httpFailure(void)
|
||||
{
|
||||
LL_WARNS("AppInit", "SimulatorFeatures") << mStatus << ": " << mReason << LL_ENDL;
|
||||
LL_WARNS("AppInit", mClassName) << dumpResponse() << LL_ENDL;
|
||||
retry();
|
||||
}
|
||||
|
||||
@@ -1934,24 +2048,32 @@ public:
|
||||
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
|
||||
if(!regionp) //region is removed or responder is not created.
|
||||
{
|
||||
LL_WARNS("AppInit", "SimulatorFeatures") << "Received results for region that no longer exists!" << LL_ENDL;
|
||||
LL_WARNS("AppInit", mClassName)
|
||||
<< "Received results for region that no longer exists!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
|
||||
regionp->setSimulatorFeatures(mContent);
|
||||
const LLSD& content = getContent();
|
||||
if (!content.isMap())
|
||||
{
|
||||
failureResult(400, "Malformed response contents", content);
|
||||
return;
|
||||
}
|
||||
mFunction(regionp, content);
|
||||
}
|
||||
|
||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return simulatorFeaturesReceived_timeout; }
|
||||
/*virtual*/ char const* getName(void) const { return "SimulatorFeaturesReceived"; }
|
||||
|
||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return baseFeaturesReceived_timeout; }
|
||||
/*virtual*/ char const* getName(void) const { return mClassName; }
|
||||
|
||||
private:
|
||||
|
||||
void retry()
|
||||
{
|
||||
if (mAttempt < mMaxAttempts)
|
||||
{
|
||||
mAttempt++;
|
||||
LL_WARNS("AppInit", "SimulatorFeatures") << "Re-trying '" << mRetryURL << "'. Retry #" << mAttempt << LL_ENDL;
|
||||
LLHTTPClient::get(mRetryURL, new SimulatorFeaturesReceived(*this));
|
||||
LL_WARNS("AppInit", mClassName) << "Re-trying '" << mRetryURL << "'. Retry #" << mAttempt << LL_ENDL;
|
||||
LLHTTPClient::get(mRetryURL, new BaseFeaturesReceived(*this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1959,49 +2081,10 @@ private:
|
||||
U64 mRegionHandle;
|
||||
S32 mAttempt;
|
||||
S32 mMaxAttempts;
|
||||
const char* mClassName;
|
||||
boost::function<void(LLViewerRegion*, const LLSD&)> mFunction;
|
||||
};
|
||||
|
||||
class GamingDataReceived : public LLHTTPClient::ResponderWithResult
|
||||
{
|
||||
LOG_CLASS(GamingDataReceived);
|
||||
public:
|
||||
GamingDataReceived(const std::string& retry_url, U64 region_handle, S32 attempt = 0, S32 max_attempts = MAX_CAP_REQUEST_ATTEMPTS)
|
||||
: mRetryURL(retry_url), mRegionHandle(region_handle), mAttempt(attempt), mMaxAttempts(max_attempts)
|
||||
{}
|
||||
|
||||
/*virtual*/ void httpFailure(void)
|
||||
{
|
||||
LL_WARNS("AppInit", "GamingData") << mStatus << ": " << mReason << LL_ENDL;
|
||||
retry();
|
||||
}
|
||||
|
||||
/*virtual*/ void httpSuccess(void)
|
||||
{
|
||||
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
|
||||
if(regionp) regionp->setGamingData(mContent);
|
||||
}
|
||||
|
||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return gamingDataReceived_timeout; }
|
||||
/*virtual*/ char const* getName(void) const { return "GamingDataReceived"; }
|
||||
|
||||
private:
|
||||
void retry()
|
||||
{
|
||||
if (mAttempt < mMaxAttempts)
|
||||
{
|
||||
mAttempt++;
|
||||
LL_WARNS("AppInit", "GamingData") << "Retrying '" << mRetryURL << "'. Retry #" << mAttempt << LL_ENDL;
|
||||
LLHTTPClient::get(mRetryURL, new GamingDataReceived(*this));
|
||||
}
|
||||
}
|
||||
|
||||
std::string mRetryURL;
|
||||
U64 mRegionHandle;
|
||||
S32 mAttempt;
|
||||
S32 mMaxAttempts;
|
||||
};
|
||||
|
||||
|
||||
void LLViewerRegion::setCapability(const std::string& name, const std::string& url)
|
||||
{
|
||||
if(name == "EventQueueGet")
|
||||
@@ -2020,13 +2103,13 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u
|
||||
mImpl->mCapabilities[name] = url;
|
||||
|
||||
// kick off a request for simulator features
|
||||
LLHTTPClient::get(url, new SimulatorFeaturesReceived(url, getHandle()));
|
||||
LLHTTPClient::get(url, new BaseFeaturesReceived(url, getHandle(), "SimulatorFeaturesReceived", &LLViewerRegion::setSimulatorFeatures));
|
||||
}
|
||||
else if (name == "GamingData")
|
||||
{
|
||||
LLSD gamingRequest = LLSD::emptyMap();
|
||||
// request settings from simulator
|
||||
LLHTTPClient::post(url, gamingRequest, new GamingDataReceived(url, getHandle()));
|
||||
LLHTTPClient::post(url, gamingRequest, new BaseFeaturesReceived(url, getHandle(), "GamingDataReceived", &LLViewerRegion::setGamingData));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2038,6 +2121,20 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u
|
||||
}
|
||||
}
|
||||
|
||||
void LLViewerRegion::setCapabilityDebug(const std::string& name, const std::string& url)
|
||||
{
|
||||
// Continue to not add certain caps, as we do in setCapability. This is so they match up when we check them later.
|
||||
if ( ! ( name == "EventQueueGet" || name == "UntrustedSimulatorMessage" || name == "SimulatorFeatures" ) )
|
||||
{
|
||||
mImpl->mSecondCapabilitiesTracker[name] = url;
|
||||
if(name == "GetTexture")
|
||||
{
|
||||
mHttpUrl = url ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool LLViewerRegion::isSpecialCapabilityName(const std::string &name)
|
||||
{
|
||||
return name == "EventQueueGet" || name == "UntrustedSimulatorMessage";
|
||||
@@ -2047,7 +2144,7 @@ std::string LLViewerRegion::getCapability(const std::string& name) const
|
||||
{
|
||||
if (!capabilitiesReceived() && (name!=std::string("Seed")) && (name!=std::string("ObjectMedia")))
|
||||
{
|
||||
LL_WARNS() << "getCapability("<<name<<") called before caps received" << LL_ENDL;
|
||||
LL_WARNS() << "getCapability called before caps received for " << name << LL_ENDL;
|
||||
}
|
||||
|
||||
CapabilityMap::const_iterator iter = mImpl->mCapabilities.find(name);
|
||||
@@ -2059,6 +2156,22 @@ std::string LLViewerRegion::getCapability(const std::string& name) const
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
bool LLViewerRegion::isCapabilityAvailable(const std::string& name) const
|
||||
{
|
||||
if (!capabilitiesReceived() && (name!=std::string("Seed")) && (name!=std::string("ObjectMedia")))
|
||||
{
|
||||
LL_WARNS() << "isCapabilityAvailable called before caps received for " << name << LL_ENDL;
|
||||
}
|
||||
|
||||
CapabilityMap::const_iterator iter = mImpl->mCapabilities.find(name);
|
||||
if(iter == mImpl->mCapabilities.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLViewerRegion::capabilitiesReceived() const
|
||||
{
|
||||
return mCapabilitiesReceived;
|
||||
@@ -2210,7 +2323,21 @@ bool LLViewerRegion::avatarHoverHeightEnabled() const
|
||||
return ( mSimulatorFeatures.has("AvatarHoverHeightEnabled") &&
|
||||
mSimulatorFeatures["AvatarHoverHeightEnabled"].asBoolean());
|
||||
}
|
||||
/* Static Functions */
|
||||
|
||||
void log_capabilities(const CapabilityMap &capmap)
|
||||
{
|
||||
S32 count = 0;
|
||||
CapabilityMap::const_iterator iter;
|
||||
for (iter = capmap.begin(); iter != capmap.end(); ++iter, ++count)
|
||||
{
|
||||
if (!iter->second.empty())
|
||||
{
|
||||
LL_INFOS() << "log_capabilities: " << iter->first << " URL is " << iter->second << LL_ENDL;
|
||||
}
|
||||
}
|
||||
LL_INFOS() << "log_capabilities: Dumped " << count << " entries." << LL_ENDL;
|
||||
}
|
||||
void LLViewerRegion::resetMaterialsCapThrottle()
|
||||
{
|
||||
F32 requests_per_sec = 1.0f; // original default;
|
||||
|
||||
@@ -272,6 +272,8 @@ public:
|
||||
void failedSeedCapability();
|
||||
S32 getNumSeedCapRetries();
|
||||
void setCapability(const std::string& name, const std::string& url);
|
||||
void setCapabilityDebug(const std::string& name, const std::string& url);
|
||||
bool isCapabilityAvailable(const std::string& name) const;
|
||||
// implements LLCapabilityProvider
|
||||
virtual std::string getCapability(const std::string& name) const;
|
||||
|
||||
@@ -377,7 +379,9 @@ public:
|
||||
|
||||
void getNeighboringRegions( std::vector<LLViewerRegion*>& uniqueRegions );
|
||||
void getNeighboringRegionsStatus( std::vector<S32>& regions );
|
||||
|
||||
const LLViewerRegionImpl * getRegionImpl() const { return mImpl; }
|
||||
LLViewerRegionImpl * getRegionImplNC() { return mImpl; }
|
||||
|
||||
void setGamingData(const LLSD& info);
|
||||
const U32 getGamingFlags() const { return mGamingFlags; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user