Adding support for COPY methods to httpclient. Implementing viewer-side use of AISv3 COPY library folder operation. (SH-4304)

master
Don Kjer 2013-07-11 15:15:04 -07:00
parent d079f0dcdc
commit a85fa3b10a
13 changed files with 703 additions and 202 deletions

View File

@ -631,6 +631,19 @@ void LLHTTPClient::move(
request(url, HTTP_MOVE, NULL, responder, timeout, headers);
}
// static
void LLHTTPClient::copy(
const std::string& url,
const std::string& destination,
ResponderPtr responder,
const LLSD& hdrs,
const F32 timeout)
{
LLSD headers = hdrs;
headers[HTTP_OUT_HEADER_DESTINATION] = destination;
request(url, HTTP_COPY, NULL, responder, timeout, headers);
}
void LLHTTPClient::setPump(LLPumpIO& pump)
{

View File

@ -119,7 +119,7 @@ public:
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
///< sends a DELETE method, but we can't call it delete in c++
/**
* @brief Send a MOVE webdav method
*
@ -136,6 +136,22 @@ public:
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
/**
* @brief Send a COPY webdav method
*
* @param url The complete serialized (and escaped) url to get.
* @param destination The complete serialized destination url.
* @param responder The responder that will handle the result.
* @param headers A map of key:value headers to pass to the request
* @param timeout The number of seconds to give the server to respond.
*/
static void copy(
const std::string& url,
const std::string& destination,
ResponderPtr responder,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
//@}
/**

View File

@ -133,6 +133,8 @@ const std::string HTTP_VERB_POST("POST");
const std::string HTTP_VERB_DELETE("DELETE");
const std::string HTTP_VERB_MOVE("MOVE");
const std::string HTTP_VERB_OPTIONS("OPTIONS");
const std::string HTTP_VERB_PATCH("PATCH");
const std::string HTTP_VERB_COPY("COPY");
const std::string& httpMethodAsVerb(EHTTPMethod method)
{
@ -145,7 +147,9 @@ const std::string& httpMethodAsVerb(EHTTPMethod method)
HTTP_VERB_POST,
HTTP_VERB_DELETE,
HTTP_VERB_MOVE,
HTTP_VERB_OPTIONS
HTTP_VERB_OPTIONS,
HTTP_VERB_PATCH,
HTTP_VERB_COPY
};
if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT))
{

View File

@ -112,6 +112,7 @@ enum EHTTPMethod
HTTP_MOVE, // Caller will need to set 'Destination' header
HTTP_OPTIONS,
HTTP_PATCH,
HTTP_COPY,
HTTP_METHOD_COUNT
};

View File

@ -516,13 +516,19 @@ bool LLURLRequest::configure()
break;
case HTTP_DELETE:
// Set the handle for an http post
// Set the handle for an http delete
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
rv = true;
break;
case HTTP_COPY:
// Set the handle for an http copy
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "COPY");
rv = true;
break;
case HTTP_MOVE:
// Set the handle for an http post
// Set the handle for an http move
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
// *NOTE: should we check for the Destination header?
rv = true;

View File

@ -40,14 +40,25 @@
// 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);
}
void AISCommand::run_command()
bool AISCommand::run_command()
{
mCommandFunc();
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)
@ -80,9 +91,9 @@ void AISCommand::httpSuccess()
if (mCallback)
{
LLUUID item_id; // will default to null if parse fails.
getResponseUUID(content,item_id);
mCallback->fire(item_id);
LLUUID id; // will default to null if parse fails.
getResponseUUID(content,id);
mCallback->fire(id);
}
}
@ -96,12 +107,12 @@ void AISCommand::httpFailure()
if (!content.isMap())
{
LL_DEBUGS("Inventory") << "Malformed response contents " << content
<< " status " << status << " reason " << reason << llendl;
<< " status " << status << " reason " << reason << LL_ENDL;
}
else
{
LL_DEBUGS("Inventory") << "failed with content: " << ll_pretty_print_sd(content)
<< " status " << status << " reason " << reason << llendl;
<< " status " << status << " reason " << reason << LL_ENDL;
}
mRetryPolicy->onFailure(status, headers);
F32 seconds_to_wait;
@ -113,12 +124,23 @@ void AISCommand::httpFailure()
{
// 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::getCap(std::string& cap)
bool AISCommand::isAPIAvailable()
{
if (gAgent.getRegion())
{
return gAgent.getRegion()->isCapabilityAvailable("InventoryAPIv3");
}
return false;
}
//static
bool AISCommand::getInvCap(std::string& cap)
{
if (gAgent.getRegion())
{
@ -131,18 +153,39 @@ bool AISCommand::getCap(std::string& cap)
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 (!getCap(cap))
if (!getInvCap(cap))
{
llwarns << "No cap found" << llendl;
return;
}
std::string url = cap + std::string("/item/") + item_id.asString();
LL_DEBUGS("Inventory") << "url: " << url << llendl;
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
LLHTTPClient::ResponderPtr responder = this;
LLSD headers;
F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
@ -155,13 +198,13 @@ RemoveCategoryCommand::RemoveCategoryCommand(const LLUUID& item_id,
AISCommand(callback)
{
std::string cap;
if (!getCap(cap))
if (!getInvCap(cap))
{
llwarns << "No cap found" << llendl;
return;
}
std::string url = cap + std::string("/category/") + item_id.asString();
LL_DEBUGS("Inventory") << "url: " << url << llendl;
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
LLHTTPClient::ResponderPtr responder = this;
LLSD headers;
F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
@ -174,13 +217,13 @@ PurgeDescendentsCommand::PurgeDescendentsCommand(const LLUUID& item_id,
AISCommand(callback)
{
std::string cap;
if (!getCap(cap))
if (!getInvCap(cap))
{
llwarns << "No cap found" << llendl;
return;
}
std::string url = cap + std::string("/category/") + item_id.asString() + "/children";
LL_DEBUGS("Inventory") << "url: " << url << llendl;
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
LLCurl::ResponderPtr responder = this;
LLSD headers;
F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
@ -195,14 +238,14 @@ UpdateItemCommand::UpdateItemCommand(const LLUUID& item_id,
AISCommand(callback)
{
std::string cap;
if (!getCap(cap))
if (!getInvCap(cap))
{
llwarns << "No cap found" << llendl;
return;
}
std::string url = cap + std::string("/item/") + item_id.asString();
LL_DEBUGS("Inventory") << "url: " << url << llendl;
LL_DEBUGS("Inventory") << "request: " << ll_pretty_print_sd(mUpdates) << llendl;
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
LL_DEBUGS("Inventory") << "request: " << ll_pretty_print_sd(mUpdates) << LL_ENDL;
LLCurl::ResponderPtr responder = this;
LLSD headers;
headers["Content-Type"] = "application/llsd+xml";
@ -218,13 +261,13 @@ UpdateCategoryCommand::UpdateCategoryCommand(const LLUUID& item_id,
AISCommand(callback)
{
std::string cap;
if (!getCap(cap))
if (!getInvCap(cap))
{
llwarns << "No cap found" << llendl;
return;
}
std::string url = cap + std::string("/category/") + item_id.asString();
LL_DEBUGS("Inventory") << "url: " << url << llendl;
LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
LLCurl::ResponderPtr responder = this;
LLSD headers;
headers["Content-Type"] = "application/llsd+xml";
@ -238,7 +281,7 @@ SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& conten
AISCommand(callback)
{
std::string cap;
if (!getCap(cap))
if (!getInvCap(cap))
{
llwarns << "No cap found" << llendl;
return;
@ -255,92 +298,106 @@ SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& conten
setCommandFunc(cmd);
}
CopyLibraryCategoryCommand::CopyLibraryCategoryCommand(const LLUUID& source_id,
const LLUUID& dest_id,
LLPointer<LLInventoryCallback> callback):
AISCommand(callback)
{
std::string cap;
if (!getLibCap(cap))
{
llwarns << "No cap found" << llendl;
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();
llinfos << url << llendl;
LLCurl::ResponderPtr responder = this;
LLSD headers;
F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
command_func_type cmd = boost::bind(&LLHTTPClient::copy, url, dest_id.asString(), responder, headers, timeout);
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)
{
// parse _categories_removed -> mObjectsDeleted
uuid_vec_t cat_ids;
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_vec_t::const_iterator it = cat_ids.begin();
for (uuid_list_t::const_iterator it = cat_ids.begin();
it != cat_ids.end(); ++it)
{
LLViewerInventoryCategory *cat = gInventory.getCategory(*it);
mCatDeltas[cat->getParentUUID()]--;
mObjectsDeleted.insert(*it);
mCatDescendentDeltas[cat->getParentUUID()]--;
mObjectsDeletedIds.insert(*it);
}
// parse _categories_items_removed -> mObjectsDeleted
uuid_vec_t item_ids;
// parse _categories_items_removed -> mObjectsDeletedIds
uuid_list_t item_ids;
parseUUIDArray(update,"_category_items_removed",item_ids);
for (uuid_vec_t::const_iterator it = item_ids.begin();
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);
mCatDeltas[item->getParentUUID()]--;
mObjectsDeleted.insert(*it);
mCatDescendentDeltas[item->getParentUUID()]--;
mObjectsDeletedIds.insert(*it);
}
// parse _broken_links_removed -> mObjectsDeleted
uuid_vec_t broken_link_ids;
// parse _broken_links_removed -> mObjectsDeletedIds
uuid_list_t broken_link_ids;
parseUUIDArray(update,"_broken_links_removed",broken_link_ids);
for (uuid_vec_t::const_iterator it = broken_link_ids.begin();
for (uuid_list_t::const_iterator it = broken_link_ids.begin();
it != broken_link_ids.end(); ++it)
{
LLViewerInventoryItem *item = gInventory.getItem(*it);
mCatDeltas[item->getParentUUID()]--;
mObjectsDeleted.insert(*it);
mCatDescendentDeltas[item->getParentUUID()]--;
mObjectsDeletedIds.insert(*it);
}
// parse _created_items
parseUUIDArray(update,"_created_items",mItemsCreatedIds);
parseUUIDArray(update,"_created_items",mItemIds);
if (update.has("_embedded"))
{
const LLSD& embedded = update["_embedded"];
for(LLSD::map_const_iterator it = embedded.beginMap(),
end = embedded.endMap();
it != end; ++it)
{
const std::string& field = (*it).first;
// parse created links
if (field == "link")
{
const LLSD& links = embedded["link"];
parseCreatedLinks(links);
}
}
}
// Parse item update at the top level.
if (update.has("item_id"))
{
LLUUID item_id = update["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(update);
if (rv)
{
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.
mCatDeltas[new_item->getParentUUID()];
}
else
{
llerrs << "unpack failed" << llendl;
}
}
// parse _created_categories
parseUUIDArray(update,"_created_categories",mCategoryIds);
// Parse updated category versions.
const std::string& ucv = "_updated_category_versions";
@ -352,49 +409,224 @@ void AISUpdate::parseUpdate(const LLSD& update)
{
const LLUUID id((*it).first);
S32 version = (*it).second.asInteger();
mCatVersions[id] = version;
mCatVersionsUpdated[id] = version;
}
}
}
void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uuid_vec_t& ids)
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?
llerrs << "unpack failed" << llendl;
}
}
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?
llerrs << "unpack failed" << llendl;
}
}
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()];
}
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?
llerrs << "unpack failed" << llendl;
}
// 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("category") &&
embedded.has("link") &&
embedded.has("item"))
{
mCatDescendentsKnown[category_id] = embedded["category"].size();
mCatDescendentsKnown[category_id] += embedded["link"].size();
mCatDescendentsKnown[category_id] += embedded["item"].size();
}
}
void AISUpdate::parseEmbedded(const LLSD& embedded)
{
if (embedded.has("link"))
{
parseEmbeddedLinks(embedded["link"]);
}
if (embedded.has("item"))
{
parseEmbeddedItems(embedded["item"]);
}
if (embedded.has("category"))
{
parseEmbeddedCategories(embedded["category"]);
}
}
void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids)
{
ids.clear();
if (content.has(name))
{
for(LLSD::array_const_iterator it = content[name].beginArray(),
end = content[name].endArray();
it != end; ++it)
{
ids.push_back((*it).asUUID());
ids.insert((*it).asUUID());
}
}
}
void AISUpdate::parseLink(const LLUUID& link_id, const LLSD& link_map)
{
LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem);
BOOL rv = new_link->unpackMessage(link_map);
if (rv)
{
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) << llendl;
mItemsCreated[link_id] = new_link;
const LLUUID& parent_id = new_link->getParentUUID();
mCatDeltas[parent_id]++;
}
else
{
llwarns << "failed to parse" << llendl;
}
}
void AISUpdate::parseCreatedLinks(const LLSD& links)
void AISUpdate::parseEmbeddedLinks(const LLSD& links)
{
for(LLSD::map_const_iterator linkit = links.beginMap(),
linkend = links.endMap();
@ -402,47 +634,134 @@ void AISUpdate::parseCreatedLinks(const LLSD& links)
{
const LLUUID link_id((*linkit).first);
const LLSD& link_map = (*linkit).second;
uuid_vec_t::const_iterator pos =
std::find(mItemsCreatedIds.begin(),
mItemsCreatedIds.end(),link_id);
if (pos != mItemsCreatedIds.end())
if (mItemIds.end() == mItemIds.find(link_id))
{
parseLink(link_id,link_map);
LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL;
}
else
{
LL_DEBUGS("Inventory") << "Ignoring link not in created items list " << link_id << llendl;
parseLink(link_map);
}
}
}
void AISUpdate::parseEmbeddedItems(const LLSD& items)
{
// Special case: this may be a single item (_embedded in a link)
if (items.has("item_id"))
{
if (mItemIds.end() != mItemIds.find(items["item_id"].asUUID()))
{
parseContent(items);
}
return;
}
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::parseEmbeddedCategories(const LLSD& categories)
{
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 descendent/version accounting.
// Can remove this if/when we use the version info directly.
for (std::map<LLUUID,S32>::const_iterator catit = mCatDeltas.begin();
catit != mCatDeltas.end(); ++catit)
// Do version/descendent accounting.
for (std::map<LLUUID,S32>::const_iterator catit = mCatDescendentDeltas.begin();
catit != mCatDescendentDeltas.end(); ++catit)
{
const LLUUID cat_id(catit->first);
S32 delta = catit->second;
LLInventoryModel::LLCategoryUpdate up(cat_id, delta);
gInventory.accountForUpdate(up);
}
// 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 = mCatVersions.begin();
ucv_it != mCatVersions.end(); ++ucv_it)
{
const LLUUID id = ucv_it->first;
S32 version = ucv_it->second;
LLViewerInventoryCategory *cat = gInventory.getCategory(id);
if (cat->getVersion() != version)
// Don't account for update if we just created this category.
if (mCategoriesCreated.find(cat_id) != mCategoriesCreated.end())
{
llwarns << "Possible version mismatch for category " << cat->getName()
<< ", viewer version " << cat->getVersion()
<< " server version " << version << llendl;
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_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);
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 (NULL == 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 " << category_id << LL_ENDL;
}
}
@ -456,7 +775,7 @@ void AISUpdate::doUpdate()
// 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 << llendl;
LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL;
gInventory.updateItem(new_item);
}
@ -469,19 +788,36 @@ void AISUpdate::doUpdate()
// 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 << llendl;
//LL_DEBUGS("Inventory") << ll_pretty_print_sd(new_item->asLLSD()) << llendl;
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 (std::set<LLUUID>::const_iterator del_it = mObjectsDeleted.begin();
del_it != mObjectsDeleted.end(); ++del_it)
for (std::set<LLUUID>::const_iterator del_it = mObjectsDeletedIds.begin();
del_it != mObjectsDeletedIds.end(); ++del_it)
{
LL_DEBUGS("Inventory") << "deleted item " << *del_it << llendl;
LL_DEBUGS("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);
if (cat->getVersion() != version)
{
llwarns << "Possible version mismatch for category " << cat->getName()
<< ", viewer version " << cat->getVersion()
<< " server version " << version << llendl;
}
}
gInventory.notifyObservers();
}

View File

@ -31,7 +31,6 @@
#include <map>
#include <set>
#include <string>
#include <vector>
#include "llcurl.h"
#include "llhttpclient.h"
#include "llhttpretrypolicy.h"
@ -46,7 +45,7 @@ public:
virtual ~AISCommand() {}
void run_command();
bool run_command();
void setCommandFunc(command_func_type command_func);
@ -54,13 +53,17 @@ public:
// LLInventoryCallback::fire(). May or may not need to bother,
// since most LLInventoryCallbacks do their work in the
// destructor.
virtual bool getResponseUUID(const LLSD& content, LLUUID& id);
/* virtual */ void httpSuccess();
/* virtual */ void httpFailure();
/*virtual*/ void httpFailure();
static bool isAPIAvailable();
static bool getInvCap(std::string& cap);
static bool getLibCap(std::string& cap);
static void getCapabilityNames(LLSD& capabilityNames);
static bool getCap(std::string& cap);
protected:
virtual bool getResponseUUID(const LLSD& content, LLUUID& id);
private:
command_func_type mCommandFunc;
@ -118,26 +121,52 @@ private:
LLSD mContents;
};
class CopyLibraryCategoryCommand: public AISCommand
{
public:
CopyLibraryCategoryCommand(const LLUUID& source_id, const LLUUID& dest_id, LLPointer<LLInventoryCallback> callback);
protected:
/* virtual */ bool getResponseUUID(const LLSD& content, LLUUID& id);
};
class AISUpdate
{
public:
AISUpdate(const LLSD& update);
void parseUpdate(const LLSD& update);
void parseUUIDArray(const LLSD& content, const std::string& name, uuid_vec_t& ids);
void parseLink(const LLUUID& link_id, const LLSD& link_map);
void parseCreatedLinks(const LLSD& links);
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& links);
void parseEmbeddedCategories(const LLSD& links);
void doUpdate();
private:
void clearParseResults();
typedef std::map<LLUUID,S32> uuid_int_map_t;
uuid_int_map_t mCatDeltas;
uuid_int_map_t mCatVersions;
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;
std::set<LLUUID> mObjectsDeleted;
uuid_vec_t mItemsCreatedIds;
// 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

View File

@ -435,6 +435,50 @@ private:
S32 LLCallAfterInventoryCopyMgr::sInstanceCount = 0;
class LLWearCategoryAfterCopy: public LLInventoryCallback
{
public:
LLWearCategoryAfterCopy(bool append):
mAppend(append)
{}
// virtual
void fire(const LLUUID& id)
{
// Wear the inventory category.
LLInventoryCategory* cat = gInventory.getCategory(id);
LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(cat, mAppend);
}
private:
bool mAppend;
};
class LLTrackPhaseWrapper : public LLInventoryCallback
{
public:
LLTrackPhaseWrapper(const std::string& phase_name, LLPointer<LLInventoryCallback> cb = NULL):
mTrackingPhase(phase_name),
mCB(cb)
{
selfStartPhase(mTrackingPhase);
}
// virtual
void fire(const LLUUID& id)
{
selfStopPhase(mTrackingPhase);
if (mCB)
{
mCB->fire(id);
}
}
protected:
std::string mTrackingPhase;
LLPointer<LLInventoryCallback> mCB;
};
LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool enforce_item_restrictions,
bool enforce_ordering,
nullary_func_t post_update_func
@ -2244,10 +2288,31 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName()
<< " )" << LL_ENDL;
selfStartPhase("wear_inventory_category_fetch");
callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
&LLAppearanceMgr::instance(),
category->getUUID(), copy, append));
// 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);
LLPointer<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,
&LLAppearanceMgr::instance(),
category->getUUID(), copy, append));
}
}
S32 LLAppearanceMgr::getActiveCopyOperations() const
@ -3544,8 +3609,7 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo
// First, make a folder in the My Outfits directory.
const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
std::string cap;
if (AISCommand::getCap(cap))
if (AISCommand::isAPIAvailable())
{
// cap-based category creation was buggy until recently. use
// existence of AIS as an indicator the fix is present. Does

View File

@ -1718,7 +1718,6 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
if(cat)
{
bool accounted = false;
S32 version = cat->getVersion();
if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
{
@ -1733,22 +1732,27 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
}
if(descendents_server == descendents_actual)
{
accounted = true;
descendents_actual += update.mDescendentDelta;
cat->setDescendentCount(descendents_actual);
cat->setVersion(++version);
lldebugs << "accounted: '" << cat->getName() << "' "
LL_DEBUGS("Inventory") << "accounted: '" << cat->getName() << "' "
<< version << " with " << descendents_actual
<< " descendents." << llendl;
<< " descendents." << LL_ENDL;
}
else
{
// Error condition, this means that the category did not register that
// it got new descendents (perhaps because it is still being loaded)
// which means its descendent count will be wrong.
llwarns << "Accounting failed for '" << cat->getName() << "' version:"
<< version << " due to mismatched descendent count: server == "
<< descendents_server << ", viewer == " << descendents_actual << llendl;
}
}
if(!accounted)
else
{
// Error condition, this means that the category did not register that
// it got new descendents (perhaps because it is still being loaded)
// which means its descendent count will be wrong.
llwarns << "Accounting failed for '" << cat->getName() << "' version:"
<< version << llendl;
llwarns << "Accounting failed for '" << cat->getName() << "' version: unknown ("
<< version << ")" << llendl;
}
}
else

View File

@ -430,7 +430,7 @@ void LLViewerInventoryItem::fetchFromServer(void) const
}
// virtual
BOOL LLViewerInventoryItem::unpackMessage(LLSD item)
BOOL LLViewerInventoryItem::unpackMessage(const LLSD& item)
{
BOOL rv = LLInventoryItem::fromLLSD(item);
@ -866,6 +866,21 @@ void LLViewerInventoryCategory::localizeName()
LLLocalizedInventoryItemsDictionary::getInstance()->localizeInventoryObjectName(mName);
}
// virtual
BOOL LLViewerInventoryCategory::unpackMessage(const LLSD& category)
{
BOOL rv = LLInventoryCategory::fromLLSD(category);
localizeName();
return rv;
}
// virtual
void LLViewerInventoryCategory::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num)
{
LLInventoryCategory::unpackMessage(msg, block, block_num);
localizeName();
}
///----------------------------------------------------------------------------
/// Local function definitions
///----------------------------------------------------------------------------
@ -1159,24 +1174,22 @@ void update_inventory_item(
const LLSD& updates,
LLPointer<LLInventoryCallback> cb)
{
LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id);
LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << llendl;
if(obj)
bool ais_ran = false;
if (AISCommand::isAPIAvailable())
{
LLPointer<LLViewerInventoryItem> new_item(new LLViewerInventoryItem);
new_item->copyViewerItem(obj);
new_item->fromLLSD(updates,false);
std::string cap;
if (AISCommand::getCap(cap))
{
LLSD new_llsd;
new_item->asLLSD(new_llsd);
LLPointer<AISCommand> cmd_ptr = new UpdateItemCommand(item_id, new_llsd, cb);
cmd_ptr->run_command();
}
else // no cap
LLPointer<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("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << llendl;
if(obj)
{
LLPointer<LLViewerInventoryItem> new_item(new LLViewerInventoryItem);
new_item->copyViewerItem(obj);
new_item->fromLLSD(updates,false);
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_UpdateInventoryItem);
msg->nextBlockFast(_PREHASH_AgentData);
@ -1216,9 +1229,8 @@ void update_inventory_category(
LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj);
new_cat->fromLLSD(updates);
//std::string cap;
// FIXME - restore this once the back-end work has been done.
if (0) // if (AISCommand::getCap(cap))
if (0) // if (AISCommand::isAPIAvailable())
{
LLSD new_llsd = new_cat->asLLSD();
LLPointer<AISCommand> cmd_ptr = new UpdateCategoryCommand(cat_id, new_llsd, cb);
@ -1254,8 +1266,7 @@ void remove_inventory_item(
LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << llendl;
if(obj)
{
std::string cap;
if (AISCommand::getCap(cap))
if (AISCommand::isAPIAvailable())
{
LLPointer<AISCommand> cmd_ptr = new RemoveItemCommand(item_id, cb);
cmd_ptr->run_command();
@ -1325,8 +1336,7 @@ void remove_inventory_category(
LLNotificationsUtil::add("CannotRemoveProtectedCategories");
return;
}
std::string cap;
if (AISCommand::getCap(cap))
if (AISCommand::isAPIAvailable())
{
LLPointer<AISCommand> cmd_ptr = new RemoveCategoryCommand(cat_id, cb);
cmd_ptr->run_command();
@ -1429,8 +1439,7 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)
}
else
{
std::string cap;
if (AISCommand::getCap(cap))
if (AISCommand::isAPIAvailable())
{
LLPointer<AISCommand> cmd_ptr = new PurgeDescendentsCommand(id, cb);
cmd_ptr->run_command();
@ -1564,8 +1573,7 @@ void slam_inventory_folder(const LLUUID& folder_id,
const LLSD& contents,
LLPointer<LLInventoryCallback> cb)
{
std::string cap;
if (AISCommand::getCap(cap))
if (AISCommand::isAPIAvailable())
{
LL_DEBUGS("Avatar") << "using AISv3 to slam folder, id " << folder_id
<< " new contents: " << ll_pretty_print_sd(contents) << llendl;

View File

@ -124,7 +124,7 @@ public:
virtual void packMessage(LLMessageSystem* msg) const;
virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
virtual BOOL unpackMessage(LLSD item);
virtual BOOL unpackMessage(const LLSD& item);
virtual BOOL importFile(LLFILE* fp);
virtual BOOL importLegacyStream(std::istream& input_stream);
@ -224,6 +224,8 @@ public:
bool importFileLocal(LLFILE* fp);
void determineFolderType();
void changeType(LLFolderType::EType new_folder_type);
virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
virtual BOOL unpackMessage(const LLSD& category);
private:
friend class LLInventoryModel;

View File

@ -30,6 +30,7 @@
// linden libraries
#include "indra_constants.h"
#include "llaisapi.h"
#include "llavatarnamecache.h" // name lookup cap url
#include "llfloaterreg.h"
#include "llmath.h"
@ -1645,7 +1646,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("FetchInventory2");
capabilityNames.append("FetchInventoryDescendents2");
capabilityNames.append("IncrementCOFVersion");
capabilityNames.append("InventoryAPIv3");
AISCommand::getCapabilityNames(capabilityNames);
}
capabilityNames.append("GetDisplayNames");
@ -1893,6 +1894,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")))
{
llwarns << "isCapabilityAvailable called before caps received for " << name << llendl;
}
CapabilityMap::const_iterator iter = mImpl->mCapabilities.find(name);
if(iter == mImpl->mCapabilities.end())
{
return false;
}
return true;
}
bool LLViewerRegion::capabilitiesReceived() const
{
return mCapabilitiesReceived;

View File

@ -244,6 +244,7 @@ public:
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;