From 80f61963272cc2311c5410671045e34212476134 Mon Sep 17 00:00:00 2001 From: Kartic Krishnamurthy Date: Tue, 16 Sep 2008 18:12:32 +0000 Subject: QAR-751 : Merge Agent Inventory Services - Fetch Inventory and Fetch Inventory Descendents - to Release Related Jiras: 1. DEV-17797 New Top Causes of Inventory Loss is Attachments with null folder_id 2. DEV-17937 null asset id not handled correctly in ais inventory fetch 3. OPSRT-1097 Update python-indra package on the system images 4. DEV-20505 QAR-751: Banning Cap "FetchLibDescendents" results in error in viewer log. 5. DEV-20328 QAR-751 Excessive log spam when using ais viewer against ais sim 6. DEV-20335 QAR-751 AIS bans are missing from message.xml svn merge -r95983:96590 svn+ssh://svn/svn/linden/branches/ais-for-merge-qar-751 . Miscellaneous commit: 1. Fixed silly bug in llsd-rest that broke connects to https on a non-standard port --- indra/lib/python/indra/base/llsd.py | 142 +++++--- indra/lib/python/indra/util/fastest_elementtree.py | 29 +- indra/llcommon/indra_constants.h | 1 + indra/llinventory/llinventory.cpp | 43 ++- indra/llinventory/llsaleinfo.cpp | 11 +- indra/llmessage/llpumpio.cpp | 18 + indra/llmessage/llpumpio.h | 9 + indra/llmessage/llurlrequest.cpp | 23 +- indra/newview/llinventorymodel.cpp | 393 +++++++++++++++------ indra/newview/llinventorymodel.h | 18 +- indra/newview/llstartup.cpp | 3 + indra/newview/llviewerinventory.cpp | 41 ++- indra/newview/llviewerregion.cpp | 5 +- 13 files changed, 562 insertions(+), 174 deletions(-) diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py index c841cdaf63..521b79c65a 100644 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py @@ -4,7 +4,7 @@ $LicenseInfo:firstyear=2006&license=mit$ -Copyright (c) 2006-2007, Linden Research, Inc. +Copyright (c) 2006-2008, Linden Research, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -33,19 +33,26 @@ import time import types import re -from indra.util.fastest_elementtree import fromstring +from indra.util.fastest_elementtree import ElementTreeError, fromstring from indra.base import lluuid -int_regex = re.compile("[-+]?\d+") -real_regex = re.compile("[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") -alpha_regex = re.compile("[a-zA-Z]+") -date_regex = re.compile("(?P\d{4})-(?P\d{2})-(?P\d{2})T(?P\d{2}):(?P\d{2}):(?P\d{2})(?P\.\d{2})?Z") +try: + import cllsd +except ImportError: + cllsd = None + +int_regex = re.compile(r"[-+]?\d+") +real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") +alpha_regex = re.compile(r"[a-zA-Z]+") +date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" + r"(?P\d{2}):(?P\d{2}):(?P\d{2})" + r"(?P(\.\d+)?)Z") #date: d"YYYY-MM-DDTHH:MM:SS.FFZ" class LLSDParseError(Exception): pass -class LLSDSerializationError(Exception): +class LLSDSerializationError(TypeError): pass @@ -64,7 +71,7 @@ def format_datestr(v): """ Formats a datetime object into the string format shared by xml and notation serializations.""" second_str = "" if v.microsecond > 0: - seconds = v.second + float(v.microsecond) / 1000000 + seconds = v.second + float(v.microsecond) / 1e6 second_str = "%05.2f" % seconds else: second_str = "%02d" % v.second @@ -89,7 +96,7 @@ def parse_datestr(datestr): seconds_float = match.group('second_float') microsecond = 0 if seconds_float: - microsecond = int(seconds_float[1:]) * 10000 + microsecond = int(float('0' + seconds_float) * 1e6) return datetime.datetime(year, month, day, hour, minute, second, microsecond) @@ -116,7 +123,7 @@ def uuid_to_python(node): return lluuid.UUID(node.text) def str_to_python(node): - return unicode(node.text or '').encode('utf8', 'replace') + return node.text or '' def bin_to_python(node): return binary(base64.decodestring(node.text or '')) @@ -189,9 +196,13 @@ class LLSDXMLFormatter(object): if(contents is None or contents is ''): return "<%s />" % (name,) else: + if type(contents) is unicode: + contents = contents.encode('utf-8') return "<%s>%s" % (name, contents, name) def xml_esc(self, v): + if type(v) is unicode: + v = v.encode('utf-8') return v.replace('&', '&').replace('<', '<').replace('>', '>') def LLSD(self, v): @@ -237,9 +248,14 @@ class LLSDXMLFormatter(object): raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( t, something)) - def format(self, something): + def _format(self, something): return '' + self.elt("llsd", self.generate(something)) + def format(self, something): + if cllsd: + return cllsd.llsd_to_xml(something) + return self._format(something) + _g_xml_formatter = None def format_xml(something): global _g_xml_formatter @@ -356,8 +372,10 @@ class LLSDNotationFormatter(object): def UUID(self, v): return "u%s" % v def BINARY(self, v): - raise LLSDSerializationError("binary notation not yet supported") + return 'b64"' + base64.encodestring(v) + '"' def STRING(self, v): + if isinstance(v, unicode): + v = v.encode('utf-8') return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") def URI(self, v): return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') @@ -366,16 +384,24 @@ class LLSDNotationFormatter(object): def ARRAY(self, v): return "[%s]" % ','.join([self.generate(item) for item in v]) def MAP(self, v): - return "{%s}" % ','.join(["'%s':%s" % (key.replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) + def fix(key): + if isinstance(key, unicode): + return key.encode('utf-8') + return key + return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) for key, value in v.items()]) def generate(self, something): t = type(something) - if self.type_map.has_key(t): - return self.type_map[t](something) + handler = self.type_map.get(t) + if handler: + return handler(something) else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - t, something)) + try: + return self.ARRAY(iter(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % (t, something)) def format(self, something): return self.generate(something) @@ -479,7 +505,6 @@ class LLSDBinaryParser(object): raise LLSDParseError("invalid map key at byte %d." % ( self._index - 1,)) value = self._parse() - #print "kv:",key,value rv[key] = value count += 1 cc = self._buffer[self._index] @@ -636,11 +661,23 @@ class LLSDNotationParser(object): # 'd' = date in seconds since epoch return self._parse_date() elif cc == 'b': - raise LLSDParseError("binary notation not yet supported") + return self._parse_binary() else: raise LLSDParseError("invalid token at index %d: %d" % ( self._index - 1, ord(cc))) + def _parse_binary(self): + i = self._index + if self._buffer[i:i+2] == '64': + q = self._buffer[i+2] + e = self._buffer.find(q, i+3) + try: + return base64.decodestring(self._buffer[i+3:e]) + finally: + self._index = e + 1 + else: + raise LLSDParseError('random horrible binary format not supported') + def _parse_map(self): """ map: { string:object, string:object } """ rv = {} @@ -653,30 +690,23 @@ class LLSDNotationParser(object): if cc in ("'", '"', 's'): key = self._parse_string(cc) found_key = True - #print "key:",key elif cc.isspace() or cc == ',': cc = self._buffer[self._index] self._index += 1 else: raise LLSDParseError("invalid map key at byte %d." % ( self._index - 1,)) + elif cc.isspace() or cc == ':': + cc = self._buffer[self._index] + self._index += 1 + continue else: - if cc.isspace() or cc == ':': - #print "skipping whitespace '%s'" % cc - cc = self._buffer[self._index] - self._index += 1 - continue self._index += 1 value = self._parse() - #print "kv:",key,value rv[key] = value found_key = False cc = self._buffer[self._index] self._index += 1 - #if cc == '}': - # break - #cc = self._buffer[self._index] - #self._index += 1 return rv @@ -840,6 +870,14 @@ def format_binary(something): return '\n' + _format_binary_recurse(something) def _format_binary_recurse(something): + def _format_list(something): + array_builder = [] + array_builder.append('[' + struct.pack('!i', len(something))) + for item in something: + array_builder.append(_format_binary_recurse(item)) + array_builder.append(']') + return ''.join(array_builder) + if something is None: return '!' elif isinstance(something, LLSD): @@ -857,7 +895,10 @@ def _format_binary_recurse(something): return 'u' + something._bits elif isinstance(something, binary): return 'b' + struct.pack('!i', len(something)) + something - elif isinstance(something, (str, unicode)): + elif isinstance(something, str): + return 's' + struct.pack('!i', len(something)) + something + elif isinstance(something, unicode): + something = something.encode('utf-8') return 's' + struct.pack('!i', len(something)) + something elif isinstance(something, uri): return 'l' + struct.pack('!i', len(something)) + something @@ -865,35 +906,50 @@ def _format_binary_recurse(something): seconds_since_epoch = time.mktime(something.timetuple()) return 'd' + struct.pack('!d', seconds_since_epoch) elif isinstance(something, (list, tuple)): - array_builder = [] - array_builder.append('[' + struct.pack('!i', len(something))) - for item in something: - array_builder.append(_format_binary_recurse(item)) - array_builder.append(']') - return ''.join(array_builder) + return _format_list(something) elif isinstance(something, dict): map_builder = [] map_builder.append('{' + struct.pack('!i', len(something))) for key, value in something.items(): + if isinstance(key, unicode): + key = key.encode('utf-8') map_builder.append('k' + struct.pack('!i', len(key)) + key) map_builder.append(_format_binary_recurse(value)) map_builder.append('}') return ''.join(map_builder) else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - type(something), something)) + try: + return _format_list(list(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % + (type(something), something)) + + +def parse_binary(something): + header = '\n' + if not something.startswith(header): + raise LLSDParseError('LLSD binary encoding header not found') + return LLSDBinaryParser().parse(something[len(header):]) + +def parse_xml(something): + try: + return to_python(fromstring(something)[0]) + except ElementTreeError, err: + raise LLSDParseError(*err.args) +def parse_notation(something): + return LLSDNotationParser().parse(something) def parse(something): try: if something.startswith(''): - just_binary = something.split('\n', 1)[1] - return LLSDBinaryParser().parse(just_binary) + return parse_binary(something) # This should be better. elif something.startswith('<'): - return to_python(fromstring(something)[0]) + return parse_xml(something) else: - return LLSDNotationParser().parse(something) + return parse_notation(something) except KeyError, e: raise Exception('LLSD could not be parsed: %s' % (e,)) diff --git a/indra/lib/python/indra/util/fastest_elementtree.py b/indra/lib/python/indra/util/fastest_elementtree.py index 228be49ed3..24701438f0 100644 --- a/indra/lib/python/indra/util/fastest_elementtree.py +++ b/indra/lib/python/indra/util/fastest_elementtree.py @@ -2,9 +2,9 @@ @file fastest_elementtree.py @brief Concealing some gnarly import logic in here. This should export the interface of elementtree. -$LicenseInfo:firstyear=2006&license=mit$ +$LicenseInfo:firstyear=2008&license=mit$ -Copyright (c) 2006-2007, Linden Research, Inc. +Copyright (c) 2008, Linden Research, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,25 +26,40 @@ THE SOFTWARE. $/LicenseInfo$ """ -# Using celementree might cause some unforeseen problems so here's a +# The parsing exception raised by the underlying library depends +# on the ElementTree implementation we're using, so we provide an +# alias here. +# +# Use ElementTreeError as the exception type for catching parsing +# errors. + + +# Using cElementTree might cause some unforeseen problems, so here's a # convenient off switch. + use_celementree = True try: if not use_celementree: raise ImportError() - from cElementTree import * ## This does not work under Windows + # Python 2.3 and 2.4. + from cElementTree import * + ElementTreeError = SyntaxError except ImportError: try: if not use_celementree: raise ImportError() - ## This is the name of cElementTree under python 2.5 + # Python 2.5 and above. from xml.etree.cElementTree import * + ElementTreeError = SyntaxError except ImportError: + # Pure Python code. try: - ## This is the old name of elementtree, for use with 2.3 + # Python 2.3 and 2.4. from elementtree.ElementTree import * except ImportError: - ## This is the name of elementtree under python 2.5 + # Python 2.5 and above. from xml.etree.ElementTree import * + # The pure Python ElementTree module uses Expat for parsing. + from xml.parsers.expat import ExpatError as ElementTreeError diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index e83da12beb..a3e3aec360 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -251,6 +251,7 @@ const U8 GOD_NOT = 0; const LLUUID LL_UUID_ALL_AGENTS("44e87126-e794-4ded-05b3-7c42da3d5cdb"); // Governor Linden's agent id. +const LLUUID ALEXANDRIA_LINDEN_ID("ba2a564a-f0f1-4b82-9c61-b7520bfcd09f"); const LLUUID GOVERNOR_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1"); const LLUUID REALESTATE_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1"); // Maintenance's group id. diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 0cdc445069..a126ae4fbf 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -60,6 +60,9 @@ static const std::string INV_SALE_INFO_LABEL("sale_info"); static const std::string INV_FLAGS_LABEL("flags"); static const std::string INV_CREATION_DATE_LABEL("created_at"); +// key used by agent-inventory-service +static const std::string INV_ASSET_TYPE_LABEL_WS("type_default"); +static const std::string INV_FOLDER_ID_LABEL_WS("category_id"); ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -949,11 +952,13 @@ LLSD LLInventoryItem::asLLSD() const sd[INV_SHADOW_ID_LABEL] = shadow_id; } sd[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType); + sd[INV_INVENTORY_TYPE_LABEL] = mInventoryType; const char* inv_type_str = LLInventoryType::lookup(mInventoryType); if(inv_type_str) { sd[INV_INVENTORY_TYPE_LABEL] = inv_type_str; } + //sd[INV_FLAGS_LABEL] = (S32)mFlags; sd[INV_FLAGS_LABEL] = ll_sd_from_U32(mFlags); sd[INV_SALE_INFO_LABEL] = mSaleInfo; sd[INV_NAME_LABEL] = mName; @@ -1026,17 +1031,40 @@ bool LLInventoryItem::fromLLSD(LLSD& sd) w = INV_ASSET_TYPE_LABEL; if (sd.has(w)) { - mType = LLAssetType::lookup(sd[w].asString()); + if (sd[w].isString()) + { + mType = LLAssetType::lookup(sd[w].asString().c_str()); + } + else if (sd[w].isInteger()) + { + S8 type = (U8)sd[w].asInteger(); + mType = static_cast(type); + } } w = INV_INVENTORY_TYPE_LABEL; if (sd.has(w)) { - mInventoryType = LLInventoryType::lookup(sd[w].asString()); + if (sd[w].isString()) + { + mInventoryType = LLInventoryType::lookup(sd[w].asString().c_str()); + } + else if (sd[w].isInteger()) + { + S8 type = (U8)sd[w].asInteger(); + mInventoryType = static_cast(type); + } } w = INV_FLAGS_LABEL; if (sd.has(w)) { - mFlags = ll_U32_from_sd(sd[w]); + if (sd[w].isBinary()) + { + mFlags = ll_U32_from_sd(sd[w]); + } + else if(sd[w].isInteger()) + { + mFlags = sd[w].asInteger(); + } } w = INV_NAME_LABEL; if (sd.has(w)) @@ -1394,7 +1422,7 @@ bool LLInventoryCategory::fromLLSD(LLSD& sd) { std::string w; - w = INV_ITEM_ID_LABEL; + w = INV_FOLDER_ID_LABEL_WS; if (sd.has(w)) { mUUID = sd[w]; @@ -1410,6 +1438,13 @@ bool LLInventoryCategory::fromLLSD(LLSD& sd) S8 type = (U8)sd[w].asInteger(); mPreferredType = static_cast(type); } + w = INV_ASSET_TYPE_LABEL_WS; + if (sd.has(w)) + { + S8 type = (U8)sd[w].asInteger(); + mPreferredType = static_cast(type); + } + w = INV_NAME_LABEL; if (sd.has(w)) { diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index 111167ae27..9c36f0b7a0 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -114,7 +114,16 @@ bool LLSaleInfo::fromLLSD(LLSD& sd, BOOL& has_perm_mask, U32& perm_mask) { const char *w; - mSaleType = lookup(sd["sale_type"].asString().c_str()); + if (sd["sale_type"].isString()) + { + mSaleType = lookup(sd["sale_type"].asString().c_str()); + } + else if(sd["sale_type"].isInteger()) + { + S8 type = (U8)sd["sale_type"].asInteger(); + mSaleType = static_cast(type); + } + mSalePrice = llclamp(sd["sale_price"].asInteger(), 0, S32_MAX); w = "perm_mask"; if (sd.has(w)) diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index 503ca947df..284a7141d0 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -269,6 +269,13 @@ bool LLPumpIO::setTimeoutSeconds(F32 timeout) return true; } +void LLPumpIO::adjustTimeoutSeconds(F32 delta) +{ + // If no chain is running, bail + if(current_chain_t() == mCurrentChain) return; + (*mCurrentChain).adjustTimeoutSeconds(delta); +} + static std::string events_2_string(apr_int16_t events) { std::ostringstream ostr; @@ -1161,3 +1168,14 @@ void LLPumpIO::LLChainInfo::setTimeoutSeconds(F32 timeout) mTimer.stop(); } } + +void LLPumpIO::LLChainInfo::adjustTimeoutSeconds(F32 delta) +{ + LLMemType m1(LLMemType::MTYPE_IO_PUMP); + if(mTimer.getStarted()) + { + F64 expiry = mTimer.expiresAt(); + expiry += delta; + mTimer.setExpiryAt(expiry); + } +} diff --git a/indra/llmessage/llpumpio.h b/indra/llmessage/llpumpio.h index fe7012ad6c..2a02bad89e 100644 --- a/indra/llmessage/llpumpio.h +++ b/indra/llmessage/llpumpio.h @@ -165,6 +165,14 @@ public: */ bool setTimeoutSeconds(F32 timeout); + /** + * @brief Adjust the timeout of the running chain. + * + * This method has no effect if there is no timeout on the chain. + * @param delta The number of seconds to add to/remove from the timeout. + */ + void adjustTimeoutSeconds(F32 delta); + /** * @brief Set up file descriptors for for the running chain. * @see rebuildPollset() @@ -349,6 +357,7 @@ protected: // methods LLChainInfo(); void setTimeoutSeconds(F32 timeout); + void adjustTimeoutSeconds(F32 delta); // basic member data bool mInit; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 6e5fa6def8..ff6ec9f077 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -68,6 +68,7 @@ public: LLChannelDescriptors mChannels; U8* mLastRead; U32 mBodyLimit; + S32 mByteAccumulator; bool mIsBodyLimitSet; }; @@ -76,8 +77,8 @@ LLURLRequestDetail::LLURLRequestDetail() : mResponseBuffer(NULL), mLastRead(NULL), mBodyLimit(0), + mByteAccumulator(0), mIsBodyLimitSet(false) - { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mCurlRequest = new LLCurlEasyRequest(); @@ -264,8 +265,25 @@ LLIOPipe::EStatus LLURLRequest::process_impl( { CURLcode result; bool newmsg = mDetail->mCurlRequest->getResult(&result); - if (!newmsg) + if(!newmsg) { + // we're still waiting or prcessing, check how many + // bytes we have accumulated. + const S32 MIN_ACCUMULATION = 100000; + if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) + { + // This is a pretty sloppy calculation, but this + // tries to make the gross assumption that if data + // is coming in at 56kb/s, then this transfer will + // probably succeed. So, if we're accumlated + // 100,000 bytes (MIN_ACCUMULATION) then let's + // give this client another 2s to complete. + const F32 TIMEOUT_ADJUSTMENT = 2.0f; + mDetail->mByteAccumulator = 0; + pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); + } + + // keep processing break; } @@ -434,6 +452,7 @@ size_t LLURLRequest::downCallback( req->mDetail->mChannels.out(), (U8*)data, bytes); + req->mDetail->mByteAccumulator += bytes; return bytes; } diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 9ee3840f7f..b56629b60a 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -84,7 +84,7 @@ static std::deque sFetchQueue; //BOOL decompress_file(const char* src_filename, const char* dst_filename); const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f; -const S32 MAX_FETCH_RETRIES = 5; +const S32 MAX_FETCH_RETRIES = 10; const char CACHE_FORMAT_STRING[] = "%s.inv"; const char* NEW_CATEGORY_NAME = "New Folder"; const char* NEW_CATEGORY_NAMES[LLAssetType::AT_COUNT] = @@ -989,13 +989,24 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) // Call this method when it's time to update everyone on a new state, // by default, the inventory model will not update observers // automatically. -void LLInventoryModel::notifyObservers() +// The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] +void LLInventoryModel::notifyObservers(const std::string service_name) { for (observer_list_t::iterator iter = mObservers.begin(); iter != mObservers.end(); ) { LLInventoryObserver* observer = *iter; - observer->changed(mModifyMask); + + if (service_name.empty()) + { + observer->changed(mModifyMask); + } + else + { + observer->mMessageName = service_name; + observer->changed(mModifyMask); + } + // safe way to incrament since changed may delete entries! (@!##%@!@&*!) iter = mObservers.upper_bound(observer); } @@ -1037,6 +1048,79 @@ void LLInventoryModel::mock(const LLUUID& root_id) } */ +//If we get back a normal response, handle it here +void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) +{ + start_new_inventory_observer(); + + /*LLUUID agent_id; + agent_id = content["agent_id"].asUUID(); + if(agent_id != gAgent.getID()) + { + llwarns << "Got a inventory update for the wrong agent: " << agent_id + << llendl; + return; + }*/ + item_array_t items; + update_map_t update; + S32 count = content["items"].size(); + bool all_one_folder = true; + LLUUID folder_id; + // Does this loop ever execute more than once? -Gigs + for(S32 i = 0; i < count; ++i) + { + LLPointer titem = new LLViewerInventoryItem; + titem->unpackMessage(content["items"][i]); + + lldebugs << "LLInventoryModel::messageUpdateCore() item id:" + << titem->getUUID() << llendl; + items.push_back(titem); + // examine update for changes. + LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); + if(itemp) + { + if(titem->getParentUUID() == itemp->getParentUUID()) + { + update[titem->getParentUUID()]; + } + else + { + ++update[titem->getParentUUID()]; + --update[itemp->getParentUUID()]; + } + } + else + { + ++update[titem->getParentUUID()]; + } + if (folder_id.isNull()) + { + folder_id = titem->getParentUUID(); + } + else + { + all_one_folder = false; + } + } + + U32 changes = 0x0; + //as above, this loop never seems to loop more than once per call + for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) + { + changes |= gInventory.updateItem(*it); + } + gInventory.notifyObservers("fetchinventory"); + gViewerWindow->getWindow()->decBusyCount(); +} + +//If we get back an error (not found, etc...), handle it here +void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason) +{ + llinfos << "fetchInventory::error " + << status << ": " << reason << llendl; + gInventory.notifyObservers("fetchinventory"); +} + void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) { LLViewerInventoryCategory* cat = getCategory(folder_id); @@ -1085,9 +1169,10 @@ class fetchDescendentsResponder: public LLHTTPClient::Responder //If we get back a normal response, handle it here void fetchDescendentsResponder::result(const LLSD& content) -{ +{ if (content.has("folders")) { + for(LLSD::array_const_iterator folder_it = content["folders"].beginArray(); folder_it != content["folders"].endArray(); ++folder_it) @@ -1095,21 +1180,49 @@ void fetchDescendentsResponder::result(const LLSD& content) LLSD folder_sd = *folder_it; - LLUUID agent_id = folder_sd["agent-id"]; + //LLUUID agent_id = folder_sd["agent_id"]; - if(agent_id != gAgent.getID()) //This should never happen. - { - llwarns << "Got a UpdateInventoryItem for the wrong agent." - << llendl; - continue; - } - LLUUID parent_id = folder_sd["folder-id"]; - LLUUID owner_id = folder_sd["owner-id"]; + //if(agent_id != gAgent.getID()) //This should never happen. + //{ + // llwarns << "Got a UpdateInventoryItem for the wrong agent." + // << llendl; + // break; + //} + + LLUUID parent_id = folder_sd["folder_id"]; + LLUUID owner_id = folder_sd["owner_id"]; S32 version = (S32)folder_sd["version"].asInteger(); S32 descendents = (S32)folder_sd["descendents"].asInteger(); LLPointer tcategory = new LLViewerInventoryCategory(owner_id); - LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id); + if (parent_id.isNull()) + { + LLPointer titem = new LLViewerInventoryItem; + for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); + item_it != folder_sd["items"].endArray(); + ++item_it) + { + LLUUID lost_uuid = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); + if (lost_uuid.notNull()) + { + LLSD item = *item_it; + titem->unpackMessage(item); + + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + titem->setParent(lost_uuid); + titem->updateParentOnServer(FALSE); + gInventory.updateItem(titem); + gInventory.notifyObservers("fetchDescendents"); + + } + } + } + + LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id); if (!pcat) { continue; @@ -1154,16 +1267,16 @@ void fetchDescendentsResponder::result(const LLSD& content) } } - if (content.has("bad-folders")) + if (content.has("bad_folders")) { - for(LLSD::array_const_iterator folder_it = content["bad-folders"].beginArray(); - folder_it != content["bad-folders"].endArray(); + for(LLSD::array_const_iterator folder_it = content["bad_folders"].beginArray(); + folder_it != content["bad_folders"].endArray(); ++folder_it) { LLSD folder_sd = *folder_it; //These folders failed on the dataserver. We probably don't want to retry them. - llinfos << "Folder " << folder_sd["folder-id"].asString() + llinfos << "Folder " << folder_sd["folder_id"].asString() << "Error: " << folder_sd["error"].asString() << llendl; } } @@ -1180,7 +1293,7 @@ void fetchDescendentsResponder::result(const LLSD& content) LLInventoryModel::stopBackgroundFetch(); } - gInventory.notifyObservers(); + gInventory.notifyObservers("fetchDescendents"); } //If we get back an error (not found, etc...), handle it here @@ -1198,7 +1311,7 @@ void fetchDescendentsResponder::error(U32 status, const std::string& reason) ++folder_it) { LLSD folder_sd = *folder_it; - LLUUID folder_id = folder_sd["folder-id"]; + LLUUID folder_id = folder_sd["folder_id"]; sFetchQueue.push_front(folder_id); } } @@ -1213,7 +1326,7 @@ void fetchDescendentsResponder::error(U32 status, const std::string& reason) LLInventoryModel::stopBackgroundFetch(); } } - gInventory.notifyObservers(); + gInventory.notifyObservers("fetchDescendents"); } //static Bundle up a bunch of requests to send all at once. @@ -1241,49 +1354,75 @@ void LLInventoryModel::bulkFetch(std::string url) U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; LLSD body; - + LLSD body_lib; while( !(sFetchQueue.empty() ) && (folder_count < max_batch_size) ) { - LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); - - if (cat) - { - if ( LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - LLSD folder_sd; - folder_sd["folder-id"] = cat->getUUID(); - folder_sd["owner-id"] = cat->getOwnerID(); - folder_sd["sort-order"] = (LLSD::Integer)sort_order; - folder_sd["fetch-folders"] = (LLSD::Boolean)sFullFetchStarted; - folder_sd["fetch-items"] = (LLSD::Boolean)TRUE; - body["folders"].append(folder_sd); - - folder_count++; - } - else if (sFullFetchStarted) - { //Already have this folder but append child folders to list. - // add all children to queue - parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); - if (cat_it != gInventory.mParentChildCategoryTree.end()) - { - cat_array_t* child_categories = cat_it->second; - - for (S32 child_num = 0; child_num < child_categories->count(); child_num++) - { - sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); - } - } + if (sFetchQueue.front().isNull()) //DEV-17797 + { + LLSD folder_sd; + folder_sd["folder_id"] = LLUUID::null.asString(); + folder_sd["owner_id"] = gAgent.getID(); + folder_sd["sort_order"] = (LLSD::Integer)sort_order; + folder_sd["fetch_folders"] = (LLSD::Boolean)FALSE; + folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; + body["folders"].append(folder_sd); + folder_count++; + } + else + { + - } - } + LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); + + if (cat) + { + if ( LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) + { + LLSD folder_sd; + folder_sd["folder_id"] = cat->getUUID(); + folder_sd["owner_id"] = cat->getOwnerID(); + folder_sd["sort_order"] = (LLSD::Integer)sort_order; + folder_sd["fetch_folders"] = TRUE; //(LLSD::Boolean)sFullFetchStarted; + folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; + + if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + body_lib["folders"].append(folder_sd); + else + body["folders"].append(folder_sd); + folder_count++; + } + if (sFullFetchStarted) + { //Already have this folder but append child folders to list. + // add all children to queue + parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); + if (cat_it != gInventory.mParentChildCategoryTree.end()) + { + cat_array_t* child_categories = cat_it->second; + + for (S32 child_num = 0; child_num < child_categories->count(); child_num++) + { + sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); + } + } + + } + } + } sFetchQueue.pop_front(); } if (folder_count > 0) { sBulkFetchCount++; - - LLHTTPClient::post(url, body, new fetchDescendentsResponder(body)); + if (body["folders"].size()) + { + LLHTTPClient::post(url, body, new fetchDescendentsResponder(body),300.0); + } + if (body_lib["folders"].size()) + { + std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents"); + LLHTTPClient::post(url_lib, body_lib, new fetchDescendentsResponder(body_lib),300.0); + } sFetchTimer.reset(); } else if (isBulkFetchProcessingComplete()) @@ -1336,6 +1475,14 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id) } } +//static +void LLInventoryModel::findLostItems() +{ + sBackgroundFetchActive = TRUE; + sFetchQueue.push_back(LLUUID::null); + gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); +} + //static void LLInventoryModel::stopBackgroundFetch() { @@ -1355,7 +1502,7 @@ void LLInventoryModel::backgroundFetch(void*) if (sBackgroundFetchActive) { //If we'll be using the capability, we'll be sending batches and the background thing isn't as important. - std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); + std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents"); if (!url.empty()) { bulkFetch(url); @@ -3312,12 +3459,72 @@ bool LLInventoryFetchObserver::isEverythingComplete() const return mIncomplete.empty(); } +void fetch_items_from_llsd(const LLSD& items_llsd) +{ + if (!items_llsd.size()) return; + LLSD body; + body[0]["cap_name"] = "FetchInventory"; + body[1]["cap_name"] = "FetchLib"; + for (S32 i=0; i= body[i].size()) continue; + std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); + + if (!url.empty()) + { + body[i]["agent_id"] = gAgent.getID(); + LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); + break; + } + + LLMessageSystem* msg = gMessageSystem; + BOOL start_new_message = TRUE; + for (S32 j=0; jnewMessageFast(_PREHASH_FetchInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID()); + msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID()); + if(msg->isSendFull(NULL)) + { + start_new_message = TRUE; + gAgent.sendReliableMessage(); + } + } + if(!start_new_message) + { + gAgent.sendReliableMessage(); + } + } +} + void LLInventoryFetchObserver::fetchItems( const LLInventoryFetchObserver::item_ref_t& ids) { - LLMessageSystem* msg = gMessageSystem; - BOOL start_new_message = TRUE; LLUUID owner_id; + LLSD items_llsd; for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it) { LLViewerInventoryItem* item = gInventory.getItem(*it); @@ -3339,31 +3546,18 @@ void LLInventoryFetchObserver::fetchItems( // assume it's agent inventory. owner_id = gAgent.getID(); } - + // It's incomplete, so put it on the incomplete container, and // pack this on the message. mIncomplete.push_back(*it); - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_FetchInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_OwnerID, owner_id); - msg->addUUIDFast(_PREHASH_ItemID, (*it)); - if(msg->isSendFull(NULL)) - { - start_new_message = TRUE; - gAgent.sendReliableMessage(); - } - } - if(!start_new_message) - { - gAgent.sendReliableMessage(); + + // Prepare the data to fetch + LLSD item_entry; + item_entry["owner_id"] = owner_id; + item_entry["item_id"] = (*it); + items_llsd.append(item_entry); } + fetch_items_from_llsd(items_llsd); } // virtual @@ -3518,9 +3712,8 @@ void LLInventoryFetchComboObserver::fetch( // descendent of an incomplete folder because the item will show // up in an inventory descendents message soon enough so we do not // have to fetch it individually. + LLSD items_llsd; LLUUID owner_id; - LLMessageSystem* msg = gMessageSystem; - bool start_new_message = true; for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit) { LLViewerInventoryItem* item = gInventory.getItem(*iit); @@ -3543,33 +3736,17 @@ void LLInventoryFetchComboObserver::fetch( } if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end()) { - lldebugs << "fetching item " << *iit << llendl; - if(start_new_message) - { - start_new_message = false; - msg->newMessageFast(_PREHASH_FetchInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_OwnerID, owner_id); - msg->addUUIDFast(_PREHASH_ItemID, (*iit)); - if(msg->isSendFullFast(_PREHASH_InventoryData)) - { - start_new_message = true; - gAgent.sendReliableMessage(); - } + LLSD item_entry; + item_entry["owner_id"] = owner_id; + item_entry["item_id"] = (*iit); + items_llsd.append(item_entry); } else { lldebugs << "not worrying about " << *iit << llendl; } } - if(!start_new_message) - { - gAgent.sendReliableMessage(); - } + fetch_items_from_llsd(items_llsd); } void LLInventoryExistenceObserver::watchItem(const LLUUID& id) @@ -3615,7 +3792,17 @@ void LLInventoryAddedObserver::changed(U32 mask) // the network, figure out which item was updated. // Code from Gigs Taggert, sin allowed by JC. LLMessageSystem* msg = gMessageSystem; - std::string msg_name = msg->getMessageName(); + + std::string msg_name; + if (mMessageName.empty()) + { + msg_name = msg->getMessageName(); + } + else + { + msg_name = mMessageName; + } + if (msg_name.empty()) { return; diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 40dd55dda2..064ace52f2 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -69,6 +69,7 @@ public: }; virtual ~LLInventoryObserver() {}; virtual void changed(U32 mask) = 0; + std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328] }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -110,7 +111,18 @@ public: LLInventoryModel(); ~LLInventoryModel(); - + class fetchInventoryResponder: public LLHTTPClient::Responder + { + public: + fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; + void result(const LLSD& content); + void error(U32 status, const std::string& reason); + + public: + typedef std::vector folder_ref_t; + protected: + LLSD mRequestSD; + }; // // Accessors @@ -254,7 +266,8 @@ public: // Call this method when it's time to update everyone on a new // state, by default, the inventory model will not update // observers automatically. - void notifyObservers(); + // The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] + void notifyObservers(const std::string service_name=""); // This allows outsiders to tell the inventory if something has // been changed 'under the hood', but outside the control of the @@ -356,6 +369,7 @@ public: // start and stop background breadth-first fetching of inventory contents // this gets triggered when performing a filter-search static void startBackgroundFetch(const LLUUID& cat_id = LLUUID::null); // start fetch process + static void findLostItems(); static BOOL backgroundFetchActive(); static bool isEverythingFetched(); static void backgroundFetch(void*); // background fetch idle function diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 793944f7fa..eaf1b90be3 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2244,6 +2244,9 @@ bool idle_startup() } } + //DEV-17797. get null folder. Any items found here moved to Lost and Found + LLInventoryModel::findLostItems(); + LLStartUp::setStartupState( STATE_PRECACHE ); timeout.reset(); return FALSE; diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 7fe076553b..c28ed7c435 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -196,15 +196,34 @@ void LLViewerInventoryItem::fetchFromServer(void) const { if(!mIsComplete) { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("FetchInventory"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("OwnerID", mPermissions.getOwner()); - msg->addUUID("ItemID", mUUID); - gAgent.sendReliableMessage(); + std::string url; + + if( ALEXANDRIA_LINDEN_ID.getString() == mPermissions.getOwner().getString()) + url = gAgent.getRegion()->getCapability("FetchLib"); + else + url = gAgent.getRegion()->getCapability("FetchInventory"); + + if (!url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"][0]["owner_id"] = mPermissions.getOwner(); + body["items"][0]["item_id"] = mUUID; + + LLHTTPClient::post(url, body, new LLInventoryModel::fetchInventoryResponder(body)); + } + else + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("FetchInventory"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("OwnerID", mPermissions.getOwner()); + msg->addUUID("ItemID", mUUID); + gAgent.sendReliableMessage(); + } } else { @@ -441,7 +460,7 @@ bool LLViewerInventoryCategory::fetchDescendents() // This comes from LLInventoryFilter from llfolderview.h U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; - std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); + std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents"); if (!url.empty()) //Capability found. Build up LLSD and use it. { @@ -449,7 +468,7 @@ bool LLViewerInventoryCategory::fetchDescendents() } else { //Deprecated, but if we don't have a capability, use the old system. - llinfos << "FetchInventoryDescendents capability not found. Using deprecated UDP message." << llendl; + llinfos << "WebFetchInventoryDescendents capability not found. Using deprecated UDP message." << llendl; LLMessageSystem* msg = gMessageSystem; msg->newMessage("FetchInventoryDescendents"); msg->nextBlock("AgentData"); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index f8c7b317f0..fe6ce6faa0 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1387,7 +1387,10 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("DispatchRegionInfo"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); - capabilityNames.append("FetchInventoryDescendents"); + capabilityNames.append("FetchInventory"); + capabilityNames.append("WebFetchInventoryDescendents"); + capabilityNames.append("FetchLib"); + capabilityNames.append("FetchLibDescendents"); capabilityNames.append("GroupProposalBallot"); capabilityNames.append("HomeLocation"); capabilityNames.append("MapLayer"); -- cgit v1.2.3