summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rwxr-xr-xindra/newview/CMakeLists.txt2
-rwxr-xr-xindra/newview/llaisapi.cpp482
-rwxr-xr-xindra/newview/llaisapi.h143
-rwxr-xr-xindra/newview/llinventorymodel.cpp206
-rwxr-xr-xindra/newview/llviewerinventory.cpp350
-rwxr-xr-xindra/newview/llviewerinventory.h9
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