From 80f61963272cc2311c5410671045e34212476134 Mon Sep 17 00:00:00 2001
From: Kartic Krishnamurthy <drunkensufi@lindenlab.com>
Date: Tue, 16 Sep 2008 18:12:32 +0000
Subject: [PATCH] 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 +++++--
 .../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 c841cdaf631..521b79c65ac 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 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<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(?P<second_float>\.\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<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T"
+                        r"(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})"
+                        r"(?P<second_float>(\.\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 @@ def elt(self, name, contents=None):
         if(contents is None or contents is ''):
             return "<%s />" % (name,)
         else:
+            if type(contents) is unicode:
+                contents = contents.encode('utf-8')
             return "<%s>%s</%s>" % (name, contents, name)
 
     def xml_esc(self, v):
+        if type(v) is unicode:
+            v = v.encode('utf-8')
         return v.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
 
     def LLSD(self, v):
@@ -237,9 +248,14 @@ def generate(self, something):
             raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
                 t, something))
 
-    def format(self, something):
+    def _format(self, something):
         return '<?xml version="1.0" ?>' + 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 @@ def REAL(self, v):
     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 @@ def DATE(self, v):
     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 @@ def _parse_map(self):
                 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 @@ def _parse(self):
             # '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 @@ def _parse_map(self):
                 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 '<?llsd/binary?>\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 = '<?llsd/binary?>\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('<?llsd/binary?>'):
-            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 228be49ed39..24701438f00 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 @@
 $/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 e83da12beb7..a3e3aec3600 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 0cdc4450694..a126ae4fbf7 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<LLAssetType::EType>(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<LLInventoryType::EType>(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<LLAssetType::EType>(type);
     }
+	w = INV_ASSET_TYPE_LABEL_WS;
+	if (sd.has(w))
+	{
+		S8 type = (U8)sd[w].asInteger();
+        mPreferredType = static_cast<LLAssetType::EType>(type);
+	}
+
     w = INV_NAME_LABEL;
     if (sd.has(w))
     {
diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp
index 111167ae271..9c36f0b7a00 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<LLSaleInfo::EForSale>(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 503ca947df2..284a7141d05 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 fe7012ad6c7..2a02bad89ec 100644
--- a/indra/llmessage/llpumpio.h
+++ b/indra/llmessage/llpumpio.h
@@ -165,6 +165,14 @@ class LLPumpIO
 	 */
 	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 @@ class LLPumpIO
 		// 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 6e5fa6def88..ff6ec9f0772 100644
--- a/indra/llmessage/llurlrequest.cpp
+++ b/indra/llmessage/llurlrequest.cpp
@@ -68,6 +68,7 @@ class LLURLRequestDetail
 	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 9ee3840f7fd..b56629b60a7 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -84,7 +84,7 @@ static std::deque<LLUUID> 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<LLViewerInventoryItem> 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<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
 
-	                LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id);
+            if (parent_id.isNull())
+            {
+			    LLPointer<LLViewerInventoryItem> 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<items_llsd.size();i++)
+	{
+		if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString())
+		{
+			body[0]["items"].append(items_llsd[i]);
+			continue;
+		}
+		if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString())
+		{
+			body[1]["items"].append(items_llsd[i]);
+			continue;
+		}
+	}
+		
+	for (S32 i=0; i<body.size(); i++)
+	{
+		if (0 >= 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; j<body[i]["items"].size(); j++)
+		{
+			LLSD item_entry = body[i]["items"][j];
+			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, 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 40dd55dda27..064ace52f2e 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -69,6 +69,7 @@ class LLInventoryObserver
 	};
 	virtual ~LLInventoryObserver() {};
 	virtual void changed(U32 mask) = 0;
+	std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328]
 };
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -110,7 +111,18 @@ class LLInventoryModel
 	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<LLViewerInventoryCategory*> folder_ref_t;
+	protected:
+		LLSD mRequestSD;
+	};
 
 	//
 	// Accessors
@@ -254,7 +266,8 @@ class LLInventoryModel
 	// 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 @@ class LLInventoryModel
 	// 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 793944f7fae..eaf1b90be38 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 7fe076553b8..c28ed7c435f 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 f8c7b317f0a..fe6ce6faa0e 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");
-- 
GitLab