diff options
Diffstat (limited to 'indra/newview')
-rwxr-xr-x | indra/newview/CMakeLists.txt | 2 | ||||
-rwxr-xr-x | indra/newview/llaisapi.cpp | 482 | ||||
-rwxr-xr-x | indra/newview/llaisapi.h | 143 | ||||
-rwxr-xr-x | indra/newview/llinventorymodel.cpp | 206 | ||||
-rwxr-xr-x | indra/newview/llviewerinventory.cpp | 350 | ||||
-rwxr-xr-x | indra/newview/llviewerinventory.h | 9 |
6 files changed, 659 insertions, 533 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4560951900..dc9370bd69 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -98,6 +98,7 @@ include_directories(SYSTEM set(viewer_SOURCE_FILES groupchatlistener.cpp llaccountingcostmanager.cpp + llaisapi.cpp llagent.cpp llagentaccess.cpp llagentcamera.cpp @@ -679,6 +680,7 @@ set(viewer_HEADER_FILES ViewerInstall.cmake groupchatlistener.h llaccountingcostmanager.h + llaisapi.h llagent.h llagentaccess.h llagentcamera.h diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp new file mode 100755 index 0000000000..393e5c0a68 --- /dev/null +++ b/indra/newview/llaisapi.cpp @@ -0,0 +1,482 @@ +/** + * @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" + +///---------------------------------------------------------------------------- +/// Classes for AISv3 support. +///---------------------------------------------------------------------------- + +// AISCommand - base class for retry-able HTTP requests using the AISv3 cap. +AISCommand::AISCommand(LLPointer<LLInventoryCallback> callback): + mCallback(callback) +{ + mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10); +} + +void AISCommand::run_command() +{ + mCommandFunc(); +} + +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(HTTP_INTERNAL_ERROR, "Malformed response contents", content); + return; + } + mRetryPolicy->onSuccess(); + + gInventory.onAISUpdateReceived("AISCommand", content); + + if (mCallback) + { + LLUUID item_id; // will default to null if parse fails. + getResponseUUID(content,item_id); + mCallback->fire(item_id); + } +} + +/*virtual*/ +void AISCommand::httpFailure() +{ + const LLSD& content = getContent(); + S32 status = getStatus(); + const std::string& reason = getReason(); + const LLSD& headers = getResponseHeaders(); + if (!content.isMap()) + { + LL_DEBUGS("Inventory") << "Malformed response contents " << content + << " status " << status << " reason " << reason << llendl; + } + else + { + LL_DEBUGS("Inventory") << "failed with content: " << ll_pretty_print_sd(content) + << " status " << status << " reason " << reason << llendl; + } + 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. + setCommandFunc(no_op); + } +} + +//static +bool AISCommand::getCap(std::string& cap) +{ + if (gAgent.getRegion()) + { + cap = gAgent.getRegion()->getCapability("InventoryAPIv3"); + } + if (!cap.empty()) + { + return true; + } + return false; +} + +RemoveItemCommand::RemoveItemCommand(const LLUUID& item_id, + LLPointer<LLInventoryCallback> callback): + AISCommand(callback) +{ + std::string cap; + if (!getCap(cap)) + { + llwarns << "No cap found" << llendl; + return; + } + std::string url = cap + std::string("/item/") + item_id.asString(); + LL_DEBUGS("Inventory") << "url: " << url << llendl; + LLHTTPClient::ResponderPtr responder = this; + LLSD headers; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); + setCommandFunc(cmd); +} + +RemoveCategoryCommand::RemoveCategoryCommand(const LLUUID& item_id, + LLPointer<LLInventoryCallback> callback): + AISCommand(callback) +{ + std::string cap; + if (!getCap(cap)) + { + llwarns << "No cap found" << llendl; + return; + } + std::string url = cap + std::string("/category/") + item_id.asString(); + LL_DEBUGS("Inventory") << "url: " << url << llendl; + LLHTTPClient::ResponderPtr responder = this; + LLSD headers; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); + setCommandFunc(cmd); +} + +PurgeDescendentsCommand::PurgeDescendentsCommand(const LLUUID& item_id, + LLPointer<LLInventoryCallback> callback): + AISCommand(callback) +{ + std::string cap; + if (!getCap(cap)) + { + llwarns << "No cap found" << llendl; + return; + } + std::string url = cap + std::string("/category/") + item_id.asString() + "/children"; + LL_DEBUGS("Inventory") << "url: " << url << llendl; + LLCurl::ResponderPtr responder = this; + LLSD headers; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); + setCommandFunc(cmd); +} + +UpdateItemCommand::UpdateItemCommand(const LLUUID& item_id, + const LLSD& updates, + LLPointer<LLInventoryCallback> callback): + mUpdates(updates), + AISCommand(callback) +{ + std::string cap; + if (!getCap(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; + LLCurl::ResponderPtr responder = this; + LLSD headers; + headers["Content-Type"] = "application/llsd+xml"; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder, headers, timeout); + setCommandFunc(cmd); +} + +UpdateCategoryCommand::UpdateCategoryCommand(const LLUUID& item_id, + const LLSD& updates, + LLPointer<LLInventoryCallback> callback): + mUpdates(updates), + AISCommand(callback) +{ + std::string cap; + if (!getCap(cap)) + { + llwarns << "No cap found" << llendl; + return; + } + std::string url = cap + std::string("/category/") + item_id.asString(); + LL_DEBUGS("Inventory") << "url: " << url << llendl; + LLCurl::ResponderPtr responder = this; + LLSD headers; + headers["Content-Type"] = "application/llsd+xml"; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder, headers, timeout); + setCommandFunc(cmd); +} + +SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback): + mContents(contents), + AISCommand(callback) +{ + std::string cap; + if (!getCap(cap)) + { + llwarns << "No cap found" << llendl; + return; + } + LLUUID tid; + tid.generate(); + std::string url = cap + std::string("/category/") + folder_id.asString() + "/links?tid=" + tid.asString(); + llinfos << url << llendl; + LLCurl::ResponderPtr responder = this; + LLSD headers; + headers["Content-Type"] = "application/llsd+xml"; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::put, url, mContents, responder, headers, timeout); + setCommandFunc(cmd); +} + +AISUpdate::AISUpdate(const LLSD& update) +{ + parseUpdate(update); +} + +void AISUpdate::parseUpdate(const LLSD& update) +{ + // parse _categories_removed -> mObjectsDeleted + uuid_vec_t cat_ids; + parseUUIDArray(update,"_categories_removed",cat_ids); + for (uuid_vec_t::const_iterator it = cat_ids.begin(); + it != cat_ids.end(); ++it) + { + LLViewerInventoryCategory *cat = gInventory.getCategory(*it); + mCatDeltas[cat->getParentUUID()]--; + mObjectsDeleted.insert(*it); + } + + // parse _categories_items_removed -> mObjectsDeleted + uuid_vec_t item_ids; + parseUUIDArray(update,"_category_items_removed",item_ids); + for (uuid_vec_t::const_iterator it = item_ids.begin(); + it != item_ids.end(); ++it) + { + LLViewerInventoryItem *item = gInventory.getItem(*it); + mCatDeltas[item->getParentUUID()]--; + mObjectsDeleted.insert(*it); + } + + // parse _broken_links_removed -> mObjectsDeleted + uuid_vec_t broken_link_ids; + parseUUIDArray(update,"_broken_links_removed",broken_link_ids); + for (uuid_vec_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); + } + + // parse _created_items + parseUUIDArray(update,"_created_items",mItemsCreatedIds); + + 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); + } + else + { + llwarns << "unrecognized embedded field " << field << llendl; + } + } + + } + + // 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); + 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 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(); + mCatVersions[id] = version; + } + } +} + +void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uuid_vec_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()); + } + } +} + +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) +{ + 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; + uuid_vec_t::const_iterator pos = + std::find(mItemsCreatedIds.begin(), + mItemsCreatedIds.end(),link_id); + if (pos != mItemsCreatedIds.end()) + { + parseLink(link_id,link_map); + } + else + { + LL_DEBUGS("Inventory") << "Ignoring link not in created items list " << link_id << llendl; + } + } +} + +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) + { + 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) + { + llwarns << "Possible version mismatch, viewer " << cat->getVersion() + << " server " << version << llendl; + } + } + + // 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 << llendl; + gInventory.updateItem(new_item); + } + + // 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 << llendl; + gInventory.updateItem(new_item); + } + + // DELETE OBJECTS + for (std::set<LLUUID>::const_iterator del_it = mObjectsDeleted.begin(); + del_it != mObjectsDeleted.end(); ++del_it) + { + LL_DEBUGS("Inventory") << "deleted item " << *del_it << llendl; + gInventory.onObjectDeletedFromServer(*del_it, false, false); + } +} + diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h new file mode 100755 index 0000000000..1f9555f004 --- /dev/null +++ b/indra/newview/llaisapi.h @@ -0,0 +1,143 @@ +/** + * @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 <vector> +#include "llcurl.h" +#include "llhttpclient.h" +#include "llhttpretrypolicy.h" +#include "llviewerinventory.h" + +class AISCommand: public LLHTTPClient::Responder +{ +public: + typedef boost::function<void()> command_func_type; + + AISCommand(LLPointer<LLInventoryCallback> callback); + + virtual ~AISCommand() {} + + void 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 bool getResponseUUID(const LLSD& content, LLUUID& id); + + /* virtual */ void httpSuccess(); + + /*virtual*/ void httpFailure(); + + static bool getCap(std::string& cap); + +private: + command_func_type mCommandFunc; + LLPointer<LLHTTPRetryPolicy> mRetryPolicy; + LLPointer<LLInventoryCallback> mCallback; +}; + +class RemoveItemCommand: public AISCommand +{ +public: + RemoveItemCommand(const LLUUID& item_id, + LLPointer<LLInventoryCallback> callback); +}; + +class RemoveCategoryCommand: public AISCommand +{ +public: + RemoveCategoryCommand(const LLUUID& item_id, + LLPointer<LLInventoryCallback> callback); +}; + +class PurgeDescendentsCommand: public AISCommand +{ +public: + PurgeDescendentsCommand(const LLUUID& item_id, + LLPointer<LLInventoryCallback> callback); +}; + +class UpdateItemCommand: public AISCommand +{ +public: + UpdateItemCommand(const LLUUID& item_id, + const LLSD& updates, + LLPointer<LLInventoryCallback> callback); +private: + LLSD mUpdates; +}; + +class UpdateCategoryCommand: public AISCommand +{ +public: + UpdateCategoryCommand(const LLUUID& item_id, + const LLSD& updates, + LLPointer<LLInventoryCallback> callback); +private: + LLSD mUpdates; +}; + +class SlamFolderCommand: public AISCommand +{ +public: + SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback); + +private: + LLSD mContents; +}; + +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 doUpdate(); +private: + typedef std::map<LLUUID,S32> uuid_int_map_t; + uuid_int_map_t mCatDeltas; + uuid_int_map_t mCatVersions; + + 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; +}; + +#endif diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 532d3a3495..6dc193292e 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llinventorymodel.h" +#include "llaisapi.h" #include "llagent.h" #include "llagentwearables.h" #include "llappearancemgr.h" @@ -1154,20 +1155,6 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, notifyObservers(); } -void parse_llsd_uuid_array(const LLSD& content, const std::string& name, uuid_vec_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()); - } - } -} - void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update) { if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) @@ -1175,195 +1162,8 @@ void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLS dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); } - // Track changes to descendent counts for accounting. - std::map<LLUUID,S32> cat_deltas; - typedef std::map<LLUUID,LLPointer<LLViewerInventoryItem> > deferred_item_map_t; - deferred_item_map_t items_created; - deferred_item_map_t items_updated; - std::set<LLUUID> objects_deleted; - - // parse _categories_removed -> objects_deleted - uuid_vec_t cat_ids; - parse_llsd_uuid_array(update,"_categories_removed",cat_ids); - for (uuid_vec_t::const_iterator it = cat_ids.begin(); - it != cat_ids.end(); ++it) - { - LLViewerInventoryCategory *cat = getCategory(*it); - cat_deltas[cat->getParentUUID()]--; - objects_deleted.insert(*it); - } - - // parse _categories_items_removed -> objects_deleted - uuid_vec_t item_ids; - parse_llsd_uuid_array(update,"_category_items_removed",item_ids); - for (uuid_vec_t::const_iterator it = item_ids.begin(); - it != item_ids.end(); ++it) - { - LLViewerInventoryItem *item = getItem(*it); - cat_deltas[item->getParentUUID()]--; - objects_deleted.insert(*it); - } - - // parse _broken_links_removed -> objects_deleted - uuid_vec_t broken_link_ids; - parse_llsd_uuid_array(update,"_broken_links_removed",broken_link_ids); - for (uuid_vec_t::const_iterator it = broken_link_ids.begin(); - it != broken_link_ids.end(); ++it) - { - LLViewerInventoryItem *item = getItem(*it); - cat_deltas[item->getParentUUID()]--; - objects_deleted.insert(*it); - } - - // parse _created_items - uuid_vec_t created_item_ids; - parse_llsd_uuid_array(update,"_created_items",created_item_ids); - - 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"]; - 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; - uuid_vec_t::const_iterator pos = - std::find(created_item_ids.begin(), - created_item_ids.end(),link_id); - if (pos != created_item_ids.end()) - { - 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; - items_created[link_id] = new_link; - const LLUUID& parent_id = new_link->getParentUUID(); - cat_deltas[parent_id]++; - } - else - { - llwarns << "failed to unpack" << llendl; - } - } - else - { - LL_DEBUGS("Inventory") << "Ignoring link not in created items list " << link_id << llendl; - } - } - } - else - { - llwarns << "unrecognized embedded field " << field << llendl; - } - } - - } - - // 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); - BOOL rv = new_item->unpackMessage(update); - if (rv) - { - items_updated[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. - cat_deltas[new_item->getParentUUID()]; - } - else - { - llerrs << "unpack failed" << llendl; - } - } - - // Do descendent/version accounting. - // Can remove this if/when we use the version info directly. - for (std::map<LLUUID,S32>::const_iterator catit = cat_deltas.begin(); - catit != cat_deltas.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. - 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(); - LLViewerInventoryCategory *cat = gInventory.getCategory(id); - if (cat->getVersion() != version) - { - llwarns << "Possible version mismatch, viewer " << cat->getVersion() - << " server " << version << llendl; - } - } - } - - // CREATE ITEMS - for (deferred_item_map_t::const_iterator create_it = items_created.begin(); - create_it != items_created.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 << llendl; - gInventory.updateItem(new_item); - } - - // UPDATE ITEMS - for (deferred_item_map_t::const_iterator update_it = items_updated.begin(); - update_it != items_updated.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 << llendl; - gInventory.updateItem(new_item); - } - - // DELETE OBJECTS - for (std::set<LLUUID>::const_iterator del_it = objects_deleted.begin(); - del_it != objects_deleted.end(); ++del_it) - { - LL_DEBUGS("Inventory") << "deleted item " << *del_it << llendl; - onObjectDeletedFromServer(*del_it, false, false); - } - + AISUpdate ais_update(update); // parse update llsd into stuff to do. + ais_update.doUpdate(); // execute the updates in the appropriate order. } void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version) diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 0608c46051..55575764b9 100755 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -31,6 +31,7 @@ #include "llsdserialize.h" #include "message.h" +#include "llaisapi.h" #include "llagent.h" #include "llagentcamera.h" #include "llagentwearables.h" @@ -258,269 +259,6 @@ public: }; LLInventoryHandler gInventoryHandler; - -///---------------------------------------------------------------------------- -/// Classes for AISv3 support. -///---------------------------------------------------------------------------- -class AISCommand: public LLHTTPClient::Responder -{ -public: - typedef boost::function<void()> command_func_type; - - AISCommand(LLPointer<LLInventoryCallback> callback): - mCallback(callback) - { - mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10); - } - - virtual ~AISCommand() - { - } - - void run_command() - { - mCommandFunc(); - } - - void setCommandFunc(command_func_type command_func) - { - mCommandFunc = command_func; - } - - // Need to do command-specific parsing to get an id here. May or - // may not need to bother, since most LLInventoryCallbacks do - // their work in the destructor. - virtual bool getResponseUUID(const LLSD& content, LLUUID& id) - { - return false; - } - - /* virtual */ void 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(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - mRetryPolicy->onSuccess(); - - gInventory.onAISUpdateReceived("AISCommand", content); - - if (mCallback) - { - LLUUID item_id; // will default to null if parse fails. - getResponseUUID(content,item_id); - mCallback->fire(item_id); - } - } - - /*virtual*/ void httpFailure() - { - const LLSD& content = getContent(); - S32 status = getStatus(); - const std::string& reason = getReason(); - const LLSD& headers = getResponseHeaders(); - if (!content.isMap()) - { - LL_DEBUGS("Inventory") << "Malformed response contents " << content - << " status " << status << " reason " << reason << llendl; - } - else - { - LL_DEBUGS("Inventory") << "failed with content: " << ll_pretty_print_sd(content) - << " status " << status << " reason " << reason << llendl; - } - 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. - setCommandFunc(no_op); - } - } - - static bool getCap(std::string& cap) - { - if (gAgent.getRegion()) - { - cap = gAgent.getRegion()->getCapability("InventoryAPIv3"); - } - if (!cap.empty()) - { - return true; - } - return false; - } - -private: - command_func_type mCommandFunc; - LLPointer<LLHTTPRetryPolicy> mRetryPolicy; - LLPointer<LLInventoryCallback> mCallback; -}; - -class RemoveItemCommand: public AISCommand -{ -public: - RemoveItemCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback): - AISCommand(callback) - { - std::string cap; - if (!getCap(cap)) - { - llwarns << "No cap found" << llendl; - return; - } - std::string url = cap + std::string("/item/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; - LLHTTPClient::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); - setCommandFunc(cmd); - } -}; - -class RemoveCategoryCommand: public AISCommand -{ -public: - RemoveCategoryCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback): - AISCommand(callback) - { - std::string cap; - if (!getCap(cap)) - { - llwarns << "No cap found" << llendl; - return; - } - std::string url = cap + std::string("/category/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; - LLHTTPClient::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); - setCommandFunc(cmd); - } -}; - -class PurgeDescendentsCommand: public AISCommand -{ -public: - PurgeDescendentsCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback): - AISCommand(callback) - { - std::string cap; - if (!getCap(cap)) - { - llwarns << "No cap found" << llendl; - return; - } - std::string url = cap + std::string("/category/") + item_id.asString() + "/children"; - LL_DEBUGS("Inventory") << "url: " << url << llendl; - LLCurl::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); - setCommandFunc(cmd); - } -}; - -class UpdateItemCommand: public AISCommand -{ -public: - UpdateItemCommand(const LLUUID& item_id, - const LLSD& updates, - LLPointer<LLInventoryCallback> callback): - mUpdates(updates), - AISCommand(callback) - { - std::string cap; - if (!getCap(cap)) - { - llwarns << "No cap found" << llendl; - return; - } - std::string url = cap + std::string("/item/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder, headers, timeout); - setCommandFunc(cmd); - } -private: - LLSD mUpdates; -}; - -class UpdateCategoryCommand: public AISCommand -{ -public: - UpdateCategoryCommand(const LLUUID& item_id, - const LLSD& updates, - LLPointer<LLInventoryCallback> callback): - mUpdates(updates), - AISCommand(callback) - { - std::string cap; - if (!getCap(cap)) - { - llwarns << "No cap found" << llendl; - return; - } - std::string url = cap + std::string("/category/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder, headers, timeout); - setCommandFunc(cmd); - } -private: - LLSD mUpdates; -}; - -class SlamFolderCommand: public AISCommand -{ -public: - SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback): - mContents(contents), - AISCommand(callback) - { - std::string cap; - if (!getCap(cap)) - { - llwarns << "No cap found" << llendl; - return; - } - LLUUID tid; - tid.generate(); - std::string url = cap + std::string("/category/") + folder_id.asString() + "/links?tid=" + tid.asString(); - llinfos << url << llendl; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::put, url, mContents, responder, headers, timeout); - setCommandFunc(cmd); - } -private: - LLSD mContents; -}; - ///---------------------------------------------------------------------------- /// Class LLViewerInventoryItem ///---------------------------------------------------------------------------- @@ -715,23 +453,9 @@ void LLViewerInventoryItem::setTransactionID(const LLTransactionID& transaction_ { mTransactionID = transaction_id; } -// virtual -void LLViewerInventoryItem::packMessage(LLMessageSystem* msg) const -{ - static const LLSD updates; - packUpdateMessage(msg,updates); -} -void LLViewerInventoryItem::packUpdateMessage(LLMessageSystem* msg, const LLSD& updates) const +void LLViewerInventoryItem::packMessage(LLMessageSystem* msg) const { - for (LLSD::map_const_iterator it = updates.beginMap(); it != updates.endMap(); ++it) - { - if ((it->first != "desc") && (it->first != "name")) - { - llerrs << "unhandled field: " << it->first << llendl; - } - } - msg->addUUIDFast(_PREHASH_ItemID, mUUID); msg->addUUIDFast(_PREHASH_FolderID, mParentUUID); mPermissions.packMessage(msg); @@ -742,26 +466,8 @@ void LLViewerInventoryItem::packUpdateMessage(LLMessageSystem* msg, const LLSD& msg->addS8Fast(_PREHASH_InvType, type); msg->addU32Fast(_PREHASH_Flags, mFlags); mSaleInfo.packMessage(msg); - if (updates.has("name")) - { - std::string new_name = updates["name"].asString(); - LLInventoryObject::correctInventoryName(new_name); - msg->addStringFast(_PREHASH_Name, new_name); - } - else - { - msg->addStringFast(_PREHASH_Name, mName); - } - if (updates.has("desc")) - { - std::string new_desc = updates["desc"].asString(); - LLInventoryItem::correctInventoryDescription(new_desc); - msg->addStringFast(_PREHASH_Description, new_desc); - } - else - { - msg->addStringFast(_PREHASH_Description, mDescription); - } + msg->addStringFast(_PREHASH_Name, mName); + msg->addStringFast(_PREHASH_Description, mDescription); msg->addS32Fast(_PREHASH_CreationDate, mCreationDate); U32 crc = getCRC32(); msg->addU32Fast(_PREHASH_CRC, crc); @@ -878,30 +584,13 @@ void LLViewerInventoryCategory::copyViewerCategory(const LLViewerInventoryCatego } -void LLViewerInventoryCategory::packUpdateMessage(LLMessageSystem* msg, const LLSD& updates) const +void LLViewerInventoryCategory::packMessage(LLMessageSystem* msg) const { - for (LLSD::map_const_iterator it = updates.beginMap(); it != updates.endMap(); ++it) - { - if (it->first != "name") - { - llerrs << "unhandled field: " << it->first << llendl; - } - } - msg->addUUIDFast(_PREHASH_FolderID, mUUID); msg->addUUIDFast(_PREHASH_ParentID, mParentUUID); S8 type = static_cast<S8>(mPreferredType); msg->addS8Fast(_PREHASH_Type, type); - if (updates.has("name")) - { - std::string new_name = updates["name"].asString(); - LLInventoryObject::correctInventoryName(new_name); - msg->addStringFast(_PREHASH_Name, new_name); - } - else - { - msg->addStringFast(_PREHASH_Name, mName); - } + msg->addStringFast(_PREHASH_Name, mName); } void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const @@ -1472,10 +1161,16 @@ void update_inventory_item( 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); + std::string cap; if (AISCommand::getCap(cap)) { - LLPointer<AISCommand> cmd_ptr = new UpdateItemCommand(item_id, updates, cb); + 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 @@ -1485,13 +1180,15 @@ void update_inventory_item( msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_TransactionID, obj->getTransactionID()); + msg->addUUIDFast(_PREHASH_TransactionID, new_item->getTransactionID()); msg->nextBlockFast(_PREHASH_InventoryData); msg->addU32Fast(_PREHASH_CallbackID, 0); - obj->packUpdateMessage(msg, updates); + new_item->packMessage(msg); gAgent.sendReliableMessage(); - gInventory.onItemUpdated(item_id, updates,true); + LLInventoryModel::LLCategoryUpdate up(new_item->getParentUUID(), 0); + gInventory.accountForUpdate(up); + gInventory.updateItem(new_item); if (cb) { cb->fire(item_id); @@ -1515,11 +1212,14 @@ void update_inventory_category( return; } + 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)) { - LLPointer<AISCommand> cmd_ptr = new UpdateCategoryCommand(cat_id, updates, cb); + LLSD new_llsd = new_cat->asLLSD(); + LLPointer<AISCommand> cmd_ptr = new UpdateCategoryCommand(cat_id, new_llsd, cb); cmd_ptr->run_command(); } else // no cap @@ -1530,10 +1230,12 @@ void update_inventory_category( msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_FolderData); - obj->packUpdateMessage(msg, updates); + new_cat->packMessage(msg); gAgent.sendReliableMessage(); - gInventory.onCategoryUpdated(cat_id, updates); + LLInventoryModel::LLCategoryUpdate up(new_cat->getParentUUID(), 0); + gInventory.accountForUpdate(up); + gInventory.updateCategory(new_cat); if (cb) { cb->fire(cat_id); diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 9af71dfc9c..032efd9542 100755 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -122,7 +122,7 @@ public: virtual void updateServer(BOOL is_new) const; void fetchFromServer(void) const; - //virtual void packMessage(LLMessageSystem* msg) const; + 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 importFile(LLFILE* fp); @@ -138,9 +138,6 @@ public: void setComplete(BOOL complete) { mIsComplete = complete; } //void updateAssetOnServer() const; - virtual void packMessage(LLMessageSystem* msg) const; - // Contents of updates will take precedence over fields of item where they differ. - void packUpdateMessage(LLMessageSystem* msg, const LLSD& updates) const; virtual void setTransactionID(const LLTransactionID& transaction_id); struct comparePointers { @@ -202,6 +199,8 @@ public: virtual void updateParentOnServer(BOOL restamp_children) const; virtual void updateServer(BOOL is_new) const; + virtual void packMessage(LLMessageSystem* msg) const; + const LLUUID& getOwnerID() const { return mOwnerID; } // Version handling @@ -226,8 +225,6 @@ public: void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); - void packUpdateMessage(LLMessageSystem* msg, const LLSD& updates) const; - private: friend class LLInventoryModel; void localizeName(); // intended to be called from the LLInventoryModel |