diff options
Diffstat (limited to 'indra')
-rwxr-xr-x | indra/llmessage/llhttpclient.cpp | 13 | ||||
-rwxr-xr-x | indra/llmessage/llhttpclient.h | 18 | ||||
-rwxr-xr-x | indra/llmessage/llhttpconstants.cpp | 6 | ||||
-rwxr-xr-x | indra/llmessage/llhttpconstants.h | 1 | ||||
-rwxr-xr-x | indra/llmessage/llurlrequest.cpp | 10 | ||||
-rwxr-xr-x | indra/newview/llaisapi.cpp | 588 | ||||
-rwxr-xr-x | indra/newview/llaisapi.h | 55 | ||||
-rwxr-xr-x | indra/newview/llappearancemgr.cpp | 76 | ||||
-rwxr-xr-x | indra/newview/llinventorymodel.cpp | 24 | ||||
-rwxr-xr-x | indra/newview/llviewerinventory.cpp | 62 | ||||
-rwxr-xr-x | indra/newview/llviewerinventory.h | 4 | ||||
-rwxr-xr-x | indra/newview/llviewerregion.cpp | 19 | ||||
-rwxr-xr-x | indra/newview/llviewerregion.h | 1 |
13 files changed, 689 insertions, 188 deletions
diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 53cef54559..70c890a8de 100755 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -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) { diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 4e7495495f..5ace2d74c9 100755 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -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); + //@} /** diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp index 016f1f1970..01f4a080b0 100755 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llmessage/llhttpconstants.cpp @@ -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)) { diff --git a/indra/llmessage/llhttpconstants.h b/indra/llmessage/llhttpconstants.h index aa947af414..4aa3cc6394 100755 --- a/indra/llmessage/llhttpconstants.h +++ b/indra/llmessage/llhttpconstants.h @@ -112,6 +112,7 @@ enum EHTTPMethod HTTP_MOVE, // Caller will need to set 'Destination' header HTTP_OPTIONS, HTTP_PATCH, + HTTP_COPY, HTTP_METHOD_COUNT }; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index cadff49cb8..7bf930aeb0 100755 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -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; diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 6d5f1951f9..cb8700865a 100755 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -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,146 +298,335 @@ 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); + + // parse _created_categories + parseUUIDArray(update,"_created_categories",mCategoryIds); - if (update.has("_embedded")) + // Parse updated category versions. + const std::string& ucv = "_updated_category_versions"; + if (update.has(ucv)) { - const LLSD& embedded = update["_embedded"]; - for(LLSD::map_const_iterator it = embedded.beginMap(), - end = embedded.endMap(); - it != end; ++it) + for(LLSD::map_const_iterator it = update[ucv].beginMap(), + end = update[ucv].endMap(); + it != end; ++it) { - const std::string& field = (*it).first; - - // parse created links - if (field == "link") - { - const LLSD& links = embedded["link"]; - parseCreatedLinks(links); - } + const LLUUID id((*it).first); + S32 version = (*it).second.asInteger(); + mCatVersionsUpdated[id] = version; } } +} - // Parse item update at the top level. - if (update.has("item_id")) +void AISUpdate::parseContent(const LLSD& update) +{ + if (update.has("linked_id")) { - LLUUID item_id = update["item_id"].asUUID(); - LLPointer<LLViewerInventoryItem> new_item(new LLViewerInventoryItem); - LLViewerInventoryItem *curr_item = gInventory.getItem(item_id); - if (curr_item) + parseLink(update); + } + else if (update.has("item_id")) + { + parseItem(update); + } + + if (update.has("category_id")) + { + parseCategory(update); + } + else + { + if (update.has("_embedded")) { - // Default to current values where not provided. - new_item->copyViewerItem(curr_item); + parseEmbedded(update["_embedded"]); } - BOOL rv = new_item->unpackMessage(update); - if (rv) + } +} + +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. - mCatDeltas[new_item->getParentUUID()]; + mCatDescendentDeltas[new_item->getParentUUID()]; } else { - llerrs << "unpack failed" << llendl; + mItemsCreated[item_id] = new_item; + mCatDescendentDeltas[new_item->getParentUUID()]++; } } + else + { + // *TODO: Wow, harsh. Should we just complain and get out? + llerrs << "unpack failed" << llendl; + } +} - // Parse updated category versions. - const std::string& ucv = "_updated_category_versions"; - if (update.has(ucv)) +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) { - for(LLSD::map_const_iterator it = update[ucv].beginMap(), - end = update[ucv].endMap(); - it != end; ++it) + // 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) { - const LLUUID id((*it).first); - S32 version = (*it).second.asInteger(); - mCatVersions[id] = version; + 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::parseUUIDArray(const LLSD& content, const std::string& name, uuid_vec_t& ids) + +void AISUpdate::parseCategory(const LLSD& category_map) { - ids.clear(); - if (content.has(name)) + 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")) { - for(LLSD::array_const_iterator it = content[name].beginArray(), - end = content[name].endArray(); - it != end; ++it) + 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 { - ids.push_back((*it).asUUID()); + // 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::parseLink(const LLUUID& link_id, const LLSD& link_map) +void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded) { - LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem); - BOOL rv = new_link->unpackMessage(link_map); - if (rv) + // We can only determine true descendent count if this contains all descendent types. + if (embedded.has("category") && + embedded.has("link") && + embedded.has("item")) { - 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]++; + mCatDescendentsKnown[category_id] = embedded["category"].size(); + mCatDescendentsKnown[category_id] += embedded["link"].size(); + mCatDescendentsKnown[category_id] += embedded["item"].size(); } - else +} + +void AISUpdate::parseEmbedded(const LLSD& embedded) +{ + if (embedded.has("link")) + { + parseEmbeddedLinks(embedded["link"]); + } + if (embedded.has("item")) + { + parseEmbeddedItems(embedded["item"]); + } + if (embedded.has("category")) { - llwarns << "failed to parse" << llendl; + parseEmbeddedCategories(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::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)) + { + LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL; + } + else + { + 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)) { - parseLink(link_id,link_map); + LL_DEBUGS("Inventory") << "Ignoring category not in categories list " << category_id << LL_ENDL; } else { - LL_DEBUGS("Inventory") << "Ignoring link not in created items list " << link_id << llendl; + 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); + // 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_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; + } } - - // 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) + + // CREATE CATEGORIES + for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin(); + create_it != mCategoriesCreated.end(); ++create_it) { - const LLUUID id = ucv_it->first; - S32 version = ucv_it->second; - LLViewerInventoryCategory *cat = gInventory.getCategory(id); - if (cat->getVersion() != version) + 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) { - llwarns << "Possible version mismatch for category " << cat->getName() - << ", viewer version " << cat->getVersion() - << " server version " << version << llendl; + 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(); } diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index 1f9555f004..f4e219e9e6 100755 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -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; - - std::set<LLUUID> mObjectsDeleted; - uuid_vec_t mItemsCreatedIds; + 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 diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index a0f8cbe911..fc07932860 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -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 diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index e1fd2e02fa..0d99bea3fc 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -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 diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index c934dd991b..5beae8ec24 100755 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -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; diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index de1f3daa1e..0d4ffaa575 100755 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -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; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index dca1a5b542..ad046accd0 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -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; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 5ac2a83aaf..4fb1b7402c 100755 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -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; |